@tbs-fitness/contract 0.1.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.
Files changed (34) hide show
  1. package/README.md +20 -0
  2. package/dist/cjs/audit/audit-log.dto.js +2 -1
  3. package/dist/cjs/auth/auth.dto.d.ts +66 -9
  4. package/dist/cjs/auth/auth.dto.js +51 -18
  5. package/dist/cjs/coach/coach-auth.dto.d.ts +114 -0
  6. package/dist/cjs/coach/coach-auth.dto.js +77 -0
  7. package/dist/cjs/coach/index.d.ts +1 -0
  8. package/dist/cjs/coach/index.js +17 -0
  9. package/dist/cjs/common/api-response.schema.js +2 -1
  10. package/dist/cjs/common/date-time.schema.d.ts +39 -0
  11. package/dist/cjs/common/date-time.schema.js +112 -0
  12. package/dist/cjs/common/index.d.ts +1 -0
  13. package/dist/cjs/common/index.js +1 -0
  14. package/dist/cjs/enums/index.d.ts +17 -2
  15. package/dist/cjs/enums/index.js +20 -3
  16. package/dist/cjs/feature-flags/feature-flag.dto.d.ts +7 -7
  17. package/dist/cjs/feature-flags/feature-flag.dto.js +10 -9
  18. package/dist/esm/audit/audit-log.dto.js +2 -1
  19. package/dist/esm/auth/auth.dto.d.ts +66 -9
  20. package/dist/esm/auth/auth.dto.js +47 -14
  21. package/dist/esm/coach/coach-auth.dto.d.ts +114 -0
  22. package/dist/esm/coach/coach-auth.dto.js +74 -0
  23. package/dist/esm/coach/index.d.ts +1 -0
  24. package/dist/esm/coach/index.js +1 -0
  25. package/dist/esm/common/api-response.schema.js +2 -1
  26. package/dist/esm/common/date-time.schema.d.ts +39 -0
  27. package/dist/esm/common/date-time.schema.js +107 -0
  28. package/dist/esm/common/index.d.ts +1 -0
  29. package/dist/esm/common/index.js +1 -0
  30. package/dist/esm/enums/index.d.ts +17 -2
  31. package/dist/esm/enums/index.js +19 -2
  32. package/dist/esm/feature-flags/feature-flag.dto.d.ts +7 -7
  33. package/dist/esm/feature-flags/feature-flag.dto.js +10 -9
  34. package/package.json +11 -1
package/README.md CHANGED
@@ -11,6 +11,7 @@ It is published as `@tbs-fitness/contract`. The package holds definitions only:
11
11
  | `@tbs-fitness/contract/enums` | `ROLES`, `ACCOUNT_STATUSES`, `TOKEN_TRANSPORTS`, `AUDIT_OUTCOMES`, `APP_ENVIRONMENTS`, `FEATURE_FLAG_RULE_TYPES` and their types |
12
12
  | `@tbs-fitness/contract/common` | Pagination, response envelopes (`{ data }`, `{ data, meta }`, `{ error }`), `emailSchema`, `phoneSchema`, `safeTextSchema` |
13
13
  | `@tbs-fitness/contract/auth` | Login, sessions, password changes and resets, account administration |
14
+ | `@tbs-fitness/contract/coach` | Coach sign-up, one-time code verification and resend, coach sign-in and profile, password recovery |
14
15
  | `@tbs-fitness/contract/audit` | The audit log query and response |
15
16
  | `@tbs-fitness/contract/feature-flags` | Feature flag requests and responses |
16
17
 
@@ -40,6 +41,25 @@ const result = createFlagSchema.safeParse(input); // The same rules as the API.
40
41
 
41
42
  Requests have two types. Clients use `*Input`, where fields with a default are optional. The backend uses `*Body`, which is the result after validation. List queries come from the URL as strings, so they have no `*Input`: use `Partial<PaginationQuery>` or `Partial<AuditLogQuery>`.
42
43
 
