gymmonk-schema 0.3.0 → 0.3.1

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.
@@ -358,4 +358,64 @@ export declare const ownerOnboardingBodySchema: z.ZodObject<{
358
358
  }, z.core.$strip>;
359
359
  }, z.core.$strip>;
360
360
  export type OwnerOnboardingBody = z.infer<typeof ownerOnboardingBodySchema>;
361
+ /**
362
+ * In-progress onboarding draft: the UI-shaped, partially-filled form the client
363
+ * persists so a returning user resumes with their typed data intact. Opaque to
364
+ * the backend by design — the client owns the form shape, and the data is
365
+ * re-validated strictly by the member/owner body schemas at finalize. Keys are
366
+ * form field names; the backend caps the stored size.
367
+ */
368
+ export declare const onboardingDraftSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
369
+ export type OnboardingDraft = z.infer<typeof onboardingDraftSchema>;
370
+ /**
371
+ * The server-persisted onboarding cursor: which role the user picked, how far
372
+ * they reached (`step`, the 1-indexed sub-step of the current form), the gym a
373
+ * member chose, and the drafts. Read on app entry to resume to the exact step.
374
+ * `welcomed` records that the one-time welcome carousel has been seen.
375
+ */
376
+ export declare const onboardingProgressSchema: z.ZodObject<{
377
+ welcomed: z.ZodBoolean;
378
+ role: z.ZodNullable<z.ZodEnum<{
379
+ owner: "owner";
380
+ member: "member";
381
+ }>>;
382
+ step: z.ZodNumber;
383
+ gymCode: z.ZodOptional<z.ZodString>;
384
+ memberDraft: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
385
+ ownerDraft: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
386
+ }, z.core.$strip>;
387
+ export type OnboardingProgress = z.infer<typeof onboardingProgressSchema>;
388
+ /** `PUT /onboarding/progress` body — any subset of the cursor, merged server-side. */
389
+ export declare const onboardingProgressUpdateSchema: z.ZodObject<{
390
+ welcomed: z.ZodOptional<z.ZodBoolean>;
391
+ role: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
392
+ owner: "owner";
393
+ member: "member";
394
+ }>>>;
395
+ step: z.ZodOptional<z.ZodNumber>;
396
+ gymCode: z.ZodOptional<z.ZodString>;
397
+ memberDraft: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
398
+ ownerDraft: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
399
+ }, z.core.$strip>;
400
+ export type OnboardingProgressUpdate = z.infer<typeof onboardingProgressUpdateSchema>;
401
+ /**
402
+ * `GET /onboarding/status` response: completion truth (derived from the user's
403
+ * domain data) plus the resume cursor. `onboarded` is authoritative — an owner
404
+ * with an organisation, or a member with a profile, is onboarded regardless of
405
+ * the cursor.
406
+ */
407
+ export declare const onboardingStatusSchema: z.ZodObject<{
408
+ onboarded: z.ZodBoolean;
409
+ role: z.ZodNullable<z.ZodEnum<{
410
+ owner: "owner";
411
+ member: "member";
412
+ }>>;
413
+ organisationId: z.ZodOptional<z.ZodString>;
414
+ welcomed: z.ZodBoolean;
415
+ step: z.ZodNumber;
416
+ gymCode: z.ZodOptional<z.ZodString>;
417
+ memberDraft: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
418
+ ownerDraft: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
419
+ }, z.core.$strip>;
420
+ export type OnboardingStatus = z.infer<typeof onboardingStatusSchema>;
361
421
  //# sourceMappingURL=onboarding.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../src/onboarding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB,eAAO,MAAM,oBAAoB;;;EAA8B,CAAC;AAChE,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAIlE,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwBnC,CAAC;AACL,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAI9E,2EAA2E;AAC3E,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAa5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,mEAAmE;AACnE,eAAO,MAAM,6BAA6B;;;iBAGxC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAKpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
1
+ {"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../src/onboarding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB,eAAO,MAAM,oBAAoB;;;EAA8B,CAAC;AAChE,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAIlE,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwBnC,CAAC;AACL,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAI9E,2EAA2E;AAC3E,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAa5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,mEAAmE;AACnE,eAAO,MAAM,6BAA6B;;;iBAGxC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAKpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAI5E;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,wCAAoC,CAAC;AACvE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;iBAOnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,sFAAsF;AACtF,eAAO,MAAM,8BAA8B;;;;;;;;;;iBAS/B,CAAC;AACb,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC;AAEtF;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;iBASjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC"}
@@ -71,4 +71,54 @@ export const ownerOnboardingBodySchema = z.object({
71
71
  center: createCenterBodySchema,
72
72
  subscription: onboardingPlanSelectionSchema,
73
73
  });
