gymmonk-schema 0.28.1 → 0.30.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.
@@ -212,6 +212,7 @@ export declare const memberProfileSchema: z.ZodObject<{
212
212
  invitePhone: z.ZodNullable<z.ZodString>;
213
213
  claimedAt: z.ZodNullable<z.ZodISODateTime>;
214
214
  centerId: z.ZodNullable<z.ZodString>;
215
+ centerName: z.ZodNullable<z.ZodString>;
215
216
  memberCode: z.ZodString;
216
217
  gender: z.ZodEnum<{
217
218
  male: "male";
@@ -1 +1 @@
1
- {"version":3,"file":"member-profile.d.ts","sourceRoot":"","sources":["../src/member-profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;iBAW5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAI5D,eAAO,MAAM,sBAAsB;;;;iBAIjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAItE;;;GAGG;AACH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkBxC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF,2CAA2C;AAC3C,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAA0C,CAAC;AACrF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAIpF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;;EAAgC,CAAC;AACtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwC9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
1
+ {"version":3,"file":"member-profile.d.ts","sourceRoot":"","sources":["../src/member-profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;iBAW5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAI5D,eAAO,MAAM,sBAAsB;;;;iBAIjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAItE;;;GAGG;AACH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkBxC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF,2CAA2C;AAC3C,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAA0C,CAAC;AACrF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAIpF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;;EAAgC,CAAC;AACtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuD9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
@@ -91,6 +91,21 @@ export const memberProfileSchema = z.object({
91
91
  * makes joining somewhere else afterwards possible.
92
92
  */
93
93
  centerId: objectIdSchema.nullable(),
94
+ /**
95
+ * That gym's name, resolved server-side. Null exactly when `centerId` is.
96
+ *
97
+ * It rides along with the id because a member CANNOT look it up themselves:
98
+ * the whole `/centers` router requires `manage Center`, so a member asking
99
+ * for the name of their own gym gets a 403. Without this field, every member
100
+ * surface that wants to name their gym either shows an id, shows nothing, or
101
+ * fires a request that can only fail — and all three happened before it
102
+ * existed.
103
+ *
104
+ * Denormalised on READ, not stored: it is resolved per request from the
105
+ * Center, so renaming a gym is immediately correct everywhere rather than
106
+ * leaving stale copies on every member record.
107
+ */
108
+ centerName: z.string().nullable(),
94
109
  /** Public member code within the gym, `GM-YYYY-NNNNN`. Server-generated. */
95
110
  memberCode: z.string(),
96
111
  gender: genderSchema,
@@ -1 +1 @@
1
- {"version":3,"file":"member-profile.js","sourceRoot":"","sources":["../src/member-profile.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gymmonk-schema — Member fitness profile\n * =======================================\n * Product-owned personal + fitness data for a gym member, separate from the\n * auth-mirrored `User` (which holds name/email/phone/avatar). One profile per\n * user, scoped to the single center they belong to.\n *\n * Body measurements are stored RAW (cm / kg); BMI and body-fat % are derived at\n * the edge, never persisted. Full Aadhaar is never stored — only its last four\n * digits and/or an uploaded ID-proof URL (DPDP data-minimisation).\n *\n * @module gymmonk-schema/member-profile\n */\n\nimport { z } from 'zod';\nimport {\n isoDateSchema,\n isoDateTimeSchema,\n objectIdSchema,\n optionalTrimmedString,\n phoneSchema,\n} from './common.js';\nimport { addressSchema, bloodGroupSchema, genderSchema } from './shared.js';\n\n// ─── Body measurements ───────────────────────────────────────────────────────\n\n/**\n * Raw body measurements in metric units. Height + weight anchor BMI; neck /\n * waist / hips feed the U.S.-Navy body-fat estimate; the rest are progress\n * tracking. All optional so a partial update touches only what changed.\n */\nexport const bodyMetricsSchema = z.object({\n heightCm: z.number().min(120).max(230).optional(),\n weightKg: z.number().min(30).max(300).optional(),\n neckCm: z.number().min(20).max(80).optional(),\n waistCm: z.number().min(40).max(200).optional(),\n hipsCm: z.number().min(50).max(200).optional(),\n forearmCm: z.number().min(10).max(80).optional(),\n armsCm: z.number().min(10).max(100).optional(),\n chestCm: z.number().min(40).max(200).optional(),\n thighCm: z.number().min(20).max(120).optional(),\n calfCm: z.number().min(15).max(80).optional(),\n});\nexport type BodyMetrics = z.infer<typeof bodyMetricsSchema>;\n\n// ─── Emergency contact ───────────────────────────────────────────────────────\n\nexport const emergencyContactSchema = z.object({\n name: z.string().trim().min(1).max(120),\n phone: phoneSchema,\n relationship: optionalTrimmedString(60),\n});\nexport type EmergencyContact = z.infer<typeof emergencyContactSchema>;\n\n// ─── Create / update body ────────────────────────────────────────────────────\n\n/**\n * Upsert the member's profile. Used by member onboarding, owner \"add member\",\n * and the member's own edit-profile screen.\n */\nexport const upsertMemberProfileBodySchema = z.object({\n gender: genderSchema,\n dob: isoDateSchema,\n bloodGroup: bloodGroupSchema.optional(),\n address: addressSchema.optional(),\n emergencyContacts: z.array(emergencyContactSchema).max(5).default([]),\n body: bodyMetricsSchema.default({}),\n /** Uploaded ID-proof document URL (Aadhaar/PAN/etc.). */\n idProofUrl: optionalTrimmedString(2000),\n /** Last four digits of Aadhaar for display only. */\n aadhaarLast4: z.preprocess(\n (v) => (typeof v === 'string' && v.trim() === '' ? undefined : v),\n z\n .string()\n .trim()\n .regex(/^\\d{4}$/, 'Enter the last 4 digits')\n .optional(),\n ),\n});\nexport type UpsertMemberProfileBody = z.infer<typeof upsertMemberProfileBodySchema>;\n\n/** Partial edit — every field optional. */\nexport const updateMemberProfileBodySchema = upsertMemberProfileBodySchema.partial();\nexport type UpdateMemberProfileBody = z.infer<typeof updateMemberProfileBodySchema>;\n\n// ─── Entity (API response) ───────────────────────────────────────────────────\n\n/**\n * Lifecycle of a member record. `invited` = the gym created it and the person\n * has not claimed their login yet (the roster still works fully); `active` = a\n * central-auth account is linked.\n */\nexport const memberRecordStatusSchema = z.enum(['invited', 'active']);\nexport type MemberRecordStatus = z.infer<typeof memberRecordStatusSchema>;\n\nexport const memberProfileSchema = z.object({\n id: objectIdSchema,\n /**\n * The linked central-auth user. NULL until the member claims their invite —\n * gyms add members at the desk long before (or without) an app login.\n */\n userId: objectIdSchema.nullable(),\n status: memberRecordStatusSchema,\n /** The address the invite was issued to; the claim must match it. */\n inviteEmail: z.string().nullable(),\n /** WhatsApp number the owner shares the invite link on. */\n invitePhone: z.string().nullable(),\n claimedAt: isoDateTimeSchema.nullable(),\n /**\n * The gym this member belongs to. NULL when they belong to none.\n *\n * Everybody signs up as a member with no gym attached and finds one\n * afterwards, so \"no gym yet\" is the ordinary starting state, not an error.\n * It is set when a gym ACCEPTS their join request (or when they claim an\n * invite a gym issued them), and cleared when they leave — which is what\n * makes joining somewhere else afterwards possible.\n */\n centerId: objectIdSchema.nullable(),\n /** Public member code within the gym, `GM-YYYY-NNNNN`. Server-generated. */\n memberCode: z.string(),\n gender: genderSchema,\n /** Null when the gym added the member without their date of birth. */\n dob: isoDateSchema.nullable(),\n bloodGroup: bloodGroupSchema.nullable(),\n address: addressSchema.nullable(),\n emergencyContacts: z.array(emergencyContactSchema),\n body: bodyMetricsSchema,\n idProofUrl: z.string().nullable(),\n aadhaarLast4: z.string().nullable(),\n /** The workout plan the gym assigned to this member (owner action). */\n assignedWorkoutPlanId: objectIdSchema.nullable(),\n /** The trainer looking after this member (owner action). */\n assignedTrainerId: objectIdSchema.nullable(),\n createdAt: isoDateTimeSchema,\n updatedAt: isoDateTimeSchema,\n});\nexport type MemberProfile = z.infer<typeof memberProfileSchema>;\n"]}
1
+ {"version":3,"file":"member-profile.js","sourceRoot":"","sources":["../src/member-profile.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gymmonk-schema — Member fitness profile\n * =======================================\n * Product-owned personal + fitness data for a gym member, separate from the\n * auth-mirrored `User` (which holds name/email/phone/avatar). One profile per\n * user, scoped to the single center they belong to.\n *\n * Body measurements are stored RAW (cm / kg); BMI and body-fat % are derived at\n * the edge, never persisted. Full Aadhaar is never stored — only its last four\n * digits and/or an uploaded ID-proof URL (DPDP data-minimisation).\n *\n * @module gymmonk-schema/member-profile\n */\n\nimport { z } from 'zod';\nimport {\n isoDateSchema,\n isoDateTimeSchema,\n objectIdSchema,\n optionalTrimmedString,\n phoneSchema,\n} from './common.js';\nimport { addressSchema, bloodGroupSchema, genderSchema } from './shared.js';\n\n// ─── Body measurements ───────────────────────────────────────────────────────\n\n/**\n * Raw body measurements in metric units. Height + weight anchor BMI; neck /\n * waist / hips feed the U.S.-Navy body-fat estimate; the rest are progress\n * tracking. All optional so a partial update touches only what changed.\n */\nexport const bodyMetricsSchema = z.object({\n heightCm: z.number().min(120).max(230).optional(),\n weightKg: z.number().min(30).max(300).optional(),\n neckCm: z.number().min(20).max(80).optional(),\n waistCm: z.number().min(40).max(200).optional(),\n hipsCm: z.number().min(50).max(200).optional(),\n forearmCm: z.number().min(10).max(80).optional(),\n armsCm: z.number().min(10).max(100).optional(),\n chestCm: z.number().min(40).max(200).optional(),\n thighCm: z.number().min(20).max(120).optional(),\n calfCm: z.number().min(15).max(80).optional(),\n});\nexport type BodyMetrics = z.infer<typeof bodyMetricsSchema>;\n\n// ─── Emergency contact ───────────────────────────────────────────────────────\n\nexport const emergencyContactSchema = z.object({\n name: z.string().trim().min(1).max(120),\n phone: phoneSchema,\n relationship: optionalTrimmedString(60),\n});\nexport type EmergencyContact = z.infer<typeof emergencyContactSchema>;\n\n// ─── Create / update body ────────────────────────────────────────────────────\n\n/**\n * Upsert the member's profile. Used by member onboarding, owner \"add member\",\n * and the member's own edit-profile screen.\n */\nexport const upsertMemberProfileBodySchema = z.object({\n gender: genderSchema,\n dob: isoDateSchema,\n bloodGroup: bloodGroupSchema.optional(),\n address: addressSchema.optional(),\n emergencyContacts: z.array(emergencyContactSchema).max(5).default([]),\n body: bodyMetricsSchema.default({}),\n /** Uploaded ID-proof document URL (Aadhaar/PAN/etc.). */\n idProofUrl: optionalTrimmedString(2000),\n /** Last four digits of Aadhaar for display only. */\n aadhaarLast4: z.preprocess(\n (v) => (typeof v === 'string' && v.trim() === '' ? undefined : v),\n z\n .string()\n .trim()\n .regex(/^\\d{4}$/, 'Enter the last 4 digits')\n .optional(),\n ),\n});\nexport type UpsertMemberProfileBody = z.infer<typeof upsertMemberProfileBodySchema>;\n\n/** Partial edit — every field optional. */\nexport const updateMemberProfileBodySchema = upsertMemberProfileBodySchema.partial();\nexport type UpdateMemberProfileBody = z.infer<typeof updateMemberProfileBodySchema>;\n\n// ─── Entity (API response) ───────────────────────────────────────────────────\n\n/**\n * Lifecycle of a member record. `invited` = the gym created it and the person\n * has not claimed their login yet (the roster still works fully); `active` = a\n * central-auth account is linked.\n */\nexport const memberRecordStatusSchema = z.enum(['invited', 'active']);\nexport type MemberRecordStatus = z.infer<typeof memberRecordStatusSchema>;\n\nexport const memberProfileSchema = z.object({\n id: objectIdSchema,\n /**\n * The linked central-auth user. NULL until the member claims their invite —\n * gyms add members at the desk long before (or without) an app login.\n */\n userId: objectIdSchema.nullable(),\n status: memberRecordStatusSchema,\n /** The address the invite was issued to; the claim must match it. */\n inviteEmail: z.string().nullable(),\n /** WhatsApp number the owner shares the invite link on. */\n invitePhone: z.string().nullable(),\n claimedAt: isoDateTimeSchema.nullable(),\n /**\n * The gym this member belongs to. NULL when they belong to none.\n *\n * Everybody signs up as a member with no gym attached and finds one\n * afterwards, so \"no gym yet\" is the ordinary starting state, not an error.\n * It is set when a gym ACCEPTS their join request (or when they claim an\n * invite a gym issued them), and cleared when they leave — which is what\n * makes joining somewhere else afterwards possible.\n */\n centerId: objectIdSchema.nullable(),\n /**\n * That gym's name, resolved server-side. Null exactly when `centerId` is.\n *\n * It rides along with the id because a member CANNOT look it up themselves:\n * the whole `/centers` router requires `manage Center`, so a member asking\n * for the name of their own gym gets a 403. Without this field, every member\n * surface that wants to name their gym either shows an id, shows nothing, or\n * fires a request that can only fail — and all three happened before it\n * existed.\n *\n * Denormalised on READ, not stored: it is resolved per request from the\n * Center, so renaming a gym is immediately correct everywhere rather than\n * leaving stale copies on every member record.\n */\n centerName: z.string().nullable(),\n /** Public member code within the gym, `GM-YYYY-NNNNN`. Server-generated. */\n memberCode: z.string(),\n gender: genderSchema,\n /** Null when the gym added the member without their date of birth. */\n dob: isoDateSchema.nullable(),\n bloodGroup: bloodGroupSchema.nullable(),\n address: addressSchema.nullable(),\n emergencyContacts: z.array(emergencyContactSchema),\n body: bodyMetricsSchema,\n idProofUrl: z.string().nullable(),\n aadhaarLast4: z.string().nullable(),\n /** The workout plan the gym assigned to this member (owner action). */\n assignedWorkoutPlanId: objectIdSchema.nullable(),\n /** The trainer looking after this member (owner action). */\n assignedTrainerId: objectIdSchema.nullable(),\n createdAt: isoDateTimeSchema,\n updatedAt: isoDateTimeSchema,\n});\nexport type MemberProfile = z.infer<typeof memberProfileSchema>;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gymmonk-schema",
3
- "version": "0.28.1",
3
+ "version": "0.30.0",
4
4
  "description": "Shared Zod schemas, enums and domain types for GymMonk (fitness SaaS) — single source of truth (SSOT) consumed by gymmonk-backend and gymmonk-web-client.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",