@singlebase/core 0.7.0 → 0.8.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.
@@ -27,9 +27,6 @@ export interface AuthUIConfig {
27
27
  signInTitle?: string;
28
28
  /** The "Auth by Singlebase" credit under the card. On unless set false. */
29
29
  branding?: boolean;
30
- /** The credit's text and link. */
31
- brandingText?: string;
32
- brandingUrl?: string;
33
30
  initialScreen?: string;
34
31
  noAccountView?: boolean;
35
32
  redirectUrl?: string;
@@ -49,6 +46,6 @@ export interface AuthUIConfig {
49
46
  messages?: Record<string, string>;
50
47
  }
51
48
  /** Config keys that are applied as element properties, not handled specially. */
52
- export declare const AUTHUI_PROP_KEYS: readonly ["theme", "density", "fieldStyle", "radius", "layout", "oauthPlacement", "stepped", "logoUrl", "logoText", "brandLine", "brandFoot", "signInTitle", "branding", "brandingText", "brandingUrl", "initialScreen", "noAccountView", "redirectUrl", "tosUrl", "privacyUrl", "nonceStorageKey", "authEnabled", "allowEmailSignin", "allowEmailSignup", "allowEmailOtp", "allowOauth", "allowAccountCreation", "inviteEmail", "inviteOrg", "inviteCode"];
49
+ export declare const AUTHUI_PROP_KEYS: readonly ["theme", "density", "fieldStyle", "radius", "layout", "oauthPlacement", "stepped", "logoUrl", "logoText", "brandLine", "brandFoot", "signInTitle", "branding", "initialScreen", "noAccountView", "redirectUrl", "tosUrl", "privacyUrl", "nonceStorageKey", "authEnabled", "allowEmailSignin", "allowEmailSignup", "allowEmailOtp", "allowOauth", "allowAccountCreation", "inviteEmail", "inviteOrg", "inviteCode"];
53
50
  /** "fieldStyle" -> "field-style", for checking whether the host set it in markup. */
54
51
  export declare function authUIAttributeFor(key: string): string;
package/dist/errors.d.ts CHANGED
@@ -20,7 +20,7 @@ export declare class SinglebaseError extends Error {
20
20
  * *behavior* (focus a field, trigger a refresh, sign out locally); the actual
21
21
  * copy shown to the user is left to each component/its `.messages` override.
22
22
  */
23
- export type ErrorHint = "highlight_invalid_fields" | "generic_credentials_message" | "reveal_code_input" | "expired_or_invalid_code_message" | "attempt_refresh_or_signout" | "clear_session_and_ask_signin" | "clear_session_config_error" | "route_to_recovery_or_invite" | "show_password_requirements" | "require_changed_field" | "disable_retry_show_rate_limited" | "render_auth_unavailable" | "hide_email_forms" | "hide_signup" | "hide_oauth" | "remove_oauth_from_signup" | "refresh_settings_hide_provider" | "restart_oauth_new_nonce" | "ask_signin_then_link" | "route_to_signin" | "show_integrator_config_error" | "unknown";
23
+ export type ErrorHint = "highlight_invalid_fields" | "generic_credentials_message" | "reveal_code_input" | "expired_or_invalid_code_message" | "attempt_refresh_or_signout" | "clear_session_and_ask_signin" | "clear_session_config_error" | "route_to_recovery_or_invite" | "show_password_requirements" | "require_changed_field" | "disable_retry_show_rate_limited" | "render_auth_unavailable" | "hide_email_forms" | "hide_signup" | "hide_oauth" | "remove_oauth_from_signin" | "remove_oauth_from_signup" | "refresh_settings_hide_provider" | "restart_oauth_new_nonce" | "restart_oauth" | "explain_verified_email_required" | "ask_signin_then_link" | "provider_already_linked" | "route_to_signin" | "show_integrator_config_error" | "unknown";
24
24
  export declare function mapErrorToHint(code: string): ErrorHint;
25
25
  /** Back-compat alias: the error type is shared by every service. */
26
26
  export { SinglebaseError as SinglebaseAuthError };
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class l extends Error{constructor(t,r){super(t.message),this.name="SinglebaseError",this.status=t.status,this.type=t.type,this.code=t.message,this.details=t.details,this.traceId=t.trace_id??null,this.cause=r}static fromNetworkError(t){return new l({type:"NETWORK_ERROR",status:0,message:"NETWORK_ERROR"},t)}}const x={INVALID_PAYLOAD:"highlight_invalid_fields",INVALID_CREDENTIALS:"generic_credentials_message",CODE_REQUIRED:"reveal_code_input",INVALID_TOKEN:"expired_or_invalid_code_message",INVALID_ID_TOKEN:"attempt_refresh_or_signout",INVALID_BEARER_TOKEN:"attempt_refresh_or_signout",INVALID_SESSION:"clear_session_and_ask_signin",INVALID_TOKEN_AUDIENCE:"clear_session_config_error",REQUIRE_PASSWORD_CHANGE:"route_to_recovery_or_invite",INVALID_PASSWORD:"show_password_requirements",MISSING_PROFILE_DATA:"require_changed_field",AUTH_RATE_LIMITED:"disable_retry_show_rate_limited",AUTH_DISABLED:"render_auth_unavailable",EMAIL_PROVIDER_DISABLED:"hide_email_forms",EMAIL_SIGNUP_DISABLED:"hide_signup",OAUTH_DISABLED:"hide_oauth",OAUTH_SIGNUP_DISABLED:"remove_oauth_from_signup",INVALID_OAUTH_PROVIDER:"refresh_settings_hide_provider",INVALID_NONCE:"restart_oauth_new_nonce",SIGN_IN_TO_LINK_PROVIDER:"ask_signin_then_link",AUTHENTICATION_REQUIRED:"route_to_signin",UNAUTHORIZED_DOMAIN:"show_integrator_config_error"};function G(e){return x[e]??"unknown"}function F(e){return e==="localhost"||e==="127.0.0.1"||e==="0.0.0.0"}const _="https://v1.api.singlebase.io";function N(e){if(e.baseUrl===void 0)return _;if(e.baseUrl===null||String(e.baseUrl).trim()==="")throw new Error(`SinglebaseOptions.baseUrl cannot be null or empty. Omit it to use ${_}.`);return String(e.baseUrl).trim().replace(/\/+$/,"")}function U(e){var n;const t=N(e);v(t);const r=(n=e.urlAccessKey)==null?void 0:n.trim();return r?`${t}/${encodeURIComponent(r)}`:t}function v(e){let t;try{t=new URL(e)}catch{throw new Error(`SinglebaseOptions.baseUrl is not a valid URL: ${e}`)}if(t.protocol!=="https:"&&!F(t.hostname))throw new Error(`SinglebaseOptions.baseUrl must use HTTPS outside of localhost/127.0.0.1/0.0.0.0 (got ${t.protocol})`)}async function b(e,t,r,n={}){const s=U(e),o={operation:t,payload:r};n.options&&(o.options=n.options);const a={"Content-Type":"application/json"};e.apiKey&&(a["X-API-Key"]=e.apiKey),n.token&&(a.Authorization=`Bearer ${n.token}`);const c=e.fetch??globalThis.fetch;let i;try{i=await c(s,{method:"POST",headers:a,body:JSON.stringify(o),signal:n.signal})}catch(d){throw l.fromNetworkError(d)}let g;try{g=await i.json()}catch(d){throw l.fromNetworkError(d)}if("error"in g)throw new l(g.error);return g.data}const J=new Set(["INVALID_ID_TOKEN","INVALID_BEARER_TOKEN"]);class W{constructor(t){this.options=t,this.bridge=null,this.provider=null}setAuthBridge(t){this.bridge=t}setAuthProvider(t){this.provider=t}async resolveBridge(){return this.bridge?this.bridge:this.provider?(this.bridge=await this.provider(),this.bridge):null}async dispatch(t,{bearer:r,signal:n}={}){const s=r!==void 0,o=s?null:await this.resolveBridge(),a=s?r:(o==null?void 0:o.getToken())??null,c={token:a,signal:n,options:t.options};try{return await b(this.options,t.operation,t.payload??{},c)}catch(i){const g=i instanceof l?i.code:"";if(!(!s&&a!==null&&o!==null&&J.has(g)))throw i;const w=await o.refresh();if(!w)throw i;return b(this.options,t.operation,t.payload??{},{...c,token:w})}}}const q="singlebase-auth",Y=1,u="session",S="wrapping-key",h="session";function A(){let e=null;return{get:()=>e,set:t=>{e=t},clear:()=>{e=null}}}function L(e,t){return{get:()=>{var n;const r=(n=e())==null?void 0:n.getItem(t);if(!r)return null;try{return JSON.parse(r)}catch{return null}},set:r=>{var n;(n=e())==null||n.setItem(t,JSON.stringify(r))},clear:()=>{var r;(r=e())==null||r.removeItem(t)}}}function Q(e="singlebase-auth-session"){return L(()=>globalThis.localStorage,e)}function $(e="singlebase-auth-session"){return L(()=>globalThis.sessionStorage,e)}function E(e){return new Promise((t,r)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>r(e.error)})}function m(){return new Promise((e,t)=>{const r=globalThis.indexedDB.open(q,Y);r.onupgradeneeded=()=>{r.result.objectStoreNames.contains(u)||r.result.createObjectStore(u)},r.onsuccess=()=>e(r.result),r.onerror=()=>t(r.error),r.onblocked=()=>t(new Error("indexedDB open blocked"))})}async function I(e){const t=await m();try{const r=t.transaction(u,"readonly");return await E(r.objectStore(u).get(e))}finally{t.close()}}async function T(e,t){const r=await m();try{const n=r.transaction(u,"readwrite");await E(n.objectStore(u).put(t,e))}finally{r.close()}}async function D(e){const t=await m();try{const r=t.transaction(u,"readwrite");await E(r.objectStore(u).delete(e))}finally{t.close()}}function C(){return typeof globalThis.indexedDB<"u"&&typeof globalThis.crypto<"u"&&typeof globalThis.crypto.subtle<"u"}function P(){let e=null;async function t(){return e||(e=(async()=>{const r=await I(S);if(r)return r;const n=await globalThis.crypto.subtle.generateKey({name:"AES-GCM",length:256},!1,["encrypt","decrypt"]);return await T(S,n),n})().catch(r=>{throw e=null,r})),e}return{async get(){const r=await I(h);if(!r)return null;try{const n=await t(),s=await globalThis.crypto.subtle.decrypt({name:"AES-GCM",iv:r.iv},n,r.data);return JSON.parse(new TextDecoder().decode(s))}catch{return await D(h).catch(()=>{}),null}},async set(r){const n=await t(),s=globalThis.crypto.getRandomValues(new Uint8Array(12)),o=await globalThis.crypto.subtle.encrypt({name:"AES-GCM",iv:s},n,new TextEncoder().encode(JSON.stringify(r)));await T(h,{iv:s,data:o})},async clear(){await D(h).catch(()=>{})}}}function Z(){let e=null,t=!1;async function r(){if(e)return e;if(!t){if(t=!0,C()){const n=P();try{return await n.get(),e=n,e}catch{}}e=typeof globalThis.sessionStorage<"u"?$():A()}return e??A()}return{async get(){try{return await(await r()).get()}catch{return null}},async set(n){try{await(await r()).set(n)}catch{}},async clear(){try{await(await r()).clear()}catch{}}}}class z{constructor(){this.listeners=new Map}on(t,r){let n=this.listeners.get(t);return n||(n=new Set,this.listeners.set(t,n)),n.add(r),()=>n.delete(r)}emit(t,r){const n=this.listeners.get(t);if(n)for(const s of n)s(r)}}const X=["theme","density","fieldStyle","radius","layout","oauthPlacement","stepped","logoUrl","logoText","brandLine","brandFoot","signInTitle","branding","brandingText","brandingUrl","initialScreen","noAccountView","redirectUrl","tosUrl","privacyUrl","nonceStorageKey","authEnabled","allowEmailSignin","allowEmailSignup","allowEmailOtp","allowOauth","allowAccountCreation","inviteEmail","inviteOrg","inviteCode"];function ee(e){return e.replace(/[A-Z]/g,t=>"-"+t.toLowerCase())}const k=["$eq","$ne","$in","$nin","$gt","$gte","$lt","$lte","$elemMatch"],B=new Set(k);function te(e){if(e===null||typeof e!="object"||Array.isArray(e))return!1;const t=Object.keys(e);return t.length>0&&t.every(r=>B.has(r))}function K(e,t){let r=e;for(const n of t.split(".")){if(r==null||typeof r!="object")return;r=r[n]}return r}function f(e,t){if(e===t)return!0;if(Array.isArray(e)&&Array.isArray(t))return e.length===t.length&&e.every((r,n)=>f(r,t[n]));if(e&&t&&typeof e=="object"&&typeof t=="object"){const r=Object.keys(e),n=Object.keys(t);return r.length===n.length&&r.every(s=>f(e[s],t[s]))}return!1}function re(e,t){return typeof e=="number"&&typeof t=="number"?Number.isNaN(e)||Number.isNaN(t)?null:e-t:typeof e=="string"&&typeof t=="string"?e<t?-1:e>t?1:0:e instanceof Date&&t instanceof Date?e.getTime()-t.getTime():null}function j(e,t,r){switch(e){case"$eq":return f(t,r);case"$ne":return!f(t,r);case"$in":return Array.isArray(r)?Array.isArray(t)?t.some(n=>r.some(s=>f(n,s))):r.some(n=>f(t,n)):!1;case"$nin":return Array.isArray(r)?!j("$in",t,r):!1;case"$gt":case"$gte":case"$lt":case"$lte":{const n=re(t,r);return n===null?!1:e==="$gt"?n>0:e==="$gte"?n>=0:e==="$lt"?n<0:n<=0}case"$elemMatch":return Array.isArray(t)?t.some(n=>V(n,r)):!1;default:return!1}}function V(e,t){return te(t)?Object.entries(t).every(([r,n])=>j(r,e,n)):f(e,t)}function ne(e,t){if(!t)return!0;const r=Object.entries(t);return r.length===0?!0:e==null?!1:r.every(([n,s])=>V(K(e,n),s))}function se(e){if(e==null)return[];if(typeof e!="object"||Array.isArray(e))return["predicate must be an object"];const t=[];for(const[r,n]of Object.entries(e)){if(r.startsWith("$")){t.push(`"${r}": top-level operators are not supported; use a field name`);continue}if(n===null||typeof n!="object"||Array.isArray(n))continue;const s=Object.keys(n),o=s.filter(a=>a.startsWith("$"));if(o.length===0){t.push(`"${r}": nested objects are not supported; use dot notation, e.g. "${r}.field"`);continue}o.length!==s.length&&t.push(`"${r}": cannot mix operators with plain keys`);for(const a of o)B.has(a)||t.push(`"${r}": unsupported operator "${a}"`)}return t}const y=()=>{};function H(){return{post:y,subscribe:()=>y,close:y}}function oe(){const e=globalThis.crypto;return e&&"randomUUID"in e?e.randomUUID():Math.random().toString(36).slice(2)+Date.now().toString(36)}function ie(e){var n;const t=oe(),r=globalThis.BroadcastChannel;if(r){let s;try{s=new r(e)}catch{return O(e,t)}return(n=s.unref)==null||n.call(s),{post(o){try{s.postMessage({signal:o,from:t,at:Date.now()})}catch{}},subscribe(o){const a=c=>{const i=c.data;!i||typeof i!="object"||i.from!==t&&(i.signal!=="session-changed"&&i.signal!=="signout"||o(i))};return s.addEventListener("message",a),()=>s.removeEventListener("message",a)},close(){try{s.close()}catch{}}}}return O(e,t)}function O(e,t){const r=ae();if(!r)return H();const n=`${e}:signal`;return{post(s){try{r.setItem(n,JSON.stringify({signal:s,from:t,at:Date.now()}))}catch{}},subscribe(s){var a;const o=c=>{if(!(c.key!==n||!c.newValue))try{const i=JSON.parse(c.newValue);if(i.from===t||i.signal!=="session-changed"&&i.signal!=="signout")return;s(i)}catch{}};return(a=globalThis.addEventListener)==null||a.call(globalThis,"storage",o),()=>{var c;return(c=globalThis.removeEventListener)==null?void 0:c.call(globalThis,"storage",o)}},close:y}}function ae(){try{const e=globalThis.localStorage;if(!e)return null;const t="__singlebase_probe__";return e.setItem(t,"1"),e.removeItem(t),e}catch{return null}}const R=Symbol.for("singlebase.client.registry");function p(){const e=globalThis;let t=e[R];return t||(t={instances:new Map,default:null},e[R]=t),t}function M(e){return[e.baseUrl??_,e.urlAccessKey??"",e.apiKey??"",e.audience??"web"].join("|")}function ce(e,t){const r=p(),n=M(e),s=r.instances.get(n);if(s)return s;const o=t();return r.instances.set(n,o),r.default||(r.default=o),o}function le(){return p().default}function ue(e){p().default=e}function fe(e){const t=p();for(const[r,n]of t.instances)n===e&&t.instances.delete(r);t.default===e&&(t.default=t.instances.values().next().value??null)}exports.AUTHUI_PROP_KEYS=X;exports.DEFAULT_BASE_URL=_;exports.EventEmitter=z;exports.OPERATORS=k;exports.RpcDispatcher=W;exports.SinglebaseAuthError=l;exports.SinglebaseError=l;exports.assertSecureBaseUrl=v;exports.authUIAttributeFor=ee;exports.canUseCryptoStorage=C;exports.clientKey=M;exports.crossTabChannel=ie;exports.defaultStorage=Z;exports.endpointFor=U;exports.getDefaultClient=le;exports.getPath=K;exports.indexedDbCryptoStorage=P;exports.localStorageAdapter=Q;exports.mapErrorToHint=G;exports.matchesPredicate=ne;exports.memoryStorage=A;exports.noopCrossTabChannel=H;exports.request=b;exports.resolveBaseUrl=N;exports.resolveClient=ce;exports.sessionStorageAdapter=$;exports.setDefaultClient=ue;exports.unregisterClient=fe;exports.validatePredicate=se;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class l extends Error{constructor(t,r){super(t.message),this.name="SinglebaseError",this.status=t.status,this.type=t.type,this.code=t.message,this.details=t.details,this.traceId=t.trace_id??null,this.cause=r}static fromNetworkError(t){return new l({type:"NETWORK_ERROR",status:0,message:"NETWORK_ERROR"},t)}}const F={INVALID_PAYLOAD:"highlight_invalid_fields",INVALID_CREDENTIALS:"generic_credentials_message",CODE_REQUIRED:"reveal_code_input",INVALID_TOKEN:"expired_or_invalid_code_message",INVALID_ID_TOKEN:"attempt_refresh_or_signout",INVALID_BEARER_TOKEN:"attempt_refresh_or_signout",INVALID_SESSION:"clear_session_and_ask_signin",INVALID_TOKEN_AUDIENCE:"clear_session_config_error",REQUIRE_PASSWORD_CHANGE:"route_to_recovery_or_invite",INVALID_PASSWORD:"show_password_requirements",MISSING_PROFILE_DATA:"require_changed_field",AUTH_RATE_LIMITED:"disable_retry_show_rate_limited",AUTH_DISABLED:"render_auth_unavailable",EMAIL_PROVIDER_DISABLED:"hide_email_forms",EMAIL_SIGNUP_DISABLED:"hide_signup",OAUTH_DISABLED:"hide_oauth",OAUTH_SIGNIN_DISABLED:"remove_oauth_from_signin",OAUTH_SIGNUP_DISABLED:"remove_oauth_from_signup",INVALID_OAUTH_PROVIDER:"refresh_settings_hide_provider",MISSING_OAUTH_CREDENTIALS:"show_integrator_config_error",OAUTH_VERIFICATION_FAILED:"restart_oauth",VERIFIED_PROVIDER_EMAIL_REQUIRED:"explain_verified_email_required",INVALID_NONCE:"restart_oauth_new_nonce",SIGN_IN_TO_LINK_PROVIDER:"ask_signin_then_link",PROVIDER_ALREADY_LINKED:"provider_already_linked",AUTHENTICATION_REQUIRED:"route_to_signin",UNAUTHORIZED_DOMAIN:"show_integrator_config_error"};function G(e){return F[e]??"unknown"}function x(e){return e==="localhost"||e==="127.0.0.1"||e==="0.0.0.0"}const y="https://v1.api.singlebase.io";function N(e){if(e.baseUrl===void 0)return y;if(e.baseUrl===null||String(e.baseUrl).trim()==="")throw new Error(`SinglebaseOptions.baseUrl cannot be null or empty. Omit it to use ${y}.`);return String(e.baseUrl).trim().replace(/\/+$/,"")}function U(e){var n;const t=N(e);v(t);const r=(n=e.urlAccessKey)==null?void 0:n.trim();return r?`${t}/${encodeURIComponent(r)}`:t}function v(e){let t;try{t=new URL(e)}catch{throw new Error(`SinglebaseOptions.baseUrl is not a valid URL: ${e}`)}if(t.protocol!=="https:"&&!x(t.hostname))throw new Error(`SinglebaseOptions.baseUrl must use HTTPS outside of localhost/127.0.0.1/0.0.0.0 (got ${t.protocol})`)}async function E(e,t,r,n={}){const s=U(e),o={operation:t,payload:r};n.options&&(o.options=n.options);const a={"Content-Type":"application/json"};e.apiKey&&(a["X-API-Key"]=e.apiKey),n.token&&(a.Authorization=`Bearer ${n.token}`);const c=e.fetch??globalThis.fetch;let i;try{i=await c(s,{method:"POST",headers:a,body:JSON.stringify(o),signal:n.signal})}catch(d){throw l.fromNetworkError(d)}let g;try{g=await i.json()}catch(d){throw l.fromNetworkError(d)}if("error"in g)throw new l(g.error);return g.data}const q=new Set(["INVALID_ID_TOKEN","INVALID_BEARER_TOKEN"]);class J{constructor(t){this.options=t,this.bridge=null,this.provider=null}setAuthBridge(t){this.bridge=t}setAuthProvider(t){this.provider=t}async resolveBridge(){return this.bridge?this.bridge:this.provider?(this.bridge=await this.provider(),this.bridge):null}async dispatch(t,{bearer:r,signal:n}={}){const s=r!==void 0,o=s?null:await this.resolveBridge(),a=s?r:(o==null?void 0:o.getToken())??null,c={token:a,signal:n,options:t.options};try{return await E(this.options,t.operation,t.payload??{},c)}catch(i){const g=i instanceof l?i.code:"";if(!(!s&&a!==null&&o!==null&&q.has(g)))throw i;const m=await o.refresh();if(!m)throw i;return E(this.options,t.operation,t.payload??{},{...c,token:m})}}}const W="singlebase-auth",Y=1,u="session",S="wrapping-key",h="session";function b(){let e=null;return{get:()=>e,set:t=>{e=t},clear:()=>{e=null}}}function L(e,t){return{get:()=>{var n;const r=(n=e())==null?void 0:n.getItem(t);if(!r)return null;try{return JSON.parse(r)}catch{return null}},set:r=>{var n;(n=e())==null||n.setItem(t,JSON.stringify(r))},clear:()=>{var r;(r=e())==null||r.removeItem(t)}}}function Q(e="singlebase-auth-session"){return L(()=>globalThis.localStorage,e)}function C(e="singlebase-auth-session"){return L(()=>globalThis.sessionStorage,e)}function A(e){return new Promise((t,r)=>{e.onsuccess=()=>t(e.result),e.onerror=()=>r(e.error)})}function I(){return new Promise((e,t)=>{const r=globalThis.indexedDB.open(W,Y);r.onupgradeneeded=()=>{r.result.objectStoreNames.contains(u)||r.result.createObjectStore(u)},r.onsuccess=()=>e(r.result),r.onerror=()=>t(r.error),r.onblocked=()=>t(new Error("indexedDB open blocked"))})}async function w(e){const t=await I();try{const r=t.transaction(u,"readonly");return await A(r.objectStore(u).get(e))}finally{t.close()}}async function D(e,t){const r=await I();try{const n=r.transaction(u,"readwrite");await A(n.objectStore(u).put(t,e))}finally{r.close()}}async function T(e){const t=await I();try{const r=t.transaction(u,"readwrite");await A(r.objectStore(u).delete(e))}finally{t.close()}}function $(){return typeof globalThis.indexedDB<"u"&&typeof globalThis.crypto<"u"&&typeof globalThis.crypto.subtle<"u"}function P(){let e=null;async function t(){return e||(e=(async()=>{const r=await w(S);if(r)return r;const n=await globalThis.crypto.subtle.generateKey({name:"AES-GCM",length:256},!1,["encrypt","decrypt"]);return await D(S,n),n})().catch(r=>{throw e=null,r})),e}return{async get(){const r=await w(h);if(!r)return null;try{const n=await t(),s=await globalThis.crypto.subtle.decrypt({name:"AES-GCM",iv:r.iv},n,r.data);return JSON.parse(new TextDecoder().decode(s))}catch{return await T(h).catch(()=>{}),null}},async set(r){const n=await t(),s=globalThis.crypto.getRandomValues(new Uint8Array(12)),o=await globalThis.crypto.subtle.encrypt({name:"AES-GCM",iv:s},n,new TextEncoder().encode(JSON.stringify(r)));await D(h,{iv:s,data:o})},async clear(){await T(h).catch(()=>{})}}}function Z(){let e=null,t=!1;async function r(){if(e)return e;if(!t){if(t=!0,$()){const n=P();try{return await n.get(),e=n,e}catch{}}e=typeof globalThis.sessionStorage<"u"?C():b()}return e??b()}return{async get(){try{return await(await r()).get()}catch{return null}},async set(n){try{await(await r()).set(n)}catch{}},async clear(){try{await(await r()).clear()}catch{}}}}class z{constructor(){this.listeners=new Map}on(t,r){let n=this.listeners.get(t);return n||(n=new Set,this.listeners.set(t,n)),n.add(r),()=>n.delete(r)}emit(t,r){const n=this.listeners.get(t);if(n)for(const s of n)s(r)}}const X=["theme","density","fieldStyle","radius","layout","oauthPlacement","stepped","logoUrl","logoText","brandLine","brandFoot","signInTitle","branding","initialScreen","noAccountView","redirectUrl","tosUrl","privacyUrl","nonceStorageKey","authEnabled","allowEmailSignin","allowEmailSignup","allowEmailOtp","allowOauth","allowAccountCreation","inviteEmail","inviteOrg","inviteCode"];function ee(e){return e.replace(/[A-Z]/g,t=>"-"+t.toLowerCase())}const k=["$eq","$ne","$in","$nin","$gt","$gte","$lt","$lte","$elemMatch"],B=new Set(k);function te(e){if(e===null||typeof e!="object"||Array.isArray(e))return!1;const t=Object.keys(e);return t.length>0&&t.every(r=>B.has(r))}function K(e,t){let r=e;for(const n of t.split(".")){if(r==null||typeof r!="object")return;r=r[n]}return r}function f(e,t){if(e===t)return!0;if(Array.isArray(e)&&Array.isArray(t))return e.length===t.length&&e.every((r,n)=>f(r,t[n]));if(e&&t&&typeof e=="object"&&typeof t=="object"){const r=Object.keys(e),n=Object.keys(t);return r.length===n.length&&r.every(s=>f(e[s],t[s]))}return!1}function re(e,t){return typeof e=="number"&&typeof t=="number"?Number.isNaN(e)||Number.isNaN(t)?null:e-t:typeof e=="string"&&typeof t=="string"?e<t?-1:e>t?1:0:e instanceof Date&&t instanceof Date?e.getTime()-t.getTime():null}function V(e,t,r){switch(e){case"$eq":return f(t,r);case"$ne":return!f(t,r);case"$in":return Array.isArray(r)?Array.isArray(t)?t.some(n=>r.some(s=>f(n,s))):r.some(n=>f(t,n)):!1;case"$nin":return Array.isArray(r)?!V("$in",t,r):!1;case"$gt":case"$gte":case"$lt":case"$lte":{const n=re(t,r);return n===null?!1:e==="$gt"?n>0:e==="$gte"?n>=0:e==="$lt"?n<0:n<=0}case"$elemMatch":return Array.isArray(t)?t.some(n=>j(n,r)):!1;default:return!1}}function j(e,t){return te(t)?Object.entries(t).every(([r,n])=>V(r,e,n)):f(e,t)}function ne(e,t){if(!t)return!0;const r=Object.entries(t);return r.length===0?!0:e==null?!1:r.every(([n,s])=>j(K(e,n),s))}function se(e){if(e==null)return[];if(typeof e!="object"||Array.isArray(e))return["predicate must be an object"];const t=[];for(const[r,n]of Object.entries(e)){if(r.startsWith("$")){t.push(`"${r}": top-level operators are not supported; use a field name`);continue}if(n===null||typeof n!="object"||Array.isArray(n))continue;const s=Object.keys(n),o=s.filter(a=>a.startsWith("$"));if(o.length===0){t.push(`"${r}": nested objects are not supported; use dot notation, e.g. "${r}.field"`);continue}o.length!==s.length&&t.push(`"${r}": cannot mix operators with plain keys`);for(const a of o)B.has(a)||t.push(`"${r}": unsupported operator "${a}"`)}return t}const _=()=>{};function H(){return{post:_,subscribe:()=>_,close:_}}function oe(){const e=globalThis.crypto;return e&&"randomUUID"in e?e.randomUUID():Math.random().toString(36).slice(2)+Date.now().toString(36)}function ie(e){var n;const t=oe(),r=globalThis.BroadcastChannel;if(r){let s;try{s=new r(e)}catch{return R(e,t)}return(n=s.unref)==null||n.call(s),{post(o){try{s.postMessage({signal:o,from:t,at:Date.now()})}catch{}},subscribe(o){const a=c=>{const i=c.data;!i||typeof i!="object"||i.from!==t&&(i.signal!=="session-changed"&&i.signal!=="signout"||o(i))};return s.addEventListener("message",a),()=>s.removeEventListener("message",a)},close(){try{s.close()}catch{}}}}return R(e,t)}function R(e,t){const r=ae();if(!r)return H();const n=`${e}:signal`;return{post(s){try{r.setItem(n,JSON.stringify({signal:s,from:t,at:Date.now()}))}catch{}},subscribe(s){var a;const o=c=>{if(!(c.key!==n||!c.newValue))try{const i=JSON.parse(c.newValue);if(i.from===t||i.signal!=="session-changed"&&i.signal!=="signout")return;s(i)}catch{}};return(a=globalThis.addEventListener)==null||a.call(globalThis,"storage",o),()=>{var c;return(c=globalThis.removeEventListener)==null?void 0:c.call(globalThis,"storage",o)}},close:_}}function ae(){try{const e=globalThis.localStorage;if(!e)return null;const t="__singlebase_probe__";return e.setItem(t,"1"),e.removeItem(t),e}catch{return null}}const O=Symbol.for("singlebase.client.registry");function p(){const e=globalThis;let t=e[O];return t||(t={instances:new Map,default:null},e[O]=t),t}function M(e){return[e.baseUrl??y,e.urlAccessKey??"",e.apiKey??"",e.audience??"web"].join("|")}function ce(e,t){const r=p(),n=M(e),s=r.instances.get(n);if(s)return s;const o=t();return r.instances.set(n,o),r.default||(r.default=o),o}function le(){return p().default}function ue(e){p().default=e}function fe(e){const t=p();for(const[r,n]of t.instances)n===e&&t.instances.delete(r);t.default===e&&(t.default=t.instances.values().next().value??null)}exports.AUTHUI_PROP_KEYS=X;exports.DEFAULT_BASE_URL=y;exports.EventEmitter=z;exports.OPERATORS=k;exports.RpcDispatcher=J;exports.SinglebaseAuthError=l;exports.SinglebaseError=l;exports.assertSecureBaseUrl=v;exports.authUIAttributeFor=ee;exports.canUseCryptoStorage=$;exports.clientKey=M;exports.crossTabChannel=ie;exports.defaultStorage=Z;exports.endpointFor=U;exports.getDefaultClient=le;exports.getPath=K;exports.indexedDbCryptoStorage=P;exports.localStorageAdapter=Q;exports.mapErrorToHint=G;exports.matchesPredicate=ne;exports.memoryStorage=b;exports.noopCrossTabChannel=H;exports.request=E;exports.resolveBaseUrl=N;exports.resolveClient=ce;exports.sessionStorageAdapter=C;exports.setDefaultClient=ue;exports.unregisterClient=fe;exports.validatePredicate=se;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/errors.ts","../src/transport.ts","../src/dispatcher.ts","../src/storage.ts","../src/events.ts","../src/authui-config.ts","../src/predicate.ts","../src/cross-tab.ts","../src/registry.ts"],"sourcesContent":["import type { ErrorResponsePayload } from \"./types.js\";\n\n/**\n * Normalized error thrown by every SDK method. `code` is the server's\n * machine-readable `error.message` (e.g. \"INVALID_CREDENTIALS\"), never the\n * human-readable text.\n */\nexport class SinglebaseError extends Error {\n status: number;\n type: string;\n code: string;\n details?: unknown;\n traceId?: string | null;\n override cause?: unknown;\n\n constructor(payload: ErrorResponsePayload, cause?: unknown) {\n super(payload.message);\n this.name = \"SinglebaseError\";\n this.status = payload.status;\n this.type = payload.type;\n this.code = payload.message;\n this.details = payload.details;\n this.traceId = payload.trace_id ?? null;\n this.cause = cause;\n }\n\n static fromNetworkError(cause: unknown): SinglebaseError {\n return new SinglebaseError(\n {\n type: \"NETWORK_ERROR\",\n status: 0,\n message: \"NETWORK_ERROR\"\n },\n cause\n );\n }\n}\n\n/**\n * Recommended UI behavior for a given server error code, taken verbatim from\n * the auth API's error-mapping table. UI components use this to decide\n * *behavior* (focus a field, trigger a refresh, sign out locally); the actual\n * copy shown to the user is left to each component/its `.messages` override.\n */\nexport type ErrorHint =\n | \"highlight_invalid_fields\"\n | \"generic_credentials_message\"\n | \"reveal_code_input\"\n | \"expired_or_invalid_code_message\"\n | \"attempt_refresh_or_signout\"\n | \"clear_session_and_ask_signin\"\n | \"clear_session_config_error\"\n | \"route_to_recovery_or_invite\"\n | \"show_password_requirements\"\n | \"require_changed_field\"\n | \"disable_retry_show_rate_limited\"\n | \"render_auth_unavailable\"\n | \"hide_email_forms\"\n | \"hide_signup\"\n | \"hide_oauth\"\n | \"remove_oauth_from_signup\"\n | \"refresh_settings_hide_provider\"\n | \"restart_oauth_new_nonce\"\n | \"ask_signin_then_link\"\n | \"route_to_signin\"\n | \"show_integrator_config_error\"\n | \"unknown\";\n\nconst ERROR_HINTS: Record<string, ErrorHint> = {\n INVALID_PAYLOAD: \"highlight_invalid_fields\",\n INVALID_CREDENTIALS: \"generic_credentials_message\",\n CODE_REQUIRED: \"reveal_code_input\",\n INVALID_TOKEN: \"expired_or_invalid_code_message\",\n INVALID_ID_TOKEN: \"attempt_refresh_or_signout\",\n INVALID_BEARER_TOKEN: \"attempt_refresh_or_signout\",\n INVALID_SESSION: \"clear_session_and_ask_signin\",\n INVALID_TOKEN_AUDIENCE: \"clear_session_config_error\",\n REQUIRE_PASSWORD_CHANGE: \"route_to_recovery_or_invite\",\n INVALID_PASSWORD: \"show_password_requirements\",\n MISSING_PROFILE_DATA: \"require_changed_field\",\n AUTH_RATE_LIMITED: \"disable_retry_show_rate_limited\",\n AUTH_DISABLED: \"render_auth_unavailable\",\n EMAIL_PROVIDER_DISABLED: \"hide_email_forms\",\n EMAIL_SIGNUP_DISABLED: \"hide_signup\",\n OAUTH_DISABLED: \"hide_oauth\",\n OAUTH_SIGNUP_DISABLED: \"remove_oauth_from_signup\",\n INVALID_OAUTH_PROVIDER: \"refresh_settings_hide_provider\",\n INVALID_NONCE: \"restart_oauth_new_nonce\",\n SIGN_IN_TO_LINK_PROVIDER: \"ask_signin_then_link\",\n AUTHENTICATION_REQUIRED: \"route_to_signin\",\n UNAUTHORIZED_DOMAIN: \"show_integrator_config_error\"\n};\n\nexport function mapErrorToHint(code: string): ErrorHint {\n return ERROR_HINTS[code] ?? \"unknown\";\n}\n\n/** Back-compat alias: the error type is shared by every service. */\nexport { SinglebaseError as SinglebaseAuthError };\n","import { SinglebaseError } from \"./errors.js\";\nimport type {\n SinglebaseOptions,\n ErrorResponse,\n RequestEnvelope,\n SuccessResponse\n} from \"./types.js\";\n\nexport interface RequestExtras {\n token?: string | null;\n signal?: AbortSignal;\n options?: Record<string, unknown>;\n}\n\nfunction isLocalHost(hostname: string): boolean {\n return hostname === \"localhost\" || hostname === \"127.0.0.1\" || hostname === \"0.0.0.0\";\n}\n\n/** Where requests go when `baseUrl` is not given. */\nexport const DEFAULT_BASE_URL = \"https://v1.api.singlebase.io\";\n\n/**\n * The effective API root: the default when `baseUrl` is omitted. An explicit\n * null or empty value is a configuration mistake, and is reported as one\n * rather than silently replaced.\n */\nexport function resolveBaseUrl(options: Pick<SinglebaseOptions, \"baseUrl\">): string {\n if (options.baseUrl === undefined) return DEFAULT_BASE_URL;\n if (options.baseUrl === null || String(options.baseUrl).trim() === \"\") {\n throw new Error(\n `SinglebaseOptions.baseUrl cannot be null or empty. Omit it to use ${DEFAULT_BASE_URL}.`\n );\n }\n return String(options.baseUrl).trim().replace(/\\/+$/, \"\");\n}\n\n/**\n * The URL every request is POSTed to: `{baseUrl}/{urlAccessKey}`, or just\n * `{baseUrl}` when there is no access key.\n */\nexport function endpointFor(options: Pick<SinglebaseOptions, \"baseUrl\" | \"urlAccessKey\">): string {\n const base = resolveBaseUrl(options);\n assertSecureBaseUrl(base);\n const key = options.urlAccessKey?.trim();\n return key ? `${base}/${encodeURIComponent(key)}` : base;\n}\n\n/** Enforces the spec's HTTPS-outside-localhost rule for the configured baseUrl. */\nexport function assertSecureBaseUrl(baseUrl: string): void {\n let url: URL;\n try {\n url = new URL(baseUrl);\n } catch {\n throw new Error(`SinglebaseOptions.baseUrl is not a valid URL: ${baseUrl}`);\n }\n if (url.protocol !== \"https:\" && !isLocalHost(url.hostname)) {\n throw new Error(\n `SinglebaseOptions.baseUrl must use HTTPS outside of localhost/127.0.0.1/0.0.0.0 (got ${url.protocol})`\n );\n }\n}\n\n/**\n * Low-level RPC request. Returns the parsed `data` on success and throws\n * `SinglebaseError` on a well-formed error response or a network/transport\n * failure. Never leaks tokens into thrown error objects, logs, or URLs.\n */\nexport async function request<TData = unknown, TPayload = unknown>(\n clientOptions: SinglebaseOptions,\n operation: string,\n payload: TPayload,\n extras: RequestExtras = {}\n): Promise<TData> {\n const url = endpointFor(clientOptions);\n\n const envelope: RequestEnvelope<TPayload> = { operation, payload };\n if (extras.options) envelope.options = extras.options;\n\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (clientOptions.apiKey) headers[\"X-API-Key\"] = clientOptions.apiKey;\n if (extras.token) {\n headers.Authorization = `Bearer ${extras.token}`;\n }\n\n const fetchImpl = clientOptions.fetch ?? globalThis.fetch;\n\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(envelope),\n signal: extras.signal\n });\n } catch (cause) {\n throw SinglebaseError.fromNetworkError(cause);\n }\n\n let json: SuccessResponse<TData> | ErrorResponse;\n try {\n json = await res.json();\n } catch (cause) {\n throw SinglebaseError.fromNetworkError(cause);\n }\n\n if (\"error\" in json) {\n throw new SinglebaseError(json.error);\n }\n return json.data;\n}\n","import { SinglebaseError } from \"./errors.js\";\nimport { request } from \"./transport.js\";\nimport type { SinglebaseOptions } from \"./types.js\";\n\n/** Server error codes that mean \"your bearer token is stale, try refreshing\". */\nconst RETRYABLE_AUTH_CODES = new Set([\"INVALID_ID_TOKEN\", \"INVALID_BEARER_TOKEN\"]);\n\nexport interface DispatchEnvelope<TPayload = unknown> {\n operation: string;\n payload?: TPayload;\n options?: Record<string, unknown>;\n}\n\nexport interface DispatchOptions {\n /**\n * Explicit bearer token. Omit to let the dispatcher inject the current\n * session's ID token; pass `null` to force an unauthenticated call.\n */\n bearer?: string | null;\n signal?: AbortSignal;\n}\n\nexport interface DispatcherAuthBridge {\n /** Current ID token, or null when signed out. */\n getToken(): string | null;\n /** Refresh once and return the new ID token, or null if unrecoverable. */\n refresh(): Promise<string | null>;\n}\n\n/**\n * Supplies the auth bridge on demand. Returning a promise lets auth be\n * *constructed lazily* — a page that never signs in pays nothing — while\n * still being *hydrated eagerly* before the first call that could carry a\n * token. Without this, a `data.query()` fired on page load would race the\n * session restore and silently go out unauthenticated.\n */\nexport type AuthBridgeProvider = () => Promise<DispatcherAuthBridge | null>;\n\n/**\n * One RPC pipe shared by every Singlebase service.\n *\n * Auth and every service client go through this, so there is a single place\n * that knows the envelope shape, the endpoint, how a bearer is attached, and\n * how a stale token is recovered.\n */\nexport class RpcDispatcher {\n private bridge: DispatcherAuthBridge | null = null;\n private provider: AuthBridgeProvider | null = null;\n\n constructor(readonly options: SinglebaseOptions) {}\n\n /** Registers the session owner (the auth client) as the token source. */\n setAuthBridge(bridge: DispatcherAuthBridge | null): void {\n this.bridge = bridge;\n }\n\n /** Registers a factory that builds+hydrates the token source on first need. */\n setAuthProvider(provider: AuthBridgeProvider | null): void {\n this.provider = provider;\n }\n\n private async resolveBridge(): Promise<DispatcherAuthBridge | null> {\n if (this.bridge) return this.bridge;\n if (!this.provider) return null;\n this.bridge = await this.provider();\n return this.bridge;\n }\n\n /**\n * Sends one operation. Injects the session bearer unless told otherwise,\n * and on a stale-token error refreshes once and retries the call exactly\n * once — never looping.\n */\n async dispatch<TResult = unknown, TPayload = unknown>(\n envelope: DispatchEnvelope<TPayload>,\n { bearer, signal }: DispatchOptions = {}\n ): Promise<TResult> {\n const explicit = bearer !== undefined;\n const bridge = explicit ? null : await this.resolveBridge();\n const token = explicit ? bearer : (bridge?.getToken() ?? null);\n\n const extras = { token, signal, options: envelope.options };\n\n try {\n return await request<TResult, TPayload>(\n this.options,\n envelope.operation,\n (envelope.payload ?? {}) as TPayload,\n extras\n );\n } catch (error) {\n const code = error instanceof SinglebaseError ? error.code : \"\";\n const canRetry =\n !explicit && token !== null && bridge !== null && RETRYABLE_AUTH_CODES.has(code);\n if (!canRetry) throw error;\n\n const refreshed = await bridge!.refresh();\n if (!refreshed) throw error;\n\n return request<TResult, TPayload>(\n this.options,\n envelope.operation,\n (envelope.payload ?? {}) as TPayload,\n { ...extras, token: refreshed }\n );\n }\n }\n}\n","import type { SinglebaseStorage } from \"./types.js\";\n\nconst DB_NAME = \"singlebase-auth\";\nconst DB_VERSION = 1;\nconst STORE = \"session\";\nconst KEY_ID = \"wrapping-key\";\nconst DATA_ID = \"session\";\n\n/** In-memory storage. Never persisted — safest, but the session ends on reload. */\nexport function memoryStorage<T = unknown>(): SinglebaseStorage<T> {\n let current: T | null = null;\n return {\n get: () => current,\n set: (session) => {\n current = session;\n },\n clear: () => {\n current = null;\n }\n };\n}\n\nfunction webStorageAdapter<T>(\n getStorage: () => Storage | undefined,\n key: string\n): SinglebaseStorage<T> {\n return {\n get: () => {\n const raw = getStorage()?.getItem(key);\n if (!raw) return null;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return null;\n }\n },\n set: (session) => {\n getStorage()?.setItem(key, JSON.stringify(session));\n },\n clear: () => {\n getStorage()?.removeItem(key);\n }\n };\n}\n\n/**\n * Persists the session in `localStorage` as plain text. Survives reloads and\n * is shared across tabs, but any script on the origin — including an XSS\n * payload — can read the tokens verbatim. Choose this deliberately.\n */\nexport function localStorageAdapter<T = unknown>(\n key = \"singlebase-auth-session\"\n): SinglebaseStorage<T> {\n return webStorageAdapter(() => globalThis.localStorage, key);\n}\n\n/** Persists the session in `sessionStorage` — plain text, cleared with the tab. */\nexport function sessionStorageAdapter<T = unknown>(\n key = \"singlebase-auth-session\"\n): SinglebaseStorage<T> {\n return webStorageAdapter(() => globalThis.sessionStorage, key);\n}\n\n// ---------------------------------------------------------------------------\n// IndexedDB + non-extractable CryptoKey\n// ---------------------------------------------------------------------------\n\nfunction idbRequest<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n}\n\nfunction openDb(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request = globalThis.indexedDB.open(DB_NAME, DB_VERSION);\n request.onupgradeneeded = () => {\n if (!request.result.objectStoreNames.contains(STORE)) {\n request.result.createObjectStore(STORE);\n }\n };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n request.onblocked = () => reject(new Error(\"indexedDB open blocked\"));\n });\n}\n\nasync function idbRead<T>(id: string): Promise<T | undefined> {\n const db = await openDb();\n try {\n const tx = db.transaction(STORE, \"readonly\");\n return await idbRequest<T>(tx.objectStore(STORE).get(id) as IDBRequest<T>);\n } finally {\n db.close();\n }\n}\n\nasync function idbWrite(id: string, value: unknown): Promise<void> {\n const db = await openDb();\n try {\n const tx = db.transaction(STORE, \"readwrite\");\n await idbRequest(tx.objectStore(STORE).put(value, id));\n } finally {\n db.close();\n }\n}\n\nasync function idbDelete(id: string): Promise<void> {\n const db = await openDb();\n try {\n const tx = db.transaction(STORE, \"readwrite\");\n await idbRequest(tx.objectStore(STORE).delete(id));\n } finally {\n db.close();\n }\n}\n\n/** True when this environment can do IndexedDB + WebCrypto (a secure context). */\nexport function canUseCryptoStorage(): boolean {\n return (\n typeof globalThis.indexedDB !== \"undefined\" &&\n typeof globalThis.crypto !== \"undefined\" &&\n typeof globalThis.crypto.subtle !== \"undefined\"\n );\n}\n\ninterface EncryptedRecord {\n // Pinned to ArrayBuffer (not ArrayBufferLike) so it satisfies BufferSource.\n iv: Uint8Array<ArrayBuffer>;\n data: ArrayBuffer;\n}\n\n/**\n * Persists the session in IndexedDB, encrypted with an AES-GCM key that is\n * generated as **non-extractable** and itself stored in IndexedDB.\n *\n * The point is not that IndexedDB is inherently safer than localStorage — for\n * an XSS payload sitting on the page, both are readable. The point is that\n * the key's raw material can never be read by JavaScript at all, so the\n * tokens at rest cannot be exfiltrated and replayed later or from another\n * machine: an attacker has to stay resident on the page to use them. That\n * turns durable token theft into session-scoped abuse.\n */\nexport function indexedDbCryptoStorage<T = unknown>(): SinglebaseStorage<T> {\n let keyPromise: Promise<CryptoKey> | null = null;\n\n async function getKey(): Promise<CryptoKey> {\n if (!keyPromise) {\n keyPromise = (async () => {\n const existing = await idbRead<CryptoKey>(KEY_ID);\n if (existing) return existing;\n const key = await globalThis.crypto.subtle.generateKey(\n { name: \"AES-GCM\", length: 256 },\n // non-extractable: crypto.subtle can use it, JS can never read it\n false,\n [\"encrypt\", \"decrypt\"]\n );\n await idbWrite(KEY_ID, key);\n return key;\n })().catch((error) => {\n keyPromise = null;\n throw error;\n });\n }\n return keyPromise;\n }\n\n return {\n async get() {\n const record = await idbRead<EncryptedRecord>(DATA_ID);\n if (!record) return null;\n try {\n const key = await getKey();\n const plain = await globalThis.crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv: record.iv },\n key,\n record.data\n );\n return JSON.parse(new TextDecoder().decode(plain)) as T;\n } catch {\n // Wrong/rotated key or corrupt record — drop it rather than throw.\n await idbDelete(DATA_ID).catch(() => {});\n return null;\n }\n },\n\n async set(session) {\n const key = await getKey();\n const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));\n const data = await globalThis.crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv },\n key,\n new TextEncoder().encode(JSON.stringify(session))\n );\n await idbWrite(DATA_ID, { iv, data } satisfies EncryptedRecord);\n },\n\n async clear() {\n await idbDelete(DATA_ID).catch(() => {});\n }\n };\n}\n\n/**\n * The default adapter: encrypted IndexedDB when the environment supports it,\n * otherwise `sessionStorage`, otherwise memory. Detection is lazy and each\n * operation degrades on failure, so a blocked/absent IndexedDB (private\n * windows, disabled site data) never breaks sign-in — it just stops the\n * session surviving a reload.\n */\nexport function defaultStorage<T = unknown>(): SinglebaseStorage<T> {\n let backing: SinglebaseStorage<T> | null = null;\n let probed = false;\n\n async function resolve(): Promise<SinglebaseStorage<T>> {\n if (backing) return backing;\n if (!probed) {\n probed = true;\n if (canUseCryptoStorage()) {\n const candidate = indexedDbCryptoStorage<T>();\n try {\n // Round-trip nothing, but force the key + a transaction to open so\n // an unusable IndexedDB is discovered now rather than at signin.\n await candidate.get();\n backing = candidate;\n return backing;\n } catch {\n // fall through\n }\n }\n backing =\n typeof globalThis.sessionStorage !== \"undefined\"\n ? sessionStorageAdapter<T>()\n : memoryStorage<T>();\n }\n return backing ?? memoryStorage<T>();\n }\n\n return {\n async get() {\n try {\n return await (await resolve()).get();\n } catch {\n return null;\n }\n },\n async set(session) {\n try {\n await (await resolve()).set(session);\n } catch {\n // Persisting is best-effort; the in-memory session remains valid.\n }\n },\n async clear() {\n try {\n await (await resolve()).clear();\n } catch {\n // ignore\n }\n }\n };\n}\n","/**\n * Minimal typed pub/sub, generic over a domain's event map. Deliberately not\n * DOM-based: core has no DOM dependency so it runs in tests, workers and\n * non-browser runtimes. UI packages re-dispatch these as CustomEvents.\n */\nexport class EventEmitter<TMap> {\n private listeners = new Map<keyof TMap, Set<(detail: any) => void>>();\n\n on<K extends keyof TMap>(event: K, listener: (detail: TMap[K]) => void): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(listener);\n return () => set!.delete(listener);\n }\n\n emit<K extends keyof TMap>(event: K, detail: TMap[K]): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const listener of set) listener(detail);\n }\n}\n","/**\n * Page-wide configuration for the singlebase-authui elements.\n *\n * It lives in core rather than in the UI package so `SinglebaseClient()` can\n * accept it without the SDK taking a dependency on Lit. The UI package reads\n * it back off the client; nothing here imports or renders anything.\n *\n * Precedence is deliberately simple: **an attribute on the element wins.**\n * This is the page-level default, so one call configures every widget, and any\n * individual widget can still opt out by setting the attribute itself.\n */\nexport interface AuthUIConfig {\n // ── appearance ───────────────────────────────────────────\n theme?: \"light\" | \"dark\";\n density?: \"comfortable\" | \"compact\";\n fieldStyle?: \"outline\" | \"underline\";\n /** Corner preset: scales --sb-radius by 0.35, 1 or 1.8. */\n radius?: \"sharp\" | \"default\" | \"round\";\n\n /** `--sb-*` custom properties, e.g. { \"--sb-accent\": \"#2f5bea\" }. */\n tokens?: Record<string, string>;\n\n // ── layout & branding ────────────────────────────────────\n layout?: \"card\" | \"split\";\n oauthPlacement?: \"top\" | \"bottom\";\n stepped?: boolean;\n logoUrl?: string;\n logoText?: string;\n brandLine?: string;\n brandFoot?: string;\n signInTitle?: string;\n /** The \"Auth by Singlebase\" credit under the card. On unless set false. */\n branding?: boolean;\n /** The credit's text and link. */\n brandingText?: string;\n brandingUrl?: string;\n\n // ── screens & behaviour ──────────────────────────────────\n initialScreen?: string;\n noAccountView?: boolean;\n redirectUrl?: string;\n tosUrl?: string;\n privacyUrl?: string;\n nonceStorageKey?: string;\n\n // ── sign-in methods ──────────────────────────────────────\n authEnabled?: boolean;\n allowEmailSignin?: boolean;\n allowEmailSignup?: boolean;\n allowEmailOtp?: boolean;\n allowOauth?: boolean;\n allowAccountCreation?: boolean;\n\n // ── invite prefill ───────────────────────────────────────\n inviteEmail?: string;\n inviteOrg?: string;\n inviteCode?: string;\n\n /** Overrides for any subset of the default message strings. */\n messages?: Record<string, string>;\n}\n\n/** Config keys that are applied as element properties, not handled specially. */\nexport const AUTHUI_PROP_KEYS = [\n \"theme\",\n \"density\",\n \"fieldStyle\",\n \"radius\",\n \"layout\",\n \"oauthPlacement\",\n \"stepped\",\n \"logoUrl\",\n \"logoText\",\n \"brandLine\",\n \"brandFoot\",\n \"signInTitle\",\n \"branding\",\n \"brandingText\",\n \"brandingUrl\",\n \"initialScreen\",\n \"noAccountView\",\n \"redirectUrl\",\n \"tosUrl\",\n \"privacyUrl\",\n \"nonceStorageKey\",\n \"authEnabled\",\n \"allowEmailSignin\",\n \"allowEmailSignup\",\n \"allowEmailOtp\",\n \"allowOauth\",\n \"allowAccountCreation\",\n \"inviteEmail\",\n \"inviteOrg\",\n \"inviteCode\"\n] as const satisfies readonly (keyof AuthUIConfig)[];\n\n/** \"fieldStyle\" -> \"field-style\", for checking whether the host set it in markup. */\nexport function authUIAttributeFor(key: string): string {\n return key.replace(/[A-Z]/g, (c) => \"-\" + c.toLowerCase());\n}\n","/**\n * A deliberately small MongoDB-flavoured matcher.\n *\n * It exists so a guard can say \"render this only for an admin in the EU\"\n * declaratively, against the profile carried by the session. The scope is\n * fixed on purpose:\n *\n * - one level of operators, never nested query objects;\n * - dot notation is the *only* way to reach into a nested value;\n * - a fixed operator set, listed in OPERATORS below.\n *\n * Anything more expressive belongs in application code, not in an attribute.\n * Keeping it this small means a predicate is always cheap, always total, and\n * can never become a way to smuggle logic into markup.\n */\n\nexport const OPERATORS = [\n \"$eq\",\n \"$ne\",\n \"$in\",\n \"$nin\",\n \"$gt\",\n \"$gte\",\n \"$lt\",\n \"$lte\",\n \"$elemMatch\"\n] as const;\n\nexport type Operator = (typeof OPERATORS)[number];\n\n/** The right-hand side of one field: a bare value (implicit $eq) or an operator bag. */\nexport type Condition = unknown | Partial<Record<Operator, unknown>>;\n\n/** `{ \"metadata.tier\": { $in: [\"pro\", \"team\"] }, status: \"active\" }` */\nexport type Predicate = Record<string, Condition>;\n\nconst OPERATOR_SET = new Set<string>(OPERATORS);\n\nfunction isOperatorBag(value: unknown): value is Partial<Record<Operator, unknown>> {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) return false;\n const keys = Object.keys(value as object);\n return keys.length > 0 && keys.every((key) => OPERATOR_SET.has(key));\n}\n\n/**\n * Reads `a.b.c` off an object. Returns undefined for any missing link rather\n * than throwing — an absent field simply fails to match.\n */\nexport function getPath(source: unknown, path: string): unknown {\n let current = source;\n for (const segment of path.split(\".\")) {\n if (current === null || current === undefined) return undefined;\n if (typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[segment];\n }\n return current;\n}\n\n/** Loose structural equality, enough for the scalars and arrays a profile holds. */\nfunction equals(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((item, i) => equals(item, b[i]));\n }\n if (a && b && typeof a === \"object\" && typeof b === \"object\") {\n const aKeys = Object.keys(a as object);\n const bKeys = Object.keys(b as object);\n return (\n aKeys.length === bKeys.length &&\n aKeys.every((key) =>\n equals((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])\n )\n );\n }\n return false;\n}\n\n/**\n * Ordered comparison for $gt/$gte/$lt/$lte. Only like-typed numbers, strings\n * and dates compare; anything else is \"not comparable\", which fails the test\n * rather than coercing its way to a surprising answer.\n */\nfunction compare(left: unknown, right: unknown): number | null {\n if (typeof left === \"number\" && typeof right === \"number\") {\n return Number.isNaN(left) || Number.isNaN(right) ? null : left - right;\n }\n if (typeof left === \"string\" && typeof right === \"string\") {\n return left < right ? -1 : left > right ? 1 : 0;\n }\n if (left instanceof Date && right instanceof Date) {\n return left.getTime() - right.getTime();\n }\n return null;\n}\n\nfunction matchOperator(operator: string, actual: unknown, expected: unknown): boolean {\n switch (operator) {\n case \"$eq\":\n return equals(actual, expected);\n case \"$ne\":\n return !equals(actual, expected);\n case \"$in\":\n if (!Array.isArray(expected)) return false;\n // Mongo semantics: when the field is an array, membership is an\n // intersection test rather than whole-array equality.\n return Array.isArray(actual)\n ? actual.some((item) => expected.some((candidate) => equals(item, candidate)))\n : expected.some((candidate) => equals(actual, candidate));\n case \"$nin\":\n if (!Array.isArray(expected)) return false;\n return !matchOperator(\"$in\", actual, expected);\n case \"$gt\":\n case \"$gte\":\n case \"$lt\":\n case \"$lte\": {\n const order = compare(actual, expected);\n if (order === null) return false;\n if (operator === \"$gt\") return order > 0;\n if (operator === \"$gte\") return order >= 0;\n if (operator === \"$lt\") return order < 0;\n return order <= 0;\n }\n case \"$elemMatch\": {\n if (!Array.isArray(actual)) return false;\n // One level only: the argument is a condition applied to each element,\n // not a nested query object.\n return actual.some((item) => matchCondition(item, expected));\n }\n default:\n return false;\n }\n}\n\nfunction matchCondition(actual: unknown, condition: Condition): boolean {\n if (isOperatorBag(condition)) {\n // Several operators on one field are ANDed, as in Mongo.\n return Object.entries(condition).every(([operator, expected]) =>\n matchOperator(operator, actual, expected)\n );\n }\n return equals(actual, condition);\n}\n\n/**\n * True when `subject` satisfies every field in `predicate`. An empty or\n * missing predicate matches everything, so a guard with no filter behaves\n * exactly like one that was never given a filter.\n */\nexport function matchesPredicate(\n subject: unknown,\n predicate: Predicate | null | undefined\n): boolean {\n if (!predicate) return true;\n const entries = Object.entries(predicate);\n if (entries.length === 0) return true;\n if (subject === null || subject === undefined) return false;\n\n return entries.every(([path, condition]) => matchCondition(getPath(subject, path), condition));\n}\n\n/**\n * Reports what is wrong with a predicate, for a console warning at the point\n * of use. Returns [] when it is well-formed. Catching `{ $regex: ... }` or a\n * nested query object early is far kinder than silently never matching.\n */\nexport function validatePredicate(predicate: unknown): string[] {\n if (predicate === null || predicate === undefined) return [];\n if (typeof predicate !== \"object\" || Array.isArray(predicate)) {\n return [\"predicate must be an object\"];\n }\n\n const problems: string[] = [];\n for (const [path, condition] of Object.entries(predicate as Record<string, unknown>)) {\n if (path.startsWith(\"$\")) {\n problems.push(`\"${path}\": top-level operators are not supported; use a field name`);\n continue;\n }\n if (condition === null || typeof condition !== \"object\" || Array.isArray(condition)) continue;\n\n const keys = Object.keys(condition as object);\n const operators = keys.filter((key) => key.startsWith(\"$\"));\n\n if (operators.length === 0) {\n problems.push(\n `\"${path}\": nested objects are not supported; use dot notation, e.g. \"${path}.field\"`\n );\n continue;\n }\n if (operators.length !== keys.length) {\n problems.push(`\"${path}\": cannot mix operators with plain keys`);\n }\n for (const operator of operators) {\n if (!OPERATOR_SET.has(operator)) {\n problems.push(`\"${path}\": unsupported operator \"${operator}\"`);\n }\n }\n }\n return problems;\n}\n","/**\n * Cross-tab coordination.\n *\n * When a user signs in or out in one tab, every other tab of the same origin\n * should follow. The signal deliberately carries *no session data*: peers are\n * only told \"something changed\", and they re-read the shared storage\n * themselves. That keeps tokens out of BroadcastChannel payloads and out of\n * the localStorage fallback key entirely, so the encrypted-at-rest guarantee\n * in storage.ts is never bypassed by the sync mechanism.\n *\n * Note this only *works* when the underlying storage is itself shared between\n * tabs (IndexedDB is; sessionStorage is not). With the sessionStorage\n * fallback the peer re-reads its own private copy, finds it unchanged, and\n * does nothing — degraded, but never wrong.\n */\n\nexport type CrossTabSignal = \"session-changed\" | \"signout\";\n\nexport interface CrossTabMessage {\n signal: CrossTabSignal;\n /** Sender id, so a channel can ignore its own echo. */\n from: string;\n at: number;\n}\n\nexport interface CrossTabChannel {\n post(signal: CrossTabSignal): void;\n subscribe(listener: (message: CrossTabMessage) => void): () => void;\n close(): void;\n}\n\nconst noop = () => {};\n\n/** A channel that does nothing — used when cross-tab sync is disabled or unavailable. */\nexport function noopCrossTabChannel(): CrossTabChannel {\n return { post: noop, subscribe: () => noop, close: noop };\n}\n\nfunction newId(): string {\n const c = globalThis.crypto;\n if (c && \"randomUUID\" in c) return c.randomUUID();\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n}\n\n/**\n * Prefers BroadcastChannel; falls back to a localStorage `storage` event,\n * which fires only in *other* tabs, exactly the semantics we want. Returns a\n * no-op channel when neither is available (SSR, tests, locked-down origins).\n */\nexport function crossTabChannel(name: string): CrossTabChannel {\n const id = newId();\n\n const BC = (globalThis as { BroadcastChannel?: typeof BroadcastChannel }).BroadcastChannel;\n if (BC) {\n let channel: BroadcastChannel;\n try {\n channel = new BC(name);\n } catch {\n return storageEventChannel(name, id);\n }\n // In Node an open channel keeps the process alive; tabs don't need that.\n (channel as { unref?: () => void }).unref?.();\n return {\n post(signal) {\n try {\n channel.postMessage({ signal, from: id, at: Date.now() } satisfies CrossTabMessage);\n } catch {\n // A closed or blocked channel must never break the auth flow.\n }\n },\n subscribe(listener) {\n const handler = (event: MessageEvent) => {\n const message = event.data as CrossTabMessage | null;\n if (!message || typeof message !== \"object\") return;\n if (message.from === id) return;\n if (message.signal !== \"session-changed\" && message.signal !== \"signout\") return;\n listener(message);\n };\n channel.addEventListener(\"message\", handler);\n return () => channel.removeEventListener(\"message\", handler);\n },\n close() {\n try {\n channel.close();\n } catch {\n // already closed\n }\n }\n };\n }\n\n return storageEventChannel(name, id);\n}\n\nfunction storageEventChannel(name: string, id: string): CrossTabChannel {\n const store = safeLocalStorage();\n if (!store) return noopCrossTabChannel();\n\n const key = `${name}:signal`;\n\n return {\n post(signal) {\n try {\n store.setItem(key, JSON.stringify({ signal, from: id, at: Date.now() }));\n } catch {\n // Quota or private-mode failures are non-fatal.\n }\n },\n subscribe(listener) {\n const handler = (event: StorageEvent) => {\n if (event.key !== key || !event.newValue) return;\n try {\n const message = JSON.parse(event.newValue) as CrossTabMessage;\n if (message.from === id) return;\n if (message.signal !== \"session-changed\" && message.signal !== \"signout\") return;\n listener(message);\n } catch {\n // Ignore anything that is not one of our messages.\n }\n };\n globalThis.addEventListener?.(\"storage\", handler as EventListener);\n return () => globalThis.removeEventListener?.(\"storage\", handler as EventListener);\n },\n close: noop\n };\n}\n\nfunction safeLocalStorage(): Storage | null {\n try {\n const store = globalThis.localStorage;\n if (!store) return null;\n const probe = \"__singlebase_probe__\";\n store.setItem(probe, \"1\");\n store.removeItem(probe);\n return store;\n } catch {\n return null;\n }\n}\n","import type { SinglebaseOptions } from \"./types.js\";\nimport { DEFAULT_BASE_URL } from \"./transport.js\";\n\n/**\n * Client registry.\n *\n * A page can hold several widgets — a header sign-in, a modal, an account\n * panel — and they must all share one client: one session, one refresh\n * timer, one set of tokens. A client per widget would mean several session\n * stores and several refresh loops racing for the single-use refresh token.\n *\n * The registry lives on `globalThis` under a well-known symbol rather than in\n * module scope, because bundles duplicate modules: a page that loads the\n * widget bundle *and* imports the SDK has two copies of this file, and a\n * module-scoped `let` would give each its own \"default\" — so widgets would\n * silently never see the client the app created. Keying off globalThis makes\n * every copy, and every independently loaded script, agree.\n */\nconst REGISTRY = Symbol.for(\"singlebase.client.registry\");\n\ninterface Registry<T> {\n instances: Map<string, T>;\n default: T | null;\n}\n\nfunction registry<T>(): Registry<T> {\n const host = globalThis as unknown as Record<symbol, Registry<T> | undefined>;\n let existing = host[REGISTRY];\n if (!existing) {\n existing = { instances: new Map(), default: null };\n host[REGISTRY] = existing;\n }\n return existing;\n}\n\n/** Connection identity — clients with the same one are the same instance. */\nexport function clientKey(options: SinglebaseOptions): string {\n return [\n options.baseUrl ?? DEFAULT_BASE_URL,\n options.urlAccessKey ?? \"\",\n options.apiKey ?? \"\",\n options.audience ?? \"web\"\n ].join(\"|\");\n}\n\n/** Returns the cached client for these options, or creates and caches one. */\nexport function resolveClient<T>(options: SinglebaseOptions, create: () => T): T {\n const reg = registry<T>();\n const key = clientKey(options);\n const existing = reg.instances.get(key);\n if (existing) return existing;\n\n const client = create();\n reg.instances.set(key, client);\n if (!reg.default) reg.default = client;\n return client;\n}\n\n/** The client widgets fall back to when none is set on them. */\nexport function getDefaultClient<T = unknown>(): T | null {\n return registry<T>().default;\n}\n\n/** Overrides the page default (useful when several projects are in play). */\nexport function setDefaultClient<T>(client: T | null): void {\n registry<T>().default = client;\n}\n\n/** Drops a client from the registry. */\nexport function unregisterClient<T>(client: T): void {\n const reg = registry<T>();\n for (const [key, value] of reg.instances) {\n if (value === client) reg.instances.delete(key);\n }\n if (reg.default === client) {\n reg.default = reg.instances.values().next().value ?? null;\n }\n}\n"],"names":["SinglebaseError","payload","cause","ERROR_HINTS","mapErrorToHint","code","isLocalHost","hostname","DEFAULT_BASE_URL","resolveBaseUrl","options","endpointFor","base","assertSecureBaseUrl","key","_a","baseUrl","url","request","clientOptions","operation","extras","envelope","headers","fetchImpl","res","json","RETRYABLE_AUTH_CODES","RpcDispatcher","bridge","provider","bearer","signal","explicit","token","error","refreshed","DB_NAME","DB_VERSION","STORE","KEY_ID","DATA_ID","memoryStorage","current","session","webStorageAdapter","getStorage","raw","localStorageAdapter","sessionStorageAdapter","idbRequest","resolve","reject","openDb","idbRead","id","db","tx","idbWrite","value","idbDelete","canUseCryptoStorage","indexedDbCryptoStorage","keyPromise","getKey","existing","record","plain","iv","data","defaultStorage","backing","probed","candidate","EventEmitter","event","listener","set","detail","AUTHUI_PROP_KEYS","authUIAttributeFor","c","OPERATORS","OPERATOR_SET","isOperatorBag","keys","getPath","source","path","segment","equals","a","b","item","i","aKeys","bKeys","compare","left","right","matchOperator","operator","actual","expected","order","matchCondition","condition","matchesPredicate","subject","predicate","entries","validatePredicate","problems","operators","noop","noopCrossTabChannel","newId","crossTabChannel","name","BC","channel","storageEventChannel","handler","message","store","safeLocalStorage","probe","REGISTRY","registry","host","clientKey","resolveClient","create","reg","client","getDefaultClient","setDefaultClient","unregisterClient"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/errors.ts","../src/transport.ts","../src/dispatcher.ts","../src/storage.ts","../src/events.ts","../src/authui-config.ts","../src/predicate.ts","../src/cross-tab.ts","../src/registry.ts"],"sourcesContent":["import type { ErrorResponsePayload } from \"./types.js\";\n\n/**\n * Normalized error thrown by every SDK method. `code` is the server's\n * machine-readable `error.message` (e.g. \"INVALID_CREDENTIALS\"), never the\n * human-readable text.\n */\nexport class SinglebaseError extends Error {\n status: number;\n type: string;\n code: string;\n details?: unknown;\n traceId?: string | null;\n override cause?: unknown;\n\n constructor(payload: ErrorResponsePayload, cause?: unknown) {\n super(payload.message);\n this.name = \"SinglebaseError\";\n this.status = payload.status;\n this.type = payload.type;\n this.code = payload.message;\n this.details = payload.details;\n this.traceId = payload.trace_id ?? null;\n this.cause = cause;\n }\n\n static fromNetworkError(cause: unknown): SinglebaseError {\n return new SinglebaseError(\n {\n type: \"NETWORK_ERROR\",\n status: 0,\n message: \"NETWORK_ERROR\"\n },\n cause\n );\n }\n}\n\n/**\n * Recommended UI behavior for a given server error code, taken verbatim from\n * the auth API's error-mapping table. UI components use this to decide\n * *behavior* (focus a field, trigger a refresh, sign out locally); the actual\n * copy shown to the user is left to each component/its `.messages` override.\n */\nexport type ErrorHint =\n | \"highlight_invalid_fields\"\n | \"generic_credentials_message\"\n | \"reveal_code_input\"\n | \"expired_or_invalid_code_message\"\n | \"attempt_refresh_or_signout\"\n | \"clear_session_and_ask_signin\"\n | \"clear_session_config_error\"\n | \"route_to_recovery_or_invite\"\n | \"show_password_requirements\"\n | \"require_changed_field\"\n | \"disable_retry_show_rate_limited\"\n | \"render_auth_unavailable\"\n | \"hide_email_forms\"\n | \"hide_signup\"\n | \"hide_oauth\"\n | \"remove_oauth_from_signin\"\n | \"remove_oauth_from_signup\"\n | \"refresh_settings_hide_provider\"\n | \"restart_oauth_new_nonce\"\n | \"restart_oauth\"\n | \"explain_verified_email_required\"\n | \"ask_signin_then_link\"\n | \"provider_already_linked\"\n | \"route_to_signin\"\n | \"show_integrator_config_error\"\n | \"unknown\";\n\nconst ERROR_HINTS: Record<string, ErrorHint> = {\n INVALID_PAYLOAD: \"highlight_invalid_fields\",\n INVALID_CREDENTIALS: \"generic_credentials_message\",\n CODE_REQUIRED: \"reveal_code_input\",\n INVALID_TOKEN: \"expired_or_invalid_code_message\",\n INVALID_ID_TOKEN: \"attempt_refresh_or_signout\",\n INVALID_BEARER_TOKEN: \"attempt_refresh_or_signout\",\n INVALID_SESSION: \"clear_session_and_ask_signin\",\n INVALID_TOKEN_AUDIENCE: \"clear_session_config_error\",\n REQUIRE_PASSWORD_CHANGE: \"route_to_recovery_or_invite\",\n INVALID_PASSWORD: \"show_password_requirements\",\n MISSING_PROFILE_DATA: \"require_changed_field\",\n AUTH_RATE_LIMITED: \"disable_retry_show_rate_limited\",\n AUTH_DISABLED: \"render_auth_unavailable\",\n EMAIL_PROVIDER_DISABLED: \"hide_email_forms\",\n EMAIL_SIGNUP_DISABLED: \"hide_signup\",\n OAUTH_DISABLED: \"hide_oauth\",\n OAUTH_SIGNIN_DISABLED: \"remove_oauth_from_signin\",\n OAUTH_SIGNUP_DISABLED: \"remove_oauth_from_signup\",\n INVALID_OAUTH_PROVIDER: \"refresh_settings_hide_provider\",\n MISSING_OAUTH_CREDENTIALS: \"show_integrator_config_error\",\n OAUTH_VERIFICATION_FAILED: \"restart_oauth\",\n VERIFIED_PROVIDER_EMAIL_REQUIRED: \"explain_verified_email_required\",\n INVALID_NONCE: \"restart_oauth_new_nonce\",\n SIGN_IN_TO_LINK_PROVIDER: \"ask_signin_then_link\",\n PROVIDER_ALREADY_LINKED: \"provider_already_linked\",\n AUTHENTICATION_REQUIRED: \"route_to_signin\",\n UNAUTHORIZED_DOMAIN: \"show_integrator_config_error\"\n};\n\nexport function mapErrorToHint(code: string): ErrorHint {\n return ERROR_HINTS[code] ?? \"unknown\";\n}\n\n/** Back-compat alias: the error type is shared by every service. */\nexport { SinglebaseError as SinglebaseAuthError };\n","import { SinglebaseError } from \"./errors.js\";\nimport type {\n SinglebaseOptions,\n ErrorResponse,\n RequestEnvelope,\n SuccessResponse\n} from \"./types.js\";\n\nexport interface RequestExtras {\n token?: string | null;\n signal?: AbortSignal;\n options?: Record<string, unknown>;\n}\n\nfunction isLocalHost(hostname: string): boolean {\n return hostname === \"localhost\" || hostname === \"127.0.0.1\" || hostname === \"0.0.0.0\";\n}\n\n/** Where requests go when `baseUrl` is not given. */\nexport const DEFAULT_BASE_URL = \"https://v1.api.singlebase.io\";\n\n/**\n * The effective API root: the default when `baseUrl` is omitted. An explicit\n * null or empty value is a configuration mistake, and is reported as one\n * rather than silently replaced.\n */\nexport function resolveBaseUrl(options: Pick<SinglebaseOptions, \"baseUrl\">): string {\n if (options.baseUrl === undefined) return DEFAULT_BASE_URL;\n if (options.baseUrl === null || String(options.baseUrl).trim() === \"\") {\n throw new Error(\n `SinglebaseOptions.baseUrl cannot be null or empty. Omit it to use ${DEFAULT_BASE_URL}.`\n );\n }\n return String(options.baseUrl).trim().replace(/\\/+$/, \"\");\n}\n\n/**\n * The URL every request is POSTed to: `{baseUrl}/{urlAccessKey}`, or just\n * `{baseUrl}` when there is no access key.\n */\nexport function endpointFor(options: Pick<SinglebaseOptions, \"baseUrl\" | \"urlAccessKey\">): string {\n const base = resolveBaseUrl(options);\n assertSecureBaseUrl(base);\n const key = options.urlAccessKey?.trim();\n return key ? `${base}/${encodeURIComponent(key)}` : base;\n}\n\n/** Enforces the spec's HTTPS-outside-localhost rule for the configured baseUrl. */\nexport function assertSecureBaseUrl(baseUrl: string): void {\n let url: URL;\n try {\n url = new URL(baseUrl);\n } catch {\n throw new Error(`SinglebaseOptions.baseUrl is not a valid URL: ${baseUrl}`);\n }\n if (url.protocol !== \"https:\" && !isLocalHost(url.hostname)) {\n throw new Error(\n `SinglebaseOptions.baseUrl must use HTTPS outside of localhost/127.0.0.1/0.0.0.0 (got ${url.protocol})`\n );\n }\n}\n\n/**\n * Low-level RPC request. Returns the parsed `data` on success and throws\n * `SinglebaseError` on a well-formed error response or a network/transport\n * failure. Never leaks tokens into thrown error objects, logs, or URLs.\n */\nexport async function request<TData = unknown, TPayload = unknown>(\n clientOptions: SinglebaseOptions,\n operation: string,\n payload: TPayload,\n extras: RequestExtras = {}\n): Promise<TData> {\n const url = endpointFor(clientOptions);\n\n const envelope: RequestEnvelope<TPayload> = { operation, payload };\n if (extras.options) envelope.options = extras.options;\n\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (clientOptions.apiKey) headers[\"X-API-Key\"] = clientOptions.apiKey;\n if (extras.token) {\n headers.Authorization = `Bearer ${extras.token}`;\n }\n\n const fetchImpl = clientOptions.fetch ?? globalThis.fetch;\n\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(envelope),\n signal: extras.signal\n });\n } catch (cause) {\n throw SinglebaseError.fromNetworkError(cause);\n }\n\n let json: SuccessResponse<TData> | ErrorResponse;\n try {\n json = await res.json();\n } catch (cause) {\n throw SinglebaseError.fromNetworkError(cause);\n }\n\n if (\"error\" in json) {\n throw new SinglebaseError(json.error);\n }\n return json.data;\n}\n","import { SinglebaseError } from \"./errors.js\";\nimport { request } from \"./transport.js\";\nimport type { SinglebaseOptions } from \"./types.js\";\n\n/** Server error codes that mean \"your bearer token is stale, try refreshing\". */\nconst RETRYABLE_AUTH_CODES = new Set([\"INVALID_ID_TOKEN\", \"INVALID_BEARER_TOKEN\"]);\n\nexport interface DispatchEnvelope<TPayload = unknown> {\n operation: string;\n payload?: TPayload;\n options?: Record<string, unknown>;\n}\n\nexport interface DispatchOptions {\n /**\n * Explicit bearer token. Omit to let the dispatcher inject the current\n * session's ID token; pass `null` to force an unauthenticated call.\n */\n bearer?: string | null;\n signal?: AbortSignal;\n}\n\nexport interface DispatcherAuthBridge {\n /** Current ID token, or null when signed out. */\n getToken(): string | null;\n /** Refresh once and return the new ID token, or null if unrecoverable. */\n refresh(): Promise<string | null>;\n}\n\n/**\n * Supplies the auth bridge on demand. Returning a promise lets auth be\n * *constructed lazily* — a page that never signs in pays nothing — while\n * still being *hydrated eagerly* before the first call that could carry a\n * token. Without this, a `data.query()` fired on page load would race the\n * session restore and silently go out unauthenticated.\n */\nexport type AuthBridgeProvider = () => Promise<DispatcherAuthBridge | null>;\n\n/**\n * One RPC pipe shared by every Singlebase service.\n *\n * Auth and every service client go through this, so there is a single place\n * that knows the envelope shape, the endpoint, how a bearer is attached, and\n * how a stale token is recovered.\n */\nexport class RpcDispatcher {\n private bridge: DispatcherAuthBridge | null = null;\n private provider: AuthBridgeProvider | null = null;\n\n constructor(readonly options: SinglebaseOptions) {}\n\n /** Registers the session owner (the auth client) as the token source. */\n setAuthBridge(bridge: DispatcherAuthBridge | null): void {\n this.bridge = bridge;\n }\n\n /** Registers a factory that builds+hydrates the token source on first need. */\n setAuthProvider(provider: AuthBridgeProvider | null): void {\n this.provider = provider;\n }\n\n private async resolveBridge(): Promise<DispatcherAuthBridge | null> {\n if (this.bridge) return this.bridge;\n if (!this.provider) return null;\n this.bridge = await this.provider();\n return this.bridge;\n }\n\n /**\n * Sends one operation. Injects the session bearer unless told otherwise,\n * and on a stale-token error refreshes once and retries the call exactly\n * once — never looping.\n */\n async dispatch<TResult = unknown, TPayload = unknown>(\n envelope: DispatchEnvelope<TPayload>,\n { bearer, signal }: DispatchOptions = {}\n ): Promise<TResult> {\n const explicit = bearer !== undefined;\n const bridge = explicit ? null : await this.resolveBridge();\n const token = explicit ? bearer : (bridge?.getToken() ?? null);\n\n const extras = { token, signal, options: envelope.options };\n\n try {\n return await request<TResult, TPayload>(\n this.options,\n envelope.operation,\n (envelope.payload ?? {}) as TPayload,\n extras\n );\n } catch (error) {\n const code = error instanceof SinglebaseError ? error.code : \"\";\n const canRetry =\n !explicit && token !== null && bridge !== null && RETRYABLE_AUTH_CODES.has(code);\n if (!canRetry) throw error;\n\n const refreshed = await bridge!.refresh();\n if (!refreshed) throw error;\n\n return request<TResult, TPayload>(\n this.options,\n envelope.operation,\n (envelope.payload ?? {}) as TPayload,\n { ...extras, token: refreshed }\n );\n }\n }\n}\n","import type { SinglebaseStorage } from \"./types.js\";\n\nconst DB_NAME = \"singlebase-auth\";\nconst DB_VERSION = 1;\nconst STORE = \"session\";\nconst KEY_ID = \"wrapping-key\";\nconst DATA_ID = \"session\";\n\n/** In-memory storage. Never persisted — safest, but the session ends on reload. */\nexport function memoryStorage<T = unknown>(): SinglebaseStorage<T> {\n let current: T | null = null;\n return {\n get: () => current,\n set: (session) => {\n current = session;\n },\n clear: () => {\n current = null;\n }\n };\n}\n\nfunction webStorageAdapter<T>(\n getStorage: () => Storage | undefined,\n key: string\n): SinglebaseStorage<T> {\n return {\n get: () => {\n const raw = getStorage()?.getItem(key);\n if (!raw) return null;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return null;\n }\n },\n set: (session) => {\n getStorage()?.setItem(key, JSON.stringify(session));\n },\n clear: () => {\n getStorage()?.removeItem(key);\n }\n };\n}\n\n/**\n * Persists the session in `localStorage` as plain text. Survives reloads and\n * is shared across tabs, but any script on the origin — including an XSS\n * payload — can read the tokens verbatim. Choose this deliberately.\n */\nexport function localStorageAdapter<T = unknown>(\n key = \"singlebase-auth-session\"\n): SinglebaseStorage<T> {\n return webStorageAdapter(() => globalThis.localStorage, key);\n}\n\n/** Persists the session in `sessionStorage` — plain text, cleared with the tab. */\nexport function sessionStorageAdapter<T = unknown>(\n key = \"singlebase-auth-session\"\n): SinglebaseStorage<T> {\n return webStorageAdapter(() => globalThis.sessionStorage, key);\n}\n\n// ---------------------------------------------------------------------------\n// IndexedDB + non-extractable CryptoKey\n// ---------------------------------------------------------------------------\n\nfunction idbRequest<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n}\n\nfunction openDb(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request = globalThis.indexedDB.open(DB_NAME, DB_VERSION);\n request.onupgradeneeded = () => {\n if (!request.result.objectStoreNames.contains(STORE)) {\n request.result.createObjectStore(STORE);\n }\n };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n request.onblocked = () => reject(new Error(\"indexedDB open blocked\"));\n });\n}\n\nasync function idbRead<T>(id: string): Promise<T | undefined> {\n const db = await openDb();\n try {\n const tx = db.transaction(STORE, \"readonly\");\n return await idbRequest<T>(tx.objectStore(STORE).get(id) as IDBRequest<T>);\n } finally {\n db.close();\n }\n}\n\nasync function idbWrite(id: string, value: unknown): Promise<void> {\n const db = await openDb();\n try {\n const tx = db.transaction(STORE, \"readwrite\");\n await idbRequest(tx.objectStore(STORE).put(value, id));\n } finally {\n db.close();\n }\n}\n\nasync function idbDelete(id: string): Promise<void> {\n const db = await openDb();\n try {\n const tx = db.transaction(STORE, \"readwrite\");\n await idbRequest(tx.objectStore(STORE).delete(id));\n } finally {\n db.close();\n }\n}\n\n/** True when this environment can do IndexedDB + WebCrypto (a secure context). */\nexport function canUseCryptoStorage(): boolean {\n return (\n typeof globalThis.indexedDB !== \"undefined\" &&\n typeof globalThis.crypto !== \"undefined\" &&\n typeof globalThis.crypto.subtle !== \"undefined\"\n );\n}\n\ninterface EncryptedRecord {\n // Pinned to ArrayBuffer (not ArrayBufferLike) so it satisfies BufferSource.\n iv: Uint8Array<ArrayBuffer>;\n data: ArrayBuffer;\n}\n\n/**\n * Persists the session in IndexedDB, encrypted with an AES-GCM key that is\n * generated as **non-extractable** and itself stored in IndexedDB.\n *\n * The point is not that IndexedDB is inherently safer than localStorage — for\n * an XSS payload sitting on the page, both are readable. The point is that\n * the key's raw material can never be read by JavaScript at all, so the\n * tokens at rest cannot be exfiltrated and replayed later or from another\n * machine: an attacker has to stay resident on the page to use them. That\n * turns durable token theft into session-scoped abuse.\n */\nexport function indexedDbCryptoStorage<T = unknown>(): SinglebaseStorage<T> {\n let keyPromise: Promise<CryptoKey> | null = null;\n\n async function getKey(): Promise<CryptoKey> {\n if (!keyPromise) {\n keyPromise = (async () => {\n const existing = await idbRead<CryptoKey>(KEY_ID);\n if (existing) return existing;\n const key = await globalThis.crypto.subtle.generateKey(\n { name: \"AES-GCM\", length: 256 },\n // non-extractable: crypto.subtle can use it, JS can never read it\n false,\n [\"encrypt\", \"decrypt\"]\n );\n await idbWrite(KEY_ID, key);\n return key;\n })().catch((error) => {\n keyPromise = null;\n throw error;\n });\n }\n return keyPromise;\n }\n\n return {\n async get() {\n const record = await idbRead<EncryptedRecord>(DATA_ID);\n if (!record) return null;\n try {\n const key = await getKey();\n const plain = await globalThis.crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv: record.iv },\n key,\n record.data\n );\n return JSON.parse(new TextDecoder().decode(plain)) as T;\n } catch {\n // Wrong/rotated key or corrupt record — drop it rather than throw.\n await idbDelete(DATA_ID).catch(() => {});\n return null;\n }\n },\n\n async set(session) {\n const key = await getKey();\n const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));\n const data = await globalThis.crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv },\n key,\n new TextEncoder().encode(JSON.stringify(session))\n );\n await idbWrite(DATA_ID, { iv, data } satisfies EncryptedRecord);\n },\n\n async clear() {\n await idbDelete(DATA_ID).catch(() => {});\n }\n };\n}\n\n/**\n * The default adapter: encrypted IndexedDB when the environment supports it,\n * otherwise `sessionStorage`, otherwise memory. Detection is lazy and each\n * operation degrades on failure, so a blocked/absent IndexedDB (private\n * windows, disabled site data) never breaks sign-in — it just stops the\n * session surviving a reload.\n */\nexport function defaultStorage<T = unknown>(): SinglebaseStorage<T> {\n let backing: SinglebaseStorage<T> | null = null;\n let probed = false;\n\n async function resolve(): Promise<SinglebaseStorage<T>> {\n if (backing) return backing;\n if (!probed) {\n probed = true;\n if (canUseCryptoStorage()) {\n const candidate = indexedDbCryptoStorage<T>();\n try {\n // Round-trip nothing, but force the key + a transaction to open so\n // an unusable IndexedDB is discovered now rather than at signin.\n await candidate.get();\n backing = candidate;\n return backing;\n } catch {\n // fall through\n }\n }\n backing =\n typeof globalThis.sessionStorage !== \"undefined\"\n ? sessionStorageAdapter<T>()\n : memoryStorage<T>();\n }\n return backing ?? memoryStorage<T>();\n }\n\n return {\n async get() {\n try {\n return await (await resolve()).get();\n } catch {\n return null;\n }\n },\n async set(session) {\n try {\n await (await resolve()).set(session);\n } catch {\n // Persisting is best-effort; the in-memory session remains valid.\n }\n },\n async clear() {\n try {\n await (await resolve()).clear();\n } catch {\n // ignore\n }\n }\n };\n}\n","/**\n * Minimal typed pub/sub, generic over a domain's event map. Deliberately not\n * DOM-based: core has no DOM dependency so it runs in tests, workers and\n * non-browser runtimes. UI packages re-dispatch these as CustomEvents.\n */\nexport class EventEmitter<TMap> {\n private listeners = new Map<keyof TMap, Set<(detail: any) => void>>();\n\n on<K extends keyof TMap>(event: K, listener: (detail: TMap[K]) => void): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(listener);\n return () => set!.delete(listener);\n }\n\n emit<K extends keyof TMap>(event: K, detail: TMap[K]): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const listener of set) listener(detail);\n }\n}\n","/**\n * Page-wide configuration for the singlebase-authui elements.\n *\n * It lives in core rather than in the UI package so `SinglebaseClient()` can\n * accept it without the SDK taking a dependency on Lit. The UI package reads\n * it back off the client; nothing here imports or renders anything.\n *\n * Precedence is deliberately simple: **an attribute on the element wins.**\n * This is the page-level default, so one call configures every widget, and any\n * individual widget can still opt out by setting the attribute itself.\n */\nexport interface AuthUIConfig {\n // ── appearance ───────────────────────────────────────────\n theme?: \"light\" | \"dark\";\n density?: \"comfortable\" | \"compact\";\n fieldStyle?: \"outline\" | \"underline\";\n /** Corner preset: scales --sb-radius by 0.35, 1 or 1.8. */\n radius?: \"sharp\" | \"default\" | \"round\";\n\n /** `--sb-*` custom properties, e.g. { \"--sb-accent\": \"#2f5bea\" }. */\n tokens?: Record<string, string>;\n\n // ── layout & branding ────────────────────────────────────\n layout?: \"card\" | \"split\";\n oauthPlacement?: \"top\" | \"bottom\";\n stepped?: boolean;\n logoUrl?: string;\n logoText?: string;\n brandLine?: string;\n brandFoot?: string;\n signInTitle?: string;\n /** The \"Auth by Singlebase\" credit under the card. On unless set false. */\n branding?: boolean;\n\n // ── screens & behaviour ──────────────────────────────────\n initialScreen?: string;\n noAccountView?: boolean;\n redirectUrl?: string;\n tosUrl?: string;\n privacyUrl?: string;\n nonceStorageKey?: string;\n\n // ── sign-in methods ──────────────────────────────────────\n authEnabled?: boolean;\n allowEmailSignin?: boolean;\n allowEmailSignup?: boolean;\n allowEmailOtp?: boolean;\n allowOauth?: boolean;\n allowAccountCreation?: boolean;\n\n // ── invite prefill ───────────────────────────────────────\n inviteEmail?: string;\n inviteOrg?: string;\n inviteCode?: string;\n\n /** Overrides for any subset of the default message strings. */\n messages?: Record<string, string>;\n}\n\n/** Config keys that are applied as element properties, not handled specially. */\nexport const AUTHUI_PROP_KEYS = [\n \"theme\",\n \"density\",\n \"fieldStyle\",\n \"radius\",\n \"layout\",\n \"oauthPlacement\",\n \"stepped\",\n \"logoUrl\",\n \"logoText\",\n \"brandLine\",\n \"brandFoot\",\n \"signInTitle\",\n \"branding\",\n \"initialScreen\",\n \"noAccountView\",\n \"redirectUrl\",\n \"tosUrl\",\n \"privacyUrl\",\n \"nonceStorageKey\",\n \"authEnabled\",\n \"allowEmailSignin\",\n \"allowEmailSignup\",\n \"allowEmailOtp\",\n \"allowOauth\",\n \"allowAccountCreation\",\n \"inviteEmail\",\n \"inviteOrg\",\n \"inviteCode\"\n] as const satisfies readonly (keyof AuthUIConfig)[];\n\n/** \"fieldStyle\" -> \"field-style\", for checking whether the host set it in markup. */\nexport function authUIAttributeFor(key: string): string {\n return key.replace(/[A-Z]/g, (c) => \"-\" + c.toLowerCase());\n}\n","/**\n * A deliberately small MongoDB-flavoured matcher.\n *\n * It exists so a guard can say \"render this only for an admin in the EU\"\n * declaratively, against the profile carried by the session. The scope is\n * fixed on purpose:\n *\n * - one level of operators, never nested query objects;\n * - dot notation is the *only* way to reach into a nested value;\n * - a fixed operator set, listed in OPERATORS below.\n *\n * Anything more expressive belongs in application code, not in an attribute.\n * Keeping it this small means a predicate is always cheap, always total, and\n * can never become a way to smuggle logic into markup.\n */\n\nexport const OPERATORS = [\n \"$eq\",\n \"$ne\",\n \"$in\",\n \"$nin\",\n \"$gt\",\n \"$gte\",\n \"$lt\",\n \"$lte\",\n \"$elemMatch\"\n] as const;\n\nexport type Operator = (typeof OPERATORS)[number];\n\n/** The right-hand side of one field: a bare value (implicit $eq) or an operator bag. */\nexport type Condition = unknown | Partial<Record<Operator, unknown>>;\n\n/** `{ \"metadata.tier\": { $in: [\"pro\", \"team\"] }, status: \"active\" }` */\nexport type Predicate = Record<string, Condition>;\n\nconst OPERATOR_SET = new Set<string>(OPERATORS);\n\nfunction isOperatorBag(value: unknown): value is Partial<Record<Operator, unknown>> {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) return false;\n const keys = Object.keys(value as object);\n return keys.length > 0 && keys.every((key) => OPERATOR_SET.has(key));\n}\n\n/**\n * Reads `a.b.c` off an object. Returns undefined for any missing link rather\n * than throwing — an absent field simply fails to match.\n */\nexport function getPath(source: unknown, path: string): unknown {\n let current = source;\n for (const segment of path.split(\".\")) {\n if (current === null || current === undefined) return undefined;\n if (typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[segment];\n }\n return current;\n}\n\n/** Loose structural equality, enough for the scalars and arrays a profile holds. */\nfunction equals(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((item, i) => equals(item, b[i]));\n }\n if (a && b && typeof a === \"object\" && typeof b === \"object\") {\n const aKeys = Object.keys(a as object);\n const bKeys = Object.keys(b as object);\n return (\n aKeys.length === bKeys.length &&\n aKeys.every((key) =>\n equals((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])\n )\n );\n }\n return false;\n}\n\n/**\n * Ordered comparison for $gt/$gte/$lt/$lte. Only like-typed numbers, strings\n * and dates compare; anything else is \"not comparable\", which fails the test\n * rather than coercing its way to a surprising answer.\n */\nfunction compare(left: unknown, right: unknown): number | null {\n if (typeof left === \"number\" && typeof right === \"number\") {\n return Number.isNaN(left) || Number.isNaN(right) ? null : left - right;\n }\n if (typeof left === \"string\" && typeof right === \"string\") {\n return left < right ? -1 : left > right ? 1 : 0;\n }\n if (left instanceof Date && right instanceof Date) {\n return left.getTime() - right.getTime();\n }\n return null;\n}\n\nfunction matchOperator(operator: string, actual: unknown, expected: unknown): boolean {\n switch (operator) {\n case \"$eq\":\n return equals(actual, expected);\n case \"$ne\":\n return !equals(actual, expected);\n case \"$in\":\n if (!Array.isArray(expected)) return false;\n // Mongo semantics: when the field is an array, membership is an\n // intersection test rather than whole-array equality.\n return Array.isArray(actual)\n ? actual.some((item) => expected.some((candidate) => equals(item, candidate)))\n : expected.some((candidate) => equals(actual, candidate));\n case \"$nin\":\n if (!Array.isArray(expected)) return false;\n return !matchOperator(\"$in\", actual, expected);\n case \"$gt\":\n case \"$gte\":\n case \"$lt\":\n case \"$lte\": {\n const order = compare(actual, expected);\n if (order === null) return false;\n if (operator === \"$gt\") return order > 0;\n if (operator === \"$gte\") return order >= 0;\n if (operator === \"$lt\") return order < 0;\n return order <= 0;\n }\n case \"$elemMatch\": {\n if (!Array.isArray(actual)) return false;\n // One level only: the argument is a condition applied to each element,\n // not a nested query object.\n return actual.some((item) => matchCondition(item, expected));\n }\n default:\n return false;\n }\n}\n\nfunction matchCondition(actual: unknown, condition: Condition): boolean {\n if (isOperatorBag(condition)) {\n // Several operators on one field are ANDed, as in Mongo.\n return Object.entries(condition).every(([operator, expected]) =>\n matchOperator(operator, actual, expected)\n );\n }\n return equals(actual, condition);\n}\n\n/**\n * True when `subject` satisfies every field in `predicate`. An empty or\n * missing predicate matches everything, so a guard with no filter behaves\n * exactly like one that was never given a filter.\n */\nexport function matchesPredicate(\n subject: unknown,\n predicate: Predicate | null | undefined\n): boolean {\n if (!predicate) return true;\n const entries = Object.entries(predicate);\n if (entries.length === 0) return true;\n if (subject === null || subject === undefined) return false;\n\n return entries.every(([path, condition]) => matchCondition(getPath(subject, path), condition));\n}\n\n/**\n * Reports what is wrong with a predicate, for a console warning at the point\n * of use. Returns [] when it is well-formed. Catching `{ $regex: ... }` or a\n * nested query object early is far kinder than silently never matching.\n */\nexport function validatePredicate(predicate: unknown): string[] {\n if (predicate === null || predicate === undefined) return [];\n if (typeof predicate !== \"object\" || Array.isArray(predicate)) {\n return [\"predicate must be an object\"];\n }\n\n const problems: string[] = [];\n for (const [path, condition] of Object.entries(predicate as Record<string, unknown>)) {\n if (path.startsWith(\"$\")) {\n problems.push(`\"${path}\": top-level operators are not supported; use a field name`);\n continue;\n }\n if (condition === null || typeof condition !== \"object\" || Array.isArray(condition)) continue;\n\n const keys = Object.keys(condition as object);\n const operators = keys.filter((key) => key.startsWith(\"$\"));\n\n if (operators.length === 0) {\n problems.push(\n `\"${path}\": nested objects are not supported; use dot notation, e.g. \"${path}.field\"`\n );\n continue;\n }\n if (operators.length !== keys.length) {\n problems.push(`\"${path}\": cannot mix operators with plain keys`);\n }\n for (const operator of operators) {\n if (!OPERATOR_SET.has(operator)) {\n problems.push(`\"${path}\": unsupported operator \"${operator}\"`);\n }\n }\n }\n return problems;\n}\n","/**\n * Cross-tab coordination.\n *\n * When a user signs in or out in one tab, every other tab of the same origin\n * should follow. The signal deliberately carries *no session data*: peers are\n * only told \"something changed\", and they re-read the shared storage\n * themselves. That keeps tokens out of BroadcastChannel payloads and out of\n * the localStorage fallback key entirely, so the encrypted-at-rest guarantee\n * in storage.ts is never bypassed by the sync mechanism.\n *\n * Note this only *works* when the underlying storage is itself shared between\n * tabs (IndexedDB is; sessionStorage is not). With the sessionStorage\n * fallback the peer re-reads its own private copy, finds it unchanged, and\n * does nothing — degraded, but never wrong.\n */\n\nexport type CrossTabSignal = \"session-changed\" | \"signout\";\n\nexport interface CrossTabMessage {\n signal: CrossTabSignal;\n /** Sender id, so a channel can ignore its own echo. */\n from: string;\n at: number;\n}\n\nexport interface CrossTabChannel {\n post(signal: CrossTabSignal): void;\n subscribe(listener: (message: CrossTabMessage) => void): () => void;\n close(): void;\n}\n\nconst noop = () => {};\n\n/** A channel that does nothing — used when cross-tab sync is disabled or unavailable. */\nexport function noopCrossTabChannel(): CrossTabChannel {\n return { post: noop, subscribe: () => noop, close: noop };\n}\n\nfunction newId(): string {\n const c = globalThis.crypto;\n if (c && \"randomUUID\" in c) return c.randomUUID();\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n}\n\n/**\n * Prefers BroadcastChannel; falls back to a localStorage `storage` event,\n * which fires only in *other* tabs, exactly the semantics we want. Returns a\n * no-op channel when neither is available (SSR, tests, locked-down origins).\n */\nexport function crossTabChannel(name: string): CrossTabChannel {\n const id = newId();\n\n const BC = (globalThis as { BroadcastChannel?: typeof BroadcastChannel }).BroadcastChannel;\n if (BC) {\n let channel: BroadcastChannel;\n try {\n channel = new BC(name);\n } catch {\n return storageEventChannel(name, id);\n }\n // In Node an open channel keeps the process alive; tabs don't need that.\n (channel as { unref?: () => void }).unref?.();\n return {\n post(signal) {\n try {\n channel.postMessage({ signal, from: id, at: Date.now() } satisfies CrossTabMessage);\n } catch {\n // A closed or blocked channel must never break the auth flow.\n }\n },\n subscribe(listener) {\n const handler = (event: MessageEvent) => {\n const message = event.data as CrossTabMessage | null;\n if (!message || typeof message !== \"object\") return;\n if (message.from === id) return;\n if (message.signal !== \"session-changed\" && message.signal !== \"signout\") return;\n listener(message);\n };\n channel.addEventListener(\"message\", handler);\n return () => channel.removeEventListener(\"message\", handler);\n },\n close() {\n try {\n channel.close();\n } catch {\n // already closed\n }\n }\n };\n }\n\n return storageEventChannel(name, id);\n}\n\nfunction storageEventChannel(name: string, id: string): CrossTabChannel {\n const store = safeLocalStorage();\n if (!store) return noopCrossTabChannel();\n\n const key = `${name}:signal`;\n\n return {\n post(signal) {\n try {\n store.setItem(key, JSON.stringify({ signal, from: id, at: Date.now() }));\n } catch {\n // Quota or private-mode failures are non-fatal.\n }\n },\n subscribe(listener) {\n const handler = (event: StorageEvent) => {\n if (event.key !== key || !event.newValue) return;\n try {\n const message = JSON.parse(event.newValue) as CrossTabMessage;\n if (message.from === id) return;\n if (message.signal !== \"session-changed\" && message.signal !== \"signout\") return;\n listener(message);\n } catch {\n // Ignore anything that is not one of our messages.\n }\n };\n globalThis.addEventListener?.(\"storage\", handler as EventListener);\n return () => globalThis.removeEventListener?.(\"storage\", handler as EventListener);\n },\n close: noop\n };\n}\n\nfunction safeLocalStorage(): Storage | null {\n try {\n const store = globalThis.localStorage;\n if (!store) return null;\n const probe = \"__singlebase_probe__\";\n store.setItem(probe, \"1\");\n store.removeItem(probe);\n return store;\n } catch {\n return null;\n }\n}\n","import type { SinglebaseOptions } from \"./types.js\";\nimport { DEFAULT_BASE_URL } from \"./transport.js\";\n\n/**\n * Client registry.\n *\n * A page can hold several widgets — a header sign-in, a modal, an account\n * panel — and they must all share one client: one session, one refresh\n * timer, one set of tokens. A client per widget would mean several session\n * stores and several refresh loops racing for the single-use refresh token.\n *\n * The registry lives on `globalThis` under a well-known symbol rather than in\n * module scope, because bundles duplicate modules: a page that loads the\n * widget bundle *and* imports the SDK has two copies of this file, and a\n * module-scoped `let` would give each its own \"default\" — so widgets would\n * silently never see the client the app created. Keying off globalThis makes\n * every copy, and every independently loaded script, agree.\n */\nconst REGISTRY = Symbol.for(\"singlebase.client.registry\");\n\ninterface Registry<T> {\n instances: Map<string, T>;\n default: T | null;\n}\n\nfunction registry<T>(): Registry<T> {\n const host = globalThis as unknown as Record<symbol, Registry<T> | undefined>;\n let existing = host[REGISTRY];\n if (!existing) {\n existing = { instances: new Map(), default: null };\n host[REGISTRY] = existing;\n }\n return existing;\n}\n\n/** Connection identity — clients with the same one are the same instance. */\nexport function clientKey(options: SinglebaseOptions): string {\n return [\n options.baseUrl ?? DEFAULT_BASE_URL,\n options.urlAccessKey ?? \"\",\n options.apiKey ?? \"\",\n options.audience ?? \"web\"\n ].join(\"|\");\n}\n\n/** Returns the cached client for these options, or creates and caches one. */\nexport function resolveClient<T>(options: SinglebaseOptions, create: () => T): T {\n const reg = registry<T>();\n const key = clientKey(options);\n const existing = reg.instances.get(key);\n if (existing) return existing;\n\n const client = create();\n reg.instances.set(key, client);\n if (!reg.default) reg.default = client;\n return client;\n}\n\n/** The client widgets fall back to when none is set on them. */\nexport function getDefaultClient<T = unknown>(): T | null {\n return registry<T>().default;\n}\n\n/** Overrides the page default (useful when several projects are in play). */\nexport function setDefaultClient<T>(client: T | null): void {\n registry<T>().default = client;\n}\n\n/** Drops a client from the registry. */\nexport function unregisterClient<T>(client: T): void {\n const reg = registry<T>();\n for (const [key, value] of reg.instances) {\n if (value === client) reg.instances.delete(key);\n }\n if (reg.default === client) {\n reg.default = reg.instances.values().next().value ?? null;\n }\n}\n"],"names":["SinglebaseError","payload","cause","ERROR_HINTS","mapErrorToHint","code","isLocalHost","hostname","DEFAULT_BASE_URL","resolveBaseUrl","options","endpointFor","base","assertSecureBaseUrl","key","_a","baseUrl","url","request","clientOptions","operation","extras","envelope","headers","fetchImpl","res","json","RETRYABLE_AUTH_CODES","RpcDispatcher","bridge","provider","bearer","signal","explicit","token","error","refreshed","DB_NAME","DB_VERSION","STORE","KEY_ID","DATA_ID","memoryStorage","current","session","webStorageAdapter","getStorage","raw","localStorageAdapter","sessionStorageAdapter","idbRequest","resolve","reject","openDb","idbRead","id","db","tx","idbWrite","value","idbDelete","canUseCryptoStorage","indexedDbCryptoStorage","keyPromise","getKey","existing","record","plain","iv","data","defaultStorage","backing","probed","candidate","EventEmitter","event","listener","set","detail","AUTHUI_PROP_KEYS","authUIAttributeFor","c","OPERATORS","OPERATOR_SET","isOperatorBag","keys","getPath","source","path","segment","equals","a","b","item","i","aKeys","bKeys","compare","left","right","matchOperator","operator","actual","expected","order","matchCondition","condition","matchesPredicate","subject","predicate","entries","validatePredicate","problems","operators","noop","noopCrossTabChannel","newId","crossTabChannel","name","BC","channel","storageEventChannel","handler","message","store","safeLocalStorage","probe","REGISTRY","registry","host","clientKey","resolveClient","create","reg","client","getDefaultClient","setDefaultClient","unregisterClient"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ class g extends Error {
13
13
  );
14
14
  }
