@vue-skuilder/common-ui 0.2.20 → 0.2.22

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.
@@ -1,4 +1,4 @@
1
- import { ReplanHints, OutcomeObserver } from '../../../db/dist/index.mjs';
1
+ import { ReplanHints, OutcomeObserver, SessionStateSnapshotProvider } from '../../../db/dist/index.mjs';
2
2
  /**
3
3
  * Misc. config for a study StudySessionConfig
4
4
  */
@@ -40,6 +40,12 @@ export type StudySessionConfig = {
40
40
  * session hints, request a replan). See `OutcomeObserver` in `@vue-skuilder/db`.
41
41
  */
42
42
  outcomeObservers?: OutcomeObserver[];
43
+ /**
44
+ * Thin learner-state snapshot taken at session open and close, stored on the
45
+ * session's `StudySessionDoc`. Keep the returned object small — counts, ids,
46
+ * scores. Time-boxed and error-isolated by the controller.
47
+ */
48
+ sessionStateSnapshot?: SessionStateSnapshotProvider;
43
49
  };
44
50
  /**
45
51
  * Built-in card transition presets provided by StudySession.
@@ -1 +1 @@
1
- {"version":3,"file":"StudySession.types.d.ts","sourceRoot":"","sources":["../../src/components/StudySession.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAErE;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,aAAa,EAAE,OAAO,CAAC;IAEvB;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,WAAW,CAAC;IAExB;;;;;;;;;OASG;IACH,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;CACtC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,GAAG,YAAY,GAAG,YAAY,CAAC;AAElF,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC"}
1
+ {"version":3,"file":"StudySession.types.d.ts","sourceRoot":"","sources":["../../src/components/StudySession.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,4BAA4B,EAAE,MAAM,kBAAkB,CAAC;AAEnG;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,aAAa,EAAE,OAAO,CAAC;IAEvB;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,WAAW,CAAC;IAExB;;;;;;;;;OASG;IACH,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;IAErC;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,4BAA4B,CAAC;CACrD,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,GAAG,YAAY,GAAG,YAAY,CAAC;AAElF,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"StudySession.vue.d.ts","sourceRoot":"","sources":["../../src/components/StudySession.vue"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"StudySession.vue.d.ts","sourceRoot":"","sources":["../../src/components/StudySession.vue"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"UserLogin.vue.d.ts","sourceRoot":"","sources":["../../../src/components/auth/UserLogin.vue"],"names":[],"mappings":"AA2DA;AAsMA,UAAU,KAAK;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAkHD,iBAAS,cAAc;WAuPT,OAAO,IAA6B;;mCAVV,GAAG;;;;EAe1C;AAqBD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;;;;;gBArYN,MAAM;6EA8YnB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
1
+ {"version":3,"file":"UserLogin.vue.d.ts","sourceRoot":"","sources":["../../../src/components/auth/UserLogin.vue"],"names":[],"mappings":"AA2DA;AA2OA,UAAU,KAAK;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAqJD,iBAAS,cAAc;WAwPT,OAAO,IAA6B;;mCAVV,GAAG;;;;EAe1C;AAsBD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;;;;;gBA1aN,MAAM;6EAmbnB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
@@ -44,6 +44,7 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
44
44
  getCourseRegDoc: (courseId: string) => Promise<import('../../../../db/dist/index.mjs').CourseRegistration>;
45
45
  getActiveCourses: () => Promise<import('../../../../db/dist/index.mjs').CourseRegistration[]>;
46
46
  getPendingReviews: (courseId?: string) => Promise<import('../../../../db/dist/index.mjs').ScheduledCard[]>;
47
+ getStudySessions: (courseId?: string) => Promise<import('../../../../db/dist/index.mjs').StudySessionDoc[]>;
47
48
  getReviewsForcast: (daysCount: number, courseId?: string) => Promise<import('../../../../db/dist/index.mjs').ScheduledCard[]>;
48
49
  getActivityRecords: () => Promise<import('../../../../db/dist/index.mjs').ActivityRecord[]>;
49
50
  getStrategyState: <T>(courseId: string, strategyKey: string) => Promise<T | null>;
@@ -73,6 +74,7 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
73
74
  putStrategyState: <T>(courseId: string, strategyKey: string, data: T) => Promise<void>;
74
75
  deleteStrategyState: (courseId: string, strategyKey: string) => Promise<void>;
75
76
  putUserOutcome: (record: import('../../../../db/dist/index.mjs').UserOutcomeRecord) => Promise<void>;
77
+ putStudySession: (doc: import('../../../../db/dist/index.mjs').StudySessionDoc) => Promise<void>;
76
78
  update: <T extends PouchDB.Core.Document<object>>(id: string, update: Partial<T> | ((x: T) => T)) => Promise<T>;
77
79
  createAccount: (username: string, password: string) => Promise<{
78
80
  status: Status;
@@ -109,6 +111,7 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
109
111
  getCourseRegDoc: (courseId: string) => Promise<import('../../../../db/dist/index.mjs').CourseRegistration>;
110
112
  getActiveCourses: () => Promise<import('../../../../db/dist/index.mjs').CourseRegistration[]>;
111
113
  getPendingReviews: (courseId?: string) => Promise<import('../../../../db/dist/index.mjs').ScheduledCard[]>;
114
+ getStudySessions: (courseId?: string) => Promise<import('../../../../db/dist/index.mjs').StudySessionDoc[]>;
112
115
  getReviewsForcast: (daysCount: number, courseId?: string) => Promise<import('../../../../db/dist/index.mjs').ScheduledCard[]>;
113
116
  getActivityRecords: () => Promise<import('../../../../db/dist/index.mjs').ActivityRecord[]>;
114
117
  getStrategyState: <T>(courseId: string, strategyKey: string) => Promise<T | null>;
@@ -138,6 +141,7 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
138
141
  putStrategyState: <T>(courseId: string, strategyKey: string, data: T) => Promise<void>;
139
142
  deleteStrategyState: (courseId: string, strategyKey: string) => Promise<void>;
140
143
  putUserOutcome: (record: import('../../../../db/dist/index.mjs').UserOutcomeRecord) => Promise<void>;
144
+ putStudySession: (doc: import('../../../../db/dist/index.mjs').StudySessionDoc) => Promise<void>;
141
145
  update: <T extends PouchDB.Core.Document<object>>(id: string, update: Partial<T> | ((x: T) => T)) => Promise<T>;
142
146
  createAccount: (username: string, password: string) => Promise<{
143
147
  status: Status;
@@ -174,6 +178,7 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
174
178
  getCourseRegDoc: (courseId: string) => Promise<import('../../../../db/dist/index.mjs').CourseRegistration>;
175
179
  getActiveCourses: () => Promise<import('../../../../db/dist/index.mjs').CourseRegistration[]>;
176
180
  getPendingReviews: (courseId?: string) => Promise<import('../../../../db/dist/index.mjs').ScheduledCard[]>;
181
+ getStudySessions: (courseId?: string) => Promise<import('../../../../db/dist/index.mjs').StudySessionDoc[]>;
177
182
  getReviewsForcast: (daysCount: number, courseId?: string) => Promise<import('../../../../db/dist/index.mjs').ScheduledCard[]>;
178
183
  getActivityRecords: () => Promise<import('../../../../db/dist/index.mjs').ActivityRecord[]>;
179
184
  getStrategyState: <T>(courseId: string, strategyKey: string) => Promise<T | null>;
@@ -203,6 +208,7 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
203
208
  putStrategyState: <T>(courseId: string, strategyKey: string, data: T) => Promise<void>;
204
209
  deleteStrategyState: (courseId: string, strategyKey: string) => Promise<void>;
205
210
  putUserOutcome: (record: import('../../../../db/dist/index.mjs').UserOutcomeRecord) => Promise<void>;
211
+ putStudySession: (doc: import('../../../../db/dist/index.mjs').StudySessionDoc) => Promise<void>;
206
212
  update: <T extends PouchDB.Core.Document<object>>(id: string, update: Partial<T> | ((x: T) => T)) => Promise<T>;
207
213
  createAccount: (username: string, password: string) => Promise<{
208
214
  status: Status;
@@ -240,6 +246,7 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
240
246
  getCourseRegDoc: (courseId: string) => Promise<import('../../../../db/dist/index.mjs').CourseRegistration>;
241
247
  getActiveCourses: () => Promise<import('../../../../db/dist/index.mjs').CourseRegistration[]>;
242
248
  getPendingReviews: (courseId?: string) => Promise<import('../../../../db/dist/index.mjs').ScheduledCard[]>;
249
+ getStudySessions: (courseId?: string) => Promise<import('../../../../db/dist/index.mjs').StudySessionDoc[]>;
243
250
  getReviewsForcast: (daysCount: number, courseId?: string) => Promise<import('../../../../db/dist/index.mjs').ScheduledCard[]>;
244
251
  getActivityRecords: () => Promise<import('../../../../db/dist/index.mjs').ActivityRecord[]>;
245
252
  getStrategyState: <T>(courseId: string, strategyKey: string) => Promise<T | null>;
@@ -269,6 +276,7 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
269
276
  putStrategyState: <T>(courseId: string, strategyKey: string, data: T) => Promise<void>;
270
277
  deleteStrategyState: (courseId: string, strategyKey: string) => Promise<void>;
271
278
  putUserOutcome: (record: import('../../../../db/dist/index.mjs').UserOutcomeRecord) => Promise<void>;
279
+ putStudySession: (doc: import('../../../../db/dist/index.mjs').StudySessionDoc) => Promise<void>;
272
280
  update: <T extends PouchDB.Core.Document<object>>(id: string, update: Partial<T> | ((x: T) => T)) => Promise<T>;
273
281
  createAccount: (username: string, password: string) => Promise<{
274
282
  status: Status;
@@ -1 +1 @@
1
- {"version":3,"file":"UserRegistration.vue.d.ts","sourceRoot":"","sources":["../../../src/components/auth/UserRegistration.vue"],"names":[],"mappings":"AA8EA;AAAA,OAiSO,EAAmB,QAAQ,EAAE,MAAM,KAAK,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,MAAM,EAAO,MAAM,sBAAsB,CAAC;;;cAU3B,QAAQ,CAAC,CAAC,OAAO,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,CAAC;;;;;;;;;;;;;;;;;;UAuBrD,eAAe,GAAG,IAAI;WACO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAmRrB,CAAC;;;;;;;;;;;;wBAqCf,CAAA;yBACd,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAtC2B,CAAC;;;;;;;;;;;;wBAqCf,CAAA;yBACd,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAtC2B,CAAC;;;;;;;;;;;;wBAqCf,CAAA;yBACd,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAtC2B,CAAC;;;;;;;;;;;;wBAqCf,CAAA;yBACd,CAAC;;;;;;;;;;;;;;;;;yBAhTgB,OAAO;;;;;qBASX,MAAM;2BAGA,MAAM;;;;;;;;cA7CT,QAAQ,CAAC,CAAC,OAAO,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,CAAC;;;;;;;AALzE,wBAqMG"}
1
+ {"version":3,"file":"UserRegistration.vue.d.ts","sourceRoot":"","sources":["../../../src/components/auth/UserRegistration.vue"],"names":[],"mappings":"AA8EA;AAAA,OAgSO,EAAmB,QAAQ,EAAE,MAAM,KAAK,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,MAAM,EAAO,MAAM,sBAAsB,CAAC;;;cAU3B,QAAQ,CAAC,CAAC,OAAO,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,CAAC;;;;;;;;;;;;;;;;;;UAuBrD,eAAe,GAAG,IAAI;WACO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBA8U9C,CAAC;;;;;;;;;;;;;wBAyC+D,CAAC;yBAC3D,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBA1CN,CAAC;;;;;;;;;;;;;wBAyC+D,CAAC;yBAC3D,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBA1CN,CAAC;;;;;;;;;;;;;wBAyC+D,CAAC;yBAC3D,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBA1CN,CAAC;;;;;;;;;;;;;wBAyC+D,CAAC;yBAC3D,CAAA;;;;;;;;;;;;;;;;;yBA/WQ,OAAO;;;;;qBASX,MAAM;2BAGA,MAAM;;;;;;;;cA7CT,QAAQ,CAAC,CAAC,OAAO,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,CAAC;;;;;;;AALzE,wBAoMG"}
@@ -5,5 +5,5 @@ export { default as UserLoginAndRegistrationContainer } from './UserLoginAndRegi
5
5
  export { default as VerifyEmail } from './VerifyEmail.vue';
6
6
  export { default as RequestPasswordReset } from './RequestPasswordReset.vue';
7
7
  export { default as ResetPassword } from './ResetPassword.vue';
8
- export { sendVerificationEmail, verifyEmail, getUserStatus, requestPasswordReset, resetPassword, type AuthResponse, type VerifyEmailResponse, type UserStatusResponse, } from '../../services/authAPI';
8
+ export { sendVerificationEmail, verifyEmail, getUserStatus, resolveLoginIdentifier, requestPasswordReset, resetPassword, type AuthResponse, type VerifyEmailResponse, type UserStatusResponse, type ResolveLoginResponse, } from '../../services/authAPI';
9
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,iCAAiC,EAAE,MAAM,yCAAyC,CAAC;AAGvG,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAG/D,OAAO,EACL,qBAAqB,EACrB,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,aAAa,EACb,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,iCAAiC,EAAE,MAAM,yCAAyC,CAAC;AAGvG,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAG/D,OAAO,EACL,qBAAqB,EACrB,WAAW,EACX,aAAa,EACb,sBAAsB,EACtB,oBAAoB,EACpB,aAAa,EACb,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,GAC1B,MAAM,wBAAwB,CAAC"}
@@ -30,6 +30,19 @@ export interface UserStatusResponse extends AuthResponse {
30
30
  email?: string | null;
31
31
  entitlements?: UserEntitlements;
32
32
  }
33
+ export interface ResolveLoginResponse extends AuthResponse {
34
+ username?: string;
35
+ /**
36
+ * HTTP status of the resolve call on failure, so callers can distinguish a
37
+ * genuine credential rejection from a system/service failure. The server
38
+ * returns a uniform 401 for every real auth failure (unknown identifier,
39
+ * unverified email, wrong password — no enumeration leak); anything else
40
+ * (404 = route not deployed, 5xx, or `undefined` for a network error) means
41
+ * the resolver itself is unavailable, not that the credentials were wrong.
42
+ * Undefined on success.
43
+ */
44
+ status?: number;
45
+ }
33
46
  /**
34
47
  * Triggers verification email send for a newly created account.
35
48
  *
@@ -44,6 +57,13 @@ export declare function sendVerificationEmail(username: string, email: string, o
44
57
  * Verify user email using token from magic link.
45
58
  */