74
+ // ─── Onboarding progress & resume ─────────────────────────────────────────────
75
+ /**
76
+ * In-progress onboarding draft: the UI-shaped, partially-filled form the client
77
+ * persists so a returning user resumes with their typed data intact. Opaque to
78
+ * the backend by design — the client owns the form shape, and the data is
79
+ * re-validated strictly by the member/owner body schemas at finalize. Keys are
80
+ * form field names; the backend caps the stored size.
81
+ */
82
+ export const onboardingDraftSchema = z.record(z.string(), z.unknown());
83
+ /**
84
+ * The server-persisted onboarding cursor: which role the user picked, how far
85
+ * they reached (`step`, the 1-indexed sub-step of the current form), the gym a
86
+ * member chose, and the drafts. Read on app entry to resume to the exact step.
87
+ * `welcomed` records that the one-time welcome carousel has been seen.
88
+ */
89
+ export const onboardingProgressSchema = z.object({
90
+ welcomed: z.boolean(),
91
+ role: onboardingRoleSchema.nullable(),
92
+ step: z.number().int().min(0).max(20),
93
+ gymCode: gymCodeSchema.optional(),
94
+ memberDraft: onboardingDraftSchema.optional(),
95
+ ownerDraft: onboardingDraftSchema.optional(),
96
+ });
97
+ /** `PUT /onboarding/progress` body — any subset of the cursor, merged server-side. */
98
+ export const onboardingProgressUpdateSchema = z
99
+ .object({
100
+ welcomed: z.boolean(),
101
+ role: onboardingRoleSchema.nullable(),
102
+ step: z.number().int().min(0).max(20),
103
+ gymCode: gymCodeSchema,
104
+ memberDraft: onboardingDraftSchema,
105
+ ownerDraft: onboardingDraftSchema,
106
+ })
107
+ .partial();
108
+ /**
109
+ * `GET /onboarding/status` response: completion truth (derived from the user's
110
+ * domain data) plus the resume cursor. `onboarded` is authoritative — an owner
111
+ * with an organisation, or a member with a profile, is onboarded regardless of
112
+ * the cursor.
113
+ */
114
+ export const onboardingStatusSchema = z.object({
115
+ onboarded: z.boolean(),
116
+ role: onboardingRoleSchema.nullable(),
117
+ organisationId: z.string().optional(),
118
+ welcomed: z.boolean(),
119
+ step: z.number().int().min(0),
120
+ gymCode: z.string().optional(),
121
+ memberDraft: onboardingDraftSchema.optional(),
122
+ ownerDraft: onboardingDraftSchema.optional(),
123
+ });
74
124
  //# sourceMappingURL=onboarding.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"onboarding.js","sourceRoot":"","sources":["../src/onboarding.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gymmonk-schema — Onboarding submit payloads\n * ============================================\n * The end-of-flow POST bodies. The frontend forms stay flat (three-part DOB,\n * etc.); at submit they map to these structured payloads, which compose the\n * canonical create schemas so the backend can fan out to the right collections.\n *\n * member → member-profile + a pending membership (join a gym by code)\n * owner → organisation + first center + SaaS subscription selection\n *\n * @module gymmonk-schema/onboarding\n */\n\nimport { z } from 'zod';\nimport { createCenterBodySchema, gymCodeSchema } from './center.js';\nimport {\n isoDateSchema,\n optionalEmailSchema,\n optionalTrimmedString,\n phoneSchema,\n} from './common.js';\nimport { bodyMetricsSchema, emergencyContactSchema } from './member-profile.js';\nimport { createOrganisationBodySchema } from './organisation.js';\nimport { addressSchema, bloodGroupSchema, genderSchema, socialLinksSchema } from './shared.js';\n\n// ─── Role ────────────────────────────────────────────────────────────────────\n\nexport const onboardingRoleSchema = z.enum(['owner', 'member']);\nexport type OnboardingRole = z.infer<typeof onboardingRoleSchema>;\n\n// ─── Member onboarding ───────────────────────────────────────────────────────\n\nexport const memberOnboardingBodySchema = z\n .object({\n /** The gym code the member picked at the \"find your gym\" step. */\n gymCode: gymCodeSchema,\n firstName: z.string().trim().min(1, 'First name is required').max(80),\n lastName: optionalTrimmedString(80),\n gender: genderSchema,\n dob: isoDateSchema,\n phone: phoneSchema,\n body: bodyMetricsSchema,\n emergencyContact: emergencyContactSchema,\n bloodGroup: bloodGroupSchema,\n })\n .refine((v) => v.body.heightCm != null && v.body.weightKg != null, {\n message: 'Height and weight are required',\n path: ['body'],\n })\n .refine((v) => v.body.neckCm != null && v.body.waistCm != null, {\n message: 'Neck and waist are required',\n path: ['body'],\n })\n .refine((v) => v.gender !== 'female' || v.body.hipsCm != null, {\n message: 'Hips measurement is required',\n path: ['body', 'hipsCm'],\n });\nexport type MemberOnboardingBody = z.infer<typeof memberOnboardingBodySchema>;\n\n// ─── Owner onboarding ────────────────────────────────────────────────────────\n\n/** The owner's personal basics captured before the organisation/center. */\nexport const ownerBasicsSchema = z.object({\n firstName: z.string().trim().min(1, 'First name is required').max(80),\n lastName: optionalTrimmedString(80),\n gender: genderSchema,\n dob: isoDateSchema.optional(),\n phone: phoneSchema,\n altEmail: optionalEmailSchema,\n website: optionalTrimmedString(200),\n socials: socialLinksSchema,\n /** The owner's personal contact address (step-2 of owner onboarding). */\n address: addressSchema.optional(),\n /** Uploaded profile photo → stored on the shared `User.avatar`. */\n avatarUrl: optionalTrimmedString(2000),\n});\nexport type OwnerBasics = z.infer<typeof ownerBasicsSchema>;\n\n/** The SaaS plan an owner selects at the final onboarding step. */\nexport const onboardingPlanSelectionSchema = z.object({\n planId: z.string().trim().min(1),\n tenureId: z.string().trim().min(1).nullable(),\n});\nexport type OnboardingPlanSelection = z.infer<typeof onboardingPlanSelectionSchema>;\n\nexport const ownerOnboardingBodySchema = z.object({\n owner: ownerBasicsSchema,\n organisation: createOrganisationBodySchema,\n center: createCenterBodySchema,\n subscription: onboardingPlanSelectionSchema,\n});\nexport type OwnerOnboardingBody = z.infer<typeof ownerOnboardingBodySchema>;\n"]}