15
15
  }
16
- const L = {
16
+ const $ = {
17
17
  INVALID_PAYLOAD: "highlight_invalid_fields",
18
18
  INVALID_CREDENTIALS: "generic_credentials_message",
19
19
  CODE_REQUIRED: "reveal_code_input",
@@ -30,15 +30,20 @@ const L = {
30
30
  EMAIL_PROVIDER_DISABLED: "hide_email_forms",
31
31
  EMAIL_SIGNUP_DISABLED: "hide_signup",
32
32
  OAUTH_DISABLED: "hide_oauth",
33
+ OAUTH_SIGNIN_DISABLED: "remove_oauth_from_signin",
33
34
  OAUTH_SIGNUP_DISABLED: "remove_oauth_from_signup",
34
35
  INVALID_OAUTH_PROVIDER: "refresh_settings_hide_provider",
36
+ MISSING_OAUTH_CREDENTIALS: "show_integrator_config_error",
37
+ OAUTH_VERIFICATION_FAILED: "restart_oauth",
38
+ VERIFIED_PROVIDER_EMAIL_REQUIRED: "explain_verified_email_required",
35
39
  INVALID_NONCE: "restart_oauth_new_nonce",
36
40
  SIGN_IN_TO_LINK_PROVIDER: "ask_signin_then_link",
41
+ PROVIDER_ALREADY_LINKED: "provider_already_linked",
37
42
  AUTHENTICATION_REQUIRED: "route_to_signin",
38
43
  UNAUTHORIZED_DOMAIN: "show_integrator_config_error"
39
44
  };
40
45
  function z(e) {
41
- return L[e] ?? "unknown";
46
+ return $[e] ?? "unknown";
42
47
  }
43
48
  function k(e) {
44
49
  return e === "localhost" || e === "127.0.0.1" || e === "0.0.0.0";
@@ -71,7 +76,7 @@ function B(e) {
71
76
  `SinglebaseOptions.baseUrl must use HTTPS outside of localhost/127.0.0.1/0.0.0.0 (got ${t.protocol})`
72
77
  );
73
78
  }
74
- async function m(e, t, r, n = {}) {
79
+ async function I(e, t, r, n = {}) {
75
80
  const s = P(e), o = { operation: t, payload: r };
76
81
  n.options && (o.options = n.options);
77
82
  const a = { "Content-Type": "application/json" };
@@ -122,7 +127,7 @@ class X {
122
127
  async dispatch(t, { bearer: r, signal: n } = {}) {
123
128
  const s = r !== void 0, o = s ? null : await this.resolveBridge(), a = s ? r : (o == null ? void 0 : o.getToken()) ?? null, c = { token: a, signal: n, options: t.options };
124
129
  try {
125
- return await m(
130
+ return await I(
126
131
  this.options,
127
132
  t.operation,
128
133
  t.payload ?? {},
@@ -133,7 +138,7 @@ class X {
133
138
  if (!(!s && a !== null && o !== null && K.has(f))) throw i;
134
139
  const A = await o.refresh();
135
140
  if (!A) throw i;
136
- return m(
141
+ return I(
137
142
  this.options,
138
143
  t.operation,
139
144
  t.payload ?? {},
@@ -142,8 +147,8 @@ class X {
142
147
  }
143
148
  }
144
149
  }
145
- const j = "singlebase-auth", V = 1, l = "session", E = "wrapping-key", d = "session";
146
- function I() {
150
+ const V = "singlebase-auth", j = 1, l = "session", m = "wrapping-key", d = "session";
151
+ function w() {
147
152
  let e = null;
148
153
  return {
149
154
  get: () => e,
@@ -155,7 +160,7 @@ function I() {
155
160
  }
156
161
  };
157
162
  }
158
- function R(e, t) {
163
+ function N(e, t) {
159
164
  return {
160
165
  get: () => {
161
166
  var n;
@@ -178,26 +183,26 @@ function R(e, t) {
178
183
  };
179
184
  }
180
185
  function ee(e = "singlebase-auth-session") {
181
- return R(() => globalThis.localStorage, e);
186
+ return N(() => globalThis.localStorage, e);
182
187
  }
183
- function M(e = "singlebase-auth-session") {
184
- return R(() => globalThis.sessionStorage, e);
188
+ function H(e = "singlebase-auth-session") {
189
+ return N(() => globalThis.sessionStorage, e);
185
190
  }
186
191
  function b(e) {
187
192
  return new Promise((t, r) => {
188
193
  e.onsuccess = () => t(e.result), e.onerror = () => r(e.error);
189
194
  });
190
195
  }
191
- function w() {
196
+ function E() {
192
197
  return new Promise((e, t) => {
193
- const r = globalThis.indexedDB.open(j, V);
198
+ const r = globalThis.indexedDB.open(V, j);
194
199
  r.onupgradeneeded = () => {
195
200
  r.result.objectStoreNames.contains(l) || r.result.createObjectStore(l);
196
201
  }, r.onsuccess = () => e(r.result), r.onerror = () => t(r.error), r.onblocked = () => t(new Error("indexedDB open blocked"));
197
202
  });
198
203
  }
199
204
  async function S(e) {
200
- const t = await w();
205
+ const t = await E();
201
206
  try {
202
207
  const r = t.transaction(l, "readonly");
203
208
  return await b(r.objectStore(l).get(e));
@@ -205,8 +210,8 @@ async function S(e) {
205
210
  t.close();
206
211
  }
207
212
  }
208
- async function T(e, t) {
209
- const r = await w();
213
+ async function D(e, t) {
214
+ const r = await E();
210
215
  try {
211
216
  const n = r.transaction(l, "readwrite");
212
217
  await b(n.objectStore(l).put(t, e));
@@ -214,8 +219,8 @@ async function T(e, t) {
214
219
  r.close();
215
220
  }
216
221
  }
217
- async function D(e) {
218
- const t = await w();
222
+ async function T(e) {
223
+ const t = await E();
219
224
  try {
220
225
  const r = t.transaction(l, "readwrite");
221
226
  await b(r.objectStore(l).delete(e));
@@ -223,14 +228,14 @@ async function D(e) {
223
228
  t.close();
224
229
  }
225
230
  }
226
- function H() {
231
+ function M() {
227
232
  return typeof globalThis.indexedDB < "u" && typeof globalThis.crypto < "u" && typeof globalThis.crypto.subtle < "u";
228
233
  }
229
- function x() {
234
+ function G() {
230
235
  let e = null;
231
236
  async function t() {
232
237
  return e || (e = (async () => {
233
- const r = await S(E);
238
+ const r = await S(m);
234
239
  if (r) return r;
235
240
  const n = await globalThis.crypto.subtle.generateKey(
236
241
  { name: "AES-GCM", length: 256 },
@@ -238,7 +243,7 @@ function x() {
238
243
  !1,
239
244
  ["encrypt", "decrypt"]
240
245
  );
241
- return await T(E, n), n;
246
+ return await D(m, n), n;
242
247
  })().catch((r) => {
243
248
  throw e = null, r;
244
249
  })), e;
@@ -255,7 +260,7 @@ function x() {
255
260
  );
256
261
  return JSON.parse(new TextDecoder().decode(s));
257
262
  } catch {
258
- return await D(d).catch(() => {
263
+ return await T(d).catch(() => {
259
264
  }), null;
260
265
  }
261
266
  },
@@ -265,10 +270,10 @@ function x() {
265
270
  n,
266
271
  new TextEncoder().encode(JSON.stringify(r))
267
272
  );
268
- await T(d, { iv: s, data: o });
273
+ await D(d, { iv: s, data: o });
269
274
  },
270
275
  async clear() {
271
- await D(d).catch(() => {
276
+ await T(d).catch(() => {
272
277
  });
273
278
  }
274
279
  };
@@ -278,16 +283,16 @@ function te() {
278
283
  async function r() {
279
284
  if (e) return e;
280
285
  if (!t) {
281
- if (t = !0, H()) {
282
- const n = x();
286
+ if (t = !0, M()) {
287
+ const n = G();
283
288
  try {
284
289
  return await n.get(), e = n, e;
285
290
  } catch {
286
291
  }
287
292
  }
288
- e = typeof globalThis.sessionStorage < "u" ? M() : I();
293
+ e = typeof globalThis.sessionStorage < "u" ? H() : w();
289
294
  }
290
- return e ?? I();
295
+ return e ?? w();
291
296
  }
292
297
  return {
293
298
  async get() {
@@ -339,8 +344,6 @@ const ne = [
339
344
  "brandFoot",
340
345
  "signInTitle",
341
346
  "branding",
342
- "brandingText",
343
- "brandingUrl",
344
347
  "initialScreen",
345
348
  "noAccountView",
346
349
  "redirectUrl",
@@ -360,7 +363,7 @@ const ne = [
360
363
  function se(e) {
361
364
  return e.replace(/[A-Z]/g, (t) => "-" + t.toLowerCase());
362
365
  }
363
- const G = [
366
+ const x = [
364
367
  "$eq",
365
368
  "$ne",
366
369
  "$in",
@@ -370,13 +373,13 @@ const G = [
370
373
  "$lt",
371
374
  "$lte",
372
375
  "$elemMatch"
373
- ], v = new Set(G);
374
- function J(e) {
376
+ ], v = new Set(x);
377
+ function F(e) {
375
378
  if (e === null || typeof e != "object" || Array.isArray(e)) return !1;
376
379
  const t = Object.keys(e);
377
380
  return t.length > 0 && t.every((r) => v.has(r));
378
381
  }
379
- function W(e, t) {
382
+ function J(e, t) {
380
383
  let r = e;
381
384
  for (const n of t.split(".")) {
382
385
  if (r == null || typeof r != "object") return;
@@ -396,7 +399,7 @@ function u(e, t) {
396
399
  }
397
400
  return !1;
398
401
  }
399
- function q(e, t) {
402
+ function W(e, t) {
400
403
  return typeof e == "number" && typeof t == "number" ? Number.isNaN(e) || Number.isNaN(t) ? null : e - t : typeof e == "string" && typeof t == "string" ? e < t ? -1 : e > t ? 1 : 0 : e instanceof Date && t instanceof Date ? e.getTime() - t.getTime() : null;
401
404
  }
402
405
  function U(e, t, r) {
@@ -413,24 +416,24 @@ function U(e, t, r) {
413
416
  case "$gte":
414
417
  case "$lt":
415
418
  case "$lte": {
416
- const n = q(t, r);
419
+ const n = W(t, r);
417
420
  return n === null ? !1 : e === "$gt" ? n > 0 : e === "$gte" ? n >= 0 : e === "$lt" ? n < 0 : n <= 0;
418
421
  }
419
422
  case "$elemMatch":
420
- return Array.isArray(t) ? t.some((n) => $(n, r)) : !1;
423
+ return Array.isArray(t) ? t.some((n) => L(n, r)) : !1;
421
424
  default:
422
425
  return !1;
423
426
  }
424
427
  }
425
- function $(e, t) {
426
- return J(t) ? Object.entries(t).every(
428
+ function L(e, t) {
429
+ return F(t) ? Object.entries(t).every(
427
430
  ([r, n]) => U(r, e, n)
428
431
  ) : u(e, t);
429
432
  }
430
433
  function oe(e, t) {
431
434
  if (!t) return !0;
432
435
  const r = Object.entries(t);
433
- return r.length === 0 ? !0 : e == null ? !1 : r.every(([n, s]) => $(W(e, n), s));
436
+ return r.length === 0 ? !0 : e == null ? !1 : r.every(([n, s]) => L(J(e, n), s));
434
437
  }
435
438
  function ie(e) {
436
439
  if (e == null) return [];
@@ -456,10 +459,10 @@ function ie(e) {
456
459
  }
457
460
  return t;
458
461
  }
459
- const y = () => {
462
+ const _ = () => {
460
463
  };
461
- function F() {
462
- return { post: y, subscribe: () => y, close: y };
464
+ function q() {
465
+ return { post: _, subscribe: () => _, close: _ };
463
466
  }
464
467
  function Y() {
465
468
  const e = globalThis.crypto;
@@ -501,7 +504,7 @@ function ae(e) {
501
504
  }
502
505
  function O(e, t) {
503
506
  const r = Q();
504
- if (!r) return F();
507
+ if (!r) return q();
505
508
  const n = `${e}:signal`;
506
509
  return {
507
510
  post(s) {
@@ -526,7 +529,7 @@ function O(e, t) {
526
529
  return (c = globalThis.removeEventListener) == null ? void 0 : c.call(globalThis, "storage", o);
527
530
  };
528
531
  },
529
- close: y
532
+ close: _
530
533
  };
531
534
  }
532
535
  function Q() {
@@ -539,11 +542,11 @@ function Q() {
539
542
  return null;
540
543
  }
541
544
  }
542
- const N = Symbol.for("singlebase.client.registry");
543
- function _() {
545
+ const R = Symbol.for("singlebase.client.registry");
546
+ function y() {
544
547
  const e = globalThis;
545
- let t = e[N];
546
- return t || (t = { instances: /* @__PURE__ */ new Map(), default: null }, e[N] = t), t;
548
+ let t = e[R];
549
+ return t || (t = { instances: /* @__PURE__ */ new Map(), default: null }, e[R] = t), t;
547
550
  }
548
551
  function Z(e) {
549
552
  return [
@@ -554,19 +557,19 @@ function Z(e) {
554
557
  ].join("|");
555
558
  }
556
559
  function ce(e, t) {
557
- const r = _(), n = Z(e), s = r.instances.get(n);
560
+ const r = y(), n = Z(e), s = r.instances.get(n);
558
561
  if (s) return s;
559
562
  const o = t();
560
563
  return r.instances.set(n, o), r.default || (r.default = o), o;
561
564
  }
562
565
  function le() {
563
- return _().default;
566
+ return y().default;
564
567
  }
565
568
  function ue(e) {
566
- _().default = e;
569
+ y().default = e;
567
570
  }
568
571
  function fe(e) {
569
- const t = _();
572
+ const t = y();
570
573
  for (const [r, n] of t.instances)
571
574
  n === e && t.instances.delete(r);
572
575
  t.default === e && (t.default = t.instances.values().next().value ?? null);
@@ -575,29 +578,29 @@ export {
575
578
  ne as AUTHUI_PROP_KEYS,
576
579
  p as DEFAULT_BASE_URL,
577
580
  re as EventEmitter,
578
- G as OPERATORS,
581
+ x as OPERATORS,
579
582
  X as RpcDispatcher,
580
583
  g as SinglebaseAuthError,
581
584
  g as SinglebaseError,
582
585
  B as assertSecureBaseUrl,
583
586
  se as authUIAttributeFor,
584
- H as canUseCryptoStorage,
587
+ M as canUseCryptoStorage,
585
588
  Z as clientKey,
586
589
  ae as crossTabChannel,
587
590
  te as defaultStorage,
588
591
  P as endpointFor,
589
592
  le as getDefaultClient,
590
- W as getPath,
591
- x as indexedDbCryptoStorage,
593
+ J as getPath,
594
+ G as indexedDbCryptoStorage,
592
595
  ee as localStorageAdapter,
593
596
  z as mapErrorToHint,
594
597
  oe as matchesPredicate,
595
- I as memoryStorage,
596
- F as noopCrossTabChannel,
597
- m as request,
598
+ w as memoryStorage,
599
+ q as noopCrossTabChannel,
600
+ I as request,
598
601
  C as resolveBaseUrl,
599
602
  ce as resolveClient,
600
- M as sessionStorageAdapter,
603
+ H as sessionStorageAdapter,
601
604
  ue as setDefaultClient,
602
605
  fe as unregisterClient,
603
606
  ie as validatePredicate
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/errors.ts","../src/transport.ts","../src/dispatcher.ts","../src/storage.ts","../src/events.ts","../src/authui-config.ts","../src/predicate.ts","../src/cross-tab.ts","../src/registry.ts"],"sourcesContent":["import type { ErrorResponsePayload } from \"./types.js\";\n\n/**\n * Normalized error thrown by every SDK method. `code` is the server's\n * machine-readable `error.message` (e.g. \"INVALID_CREDENTIALS\"), never the\n * human-readable text.\n */\nexport class SinglebaseError extends Error {\n status: number;\n type: string;\n code: string;\n details?: unknown;\n traceId?: string | null;\n override cause?: unknown;\n\n constructor(payload: ErrorResponsePayload, cause?: unknown) {\n super(payload.message);\n this.name = \"SinglebaseError\";\n this.status = payload.status;\n this.type = payload.type;\n this.code = payload.message;\n this.details = payload.details;\n this.traceId = payload.trace_id ?? null;\n this.cause = cause;\n }\n\n static fromNetworkError(cause: unknown): SinglebaseError {\n return new SinglebaseError(\n {\n type: \"NETWORK_ERROR\",\n status: 0,\n message: \"NETWORK_ERROR\"\n },\n cause\n );\n }\n}\n\n/**\n * Recommended UI behavior for a given server error code, taken verbatim from\n * the auth API's error-mapping table. UI components use this to decide\n * *behavior* (focus a field, trigger a refresh, sign out locally); the actual\n * copy shown to the user is left to each component/its `.messages` override.\n */\nexport type ErrorHint =\n | \"highlight_invalid_fields\"\n | \"generic_credentials_message\"\n | \"reveal_code_input\"\n | \"expired_or_invalid_code_message\"\n | \"attempt_refresh_or_signout\"\n | \"clear_session_and_ask_signin\"\n | \"clear_session_config_error\"\n | \"route_to_recovery_or_invite\"\n | \"show_password_requirements\"\n | \"require_changed_field\"\n | \"disable_retry_show_rate_limited\"\n | \"render_auth_unavailable\"\n | \"hide_email_forms\"\n | \"hide_signup\"\n | \"hide_oauth\"\n | \"remove_oauth_from_signup\"\n | \"refresh_settings_hide_provider\"\n | \"restart_oauth_new_nonce\"\n | \"ask_signin_then_link\"\n | \"route_to_signin\"\n | \"show_integrator_config_error\"\n | \"unknown\";\n\nconst ERROR_HINTS: Record<string, ErrorHint> = {\n INVALID_PAYLOAD: \"highlight_invalid_fields\",\n INVALID_CREDENTIALS: \"generic_credentials_message\",\n CODE_REQUIRED: \"reveal_code_input\",\n INVALID_TOKEN: \"expired_or_invalid_code_message\",\n INVALID_ID_TOKEN: \"attempt_refresh_or_signout\",\n INVALID_BEARER_TOKEN: \"attempt_refresh_or_signout\",\n INVALID_SESSION: \"clear_session_and_ask_signin\",\n INVALID_TOKEN_AUDIENCE: \"clear_session_config_error\",\n REQUIRE_PASSWORD_CHANGE: \"route_to_recovery_or_invite\",\n INVALID_PASSWORD: \"show_password_requirements\",\n MISSING_PROFILE_DATA: \"require_changed_field\",\n AUTH_RATE_LIMITED: \"disable_retry_show_rate_limited\",\n AUTH_DISABLED: \"render_auth_unavailable\",\n EMAIL_PROVIDER_DISABLED: \"hide_email_forms\",\n EMAIL_SIGNUP_DISABLED: \"hide_signup\",\n OAUTH_DISABLED: \"hide_oauth\",\n OAUTH_SIGNUP_DISABLED: \"remove_oauth_from_signup\",\n INVALID_OAUTH_PROVIDER: \"refresh_settings_hide_provider\",\n INVALID_NONCE: \"restart_oauth_new_nonce\",\n SIGN_IN_TO_LINK_PROVIDER: \"ask_signin_then_link\",\n AUTHENTICATION_REQUIRED: \"route_to_signin\",\n UNAUTHORIZED_DOMAIN: \"show_integrator_config_error\"\n};\n\nexport function mapErrorToHint(code: string): ErrorHint {\n return ERROR_HINTS[code] ?? \"unknown\";\n}\n\n/** Back-compat alias: the error type is shared by every service. */\nexport { SinglebaseError as SinglebaseAuthError };\n","import { SinglebaseError } from \"./errors.js\";\nimport type {\n SinglebaseOptions,\n ErrorResponse,\n RequestEnvelope,\n SuccessResponse\n} from \"./types.js\";\n\nexport interface RequestExtras {\n token?: string | null;\n signal?: AbortSignal;\n options?: Record<string, unknown>;\n}\n\nfunction isLocalHost(hostname: string): boolean {\n return hostname === \"localhost\" || hostname === \"127.0.0.1\" || hostname === \"0.0.0.0\";\n}\n\n/** Where requests go when `baseUrl` is not given. */\nexport const DEFAULT_BASE_URL = \"https://v1.api.singlebase.io\";\n\n/**\n * The effective API root: the default when `baseUrl` is omitted. An explicit\n * null or empty value is a configuration mistake, and is reported as one\n * rather than silently replaced.\n */\nexport function resolveBaseUrl(options: Pick<SinglebaseOptions, \"baseUrl\">): string {\n if (options.baseUrl === undefined) return DEFAULT_BASE_URL;\n if (options.baseUrl === null || String(options.baseUrl).trim() === \"\") {\n throw new Error(\n `SinglebaseOptions.baseUrl cannot be null or empty. Omit it to use ${DEFAULT_BASE_URL}.`\n );\n }\n return String(options.baseUrl).trim().replace(/\\/+$/, \"\");\n}\n\n/**\n * The URL every request is POSTed to: `{baseUrl}/{urlAccessKey}`, or just\n * `{baseUrl}` when there is no access key.\n */\nexport function endpointFor(options: Pick<SinglebaseOptions, \"baseUrl\" | \"urlAccessKey\">): string {\n const base = resolveBaseUrl(options);\n assertSecureBaseUrl(base);\n const key = options.urlAccessKey?.trim();\n return key ? `${base}/${encodeURIComponent(key)}` : base;\n}\n\n/** Enforces the spec's HTTPS-outside-localhost rule for the configured baseUrl. */\nexport function assertSecureBaseUrl(baseUrl: string): void {\n let url: URL;\n try {\n url = new URL(baseUrl);\n } catch {\n throw new Error(`SinglebaseOptions.baseUrl is not a valid URL: ${baseUrl}`);\n }\n if (url.protocol !== \"https:\" && !isLocalHost(url.hostname)) {\n throw new Error(\n `SinglebaseOptions.baseUrl must use HTTPS outside of localhost/127.0.0.1/0.0.0.0 (got ${url.protocol})`\n );\n }\n}\n\n/**\n * Low-level RPC request. Returns the parsed `data` on success and throws\n * `SinglebaseError` on a well-formed error response or a network/transport\n * failure. Never leaks tokens into thrown error objects, logs, or URLs.\n */\nexport async function request<TData = unknown, TPayload = unknown>(\n clientOptions: SinglebaseOptions,\n operation: string,\n payload: TPayload,\n extras: RequestExtras = {}\n): Promise<TData> {\n const url = endpointFor(clientOptions);\n\n const envelope: RequestEnvelope<TPayload> = { operation, payload };\n if (extras.options) envelope.options = extras.options;\n\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (clientOptions.apiKey) headers[\"X-API-Key\"] = clientOptions.apiKey;\n if (extras.token) {\n headers.Authorization = `Bearer ${extras.token}`;\n }\n\n const fetchImpl = clientOptions.fetch ?? globalThis.fetch;\n\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(envelope),\n signal: extras.signal\n });\n } catch (cause) {\n throw SinglebaseError.fromNetworkError(cause);\n }\n\n let json: SuccessResponse<TData> | ErrorResponse;\n try {\n json = await res.json();\n } catch (cause) {\n throw SinglebaseError.fromNetworkError(cause);\n }\n\n if (\"error\" in json) {\n throw new SinglebaseError(json.error);\n }\n return json.data;\n}\n","import { SinglebaseError } from \"./errors.js\";\nimport { request } from \"./transport.js\";\nimport type { SinglebaseOptions } from \"./types.js\";\n\n/** Server error codes that mean \"your bearer token is stale, try refreshing\". */\nconst RETRYABLE_AUTH_CODES = new Set([\"INVALID_ID_TOKEN\", \"INVALID_BEARER_TOKEN\"]);\n\nexport interface DispatchEnvelope<TPayload = unknown> {\n operation: string;\n payload?: TPayload;\n options?: Record<string, unknown>;\n}\n\nexport interface DispatchOptions {\n /**\n * Explicit bearer token. Omit to let the dispatcher inject the current\n * session's ID token; pass `null` to force an unauthenticated call.\n */\n bearer?: string | null;\n signal?: AbortSignal;\n}\n\nexport interface DispatcherAuthBridge {\n /** Current ID token, or null when signed out. */\n getToken(): string | null;\n /** Refresh once and return the new ID token, or null if unrecoverable. */\n refresh(): Promise<string | null>;\n}\n\n/**\n * Supplies the auth bridge on demand. Returning a promise lets auth be\n * *constructed lazily* — a page that never signs in pays nothing — while\n * still being *hydrated eagerly* before the first call that could carry a\n * token. Without this, a `data.query()` fired on page load would race the\n * session restore and silently go out unauthenticated.\n */\nexport type AuthBridgeProvider = () => Promise<DispatcherAuthBridge | null>;\n\n/**\n * One RPC pipe shared by every Singlebase service.\n *\n * Auth and every service client go through this, so there is a single place\n * that knows the envelope shape, the endpoint, how a bearer is attached, and\n * how a stale token is recovered.\n */\nexport class RpcDispatcher {\n private bridge: DispatcherAuthBridge | null = null;\n private provider: AuthBridgeProvider | null = null;\n\n constructor(readonly options: SinglebaseOptions) {}\n\n /** Registers the session owner (the auth client) as the token source. */\n setAuthBridge(bridge: DispatcherAuthBridge | null): void {\n this.bridge = bridge;\n }\n\n /** Registers a factory that builds+hydrates the token source on first need. */\n setAuthProvider(provider: AuthBridgeProvider | null): void {\n this.provider = provider;\n }\n\n private async resolveBridge(): Promise<DispatcherAuthBridge | null> {\n if (this.bridge) return this.bridge;\n if (!this.provider) return null;\n this.bridge = await this.provider();\n return this.bridge;\n }\n\n /**\n * Sends one operation. Injects the session bearer unless told otherwise,\n * and on a stale-token error refreshes once and retries the call exactly\n * once — never looping.\n */\n async dispatch<TResult = unknown, TPayload = unknown>(\n envelope: DispatchEnvelope<TPayload>,\n { bearer, signal }: DispatchOptions = {}\n ): Promise<TResult> {\n const explicit = bearer !== undefined;\n const bridge = explicit ? null : await this.resolveBridge();\n const token = explicit ? bearer : (bridge?.getToken() ?? null);\n\n const extras = { token, signal, options: envelope.options };\n\n try {\n return await request<TResult, TPayload>(\n this.options,\n envelope.operation,\n (envelope.payload ?? {}) as TPayload,\n extras\n );\n } catch (error) {\n const code = error instanceof SinglebaseError ? error.code : \"\";\n const canRetry =\n !explicit && token !== null && bridge !== null && RETRYABLE_AUTH_CODES.has(code);\n if (!canRetry) throw error;\n\n const refreshed = await bridge!.refresh();\n if (!refreshed) throw error;\n\n return request<TResult, TPayload>(\n this.options,\n envelope.operation,\n (envelope.payload ?? {}) as TPayload,\n { ...extras, token: refreshed }\n );\n }\n }\n}\n","import type { SinglebaseStorage } from \"./types.js\";\n\nconst DB_NAME = \"singlebase-auth\";\nconst DB_VERSION = 1;\nconst STORE = \"session\";\nconst KEY_ID = \"wrapping-key\";\nconst DATA_ID = \"session\";\n\n/** In-memory storage. Never persisted — safest, but the session ends on reload. */\nexport function memoryStorage<T = unknown>(): SinglebaseStorage<T> {\n let current: T | null = null;\n return {\n get: () => current,\n set: (session) => {\n current = session;\n },\n clear: () => {\n current = null;\n }\n };\n}\n\nfunction webStorageAdapter<T>(\n getStorage: () => Storage | undefined,\n key: string\n): SinglebaseStorage<T> {\n return {\n get: () => {\n const raw = getStorage()?.getItem(key);\n if (!raw) return null;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return null;\n }\n },\n set: (session) => {\n getStorage()?.setItem(key, JSON.stringify(session));\n },\n clear: () => {\n getStorage()?.removeItem(key);\n }\n };\n}\n\n/**\n * Persists the session in `localStorage` as plain text. Survives reloads and\n * is shared across tabs, but any script on the origin — including an XSS\n * payload — can read the tokens verbatim. Choose this deliberately.\n */\nexport function localStorageAdapter<T = unknown>(\n key = \"singlebase-auth-session\"\n): SinglebaseStorage<T> {\n return webStorageAdapter(() => globalThis.localStorage, key);\n}\n\n/** Persists the session in `sessionStorage` — plain text, cleared with the tab. */\nexport function sessionStorageAdapter<T = unknown>(\n key = \"singlebase-auth-session\"\n): SinglebaseStorage<T> {\n return webStorageAdapter(() => globalThis.sessionStorage, key);\n}\n\n// ---------------------------------------------------------------------------\n// IndexedDB + non-extractable CryptoKey\n// ---------------------------------------------------------------------------\n\nfunction idbRequest<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n}\n\nfunction openDb(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request = globalThis.indexedDB.open(DB_NAME, DB_VERSION);\n request.onupgradeneeded = () => {\n if (!request.result.objectStoreNames.contains(STORE)) {\n request.result.createObjectStore(STORE);\n }\n };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n request.onblocked = () => reject(new Error(\"indexedDB open blocked\"));\n });\n}\n\nasync function idbRead<T>(id: string): Promise<T | undefined> {\n const db = await openDb();\n try {\n const tx = db.transaction(STORE, \"readonly\");\n return await idbRequest<T>(tx.objectStore(STORE).get(id) as IDBRequest<T>);\n } finally {\n db.close();\n }\n}\n\nasync function idbWrite(id: string, value: unknown): Promise<void> {\n const db = await openDb();\n try {\n const tx = db.transaction(STORE, \"readwrite\");\n await idbRequest(tx.objectStore(STORE).put(value, id));\n } finally {\n db.close();\n }\n}\n\nasync function idbDelete(id: string): Promise<void> {\n const db = await openDb();\n try {\n const tx = db.transaction(STORE, \"readwrite\");\n await idbRequest(tx.objectStore(STORE).delete(id));\n } finally {\n db.close();\n }\n}\n\n/** True when this environment can do IndexedDB + WebCrypto (a secure context). */\nexport function canUseCryptoStorage(): boolean {\n return (\n typeof globalThis.indexedDB !== \"undefined\" &&\n typeof globalThis.crypto !== \"undefined\" &&\n typeof globalThis.crypto.subtle !== \"undefined\"\n );\n}\n\ninterface EncryptedRecord {\n // Pinned to ArrayBuffer (not ArrayBufferLike) so it satisfies BufferSource.\n iv: Uint8Array<ArrayBuffer>;\n data: ArrayBuffer;\n}\n\n/**\n * Persists the session in IndexedDB, encrypted with an AES-GCM key that is\n * generated as **non-extractable** and itself stored in IndexedDB.\n *\n * The point is not that IndexedDB is inherently safer than localStorage — for\n * an XSS payload sitting on the page, both are readable. The point is that\n * the key's raw material can never be read by JavaScript at all, so the\n * tokens at rest cannot be exfiltrated and replayed later or from another\n * machine: an attacker has to stay resident on the page to use them. That\n * turns durable token theft into session-scoped abuse.\n */\nexport function indexedDbCryptoStorage<T = unknown>(): SinglebaseStorage<T> {\n let keyPromise: Promise<CryptoKey> | null = null;\n\n async function getKey(): Promise<CryptoKey> {\n if (!keyPromise) {\n keyPromise = (async () => {\n const existing = await idbRead<CryptoKey>(KEY_ID);\n if (existing) return existing;\n const key = await globalThis.crypto.subtle.generateKey(\n { name: \"AES-GCM\", length: 256 },\n // non-extractable: crypto.subtle can use it, JS can never read it\n false,\n [\"encrypt\", \"decrypt\"]\n );\n await idbWrite(KEY_ID, key);\n return key;\n })().catch((error) => {\n keyPromise = null;\n throw error;\n });\n }\n return keyPromise;\n }\n\n return {\n async get() {\n const record = await idbRead<EncryptedRecord>(DATA_ID);\n if (!record) return null;\n try {\n const key = await getKey();\n const plain = await globalThis.crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv: record.iv },\n key,\n record.data\n );\n return JSON.parse(new TextDecoder().decode(plain)) as T;\n } catch {\n // Wrong/rotated key or corrupt record — drop it rather than throw.\n await idbDelete(DATA_ID).catch(() => {});\n return null;\n }\n },\n\n async set(session) {\n const key = await getKey();\n const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));\n const data = await globalThis.crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv },\n key,\n new TextEncoder().encode(JSON.stringify(session))\n );\n await idbWrite(DATA_ID, { iv, data } satisfies EncryptedRecord);\n },\n\n async clear() {\n await idbDelete(DATA_ID).catch(() => {});\n }\n };\n}\n\n/**\n * The default adapter: encrypted IndexedDB when the environment supports it,\n * otherwise `sessionStorage`, otherwise memory. Detection is lazy and each\n * operation degrades on failure, so a blocked/absent IndexedDB (private\n * windows, disabled site data) never breaks sign-in — it just stops the\n * session surviving a reload.\n */\nexport function defaultStorage<T = unknown>(): SinglebaseStorage<T> {\n let backing: SinglebaseStorage<T> | null = null;\n let probed = false;\n\n async function resolve(): Promise<SinglebaseStorage<T>> {\n if (backing) return backing;\n if (!probed) {\n probed = true;\n if (canUseCryptoStorage()) {\n const candidate = indexedDbCryptoStorage<T>();\n try {\n // Round-trip nothing, but force the key + a transaction to open so\n // an unusable IndexedDB is discovered now rather than at signin.\n await candidate.get();\n backing = candidate;\n return backing;\n } catch {\n // fall through\n }\n }\n backing =\n typeof globalThis.sessionStorage !== \"undefined\"\n ? sessionStorageAdapter<T>()\n : memoryStorage<T>();\n }\n return backing ?? memoryStorage<T>();\n }\n\n return {\n async get() {\n try {\n return await (await resolve()).get();\n } catch {\n return null;\n }\n },\n async set(session) {\n try {\n await (await resolve()).set(session);\n } catch {\n // Persisting is best-effort; the in-memory session remains valid.\n }\n },\n async clear() {\n try {\n await (await resolve()).clear();\n } catch {\n // ignore\n }\n }\n };\n}\n","/**\n * Minimal typed pub/sub, generic over a domain's event map. Deliberately not\n * DOM-based: core has no DOM dependency so it runs in tests, workers and\n * non-browser runtimes. UI packages re-dispatch these as CustomEvents.\n */\nexport class EventEmitter<TMap> {\n private listeners = new Map<keyof TMap, Set<(detail: any) => void>>();\n\n on<K extends keyof TMap>(event: K, listener: (detail: TMap[K]) => void): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(listener);\n return () => set!.delete(listener);\n }\n\n emit<K extends keyof TMap>(event: K, detail: TMap[K]): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const listener of set) listener(detail);\n }\n}\n","/**\n * Page-wide configuration for the singlebase-authui elements.\n *\n * It lives in core rather than in the UI package so `SinglebaseClient()` can\n * accept it without the SDK taking a dependency on Lit. The UI package reads\n * it back off the client; nothing here imports or renders anything.\n *\n * Precedence is deliberately simple: **an attribute on the element wins.**\n * This is the page-level default, so one call configures every widget, and any\n * individual widget can still opt out by setting the attribute itself.\n */\nexport interface AuthUIConfig {\n // ── appearance ───────────────────────────────────────────\n theme?: \"light\" | \"dark\";\n density?: \"comfortable\" | \"compact\";\n fieldStyle?: \"outline\" | \"underline\";\n /** Corner preset: scales --sb-radius by 0.35, 1 or 1.8. */\n radius?: \"sharp\" | \"default\" | \"round\";\n\n /** `--sb-*` custom properties, e.g. { \"--sb-accent\": \"#2f5bea\" }. */\n tokens?: Record<string, string>;\n\n // ── layout & branding ────────────────────────────────────\n layout?: \"card\" | \"split\";\n oauthPlacement?: \"top\" | \"bottom\";\n stepped?: boolean;\n logoUrl?: string;\n logoText?: string;\n brandLine?: string;\n brandFoot?: string;\n signInTitle?: string;\n /** The \"Auth by Singlebase\" credit under the card. On unless set false. */\n branding?: boolean;\n /** The credit's text and link. */\n brandingText?: string;\n brandingUrl?: string;\n\n // ── screens & behaviour ──────────────────────────────────\n initialScreen?: string;\n noAccountView?: boolean;\n redirectUrl?: string;\n tosUrl?: string;\n privacyUrl?: string;\n nonceStorageKey?: string;\n\n // ── sign-in methods ──────────────────────────────────────\n authEnabled?: boolean;\n allowEmailSignin?: boolean;\n allowEmailSignup?: boolean;\n allowEmailOtp?: boolean;\n allowOauth?: boolean;\n allowAccountCreation?: boolean;\n\n // ── invite prefill ───────────────────────────────────────\n inviteEmail?: string;\n inviteOrg?: string;\n inviteCode?: string;\n\n /** Overrides for any subset of the default message strings. */\n messages?: Record<string, string>;\n}\n\n/** Config keys that are applied as element properties, not handled specially. */\nexport const AUTHUI_PROP_KEYS = [\n \"theme\",\n \"density\",\n \"fieldStyle\",\n \"radius\",\n \"layout\",\n \"oauthPlacement\",\n \"stepped\",\n \"logoUrl\",\n \"logoText\",\n \"brandLine\",\n \"brandFoot\",\n \"signInTitle\",\n \"branding\",\n \"brandingText\",\n \"brandingUrl\",\n \"initialScreen\",\n \"noAccountView\",\n \"redirectUrl\",\n \"tosUrl\",\n \"privacyUrl\",\n \"nonceStorageKey\",\n \"authEnabled\",\n \"allowEmailSignin\",\n \"allowEmailSignup\",\n \"allowEmailOtp\",\n \"allowOauth\",\n \"allowAccountCreation\",\n \"inviteEmail\",\n \"inviteOrg\",\n \"inviteCode\"\n] as const satisfies readonly (keyof AuthUIConfig)[];\n\n/** \"fieldStyle\" -> \"field-style\", for checking whether the host set it in markup. */\nexport function authUIAttributeFor(key: string): string {\n return key.replace(/[A-Z]/g, (c) => \"-\" + c.toLowerCase());\n}\n","/**\n * A deliberately small MongoDB-flavoured matcher.\n *\n * It exists so a guard can say \"render this only for an admin in the EU\"\n * declaratively, against the profile carried by the session. The scope is\n * fixed on purpose:\n *\n * - one level of operators, never nested query objects;\n * - dot notation is the *only* way to reach into a nested value;\n * - a fixed operator set, listed in OPERATORS below.\n *\n * Anything more expressive belongs in application code, not in an attribute.\n * Keeping it this small means a predicate is always cheap, always total, and\n * can never become a way to smuggle logic into markup.\n */\n\nexport const OPERATORS = [\n \"$eq\",\n \"$ne\",\n \"$in\",\n \"$nin\",\n \"$gt\",\n \"$gte\",\n \"$lt\",\n \"$lte\",\n \"$elemMatch\"\n] as const;\n\nexport type Operator = (typeof OPERATORS)[number];\n\n/** The right-hand side of one field: a bare value (implicit $eq) or an operator bag. */\nexport type Condition = unknown | Partial<Record<Operator, unknown>>;\n\n/** `{ \"metadata.tier\": { $in: [\"pro\", \"team\"] }, status: \"active\" }` */\nexport type Predicate = Record<string, Condition>;\n\nconst OPERATOR_SET = new Set<string>(OPERATORS);\n\nfunction isOperatorBag(value: unknown): value is Partial<Record<Operator, unknown>> {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) return false;\n const keys = Object.keys(value as object);\n return keys.length > 0 && keys.every((key) => OPERATOR_SET.has(key));\n}\n\n/**\n * Reads `a.b.c` off an object. Returns undefined for any missing link rather\n * than throwing — an absent field simply fails to match.\n */\nexport function getPath(source: unknown, path: string): unknown {\n let current = source;\n for (const segment of path.split(\".\")) {\n if (current === null || current === undefined) return undefined;\n if (typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[segment];\n }\n return current;\n}\n\n/** Loose structural equality, enough for the scalars and arrays a profile holds. */\nfunction equals(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((item, i) => equals(item, b[i]));\n }\n if (a && b && typeof a === \"object\" && typeof b === \"object\") {\n const aKeys = Object.keys(a as object);\n const bKeys = Object.keys(b as object);\n return (\n aKeys.length === bKeys.length &&\n aKeys.every((key) =>\n equals((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])\n )\n );\n }\n return false;\n}\n\n/**\n * Ordered comparison for $gt/$gte/$lt/$lte. Only like-typed numbers, strings\n * and dates compare; anything else is \"not comparable\", which fails the test\n * rather than coercing its way to a surprising answer.\n */\nfunction compare(left: unknown, right: unknown): number | null {\n if (typeof left === \"number\" && typeof right === \"number\") {\n return Number.isNaN(left) || Number.isNaN(right) ? null : left - right;\n }\n if (typeof left === \"string\" && typeof right === \"string\") {\n return left < right ? -1 : left > right ? 1 : 0;\n }\n if (left instanceof Date && right instanceof Date) {\n return left.getTime() - right.getTime();\n }\n return null;\n}\n\nfunction matchOperator(operator: string, actual: unknown, expected: unknown): boolean {\n switch (operator) {\n case \"$eq\":\n return equals(actual, expected);\n case \"$ne\":\n return !equals(actual, expected);\n case \"$in\":\n if (!Array.isArray(expected)) return false;\n // Mongo semantics: when the field is an array, membership is an\n // intersection test rather than whole-array equality.\n return Array.isArray(actual)\n ? actual.some((item) => expected.some((candidate) => equals(item, candidate)))\n : expected.some((candidate) => equals(actual, candidate));\n case \"$nin\":\n if (!Array.isArray(expected)) return false;\n return !matchOperator(\"$in\", actual, expected);\n case \"$gt\":\n case \"$gte\":\n case \"$lt\":\n case \"$lte\": {\n const order = compare(actual, expected);\n if (order === null) return false;\n if (operator === \"$gt\") return order > 0;\n if (operator === \"$gte\") return order >= 0;\n if (operator === \"$lt\") return order < 0;\n return order <= 0;\n }\n case \"$elemMatch\": {\n if (!Array.isArray(actual)) return false;\n // One level only: the argument is a condition applied to each element,\n // not a nested query object.\n return actual.some((item) => matchCondition(item, expected));\n }\n default:\n return false;\n }\n}\n\nfunction matchCondition(actual: unknown, condition: Condition): boolean {\n if (isOperatorBag(condition)) {\n // Several operators on one field are ANDed, as in Mongo.\n return Object.entries(condition).every(([operator, expected]) =>\n matchOperator(operator, actual, expected)\n );\n }\n return equals(actual, condition);\n}\n\n/**\n * True when `subject` satisfies every field in `predicate`. An empty or\n * missing predicate matches everything, so a guard with no filter behaves\n * exactly like one that was never given a filter.\n */\nexport function matchesPredicate(\n subject: unknown,\n predicate: Predicate | null | undefined\n): boolean {\n if (!predicate) return true;\n const entries = Object.entries(predicate);\n if (entries.length === 0) return true;\n if (subject === null || subject === undefined) return false;\n\n return entries.every(([path, condition]) => matchCondition(getPath(subject, path), condition));\n}\n\n/**\n * Reports what is wrong with a predicate, for a console warning at the point\n * of use. Returns [] when it is well-formed. Catching `{ $regex: ... }` or a\n * nested query object early is far kinder than silently never matching.\n */\nexport function validatePredicate(predicate: unknown): string[] {\n if (predicate === null || predicate === undefined) return [];\n if (typeof predicate !== \"object\" || Array.isArray(predicate)) {\n return [\"predicate must be an object\"];\n }\n\n const problems: string[] = [];\n for (const [path, condition] of Object.entries(predicate as Record<string, unknown>)) {\n if (path.startsWith(\"$\")) {\n problems.push(`\"${path}\": top-level operators are not supported; use a field name`);\n continue;\n }\n if (condition === null || typeof condition !== \"object\" || Array.isArray(condition)) continue;\n\n const keys = Object.keys(condition as object);\n const operators = keys.filter((key) => key.startsWith(\"$\"));\n\n if (operators.length === 0) {\n problems.push(\n `\"${path}\": nested objects are not supported; use dot notation, e.g. \"${path}.field\"`\n );\n continue;\n }\n if (operators.length !== keys.length) {\n problems.push(`\"${path}\": cannot mix operators with plain keys`);\n }\n for (const operator of operators) {\n if (!OPERATOR_SET.has(operator)) {\n problems.push(`\"${path}\": unsupported operator \"${operator}\"`);\n }\n }\n }\n return problems;\n}\n","/**\n * Cross-tab coordination.\n *\n * When a user signs in or out in one tab, every other tab of the same origin\n * should follow. The signal deliberately carries *no session data*: peers are\n * only told \"something changed\", and they re-read the shared storage\n * themselves. That keeps tokens out of BroadcastChannel payloads and out of\n * the localStorage fallback key entirely, so the encrypted-at-rest guarantee\n * in storage.ts is never bypassed by the sync mechanism.\n *\n * Note this only *works* when the underlying storage is itself shared between\n * tabs (IndexedDB is; sessionStorage is not). With the sessionStorage\n * fallback the peer re-reads its own private copy, finds it unchanged, and\n * does nothing — degraded, but never wrong.\n */\n\nexport type CrossTabSignal = \"session-changed\" | \"signout\";\n\nexport interface CrossTabMessage {\n signal: CrossTabSignal;\n /** Sender id, so a channel can ignore its own echo. */\n from: string;\n at: number;\n}\n\nexport interface CrossTabChannel {\n post(signal: CrossTabSignal): void;\n subscribe(listener: (message: CrossTabMessage) => void): () => void;\n close(): void;\n}\n\nconst noop = () => {};\n\n/** A channel that does nothing — used when cross-tab sync is disabled or unavailable. */\nexport function noopCrossTabChannel(): CrossTabChannel {\n return { post: noop, subscribe: () => noop, close: noop };\n}\n\nfunction newId(): string {\n const c = globalThis.crypto;\n if (c && \"randomUUID\" in c) return c.randomUUID();\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n}\n\n/**\n * Prefers BroadcastChannel; falls back to a localStorage `storage` event,\n * which fires only in *other* tabs, exactly the semantics we want. Returns a\n * no-op channel when neither is available (SSR, tests, locked-down origins).\n */\nexport function crossTabChannel(name: string): CrossTabChannel {\n const id = newId();\n\n const BC = (globalThis as { BroadcastChannel?: typeof BroadcastChannel }).BroadcastChannel;\n if (BC) {\n let channel: BroadcastChannel;\n try {\n channel = new BC(name);\n } catch {\n return storageEventChannel(name, id);\n }\n // In Node an open channel keeps the process alive; tabs don't need that.\n (channel as { unref?: () => void }).unref?.();\n return {\n post(signal) {\n try {\n channel.postMessage({ signal, from: id, at: Date.now() } satisfies CrossTabMessage);\n } catch {\n // A closed or blocked channel must never break the auth flow.\n }\n },\n subscribe(listener) {\n const handler = (event: MessageEvent) => {\n const message = event.data as CrossTabMessage | null;\n if (!message || typeof message !== \"object\") return;\n if (message.from === id) return;\n if (message.signal !== \"session-changed\" && message.signal !== \"signout\") return;\n listener(message);\n };\n channel.addEventListener(\"message\", handler);\n return () => channel.removeEventListener(\"message\", handler);\n },\n close() {\n try {\n channel.close();\n } catch {\n // already closed\n }\n }\n };\n }\n\n return storageEventChannel(name, id);\n}\n\nfunction storageEventChannel(name: string, id: string): CrossTabChannel {\n const store = safeLocalStorage();\n if (!store) return noopCrossTabChannel();\n\n const key = `${name}:signal`;\n\n return {\n post(signal) {\n try {\n store.setItem(key, JSON.stringify({ signal, from: id, at: Date.now() }));\n } catch {\n // Quota or private-mode failures are non-fatal.\n }\n },\n subscribe(listener) {\n const handler = (event: StorageEvent) => {\n if (event.key !== key || !event.newValue) return;\n try {\n const message = JSON.parse(event.newValue) as CrossTabMessage;\n if (message.from === id) return;\n if (message.signal !== \"session-changed\" && message.signal !== \"signout\") return;\n listener(message);\n } catch {\n // Ignore anything that is not one of our messages.\n }\n };\n globalThis.addEventListener?.(\"storage\", handler as EventListener);\n return () => globalThis.removeEventListener?.(\"storage\", handler as EventListener);\n },\n close: noop\n };\n}\n\nfunction safeLocalStorage(): Storage | null {\n try {\n const store = globalThis.localStorage;\n if (!store) return null;\n const probe = \"__singlebase_probe__\";\n store.setItem(probe, \"1\");\n store.removeItem(probe);\n return store;\n } catch {\n return null;\n }\n}\n","import type { SinglebaseOptions } from \"./types.js\";\nimport { DEFAULT_BASE_URL } from \"./transport.js\";\n\n/**\n * Client registry.\n *\n * A page can hold several widgets — a header sign-in, a modal, an account\n * panel — and they must all share one client: one session, one refresh\n * timer, one set of tokens. A client per widget would mean several session\n * stores and several refresh loops racing for the single-use refresh token.\n *\n * The registry lives on `globalThis` under a well-known symbol rather than in\n * module scope, because bundles duplicate modules: a page that loads the\n * widget bundle *and* imports the SDK has two copies of this file, and a\n * module-scoped `let` would give each its own \"default\" — so widgets would\n * silently never see the client the app created. Keying off globalThis makes\n * every copy, and every independently loaded script, agree.\n */\nconst REGISTRY = Symbol.for(\"singlebase.client.registry\");\n\ninterface Registry<T> {\n instances: Map<string, T>;\n default: T | null;\n}\n\nfunction registry<T>(): Registry<T> {\n const host = globalThis as unknown as Record<symbol, Registry<T> | undefined>;\n let existing = host[REGISTRY];\n if (!existing) {\n existing = { instances: new Map(), default: null };\n host[REGISTRY] = existing;\n }\n return existing;\n}\n\n/** Connection identity — clients with the same one are the same instance. */\nexport function clientKey(options: SinglebaseOptions): string {\n return [\n options.baseUrl ?? DEFAULT_BASE_URL,\n options.urlAccessKey ?? \"\",\n options.apiKey ?? \"\",\n options.audience ?? \"web\"\n ].join(\"|\");\n}\n\n/** Returns the cached client for these options, or creates and caches one. */\nexport function resolveClient<T>(options: SinglebaseOptions, create: () => T): T {\n const reg = registry<T>();\n const key = clientKey(options);\n const existing = reg.instances.get(key);\n if (existing) return existing;\n\n const client = create();\n reg.instances.set(key, client);\n if (!reg.default) reg.default = client;\n return client;\n}\n\n/** The client widgets fall back to when none is set on them. */\nexport function getDefaultClient<T = unknown>(): T | null {\n return registry<T>().default;\n}\n\n/** Overrides the page default (useful when several projects are in play). */\nexport function setDefaultClient<T>(client: T | null): void {\n registry<T>().default = client;\n}\n\n/** Drops a client from the registry. */\nexport function unregisterClient<T>(client: T): void {\n const reg = registry<T>();\n for (const [key, value] of reg.instances) {\n if (value === client) reg.instances.delete(key);\n }\n if (reg.default === client) {\n reg.default = reg.instances.values().next().value ?? null;\n }\n}\n"],"names":["SinglebaseError","payload","cause","ERROR_HINTS","mapErrorToHint","code","isLocalHost","hostname","DEFAULT_BASE_URL","resolveBaseUrl","options","endpointFor","_a","base","assertSecureBaseUrl","key","baseUrl","url","request","clientOptions","operation","extras","envelope","headers","fetchImpl","res","json","RETRYABLE_AUTH_CODES","RpcDispatcher","bridge","provider","bearer","signal","explicit","token","error","refreshed","DB_NAME","DB_VERSION","STORE","KEY_ID","DATA_ID","memoryStorage","current","session","webStorageAdapter","getStorage","raw","localStorageAdapter","sessionStorageAdapter","idbRequest","resolve","reject","openDb","idbRead","id","db","tx","idbWrite","value","idbDelete","canUseCryptoStorage","indexedDbCryptoStorage","keyPromise","getKey","existing","record","plain","iv","data","defaultStorage","backing","probed","candidate","EventEmitter","event","listener","set","detail","AUTHUI_PROP_KEYS","authUIAttributeFor","c","OPERATORS","OPERATOR_SET","isOperatorBag","keys","getPath","source","path","segment","equals","a","b","item","i","aKeys","bKeys","compare","left","right","matchOperator","operator","actual","expected","order","matchCondition","condition","matchesPredicate","subject","predicate","entries","validatePredicate","problems","operators","noop","noopCrossTabChannel","newId","crossTabChannel","name","BC","channel","storageEventChannel","handler","message","store","safeLocalStorage","probe","REGISTRY","registry","host","clientKey","resolveClient","create","reg","client","getDefaultClient","setDefaultClient","unregisterClient"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/errors.ts","../src/transport.ts","../src/dispatcher.ts","../src/storage.ts","../src/events.ts","../src/authui-config.ts","../src/predicate.ts","../src/cross-tab.ts","../src/registry.ts"],"sourcesContent":["import type { ErrorResponsePayload } from \"./types.js\";\n\n/**\n * Normalized error thrown by every SDK method. `code` is the server's\n * machine-readable `error.message` (e.g. \"INVALID_CREDENTIALS\"), never the\n * human-readable text.\n */\nexport class SinglebaseError extends Error {\n status: number;\n type: string;\n code: string;\n details?: unknown;\n traceId?: string | null;\n override cause?: unknown;\n\n constructor(payload: ErrorResponsePayload, cause?: unknown) {\n super(payload.message);\n this.name = \"SinglebaseError\";\n this.status = payload.status;\n this.type = payload.type;\n this.code = payload.message;\n this.details = payload.details;\n this.traceId = payload.trace_id ?? null;\n this.cause = cause;\n }\n\n static fromNetworkError(cause: unknown): SinglebaseError {\n return new SinglebaseError(\n {\n type: \"NETWORK_ERROR\",\n status: 0,\n message: \"NETWORK_ERROR\"\n },\n cause\n );\n }\n}\n\n/**\n * Recommended UI behavior for a given server error code, taken verbatim from\n * the auth API's error-mapping table. UI components use this to decide\n * *behavior* (focus a field, trigger a refresh, sign out locally); the actual\n * copy shown to the user is left to each component/its `.messages` override.\n */\nexport type ErrorHint =\n | \"highlight_invalid_fields\"\n | \"generic_credentials_message\"\n | \"reveal_code_input\"\n | \"expired_or_invalid_code_message\"\n | \"attempt_refresh_or_signout\"\n | \"clear_session_and_ask_signin\"\n | \"clear_session_config_error\"\n | \"route_to_recovery_or_invite\"\n | \"show_password_requirements\"\n | \"require_changed_field\"\n | \"disable_retry_show_rate_limited\"\n | \"render_auth_unavailable\"\n | \"hide_email_forms\"\n | \"hide_signup\"\n | \"hide_oauth\"\n | \"remove_oauth_from_signin\"\n | \"remove_oauth_from_signup\"\n | \"refresh_settings_hide_provider\"\n | \"restart_oauth_new_nonce\"\n | \"restart_oauth\"\n | \"explain_verified_email_required\"\n | \"ask_signin_then_link\"\n | \"provider_already_linked\"\n | \"route_to_signin\"\n | \"show_integrator_config_error\"\n | \"unknown\";\n\nconst ERROR_HINTS: Record<string, ErrorHint> = {\n INVALID_PAYLOAD: \"highlight_invalid_fields\",\n INVALID_CREDENTIALS: \"generic_credentials_message\",\n CODE_REQUIRED: \"reveal_code_input\",\n INVALID_TOKEN: \"expired_or_invalid_code_message\",\n INVALID_ID_TOKEN: \"attempt_refresh_or_signout\",\n INVALID_BEARER_TOKEN: \"attempt_refresh_or_signout\",\n INVALID_SESSION: \"clear_session_and_ask_signin\",\n INVALID_TOKEN_AUDIENCE: \"clear_session_config_error\",\n REQUIRE_PASSWORD_CHANGE: \"route_to_recovery_or_invite\",\n INVALID_PASSWORD: \"show_password_requirements\",\n MISSING_PROFILE_DATA: \"require_changed_field\",\n AUTH_RATE_LIMITED: \"disable_retry_show_rate_limited\",\n AUTH_DISABLED: \"render_auth_unavailable\",\n EMAIL_PROVIDER_DISABLED: \"hide_email_forms\",\n EMAIL_SIGNUP_DISABLED: \"hide_signup\",\n OAUTH_DISABLED: \"hide_oauth\",\n OAUTH_SIGNIN_DISABLED: \"remove_oauth_from_signin\",\n OAUTH_SIGNUP_DISABLED: \"remove_oauth_from_signup\",\n INVALID_OAUTH_PROVIDER: \"refresh_settings_hide_provider\",\n MISSING_OAUTH_CREDENTIALS: \"show_integrator_config_error\",\n OAUTH_VERIFICATION_FAILED: \"restart_oauth\",\n VERIFIED_PROVIDER_EMAIL_REQUIRED: \"explain_verified_email_required\",\n INVALID_NONCE: \"restart_oauth_new_nonce\",\n SIGN_IN_TO_LINK_PROVIDER: \"ask_signin_then_link\",\n PROVIDER_ALREADY_LINKED: \"provider_already_linked\",\n AUTHENTICATION_REQUIRED: \"route_to_signin\",\n UNAUTHORIZED_DOMAIN: \"show_integrator_config_error\"\n};\n\nexport function mapErrorToHint(code: string): ErrorHint {\n return ERROR_HINTS[code] ?? \"unknown\";\n}\n\n/** Back-compat alias: the error type is shared by every service. */\nexport { SinglebaseError as SinglebaseAuthError };\n","import { SinglebaseError } from \"./errors.js\";\nimport type {\n SinglebaseOptions,\n ErrorResponse,\n RequestEnvelope,\n SuccessResponse\n} from \"./types.js\";\n\nexport interface RequestExtras {\n token?: string | null;\n signal?: AbortSignal;\n options?: Record<string, unknown>;\n}\n\nfunction isLocalHost(hostname: string): boolean {\n return hostname === \"localhost\" || hostname === \"127.0.0.1\" || hostname === \"0.0.0.0\";\n}\n\n/** Where requests go when `baseUrl` is not given. */\nexport const DEFAULT_BASE_URL = \"https://v1.api.singlebase.io\";\n\n/**\n * The effective API root: the default when `baseUrl` is omitted. An explicit\n * null or empty value is a configuration mistake, and is reported as one\n * rather than silently replaced.\n */\nexport function resolveBaseUrl(options: Pick<SinglebaseOptions, \"baseUrl\">): string {\n if (options.baseUrl === undefined) return DEFAULT_BASE_URL;\n if (options.baseUrl === null || String(options.baseUrl).trim() === \"\") {\n throw new Error(\n `SinglebaseOptions.baseUrl cannot be null or empty. Omit it to use ${DEFAULT_BASE_URL}.`\n );\n }\n return String(options.baseUrl).trim().replace(/\\/+$/, \"\");\n}\n\n/**\n * The URL every request is POSTed to: `{baseUrl}/{urlAccessKey}`, or just\n * `{baseUrl}` when there is no access key.\n */\nexport function endpointFor(options: Pick<SinglebaseOptions, \"baseUrl\" | \"urlAccessKey\">): string {\n const base = resolveBaseUrl(options);\n assertSecureBaseUrl(base);\n const key = options.urlAccessKey?.trim();\n return key ? `${base}/${encodeURIComponent(key)}` : base;\n}\n\n/** Enforces the spec's HTTPS-outside-localhost rule for the configured baseUrl. */\nexport function assertSecureBaseUrl(baseUrl: string): void {\n let url: URL;\n try {\n url = new URL(baseUrl);\n } catch {\n throw new Error(`SinglebaseOptions.baseUrl is not a valid URL: ${baseUrl}`);\n }\n if (url.protocol !== \"https:\" && !isLocalHost(url.hostname)) {\n throw new Error(\n `SinglebaseOptions.baseUrl must use HTTPS outside of localhost/127.0.0.1/0.0.0.0 (got ${url.protocol})`\n );\n }\n}\n\n/**\n * Low-level RPC request. Returns the parsed `data` on success and throws\n * `SinglebaseError` on a well-formed error response or a network/transport\n * failure. Never leaks tokens into thrown error objects, logs, or URLs.\n */\nexport async function request<TData = unknown, TPayload = unknown>(\n clientOptions: SinglebaseOptions,\n operation: string,\n payload: TPayload,\n extras: RequestExtras = {}\n): Promise<TData> {\n const url = endpointFor(clientOptions);\n\n const envelope: RequestEnvelope<TPayload> = { operation, payload };\n if (extras.options) envelope.options = extras.options;\n\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (clientOptions.apiKey) headers[\"X-API-Key\"] = clientOptions.apiKey;\n if (extras.token) {\n headers.Authorization = `Bearer ${extras.token}`;\n }\n\n const fetchImpl = clientOptions.fetch ?? globalThis.fetch;\n\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(envelope),\n signal: extras.signal\n });\n } catch (cause) {\n throw SinglebaseError.fromNetworkError(cause);\n }\n\n let json: SuccessResponse<TData> | ErrorResponse;\n try {\n json = await res.json();\n } catch (cause) {\n throw SinglebaseError.fromNetworkError(cause);\n }\n\n if (\"error\" in json) {\n throw new SinglebaseError(json.error);\n }\n return json.data;\n}\n","import { SinglebaseError } from \"./errors.js\";\nimport { request } from \"./transport.js\";\nimport type { SinglebaseOptions } from \"./types.js\";\n\n/** Server error codes that mean \"your bearer token is stale, try refreshing\". */\nconst RETRYABLE_AUTH_CODES = new Set([\"INVALID_ID_TOKEN\", \"INVALID_BEARER_TOKEN\"]);\n\nexport interface DispatchEnvelope<TPayload = unknown> {\n operation: string;\n payload?: TPayload;\n options?: Record<string, unknown>;\n}\n\nexport interface DispatchOptions {\n /**\n * Explicit bearer token. Omit to let the dispatcher inject the current\n * session's ID token; pass `null` to force an unauthenticated call.\n */\n bearer?: string | null;\n signal?: AbortSignal;\n}\n\nexport interface DispatcherAuthBridge {\n /** Current ID token, or null when signed out. */\n getToken(): string | null;\n /** Refresh once and return the new ID token, or null if unrecoverable. */\n refresh(): Promise<string | null>;\n}\n\n/**\n * Supplies the auth bridge on demand. Returning a promise lets auth be\n * *constructed lazily* — a page that never signs in pays nothing — while\n * still being *hydrated eagerly* before the first call that could carry a\n * token. Without this, a `data.query()` fired on page load would race the\n * session restore and silently go out unauthenticated.\n */\nexport type AuthBridgeProvider = () => Promise<DispatcherAuthBridge | null>;\n\n/**\n * One RPC pipe shared by every Singlebase service.\n *\n * Auth and every service client go through this, so there is a single place\n * that knows the envelope shape, the endpoint, how a bearer is attached, and\n * how a stale token is recovered.\n */\nexport class RpcDispatcher {\n private bridge: DispatcherAuthBridge | null = null;\n private provider: AuthBridgeProvider | null = null;\n\n constructor(readonly options: SinglebaseOptions) {}\n\n /** Registers the session owner (the auth client) as the token source. */\n setAuthBridge(bridge: DispatcherAuthBridge | null): void {\n this.bridge = bridge;\n }\n\n /** Registers a factory that builds+hydrates the token source on first need. */\n setAuthProvider(provider: AuthBridgeProvider | null): void {\n this.provider = provider;\n }\n\n private async resolveBridge(): Promise<DispatcherAuthBridge | null> {\n if (this.bridge) return this.bridge;\n if (!this.provider) return null;\n this.bridge = await this.provider();\n return this.bridge;\n }\n\n /**\n * Sends one operation. Injects the session bearer unless told otherwise,\n * and on a stale-token error refreshes once and retries the call exactly\n * once — never looping.\n */\n async dispatch<TResult = unknown, TPayload = unknown>(\n envelope: DispatchEnvelope<TPayload>,\n { bearer, signal }: DispatchOptions = {}\n ): Promise<TResult> {\n const explicit = bearer !== undefined;\n const bridge = explicit ? null : await this.resolveBridge();\n const token = explicit ? bearer : (bridge?.getToken() ?? null);\n\n const extras = { token, signal, options: envelope.options };\n\n try {\n return await request<TResult, TPayload>(\n this.options,\n envelope.operation,\n (envelope.payload ?? {}) as TPayload,\n extras\n );\n } catch (error) {\n const code = error instanceof SinglebaseError ? error.code : \"\";\n const canRetry =\n !explicit && token !== null && bridge !== null && RETRYABLE_AUTH_CODES.has(code);\n if (!canRetry) throw error;\n\n const refreshed = await bridge!.refresh();\n if (!refreshed) throw error;\n\n return request<TResult, TPayload>(\n this.options,\n envelope.operation,\n (envelope.payload ?? {}) as TPayload,\n { ...extras, token: refreshed }\n );\n }\n }\n}\n","import type { SinglebaseStorage } from \"./types.js\";\n\nconst DB_NAME = \"singlebase-auth\";\nconst DB_VERSION = 1;\nconst STORE = \"session\";\nconst KEY_ID = \"wrapping-key\";\nconst DATA_ID = \"session\";\n\n/** In-memory storage. Never persisted — safest, but the session ends on reload. */\nexport function memoryStorage<T = unknown>(): SinglebaseStorage<T> {\n let current: T | null = null;\n return {\n get: () => current,\n set: (session) => {\n current = session;\n },\n clear: () => {\n current = null;\n }\n };\n}\n\nfunction webStorageAdapter<T>(\n getStorage: () => Storage | undefined,\n key: string\n): SinglebaseStorage<T> {\n return {\n get: () => {\n const raw = getStorage()?.getItem(key);\n if (!raw) return null;\n try {\n return JSON.parse(raw) as T;\n } catch {\n return null;\n }\n },\n set: (session) => {\n getStorage()?.setItem(key, JSON.stringify(session));\n },\n clear: () => {\n getStorage()?.removeItem(key);\n }\n };\n}\n\n/**\n * Persists the session in `localStorage` as plain text. Survives reloads and\n * is shared across tabs, but any script on the origin — including an XSS\n * payload — can read the tokens verbatim. Choose this deliberately.\n */\nexport function localStorageAdapter<T = unknown>(\n key = \"singlebase-auth-session\"\n): SinglebaseStorage<T> {\n return webStorageAdapter(() => globalThis.localStorage, key);\n}\n\n/** Persists the session in `sessionStorage` — plain text, cleared with the tab. */\nexport function sessionStorageAdapter<T = unknown>(\n key = \"singlebase-auth-session\"\n): SinglebaseStorage<T> {\n return webStorageAdapter(() => globalThis.sessionStorage, key);\n}\n\n// ---------------------------------------------------------------------------\n// IndexedDB + non-extractable CryptoKey\n// ---------------------------------------------------------------------------\n\nfunction idbRequest<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n}\n\nfunction openDb(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request = globalThis.indexedDB.open(DB_NAME, DB_VERSION);\n request.onupgradeneeded = () => {\n if (!request.result.objectStoreNames.contains(STORE)) {\n request.result.createObjectStore(STORE);\n }\n };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n request.onblocked = () => reject(new Error(\"indexedDB open blocked\"));\n });\n}\n\nasync function idbRead<T>(id: string): Promise<T | undefined> {\n const db = await openDb();\n try {\n const tx = db.transaction(STORE, \"readonly\");\n return await idbRequest<T>(tx.objectStore(STORE).get(id) as IDBRequest<T>);\n } finally {\n db.close();\n }\n}\n\nasync function idbWrite(id: string, value: unknown): Promise<void> {\n const db = await openDb();\n try {\n const tx = db.transaction(STORE, \"readwrite\");\n await idbRequest(tx.objectStore(STORE).put(value, id));\n } finally {\n db.close();\n }\n}\n\nasync function idbDelete(id: string): Promise<void> {\n const db = await openDb();\n try {\n const tx = db.transaction(STORE, \"readwrite\");\n await idbRequest(tx.objectStore(STORE).delete(id));\n } finally {\n db.close();\n }\n}\n\n/** True when this environment can do IndexedDB + WebCrypto (a secure context). */\nexport function canUseCryptoStorage(): boolean {\n return (\n typeof globalThis.indexedDB !== \"undefined\" &&\n typeof globalThis.crypto !== \"undefined\" &&\n typeof globalThis.crypto.subtle !== \"undefined\"\n );\n}\n\ninterface EncryptedRecord {\n // Pinned to ArrayBuffer (not ArrayBufferLike) so it satisfies BufferSource.\n iv: Uint8Array<ArrayBuffer>;\n data: ArrayBuffer;\n}\n\n/**\n * Persists the session in IndexedDB, encrypted with an AES-GCM key that is\n * generated as **non-extractable** and itself stored in IndexedDB.\n *\n * The point is not that IndexedDB is inherently safer than localStorage — for\n * an XSS payload sitting on the page, both are readable. The point is that\n * the key's raw material can never be read by JavaScript at all, so the\n * tokens at rest cannot be exfiltrated and replayed later or from another\n * machine: an attacker has to stay resident on the page to use them. That\n * turns durable token theft into session-scoped abuse.\n */\nexport function indexedDbCryptoStorage<T = unknown>(): SinglebaseStorage<T> {\n let keyPromise: Promise<CryptoKey> | null = null;\n\n async function getKey(): Promise<CryptoKey> {\n if (!keyPromise) {\n keyPromise = (async () => {\n const existing = await idbRead<CryptoKey>(KEY_ID);\n if (existing) return existing;\n const key = await globalThis.crypto.subtle.generateKey(\n { name: \"AES-GCM\", length: 256 },\n // non-extractable: crypto.subtle can use it, JS can never read it\n false,\n [\"encrypt\", \"decrypt\"]\n );\n await idbWrite(KEY_ID, key);\n return key;\n })().catch((error) => {\n keyPromise = null;\n throw error;\n });\n }\n return keyPromise;\n }\n\n return {\n async get() {\n const record = await idbRead<EncryptedRecord>(DATA_ID);\n if (!record) return null;\n try {\n const key = await getKey();\n const plain = await globalThis.crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv: record.iv },\n key,\n record.data\n );\n return JSON.parse(new TextDecoder().decode(plain)) as T;\n } catch {\n // Wrong/rotated key or corrupt record — drop it rather than throw.\n await idbDelete(DATA_ID).catch(() => {});\n return null;\n }\n },\n\n async set(session) {\n const key = await getKey();\n const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));\n const data = await globalThis.crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv },\n key,\n new TextEncoder().encode(JSON.stringify(session))\n );\n await idbWrite(DATA_ID, { iv, data } satisfies EncryptedRecord);\n },\n\n async clear() {\n await idbDelete(DATA_ID).catch(() => {});\n }\n };\n}\n\n/**\n * The default adapter: encrypted IndexedDB when the environment supports it,\n * otherwise `sessionStorage`, otherwise memory. Detection is lazy and each\n * operation degrades on failure, so a blocked/absent IndexedDB (private\n * windows, disabled site data) never breaks sign-in — it just stops the\n * session surviving a reload.\n */\nexport function defaultStorage<T = unknown>(): SinglebaseStorage<T> {\n let backing: SinglebaseStorage<T> | null = null;\n let probed = false;\n\n async function resolve(): Promise<SinglebaseStorage<T>> {\n if (backing) return backing;\n if (!probed) {\n probed = true;\n if (canUseCryptoStorage()) {\n const candidate = indexedDbCryptoStorage<T>();\n try {\n // Round-trip nothing, but force the key + a transaction to open so\n // an unusable IndexedDB is discovered now rather than at signin.\n await candidate.get();\n backing = candidate;\n return backing;\n } catch {\n // fall through\n }\n }\n backing =\n typeof globalThis.sessionStorage !== \"undefined\"\n ? sessionStorageAdapter<T>()\n : memoryStorage<T>();\n }\n return backing ?? memoryStorage<T>();\n }\n\n return {\n async get() {\n try {\n return await (await resolve()).get();\n } catch {\n return null;\n }\n },\n async set(session) {\n try {\n await (await resolve()).set(session);\n } catch {\n // Persisting is best-effort; the in-memory session remains valid.\n }\n },\n async clear() {\n try {\n await (await resolve()).clear();\n } catch {\n // ignore\n }\n }\n };\n}\n","/**\n * Minimal typed pub/sub, generic over a domain's event map. Deliberately not\n * DOM-based: core has no DOM dependency so it runs in tests, workers and\n * non-browser runtimes. UI packages re-dispatch these as CustomEvents.\n */\nexport class EventEmitter<TMap> {\n private listeners = new Map<keyof TMap, Set<(detail: any) => void>>();\n\n on<K extends keyof TMap>(event: K, listener: (detail: TMap[K]) => void): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(listener);\n return () => set!.delete(listener);\n }\n\n emit<K extends keyof TMap>(event: K, detail: TMap[K]): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const listener of set) listener(detail);\n }\n}\n","/**\n * Page-wide configuration for the singlebase-authui elements.\n *\n * It lives in core rather than in the UI package so `SinglebaseClient()` can\n * accept it without the SDK taking a dependency on Lit. The UI package reads\n * it back off the client; nothing here imports or renders anything.\n *\n * Precedence is deliberately simple: **an attribute on the element wins.**\n * This is the page-level default, so one call configures every widget, and any\n * individual widget can still opt out by setting the attribute itself.\n */\nexport interface AuthUIConfig {\n // ── appearance ───────────────────────────────────────────\n theme?: \"light\" | \"dark\";\n density?: \"comfortable\" | \"compact\";\n fieldStyle?: \"outline\" | \"underline\";\n /** Corner preset: scales --sb-radius by 0.35, 1 or 1.8. */\n radius?: \"sharp\" | \"default\" | \"round\";\n\n /** `--sb-*` custom properties, e.g. { \"--sb-accent\": \"#2f5bea\" }. */\n tokens?: Record<string, string>;\n\n // ── layout & branding ────────────────────────────────────\n layout?: \"card\" | \"split\";\n oauthPlacement?: \"top\" | \"bottom\";\n stepped?: boolean;\n logoUrl?: string;\n logoText?: string;\n brandLine?: string;\n brandFoot?: string;\n signInTitle?: string;\n /** The \"Auth by Singlebase\" credit under the card. On unless set false. */\n branding?: boolean;\n\n // ── screens & behaviour ──────────────────────────────────\n initialScreen?: string;\n noAccountView?: boolean;\n redirectUrl?: string;\n tosUrl?: string;\n privacyUrl?: string;\n nonceStorageKey?: string;\n\n // ── sign-in methods ──────────────────────────────────────\n authEnabled?: boolean;\n allowEmailSignin?: boolean;\n allowEmailSignup?: boolean;\n allowEmailOtp?: boolean;\n allowOauth?: boolean;\n allowAccountCreation?: boolean;\n\n // ── invite prefill ───────────────────────────────────────\n inviteEmail?: string;\n inviteOrg?: string;\n inviteCode?: string;\n\n /** Overrides for any subset of the default message strings. */\n messages?: Record<string, string>;\n}\n\n/** Config keys that are applied as element properties, not handled specially. */\nexport const AUTHUI_PROP_KEYS = [\n \"theme\",\n \"density\",\n \"fieldStyle\",\n \"radius\",\n \"layout\",\n \"oauthPlacement\",\n \"stepped\",\n \"logoUrl\",\n \"logoText\",\n \"brandLine\",\n \"brandFoot\",\n \"signInTitle\",\n \"branding\",\n \"initialScreen\",\n \"noAccountView\",\n \"redirectUrl\",\n \"tosUrl\",\n \"privacyUrl\",\n \"nonceStorageKey\",\n \"authEnabled\",\n \"allowEmailSignin\",\n \"allowEmailSignup\",\n \"allowEmailOtp\",\n \"allowOauth\",\n \"allowAccountCreation\",\n \"inviteEmail\",\n \"inviteOrg\",\n \"inviteCode\"\n] as const satisfies readonly (keyof AuthUIConfig)[];\n\n/** \"fieldStyle\" -> \"field-style\", for checking whether the host set it in markup. */\nexport function authUIAttributeFor(key: string): string {\n return key.replace(/[A-Z]/g, (c) => \"-\" + c.toLowerCase());\n}\n","/**\n * A deliberately small MongoDB-flavoured matcher.\n *\n * It exists so a guard can say \"render this only for an admin in the EU\"\n * declaratively, against the profile carried by the session. The scope is\n * fixed on purpose:\n *\n * - one level of operators, never nested query objects;\n * - dot notation is the *only* way to reach into a nested value;\n * - a fixed operator set, listed in OPERATORS below.\n *\n * Anything more expressive belongs in application code, not in an attribute.\n * Keeping it this small means a predicate is always cheap, always total, and\n * can never become a way to smuggle logic into markup.\n */\n\nexport const OPERATORS = [\n \"$eq\",\n \"$ne\",\n \"$in\",\n \"$nin\",\n \"$gt\",\n \"$gte\",\n \"$lt\",\n \"$lte\",\n \"$elemMatch\"\n] as const;\n\nexport type Operator = (typeof OPERATORS)[number];\n\n/** The right-hand side of one field: a bare value (implicit $eq) or an operator bag. */\nexport type Condition = unknown | Partial<Record<Operator, unknown>>;\n\n/** `{ \"metadata.tier\": { $in: [\"pro\", \"team\"] }, status: \"active\" }` */\nexport type Predicate = Record<string, Condition>;\n\nconst OPERATOR_SET = new Set<string>(OPERATORS);\n\nfunction isOperatorBag(value: unknown): value is Partial<Record<Operator, unknown>> {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) return false;\n const keys = Object.keys(value as object);\n return keys.length > 0 && keys.every((key) => OPERATOR_SET.has(key));\n}\n\n/**\n * Reads `a.b.c` off an object. Returns undefined for any missing link rather\n * than throwing — an absent field simply fails to match.\n */\nexport function getPath(source: unknown, path: string): unknown {\n let current = source;\n for (const segment of path.split(\".\")) {\n if (current === null || current === undefined) return undefined;\n if (typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[segment];\n }\n return current;\n}\n\n/** Loose structural equality, enough for the scalars and arrays a profile holds. */\nfunction equals(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((item, i) => equals(item, b[i]));\n }\n if (a && b && typeof a === \"object\" && typeof b === \"object\") {\n const aKeys = Object.keys(a as object);\n const bKeys = Object.keys(b as object);\n return (\n aKeys.length === bKeys.length &&\n aKeys.every((key) =>\n equals((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])\n )\n );\n }\n return false;\n}\n\n/**\n * Ordered comparison for $gt/$gte/$lt/$lte. Only like-typed numbers, strings\n * and dates compare; anything else is \"not comparable\", which fails the test\n * rather than coercing its way to a surprising answer.\n */\nfunction compare(left: unknown, right: unknown): number | null {\n if (typeof left === \"number\" && typeof right === \"number\") {\n return Number.isNaN(left) || Number.isNaN(right) ? null : left - right;\n }\n if (typeof left === \"string\" && typeof right === \"string\") {\n return left < right ? -1 : left > right ? 1 : 0;\n }\n if (left instanceof Date && right instanceof Date) {\n return left.getTime() - right.getTime();\n }\n return null;\n}\n\nfunction matchOperator(operator: string, actual: unknown, expected: unknown): boolean {\n switch (operator) {\n case \"$eq\":\n return equals(actual, expected);\n case \"$ne\":\n return !equals(actual, expected);\n case \"$in\":\n if (!Array.isArray(expected)) return false;\n // Mongo semantics: when the field is an array, membership is an\n // intersection test rather than whole-array equality.\n return Array.isArray(actual)\n ? actual.some((item) => expected.some((candidate) => equals(item, candidate)))\n : expected.some((candidate) => equals(actual, candidate));\n case \"$nin\":\n if (!Array.isArray(expected)) return false;\n return !matchOperator(\"$in\", actual, expected);\n case \"$gt\":\n case \"$gte\":\n case \"$lt\":\n case \"$lte\": {\n const order = compare(actual, expected);\n if (order === null) return false;\n if (operator === \"$gt\") return order > 0;\n if (operator === \"$gte\") return order >= 0;\n if (operator === \"$lt\") return order < 0;\n return order <= 0;\n }\n case \"$elemMatch\": {\n if (!Array.isArray(actual)) return false;\n // One level only: the argument is a condition applied to each element,\n // not a nested query object.\n return actual.some((item) => matchCondition(item, expected));\n }\n default:\n return false;\n }\n}\n\nfunction matchCondition(actual: unknown, condition: Condition): boolean {\n if (isOperatorBag(condition)) {\n // Several operators on one field are ANDed, as in Mongo.\n return Object.entries(condition).every(([operator, expected]) =>\n matchOperator(operator, actual, expected)\n );\n }\n return equals(actual, condition);\n}\n\n/**\n * True when `subject` satisfies every field in `predicate`. An empty or\n * missing predicate matches everything, so a guard with no filter behaves\n * exactly like one that was never given a filter.\n */\nexport function matchesPredicate(\n subject: unknown,\n predicate: Predicate | null | undefined\n): boolean {\n if (!predicate) return true;\n const entries = Object.entries(predicate);\n if (entries.length === 0) return true;\n if (subject === null || subject === undefined) return false;\n\n return entries.every(([path, condition]) => matchCondition(getPath(subject, path), condition));\n}\n\n/**\n * Reports what is wrong with a predicate, for a console warning at the point\n * of use. Returns [] when it is well-formed. Catching `{ $regex: ... }` or a\n * nested query object early is far kinder than silently never matching.\n */\nexport function validatePredicate(predicate: unknown): string[] {\n if (predicate === null || predicate === undefined) return [];\n if (typeof predicate !== \"object\" || Array.isArray(predicate)) {\n return [\"predicate must be an object\"];\n }\n\n const problems: string[] = [];\n for (const [path, condition] of Object.entries(predicate as Record<string, unknown>)) {\n if (path.startsWith(\"$\")) {\n problems.push(`\"${path}\": top-level operators are not supported; use a field name`);\n continue;\n }\n if (condition === null || typeof condition !== \"object\" || Array.isArray(condition)) continue;\n\n const keys = Object.keys(condition as object);\n const operators = keys.filter((key) => key.startsWith(\"$\"));\n\n if (operators.length === 0) {\n problems.push(\n `\"${path}\": nested objects are not supported; use dot notation, e.g. \"${path}.field\"`\n );\n continue;\n }\n if (operators.length !== keys.length) {\n problems.push(`\"${path}\": cannot mix operators with plain keys`);\n }\n for (const operator of operators) {\n if (!OPERATOR_SET.has(operator)) {\n problems.push(`\"${path}\": unsupported operator \"${operator}\"`);\n }\n }\n }\n return problems;\n}\n","/**\n * Cross-tab coordination.\n *\n * When a user signs in or out in one tab, every other tab of the same origin\n * should follow. The signal deliberately carries *no session data*: peers are\n * only told \"something changed\", and they re-read the shared storage\n * themselves. That keeps tokens out of BroadcastChannel payloads and out of\n * the localStorage fallback key entirely, so the encrypted-at-rest guarantee\n * in storage.ts is never bypassed by the sync mechanism.\n *\n * Note this only *works* when the underlying storage is itself shared between\n * tabs (IndexedDB is; sessionStorage is not). With the sessionStorage\n * fallback the peer re-reads its own private copy, finds it unchanged, and\n * does nothing — degraded, but never wrong.\n */\n\nexport type CrossTabSignal = \"session-changed\" | \"signout\";\n\nexport interface CrossTabMessage {\n signal: CrossTabSignal;\n /** Sender id, so a channel can ignore its own echo. */\n from: string;\n at: number;\n}\n\nexport interface CrossTabChannel {\n post(signal: CrossTabSignal): void;\n subscribe(listener: (message: CrossTabMessage) => void): () => void;\n close(): void;\n}\n\nconst noop = () => {};\n\n/** A channel that does nothing — used when cross-tab sync is disabled or unavailable. */\nexport function noopCrossTabChannel(): CrossTabChannel {\n return { post: noop, subscribe: () => noop, close: noop };\n}\n\nfunction newId(): string {\n const c = globalThis.crypto;\n if (c && \"randomUUID\" in c) return c.randomUUID();\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n}\n\n/**\n * Prefers BroadcastChannel; falls back to a localStorage `storage` event,\n * which fires only in *other* tabs, exactly the semantics we want. Returns a\n * no-op channel when neither is available (SSR, tests, locked-down origins).\n */\nexport function crossTabChannel(name: string): CrossTabChannel {\n const id = newId();\n\n const BC = (globalThis as { BroadcastChannel?: typeof BroadcastChannel }).BroadcastChannel;\n if (BC) {\n let channel: BroadcastChannel;\n try {\n channel = new BC(name);\n } catch {\n return storageEventChannel(name, id);\n }\n // In Node an open channel keeps the process alive; tabs don't need that.\n (channel as { unref?: () => void }).unref?.();\n return {\n post(signal) {\n try {\n channel.postMessage({ signal, from: id, at: Date.now() } satisfies CrossTabMessage);\n } catch {\n // A closed or blocked channel must never break the auth flow.\n }\n },\n subscribe(listener) {\n const handler = (event: MessageEvent) => {\n const message = event.data as CrossTabMessage | null;\n if (!message || typeof message !== \"object\") return;\n if (message.from === id) return;\n if (message.signal !== \"session-changed\" && message.signal !== \"signout\") return;\n listener(message);\n };\n channel.addEventListener(\"message\", handler);\n return () => channel.removeEventListener(\"message\", handler);\n },\n close() {\n try {\n channel.close();\n } catch {\n // already closed\n }\n }\n };\n }\n\n return storageEventChannel(name, id);\n}\n\nfunction storageEventChannel(name: string, id: string): CrossTabChannel {\n const store = safeLocalStorage();\n if (!store) return noopCrossTabChannel();\n\n const key = `${name}:signal`;\n\n return {\n post(signal) {\n try {\n store.setItem(key, JSON.stringify({ signal, from: id, at: Date.now() }));\n } catch {\n // Quota or private-mode failures are non-fatal.\n }\n },\n subscribe(listener) {\n const handler = (event: StorageEvent) => {\n if (event.key !== key || !event.newValue) return;\n try {\n const message = JSON.parse(event.newValue) as CrossTabMessage;\n if (message.from === id) return;\n if (message.signal !== \"session-changed\" && message.signal !== \"signout\") return;\n listener(message);\n } catch {\n // Ignore anything that is not one of our messages.\n }\n };\n globalThis.addEventListener?.(\"storage\", handler as EventListener);\n return () => globalThis.removeEventListener?.(\"storage\", handler as EventListener);\n },\n close: noop\n };\n}\n\nfunction safeLocalStorage(): Storage | null {\n try {\n const store = globalThis.localStorage;\n if (!store) return null;\n const probe = \"__singlebase_probe__\";\n store.setItem(probe, \"1\");\n store.removeItem(probe);\n return store;\n } catch {\n return null;\n }\n}\n","import type { SinglebaseOptions } from \"./types.js\";\nimport { DEFAULT_BASE_URL } from \"./transport.js\";\n\n/**\n * Client registry.\n *\n * A page can hold several widgets — a header sign-in, a modal, an account\n * panel — and they must all share one client: one session, one refresh\n * timer, one set of tokens. A client per widget would mean several session\n * stores and several refresh loops racing for the single-use refresh token.\n *\n * The registry lives on `globalThis` under a well-known symbol rather than in\n * module scope, because bundles duplicate modules: a page that loads the\n * widget bundle *and* imports the SDK has two copies of this file, and a\n * module-scoped `let` would give each its own \"default\" — so widgets would\n * silently never see the client the app created. Keying off globalThis makes\n * every copy, and every independently loaded script, agree.\n */\nconst REGISTRY = Symbol.for(\"singlebase.client.registry\");\n\ninterface Registry<T> {\n instances: Map<string, T>;\n default: T | null;\n}\n\nfunction registry<T>(): Registry<T> {\n const host = globalThis as unknown as Record<symbol, Registry<T> | undefined>;\n let existing = host[REGISTRY];\n if (!existing) {\n existing = { instances: new Map(), default: null };\n host[REGISTRY] = existing;\n }\n return existing;\n}\n\n/** Connection identity — clients with the same one are the same instance. */\nexport function clientKey(options: SinglebaseOptions): string {\n return [\n options.baseUrl ?? DEFAULT_BASE_URL,\n options.urlAccessKey ?? \"\",\n options.apiKey ?? \"\",\n options.audience ?? \"web\"\n ].join(\"|\");\n}\n\n/** Returns the cached client for these options, or creates and caches one. */\nexport function resolveClient<T>(options: SinglebaseOptions, create: () => T): T {\n const reg = registry<T>();\n const key = clientKey(options);\n const existing = reg.instances.get(key);\n if (existing) return existing;\n\n const client = create();\n reg.instances.set(key, client);\n if (!reg.default) reg.default = client;\n return client;\n}\n\n/** The client widgets fall back to when none is set on them. */\nexport function getDefaultClient<T = unknown>(): T | null {\n return registry<T>().default;\n}\n\n/** Overrides the page default (useful when several projects are in play). */\nexport function setDefaultClient<T>(client: T | null): void {\n registry<T>().default = client;\n}\n\n/** Drops a client from the registry. */\nexport function unregisterClient<T>(client: T): void {\n const reg = registry<T>();\n for (const [key, value] of reg.instances) {\n if (value === client) reg.instances.delete(key);\n }\n if (reg.default === client) {\n reg.default = reg.instances.values().next().value ?? null;\n }\n}\n"],"names":["SinglebaseError","payload","cause","ERROR_HINTS","mapErrorToHint","code","isLocalHost","hostname","DEFAULT_BASE_URL","resolveBaseUrl","options","endpointFor","_a","base","assertSecureBaseUrl","key","baseUrl","url","request","clientOptions","operation","extras","envelope","headers","fetchImpl","res","json","RETRYABLE_AUTH_CODES","RpcDispatcher","bridge","provider","bearer","signal","explicit","token","error","refreshed","DB_NAME","DB_VERSION","STORE","KEY_ID","DATA_ID","memoryStorage","current","session","webStorageAdapter","getStorage","raw","localStorageAdapter","sessionStorageAdapter","idbRequest","resolve","reject","openDb","idbRead","id","db","tx","idbWrite","value","idbDelete","canUseCryptoStorage","indexedDbCryptoStorage","keyPromise","getKey","existing","record","plain","iv","data","defaultStorage","backing","probed","candidate","EventEmitter","event","listener","set","detail","AUTHUI_PROP_KEYS","authUIAttributeFor","c","OPERATORS","OPERATOR_SET","isOperatorBag","keys","getPath","source","path","segment","equals","a","b","item","i","aKeys","bKeys","compare","left","right","matchOperator","operator","actual","expected","order","matchCondition","condition","matchesPredicate","subject","predicate","entries","validatePredicate","problems","operators","noop","noopCrossTabChannel","newId","crossTabChannel","name","BC","channel","storageEventChannel","handler","message","store","safeLocalStorage","probe","REGISTRY","registry","host","clientKey","resolveClient","create","reg","client","getDefaultClient","setDefaultClient","unregisterClient"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@singlebase/core",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Singlebase client kernel — RPC transport, dispatcher, storage, errors",
5
5
  "homepage": "https://singlebase.cloud",
6
6
  "type": "module",