46
59
  export declare function verifyEmail(token: string): Promise<VerifyEmailResponse>;
60
+ /**
61
+ * Resolve a login identifier (username OR email) to a couch username, gated on
62
+ * the correct password. Returns the username only on success; every failure is
63
+ * a uniform "invalid credentials" (no account-enumeration leak). Used by the
64
+ * login form before calling user.login() when the identifier is an email.
65
+ */
66
+ export declare function resolveLoginIdentifier(identifier: string, password: string): Promise<ResolveLoginResponse>;
47
67
  /**
48
68
  * Get current user's account status (verification status, email).
49
69
  * Requires valid AuthSession cookie.
@@ -1 +1 @@
1
- {"version":3,"file":"authAPI.d.ts","sourceRoot":"","sources":["../../src/services/authAPI.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,mBAAmB,CAAC,EAAE;YACpB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;SACxB,CAAC;KACH;CACF;AAgCD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAE3D,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,sBAAsB,GAAG,UAAU,GAAG,WAAW,CAAC;IAC3D,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CA+BvB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuB7E;AAED;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAsBjE;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CA4BvB;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC,CAuBvB"}
1
+ {"version":3,"file":"authAPI.d.ts","sourceRoot":"","sources":["../../src/services/authAPI.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,mBAAmB,CAAC,EAAE;YACpB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;SACxB,CAAC;KACH;CACF;AAgCD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAE3D,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,sBAAsB,GAAG,UAAU,GAAG,WAAW,CAAC;IAC3D,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAED,MAAM,WAAW,oBAAqB,SAAQ,YAAY;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CA+BvB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA+B7E;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,oBAAoB,CAAC,CAgC/B;AAED;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAsBjE;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC,CA4BvB;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC,CAuBvB"}
@@ -26,6 +26,7 @@ export declare const useAuthStore: () => import('pinia').Store<"auth", AuthState
26
26
  getCourseRegDoc: (courseId: string) => Promise<import('../../../db/dist/index.mjs').CourseRegistration>;
27
27
  getActiveCourses: () => Promise<import('../../../db/dist/index.mjs').CourseRegistration[]>;
28
28
  getPendingReviews: (courseId?: string) => Promise<import('../../../db/dist/index.mjs').ScheduledCard[]>;
29
+ getStudySessions: (courseId?: string) => Promise<import('../../../db/dist/index.mjs').StudySessionDoc[]>;
29
30
  getReviewsForcast: (daysCount: number, courseId?: string) => Promise<import('../../../db/dist/index.mjs').ScheduledCard[]>;
30
31
  getActivityRecords: () => Promise<import('../../../db/dist/index.mjs').ActivityRecord[]>;
31
32
  getStrategyState: <T>(courseId: string, strategyKey: string) => Promise<T | null>;
@@ -55,6 +56,7 @@ export declare const useAuthStore: () => import('pinia').Store<"auth", AuthState
55
56
  putStrategyState: <T>(courseId: string, strategyKey: string, data: T) => Promise<void>;
56
57
  deleteStrategyState: (courseId: string, strategyKey: string) => Promise<void>;
57
58
  putUserOutcome: (record: import('../../../db/dist/index.mjs').UserOutcomeRecord) => Promise<void>;
59
+ putStudySession: (doc: import('../../../db/dist/index.mjs').StudySessionDoc) => Promise<void>;
58
60
  update: <T extends PouchDB.Core.Document<object>>(id: string, update: Partial<T> | ((x: T) => T)) => Promise<T>;
59
61
  createAccount: (username: string, password: string) => Promise<{
60
62
  status: import('../../../common/dist/index.mjs').Status;
@@ -91,6 +93,7 @@ export declare const useAuthStore: () => import('pinia').Store<"auth", AuthState
91
93
  getCourseRegDoc: (courseId: string) => Promise<import('../../../db/dist/index.mjs').CourseRegistration>;
92
94
  getActiveCourses: () => Promise<import('../../../db/dist/index.mjs').CourseRegistration[]>;
93
95
  getPendingReviews: (courseId?: string) => Promise<import('../../../db/dist/index.mjs').ScheduledCard[]>;
96
+ getStudySessions: (courseId?: string) => Promise<import('../../../db/dist/index.mjs').StudySessionDoc[]>;
94
97
  getReviewsForcast: (daysCount: number, courseId?: string) => Promise<import('../../../db/dist/index.mjs').ScheduledCard[]>;
95
98
  getActivityRecords: () => Promise<import('../../../db/dist/index.mjs').ActivityRecord[]>;
96
99
  getStrategyState: <T>(courseId: string, strategyKey: string) => Promise<T | null>;
@@ -120,6 +123,7 @@ export declare const useAuthStore: () => import('pinia').Store<"auth", AuthState
120
123
  putStrategyState: <T>(courseId: string, strategyKey: string, data: T) => Promise<void>;
121
124
  deleteStrategyState: (courseId: string, strategyKey: string) => Promise<void>;
122
125
  putUserOutcome: (record: import('../../../db/dist/index.mjs').UserOutcomeRecord) => Promise<void>;
126
+ putStudySession: (doc: import('../../../db/dist/index.mjs').StudySessionDoc) => Promise<void>;
123
127
  update: <T extends PouchDB.Core.Document<object>>(id: string, update: Partial<T> | ((x: T) => T)) => Promise<T>;
124
128
  createAccount: (username: string, password: string) => Promise<{
125
129
  status: import('../../../common/dist/index.mjs').Status;
@@ -156,6 +160,7 @@ export declare const useAuthStore: () => import('pinia').Store<"auth", AuthState
156
160
  getCourseRegDoc: (courseId: string) => Promise<import('../../../db/dist/index.mjs').CourseRegistration>;
157
161
  getActiveCourses: () => Promise<import('../../../db/dist/index.mjs').CourseRegistration[]>;
158
162
  getPendingReviews: (courseId?: string) => Promise<import('../../../db/dist/index.mjs').ScheduledCard[]>;
163
+ getStudySessions: (courseId?: string) => Promise<import('../../../db/dist/index.mjs').StudySessionDoc[]>;
159
164
  getReviewsForcast: (daysCount: number, courseId?: string) => Promise<import('../../../db/dist/index.mjs').ScheduledCard[]>;
160
165
  getActivityRecords: () => Promise<import('../../../db/dist/index.mjs').ActivityRecord[]>;
161
166
  getStrategyState: <T>(courseId: string, strategyKey: string) => Promise<T | null>;
@@ -185,6 +190,7 @@ export declare const useAuthStore: () => import('pinia').Store<"auth", AuthState
185
190
  putStrategyState: <T>(courseId: string, strategyKey: string, data: T) => Promise<void>;
186
191
  deleteStrategyState: (courseId: string, strategyKey: string) => Promise<void>;
187
192
  putUserOutcome: (record: import('../../../db/dist/index.mjs').UserOutcomeRecord) => Promise<void>;
193
+ putStudySession: (doc: import('../../../db/dist/index.mjs').StudySessionDoc) => Promise<void>;
188
194
  update: <T extends PouchDB.Core.Document<object>>(id: string, update: Partial<T> | ((x: T) => T)) => Promise<T>;
189
195
  createAccount: (username: string, password: string) => Promise<{
190
196
  status: import('../../../common/dist/index.mjs').Status;
@@ -222,6 +228,7 @@ export declare const useAuthStore: () => import('pinia').Store<"auth", AuthState
222
228
  getCourseRegDoc: (courseId: string) => Promise<import('../../../db/dist/index.mjs').CourseRegistration>;
223
229
  getActiveCourses: () => Promise<import('../../../db/dist/index.mjs').CourseRegistration[]>;
224
230
  getPendingReviews: (courseId?: string) => Promise<import('../../../db/dist/index.mjs').ScheduledCard[]>;
231
+ getStudySessions: (courseId?: string) => Promise<import('../../../db/dist/index.mjs').StudySessionDoc[]>;
225
232
  getReviewsForcast: (daysCount: number, courseId?: string) => Promise<import('../../../db/dist/index.mjs').ScheduledCard[]>;
226
233
  getActivityRecords: () => Promise<import('../../../db/dist/index.mjs').ActivityRecord[]>;
227
234
  getStrategyState: <T>(courseId: string, strategyKey: string) => Promise<T | null>;
@@ -251,6 +258,7 @@ export declare const useAuthStore: () => import('pinia').Store<"auth", AuthState
251
258
  putStrategyState: <T>(courseId: string, strategyKey: string, data: T) => Promise<void>;
252
259
  deleteStrategyState: (courseId: string, strategyKey: string) => Promise<void>;
253
260
  putUserOutcome: (record: import('../../../db/dist/index.mjs').UserOutcomeRecord) => Promise<void>;
261
+ putStudySession: (doc: import('../../../db/dist/index.mjs').StudySessionDoc) => Promise<void>;
254
262
  update: <T extends PouchDB.Core.Document<object>>(id: string, update: Partial<T> | ((x: T) => T)) => Promise<T>;
255
263
  createAccount: (username: string, password: string) => Promise<{
256
264
  status: import('../../../common/dist/index.mjs').Status;
@@ -1 +1 @@
1
- {"version":3,"file":"useAuthStore.d.ts","sourceRoot":"","sources":["../../src/stores/useAuthStore.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGjE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,eAAe,GAAG,SAAS,CAAC;IACnC,oBAAoB,EAAE;QACpB,IAAI,EAAE,OAAO,CAAC;QACd,QAAQ,EAAE,OAAO,CAAC;QAClB,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,EAAE,OAAO,CAAC;KAC1B,CAAC;IACF,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAiB3E;AAED,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAgFw/jB,CAAC;;;;;;;;;;;;oBAAm0D,CAAC;qBAAuB,CAAC;;;;;;;kBA3Gp2nB,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAsGw/jB,CAAC;;;;;;;;;;;;oBAAm0D,CAAC;qBAAuB,CAAC;;;;;;;kBA3Gp2nB,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAsGw/jB,CAAC;;;;;;;;;;;;oBAAm0D,CAAC;qBAAuB,CAAC;;;;;;;kBA3Gp2nB,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAsGw/jB,CAAC;;;;;;;;;;;;oBAAm0D,CAAC;qBAAuB,CAAC;;;;;;;;;yBA1Cn1nB,OAAO;uBAIT,OAAO;;;;;EAqC/B,CAAC"}
1
+ {"version":3,"file":"useAuthStore.d.ts","sourceRoot":"","sources":["../../src/stores/useAuthStore.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGjE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,eAAe,GAAG,SAAS,CAAC;IACnC,oBAAoB,EAAE;QACpB,IAAI,EAAE,OAAO,CAAC;QACd,QAAQ,EAAE,OAAO,CAAC;QAClB,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,EAAE,OAAO,CAAC;KAC1B,CAAC;IACF,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAiB3E;AAED,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAgFk9pB,CAAC;;;;;;;;;;;;;oBAAm7D,CAAC;qBAAuB,CAAC;;;;;;;kBA3G96tB,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAsGk9pB,CAAC;;;;;;;;;;;;;oBAAm7D,CAAC;qBAAuB,CAAC;;;;;;;kBA3G96tB,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAsGk9pB,CAAC;;;;;;;;;;;;;oBAAm7D,CAAC;qBAAuB,CAAC;;;;;;;kBA3G96tB,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAsGk9pB,CAAC;;;;;;;;;;;;;oBAAm7D,CAAC;qBAAuB,CAAC;;;;;;;;;yBA1C75tB,OAAO;uBAIT,OAAO;;;;;EAqC/B,CAAC"}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.2.20",
6
+ "version": "0.2.22",
7
7
  "sideEffects": false,
8
8
  "main": "./dist/common-ui.umd.js",
9
9
  "module": "./dist/common-ui.es.js",
@@ -34,8 +34,8 @@
34
34
  "@highlightjs/vue-plugin": "^2.1.2",
35
35
  "@mdi/font": "^7.3.67",
36
36
  "@vojtechlanka/vue-tags-input": "^3",
37
- "@vue-skuilder/common": "0.2.20",
38
- "@vue-skuilder/db": "0.2.20",
37
+ "@vue-skuilder/common": "0.2.22",
38
+ "@vue-skuilder/db": "0.2.22",
39
39
  "highlight.js": "^11.0.1",
40
40
  "marked": "^15.0.6",
41
41
  "moment": "^2.29.4",
@@ -71,5 +71,5 @@
71
71
  "vite-plugin-dts": "^4.5.3",
72
72
  "vitest": "^4.1.0"
73
73
  },
74
- "stableVersion": "0.2.20"
74
+ "stableVersion": "0.2.22"
75
75
  }
@@ -1,4 +1,4 @@
1
- import type { ReplanHints, OutcomeObserver } from '@vue-skuilder/db';
1
+ import type { ReplanHints, OutcomeObserver, SessionStateSnapshotProvider } from '@vue-skuilder/db';
2
2
 
3
3
  /**
4
4
  * Misc. config for a study StudySessionConfig
@@ -44,6 +44,13 @@ export type StudySessionConfig = {
44
44
  * session hints, request a replan). See `OutcomeObserver` in `@vue-skuilder/db`.
45
45
  */
