dink-pets-shared 1.0.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 +119 -0
- package/dist/mobile-global.css +69 -0
- package/dist/src/activity-details.d.ts +223 -0
- package/dist/src/activity-details.js +166 -0
- package/dist/src/admin.d.ts +28 -0
- package/dist/src/admin.js +52 -0
- package/dist/src/auth.d.ts +21 -0
- package/dist/src/auth.js +43 -0
- package/dist/src/constants.d.ts +13 -0
- package/dist/src/constants.js +41 -0
- package/dist/src/enums.d.ts +121 -0
- package/dist/src/enums.js +41 -0
- package/dist/src/feature-flags.d.ts +35 -0
- package/dist/src/feature-flags.js +41 -0
- package/dist/src/ids.d.ts +70 -0
- package/dist/src/ids.js +47 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.js +14 -0
- package/dist/src/roles.d.ts +6 -0
- package/dist/src/roles.js +17 -0
- package/dist/src/schemas.d.ts +112 -0
- package/dist/src/schemas.js +166 -0
- package/dist/src/theme.d.ts +224 -0
- package/dist/src/theme.js +343 -0
- package/dist/src/updates.d.ts +53 -0
- package/dist/src/updates.js +58 -0
- package/dist/src/uploads.d.ts +41 -0
- package/dist/src/uploads.js +73 -0
- package/dist/src/utils.d.ts +20 -0
- package/dist/src/utils.js +22 -0
- package/dist/src/web-routes.d.ts +28 -0
- package/dist/src/web-routes.js +28 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +22 -0
package/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# dink-pets-shared
|
|
2
|
+
|
|
3
|
+
Shared types, schemas, and utilities for DINK Pets mobile app and server.
|
|
4
|
+
|
|
5
|
+
## Enum Architecture
|
|
6
|
+
|
|
7
|
+
Enums are auto-generated from Prisma schema. **Do not edit `src/enums.ts` manually.**
|
|
8
|
+
|
|
9
|
+
### Why?
|
|
10
|
+
|
|
11
|
+
Prisma's generated client includes Node.js code that crashes React Native. We extract enum definitions to pure TypeScript that works everywhere.
|
|
12
|
+
|
|
13
|
+
### Structure
|
|
14
|
+
|
|
15
|
+
Each enum generates three exports:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
// Array of valid values (camelCase, plural)
|
|
19
|
+
export const petRoles = ["OWNER", "COOWNER", "CARETAKER"] as const;
|
|
20
|
+
|
|
21
|
+
// Zod schema for validation (PascalCase)
|
|
22
|
+
export const PetRole = z.enum(petRoles);
|
|
23
|
+
|
|
24
|
+
// TypeScript type inferred from Zod
|
|
25
|
+
export type PetRole = z.infer<typeof PetRole>;
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Why Zod schemas instead of TypeScript enums?
|
|
29
|
+
|
|
30
|
+
TypeScript enums are **nominally typed**. Even with identical values, TS treats enums from different files as incompatible types:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
// These are INCOMPATIBLE even though values match:
|
|
34
|
+
enum PrismaRole { OWNER = "OWNER" } // from Prisma client
|
|
35
|
+
enum SharedRole { OWNER = "OWNER" } // from shared package
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Zod schemas produce **string union types** which are structurally typed:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
type PetRole = "OWNER" | "COOWNER" | "CARETAKER"
|
|
42
|
+
|
|
43
|
+
// Prisma's "OWNER" string is assignable to this union
|
|
44
|
+
const fromPrisma: PrismaEnum.PetRole = PrismaEnum.OWNER;
|
|
45
|
+
const works: PetRole = fromPrisma; // OK!
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Import Rules
|
|
49
|
+
|
|
50
|
+
| Context | Import From | Why |
|
|
51
|
+
|---------|-------------|-----|
|
|
52
|
+
| Server (database ops) | `./generated/prisma/index.js` | Use Prisma's types for queries |
|
|
53
|
+
| Mobile | `dink-pets-shared` | Can't use Prisma (Node.js code) |
|
|
54
|
+
| Shared validation | `./enums.js` (local) | Zod schemas accept any matching string |
|
|
55
|
+
|
|
56
|
+
### Regenerating
|
|
57
|
+
|
|
58
|
+
Enums regenerate automatically:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# In shared/
|
|
62
|
+
npm run build
|
|
63
|
+
|
|
64
|
+
# In server/ (runs postgenerate hook)
|
|
65
|
+
npx prisma generate
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Manual regeneration:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
node shared/scripts/generate-enums.js
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Usage
|
|
75
|
+
|
|
76
|
+
**Accessing string values:**
|
|
77
|
+
|
|
78
|
+
Use `.enum` property on the Zod schema:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
// Get the string "WALK"
|
|
82
|
+
ActivityType.enum.WALK // "WALK"
|
|
83
|
+
|
|
84
|
+
// Use in z.literal() for discriminated unions
|
|
85
|
+
z.literal(ActivityType.enum.WALK)
|
|
86
|
+
|
|
87
|
+
// Use in Record keys
|
|
88
|
+
const labels: Record<AchievementType, string> = {
|
|
89
|
+
[AchievementType.enum.FIRST_WALK]: "First Walk",
|
|
90
|
+
};
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Creating z.enum() validators:**
|
|
94
|
+
|
|
95
|
+
Use the const array, not the Zod schema:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// Correct - petRoles is the array
|
|
99
|
+
z.enum(petRoles)
|
|
100
|
+
|
|
101
|
+
// Wrong - PetRole is already a z.enum schema
|
|
102
|
+
z.enum(PetRole) // Type error
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Type annotations:**
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// Use the type (same name as schema)
|
|
109
|
+
function checkRole(role: PetRole) { ... }
|
|
110
|
+
|
|
111
|
+
// Works with Prisma values (structural typing)
|
|
112
|
+
const prismaRole = await prisma.petAccess.findFirst();
|
|
113
|
+
checkRole(prismaRole.role); // OK!
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Files
|
|
117
|
+
|
|
118
|
+
- `scripts/generate-enums.js` - Generator script
|
|
119
|
+
- `src/enums.ts` - **Auto-generated, gitignored**
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/* =============================================================================
|
|
2
|
+
* AUTO-GENERATED FILE FOR MOBILE (NativeWind) - DO NOT EDIT DIRECTLY
|
|
3
|
+
* =============================================================================
|
|
4
|
+
* Generated from: shared/src/theme.ts
|
|
5
|
+
* To regenerate: cd shared && npm run build
|
|
6
|
+
*
|
|
7
|
+
* This file is copied to mobile/global.css by mobile's prestart script.
|
|
8
|
+
* Web app does NOT use this - it generates CSS variables inline in layout.tsx.
|
|
9
|
+
*
|
|
10
|
+
* IMPORTANT: NativeWind v4 requires media queries for dark mode CSS variables.
|
|
11
|
+
* Using .dark class selector causes issues. See NativeWind docs.
|
|
12
|
+
* =============================================================================
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
@tailwind base;
|
|
16
|
+
@tailwind components;
|
|
17
|
+
@tailwind utilities;
|
|
18
|
+
|
|
19
|
+
@media (prefers-color-scheme: light) {
|
|
20
|
+
:root {
|
|
21
|
+
--color-background: #ffffff;
|
|
22
|
+
--color-background-subtle: #f3f4f6;
|
|
23
|
+
--color-card: #ffffff;
|
|
24
|
+
--color-gradient-from: #a78bfa;
|
|
25
|
+
--color-gradient-via: #f9a8d4;
|
|
26
|
+
--color-gradient-to: #fdba74;
|
|
27
|
+
--color-text-primary: #1f2937;
|
|
28
|
+
--color-text-secondary: #6b7280;
|
|
29
|
+
--color-text-muted: #9ca3af;
|
|
30
|
+
--color-text-inverse: #ffffff;
|
|
31
|
+
--color-text-on-gradient: #ffffff;
|
|
32
|
+
--color-accent-primary: #7c3aed;
|
|
33
|
+
--color-accent-secondary: #ec4899;
|
|
34
|
+
--color-destructive: #ef4444;
|
|
35
|
+
--color-destructive-subtle: #fef2f2;
|
|
36
|
+
--color-border: #e5e7eb;
|
|
37
|
+
--color-border-strong: #9ca3af;
|
|
38
|
+
--color-button-primary: #7c3aed;
|
|
39
|
+
--color-button-primary-text: #ffffff;
|
|
40
|
+
--color-button-secondary: #ffffff;
|
|
41
|
+
--color-button-secondary-text: #7c3aed;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@media (prefers-color-scheme: dark) {
|
|
46
|
+
:root {
|
|
47
|
+
--color-background: #111827;
|
|
48
|
+
--color-background-subtle: #1f2937;
|
|
49
|
+
--color-card: #252033;
|
|
50
|
+
--color-gradient-from: #111827;
|
|
51
|
+
--color-gradient-via: #4c1d95;
|
|
52
|
+
--color-gradient-to: #831843;
|
|
53
|
+
--color-text-primary: #e5e7eb;
|
|
54
|
+
--color-text-secondary: #9ca3af;
|
|
55
|
+
--color-text-muted: #6b7280;
|
|
56
|
+
--color-text-inverse: #111827;
|
|
57
|
+
--color-text-on-gradient: #e5e7eb;
|
|
58
|
+
--color-accent-primary: #a78bfa;
|
|
59
|
+
--color-accent-secondary: #ec4899;
|
|
60
|
+
--color-destructive: #ef4444;
|
|
61
|
+
--color-destructive-subtle: #450a0a;
|
|
62
|
+
--color-border: #4b5563;
|
|
63
|
+
--color-border-strong: #9ca3af;
|
|
64
|
+
--color-button-primary: #8b5cf6;
|
|
65
|
+
--color-button-primary-text: #ffffff;
|
|
66
|
+
--color-button-secondary: #1f2937;
|
|
67
|
+
--color-button-secondary-text: #a78bfa;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const WalkActivityDetails: z.ZodObject<{
|
|
3
|
+
type: z.ZodLiteral<"WALK">;
|
|
4
|
+
durationMin: z.ZodOptional<z.ZodNumber>;
|
|
5
|
+
}, z.core.$strict>;
|
|
6
|
+
export declare const MealActivityDetails: z.ZodObject<{
|
|
7
|
+
type: z.ZodLiteral<"MEAL">;
|
|
8
|
+
amount: z.ZodOptional<z.ZodNumber>;
|
|
9
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
10
|
+
foodType: z.ZodOptional<z.ZodString>;
|
|
11
|
+
}, z.core.$strict>;
|
|
12
|
+
export declare const BathroomActivityDetails: z.ZodObject<{
|
|
13
|
+
type: z.ZodLiteral<"BATHROOM">;
|
|
14
|
+
subtype: z.ZodEnum<{
|
|
15
|
+
pee: "pee";
|
|
16
|
+
poo: "poo";
|
|
17
|
+
}>;
|
|
18
|
+
}, z.core.$strict>;
|
|
19
|
+
export declare const TreatActivityDetails: z.ZodObject<{
|
|
20
|
+
type: z.ZodLiteral<"TREAT">;
|
|
21
|
+
treatType: z.ZodOptional<z.ZodString>;
|
|
22
|
+
}, z.core.$strict>;
|
|
23
|
+
export declare const PlaytimeActivityDetails: z.ZodObject<{
|
|
24
|
+
type: z.ZodLiteral<"PLAYTIME">;
|
|
25
|
+
durationMin: z.ZodOptional<z.ZodNumber>;
|
|
26
|
+
}, z.core.$strict>;
|
|
27
|
+
export declare const MedicationActivityDetails: z.ZodObject<{
|
|
28
|
+
type: z.ZodLiteral<"MEDICATION">;
|
|
29
|
+
name: z.ZodOptional<z.ZodString>;
|
|
30
|
+
amount: z.ZodOptional<z.ZodNumber>;
|
|
31
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
32
|
+
}, z.core.$strict>;
|
|
33
|
+
export declare const VetActivityDetails: z.ZodObject<{
|
|
34
|
+
type: z.ZodLiteral<"VET_APPOINTMENT">;
|
|
35
|
+
visitType: z.ZodOptional<z.ZodString>;
|
|
36
|
+
location: z.ZodOptional<z.ZodString>;
|
|
37
|
+
}, z.core.$strict>;
|
|
38
|
+
export declare const GroomingActivityDetails: z.ZodObject<{
|
|
39
|
+
type: z.ZodLiteral<"GROOMING">;
|
|
40
|
+
groomingType: z.ZodOptional<z.ZodString>;
|
|
41
|
+
}, z.core.$strict>;
|
|
42
|
+
export declare const BathActivityDetails: z.ZodObject<{
|
|
43
|
+
type: z.ZodLiteral<"BATH">;
|
|
44
|
+
durationMin: z.ZodOptional<z.ZodNumber>;
|
|
45
|
+
}, z.core.$strict>;
|
|
46
|
+
export declare const NailTrimActivityDetails: z.ZodObject<{
|
|
47
|
+
type: z.ZodLiteral<"NAIL_TRIM">;
|
|
48
|
+
}, z.core.$strict>;
|
|
49
|
+
export declare const ActivityDetails: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
50
|
+
type: z.ZodLiteral<"WALK">;
|
|
51
|
+
durationMin: z.ZodOptional<z.ZodNumber>;
|
|
52
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
53
|
+
type: z.ZodLiteral<"MEAL">;
|
|
54
|
+
amount: z.ZodOptional<z.ZodNumber>;
|
|
55
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
56
|
+
foodType: z.ZodOptional<z.ZodString>;
|
|
57
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
58
|
+
type: z.ZodLiteral<"BATHROOM">;
|
|
59
|
+
subtype: z.ZodEnum<{
|
|
60
|
+
pee: "pee";
|
|
61
|
+
poo: "poo";
|
|
62
|
+
}>;
|
|
63
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
64
|
+
type: z.ZodLiteral<"TREAT">;
|
|
65
|
+
treatType: z.ZodOptional<z.ZodString>;
|
|
66
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
67
|
+
type: z.ZodLiteral<"PLAYTIME">;
|
|
68
|
+
durationMin: z.ZodOptional<z.ZodNumber>;
|
|
69
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
70
|
+
type: z.ZodLiteral<"MEDICATION">;
|
|
71
|
+
name: z.ZodOptional<z.ZodString>;
|
|
72
|
+
amount: z.ZodOptional<z.ZodNumber>;
|
|
73
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
74
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
75
|
+
type: z.ZodLiteral<"VET_APPOINTMENT">;
|
|
76
|
+
visitType: z.ZodOptional<z.ZodString>;
|
|
77
|
+
location: z.ZodOptional<z.ZodString>;
|
|
78
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
79
|
+
type: z.ZodLiteral<"GROOMING">;
|
|
80
|
+
groomingType: z.ZodOptional<z.ZodString>;
|
|
81
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
82
|
+
type: z.ZodLiteral<"BATH">;
|
|
83
|
+
durationMin: z.ZodOptional<z.ZodNumber>;
|
|
84
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
85
|
+
type: z.ZodLiteral<"NAIL_TRIM">;
|
|
86
|
+
}, z.core.$strict>], "type">;
|
|
87
|
+
export declare const WalkReminderDetails: z.ZodObject<{
|
|
88
|
+
type: z.ZodLiteral<"WALK">;
|
|
89
|
+
durationMin: z.ZodOptional<z.ZodNumber>;
|
|
90
|
+
}, z.core.$strict>;
|
|
91
|
+
export declare const MealReminderDetails: z.ZodObject<{
|
|
92
|
+
type: z.ZodLiteral<"MEAL">;
|
|
93
|
+
amount: z.ZodOptional<z.ZodNumber>;
|
|
94
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
95
|
+
foodType: z.ZodOptional<z.ZodString>;
|
|
96
|
+
}, z.core.$strict>;
|
|
97
|
+
export declare const BathroomReminderDetails: z.ZodObject<{
|
|
98
|
+
type: z.ZodLiteral<"BATHROOM">;
|
|
99
|
+
}, z.core.$strict>;
|
|
100
|
+
export declare const TreatReminderDetails: z.ZodObject<{
|
|
101
|
+
type: z.ZodLiteral<"TREAT">;
|
|
102
|
+
treatType: z.ZodOptional<z.ZodString>;
|
|
103
|
+
}, z.core.$strict>;
|
|
104
|
+
export declare const PlaytimeReminderDetails: z.ZodObject<{
|
|
105
|
+
type: z.ZodLiteral<"PLAYTIME">;
|
|
106
|
+
durationMin: z.ZodOptional<z.ZodNumber>;
|
|
107
|
+
}, z.core.$strict>;
|
|
108
|
+
export declare const MedicationReminderDetails: z.ZodObject<{
|
|
109
|
+
type: z.ZodLiteral<"MEDICATION">;
|
|
110
|
+
name: z.ZodString;
|
|
111
|
+
}, z.core.$strict>;
|
|
112
|
+
export declare const VetReminderDetails: z.ZodObject<{
|
|
113
|
+
type: z.ZodLiteral<"VET_APPOINTMENT">;
|
|
114
|
+
visitType: z.ZodOptional<z.ZodString>;
|
|
115
|
+
location: z.ZodOptional<z.ZodString>;
|
|
116
|
+
}, z.core.$strict>;
|
|
117
|
+
export declare const GroomingReminderDetails: z.ZodObject<{
|
|
118
|
+
type: z.ZodLiteral<"GROOMING">;
|
|
119
|
+
groomingType: z.ZodOptional<z.ZodString>;
|
|
120
|
+
}, z.core.$strict>;
|
|
121
|
+
export declare const BathReminderDetails: z.ZodObject<{
|
|
122
|
+
type: z.ZodLiteral<"BATH">;
|
|
123
|
+
}, z.core.$strict>;
|
|
124
|
+
export declare const NailTrimReminderDetails: z.ZodObject<{
|
|
125
|
+
type: z.ZodLiteral<"NAIL_TRIM">;
|
|
126
|
+
}, z.core.$strict>;
|
|
127
|
+
export declare const ReminderDetails: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
128
|
+
type: z.ZodLiteral<"WALK">;
|
|
129
|
+
durationMin: z.ZodOptional<z.ZodNumber>;
|
|
130
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
131
|
+
type: z.ZodLiteral<"MEAL">;
|
|
132
|
+
amount: z.ZodOptional<z.ZodNumber>;
|
|
133
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
134
|
+
foodType: z.ZodOptional<z.ZodString>;
|
|
135
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
136
|
+
type: z.ZodLiteral<"BATHROOM">;
|
|
137
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
138
|
+
type: z.ZodLiteral<"TREAT">;
|
|
139
|
+
treatType: z.ZodOptional<z.ZodString>;
|
|
140
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
141
|
+
type: z.ZodLiteral<"PLAYTIME">;
|
|
142
|
+
durationMin: z.ZodOptional<z.ZodNumber>;
|
|
143
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
144
|
+
type: z.ZodLiteral<"MEDICATION">;
|
|
145
|
+
name: z.ZodString;
|
|
146
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
147
|
+
type: z.ZodLiteral<"VET_APPOINTMENT">;
|
|
148
|
+
visitType: z.ZodOptional<z.ZodString>;
|
|
149
|
+
location: z.ZodOptional<z.ZodString>;
|
|
150
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
151
|
+
type: z.ZodLiteral<"GROOMING">;
|
|
152
|
+
groomingType: z.ZodOptional<z.ZodString>;
|
|
153
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
154
|
+
type: z.ZodLiteral<"BATH">;
|
|
155
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
156
|
+
type: z.ZodLiteral<"NAIL_TRIM">;
|
|
157
|
+
}, z.core.$strict>], "type">;
|
|
158
|
+
export declare function validateActivityDetails(details: unknown): {
|
|
159
|
+
type: "WALK";
|
|
160
|
+
durationMin?: number | undefined;
|
|
161
|
+
} | {
|
|
162
|
+
type: "MEAL";
|
|
163
|
+
amount?: number | undefined;
|
|
164
|
+
unit?: string | undefined;
|
|
165
|
+
foodType?: string | undefined;
|
|
166
|
+
} | {
|
|
167
|
+
type: "BATHROOM";
|
|
168
|
+
subtype: "pee" | "poo";
|
|
169
|
+
} | {
|
|
170
|
+
type: "TREAT";
|
|
171
|
+
treatType?: string | undefined;
|
|
172
|
+
} | {
|
|
173
|
+
type: "PLAYTIME";
|
|
174
|
+
durationMin?: number | undefined;
|
|
175
|
+
} | {
|
|
176
|
+
type: "MEDICATION";
|
|
177
|
+
name?: string | undefined;
|
|
178
|
+
amount?: number | undefined;
|
|
179
|
+
unit?: string | undefined;
|
|
180
|
+
} | {
|
|
181
|
+
type: "VET_APPOINTMENT";
|
|
182
|
+
visitType?: string | undefined;
|
|
183
|
+
location?: string | undefined;
|
|
184
|
+
} | {
|
|
185
|
+
type: "GROOMING";
|
|
186
|
+
groomingType?: string | undefined;
|
|
187
|
+
} | {
|
|
188
|
+
type: "BATH";
|
|
189
|
+
durationMin?: number | undefined;
|
|
190
|
+
} | {
|
|
191
|
+
type: "NAIL_TRIM";
|
|
192
|
+
};
|
|
193
|
+
export declare function validateReminderDetails(details: unknown): {
|
|
194
|
+
type: "WALK";
|
|
195
|
+
durationMin?: number | undefined;
|
|
196
|
+
} | {
|
|
197
|
+
type: "MEAL";
|
|
198
|
+
amount?: number | undefined;
|
|
199
|
+
unit?: string | undefined;
|
|
200
|
+
foodType?: string | undefined;
|
|
201
|
+
} | {
|
|
202
|
+
type: "BATHROOM";
|
|
203
|
+
} | {
|
|
204
|
+
type: "TREAT";
|
|
205
|
+
treatType?: string | undefined;
|
|
206
|
+
} | {
|
|
207
|
+
type: "PLAYTIME";
|
|
208
|
+
durationMin?: number | undefined;
|
|
209
|
+
} | {
|
|
210
|
+
type: "MEDICATION";
|
|
211
|
+
name: string;
|
|
212
|
+
} | {
|
|
213
|
+
type: "VET_APPOINTMENT";
|
|
214
|
+
visitType?: string | undefined;
|
|
215
|
+
location?: string | undefined;
|
|
216
|
+
} | {
|
|
217
|
+
type: "GROOMING";
|
|
218
|
+
groomingType?: string | undefined;
|
|
219
|
+
} | {
|
|
220
|
+
type: "BATH";
|
|
221
|
+
} | {
|
|
222
|
+
type: "NAIL_TRIM";
|
|
223
|
+
};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ActivityType } from "./enums.js";
|
|
3
|
+
import { Amount, AmountUnit, BathroomActivitySubtype, DurationMin, TinyText, } from "./schemas.js";
|
|
4
|
+
// =============================================================================
|
|
5
|
+
// ACTIVITY DETAILS - validated when logging an activity
|
|
6
|
+
// =============================================================================
|
|
7
|
+
export const WalkActivityDetails = z
|
|
8
|
+
.object({
|
|
9
|
+
type: z.literal(ActivityType.enum.WALK),
|
|
10
|
+
durationMin: DurationMin.optional(),
|
|
11
|
+
})
|
|
12
|
+
.strict();
|
|
13
|
+
export const MealActivityDetails = z
|
|
14
|
+
.object({
|
|
15
|
+
type: z.literal(ActivityType.enum.MEAL),
|
|
16
|
+
amount: Amount.optional(),
|
|
17
|
+
unit: AmountUnit.optional(),
|
|
18
|
+
foodType: TinyText.optional(),
|
|
19
|
+
})
|
|
20
|
+
.strict();
|
|
21
|
+
export const BathroomActivityDetails = z
|
|
22
|
+
.object({
|
|
23
|
+
type: z.literal(ActivityType.enum.BATHROOM),
|
|
24
|
+
subtype: BathroomActivitySubtype,
|
|
25
|
+
})
|
|
26
|
+
.strict();
|
|
27
|
+
export const TreatActivityDetails = z
|
|
28
|
+
.object({
|
|
29
|
+
type: z.literal(ActivityType.enum.TREAT),
|
|
30
|
+
treatType: TinyText.optional(),
|
|
31
|
+
})
|
|
32
|
+
.strict();
|
|
33
|
+
export const PlaytimeActivityDetails = z
|
|
34
|
+
.object({
|
|
35
|
+
type: z.literal(ActivityType.enum.PLAYTIME),
|
|
36
|
+
durationMin: DurationMin.optional(),
|
|
37
|
+
})
|
|
38
|
+
.strict();
|
|
39
|
+
export const MedicationActivityDetails = z
|
|
40
|
+
.object({
|
|
41
|
+
type: z.literal(ActivityType.enum.MEDICATION),
|
|
42
|
+
name: TinyText.optional(),
|
|
43
|
+
amount: Amount.optional(),
|
|
44
|
+
unit: AmountUnit.optional(),
|
|
45
|
+
})
|
|
46
|
+
.strict();
|
|
47
|
+
export const VetActivityDetails = z
|
|
48
|
+
.object({
|
|
49
|
+
type: z.literal(ActivityType.enum.VET_APPOINTMENT),
|
|
50
|
+
visitType: TinyText.optional(),
|
|
51
|
+
location: TinyText.optional(),
|
|
52
|
+
})
|
|
53
|
+
.strict();
|
|
54
|
+
export const GroomingActivityDetails = z
|
|
55
|
+
.object({
|
|
56
|
+
type: z.literal(ActivityType.enum.GROOMING),
|
|
57
|
+
groomingType: TinyText.optional(),
|
|
58
|
+
})
|
|
59
|
+
.strict();
|
|
60
|
+
export const BathActivityDetails = z
|
|
61
|
+
.object({
|
|
62
|
+
type: z.literal(ActivityType.enum.BATH),
|
|
63
|
+
durationMin: DurationMin.optional(),
|
|
64
|
+
})
|
|
65
|
+
.strict();
|
|
66
|
+
export const NailTrimActivityDetails = z
|
|
67
|
+
.object({
|
|
68
|
+
type: z.literal(ActivityType.enum.NAIL_TRIM),
|
|
69
|
+
})
|
|
70
|
+
.strict();
|
|
71
|
+
export const ActivityDetails = z.discriminatedUnion("type", [
|
|
72
|
+
WalkActivityDetails,
|
|
73
|
+
MealActivityDetails,
|
|
74
|
+
BathroomActivityDetails,
|
|
75
|
+
TreatActivityDetails,
|
|
76
|
+
PlaytimeActivityDetails,
|
|
77
|
+
MedicationActivityDetails,
|
|
78
|
+
VetActivityDetails,
|
|
79
|
+
GroomingActivityDetails,
|
|
80
|
+
BathActivityDetails,
|
|
81
|
+
NailTrimActivityDetails,
|
|
82
|
+
]);
|
|
83
|
+
// =============================================================================
|
|
84
|
+
// REMINDER DETAILS - validated when creating/updating a reminder
|
|
85
|
+
// =============================================================================
|
|
86
|
+
export const WalkReminderDetails = z
|
|
87
|
+
.object({
|
|
88
|
+
type: z.literal(ActivityType.enum.WALK),
|
|
89
|
+
durationMin: DurationMin.optional(),
|
|
90
|
+
})
|
|
91
|
+
.strict();
|
|
92
|
+
export const MealReminderDetails = z
|
|
93
|
+
.object({
|
|
94
|
+
type: z.literal(ActivityType.enum.MEAL),
|
|
95
|
+
amount: Amount.optional(),
|
|
96
|
+
unit: AmountUnit.optional(),
|
|
97
|
+
foodType: TinyText.optional(),
|
|
98
|
+
})
|
|
99
|
+
.strict();
|
|
100
|
+
export const BathroomReminderDetails = z
|
|
101
|
+
.object({
|
|
102
|
+
type: z.literal(ActivityType.enum.BATHROOM),
|
|
103
|
+
})
|
|
104
|
+
.strict();
|
|
105
|
+
export const TreatReminderDetails = z
|
|
106
|
+
.object({
|
|
107
|
+
type: z.literal(ActivityType.enum.TREAT),
|
|
108
|
+
treatType: TinyText.optional(),
|
|
109
|
+
})
|
|
110
|
+
.strict();
|
|
111
|
+
export const PlaytimeReminderDetails = z
|
|
112
|
+
.object({
|
|
113
|
+
type: z.literal(ActivityType.enum.PLAYTIME),
|
|
114
|
+
durationMin: DurationMin.optional(),
|
|
115
|
+
})
|
|
116
|
+
.strict();
|
|
117
|
+
export const MedicationReminderDetails = z
|
|
118
|
+
.object({
|
|
119
|
+
type: z.literal(ActivityType.enum.MEDICATION),
|
|
120
|
+
name: TinyText.min(1),
|
|
121
|
+
})
|
|
122
|
+
.strict();
|
|
123
|
+
export const VetReminderDetails = z
|
|
124
|
+
.object({
|
|
125
|
+
type: z.literal(ActivityType.enum.VET_APPOINTMENT),
|
|
126
|
+
visitType: TinyText.optional(),
|
|
127
|
+
location: TinyText.optional(),
|
|
128
|
+
})
|
|
129
|
+
.strict();
|
|
130
|
+
export const GroomingReminderDetails = z
|
|
131
|
+
.object({
|
|
132
|
+
type: z.literal(ActivityType.enum.GROOMING),
|
|
133
|
+
groomingType: TinyText.optional(),
|
|
134
|
+
})
|
|
135
|
+
.strict();
|
|
136
|
+
export const BathReminderDetails = z
|
|
137
|
+
.object({
|
|
138
|
+
type: z.literal(ActivityType.enum.BATH),
|
|
139
|
+
})
|
|
140
|
+
.strict();
|
|
141
|
+
export const NailTrimReminderDetails = z
|
|
142
|
+
.object({
|
|
143
|
+
type: z.literal(ActivityType.enum.NAIL_TRIM),
|
|
144
|
+
})
|
|
145
|
+
.strict();
|
|
146
|
+
export const ReminderDetails = z.discriminatedUnion("type", [
|
|
147
|
+
WalkReminderDetails,
|
|
148
|
+
MealReminderDetails,
|
|
149
|
+
BathroomReminderDetails,
|
|
150
|
+
TreatReminderDetails,
|
|
151
|
+
PlaytimeReminderDetails,
|
|
152
|
+
MedicationReminderDetails,
|
|
153
|
+
VetReminderDetails,
|
|
154
|
+
GroomingReminderDetails,
|
|
155
|
+
BathReminderDetails,
|
|
156
|
+
NailTrimReminderDetails,
|
|
157
|
+
]);
|
|
158
|
+
// =============================================================================
|
|
159
|
+
// HELPERS
|
|
160
|
+
// =============================================================================
|
|
161
|
+
export function validateActivityDetails(details) {
|
|
162
|
+
return ActivityDetails.parse(details);
|
|
163
|
+
}
|
|
164
|
+
export function validateReminderDetails(details) {
|
|
165
|
+
return ReminderDetails.parse(details);
|
|
166
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Tables that can be wiped by the admin.wipeTestData endpoint.
|
|
4
|
+
* Order matters for display (roughly grouped by category).
|
|
5
|
+
*/
|
|
6
|
+
export declare const wipeTables: readonly ["activities", "reminderAlerts", "reminders", "photos", "healthLogs", "achievements", "petAccess", "pendingInvites", "pets"];
|
|
7
|
+
export type WipeTable = (typeof wipeTables)[number];
|
|
8
|
+
/**
|
|
9
|
+
* Human-readable labels for each table.
|
|
10
|
+
*/
|
|
11
|
+
export declare const WIPE_TABLE_LABELS: Record<WipeTable, string>;
|
|
12
|
+
/**
|
|
13
|
+
* Output schema for admin.wipeTestData endpoint.
|
|
14
|
+
*/
|
|
15
|
+
export declare const WipeTestDataOutput: z.ZodObject<{
|
|
16
|
+
deleted: z.ZodObject<{
|
|
17
|
+
activities: z.ZodNumber;
|
|
18
|
+
reminderAlerts: z.ZodNumber;
|
|
19
|
+
reminders: z.ZodNumber;
|
|
20
|
+
photos: z.ZodNumber;
|
|
21
|
+
healthLogs: z.ZodNumber;
|
|
22
|
+
achievements: z.ZodNumber;
|
|
23
|
+
petAccess: z.ZodNumber;
|
|
24
|
+
pendingInvites: z.ZodNumber;
|
|
25
|
+
pets: z.ZodNumber;
|
|
26
|
+
}, z.core.$strip>;
|
|
27
|
+
}, z.core.$strip>;
|
|
28
|
+
export type WipeTestDataOutput = z.infer<typeof WipeTestDataOutput>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
// =============================================================================
|
|
3
|
+
// ADMIN SCHEMAS
|
|
4
|
+
// =============================================================================
|
|
5
|
+
// Schemas for admin-only endpoints. Used by both server (output validation)
|
|
6
|
+
// and mobile (type inference for responses).
|
|
7
|
+
// =============================================================================
|
|
8
|
+
/**
|
|
9
|
+
* Tables that can be wiped by the admin.wipeTestData endpoint.
|
|
10
|
+
* Order matters for display (roughly grouped by category).
|
|
11
|
+
*/
|
|
12
|
+
export const wipeTables = [
|
|
13
|
+
"activities",
|
|
14
|
+
"reminderAlerts",
|
|
15
|
+
"reminders",
|
|
16
|
+
"photos",
|
|
17
|
+
"healthLogs",
|
|
18
|
+
"achievements",
|
|
19
|
+
"petAccess",
|
|
20
|
+
"pendingInvites",
|
|
21
|
+
"pets",
|
|
22
|
+
];
|
|
23
|
+
/**
|
|
24
|
+
* Human-readable labels for each table.
|
|
25
|
+
*/
|
|
26
|
+
export const WIPE_TABLE_LABELS = {
|
|
27
|
+
activities: "Activities",
|
|
28
|
+
reminderAlerts: "Reminder Alerts",
|
|
29
|
+
reminders: "Reminders",
|
|
30
|
+
photos: "Photos",
|
|
31
|
+
healthLogs: "Health Logs",
|
|
32
|
+
achievements: "Achievements",
|
|
33
|
+
petAccess: "Pet Access Records",
|
|
34
|
+
pendingInvites: "Pending Invites",
|
|
35
|
+
pets: "Pets",
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Output schema for admin.wipeTestData endpoint.
|
|
39
|
+
*/
|
|
40
|
+
export const WipeTestDataOutput = z.object({
|
|
41
|
+
deleted: z.object({
|
|
42
|
+
activities: z.number(),
|
|
43
|
+
reminderAlerts: z.number(),
|
|
44
|
+
reminders: z.number(),
|
|
45
|
+
photos: z.number(),
|
|
46
|
+
healthLogs: z.number(),
|
|
47
|
+
achievements: z.number(),
|
|
48
|
+
petAccess: z.number(),
|
|
49
|
+
pendingInvites: z.number(),
|
|
50
|
+
pets: z.number(),
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { JwtToken, UserId } from "./ids.js";
|
|
3
|
+
export declare const OAuthTokenResponse: z.ZodObject<{
|
|
4
|
+
access_token: z.ZodPipe<z.ZodString, z.ZodCustom<JwtToken, JwtToken>>;
|
|
5
|
+
refresh_token: z.ZodOptional<z.ZodString>;
|
|
6
|
+
id_token: z.ZodOptional<z.ZodString>;
|
|
7
|
+
token_type: z.ZodOptional<z.ZodString>;
|
|
8
|
+
expires_in: z.ZodOptional<z.ZodNumber>;
|
|
9
|
+
}, z.core.$strip>;
|
|
10
|
+
export type OAuthTokenResponse = z.infer<typeof OAuthTokenResponse>;
|
|
11
|
+
export declare const AuthInfo: z.ZodObject<{
|
|
12
|
+
sub: z.ZodPipe<z.ZodString, z.ZodCustom<UserId, UserId>>;
|
|
13
|
+
email: z.ZodEmail;
|
|
14
|
+
name: z.ZodOptional<z.ZodString>;
|
|
15
|
+
picture: z.ZodOptional<z.ZodURL>;
|
|
16
|
+
roles: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
17
|
+
admin: "admin";
|
|
18
|
+
}>>>;
|
|
19
|
+
}, z.core.$strip>;
|
|
20
|
+
export type AuthInfo = z.infer<typeof AuthInfo>;
|
|
21
|
+
export { JwtToken, UserId };
|