@tbs-fitness/contract 0.6.0 → 0.8.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 +23 -10
- package/dist/cjs/auth/auth.dto.d.ts +3 -1
- package/dist/cjs/auth/auth.dto.js +13 -5
- package/dist/cjs/enums/index.d.ts +42 -0
- package/dist/cjs/enums/index.js +42 -1
- package/dist/cjs/feature-flags/feature-flag.dto.d.ts +9 -0
- package/dist/cjs/feature-flags/feature-flag.dto.js +9 -1
- package/dist/esm/auth/auth.dto.d.ts +3 -1
- package/dist/esm/auth/auth.dto.js +12 -4
- package/dist/esm/enums/index.d.ts +42 -0
- package/dist/esm/enums/index.js +41 -0
- package/dist/esm/feature-flags/feature-flag.dto.d.ts +9 -0
- package/dist/esm/feature-flags/feature-flag.dto.js +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,16 +6,16 @@ It is published as `@tbs-fitness/contract`. The package holds definitions only:
|
|
|
6
6
|
|
|
7
7
|
## What is in it
|
|
8
8
|
|
|
9
|
-
| Import | Contents
|
|
10
|
-
| ------------------------------------- |
|
|
11
|
-
| `@tbs-fitness/contract/enums` | `ROLES`, `ACCOUNT_STATUSES`, `TOKEN_TRANSPORTS`, `AUDIT_OUTCOMES`, `APP_ENVIRONMENTS`, `FEATURE_FLAG_RULE_TYPES` and their types |
|
|
12
|
-
| `@tbs-fitness/contract/common` | Pagination, response envelopes (`{ data }`, `{ data, meta }`, `{ error }`), `emailSchema`, `phoneSchema`, `safeTextSchema`
|
|
13
|
-
| `@tbs-fitness/contract/auth` | Login, sessions, password changes and resets, account administration
|
|
14
|
-
| `@tbs-fitness/contract/client` | Client sign-up, one-time code verification and resend, client sign-in and profile, password recovery
|
|
15
|
-
| `@tbs-fitness/contract/coach` | Coach sign-up, one-time code verification and resend, coach sign-in and profile, password recovery
|
|
16
|
-
| `@tbs-fitness/contract/audit` | The audit log query and response
|
|
17
|
-
| `@tbs-fitness/contract/feature-flags` | Feature flag requests and responses
|
|
18
|
-
| `@tbs-fitness/contract/food` | Food library, metadata, CSV/XLSX import, client food suggestions and their review, and the shared nutrition helpers
|
|
9
|
+
| Import | Contents |
|
|
10
|
+
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
11
|
+
| `@tbs-fitness/contract/enums` | `ROLES`, `PERMISSIONS`, `CSRF_COOKIE_NAMES`, `CSRF_HEADER`, `ACCOUNT_STATUSES`, `TOKEN_TRANSPORTS`, `AUDIT_OUTCOMES`, `APP_ENVIRONMENTS`, `FEATURE_FLAG_RULE_TYPES` and their types |
|
|
12
|
+
| `@tbs-fitness/contract/common` | Pagination, response envelopes (`{ data }`, `{ data, meta }`, `{ error }`), `emailSchema`, `phoneSchema`, `safeTextSchema` |
|
|
13
|
+
| `@tbs-fitness/contract/auth` | Login, sessions, password changes and resets, account administration |
|
|
14
|
+
| `@tbs-fitness/contract/client` | Client sign-up, one-time code verification and resend, client sign-in and profile, password recovery |
|
|
15
|
+
| `@tbs-fitness/contract/coach` | Coach sign-up, one-time code verification and resend, coach sign-in and profile, password recovery |
|
|
16
|
+
| `@tbs-fitness/contract/audit` | The audit log query and response |
|
|
17
|
+
| `@tbs-fitness/contract/feature-flags` | Feature flag requests and responses |
|
|
18
|
+
| `@tbs-fitness/contract/food` | Food library, metadata, CSV/XLSX import, client food suggestions and their review, and the shared nutrition helpers |
|
|
19
19
|
|
|
20
20
|
Each domain has its own entry point, so names only need to be unique within a domain. `auditLogResponseSchema`, for example, exists in both `/audit` and `/feature-flags`.
|
|
21
21
|
|
|
@@ -43,6 +43,19 @@ const result = createFlagSchema.safeParse(input); // The same rules as the API.
|
|
|
43
43
|
|
|
44
44
|
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>`.
|
|
45
45
|
|
|
46
|
+
## Version 0.8.0 (breaking)
|
|
47
|
+
|
|
48
|
+
- `newPasswordSchema` now matches the new-password screens: at least 8 characters (was 12) and at least one number. `MIN_PASSWORD_LENGTH` is `8`, and the new `PASSWORD_DIGIT_PATTERN` lets apps show the number rule as it is typed. Sign-up, password change and reset all use it; login does not, so existing passwords keep working.
|
|
49
|
+
|
|
50
|
+
## Version 0.7.0
|
|
51
|
+
|
|
52
|
+
Permission names, CSRF names and the client feature-flag response. Additive; consumers install `^0.7.0` explicitly.
|
|
53
|
+
|
|
54
|
+
- `/enums`: `PERMISSIONS` and `Permission`, every permission name the API grants. Which role has which permission stays in the backend.
|
|
55
|
+
- `/enums`: `CSRF_COOKIE_NAMES` (`__Host-tbs_csrf` when Secure, else `tbs_csrf`) and `CSRF_HEADER` (`x-csrf-token`).
|
|
56
|
+
- `/feature-flags`: `evaluatedFlagsResponseSchema` and `EvaluatedFlagsResponse`, the response of `GET /feature-flags/me`: only the flags that are on for the caller.
|
|
57
|
+
- `accountResponseSchema.permissions` stays an array of plain strings, so a client older than a new permission still accepts the sign-in.
|
|
58
|
+
|
|
46
59
|
## Version 0.6.0
|
|
47
60
|
|
|
48
61
|
The food library. Additive; consumers install `^0.6.0` explicitly.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
export declare const MIN_PASSWORD_LENGTH =
|
|
2
|
+
export declare const MIN_PASSWORD_LENGTH = 8;
|
|
3
3
|
export declare const MAX_PASSWORD_LENGTH = 128;
|
|
4
|
+
/** A new password must contain at least one character that matches this. */
|
|
5
|
+
export declare const PASSWORD_DIGIT_PATTERN: RegExp;
|
|
4
6
|
/** Every one-time code the API sends has this many digits. */
|
|
5
7
|
export declare const OTP_LENGTH = 6;
|
|
6
8
|
export declare const otpCodeSchema: z.ZodString;
|
|
@@ -1,15 +1,17 @@
|
|
|
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.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;
|
|
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.PASSWORD_DIGIT_PATTERN = 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
6
|
const date_time_schema_js_1 = require("../common/date-time.schema.js");
|
|
7
7
|
const safe_text_schema_js_1 = require("../common/safe-text.schema.js");
|
|
8
8
|
const index_js_1 = require("../enums/index.js");
|
|
9
|
-
//
|
|
10
|
-
// stops very long inputs from being used to exhaust the hashing function.
|
|
11
|
-
exports.MIN_PASSWORD_LENGTH =
|
|
9
|
+
// The product's new-password rules: at least 8 characters and one number. The
|
|
10
|
+
// upper limit stops very long inputs from being used to exhaust the hashing function.
|
|
11
|
+
exports.MIN_PASSWORD_LENGTH = 8;
|
|
12
12
|
exports.MAX_PASSWORD_LENGTH = 128;
|
|
13
|
+
/** A new password must contain at least one character that matches this. */
|
|
14
|
+
exports.PASSWORD_DIGIT_PATTERN = /\d/;
|
|
13
15
|
const MAX_OPAQUE_TOKEN_LENGTH = 512;
|
|
14
16
|
/** Every one-time code the API sends has this many digits. */
|
|
15
17
|
exports.OTP_LENGTH = 6;
|
|
@@ -22,7 +24,8 @@ exports.otpCodeSchema = zod_1.z
|
|
|
22
24
|
exports.newPasswordSchema = zod_1.z
|
|
23
25
|
.string()
|
|
24
26
|
.min(exports.MIN_PASSWORD_LENGTH)
|
|
25
|
-
.max(exports.MAX_PASSWORD_LENGTH)
|
|
27
|
+
.max(exports.MAX_PASSWORD_LENGTH)
|
|
28
|
+
.regex(exports.PASSWORD_DIGIT_PATTERN, { error: 'Include at least one number' });
|
|
26
29
|
/** No minimum at login, so an old short password fails like any wrong password. */
|
|
27
30
|
exports.submittedPasswordSchema = zod_1.z
|
|
28
31
|
.string()
|
|
@@ -90,6 +93,11 @@ exports.accountResponseSchema = zod_1.z
|
|
|
90
93
|
role: zod_1.z.enum(index_js_1.ROLES),
|
|
91
94
|
/** @deprecated Always `[role]`. Kept for clients written before `role`. */
|
|
92
95
|
roles: zod_1.z.array(zod_1.z.string()),
|
|
96
|
+
/**
|
|
97
|
+
* Plain strings rather than `PERMISSIONS` values, because clients validate
|
|
98
|
+
* this response. With an enum, a client older than a new permission would
|
|
99
|
+
* reject the sign-in of everyone granted it.
|
|
100
|
+
*/
|
|
93
101
|
permissions: zod_1.z.array(zod_1.z.string()),
|
|
94
102
|
lastLoginAt: date_time_schema_js_1.utcTimestampSchema.nullable(),
|
|
95
103
|
})
|
|
@@ -3,6 +3,37 @@ 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
4
|
export declare const ROLES: readonly ["ADMIN", "COACH", "CLIENT"];
|
|
5
5
|
export type Role = (typeof ROLES)[number];
|
|
6
|
+
/**
|
|
7
|
+
* Every permission name the API grants. Renaming one is a breaking change:
|
|
8
|
+
* clients compare these strings with what the API returns, so one on the old
|
|
9
|
+
* name silently loses whatever it guards. Which role has which permission is a
|
|
10
|
+
* server decision, so that mapping stays in the backend.
|
|
11
|
+
*/
|
|
12
|
+
export declare const PERMISSIONS: {
|
|
13
|
+
readonly accountsManage: "accounts:manage";
|
|
14
|
+
readonly rolesManage: "roles:manage";
|
|
15
|
+
readonly auditRead: "audit:read";
|
|
16
|
+
readonly featureFlagsManage: "feature_flags:manage";
|
|
17
|
+
readonly appSettingsManage: "app_settings:manage";
|
|
18
|
+
readonly communicationTemplatesManage: "communication_templates:manage";
|
|
19
|
+
readonly clientsReadAny: "clients:read:any";
|
|
20
|
+
readonly clientsWriteAny: "clients:write:any";
|
|
21
|
+
readonly clientsReadAssigned: "clients:read:assigned";
|
|
22
|
+
readonly clientsWriteAssigned: "clients:write:assigned";
|
|
23
|
+
readonly clientsReadSelf: "clients:read:self";
|
|
24
|
+
readonly clientsWriteSelf: "clients:write:self";
|
|
25
|
+
readonly foodsRead: "foods:read";
|
|
26
|
+
readonly foodsCreate: "foods:create";
|
|
27
|
+
readonly foodsUpdate: "foods:update";
|
|
28
|
+
readonly foodsDelete: "foods:delete";
|
|
29
|
+
readonly foodsImport: "foods:import";
|
|
30
|
+
readonly foodMetadataManage: "food_metadata:manage";
|
|
31
|
+
readonly foodSuggestionsReview: "food_suggestions:review";
|
|
32
|
+
readonly foodsSearchSelf: "foods:search:self";
|
|
33
|
+
readonly foodSuggestionsSubmitSelf: "food_suggestions:submit:self";
|
|
34
|
+
readonly foodSuggestionsReadSelf: "food_suggestions:read:self";
|
|
35
|
+
};
|
|
36
|
+
export type Permission = (typeof PERMISSIONS)[keyof typeof PERMISSIONS];
|
|
6
37
|
/**
|
|
7
38
|
* `pending_verification` is a self-registered user who has not yet proved they
|
|
8
39
|
* own their email address. Such a user cannot sign in.
|
|
@@ -21,6 +52,17 @@ export declare const TIMEZONE_SOURCES: readonly ["AUTO", "MANUAL"];
|
|
|
21
52
|
export type TimezoneSource = (typeof TIMEZONE_SOURCES)[number];
|
|
22
53
|
export declare const TOKEN_TRANSPORTS: readonly ["body", "cookie"];
|
|
23
54
|
export type TokenTransport = (typeof TOKEN_TRANSPORTS)[number];
|
|
55
|
+
/**
|
|
56
|
+
* The CSRF cookie the API sets, which scripts may read. The `__Host-` name is
|
|
57
|
+
* used when cookies are Secure, because browsers only honour that prefix on
|
|
58
|
+
* Secure cookies.
|
|
59
|
+
*/
|
|
60
|
+
export declare const CSRF_COOKIE_NAMES: {
|
|
61
|
+
readonly secure: "__Host-tbs_csrf";
|
|
62
|
+
readonly insecure: "tbs_csrf";
|
|
63
|
+
};
|
|
64
|
+
/** The header that carries the CSRF token back to the API. */
|
|
65
|
+
export declare const CSRF_HEADER = "x-csrf-token";
|
|
24
66
|
export declare const AUDIT_OUTCOMES: readonly ["success", "failure"];
|
|
25
67
|
export type AuditOutcome = (typeof AUDIT_OUTCOMES)[number];
|
|
26
68
|
export declare const FEATURE_FLAG_RULE_TYPES: readonly ["user", "client", "trainer", "role"];
|
package/dist/cjs/enums/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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.SORT_ORDERS = exports.FOOD_SORT_FIELDS = exports.FOOD_METADATA_SELECTIONS = exports.FOOD_IMPORT_ISSUE_SEVERITIES = exports.FOOD_IMPORT_FILE_TYPES = exports.FOOD_IMPORT_ROW_STATUSES = exports.FOOD_IMPORT_STATUSES = exports.FOOD_IMPORT_MODES = exports.FOOD_SUGGESTION_RESOLUTIONS = exports.FOOD_SUGGESTION_STATUSES = exports.NUTRITION_BASES = exports.PORTION_UNITS = exports.SERVING_UNITS = exports.FOOD_SOURCES = exports.TEMPLATE_STATUSES = exports.DELIVERY_STATUSES = exports.MESSAGE_TYPES = exports.COMMUNICATION_CHANNELS = 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;
|
|
5
|
+
exports.SORT_ORDERS = exports.FOOD_SORT_FIELDS = exports.FOOD_METADATA_SELECTIONS = exports.FOOD_IMPORT_ISSUE_SEVERITIES = exports.FOOD_IMPORT_FILE_TYPES = exports.FOOD_IMPORT_ROW_STATUSES = exports.FOOD_IMPORT_STATUSES = exports.FOOD_IMPORT_MODES = exports.FOOD_SUGGESTION_RESOLUTIONS = exports.FOOD_SUGGESTION_STATUSES = exports.NUTRITION_BASES = exports.PORTION_UNITS = exports.SERVING_UNITS = exports.FOOD_SOURCES = exports.TEMPLATE_STATUSES = exports.DELIVERY_STATUSES = exports.MESSAGE_TYPES = exports.COMMUNICATION_CHANNELS = exports.FEATURE_FLAG_RULE_TYPES = exports.AUDIT_OUTCOMES = exports.CSRF_HEADER = exports.CSRF_COOKIE_NAMES = exports.TOKEN_TRANSPORTS = exports.TIMEZONE_SOURCES = exports.ADMIN_SETTABLE_ACCOUNT_STATUSES = exports.ACCOUNT_STATUSES = exports.PERMISSIONS = exports.ROLES = exports.APP_ENVIRONMENTS = void 0;
|
|
6
6
|
exports.APP_ENVIRONMENTS = [
|
|
7
7
|
'development',
|
|
8
8
|
'staging',
|
|
@@ -10,6 +10,36 @@ exports.APP_ENVIRONMENTS = [
|
|
|
10
10
|
];
|
|
11
11
|
/** Each user has exactly one role. A new role is added here and nowhere else. */
|
|
12
12
|
exports.ROLES = ['ADMIN', 'COACH', 'CLIENT'];
|
|
13
|
+
/**
|
|
14
|
+
* Every permission name the API grants. Renaming one is a breaking change:
|
|
15
|
+
* clients compare these strings with what the API returns, so one on the old
|
|
16
|
+
* name silently loses whatever it guards. Which role has which permission is a
|
|
17
|
+
* server decision, so that mapping stays in the backend.
|
|
18
|
+
*/
|
|
19
|
+
exports.PERMISSIONS = {
|
|
20
|
+
accountsManage: 'accounts:manage',
|
|
21
|
+
rolesManage: 'roles:manage',
|
|
22
|
+
auditRead: 'audit:read',
|
|
23
|
+
featureFlagsManage: 'feature_flags:manage',
|
|
24
|
+
appSettingsManage: 'app_settings:manage',
|
|
25
|
+
communicationTemplatesManage: 'communication_templates:manage',
|
|
26
|
+
clientsReadAny: 'clients:read:any',
|
|
27
|
+
clientsWriteAny: 'clients:write:any',
|
|
28
|
+
clientsReadAssigned: 'clients:read:assigned',
|
|
29
|
+
clientsWriteAssigned: 'clients:write:assigned',
|
|
30
|
+
clientsReadSelf: 'clients:read:self',
|
|
31
|
+
clientsWriteSelf: 'clients:write:self',
|
|
32
|
+
foodsRead: 'foods:read',
|
|
33
|
+
foodsCreate: 'foods:create',
|
|
34
|
+
foodsUpdate: 'foods:update',
|
|
35
|
+
foodsDelete: 'foods:delete',
|
|
36
|
+
foodsImport: 'foods:import',
|
|
37
|
+
foodMetadataManage: 'food_metadata:manage',
|
|
38
|
+
foodSuggestionsReview: 'food_suggestions:review',
|
|
39
|
+
foodsSearchSelf: 'foods:search:self',
|
|
40
|
+
foodSuggestionsSubmitSelf: 'food_suggestions:submit:self',
|
|
41
|
+
foodSuggestionsReadSelf: 'food_suggestions:read:self',
|
|
42
|
+
};
|
|
13
43
|
/**
|
|
14
44
|
* `pending_verification` is a self-registered user who has not yet proved they
|
|
15
45
|
* own their email address. Such a user cannot sign in.
|
|
@@ -28,6 +58,17 @@ exports.ADMIN_SETTABLE_ACCOUNT_STATUSES = ['active', 'disabled'];
|
|
|
28
58
|
*/
|
|
29
59
|
exports.TIMEZONE_SOURCES = ['AUTO', 'MANUAL'];
|
|
30
60
|
exports.TOKEN_TRANSPORTS = ['body', 'cookie'];
|
|
61
|
+
/**
|
|
62
|
+
* The CSRF cookie the API sets, which scripts may read. The `__Host-` name is
|
|
63
|
+
* used when cookies are Secure, because browsers only honour that prefix on
|
|
64
|
+
* Secure cookies.
|
|
65
|
+
*/
|
|
66
|
+
exports.CSRF_COOKIE_NAMES = {
|
|
67
|
+
secure: '__Host-tbs_csrf',
|
|
68
|
+
insecure: 'tbs_csrf',
|
|
69
|
+
};
|
|
70
|
+
/** The header that carries the CSRF token back to the API. */
|
|
71
|
+
exports.CSRF_HEADER = 'x-csrf-token';
|
|
31
72
|
exports.AUDIT_OUTCOMES = ['success', 'failure'];
|
|
32
73
|
exports.FEATURE_FLAG_RULE_TYPES = [
|
|
33
74
|
'user',
|
|
@@ -140,3 +140,12 @@ export declare const killSwitchResponseSchema: z.ZodObject<{
|
|
|
140
140
|
updatedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
141
141
|
}, z.core.$strip>;
|
|
142
142
|
export type KillSwitchResponse = z.output<typeof killSwitchResponseSchema>;
|
|
143
|
+
/**
|
|
144
|
+
* What a signed-in user may know about flags: only the keys that are on for
|
|
145
|
+
* them. It never holds rules, targeting values, rollout percentages or who
|
|
146
|
+
* changed what. A missing key means off, so always compare a value with `true`.
|
|
147
|
+
*/
|
|
148
|
+
export declare const evaluatedFlagsResponseSchema: z.ZodObject<{
|
|
149
|
+
flags: z.ZodRecord<z.ZodString, z.ZodBoolean>;
|
|
150
|
+
}, z.core.$strip>;
|
|
151
|
+
export type EvaluatedFlagsResponse = z.output<typeof evaluatedFlagsResponseSchema>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
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;
|
|
3
|
+
exports.evaluatedFlagsResponseSchema = 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
5
|
const date_time_schema_js_1 = require("../common/date-time.schema.js");
|
|
6
6
|
const pagination_schema_js_1 = require("../common/pagination.schema.js");
|
|
@@ -134,3 +134,11 @@ exports.killSwitchResponseSchema = zod_1.z.object({
|
|
|
134
134
|
updatedBy: zod_1.z.uuid().nullable(),
|
|
135
135
|
updatedAt: date_time_schema_js_1.utcTimestampSchema.nullable(),
|
|
136
136
|
});
|
|
137
|
+
/**
|
|
138
|
+
* What a signed-in user may know about flags: only the keys that are on for
|
|
139
|
+
* them. It never holds rules, targeting values, rollout percentages or who
|
|
140
|
+
* changed what. A missing key means off, so always compare a value with `true`.
|
|
141
|
+
*/
|
|
142
|
+
exports.evaluatedFlagsResponseSchema = zod_1.z.object({
|
|
143
|
+
flags: zod_1.z.record(zod_1.z.string(), zod_1.z.boolean()),
|
|
144
|
+
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
export declare const MIN_PASSWORD_LENGTH =
|
|
2
|
+
export declare const MIN_PASSWORD_LENGTH = 8;
|
|
3
3
|
export declare const MAX_PASSWORD_LENGTH = 128;
|
|
4
|
+
/** A new password must contain at least one character that matches this. */
|
|
5
|
+
export declare const PASSWORD_DIGIT_PATTERN: RegExp;
|
|
4
6
|
/** Every one-time code the API sends has this many digits. */
|
|
5
7
|
export declare const OTP_LENGTH = 6;
|
|
6
8
|
export declare const otpCodeSchema: z.ZodString;
|
|
@@ -3,10 +3,12 @@ import { emailSchema } from '../common/contact.schema.js';
|
|
|
3
3
|
import { timezoneSchema, utcTimestampSchema, } from '../common/date-time.schema.js';
|
|
4
4
|
import { safeTextSchema } from '../common/safe-text.schema.js';
|
|
5
5
|
import { ACCOUNT_STATUSES, ADMIN_SETTABLE_ACCOUNT_STATUSES, ROLES, TIMEZONE_SOURCES, TOKEN_TRANSPORTS, } from '../enums/index.js';
|
|
6
|
-
//
|
|
7
|
-
// stops very long inputs from being used to exhaust the hashing function.
|
|
8
|
-
export const MIN_PASSWORD_LENGTH =
|
|
6
|
+
// The product's new-password rules: at least 8 characters and one number. The
|
|
7
|
+
// upper limit stops very long inputs from being used to exhaust the hashing function.
|
|
8
|
+
export const MIN_PASSWORD_LENGTH = 8;
|
|
9
9
|
export const MAX_PASSWORD_LENGTH = 128;
|
|
10
|
+
/** A new password must contain at least one character that matches this. */
|
|
11
|
+
export const PASSWORD_DIGIT_PATTERN = /\d/;
|
|
10
12
|
const MAX_OPAQUE_TOKEN_LENGTH = 512;
|
|
11
13
|
/** Every one-time code the API sends has this many digits. */
|
|
12
14
|
export const OTP_LENGTH = 6;
|
|
@@ -19,7 +21,8 @@ export const otpCodeSchema = z
|
|
|
19
21
|
export const newPasswordSchema = z
|
|
20
22
|
.string()
|
|
21
23
|
.min(MIN_PASSWORD_LENGTH)
|
|
22
|
-
.max(MAX_PASSWORD_LENGTH)
|
|
24
|
+
.max(MAX_PASSWORD_LENGTH)
|
|
25
|
+
.regex(PASSWORD_DIGIT_PATTERN, { error: 'Include at least one number' });
|
|
23
26
|
/** No minimum at login, so an old short password fails like any wrong password. */
|
|
24
27
|
export const submittedPasswordSchema = z
|
|
25
28
|
.string()
|
|
@@ -87,6 +90,11 @@ export const accountResponseSchema = z
|
|
|
87
90
|
role: z.enum(ROLES),
|
|
88
91
|
/** @deprecated Always `[role]`. Kept for clients written before `role`. */
|
|
89
92
|
roles: z.array(z.string()),
|
|
93
|
+
/**
|
|
94
|
+
* Plain strings rather than `PERMISSIONS` values, because clients validate
|
|
95
|
+
* this response. With an enum, a client older than a new permission would
|
|
96
|
+
* reject the sign-in of everyone granted it.
|
|
97
|
+
*/
|
|
90
98
|
permissions: z.array(z.string()),
|
|
91
99
|
lastLoginAt: utcTimestampSchema.nullable(),
|
|
92
100
|
})
|
|
@@ -3,6 +3,37 @@ 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
4
|
export declare const ROLES: readonly ["ADMIN", "COACH", "CLIENT"];
|
|
5
5
|
export type Role = (typeof ROLES)[number];
|
|
6
|
+
/**
|
|
7
|
+
* Every permission name the API grants. Renaming one is a breaking change:
|
|
8
|
+
* clients compare these strings with what the API returns, so one on the old
|
|
9
|
+
* name silently loses whatever it guards. Which role has which permission is a
|
|
10
|
+
* server decision, so that mapping stays in the backend.
|
|
11
|
+
*/
|
|
12
|
+
export declare const PERMISSIONS: {
|
|
13
|
+
readonly accountsManage: "accounts:manage";
|
|
14
|
+
readonly rolesManage: "roles:manage";
|
|
15
|
+
readonly auditRead: "audit:read";
|
|
16
|
+
readonly featureFlagsManage: "feature_flags:manage";
|
|
17
|
+
readonly appSettingsManage: "app_settings:manage";
|
|
18
|
+
readonly communicationTemplatesManage: "communication_templates:manage";
|
|
19
|
+
readonly clientsReadAny: "clients:read:any";
|
|
20
|
+
readonly clientsWriteAny: "clients:write:any";
|
|
21
|
+
readonly clientsReadAssigned: "clients:read:assigned";
|
|
22
|
+
readonly clientsWriteAssigned: "clients:write:assigned";
|
|
23
|
+
readonly clientsReadSelf: "clients:read:self";
|
|
24
|
+
readonly clientsWriteSelf: "clients:write:self";
|
|
25
|
+
readonly foodsRead: "foods:read";
|
|
26
|
+
readonly foodsCreate: "foods:create";
|
|
27
|
+
readonly foodsUpdate: "foods:update";
|
|
28
|
+
readonly foodsDelete: "foods:delete";
|
|
29
|
+
readonly foodsImport: "foods:import";
|
|
30
|
+
readonly foodMetadataManage: "food_metadata:manage";
|
|
31
|
+
readonly foodSuggestionsReview: "food_suggestions:review";
|
|
32
|
+
readonly foodsSearchSelf: "foods:search:self";
|
|
33
|
+
readonly foodSuggestionsSubmitSelf: "food_suggestions:submit:self";
|
|
34
|
+
readonly foodSuggestionsReadSelf: "food_suggestions:read:self";
|
|
35
|
+
};
|
|
36
|
+
export type Permission = (typeof PERMISSIONS)[keyof typeof PERMISSIONS];
|
|
6
37
|
/**
|
|
7
38
|
* `pending_verification` is a self-registered user who has not yet proved they
|
|
8
39
|
* own their email address. Such a user cannot sign in.
|
|
@@ -21,6 +52,17 @@ export declare const TIMEZONE_SOURCES: readonly ["AUTO", "MANUAL"];
|
|
|
21
52
|
export type TimezoneSource = (typeof TIMEZONE_SOURCES)[number];
|
|
22
53
|
export declare const TOKEN_TRANSPORTS: readonly ["body", "cookie"];
|
|
23
54
|
export type TokenTransport = (typeof TOKEN_TRANSPORTS)[number];
|
|
55
|
+
/**
|
|
56
|
+
* The CSRF cookie the API sets, which scripts may read. The `__Host-` name is
|
|
57
|
+
* used when cookies are Secure, because browsers only honour that prefix on
|
|
58
|
+
* Secure cookies.
|
|
59
|
+
*/
|
|
60
|
+
export declare const CSRF_COOKIE_NAMES: {
|
|
61
|
+
readonly secure: "__Host-tbs_csrf";
|
|
62
|
+
readonly insecure: "tbs_csrf";
|
|
63
|
+
};
|
|
64
|
+
/** The header that carries the CSRF token back to the API. */
|
|
65
|
+
export declare const CSRF_HEADER = "x-csrf-token";
|
|
24
66
|
export declare const AUDIT_OUTCOMES: readonly ["success", "failure"];
|
|
25
67
|
export type AuditOutcome = (typeof AUDIT_OUTCOMES)[number];
|
|
26
68
|
export declare const FEATURE_FLAG_RULE_TYPES: readonly ["user", "client", "trainer", "role"];
|
package/dist/esm/enums/index.js
CHANGED
|
@@ -7,6 +7,36 @@ export const APP_ENVIRONMENTS = [
|
|
|
7
7
|
];
|
|
8
8
|
/** Each user has exactly one role. A new role is added here and nowhere else. */
|
|
9
9
|
export const ROLES = ['ADMIN', 'COACH', 'CLIENT'];
|
|
10
|
+
/**
|
|
11
|
+
* Every permission name the API grants. Renaming one is a breaking change:
|
|
12
|
+
* clients compare these strings with what the API returns, so one on the old
|
|
13
|
+
* name silently loses whatever it guards. Which role has which permission is a
|
|
14
|
+
* server decision, so that mapping stays in the backend.
|
|
15
|
+
*/
|
|
16
|
+
export const PERMISSIONS = {
|
|
17
|
+
accountsManage: 'accounts:manage',
|
|
18
|
+
rolesManage: 'roles:manage',
|
|
19
|
+
auditRead: 'audit:read',
|
|
20
|
+
featureFlagsManage: 'feature_flags:manage',
|
|
21
|
+
appSettingsManage: 'app_settings:manage',
|
|
22
|
+
communicationTemplatesManage: 'communication_templates:manage',
|
|
23
|
+
clientsReadAny: 'clients:read:any',
|
|
24
|
+
clientsWriteAny: 'clients:write:any',
|
|
25
|
+
clientsReadAssigned: 'clients:read:assigned',
|
|
26
|
+
clientsWriteAssigned: 'clients:write:assigned',
|
|
27
|
+
clientsReadSelf: 'clients:read:self',
|
|
28
|
+
clientsWriteSelf: 'clients:write:self',
|
|
29
|
+
foodsRead: 'foods:read',
|
|
30
|
+
foodsCreate: 'foods:create',
|
|
31
|
+
foodsUpdate: 'foods:update',
|
|
32
|
+
foodsDelete: 'foods:delete',
|
|
33
|
+
foodsImport: 'foods:import',
|
|
34
|
+
foodMetadataManage: 'food_metadata:manage',
|
|
35
|
+
foodSuggestionsReview: 'food_suggestions:review',
|
|
36
|
+
foodsSearchSelf: 'foods:search:self',
|
|
37
|
+
foodSuggestionsSubmitSelf: 'food_suggestions:submit:self',
|
|
38
|
+
foodSuggestionsReadSelf: 'food_suggestions:read:self',
|
|
39
|
+
};
|
|
10
40
|
/**
|
|
11
41
|
* `pending_verification` is a self-registered user who has not yet proved they
|
|
12
42
|
* own their email address. Such a user cannot sign in.
|
|
@@ -25,6 +55,17 @@ export const ADMIN_SETTABLE_ACCOUNT_STATUSES = ['active', 'disabled'];
|
|
|
25
55
|
*/
|
|
26
56
|
export const TIMEZONE_SOURCES = ['AUTO', 'MANUAL'];
|
|
27
57
|
export const TOKEN_TRANSPORTS = ['body', 'cookie'];
|
|
58
|
+
/**
|
|
59
|
+
* The CSRF cookie the API sets, which scripts may read. The `__Host-` name is
|
|
60
|
+
* used when cookies are Secure, because browsers only honour that prefix on
|
|
61
|
+
* Secure cookies.
|
|
62
|
+
*/
|
|
63
|
+
export const CSRF_COOKIE_NAMES = {
|
|
64
|
+
secure: '__Host-tbs_csrf',
|
|
65
|
+
insecure: 'tbs_csrf',
|
|
66
|
+
};
|
|
67
|
+
/** The header that carries the CSRF token back to the API. */
|
|
68
|
+
export const CSRF_HEADER = 'x-csrf-token';
|
|
28
69
|
export const AUDIT_OUTCOMES = ['success', 'failure'];
|
|
29
70
|
export const FEATURE_FLAG_RULE_TYPES = [
|
|
30
71
|
'user',
|
|
@@ -140,3 +140,12 @@ export declare const killSwitchResponseSchema: z.ZodObject<{
|
|
|
140
140
|
updatedAt: z.ZodNullable<z.ZodISODateTime>;
|
|
141
141
|
}, z.core.$strip>;
|
|
142
142
|
export type KillSwitchResponse = z.output<typeof killSwitchResponseSchema>;
|
|
143
|
+
/**
|
|
144
|
+
* What a signed-in user may know about flags: only the keys that are on for
|
|
145
|
+
* them. It never holds rules, targeting values, rollout percentages or who
|
|
146
|
+
* changed what. A missing key means off, so always compare a value with `true`.
|
|
147
|
+
*/
|
|
148
|
+
export declare const evaluatedFlagsResponseSchema: z.ZodObject<{
|
|
149
|
+
flags: z.ZodRecord<z.ZodString, z.ZodBoolean>;
|
|
150
|
+
}, z.core.$strip>;
|
|
151
|
+
export type EvaluatedFlagsResponse = z.output<typeof evaluatedFlagsResponseSchema>;
|
|
@@ -131,3 +131,11 @@ export const killSwitchResponseSchema = z.object({
|
|
|
131
131
|
updatedBy: z.uuid().nullable(),
|
|
132
132
|
updatedAt: utcTimestampSchema.nullable(),
|
|
133
133
|
});
|
|
134
|
+
/**
|
|
135
|
+
* What a signed-in user may know about flags: only the keys that are on for
|
|
136
|
+
* them. It never holds rules, targeting values, rollout percentages or who
|
|
137
|
+
* changed what. A missing key means off, so always compare a value with `true`.
|
|
138
|
+
*/
|
|
139
|
+
export const evaluatedFlagsResponseSchema = z.object({
|
|
140
|
+
flags: z.record(z.string(), z.boolean()),
|
|
141
|
+
});
|
package/package.json
CHANGED