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 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 };