44
+ ## Version 0.3.0
45
+
46
+ Time zones and the platform's date and time rules. Additive, but a caret range on 0.x does not cross minor versions, so consumers install `^0.3.0` explicitly.
47
+
48
+ - `/common`: `DEFAULT_TIMEZONE` (`UTC`), `isValidTimezone`, `canonicalTimezone` (the current IANA name for a renamed zone, such as `Asia/Kolkata` for `Asia/Calcutta`, which Node and some browsers still report; both schemas below apply it), `timezoneSchema` (an IANA `Area/Location` name or `UTC`; abbreviations such as `IST` and offsets such as `+05:30` are refused), `detectedTimezoneSchema` (a browser suggestion: unusable values become `undefined`), `utcTimestampSchema` (response instants, `Z` only), `instantInputSchema` (request instants, `Z` or an explicit offset, never a bare local time) and `dateOnlySchema` (`YYYY-MM-DD`, never shifted between zones).
49
+ - `/enums`: `TIMEZONE_SOURCES` (`AUTO`, `MANUAL`).
50
+ - `/auth`: `timezonePreferenceSchema`, `updateTimezoneSchema` (`{ timezone, source? }`, source defaults to `MANUAL`), and `timezone` and `timezoneSource` on `accountResponseSchema`.
51
+ - `/coach`: an optional `timezone` on `coachSignupSchema` and `coachLoginSchema`, and `timezone` and `timezoneSource` on `coachProfileSchema`. Both response fields are required, so deploy the backend before a client built on 0.3.0.
52
+ - Every response timestamp now uses `utcTimestampSchema`, which is the same rule as before. Feature-flag request timestamps use `instantInputSchema`.
53
+
54
+ ## Version 0.2.0 (breaking)
55
+
56
+ - `ROLES` is now `ADMIN`, `COACH` and `CUSTOMER`. A user has exactly one role.
57
+ - `ACCOUNT_STATUSES` adds `pending_verification`. Administrators set only `ADMIN_SETTABLE_ACCOUNT_STATUSES`.
58
+ - `setRolesSchema` is replaced by `setRoleSchema` (`{ role }`), and `adminAccountResponseSchema` has `role` instead of `roles`. `accountResponseSchema` adds `role` and keeps `roles` as `[role]`.
59
+ - New: `@tbs-fitness/contract/coach`, and `OTP_LENGTH`, `otpCodeSchema`, `opaqueTokenSchema` and `submittedPasswordSchema` in `/auth`.
60
+
61
+ A caret range on 0.x does not cross minor versions, so consumers must install `^0.2.0` explicitly.
62
+
43
63
  ## Installing it
44
64
 
45
65
  The package is public on npm, so installing it needs no token:
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.auditLogResponseSchema = exports.auditLogQuerySchema = void 0;
4
4
  const zod_1 = require("zod");
5
+ const date_time_schema_js_1 = require("../common/date-time.schema.js");
5
6
  const pagination_schema_js_1 = require("../common/pagination.schema.js");
6
7
  const index_js_1 = require("../enums/index.js");
