@planetlogin/core 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -6,6 +6,8 @@ interface Locale {
6
6
  language?: string;
7
7
  timezone?: string;
8
8
  country?: string;
9
+ lat?: number;
10
+ lon?: number;
9
11
  }
10
12
  interface SessionClaims {
11
13
  sub: string;
@@ -41,6 +43,13 @@ interface DownstreamUser {
41
43
  locale?: Locale;
42
44
  totpEnabled?: boolean;
43
45
  }
46
+ /** Per-user, dev-owned preferences (spec §4). `locale` is the typed, first-class
47
+ * piece PlanetLogin reads/writes (fly-to + i18n); `data` is an open bag the
48
+ * integrator can store any "that kind of info" in. */
49
+ interface UserPreferences {
50
+ locale?: Locale;
51
+ data?: Record<string, unknown>;
52
+ }
44
53
  declare class Downstream {
45
54
  private baseUrl;
46
55
  private secret;
@@ -86,6 +95,13 @@ declare class Downstream {
86
95
  secret: string;
87
96
  enabled: boolean;
88
97
  }): Promise<unknown>;
98
+ /** Preferences (spec §4): per-user locale + open data bag. Null when none. */
99
+ preferencesGet(query: {
100
+ userId: string;
101
+ }): Promise<UserPreferences | null>;
102
+ preferencesSave(data: {
103
+ userId: string;
104
+ } & UserPreferences): Promise<unknown>;
89
105
  }
90
106
 