1
+ {"version":3,"file":"onboarding.js","sourceRoot":"","sources":["../src/onboarding.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gymmonk-schema — Onboarding submit payloads\n * ============================================\n * The end-of-flow POST bodies. The frontend forms stay flat (three-part DOB,\n * etc.); at submit they map to these structured payloads, which compose the\n * canonical create schemas so the backend can fan out to the right collections.\n *\n * member → member-profile + a pending membership (join a gym by code)\n * owner → organisation + first center + SaaS subscription selection\n *\n * @module gymmonk-schema/onboarding\n */\n\nimport { z } from 'zod';\nimport { createCenterBodySchema, gymCodeSchema } from './center.js';\nimport {\n isoDateSchema,\n optionalEmailSchema,\n optionalTrimmedString,\n phoneSchema,\n} from './common.js';\nimport { bodyMetricsSchema, emergencyContactSchema } from './member-profile.js';\nimport { createOrganisationBodySchema } from './organisation.js';\nimport { addressSchema, bloodGroupSchema, genderSchema, socialLinksSchema } from './shared.js';\n\n// ─── Role ────────────────────────────────────────────────────────────────────\n\nexport const onboardingRoleSchema = z.enum(['owner', 'member']);\nexport type OnboardingRole = z.infer<typeof onboardingRoleSchema>;\n\n// ─── Member onboarding ───────────────────────────────────────────────────────\n\nexport const memberOnboardingBodySchema = z\n .object({\n /** The gym code the member picked at the \"find your gym\" step. */\n gymCode: gymCodeSchema,\n firstName: z.string().trim().min(1, 'First name is required').max(80),\n lastName: optionalTrimmedString(80),\n gender: genderSchema,\n dob: isoDateSchema,\n phone: phoneSchema,\n body: bodyMetricsSchema,\n emergencyContact: emergencyContactSchema,\n bloodGroup: bloodGroupSchema,\n })\n .refine((v) => v.body.heightCm != null && v.body.weightKg != null, {\n message: 'Height and weight are required',\n path: ['body'],\n })\n .refine((v) => v.body.neckCm != null && v.body.waistCm != null, {\n message: 'Neck and waist are required',\n path: ['body'],\n })\n .refine((v) => v.gender !== 'female' || v.body.hipsCm != null, {\n message: 'Hips measurement is required',\n path: ['body', 'hipsCm'],\n });\nexport type MemberOnboardingBody = z.infer<typeof memberOnboardingBodySchema>;\n\n// ─── Owner onboarding ────────────────────────────────────────────────────────\n\n/** The owner's personal basics captured before the organisation/center. */\nexport const ownerBasicsSchema = z.object({\n firstName: z.string().trim().min(1, 'First name is required').max(80),\n lastName: optionalTrimmedString(80),\n gender: genderSchema,\n dob: isoDateSchema.optional(),\n phone: phoneSchema,\n altEmail: optionalEmailSchema,\n website: optionalTrimmedString(200),\n socials: socialLinksSchema,\n /** The owner's personal contact address (step-2 of owner onboarding). */\n address: addressSchema.optional(),\n /** Uploaded profile photo → stored on the shared `User.avatar`. */\n avatarUrl: optionalTrimmedString(2000),\n});\nexport type OwnerBasics = z.infer<typeof ownerBasicsSchema>;\n\n/** The SaaS plan an owner selects at the final onboarding step. */\nexport const onboardingPlanSelectionSchema = z.object({\n planId: z.string().trim().min(1),\n tenureId: z.string().trim().min(1).nullable(),\n});\nexport type OnboardingPlanSelection = z.infer<typeof onboardingPlanSelectionSchema>;\n\nexport const ownerOnboardingBodySchema = z.object({\n owner: ownerBasicsSchema,\n organisation: createOrganisationBodySchema,\n center: createCenterBodySchema,\n subscription: onboardingPlanSelectionSchema,\n});\nexport type OwnerOnboardingBody = z.infer<typeof ownerOnboardingBodySchema>;\n\n// ─── Onboarding progress & resume ─────────────────────────────────────────────\n\n/**\n * In-progress onboarding draft: the UI-shaped, partially-filled form the client\n * persists so a returning user resumes with their typed data intact. Opaque to\n * the backend by design — the client owns the form shape, and the data is\n * re-validated strictly by the member/owner body schemas at finalize. Keys are\n * form field names; the backend caps the stored size.\n */\nexport const onboardingDraftSchema = z.record(z.string(), z.unknown());\nexport type OnboardingDraft = z.infer<typeof onboardingDraftSchema>;\n\n/**\n * The server-persisted onboarding cursor: which role the user picked, how far\n * they reached (`step`, the 1-indexed sub-step of the current form), the gym a\n * member chose, and the drafts. Read on app entry to resume to the exact step.\n * `welcomed` records that the one-time welcome carousel has been seen.\n */\nexport const onboardingProgressSchema = z.object({\n welcomed: z.boolean(),\n role: onboardingRoleSchema.nullable(),\n step: z.number().int().min(0).max(20),\n gymCode: gymCodeSchema.optional(),\n memberDraft: onboardingDraftSchema.optional(),\n ownerDraft: onboardingDraftSchema.optional(),\n});\nexport type OnboardingProgress = z.infer<typeof onboardingProgressSchema>;\n\n/** `PUT /onboarding/progress` body — any subset of the cursor, merged server-side. */\nexport const onboardingProgressUpdateSchema = z\n .object({\n welcomed: z.boolean(),\n role: onboardingRoleSchema.nullable(),\n step: z.number().int().min(0).max(20),\n gymCode: gymCodeSchema,\n memberDraft: onboardingDraftSchema,\n ownerDraft: onboardingDraftSchema,\n })\n .partial();\nexport type OnboardingProgressUpdate = z.infer<typeof onboardingProgressUpdateSchema>;\n\n/**\n * `GET /onboarding/status` response: completion truth (derived from the user's\n * domain data) plus the resume cursor. `onboarded` is authoritative — an owner\n * with an organisation, or a member with a profile, is onboarded regardless of\n * the cursor.\n */\nexport const onboardingStatusSchema = z.object({\n onboarded: z.boolean(),\n role: onboardingRoleSchema.nullable(),\n organisationId: z.string().optional(),\n welcomed: z.boolean(),\n step: z.number().int().min(0),\n gymCode: z.string().optional(),\n memberDraft: onboardingDraftSchema.optional(),\n ownerDraft: onboardingDraftSchema.optional(),\n});\nexport type OnboardingStatus = z.infer<typeof onboardingStatusSchema>;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gymmonk-schema",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
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",