@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.
- package/AGENTS.md +11 -4
- package/API.md +27 -6
- package/README.md +5 -2
- package/dist/adapters/mock-auth.d.ts +24 -2
- package/dist/adapters/mock-auth.js +8 -0
- package/dist/adapters/mock.d.ts +2 -0
- package/dist/adapters/stack-account.d.ts +7 -0
- package/dist/adapters/stack-account.js +6 -0
- package/dist/domains/auth.d.ts +49 -5
- package/dist/domains/auth.js +59 -10
- package/dist/domains/base.d.ts +20 -0
- package/dist/domains/base.js +18 -0
- package/dist/domains/owned-collection.d.ts +5 -0
- package/dist/domains/owned-collection.js +2 -0
- package/dist/domains/profile.d.ts +18 -0
- package/dist/domains/profile.js +11 -0
- package/dist/domains/session.d.ts +35 -7
- package/dist/domains/session.js +131 -26
- package/dist/mod.d.ts +3 -0
- package/dist/oauth/popup.d.ts +25 -0
- package/dist/ownsuite.d.ts +13 -2
- package/dist/ownsuite.js +10 -2
- package/dist/types/adapter.d.ts +4 -0
- package/dist/types/auth.d.ts +88 -0
- package/dist/types/events.d.ts +59 -8
- package/package.json +1 -1
package/dist/types/auth.d.ts
CHANGED
|
@@ -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
|
}
|
package/dist/types/events.d.ts
CHANGED
|
@@ -20,82 +20,133 @@ export interface OwnsuiteEventBase {
|
|
|
20
20
|
/** Domain that emitted the event */
|
|
21
21
|
domain: DomainName;
|
|
22
22
|
}
|
|
23
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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. */
|