7
8
  exports.auditLogQuerySchema = pagination_schema_js_1.paginationQuerySchema.extend({
@@ -20,5 +21,5 @@ exports.auditLogResponseSchema = zod_1.z.object({
20
21
  requestId: zod_1.z.string().nullable(),
21
22
  correlationId: zod_1.z.string().nullable(),
22
23
  metadata: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).nullable(),
23
- createdAt: zod_1.z.iso.datetime(),
24
+ createdAt: date_time_schema_js_1.utcTimestampSchema,
24
25
  });
@@ -1,7 +1,13 @@
1
1
  import { z } from 'zod';
2
2
  export declare const MIN_PASSWORD_LENGTH = 12;
3
3
  export declare const MAX_PASSWORD_LENGTH = 128;
4
+ /** Every one-time code the API sends has this many digits. */
5
+ export declare const OTP_LENGTH = 6;
6
+ export declare const otpCodeSchema: z.ZodString;
4
7
  export declare const newPasswordSchema: z.ZodString;
8
+ /** No minimum at login, so an old short password fails like any wrong password. */
9
+ export declare const submittedPasswordSchema: z.ZodString;
10
+ export declare const opaqueTokenSchema: z.ZodString;
5
11
  export declare const loginSchema: z.ZodObject<{
6
12
  email: z.ZodPipe<z.ZodString, z.ZodEmail>;
7
13
  password: z.ZodString;
@@ -35,15 +41,15 @@ export declare const passwordResetConfirmSchema: z.ZodObject<{
35
41
  }, z.core.$strict>;
36
42
  export type PasswordResetConfirmInput = z.input<typeof passwordResetConfirmSchema>;
37
43
  export type PasswordResetConfirmBody = z.output<typeof passwordResetConfirmSchema>;
38
- export declare const setRolesSchema: z.ZodObject<{
39
- roles: z.ZodArray<z.ZodEnum<{
40
- admin: "admin";
41
- trainer: "trainer";
42
- customer: "customer";
43
- }>>;
44
+ export declare const setRoleSchema: z.ZodObject<{
45
+ role: z.ZodEnum<{
46
+ ADMIN: "ADMIN";
47
+ COACH: "COACH";
48
+ CUSTOMER: "CUSTOMER";
49
+ }>;
44
50
  }, z.core.$strict>;
45
- export type SetRolesInput = z.input<typeof setRolesSchema>;
46
- export type SetRolesBody = z.output<typeof setRolesSchema>;
51
+ export type SetRoleInput = z.input<typeof setRoleSchema>;
52
+ export type SetRoleBody = z.output<typeof setRoleSchema>;
47
53
  export declare const setStatusSchema: z.ZodObject<{
48
54
  status: z.ZodEnum<{
49
55
  active: "active";
@@ -62,12 +68,48 @@ export declare const tokenResponseSchema: z.ZodObject<{
62
68
  sessionId: z.ZodUUID;
63
69
  }, z.core.$strip>;
64
70
  export type TokenResponse = z.output<typeof tokenResponseSchema>;
71
+ /**
72
+ * A user's time zone preference: an IANA zone, and whether it was detected
73
+ * (`AUTO`) or chosen (`MANUAL`).
74
+ */
75
+ export declare const timezonePreferenceSchema: z.ZodObject<{
76
+ timezone: z.ZodString;
77
+ timezoneSource: z.ZodEnum<{
78
+ AUTO: "AUTO";
79
+ MANUAL: "MANUAL";
80
+ }>;
81
+ }, z.core.$strip>;
82
+ export type TimezonePreference = z.output<typeof timezonePreferenceSchema>;
83
+ /**
84
+ * Sets the signed-in user's own time zone. `MANUAL` (the default) is a choice
85
+ * the user made. `AUTO` records a detected zone that a later sign-in may
86
+ * replace.
87
+ */
88
+ export declare const updateTimezoneSchema: z.ZodObject<{
89
+ timezone: z.ZodPipe<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>, z.ZodTransform<string, string>>;
90
+ source: z.ZodDefault<z.ZodEnum<{
91
+ AUTO: "AUTO";
92
+ MANUAL: "MANUAL";
93
+ }>>;
94
+ }, z.core.$strict>;
95
+ export type UpdateTimezoneInput = z.input<typeof updateTimezoneSchema>;
96
+ export type UpdateTimezoneBody = z.output<typeof updateTimezoneSchema>;
65
97
  export declare const accountResponseSchema: z.ZodObject<{
66
98
  id: z.ZodUUID;
67
99
  email: z.ZodString;
100
+ role: z.ZodEnum<{
101
+ ADMIN: "ADMIN";
102
+ COACH: "COACH";
103
+ CUSTOMER: "CUSTOMER";
104
+ }>;
68
105
  roles: z.ZodArray<z.ZodString>;
69
106
  permissions: z.ZodArray<z.ZodString>;
70
107
  lastLoginAt: z.ZodNullable<z.ZodISODateTime>;
108
+ timezone: z.ZodString;
109
+ timezoneSource: z.ZodEnum<{
110
+ AUTO: "AUTO";
111
+ MANUAL: "MANUAL";
112
+ }>;
71
113
  }, z.core.$strip>;
72
114
  export type AccountResponse = z.output<typeof accountResponseSchema>;
73
115
  export declare const loginResponseSchema: z.ZodObject<{
@@ -80,9 +122,19 @@ export declare const loginResponseSchema: z.ZodObject<{
80
122
  account: z.ZodObject<{
81
123
  id: z.ZodUUID;
82
124
  email: z.ZodString;
125
+ role: z.ZodEnum<{
126
+ ADMIN: "ADMIN";
127
+ COACH: "COACH";
128
+ CUSTOMER: "CUSTOMER";
129
+ }>;
83
130
  roles: z.ZodArray<z.ZodString>;
84
131
  permissions: z.ZodArray<z.ZodString>;
85
132
  lastLoginAt: z.ZodNullable<z.ZodISODateTime>;
133
+ timezone: z.ZodString;
134
+ timezoneSource: z.ZodEnum<{
135
+ AUTO: "AUTO";
136
+ MANUAL: "MANUAL";
137
+ }>;
86
138
  }, z.core.$strip>;
87
139
  }, z.core.$strip>;
88
140
  export type LoginResponse = z.output<typeof loginResponseSchema>;
@@ -107,8 +159,13 @@ export type PasswordResetRequestedResponse = z.output<typeof passwordResetReques
107
159
  export declare const adminAccountResponseSchema: z.ZodObject<{
108
160
  id: z.ZodUUID;
109
161
  email: z.ZodString;
110
- roles: z.ZodArray<z.ZodString>;
162
+ role: z.ZodEnum<{
163
+ ADMIN: "ADMIN";
164
+ COACH: "COACH";
165
+ CUSTOMER: "CUSTOMER";
166
+ }>;
111
167
  status: z.ZodEnum<{
168
+ pending_verification: "pending_verification";
112
169
  active: "active";
113
170
  disabled: "disabled";
114
171
  }>;
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.revokedSessionsResponseSchema = exports.adminAccountResponseSchema = exports.passwordResetRequestedResponseSchema = exports.logoutAllResponseSchema = exports.sessionResponseSchema = exports.loginResponseSchema = exports.accountResponseSchema = exports.tokenResponseSchema = exports.idParamSchema = exports.setStatusSchema = exports.setRolesSchema = exports.passwordResetConfirmSchema = exports.passwordResetRequestSchema = exports.changePasswordSchema = exports.refreshSchema = exports.loginSchema = exports.newPasswordSchema = exports.MAX_PASSWORD_LENGTH = exports.MIN_PASSWORD_LENGTH = void 0;
3
+ exports.revokedSessionsResponseSchema = exports.adminAccountResponseSchema = exports.passwordResetRequestedResponseSchema = exports.logoutAllResponseSchema = exports.sessionResponseSchema = exports.loginResponseSchema = exports.accountResponseSchema = exports.updateTimezoneSchema = exports.timezonePreferenceSchema = exports.tokenResponseSchema = exports.idParamSchema = exports.setStatusSchema = exports.setRoleSchema = exports.passwordResetConfirmSchema = exports.passwordResetRequestSchema = exports.changePasswordSchema = exports.refreshSchema = exports.loginSchema = exports.opaqueTokenSchema = exports.submittedPasswordSchema = exports.newPasswordSchema = exports.otpCodeSchema = exports.OTP_LENGTH = exports.MAX_PASSWORD_LENGTH = exports.MIN_PASSWORD_LENGTH = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const contact_schema_js_1 = require("../common/contact.schema.js");
6
+ const date_time_schema_js_1 = require("../common/date-time.schema.js");
6
7
  const safe_text_schema_js_1 = require("../common/safe-text.schema.js");
7
8
  const index_js_1 = require("../enums/index.js");
8
9
  // Per OWASP: length is what matters, with no composition rules. The upper limit
@@ -10,40 +11,51 @@ const index_js_1 = require("../enums/index.js");
10
11
  exports.MIN_PASSWORD_LENGTH = 12;
11
12
  exports.MAX_PASSWORD_LENGTH = 128;
12
13
  const MAX_OPAQUE_TOKEN_LENGTH = 512;
14
+ /** Every one-time code the API sends has this many digits. */
15
+ exports.OTP_LENGTH = 6;
16
+ exports.otpCodeSchema = zod_1.z
17
+ .string()
18
+ .trim()
19
+ .regex(new RegExp(`^\\d{${exports.OTP_LENGTH}}$`), {
20
+ error: `Enter the ${exports.OTP_LENGTH}-digit code`,
21
+ });
13
22
  exports.newPasswordSchema = zod_1.z
14
23
  .string()
15
24
  .min(exports.MIN_PASSWORD_LENGTH)
16
25
  .max(exports.MAX_PASSWORD_LENGTH);
17
26
  /** No minimum at login, so an old short password fails like any wrong password. */
18
- const submittedPasswordSchema = zod_1.z.string().min(1).max(exports.MAX_PASSWORD_LENGTH);
19
- const opaqueTokenSchema = zod_1.z.string().min(1).max(MAX_OPAQUE_TOKEN_LENGTH);
27
+ exports.submittedPasswordSchema = zod_1.z
28
+ .string()
29
+ .min(1)
30
+ .max(exports.MAX_PASSWORD_LENGTH);
31
+ exports.opaqueTokenSchema = zod_1.z.string().min(1).max(MAX_OPAQUE_TOKEN_LENGTH);
20
32
  // Strict objects reject unknown fields, so an attempt to send `roles` or
21
33
  // `status` fails loudly rather than being silently ignored.
22
34
  exports.loginSchema = zod_1.z.strictObject({
23
35
  email: contact_schema_js_1.emailSchema,
24
- password: submittedPasswordSchema,
36
+ password: exports.submittedPasswordSchema,
25
37
  deviceName: (0, safe_text_schema_js_1.safeTextSchema)(100).optional(),
26
38
  tokenTransport: zod_1.z.enum(index_js_1.TOKEN_TRANSPORTS).default('body'),
27
39
  });
28
40
  exports.refreshSchema = zod_1.z
29
- .strictObject({ refreshToken: opaqueTokenSchema.optional() })
41
+ .strictObject({ refreshToken: exports.opaqueTokenSchema.optional() })
30
42
  .default({});
31
43
  exports.changePasswordSchema = zod_1.z.strictObject({
32
- currentPassword: submittedPasswordSchema,
44
+ currentPassword: exports.submittedPasswordSchema,
33
45
  newPassword: exports.newPasswordSchema,
34
46
  });
35
47
  exports.passwordResetRequestSchema = zod_1.z.strictObject({
36
48
  email: contact_schema_js_1.emailSchema,
37
49
  });
38
50
  exports.passwordResetConfirmSchema = zod_1.z.strictObject({
39
- token: opaqueTokenSchema,
51
+ token: exports.opaqueTokenSchema,
40
52
  newPassword: exports.newPasswordSchema,
41
53
  });
42
- exports.setRolesSchema = zod_1.z.strictObject({
43
- roles: zod_1.z.array(zod_1.z.enum(index_js_1.ROLES)).max(index_js_1.ROLES.length),
54
+ exports.setRoleSchema = zod_1.z.strictObject({
55
+ role: zod_1.z.enum(index_js_1.ROLES),
44
56
  });
45
57
  exports.setStatusSchema = zod_1.z.strictObject({
46
- status: zod_1.z.enum(index_js_1.ACCOUNT_STATUSES),
58
+ status: zod_1.z.enum(index_js_1.ADMIN_SETTABLE_ACCOUNT_STATUSES),
47
59
  });
48
60
  exports.idParamSchema = zod_1.z.uuid();
49
61
  exports.tokenResponseSchema = zod_1.z.object({
@@ -54,13 +66,34 @@ exports.tokenResponseSchema = zod_1.z.object({
54
66
  csrfToken: zod_1.z.string().optional(),
55
67
  sessionId: zod_1.z.uuid(),
56
68
  });
57
- exports.accountResponseSchema = zod_1.z.object({
69
+ /**
70
+ * A user's time zone preference: an IANA zone, and whether it was detected
71
+ * (`AUTO`) or chosen (`MANUAL`).
72
+ */
73
+ exports.timezonePreferenceSchema = zod_1.z.object({
74
+ timezone: zod_1.z.string(),
75
+ timezoneSource: zod_1.z.enum(index_js_1.TIMEZONE_SOURCES),
76
+ });
77
+ /**
78
+ * Sets the signed-in user's own time zone. `MANUAL` (the default) is a choice
79
+ * the user made. `AUTO` records a detected zone that a later sign-in may
80
+ * replace.
81
+ */
82
+ exports.updateTimezoneSchema = zod_1.z.strictObject({
83
+ timezone: date_time_schema_js_1.timezoneSchema,
84
+ source: zod_1.z.enum(index_js_1.TIMEZONE_SOURCES).default('MANUAL'),
85
+ });
86
+ exports.accountResponseSchema = zod_1.z
87
+ .object({
58
88
  id: zod_1.z.uuid(),
59
89
  email: zod_1.z.string(),
90
+ role: zod_1.z.enum(index_js_1.ROLES),
91
+ /** @deprecated Always `[role]`. Kept for clients written before `role`. */
60
92
  roles: zod_1.z.array(zod_1.z.string()),
61
93
  permissions: zod_1.z.array(zod_1.z.string()),
62
- lastLoginAt: zod_1.z.iso.datetime().nullable(),
63
- });
94
+ lastLoginAt: date_time_schema_js_1.utcTimestampSchema.nullable(),
95
+ })
96
+ .extend(exports.timezonePreferenceSchema.shape);
64
97
  exports.loginResponseSchema = exports.tokenResponseSchema.extend({
65
98
  account: exports.accountResponseSchema,
66
99
  });
@@ -68,9 +101,9 @@ exports.sessionResponseSchema = zod_1.z.object({
68
101
  id: zod_1.z.uuid(),
69
102
  deviceName: zod_1.z.string().nullable(),
70
103
  userAgent: zod_1.z.string().nullable(),
71
- createdAt: zod_1.z.iso.datetime(),
72
- lastUsedAt: zod_1.z.iso.datetime(),
73
- expiresAt: zod_1.z.iso.datetime(),
104
+ createdAt: date_time_schema_js_1.utcTimestampSchema,
105
+ lastUsedAt: date_time_schema_js_1.utcTimestampSchema,
106
+ expiresAt: date_time_schema_js_1.utcTimestampSchema,
74
107
  current: zod_1.z.boolean(),
75
108
  });
76
109
  exports.logoutAllResponseSchema = zod_1.z.object({
@@ -82,9 +115,9 @@ exports.passwordResetRequestedResponseSchema = zod_1.z.object({
82
115
  exports.adminAccountResponseSchema = zod_1.z.object({
83
116
  id: zod_1.z.uuid(),
84
117
  email: zod_1.z.string(),
85
- roles: zod_1.z.array(zod_1.z.string()),
118
+ role: zod_1.z.enum(index_js_1.ROLES),
86
119
  status: zod_1.z.enum(index_js_1.ACCOUNT_STATUSES),
87
- lastLoginAt: zod_1.z.iso.datetime().nullable(),
120
+ lastLoginAt: date_time_schema_js_1.utcTimestampSchema.nullable(),
88
121
  });
89
122
  exports.revokedSessionsResponseSchema = zod_1.z.object({
90
123
  revokedSessions: zod_1.z.number().int(),
@@ -0,0 +1,114 @@
1
+ import { z } from 'zod';
2
+ export declare const coachSignupSchema: z.ZodObject<{
3
+ fullName: z.ZodPipe<z.ZodString, z.ZodString>;
4
+ email: z.ZodPipe<z.ZodString, z.ZodEmail>;
5
+ phone: z.ZodOptional<z.ZodString>;
6
+ password: z.ZodString;
7
+ timezone: z.ZodOptional<z.ZodPipe<z.ZodUnknown, z.ZodTransform<string | undefined, unknown>>>;
8
+ }, z.core.$strict>;
9
+ export type CoachSignupInput = z.input<typeof coachSignupSchema>;
10
+ export type CoachSignupBody = z.output<typeof coachSignupSchema>;
11
+ /** Used by both resend endpoints and by forgot-password. */
12
+ export declare const otpEmailSchema: z.ZodObject<{
13
+ email: z.ZodPipe<z.ZodString, z.ZodEmail>;
14
+ }, z.core.$strict>;
15
+ export type OtpEmailInput = z.input<typeof otpEmailSchema>;
16
+ export type OtpEmailBody = z.output<typeof otpEmailSchema>;
17
+ export declare const verifyOtpSchema: z.ZodObject<{
18
+ email: z.ZodPipe<z.ZodString, z.ZodEmail>;
19
+ code: z.ZodString;
20
+ }, z.core.$strict>;
21
+ export type VerifyOtpInput = z.input<typeof verifyOtpSchema>;
22
+ export type VerifyOtpBody = z.output<typeof verifyOtpSchema>;
23
+ /**
24
+ * Coaches sign in from the browser, so the refresh token always travels in an
25
+ * HttpOnly cookie. `rememberMe: false` makes that cookie end with the browser
26
+ * session instead of persisting.
27
+ */
28
+ export declare const coachLoginSchema: z.ZodObject<{
29
+ email: z.ZodPipe<z.ZodString, z.ZodEmail>;
30
+ password: z.ZodString;
31
+ deviceName: z.ZodOptional<z.ZodString>;
32
+ rememberMe: z.ZodDefault<z.ZodBoolean>;
33
+ timezone: z.ZodOptional<z.ZodPipe<z.ZodUnknown, z.ZodTransform<string | undefined, unknown>>>;
34
+ }, z.core.$strict>;
35
+ export type CoachLoginInput = z.input<typeof coachLoginSchema>;
36
+ export type CoachLoginBody = z.output<typeof coachLoginSchema>;
37
+ export declare const resetPasswordSchema: z.ZodObject<{
38
+ resetToken: z.ZodString;
39
+ newPassword: z.ZodString;
40
+ }, z.core.$strict>;
41
+ export type ResetPasswordInput = z.input<typeof resetPasswordSchema>;
42
+ export type ResetPasswordBody = z.output<typeof resetPasswordSchema>;
43
+ /**
44
+ * Says what the client needs to drive the code screen, and nothing about
45
+ * whether the email belongs to an account: the same values are returned either
46
+ * way.
47
+ */
48
+ export declare const otpDispatchResponseSchema: z.ZodObject<{
49
+ message: z.ZodString;
50
+ otpLength: z.ZodNumber;
51
+ expiresInSeconds: z.ZodNumber;
52
+ resendAvailableInSeconds: z.ZodNumber;
53
+ }, z.core.$strip>;
54
+ export type OtpDispatchResponse = z.output<typeof otpDispatchResponseSchema>;
55
+ export declare const signupVerifiedResponseSchema: z.ZodObject<{
56
+ message: z.ZodString;
57
+ }, z.core.$strip>;
58
+ export type SignupVerifiedResponse = z.output<typeof signupVerifiedResponseSchema>;
59
+ /** Issued only after a recovery code is verified. It can reset a password once. */
60
+ export declare const resetTokenResponseSchema: z.ZodObject<{
61
+ resetToken: z.ZodString;
62
+ expiresInSeconds: z.ZodNumber;
63
+ }, z.core.$strip>;
64
+ export type ResetTokenResponse = z.output<typeof resetTokenResponseSchema>;
65
+ export declare const coachProfileSchema: z.ZodObject<{
66
+ id: z.ZodUUID;
67
+ email: z.ZodString;
68
+ fullName: z.ZodNullable<z.ZodString>;
69
+ phone: z.ZodNullable<z.ZodString>;
70
+ role: z.ZodLiteral<"COACH">;
71
+ status: z.ZodEnum<{
72
+ pending_verification: "pending_verification";
73
+ active: "active";
74
+ disabled: "disabled";
75
+ }>;
76
+ emailVerifiedAt: z.ZodNullable<z.ZodISODateTime>;
77
+ lastLoginAt: z.ZodNullable<z.ZodISODateTime>;
78
+ createdAt: z.ZodISODateTime;
79
+ timezone: z.ZodString;
80
+ timezoneSource: z.ZodEnum<{
81
+ AUTO: "AUTO";
82
+ MANUAL: "MANUAL";
83
+ }>;
84
+ }, z.core.$strip>;
85
+ export type CoachProfile = z.output<typeof coachProfileSchema>;
86
+ export declare const coachLoginResponseSchema: z.ZodObject<{
87
+ accessToken: z.ZodString;
88
+ tokenType: z.ZodLiteral<"Bearer">;
89
+ expiresInSeconds: z.ZodNumber;
90
+ refreshToken: z.ZodOptional<z.ZodString>;
91
+ csrfToken: z.ZodOptional<z.ZodString>;
92
+ sessionId: z.ZodUUID;
93
+ coach: z.ZodObject<{
94
+ id: z.ZodUUID;
95
+ email: z.ZodString;
96
+ fullName: z.ZodNullable<z.ZodString>;
97
+ phone: z.ZodNullable<z.ZodString>;
98
+ role: z.ZodLiteral<"COACH">;
99
+ status: z.ZodEnum<{
100
+ pending_verification: "pending_verification";
101
+ active: "active";
102
+ disabled: "disabled";
103
+ }>;
104
+ emailVerifiedAt: z.ZodNullable<z.ZodISODateTime>;
105
+ lastLoginAt: z.ZodNullable<z.ZodISODateTime>;
106
+ createdAt: z.ZodISODateTime;
107
+ timezone: z.ZodString;
108
+ timezoneSource: z.ZodEnum<{
109
+ AUTO: "AUTO";
110
+ MANUAL: "MANUAL";
111
+ }>;
112
+ }, z.core.$strip>;
113
+ }, z.core.$strip>;
114
+ export type CoachLoginResponse = z.output<typeof coachLoginResponseSchema>;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.coachLoginResponseSchema = exports.coachProfileSchema = exports.resetTokenResponseSchema = exports.signupVerifiedResponseSchema = exports.otpDispatchResponseSchema = exports.resetPasswordSchema = exports.coachLoginSchema = exports.verifyOtpSchema = exports.otpEmailSchema = exports.coachSignupSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ const auth_dto_js_1 = require("../auth/auth.dto.js");
6
+ const contact_schema_js_1 = require("../common/contact.schema.js");
7
+ const date_time_schema_js_1 = require("../common/date-time.schema.js");
8
+ const safe_text_schema_js_1 = require("../common/safe-text.schema.js");
9
+ const index_js_1 = require("../enums/index.js");
10
+ exports.coachSignupSchema = zod_1.z.strictObject({
11
+ fullName: (0, safe_text_schema_js_1.safeTextSchema)(100).pipe(zod_1.z.string().min(2, { error: 'Enter your full name' })),
12
+ email: contact_schema_js_1.emailSchema,
13
+ phone: contact_schema_js_1.phoneSchema.optional(),
14
+ password: auth_dto_js_1.newPasswordSchema,
15
+ /** The browser's time zone. Missing or unusable becomes UTC on the server. */
16
+ timezone: date_time_schema_js_1.detectedTimezoneSchema,
17
+ });
18
+ /** Used by both resend endpoints and by forgot-password. */
19
+ exports.otpEmailSchema = zod_1.z.strictObject({ email: contact_schema_js_1.emailSchema });
20
+ exports.verifyOtpSchema = zod_1.z.strictObject({
21
+ email: contact_schema_js_1.emailSchema,
22
+ code: auth_dto_js_1.otpCodeSchema,
23
+ });
24
+ /**
25
+ * Coaches sign in from the browser, so the refresh token always travels in an
26
+ * HttpOnly cookie. `rememberMe: false` makes that cookie end with the browser
27
+ * session instead of persisting.
28
+ */
29
+ exports.coachLoginSchema = auth_dto_js_1.loginSchema
30
+ .omit({ tokenTransport: true })
31
+ .extend({
32
+ rememberMe: zod_1.z.boolean().default(true),
33
+ /**
34
+ * The browser's time zone. It replaces the saved one only while the
35
+ * coach's preference is `AUTO`; a manual choice is never overwritten.
36
+ */
37
+ timezone: date_time_schema_js_1.detectedTimezoneSchema,
38
+ });
39
+ exports.resetPasswordSchema = zod_1.z.strictObject({
40
+ resetToken: auth_dto_js_1.opaqueTokenSchema,
41
+ newPassword: auth_dto_js_1.newPasswordSchema,
42
+ });
43
+ /**
44
+ * Says what the client needs to drive the code screen, and nothing about
45
+ * whether the email belongs to an account: the same values are returned either
46
+ * way.
47
+ */
48
+ exports.otpDispatchResponseSchema = zod_1.z.object({
49
+ message: zod_1.z.string(),
50
+ otpLength: zod_1.z.number().int(),
51
+ expiresInSeconds: zod_1.z.number().int(),
52
+ resendAvailableInSeconds: zod_1.z.number().int(),
53
+ });
54
+ exports.signupVerifiedResponseSchema = zod_1.z.object({
55
+ message: zod_1.z.string(),
56
+ });
57
+ /** Issued only after a recovery code is verified. It can reset a password once. */
58
+ exports.resetTokenResponseSchema = zod_1.z.object({
59
+ resetToken: zod_1.z.string(),
60
+ expiresInSeconds: zod_1.z.number().int(),
61
+ });
62
+ exports.coachProfileSchema = zod_1.z
63
+ .object({
64
+ id: zod_1.z.uuid(),
65
+ email: zod_1.z.string(),
66
+ fullName: zod_1.z.string().nullable(),
67
+ phone: zod_1.z.string().nullable(),
68
+ role: zod_1.z.literal('COACH'),
69
+ status: zod_1.z.enum(index_js_1.ACCOUNT_STATUSES),
70
+ emailVerifiedAt: date_time_schema_js_1.utcTimestampSchema.nullable(),
71
+ lastLoginAt: date_time_schema_js_1.utcTimestampSchema.nullable(),
72
+ createdAt: date_time_schema_js_1.utcTimestampSchema,
73
+ })
74
+ .extend(auth_dto_js_1.timezonePreferenceSchema.shape);
75
+ exports.coachLoginResponseSchema = auth_dto_js_1.tokenResponseSchema.extend({
76
+ coach: exports.coachProfileSchema,
77
+ });
@@ -0,0 +1 @@
1
+ export * from './coach-auth.dto.js';
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./coach-auth.dto.js"), exports);
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.paginatedResponseSchema = exports.dataResponseSchema = exports.apiErrorResponseSchema = void 0;
4
4
  const zod_1 = require("zod");
5
+ const date_time_schema_js_1 = require("./date-time.schema.js");
5
6
  const pagination_schema_js_1 = require("./pagination.schema.js");
6
7
  exports.apiErrorResponseSchema = zod_1.z.object({
7
8
  error: zod_1.z.object({
@@ -16,7 +17,7 @@ exports.apiErrorResponseSchema = zod_1.z.object({
16
17
  }))
17
18
  .optional(),
18
19
  requestId: zod_1.z.string(),
19
- timestamp: zod_1.z.iso.datetime(),
20
+ timestamp: date_time_schema_js_1.utcTimestampSchema,
20
21
  path: zod_1.z.string(),
21
22
  }),
22
23
  });
@@ -0,0 +1,39 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * The platform's date and time rules, shared so the backend and the apps
4
+ * validate them identically:
5
+ *
6
+ * - An instant (something that happened at one moment) travels as ISO 8601.
7
+ * Responses always use UTC with `Z`. Requests may use `Z` or an explicit
8
+ * offset, but never a bare local time, which would be ambiguous.
9
+ * - A date-only value (a birthday, an expiry date) travels as `YYYY-MM-DD` and
10
+ * is never converted between time zones.
11
+ * - A time zone is an IANA identifier such as `Asia/Kolkata`. Abbreviations
12
+ * (`IST`) and fixed offsets (`+05:30`) are refused, because offsets change
13
+ * with daylight saving.
14
+ */
15
+ export declare const DEFAULT_TIMEZONE = "UTC";
16
+ /**
17
+ * True for `UTC` and for an IANA `Area/Location` name the runtime's time zone
18
+ * database knows. Runs anywhere `Intl` does, so the browser and the server
19
+ * agree.
20
+ */
21
+ export declare function isValidTimezone(value: unknown): value is string;
22
+ /** The current IANA name for a zone, such as `Asia/Kolkata` for `Asia/Calcutta`. */
23
+ export declare function canonicalTimezone(value: string): string;
24
+ export declare const timezoneSchema: z.ZodPipe<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>, z.ZodTransform<string, string>>;
25
+ /**
26
+ * A time zone the client detected and suggests, such as at sign-up. A missing
27
+ * or unusable value becomes `undefined` instead of failing the request, and the
28
+ * server then applies its default.
29
+ */
30
+ export declare const detectedTimezoneSchema: z.ZodOptional<z.ZodPipe<z.ZodUnknown, z.ZodTransform<string | undefined, unknown>>>;
31
+ /** An instant in a response: ISO 8601 in UTC, ending in `Z`. */
32
+ export declare const utcTimestampSchema: z.ZodISODateTime;
33
+ /**
34
+ * An instant in a request: ISO 8601 with `Z` or an explicit offset, such as
35
+ * `2026-11-02T15:00:00+05:30`. A local time without either is refused.
36
+ */
37
+ export declare const instantInputSchema: z.ZodISODateTime;
38
+ /** A calendar date with no time and no zone, such as `2026-11-02`. */
39
+ export declare const dateOnlySchema: z.ZodString;