@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.
- package/README.md +20 -0
- package/dist/cjs/audit/audit-log.dto.js +2 -1
- package/dist/cjs/auth/auth.dto.d.ts +66 -9
- package/dist/cjs/auth/auth.dto.js +51 -18
- package/dist/cjs/coach/coach-auth.dto.d.ts +114 -0
- package/dist/cjs/coach/coach-auth.dto.js +77 -0
- package/dist/cjs/coach/index.d.ts +1 -0
- package/dist/cjs/coach/index.js +17 -0
- package/dist/cjs/common/api-response.schema.js +2 -1
- package/dist/cjs/common/date-time.schema.d.ts +39 -0
- package/dist/cjs/common/date-time.schema.js +112 -0
- package/dist/cjs/common/index.d.ts +1 -0
- package/dist/cjs/common/index.js +1 -0
- package/dist/cjs/enums/index.d.ts +17 -2
- package/dist/cjs/enums/index.js +20 -3
- package/dist/cjs/feature-flags/feature-flag.dto.d.ts +7 -7
- package/dist/cjs/feature-flags/feature-flag.dto.js +10 -9
- package/dist/esm/audit/audit-log.dto.js +2 -1
- package/dist/esm/auth/auth.dto.d.ts +66 -9
- package/dist/esm/auth/auth.dto.js +47 -14
- package/dist/esm/coach/coach-auth.dto.d.ts +114 -0
- package/dist/esm/coach/coach-auth.dto.js +74 -0
- package/dist/esm/coach/index.d.ts +1 -0
- package/dist/esm/coach/index.js +1 -0
- package/dist/esm/common/api-response.schema.js +2 -1
- package/dist/esm/common/date-time.schema.d.ts +39 -0
- package/dist/esm/common/date-time.schema.js +107 -0
- package/dist/esm/common/index.d.ts +1 -0
- package/dist/esm/common/index.js +1 -0
- package/dist/esm/enums/index.d.ts +17 -2
- package/dist/esm/enums/index.js +19 -2
- package/dist/esm/feature-flags/feature-flag.dto.d.ts +7 -7
- package/dist/esm/feature-flags/feature-flag.dto.js +10 -9
- package/package.json +11 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dateOnlySchema = exports.instantInputSchema = exports.utcTimestampSchema = exports.detectedTimezoneSchema = exports.timezoneSchema = exports.DEFAULT_TIMEZONE = void 0;
|
|
4
|
+
exports.isValidTimezone = isValidTimezone;
|
|
5
|
+
exports.canonicalTimezone = canonicalTimezone;
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
/**
|
|
8
|
+
* The platform's date and time rules, shared so the backend and the apps
|
|
9
|
+
* validate them identically:
|
|
10
|
+
*
|
|
11
|
+
* - An instant (something that happened at one moment) travels as ISO 8601.
|
|
12
|
+
* Responses always use UTC with `Z`. Requests may use `Z` or an explicit
|
|
13
|
+
* offset, but never a bare local time, which would be ambiguous.
|
|
14
|
+
* - A date-only value (a birthday, an expiry date) travels as `YYYY-MM-DD` and
|
|
15
|
+
* is never converted between time zones.
|
|
16
|
+
* - A time zone is an IANA identifier such as `Asia/Kolkata`. Abbreviations
|
|
17
|
+
* (`IST`) and fixed offsets (`+05:30`) are refused, because offsets change
|
|
18
|
+
* with daylight saving.
|
|
19
|
+
*/
|
|
20
|
+
exports.DEFAULT_TIMEZONE = 'UTC';
|
|
21
|
+
const MAX_TIMEZONE_LENGTH = 64;
|
|
22
|
+
// The IANA areas used for real places. `Etc/*` and the legacy aliases
|
|
23
|
+
// (`US/Eastern`, `EST`) are left out: the first are fixed offsets, and the
|
|
24
|
+
// second have modern names.
|
|
25
|
+
const AREA_LOCATION = /^(?:Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Europe|Indian|Pacific)\/[A-Za-z0-9_+-]+(?:\/[A-Za-z0-9_+-]+)?$/;
|
|
26
|
+
/**
|
|
27
|
+
* True for `UTC` and for an IANA `Area/Location` name the runtime's time zone
|
|
28
|
+
* database knows. Runs anywhere `Intl` does, so the browser and the server
|
|
29
|
+
* agree.
|
|
30
|
+
*/
|
|
31
|
+
function isValidTimezone(value) {
|
|
32
|
+
if (typeof value !== 'string' || value.length > MAX_TIMEZONE_LENGTH) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
if (value !== exports.DEFAULT_TIMEZONE && !AREA_LOCATION.test(value)) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
new Intl.DateTimeFormat('en-US', { timeZone: value });
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Browsers and Node report some zones by names IANA has since replaced, because
|
|
47
|
+
// their Unicode data keeps the old ones. Saving the current name means one
|
|
48
|
+
// place is stored, compared and shown one way everywhere.
|
|
49
|
+
const RENAMED_TIMEZONES = {
|
|
50
|
+
'Asia/Calcutta': 'Asia/Kolkata',
|
|
51
|
+
'Asia/Saigon': 'Asia/Ho_Chi_Minh',
|
|
52
|
+
'Asia/Katmandu': 'Asia/Kathmandu',
|
|
53
|
+
'Asia/Rangoon': 'Asia/Yangon',
|
|
54
|
+
'Europe/Kiev': 'Europe/Kyiv',
|
|
55
|
+
'Europe/Uzhgorod': 'Europe/Kyiv',
|
|
56
|
+
'Europe/Zaporozhye': 'Europe/Kyiv',
|
|
57
|
+
'Atlantic/Faeroe': 'Atlantic/Faroe',
|
|
58
|
+
'America/Godthab': 'America/Nuuk',
|
|
59
|
+
'Pacific/Enderbury': 'Pacific/Kanton',
|
|
60
|
+
'Pacific/Truk': 'Pacific/Chuuk',
|
|
61
|
+
'Pacific/Ponape': 'Pacific/Pohnpei',
|
|
62
|
+
'America/Buenos_Aires': 'America/Argentina/Buenos_Aires',
|
|
63
|
+
'America/Catamarca': 'America/Argentina/Catamarca',
|
|
64
|
+
'America/Cordoba': 'America/Argentina/Cordoba',
|
|
65
|
+
'America/Jujuy': 'America/Argentina/Jujuy',
|
|
66
|
+
'America/Mendoza': 'America/Argentina/Mendoza',
|
|
67
|
+
'America/Indianapolis': 'America/Indiana/Indianapolis',
|
|
68
|
+
'America/Louisville': 'America/Kentucky/Louisville',
|
|
69
|
+
};
|
|
70
|
+
/** The current IANA name for a zone, such as `Asia/Kolkata` for `Asia/Calcutta`. */
|
|
71
|
+
function canonicalTimezone(value) {
|
|
72
|
+
return RENAMED_TIMEZONES[value] ?? value;
|
|
73
|
+
}
|
|
74
|
+
exports.timezoneSchema = zod_1.z
|
|
75
|
+
.string()
|
|
76
|
+
.trim()
|
|
77
|
+
.refine(isValidTimezone, {
|
|
78
|
+
error: 'Choose a time zone such as Asia/Kolkata',
|
|
79
|
+
})
|
|
80
|
+
.transform(canonicalTimezone);
|
|
81
|
+
/**
|
|
82
|
+
* A time zone the client detected and suggests, such as at sign-up. A missing
|
|
83
|
+
* or unusable value becomes `undefined` instead of failing the request, and the
|
|
84
|
+
* server then applies its default.
|
|
85
|
+
*/
|
|
86
|
+
exports.detectedTimezoneSchema = zod_1.z
|
|
87
|
+
.unknown()
|
|
88
|
+
.transform((value) => {
|
|
89
|
+
const trimmed = typeof value === 'string' ? value.trim() : value;
|
|
90
|
+
return isValidTimezone(trimmed) ? canonicalTimezone(trimmed) : undefined;
|
|
91
|
+
})
|
|
92
|
+
.optional();
|
|
93
|
+
/** An instant in a response: ISO 8601 in UTC, ending in `Z`. */
|
|
94
|
+
exports.utcTimestampSchema = zod_1.z.iso.datetime();
|
|
95
|
+
/**
|
|
96
|
+
* An instant in a request: ISO 8601 with `Z` or an explicit offset, such as
|
|
97
|
+
* `2026-11-02T15:00:00+05:30`. A local time without either is refused.
|
|
98
|
+
*/
|
|
99
|
+
exports.instantInputSchema = zod_1.z.iso.datetime({ offset: true });
|
|
100
|
+
/** A calendar date with no time and no zone, such as `2026-11-02`. */
|
|
101
|
+
exports.dateOnlySchema = zod_1.z
|
|
102
|
+
.string()
|
|
103
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/, { error: 'Use the format YYYY-MM-DD' })
|
|
104
|
+
.refine(isRealCalendarDate, { error: 'Enter a real calendar date' });
|
|
105
|
+
function isRealCalendarDate(value) {
|
|
106
|
+
const [year, month, day] = value.split('-').map(Number);
|
|
107
|
+
// UTC is used only to check the calendar, so no zone can move the date.
|
|
108
|
+
const date = new Date(Date.UTC(year, month - 1, day));
|
|
109
|
+
return (date.getUTCFullYear() === year &&
|
|
110
|
+
date.getUTCMonth() === month - 1 &&
|
|
111
|
+
date.getUTCDate() === day);
|
|
112
|
+
}
|
package/dist/cjs/common/index.js
CHANGED
|
@@ -16,5 +16,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./api-response.schema.js"), exports);
|
|
18
18
|
__exportStar(require("./contact.schema.js"), exports);
|
|
19
|
+
__exportStar(require("./date-time.schema.js"), exports);
|
|
19
20
|
__exportStar(require("./pagination.schema.js"), exports);
|
|
20
21
|
__exportStar(require("./safe-text.schema.js"), exports);
|
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
export declare const APP_ENVIRONMENTS: readonly ["development", "staging", "production"];
|
|
2
2
|
export type AppEnvironment = (typeof APP_ENVIRONMENTS)[number];
|
|
3
|
-
|
|
3
|
+
/** Each user has exactly one role. A new role is added here and nowhere else. */
|
|
4
|
+
export declare const ROLES: readonly ["ADMIN", "COACH", "CUSTOMER"];
|
|
4
5
|
export type Role = (typeof ROLES)[number];
|
|
5
|
-
|
|
6
|
+
/**
|
|
7
|
+
* `pending_verification` is a self-registered user who has not yet proved they
|
|
8
|
+
* own their email address. Such a user cannot sign in.
|
|
9
|
+
*/
|
|
10
|
+
export declare const ACCOUNT_STATUSES: readonly ["pending_verification", "active", "disabled"];
|
|
6
11
|
export type AccountStatus = (typeof ACCOUNT_STATUSES)[number];
|
|
12
|
+
/** The statuses an administrator may set. Verification is never set by hand. */
|
|
13
|
+
export declare const ADMIN_SETTABLE_ACCOUNT_STATUSES: readonly ["active", "disabled"];
|
|
14
|
+
export type AdminSettableAccountStatus = (typeof ADMIN_SETTABLE_ACCOUNT_STATUSES)[number];
|
|
15
|
+
/**
|
|
16
|
+
* Where a user's time zone came from. `AUTO` was detected from their browser
|
|
17
|
+
* and may be updated when they sign in elsewhere. `MANUAL` was chosen by the
|
|
18
|
+
* user, and only the user changes it.
|
|
19
|
+
*/
|
|
20
|
+
export declare const TIMEZONE_SOURCES: readonly ["AUTO", "MANUAL"];
|
|
21
|
+
export type TimezoneSource = (typeof TIMEZONE_SOURCES)[number];
|
|
7
22
|
export declare const TOKEN_TRANSPORTS: readonly ["body", "cookie"];
|
|
8
23
|
export type TokenTransport = (typeof TOKEN_TRANSPORTS)[number];
|
|
9
24
|
export declare const AUDIT_OUTCOMES: readonly ["success", "failure"];
|
package/dist/cjs/enums/index.js
CHANGED
|
@@ -2,14 +2,31 @@
|
|
|
2
2
|
// These values are stored in the database and sent over the API. Renaming or
|
|
3
3
|
// removing one is a breaking change, so it needs a major version.
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.FEATURE_FLAG_RULE_TYPES = exports.AUDIT_OUTCOMES = exports.TOKEN_TRANSPORTS = exports.ACCOUNT_STATUSES = exports.ROLES = exports.APP_ENVIRONMENTS = void 0;
|
|
5
|
+
exports.FEATURE_FLAG_RULE_TYPES = exports.AUDIT_OUTCOMES = exports.TOKEN_TRANSPORTS = exports.TIMEZONE_SOURCES = exports.ADMIN_SETTABLE_ACCOUNT_STATUSES = exports.ACCOUNT_STATUSES = exports.ROLES = exports.APP_ENVIRONMENTS = void 0;
|
|
6
6
|
exports.APP_ENVIRONMENTS = [
|
|
7
7
|
'development',
|
|
8
8
|
'staging',
|
|
9
9
|
'production',
|
|
10
10
|
];
|
|
11
|
-
|
|
12
|
-
exports.
|
|
11
|
+
/** Each user has exactly one role. A new role is added here and nowhere else. */
|
|
12
|
+
exports.ROLES = ['ADMIN', 'COACH', 'CUSTOMER'];
|
|
13
|
+
/**
|
|
14
|
+
* `pending_verification` is a self-registered user who has not yet proved they
|
|
15
|
+
* own their email address. Such a user cannot sign in.
|
|
16
|
+
*/
|
|
17
|
+
exports.ACCOUNT_STATUSES = [
|
|
18
|
+
'pending_verification',
|
|
19
|
+
'active',
|
|
20
|
+
'disabled',
|
|
21
|
+
];
|
|
22
|
+
/** The statuses an administrator may set. Verification is never set by hand. */
|
|
23
|
+
exports.ADMIN_SETTABLE_ACCOUNT_STATUSES = ['active', 'disabled'];
|
|
24
|
+
/**
|
|
25
|
+
* Where a user's time zone came from. `AUTO` was detected from their browser
|
|
26
|
+
* and may be updated when they sign in elsewhere. `MANUAL` was chosen by the
|
|
27
|
+
* user, and only the user changes it.
|
|
28
|
+
*/
|
|
29
|
+
exports.TIMEZONE_SOURCES = ['AUTO', 'MANUAL'];
|
|
13
30
|
exports.TOKEN_TRANSPORTS = ['body', 'cookie'];
|
|
14
31
|
exports.AUDIT_OUTCOMES = ['success', 'failure'];
|
|
15
32
|
exports.FEATURE_FLAG_RULE_TYPES = [
|
|
@@ -41,9 +41,9 @@ export declare const createRuleSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
41
41
|
}, z.core.$strict>, z.ZodObject<{
|
|
42
42
|
ruleType: z.ZodLiteral<"role">;
|
|
43
43
|
ruleValue: z.ZodEnum<{
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
ADMIN: "ADMIN";
|
|
45
|
+
COACH: "COACH";
|
|
46
|
+
CUSTOMER: "CUSTOMER";
|
|
47
47
|
}>;
|
|
48
48
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
49
49
|
}, z.core.$strict>], "ruleType">;
|
|
@@ -67,9 +67,9 @@ export declare const flagListQuerySchema: z.ZodObject<{
|
|
|
67
67
|
export declare const ruleResponseSchema: z.ZodObject<{
|
|
68
68
|
id: z.ZodUUID;
|
|
69
69
|
ruleType: z.ZodEnum<{
|
|
70
|
-
trainer: "trainer";
|
|
71
|
-
customer: "customer";
|
|
72
70
|
user: "user";
|
|
71
|
+
customer: "customer";
|
|
72
|
+
trainer: "trainer";
|
|
73
73
|
role: "role";
|
|
74
74
|
}>;
|
|
75
75
|
ruleValue: z.ZodString;
|
|
@@ -100,9 +100,9 @@ export declare const flagResponseSchema: z.ZodObject<{
|
|
|
100
100
|
rules: z.ZodArray<z.ZodObject<{
|
|
101
101
|
id: z.ZodUUID;
|
|
102
102
|
ruleType: z.ZodEnum<{
|
|
103
|
-
trainer: "trainer";
|
|
104
|
-
customer: "customer";
|
|
105
103
|
user: "user";
|
|
104
|
+
customer: "customer";
|
|
105
|
+
trainer: "trainer";
|
|
106
106
|
role: "role";
|
|
107
107
|
}>;
|
|
108
108
|
ruleValue: z.ZodString;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.killSwitchResponseSchema = exports.auditLogResponseSchema = exports.flagResponseSchema = exports.ruleResponseSchema = exports.flagListQuerySchema = exports.setKillSwitchSchema = exports.updateRuleSchema = exports.createRuleSchema = exports.updateFlagSchema = exports.createFlagSchema = exports.flagIdParamSchema = 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 safe_text_schema_js_1 = require("../common/safe-text.schema.js");
|
|
7
8
|
const index_js_1 = require("../enums/index.js");
|
|
@@ -17,7 +18,7 @@ const keySchema = zod_1.z
|
|
|
17
18
|
const nameSchema = (0, safe_text_schema_js_1.safeTextSchema)(120);
|
|
18
19
|
const descriptionSchema = (0, safe_text_schema_js_1.safeTextSchema)(500);
|
|
19
20
|
const percentageSchema = zod_1.z.number().int().min(0).max(100);
|
|
20
|
-
const timestampSchema =
|
|
21
|
+
const timestampSchema = date_time_schema_js_1.instantInputSchema;
|
|
21
22
|
/** Lower-cased, so that it matches IDs as they come from the database. */
|
|
22
23
|
const accountIdSchema = zod_1.z.string().toLowerCase().pipe(zod_1.z.uuid());
|
|
23
24
|
const scheduleIsOrdered = ({ activationAt, expiresAt, }) => !activationAt || !expiresAt || new Date(activationAt) < new Date(expiresAt);
|
|
@@ -95,8 +96,8 @@ exports.ruleResponseSchema = zod_1.z.object({
|
|
|
95
96
|
ruleType: zod_1.z.enum(index_js_1.FEATURE_FLAG_RULE_TYPES),
|
|
96
97
|
ruleValue: zod_1.z.string(),
|
|
97
98
|
enabled: zod_1.z.boolean(),
|
|
98
|
-
createdAt:
|
|
99
|
-
updatedAt:
|
|
99
|
+
createdAt: date_time_schema_js_1.utcTimestampSchema,
|
|
100
|
+
updatedAt: date_time_schema_js_1.utcTimestampSchema,
|
|
100
101
|
});
|
|
101
102
|
exports.flagResponseSchema = zod_1.z.object({
|
|
102
103
|
id: zod_1.z.uuid(),
|
|
@@ -106,13 +107,13 @@ exports.flagResponseSchema = zod_1.z.object({
|
|
|
106
107
|
environment: zod_1.z.enum(index_js_1.APP_ENVIRONMENTS),
|
|
107
108
|
enabled: zod_1.z.boolean(),
|
|
108
109
|
rolloutPercentage: zod_1.z.number().int(),
|
|
109
|
-
activationAt:
|
|
110
|
-
expiresAt:
|
|
110
|
+
activationAt: date_time_schema_js_1.utcTimestampSchema.nullable(),
|
|
111
|
+
expiresAt: date_time_schema_js_1.utcTimestampSchema.nullable(),
|
|
111
112
|
version: zod_1.z.number().int(),
|
|
112
113
|
createdBy: zod_1.z.uuid(),
|
|
113
114
|
updatedBy: zod_1.z.uuid(),
|
|
114
|
-
createdAt:
|
|
115
|
-
updatedAt:
|
|
115
|
+
createdAt: date_time_schema_js_1.utcTimestampSchema,
|
|
116
|
+
updatedAt: date_time_schema_js_1.utcTimestampSchema,
|
|
116
117
|
rules: zod_1.z.array(exports.ruleResponseSchema),
|
|
117
118
|
});
|
|
118
119
|
exports.auditLogResponseSchema = zod_1.z.object({
|
|
@@ -124,12 +125,12 @@ exports.auditLogResponseSchema = zod_1.z.object({
|
|
|
124
125
|
action: zod_1.z.string(),
|
|
125
126
|
previousValue: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).nullable(),
|
|
126
127
|
newValue: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).nullable(),
|
|
127
|
-
createdAt:
|
|
128
|
+
createdAt: date_time_schema_js_1.utcTimestampSchema,
|
|
128
129
|
});
|
|
129
130
|
exports.killSwitchResponseSchema = zod_1.z.object({
|
|
130
131
|
environment: zod_1.z.enum(index_js_1.APP_ENVIRONMENTS),
|
|
131
132
|
engaged: zod_1.z.boolean(),
|
|
132
133
|
reason: zod_1.z.string().nullable(),
|
|
133
134
|
updatedBy: zod_1.z.uuid().nullable(),
|
|
134
|
-
updatedAt:
|
|
135
|
+
updatedAt: date_time_schema_js_1.utcTimestampSchema.nullable(),
|
|
135
136
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { utcTimestampSchema } from '../common/date-time.schema.js';
|
|
2
3
|
import { paginationQuerySchema } from '../common/pagination.schema.js';
|
|
3
4
|
import { AUDIT_OUTCOMES } from '../enums/index.js';
|
|
4
5
|
export const auditLogQuerySchema = paginationQuerySchema.extend({
|
|
@@ -17,5 +18,5 @@ export const auditLogResponseSchema = z.object({
|
|
|
17
18
|
requestId: z.string().nullable(),
|
|
18
19
|
correlationId: z.string().nullable(),
|
|
19
20
|
metadata: z.record(z.string(), z.unknown()).nullable(),
|
|
20
|
-
createdAt:
|
|
21
|
+
createdAt: utcTimestampSchema,
|
|
21
22
|
});
|
|
@@ -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
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
|
46
|
-
export type
|
|
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
|
-
|
|
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,19 +1,31 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { emailSchema } from '../common/contact.schema.js';
|
|
3
|
+
import { timezoneSchema, utcTimestampSchema, } from '../common/date-time.schema.js';
|
|
3
4
|
import { safeTextSchema } from '../common/safe-text.schema.js';
|
|
4
|
-
import { ACCOUNT_STATUSES, ROLES, TOKEN_TRANSPORTS } from '../enums/index.js';
|
|
5
|
+
import { ACCOUNT_STATUSES, ADMIN_SETTABLE_ACCOUNT_STATUSES, ROLES, TIMEZONE_SOURCES, TOKEN_TRANSPORTS, } from '../enums/index.js';
|
|
5
6
|
// Per OWASP: length is what matters, with no composition rules. The upper limit
|
|
6
7
|
// stops very long inputs from being used to exhaust the hashing function.
|
|
7
8
|
export const MIN_PASSWORD_LENGTH = 12;
|
|
8
9
|
export const MAX_PASSWORD_LENGTH = 128;
|
|
9
10
|
const MAX_OPAQUE_TOKEN_LENGTH = 512;
|
|
11
|
+
/** Every one-time code the API sends has this many digits. */
|
|
12
|
+
export const OTP_LENGTH = 6;
|
|
13
|
+
export const otpCodeSchema = z
|
|
14
|
+
.string()
|
|
15
|
+
.trim()
|
|
16
|
+
.regex(new RegExp(`^\\d{${OTP_LENGTH}}$`), {
|
|
17
|
+
error: `Enter the ${OTP_LENGTH}-digit code`,
|
|
18
|
+
});
|
|
10
19
|
export const newPasswordSchema = z
|
|
11
20
|
.string()
|
|
12
21
|
.min(MIN_PASSWORD_LENGTH)
|
|
13
22
|
.max(MAX_PASSWORD_LENGTH);
|
|
14
23
|
/** No minimum at login, so an old short password fails like any wrong password. */
|
|
15
|
-
const submittedPasswordSchema = z
|
|
16
|
-
|
|
24
|
+
export const submittedPasswordSchema = z
|
|
25
|
+
.string()
|
|
26
|
+
.min(1)
|
|
27
|
+
.max(MAX_PASSWORD_LENGTH);
|
|
28
|
+
export const opaqueTokenSchema = z.string().min(1).max(MAX_OPAQUE_TOKEN_LENGTH);
|
|
17
29
|
// Strict objects reject unknown fields, so an attempt to send `roles` or
|
|
18
30
|
// `status` fails loudly rather than being silently ignored.
|
|
19
31
|
export const loginSchema = z.strictObject({
|
|
@@ -36,11 +48,11 @@ export const passwordResetConfirmSchema = z.strictObject({
|
|
|
36
48
|
token: opaqueTokenSchema,
|
|
37
49
|
newPassword: newPasswordSchema,
|
|
38
50
|
});
|
|
39
|
-
export const
|
|
40
|
-
|
|
51
|
+
export const setRoleSchema = z.strictObject({
|
|
52
|
+
role: z.enum(ROLES),
|
|
41
53
|
});
|
|
42
54
|
export const setStatusSchema = z.strictObject({
|
|
43
|
-
status: z.enum(
|
|
55
|
+
status: z.enum(ADMIN_SETTABLE_ACCOUNT_STATUSES),
|
|
44
56
|
});
|
|
45
57
|
export const idParamSchema = z.uuid();
|
|
46
58
|
export const tokenResponseSchema = z.object({
|
|
@@ -51,13 +63,34 @@ export const tokenResponseSchema = z.object({
|
|
|
51
63
|
csrfToken: z.string().optional(),
|
|
52
64
|
sessionId: z.uuid(),
|
|
53
65
|
});
|
|
54
|
-
|
|
66
|
+
/**
|
|
67
|
+
* A user's time zone preference: an IANA zone, and whether it was detected
|
|
68
|
+
* (`AUTO`) or chosen (`MANUAL`).
|
|
69
|
+
*/
|
|
70
|
+
export const timezonePreferenceSchema = z.object({
|
|
71
|
+
timezone: z.string(),
|
|
72
|
+
timezoneSource: z.enum(TIMEZONE_SOURCES),
|
|
73
|
+
});
|
|
74
|
+
/**
|
|
75
|
+
* Sets the signed-in user's own time zone. `MANUAL` (the default) is a choice
|
|
76
|
+
* the user made. `AUTO` records a detected zone that a later sign-in may
|
|
77
|
+
* replace.
|
|
78
|
+
*/
|
|
79
|
+
export const updateTimezoneSchema = z.strictObject({
|
|
80
|
+
timezone: timezoneSchema,
|
|
81
|
+
source: z.enum(TIMEZONE_SOURCES).default('MANUAL'),
|
|
82
|
+
});
|
|
83
|
+
export const accountResponseSchema = z
|
|
84
|
+
.object({
|
|
55
85
|
id: z.uuid(),
|
|
56
86
|
email: z.string(),
|
|
87
|
+
role: z.enum(ROLES),
|
|
88
|
+
/** @deprecated Always `[role]`. Kept for clients written before `role`. */
|
|
57
89
|
roles: z.array(z.string()),
|
|
58
90
|
permissions: z.array(z.string()),
|
|
59
|
-
lastLoginAt:
|
|
60
|
-
})
|
|
91
|
+
lastLoginAt: utcTimestampSchema.nullable(),
|
|
92
|
+
})
|
|
93
|
+
.extend(timezonePreferenceSchema.shape);
|
|
61
94
|
export const loginResponseSchema = tokenResponseSchema.extend({
|
|
62
95
|
account: accountResponseSchema,
|
|
63
96
|
});
|
|
@@ -65,9 +98,9 @@ export const sessionResponseSchema = z.object({
|
|
|
65
98
|
id: z.uuid(),
|
|
66
99
|
deviceName: z.string().nullable(),
|
|
67
100
|
userAgent: z.string().nullable(),
|
|
68
|
-
createdAt:
|
|
69
|
-
lastUsedAt:
|
|
70
|
-
expiresAt:
|
|
101
|
+
createdAt: utcTimestampSchema,
|
|
102
|
+
lastUsedAt: utcTimestampSchema,
|
|
103
|
+
expiresAt: utcTimestampSchema,
|
|
71
104
|
current: z.boolean(),
|
|
72
105
|
});
|
|
73
106
|
export const logoutAllResponseSchema = z.object({
|
|
@@ -79,9 +112,9 @@ export const passwordResetRequestedResponseSchema = z.object({
|
|
|
79
112
|
export const adminAccountResponseSchema = z.object({
|
|
80
113
|
id: z.uuid(),
|
|
81
114
|
email: z.string(),
|
|
82
|
-
|
|
115
|
+
role: z.enum(ROLES),
|
|
83
116
|
status: z.enum(ACCOUNT_STATUSES),
|
|
84
|
-
lastLoginAt:
|
|
117
|
+
lastLoginAt: utcTimestampSchema.nullable(),
|
|
85
118
|
});
|
|
86
119
|
export const revokedSessionsResponseSchema = z.object({
|
|
87
120
|
revokedSessions: 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>;
|