91
107
  interface PlanetLoginConfig {
@@ -127,6 +143,10 @@ interface PlanetLoginConfig {
127
143
  showSearch?: boolean;
128
144
  autoSpin?: boolean;
129
145
  };
146
+ locale?: {
147
+ persist?: boolean;
148
+ flyToOnLogin?: boolean;
149
+ };
130
150
  token?: {
131
151
  issuer?: string;
132
152
  audience?: string;
@@ -198,6 +218,9 @@ declare function publicConfig(c?: PlanetLoginConfig): {
198
218
  showSearch?: boolean;
199
219
  autoSpin?: boolean;
200
220
  };
221
+ locale: {
222
+ flyToOnLogin: boolean;
223
+ };
201
224
  };
202
225
  declare function downstreamFromEnv(): Downstream;
203
226
 
@@ -427,6 +450,24 @@ type PasswordLoginResult = {
427
450
  };
428
451
  declare function passwordLogin(deps: PasswordLoginDeps, input: PasswordLoginInput): Promise<PasswordLoginResult>;
429
452
 
453
+ interface PreferencesDeps {
454
+ downstream: Pick<Downstream, 'preferencesGet' | 'preferencesSave'>;
455
+ }
456
+ /** Keep only the known, well-typed locale fields (drop anything else). */
457
+ declare function sanitizeLocale(input: unknown): Locale | undefined;
458
+ /** Read the user's saved preferences. Always resolves (empty object if none/error). */
459
+ declare function getPreferences(deps: PreferencesDeps, input: {
460
+ userId: string;
461
+ }): Promise<UserPreferences>;
462
+ /** Write the user's preferences. No-op when there's nothing valid to write. */
463
+ declare function savePreferences(deps: PreferencesDeps, input: {
464
+ userId: string;
465
+ locale?: unknown;
466
+ data?: unknown;
467
+ }): Promise<{
468
+ saved: boolean;
469
+ }>;
470
+
430
471
  interface MagicRequestDeps {
431
472
  downstream: Downstream;
432
473
  signMagicToken: (identifier: string) => Promise<string>;
@@ -570,4 +611,4 @@ declare function totpVerify(deps: TotpDeps, input: {
570
611
  ok: boolean;
571
612
  }>;
572
613
 
573
- export { type AuthResult, type AuthVerifyDeps, type CorsConfig, DEFAULT_RULES, Downstream, type DownstreamUser, type JwtAlg, type Locale, type MagicRequestDeps, type MagicVerifyDeps, type MagicVerifyResult, type OAuthLoginDeps, type OAuthLoginInput, type OAuthLoginResult, type OAuthStateData, type PasswordLoginDeps, type PasswordLoginInput, type PasswordLoginResult, type PlanetLoginConfig, type ProviderConfig, type RateLimitDecision, type RateLimitRule, type RegisterResult, type RegisterVerifyDeps, type SessionClaims, type SessionStore, type StoreKind, type StoredCredential, type TotpDeps, _stores, authenticationOptions, buildAuthUrl, corsFromEnv, corsHeaders, downstreamFromEnv, exchangeCode, fetchProfile, getProvider, getStore, hashPassword, isPreflight, jwks, loadConfig, newTotpSecret, oauthCallback, oauthStart, openEnc, openOAuthState, originAllowed, passkeyAuthVerify, passkeyRegisterVerify, passwordLogin, pkcePair, publicConfig, rateLimit, registrationOptions, requestMagicLink, rlKey, ruleFor, sealEnc, sealOAuthState, signMagicToken, signSession, totpEnroll, totpKeyUri, totpVerify, verifyAuthentication, verifyMagicLink, verifyMagicToken, verifyPassword, verifyRegistration, verifySession, verifyTotp };
614
+ export { type AuthResult, type AuthVerifyDeps, type CorsConfig, DEFAULT_RULES, Downstream, type DownstreamUser, type JwtAlg, type Locale, type MagicRequestDeps, type MagicVerifyDeps, type MagicVerifyResult, type OAuthLoginDeps, type OAuthLoginInput, type OAuthLoginResult, type OAuthStateData, type PasswordLoginDeps, type PasswordLoginInput, type PasswordLoginResult, type PlanetLoginConfig, type PreferencesDeps, type ProviderConfig, type RateLimitDecision, type RateLimitRule, type RegisterResult, type RegisterVerifyDeps, type SessionClaims, type SessionStore, type StoreKind, type StoredCredential, type TotpDeps, type UserPreferences, _stores, authenticationOptions, buildAuthUrl, corsFromEnv, corsHeaders, downstreamFromEnv, exchangeCode, fetchProfile, getPreferences, getProvider, getStore, hashPassword, isPreflight, jwks, loadConfig, newTotpSecret, oauthCallback, oauthStart, openEnc, openOAuthState, originAllowed, passkeyAuthVerify, passkeyRegisterVerify, passwordLogin, pkcePair, publicConfig, rateLimit, registrationOptions, requestMagicLink, rlKey, ruleFor, sanitizeLocale, savePreferences, sealEnc, sealOAuthState, signMagicToken, signSession, totpEnroll, totpKeyUri, totpVerify, verifyAuthentication, verifyMagicLink, verifyMagicToken, verifyPassword, verifyRegistration, verifySession, verifyTotp };
package/dist/index.js CHANGED
@@ -55,6 +55,13 @@ var Downstream = class {
55
55
  totpSave(data) {
56
56
  return this.call("/totp/save", data);
57
57
  }
58
+ /** Preferences (spec §4): per-user locale + open data bag. Null when none. */
59
+ preferencesGet(query) {
60
+ return this.call("/preferences/find", query);
61
+ }
62
+ preferencesSave(data) {
63
+ return this.call("/preferences/save", data);
64
+ }
58
65
  };
59
66
 
60
67
  // src/config.ts
@@ -71,7 +78,15 @@ function loadConfig() {
71
78
  return cfg;
72
79
  }
73
80
  function publicConfig(c = loadConfig()) {
74
- return { spec: c.spec, brand: c.brand, providers: c.providers, copy: c.copy ?? {}, layout: c.layout ?? {} };
81
+ return {
82
+ spec: c.spec,
83
+ brand: c.brand,
84
+ providers: c.providers,
85
+ copy: c.copy ?? {},
86
+ layout: c.layout ?? {},
87
+ // Only the client-relevant gate (flyToOnLogin drives the post-login fly-to).
88
+ locale: { flyToOnLogin: c.locale?.flyToOnLogin ?? false }
89
+ };
75
90
  }
76
91
  function downstreamFromEnv() {
77
92
  const url = process.env.PLANETLOGIN_DOWNSTREAM_URL;
@@ -557,6 +572,30 @@ async function passwordLogin(deps, input) {
557
572
  return { ok: true, token, user: { id: user.id, email: user.email, name: user.name } };
558
573
  }
559
574
 
575
+ // src/flows/preferences.ts
576
+ function sanitizeLocale(input) {
577
+ if (!input || typeof input !== "object") return void 0;
578
+ const i = input;
579
+ const out = {};
580
+ if (typeof i.language === "string") out.language = i.language.slice(0, 16);
581
+ if (typeof i.timezone === "string") out.timezone = i.timezone.slice(0, 64);
582
+ if (typeof i.country === "string") out.country = i.country.slice(0, 2).toUpperCase();
583
+ if (typeof i.lat === "number" && Number.isFinite(i.lat)) out.lat = i.lat;
584
+ if (typeof i.lon === "number" && Number.isFinite(i.lon)) out.lon = i.lon;
585
+ return Object.keys(out).length ? out : void 0;
586
+ }
587
+ async function getPreferences(deps, input) {
588
+ const p = await deps.downstream.preferencesGet({ userId: input.userId }).catch(() => null);
589
+ return p ?? {};
590
+ }
591
+ async function savePreferences(deps, input) {
592
+ const locale = sanitizeLocale(input.locale);
593
+ const data = input.data && typeof input.data === "object" && !Array.isArray(input.data) ? input.data : void 0;
594
+ if (!locale && !data) return { saved: false };
595
+ await deps.downstream.preferencesSave({ userId: input.userId, ...locale ? { locale } : {}, ...data ? { data } : {} });
596
+ return { saved: true };
597
+ }
598
+
560
599
  // src/flows/magicLink.ts
561
600
  async function requestMagicLink(deps, input) {
562
601
  try {
@@ -675,6 +714,7 @@ export {
675
714
  downstreamFromEnv,
676
715
  exchangeCode,
677
716
  fetchProfile,
717
+ getPreferences,
678
718
  getProvider,
679
719
  getStore,
680
720
  hashPassword,
@@ -697,6 +737,8 @@ export {
697
737
  requestMagicLink,
698
738
  rlKey,
699
739
  ruleFor,
740
+ sanitizeLocale,
741
+ savePreferences,
700
742
  sealEnc,
701
743
  sealOAuthState,
702
744
  signMagicToken,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planetlogin/core",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "PlanetLogin auth core — framework-agnostic flows, JWT, crypto, downstream contract. Consumed by every flavor; the HTTP binding stays per-framework.",
6
6
  "exports": {