@marianmeres/ownsuite 2.1.0 → 2.2.2

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.
@@ -33,17 +33,24 @@ export type SessionStatus =
33
33
  * `/me` returns plus the fields needed for role-gating.
34
34
  */
35
35
  export interface SessionSubject {
36
+ /** Server-assigned subject identifier. Empty string before `/me` lands. */
36
37
  id: string;
38
+ /** Verified or unverified email address of the subject. */
37
39
  email: string;
40
+ /** Authorization roles — drive UI gating, not security. */
38
41
  roles: string[];
42
+ /** True when the server has confirmed the email address. */
39
43
  isVerified: boolean;
40
44
  /** Whether the account has a password (OAuth-only accounts do not). */
41
45
  hasPassword: boolean;
42
46
  }
43
47
  /** Observable session state — stored by the SessionManager. */
44
48
  export interface SessionState {
49
+ /** Current lifecycle bucket; see {@link SessionStatus}. */
45
50
  status: SessionStatus;
51
+ /** Loaded subject, or `null` when anonymous. */
46
52
  subject: SessionSubject | null;
53
+ /** JWT for outgoing `Authorization` headers, `null` when anonymous. */
47
54
  jwt: string | null;
48
55
  /** Unix-seconds expiry (optional — not every server shape returns one). */
49
56
  expiresAt: number | null;
@@ -54,22 +61,39 @@ export interface SessionState {
54
61
  * matching this interface (useful for Tauri, WebExtensions, SSR guards).
55
62
  */
56
63
  export interface SessionStorage {
64
+ /** Read a value by key; return `null` when missing or unreadable. */
57
65
  get(key: string): string | null;
66
+ /** Write a value; silently no-op on quota errors. */
58
67
  set(key: string, value: string): void;
68
+ /** Delete a value; silently no-op when absent. */
59
69
  del(key: string): void;
60
70
  }
71
+ /** Accepted `storage` option for `SessionManager`. String values resolve to
72
+ * the three built-in backends; a {@link SessionStorage} object overrides
73
+ * the defaults entirely (single custom backend — per-login `remember` is
74
+ * silently ignored in that mode). */
61
75
  export type SessionStorageType = "local" | "session" | "memory" | SessionStorage;
76
+ /** Supported OAuth provider identifiers — the server side (`@marianmeres/
77
+ * stack-account`) decides which are actually enabled. */
62
78
  export type OAuthProvider = "google" | "facebook" | "apple" | "twitter";
79
+ /** A single linked OAuth provider connection on the subject's account. */
63
80
  export interface OAuthConnection {
81
+ /** Which provider this connection represents. */
64
82
  provider: OAuthProvider;
83
+ /** Human-readable display name as reported by the provider. */
65
84
  display_name?: string;
85
+ /** Avatar URL reported by the provider (not proxied — may be public). */
66
86
  avatar_url?: string;
87
+ /** Email reported by the provider (may differ from the account email). */
67
88
  email?: string;
68
89
  }
69
90
  /** Action verb for the OAuth init URL — `login` creates/looks-up an account,
70
91
  * `link` attaches the provider to the currently authenticated subject. */
71
92
  export type OAuthAction = "login" | "link";
93
+ /** Options for starting an OAuth flow via `AuthManager.initiateOAuth`. */
72
94
  export interface OAuthInitOptions {
95
+ /** Whether this flow creates/looks-up an account (`login`) or attaches
96
+ * the provider to the currently authenticated subject (`link`). */
73
97
  action: OAuthAction;
74
98
  /** Where to redirect after the provider callback (server honours this). */
75
99
  redirect?: string;
@@ -79,6 +103,26 @@ export interface OAuthInitOptions {
79
103
  * whether to open a popup and wait for a postMessage, or redirect the
80
104
  * top window. */
81
105
  mode?: "popup" | "redirect";
106
+ /** Same semantics as {@link AuthActionOptions.remember} — pins the
107
+ * resulting session to `localStorage` (`true`) or `sessionStorage`
108
+ * (`false`). Only meaningful for `action: "login"`. */
109
+ remember?: boolean;
110
+ }
111
+ /**
112
+ * Options accepted by `AuthManager.login` / `register` /
113
+ * `handleOAuthCallback` to express per-login storage preference
114
+ * ("Remember me").
115
+ */
116
+ export interface AuthActionOptions {
117
+ /** `true` → persist the resulting session to `localStorage` (survives
118
+ * browser restart).
119
+ * `false` → persist to `sessionStorage` (dies with the tab).
120
+ * `undefined` → use the `SessionManager`'s configured default backend.
121
+ *
122
+ * Silently ignored when the `SessionManager` was constructed with a
123
+ * custom `SessionStorage` object — the single custom backend is used
124
+ * regardless. */
125
+ remember?: boolean;
82
126
  }
83
127
  /**
84
128
  * Uniform result shape for `register` / `login` / OAuth success. When the
@@ -87,33 +131,60 @@ export interface OAuthInitOptions {
87
131
  * manager flips session.status to "unverified".
88
132
  */
89
133
  export interface AuthTokenResult {
134
+ /** JWT to present on subsequent authenticated requests. Absent when
135
+ * `requiresVerification` is `true`. */
90
136
  jwt?: string;
137
+ /** Email as recognised by the server (may be normalised). */
91
138
  email: string;
139
+ /** Roles granted by the server. */
92
140
  roles: string[];
141
+ /** True when the server confirms the email is already verified. */
93
142
  isVerified?: boolean;
143
+ /** JWT `not-before` claim in unix seconds (when server returns one). */
94
144
  validFrom?: number;
145
+ /** JWT `expires-at` claim in unix seconds (when server returns one). */
95
146
  validUntil?: number;
96
147
  /** Set by the server when login auto-login is declined for a not-yet-
97
148
  * verified account. Mutually exclusive with jwt in practice. */
98
149
  requiresVerification?: boolean;
99
150
  }
151
+ /** `/me` profile payload returned by `ProfileAdapter.get` / `update`. */
100
152
  export interface ProfileResult {
153
+ /** Current email on the account. */
101
154
  email: string;
155
+ /** Current authorization roles. */
102
156
  roles: string[];
157
+ /** Whether the email is verified. */
103
158
  isVerified: boolean;
159
+ /** Whether the account has a password. */
104
160
  hasPassword: boolean;
161
+ /** Linked OAuth provider connections. */
105
162
  oauthConnections: OAuthConnection[];
106
163
  }
164
+ /**
165
+ * Contract the `AuthManager` uses to talk to the server. Implement this
166
+ * against whatever transport the backend exposes — `createStackAccountAuth
167
+ * Adapter` provides the default for `@marianmeres/stack-account`. Adapters
168
+ * hold no state; they forward `ctx.jwt` / `ctx.signal` per call.
169
+ */
107
170
  export interface AuthAdapter {
171
+ /** Create an account. When the verification gate is on, the returned
172
+ * result sets `requiresVerification: true` and omits the JWT. */
108
173
  register(input: {
174
+ /** Email as entered; server normalises + stores. */
109
175
  email: string;
176
+ /** Plaintext password — server hashes. */
110
177
  password: string;
178
+ /** Plaintext confirmation, server cross-checks. */
111
179
  password_confirm: string;
180
+ /** Roles to request on the account (server may reject). */
112
181
  roles?: string[];
113
182
  /** Optional extras — consumer forwards any configured
114
183
  * registrationFields the server expects. */
115
184
  extras?: Record<string, unknown>;
116
185
  }, ctx: OwnsuiteContext): Promise<AuthTokenResult>;
186
+ /** Exchange credentials for a JWT. Throws on bad credentials; sets
187
+ * `requiresVerification: true` when the verification gate is on. */
117
188
  login(input: {
118
189
  email: string;
119
190
  password: string;
@@ -128,22 +199,32 @@ export interface AuthAdapter {
128
199
  * URL (or a server-rendered payload) and return it. Popup-mode uses the
129
200
  * `openOAuthPopup` helper directly and does not call this. */
130
201
  handleOAuthCallback?(ctx: OwnsuiteContext): Promise<AuthTokenResult>;
202
+ /** Request a fresh verification email. Anti-enumeration: must resolve
203
+ * regardless of whether the address exists. */
131
204
  resendVerification(input: {
132
205
  email: string;
133
206
  lang?: string;
134
207
  }, ctx: OwnsuiteContext): Promise<void>;
208
+ /** Request a password-reset email. Anti-enumeration: must resolve
209
+ * regardless of whether the address exists. */
135
210
  requestPasswordReset(input: {
136
211
  email: string;
137
212
  lang?: string;
138
213
  }, ctx: OwnsuiteContext): Promise<void>;
214
+ /** Change or reset the password. Provide `current_password` for an
215
+ * authenticated self-change, or `token` for a reset-link flow. */
139
216
  changePassword(input: {
140
217
  /** Required for authenticated self-change. */
141
218
  current_password?: string;
219
+ /** New password (plaintext — hashed server-side). */
142
220
  new_password: string;
221
+ /** Confirmation — must match new_password. */
143
222
  confirm_password: string;
144
223
  /** Required for token-based reset. */
145
224
  token?: string;
146
225
  }, ctx: OwnsuiteContext): Promise<void>;
226
+ /** Irreversibly delete the authenticated account. Resolves with
227
+ * `{ deleted: true }` on success; any failure rejects. */
147
228
  deleteAccount(input: {
148
229
  password?: string;
149
230
  confirm?: boolean;
@@ -151,12 +232,19 @@ export interface AuthAdapter {
151
232
  deleted: true;
152
233
  }>;
153
234
  }
235
+ /** Contract the `ProfileManager` uses to read + mutate `/me`. Like
236
+ * `AuthAdapter`, stateless — reads `ctx.jwt` per call. */
154
237
  export interface ProfileAdapter {
238
+ /** GET `/me` → current profile snapshot. */
155
239
  get(ctx: OwnsuiteContext): Promise<ProfileResult>;
240
+ /** PUT `/me` — update email and/or confirm current password. Returns
241
+ * the refreshed profile. */
156
242
  update(input: {
157
243
  email?: string;
158
244
  current_password?: string;
159
245
  }, ctx: OwnsuiteContext): Promise<ProfileResult>;
246
+ /** GET linked OAuth connections for the authenticated subject. */
160
247
  listOAuth(ctx: OwnsuiteContext): Promise<OAuthConnection[]>;
248
+ /** DELETE a linked OAuth connection. */
161
249
  unlinkOAuth(provider: OAuthProvider, ctx: OwnsuiteContext): Promise<void>;
162
250
  }
@@ -20,82 +20,133 @@ export interface OwnsuiteEventBase {
20
20
  /** Domain that emitted the event */
21
21
  domain: DomainName;
22
22
  }
23
- /** State change event. */
23
+ /** Emitted when a domain transitions between lifecycle states
24
+ * (`initializing` → `ready` ↔ `syncing` → `error`). */
24
25
  export interface StateChangedEvent extends OwnsuiteEventBase {
26
+ /** Discriminator. */
25
27
  type: "domain:state:changed";
28
+ /** State before the transition. */
26
29
  previousState: DomainState;
30
+ /** State after the transition. */
27
31
  newState: DomainState;
28
32
  }
29
- /** Error event. */
33
+ /** Emitted when a domain's mutation or read fails. */
30
34
  export interface ErrorEvent extends OwnsuiteEventBase {
35
+ /** Discriminator. */
31
36
  type: "domain:error";
37
+ /** The failure — normalized across list / get / mutation paths. */
32
38
  error: DomainError;
33
39
  }
34
- /** Sync completed event. */
40
+ /** Emitted when a sync (initialize / refresh / mutation) completes. */
35
41
  export interface SyncedEvent extends OwnsuiteEventBase {
42
+ /** Discriminator. */
36
43
  type: "domain:synced";
37
44
  }
38
- /** List fetched event. */
45
+ /** Emitted after a successful list read. */
39
46
  export interface ListFetchedEvent extends OwnsuiteEventBase {
47
+ /** Discriminator. */
40
48
  type: "own:list:fetched";
49
+ /** Number of rows returned. */
41
50
  count: number;
42
51
  }
43
- /** Single row fetched event. */
52
+ /** Emitted after a successful single-row read (`getOne`). */
44
53
  export interface RowFetchedEvent extends OwnsuiteEventBase {
54
+ /** Discriminator. */
45
55
  type: "own:row:fetched";
56
+ /** Id of the row that was fetched. */
46
57
  rowId: string;
47
58
  }
48
- /** Row created event. */
59
+ /** Emitted after a row is created on the server. */
49
60
  export interface RowCreatedEvent extends OwnsuiteEventBase {
61
+ /** Discriminator. */
50
62
  type: "own:row:created";
63
+ /** Server-assigned id of the new row. */
51
64
  rowId: string;
52
65
  }
53
- /** Row updated event. */
66
+ /** Emitted after a row is updated on the server. */
54
67
  export interface RowUpdatedEvent extends OwnsuiteEventBase {
68
+ /** Discriminator. */
55
69
  type: "own:row:updated";
70
+ /** Id of the updated row. */
56
71
  rowId: string;
57
72
  }
58
- /** Row deleted event. */
73
+ /** Emitted after a row is deleted on the server. */
59
74
  export interface RowDeletedEvent extends OwnsuiteEventBase {
75
+ /** Discriminator. */
60
76
  type: "own:row:deleted";
77
+ /** Id of the deleted row. */
61
78
  rowId: string;
62
79
  }
80
+ /** Base fields shared by every auth / profile / session event. Unlike
81
+ * {@link OwnsuiteEventBase} there is no `domain` — these events live at the
82
+ * suite level, not a specific domain. */
63
83
  export interface AuthEventBase {
84
+ /** Unix millis at emission time. */
64
85
  timestamp: number;
65
86
  }
87
+ /** Emitted after `AuthManager.register` returns a server response. Fires
88
+ * whether or not the verification gate was hit. */
66
89
  export interface AuthRegisterEvent extends AuthEventBase {
90
+ /** Discriminator. */
67
91
  type: "auth:register";
92
+ /** Email the account was created with. */
68
93
  email: string;
69
94
  /** True when the server requires email verification (no auto-login). */
70
95
  requiresVerification: boolean;
71
96
  }
97
+ /** Emitted after `AuthManager.login` completes a credential exchange. Does
98
+ * not imply `status === "authenticated"` — the verification gate may still
99
+ * flip it to `"unverified"` afterwards. */
72
100
  export interface AuthLoginEvent extends AuthEventBase {
101
+ /** Discriminator. */
73
102
  type: "auth:login";
103
+ /** Email used to log in. */
74
104
  email: string;
75
105
  }
106
+ /** Emitted on `AuthManager.logout` and `deleteAccount` once the session has
107
+ * been cleared locally. */
76
108
  export interface AuthLogoutEvent extends AuthEventBase {
109
+ /** Discriminator. */
77
110
  type: "auth:logout";
78
111
  /** Id of the subject that just logged out, if known. */
79
112
  subjectId?: string;
80
113
  }
114
+ /** Emitted whenever `SessionManager` persists a new state — including
115
+ * hydration-driven changes and `patchSubject` writes. */
81
116
  export interface AuthSessionChangedEvent extends AuthEventBase {
117
+ /** Discriminator. */
82
118
  type: "auth:session:changed";
119
+ /** Snapshot of the session *after* the change. */
83
120
  session: SessionState;
84
121
  }
122
+ /** Emitted when the server rejects login/register because the account is
123
+ * not yet verified. UI should surface a "check your inbox" prompt. */
85
124
  export interface AuthVerificationRequiredEvent extends AuthEventBase {
125
+ /** Discriminator. */
86
126
  type: "auth:verification:required";
127
+ /** Email that needs to be verified. */
87
128
  email: string;
88
129
  }
130
+ /** Emitted after `ProfileManager.update` succeeds. The session subject has
131
+ * already been patched in place by the time this fires. */
89
132
  export interface ProfileUpdatedEvent extends AuthEventBase {
133
+ /** Discriminator. */
90
134
  type: "profile:updated";
135
+ /** Email on the profile *after* the update. */
91
136
  email: string;
92
137
  }
138
+ /** Emitted after a successful OAuth *link* flow (popup / redirect). */
93
139
  export interface OAuthLinkedEvent extends AuthEventBase {
140
+ /** Discriminator. */
94
141
  type: "oauth:linked";
142
+ /** Connection that was just added to the subject's account. */
95
143
  connection: OAuthConnection;
96
144
  }
145
+ /** Emitted after `ProfileManager.unlinkOAuth` succeeds. */
97
146
  export interface OAuthUnlinkedEvent extends AuthEventBase {
147
+ /** Discriminator. */
98
148
  type: "oauth:unlinked";
149
+ /** Provider that was just unlinked. */
99
150
  provider: OAuthProvider;
100
151
  }
101
152
  /** All event types union. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/ownsuite",
3
- "version": "2.1.0",
3
+ "version": "2.2.2",
4
4
  "type": "module",
5
5
  "main": "dist/mod.js",
6
6
  "types": "dist/mod.d.ts",