46
46
  outcomeObservers?: OutcomeObserver[];
47
+
48
+ /**
49
+ * Thin learner-state snapshot taken at session open and close, stored on the
50
+ * session's `StudySessionDoc`. Keep the returned object small — counts, ids,
51
+ * scores. Time-boxed and error-isolated by the controller.
52
+ */
53
+ sessionStateSnapshot?: SessionStateSnapshotProvider;
47
54
  };
48
55
 
49
56
  /**
@@ -113,6 +113,7 @@ import {
113
113
  import confetti from 'canvas-confetti';
114
114
 
115
115
  import { StudySessionConfig, CardTransitionPreset, CardTransitionMode } from './StudySession.types';
116
+ import type { SessionStateSnapshotProvider } from '@vue-skuilder/db';
116
117
 
117
118
  interface StudyRefs {
118
119
  shadowWrapper: HTMLDivElement;
@@ -273,6 +274,17 @@ export default defineComponent({
273
274
  console.log('[StudySession] InitSession completed in created hook');
274
275
  },
275
276
 
277
+ beforeUnmount() {
278
+ // Torn down mid-session (route change, parent v-if) = abandoned.
279
+ // `endSession` is idempotent, so a naturally-ended session keeps `closed`.
280
+ if (this.sessionController && !this.sessionFinished) {
281
+ void this.sessionController.endSession('abandoned');
282
+ }
283
+ if (this.intervalHandler) {
284
+ clearInterval(this.intervalHandler);
285
+ }
286
+ },
287
+
276
288
  errorCaptured(err: unknown, _instance: unknown, info: string) {
277
289
  console.error(`[StudySession] Card render error (${info}), skipping card "${this.cardID}":`, err);
278
290
  if (this.sessionController) {
@@ -399,6 +411,8 @@ export default defineComponent({
399
411
  const scOptions: {
400
412
  defaultBatchLimit?: number;
401
413
  outcomeObservers?: OutcomeObserver[];
414
+ courseId?: string;
415
+ stateSnapshotProvider?: SessionStateSnapshotProvider;
402
416
  } = {};
403
417
  if (this.sessionConfig?.defaultBatchLimit !== undefined) {
404
418
  scOptions.defaultBatchLimit = this.sessionConfig.defaultBatchLimit;
@@ -406,6 +420,15 @@ export default defineComponent({
406
420
  if (this.sessionConfig?.outcomeObservers?.length) {
407
421
  scOptions.outcomeObservers = this.sessionConfig.outcomeObservers;
408
422
  }
423
+ // A session can span several sources; the first course-type one owns
424
+ // the record. Absent it, the controller skips session recording.
425
+ const recordingCourseId = this.contentSources.find((s) => s.type === 'course')?.id;
426
+ if (recordingCourseId) {
427
+ scOptions.courseId = recordingCourseId;
428
+ }
429
+ if (this.sessionConfig?.sessionStateSnapshot) {
430
+ scOptions.stateSnapshotProvider = this.sessionConfig.sessionStateSnapshot;
431
+ }
409
432
 
410
433
  this.sessionController = markRaw(
411
434
  new SessionController<ViewComponent>(
@@ -487,6 +510,9 @@ export default defineComponent({
487
510
 
488
511
  r.cardID = this.cardID;
489
512
  r.courseID = this.courseID;
513
+ // The single chokepoint every response passes through en route to
514
+ // putCardRecord.
515
+ r.sessionId = this.sessionController?.sessionId;
490
516
  this.currentCard.records.push(r);
491
517
 
492
518
  console.log(`[StudySession] StudySession.processResponse is running...`);
@@ -6,10 +6,10 @@
6
6
  <v-form onsubmit="return false;" @submit.prevent="login">
7
7
  <v-text-field
8
8
  id=""
9
- v-model="username"
9
+ v-model="identifier"
10
10
  autofocus
11
11
  name="username"
12
- label="Username"
12
+ label="Username or email"
13
13
  prepend-icon="mdi-account-circle"
14
14
  ></v-text-field>
15
15
  <v-text-field
@@ -26,7 +26,7 @@
26
26
  ></v-text-field>
27
27
 
28
28
  <v-snackbar v-model="badLoginAttempt" location="bottom right" :timeout="errorTimeout">
29
- Username or password was incorrect.
29
+ {{ errorMessage }}
30
30
  <v-btn color="pink" variant="text" @click="badLoginAttempt = false">Close</v-btn>
31
31
  </v-snackbar>
32
32
 
@@ -67,6 +67,7 @@ import { Status } from '@vue-skuilder/common';
67
67
  import { User } from '@vue-skuilder/db';
68
68
  import { getCurrentUser, useAuthStore } from '../../stores/useAuthStore';
69
69
  import { useConfigStore } from '../../stores/useConfigStore';
70
+ import { resolveLoginIdentifier } from '../../services/authAPI';
70
71
 
71
72
  // Define props
72
73
  interface Props {
@@ -89,12 +90,22 @@ const route = useRoute();
89
90
  const authStore = useAuthStore();
90
91
  const configStore = useConfigStore();
91
92
 
92
- const username = ref('');
93
+ // Holds a username OR an email address (see login()).
94
+ const identifier = ref('');
93
95
  const password = ref('');
94
96
  const passwordVisible = ref(false);
95
97
  const awaitingResponse = ref(false);
96
98
  const badLoginAttempt = ref(false);
97
99
  const errorTimeout = ref(7000);
100
+ const errorMessage = ref('Username or password was incorrect.');
101
+
102
+ // Shown when the email→username resolver is unreachable (route missing / 5xx /
103
+ // network) rather than when credentials are actually wrong. Telling the two
104
+ // apart matters: "wrong password" sends the user to re-check what is in fact
105
+ // correct, and hides a real backend outage.
106
+ const BAD_CREDENTIALS_MSG = 'Username or password was incorrect.';
107
+ const LOGIN_UNAVAILABLE_MSG =
108
+ 'Login is temporarily unavailable. Please try again in a moment.';
98
109
  const user = ref<User | undefined>(undefined);
99
110
 
100
111
  const loginRoute = computed(() => route.name === 'login');
@@ -104,11 +115,12 @@ const buttonStatus = computed(() => ({
104
115
  text: badLoginAttempt.value ? 'Try again' : 'Log In',
105
116
  }));
106
117
 
107
- const initBadLogin = () => {
118
+ const initBadLogin = (message: string = BAD_CREDENTIALS_MSG) => {
119
+ errorMessage.value = message;
108
120
  badLoginAttempt.value = true;
109
121
 
110
122
  alertUser({
111
- text: 'Username or password was incorrect.',
123
+ text: message,
112
124
  status: Status.error,
113
125
  timeout: errorTimeout.value,
114
126
  });
@@ -120,7 +132,7 @@ const initBadLogin = () => {
120
132
  const login = async () => {
121
133
  awaitingResponse.value = true;
122
134
  log('Starting login attempt');
123
- log(`Login attempt for username: ${username.value}`);
135
+ log(`Login attempt for identifier: ${identifier.value}`);
124
136
 
125
137
  try {
126
138
  log('Attempting to get User instance');
@@ -128,7 +140,31 @@ const login = async () => {
128
140
  user.value = await getCurrentUser();
129
141
  log('Got User instance, attempting login');
130
142
 
131
- await user.value.login(username.value, password.value);
143
+ // Couch _session authenticates by username only. When the identifier is an
144
+ // email, resolve it to the account username first — server-side and gated
145
+ // on the correct password, so it can't be used to enumerate accounts. A
146
+ // plain username is passed straight through.
147
+ let loginName = identifier.value;
148
+ if (identifier.value.includes('@')) {
149
+ const resolved = await resolveLoginIdentifier(identifier.value, password.value);
150
+ if (!resolved.ok || !resolved.username) {
151
+ // 401 is the server's uniform "real auth failure" code; anything else
152
+ // (404 = route not deployed, 5xx, or no status = network) means the
153
+ // resolver is unavailable, not that the credentials were wrong.
154
+ if (resolved.status === 401) {
155
+ log('Email identifier did not resolve: invalid credentials');
156
+ initBadLogin();
157
+ } else {
158
+ log(`Email login unavailable (resolver error: ${resolved.error ?? 'network'})`);
159
+ initBadLogin(LOGIN_UNAVAILABLE_MSG);
160
+ }
161
+ awaitingResponse.value = false;
162
+ return;
163
+ }
164
+ loginName = resolved.username;
165
+ }
166
+
167
+ await user.value.login(loginName, password.value);
132
168
  log('Login successful');
133
169
 
134
170
  // load user config
@@ -209,12 +209,11 @@ Author: ${this.author}
209
209
  this.authStore.loginAndRegistration.init = false;
210
210
  this.authStore.loginAndRegistration.init = true;
211
211
 
212
- // Save email to user config if provided
212
+ // Send verification email if an address was provided. The address
213
+ // is persisted to the _users doc by send-verification (the single
214
+ // source of truth); no separate CONFIG.email copy is kept.
213
215
  if (this.email) {
214
216
  try {
215
- const currentUser = await getCurrentUser();
216
- await currentUser.setConfig({ email: this.email });
217
-
218
217
  // Trigger verification email send with current origin
219
218
  const origin = typeof window !== 'undefined' ? window.location.origin : undefined;
220
219
  const verificationResult = await sendVerificationEmail(this.username, this.email, origin);
@@ -14,9 +14,11 @@ export {
14
14
  sendVerificationEmail,
15
15
  verifyEmail,
16
16
  getUserStatus,
17
+ resolveLoginIdentifier,
17
18
  requestPasswordReset,
18
19
  resetPassword,
19
20
  type AuthResponse,
20
21
  type VerifyEmailResponse,
21
22
  type UserStatusResponse,
23
+ type ResolveLoginResponse,
22
24
  } from '../../services/authAPI';
@@ -68,6 +68,20 @@ export interface UserStatusResponse extends AuthResponse {
68
68
  entitlements?: UserEntitlements;
69
69
  }
70
70
 
71
+ export interface ResolveLoginResponse extends AuthResponse {
72
+ username?: string;
73
+ /**
74
+ * HTTP status of the resolve call on failure, so callers can distinguish a
75
+ * genuine credential rejection from a system/service failure. The server
76
+ * returns a uniform 401 for every real auth failure (unknown identifier,
77
+ * unverified email, wrong password — no enumeration leak); anything else
78
+ * (404 = route not deployed, 5xx, or `undefined` for a network error) means
79
+ * the resolver itself is unavailable, not that the credentials were wrong.
80
+ * Undefined on success.
81
+ */
82
+ status?: number;
83
+ }
84
+
71
85
  /**
72
86
  * Triggers verification email send for a newly created account.
73
87
  *
@@ -127,14 +141,65 @@ export async function verifyEmail(token: string): Promise<VerifyEmailResponse> {
127
141
  });
128
142
 
129
143
  if (!response.ok) {
144
+ // Prefer the server's explanatory message (expired token, email already
145
+ // verified on another account, …) over a bare HTTP status.
146
+ let serverError: string | undefined;
147
+ try {
148
+ serverError = (await response.json())?.error;
149
+ } catch {
150
+ // non-JSON body — fall through to the generic message
151
+ }
130
152
  return {
131
153
  ok: false,
132
- error: `HTTP ${response.status}: ${response.statusText}`,
154
+ error: serverError || `HTTP ${response.status}: ${response.statusText}`,
155
+ };
156
+ }
157
+
158
+ return await response.json();
159
+ } catch (error) {
160
+ return {
161
+ ok: false,
162
+ error: error instanceof Error ? error.message : 'Network error',
163
+ };
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Resolve a login identifier (username OR email) to a couch username, gated on
169
+ * the correct password. Returns the username only on success; every failure is
170
+ * a uniform "invalid credentials" (no account-enumeration leak). Used by the
171
+ * login form before calling user.login() when the identifier is an email.
172
+ */
173
+ export async function resolveLoginIdentifier(
174
+ identifier: string,
175
+ password: string
176
+ ): Promise<ResolveLoginResponse> {
177
+ try {
178
+ const response = await fetch(`${getApiBase()}/auth/resolve-login`, {
179
+ method: 'POST',
180
+ headers: { 'Content-Type': 'application/json' },
181
+ credentials: 'include',
182
+ body: JSON.stringify({ identifier, password }),
183
+ });
184
+
185
+ if (!response.ok) {
186
+ let serverError: string | undefined;
187
+ try {
188
+ serverError = (await response.json())?.error;
189
+ } catch {
190
+ // non-JSON body — fall through to the generic message
191
+ }
192
+ return {
193
+ ok: false,
194
+ status: response.status,
195
+ error: serverError || `HTTP ${response.status}: ${response.statusText}`,
133
196
  };
134
197
  }
135
198
 
136
199
  return await response.json();
137
200
  } catch (error) {
201
+ // Network / fetch failure — no HTTP status. Left undefined so callers treat
202
+ // it as a system failure, not a credential rejection.
138
203
  return {
139
204
  ok: false,
140
205
  error: error instanceof Error ? error.message : 'Network error',