founder-schema 0.17.0 → 0.18.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 +125 -17
- package/dist/access/access.d.ts +1 -1
- package/dist/access/access.d.ts.map +1 -1
- package/dist/access/access.js +4 -1
- package/dist/access/access.js.map +1 -1
- package/dist/access/permissions.d.ts +27 -29
- package/dist/access/permissions.d.ts.map +1 -1
- package/dist/access/permissions.js +39 -3
- package/dist/access/permissions.js.map +1 -1
- package/dist/core/common.d.ts +47 -10
- package/dist/core/common.d.ts.map +1 -1
- package/dist/core/common.js +42 -13
- package/dist/core/common.js.map +1 -1
- package/dist/core/errors.d.ts +7 -5
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +17 -11
- package/dist/core/errors.js.map +1 -1
- package/dist/core/limits.d.ts +48 -17
- package/dist/core/limits.d.ts.map +1 -1
- package/dist/core/limits.js +52 -22
- package/dist/core/limits.js.map +1 -1
- package/dist/core/money.d.ts +33 -0
- package/dist/core/money.d.ts.map +1 -0
- package/dist/core/money.js +36 -0
- package/dist/core/money.js.map +1 -0
- package/dist/core/responses.d.ts +14 -4
- package/dist/core/responses.d.ts.map +1 -1
- package/dist/core/responses.js.map +1 -1
- package/dist/core/validation.d.ts +6 -2
- package/dist/core/validation.d.ts.map +1 -1
- package/dist/core/validation.js +6 -2
- package/dist/core/validation.js.map +1 -1
- package/dist/domains/billing/billing.d.ts +61 -8
- package/dist/domains/billing/billing.d.ts.map +1 -1
- package/dist/domains/billing/billing.js +58 -22
- package/dist/domains/billing/billing.js.map +1 -1
- package/dist/domains/billing/gst.d.ts.map +1 -1
- package/dist/domains/billing/gst.js +4 -3
- package/dist/domains/billing/gst.js.map +1 -1
- package/dist/domains/chat/chat.d.ts +0 -21
- package/dist/domains/chat/chat.d.ts.map +1 -1
- package/dist/domains/chat/chat.js +0 -20
- package/dist/domains/chat/chat.js.map +1 -1
- package/dist/domains/communities/communities.d.ts +30 -12
- package/dist/domains/communities/communities.d.ts.map +1 -1
- package/dist/domains/communities/communities.js +43 -15
- package/dist/domains/communities/communities.js.map +1 -1
- package/dist/domains/contributions/contributions.d.ts +1 -1
- package/dist/domains/contributions/contributions.js +1 -1
- package/dist/domains/contributions/contributions.js.map +1 -1
- package/dist/domains/dashboard/dashboard.d.ts +3 -1
- package/dist/domains/dashboard/dashboard.d.ts.map +1 -1
- package/dist/domains/dashboard/dashboard.js +23 -7
- package/dist/domains/dashboard/dashboard.js.map +1 -1
- package/dist/domains/discover/discover.d.ts +8 -8
- package/dist/domains/feed/feed.d.ts +55 -153
- package/dist/domains/feed/feed.d.ts.map +1 -1
- package/dist/domains/feed/feed.js +53 -58
- package/dist/domains/feed/feed.js.map +1 -1
- package/dist/domains/location/india.d.ts +14 -4
- package/dist/domains/location/india.d.ts.map +1 -1
- package/dist/domains/location/india.js +36 -8
- package/dist/domains/location/india.js.map +1 -1
- package/dist/domains/members/members.d.ts +2 -2
- package/dist/domains/membership/membership.d.ts +26 -0
- package/dist/domains/membership/membership.d.ts.map +1 -1
- package/dist/domains/membership/membership.js +55 -1
- package/dist/domains/membership/membership.js.map +1 -1
- package/dist/domains/moderation/moderation.d.ts +2 -2
- package/dist/domains/moderation/moderation.js +3 -3
- package/dist/domains/moderation/moderation.js.map +1 -1
- package/dist/domains/notifications/notifications.d.ts +1 -10
- package/dist/domains/notifications/notifications.d.ts.map +1 -1
- package/dist/domains/notifications/notifications.js +2 -3
- package/dist/domains/notifications/notifications.js.map +1 -1
- package/dist/domains/onboarding/onboarding-answers.d.ts +33 -2
- package/dist/domains/onboarding/onboarding-answers.d.ts.map +1 -1
- package/dist/domains/onboarding/onboarding-answers.js +38 -15
- package/dist/domains/onboarding/onboarding-answers.js.map +1 -1
- package/dist/domains/onboarding/onboarding.d.ts +229 -57
- package/dist/domains/onboarding/onboarding.d.ts.map +1 -1
- package/dist/domains/onboarding/onboarding.js +34 -41
- package/dist/domains/onboarding/onboarding.js.map +1 -1
- package/dist/domains/products/products.d.ts +43 -8
- package/dist/domains/products/products.d.ts.map +1 -1
- package/dist/domains/products/products.js +57 -11
- package/dist/domains/products/products.js.map +1 -1
- package/dist/domains/profile/founder-profile.d.ts +68 -104
- package/dist/domains/profile/founder-profile.d.ts.map +1 -1
- package/dist/domains/profile/founder-profile.js +66 -32
- package/dist/domains/profile/founder-profile.js.map +1 -1
- package/dist/domains/profile/my-profile.d.ts +3 -3
- package/dist/domains/reviews/reviews.d.ts +1 -1
- package/dist/domains/reviews/reviews.d.ts.map +1 -1
- package/dist/domains/reviews/reviews.js +2 -2
- package/dist/domains/reviews/reviews.js.map +1 -1
- package/dist/domains/support/support.d.ts +22 -3
- package/dist/domains/support/support.d.ts.map +1 -1
- package/dist/domains/support/support.js +19 -5
- package/dist/domains/support/support.js.map +1 -1
- package/dist/domains/team/team.d.ts.map +1 -1
- package/dist/domains/team/team.js +3 -6
- package/dist/domains/team/team.js.map +1 -1
- package/dist/domains/uploads/uploads.d.ts +7 -22
- package/dist/domains/uploads/uploads.d.ts.map +1 -1
- package/dist/domains/uploads/uploads.js +7 -21
- package/dist/domains/uploads/uploads.js.map +1 -1
- package/dist/env.d.ts +26 -13
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +58 -23
- package/dist/env.js.map +1 -1
- package/dist/index.d.ts +18 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @module founder-schema/domains/profile
|
|
8
8
|
*/
|
|
9
9
|
import { z } from 'zod';
|
|
10
|
-
import { chosenUsernameSchema, displayNameSchema, GOAL_VALUES, genderSchema,
|
|
10
|
+
import { chosenUsernameSchema, displayNameSchema, GOAL_VALUES, genderSchema, goalSchema, httpUrlSchema, industrySchema, locationSchema, objectIdSchema, optionalTrimmedString, phoneSchema, requiredGoalListSchema, skillListSchema, skillSchema, startupStageSchema, usernameSchema, } from '../../core/common.js';
|
|
11
11
|
import { ABOUT_MAX, DISPLAY_NAME_MAX, FOUNDED_YEAR_MIN, HEADLINE_MAX, SKILLS_MAX, TEAM_SIZE_MAX, TEAM_SIZE_MIN, VENTURE_NAME_MAX, VENTURE_TAGLINE_MAX, } from '../../core/limits.js';
|
|
12
12
|
import { VALIDATION_MESSAGE } from '../../core/validation.js';
|
|
13
13
|
import { indiaLocationSchema } from '../location/india.js';
|
|
@@ -199,6 +199,29 @@ export const founderCardSchema = founderProfileSchema.pick({
|
|
|
199
199
|
goals: true,
|
|
200
200
|
contribution: true,
|
|
201
201
|
});
|
|
202
|
+
/** The fields a Discover card shows. */
|
|
203
|
+
export const FOUNDER_CARD_FIELDS = founderCardSchema.keyof().options;
|
|
204
|
+
/**
|
|
205
|
+
* The card fields a member's own edit can change, the ones that move them up
|
|
206
|
+
* Discover: every card field but the ids, which never change, and the points
|
|
207
|
+
* (`contribution`), which other people's acts move all day and which would
|
|
208
|
+
* otherwise reorder Discover with every helpful mark.
|
|
209
|
+
*/
|
|
210
|
+
export const FOUNDER_CARD_EDIT_FIELDS = FOUNDER_CARD_FIELDS.filter((field) => field !== 'id' && field !== 'userId' && field !== 'contribution');
|
|
211
|
+
/**
|
|
212
|
+
* Whether a write changed what a member's Discover card shows (0.18.0): ONE
|
|
213
|
+
* rule for every profile writer, which decides whether the member moves up
|
|
214
|
+
* Discover (its "recently updated" order) and whether a client reloads it.
|
|
215
|
+
* `changed` names the fields the write actually changed: a body's keys, or
|
|
216
|
+
* for the business editor `venture` only when its PUBLIC part changed. Before
|
|
217
|
+
* 0.18.0 five writers decided it four ways: an edit to the About text alone
|
|
218
|
+
* lifted a member to the top of everyone's Discover, a new username never did.
|
|
219
|
+
* Points never count (`FOUNDER_CARD_EDIT_FIELDS`): they are not the member's
|
|
220
|
+
* edit.
|
|
221
|
+
*/
|
|
222
|
+
export function changesDiscoverCard(changed) {
|
|
223
|
+
return changed.some((field) => FOUNDER_CARD_EDIT_FIELDS.includes(field));
|
|
224
|
+
}
|
|
202
225
|
// ─── What a member chooses to show (0.11.0) ──────────────────────────────────
|
|
203
226
|
/**
|
|
204
227
|
* The four personal details a member MAY show on their profile page (the
|
|
@@ -207,13 +230,22 @@ export const founderCardSchema = founderProfileSchema.pick({
|
|
|
207
230
|
* `PATCH /profiles/me/personal`.
|
|
208
231
|
*/
|
|
209
232
|
export const CONTACT_VISIBILITY_FIELDS = ['email', 'phone', 'gender', 'birthday'];
|
|
233
|
+
/**
|
|
234
|
+
* WHO sees each of the four once its owner turns it on (0.18.0). The user,
|
|
235
|
+
* 2026-10-20: "any person who has the platform membership can view them if
|
|
236
|
+
* they are public", so all four go to members only (and to the member
|
|
237
|
+
* themself), never to an account without membership. One table: the server
|
|
238
|
+
* builds `shown` from it, and every screen words its switch from it.
|
|
239
|
+
*/
|
|
240
|
+
export const PROFILE_AUDIENCE_VALUES = ['members'];
|
|
241
|
+
export const CONTACT_VISIBILITY_AUDIENCE = {
|
|
242
|
+
email: 'members',
|
|
243
|
+
phone: 'members',
|
|
244
|
+
gender: 'members',
|
|
245
|
+
birthday: 'members',
|
|
246
|
+
};
|
|
210
247
|
/** Which of the four the member shows. Every key present on a read: false is private. */
|
|
211
|
-
export const contactVisibilitySchema = z.object(
|
|
212
|
-
email: z.boolean(),
|
|
213
|
-
phone: z.boolean(),
|
|
214
|
-
gender: z.boolean(),
|
|
215
|
-
birthday: z.boolean(),
|
|
216
|
-
});
|
|
248
|
+
export const contactVisibilitySchema = z.object(Object.fromEntries(CONTACT_VISIBILITY_FIELDS.map((field) => [field, z.boolean()])));
|
|
217
249
|
/**
|
|
218
250
|
* A birthday as OTHERS see it: day and month, NEVER the year (the user's
|
|
219
251
|
* ruling). The year would give away the age and, with the name, the full date
|
|
@@ -229,9 +261,9 @@ export const publicBirthdaySchema = z.object({
|
|
|
229
261
|
*
|
|
230
262
|
* The card's fields, what only a page has room for (about, links, member
|
|
231
263
|
* since), and `shown`: the personal details this member has CHOSEN to show,
|
|
232
|
-
* and nothing else
|
|
233
|
-
*
|
|
234
|
-
*
|
|
264
|
+
* and nothing else, to the audience `CONTACT_VISIBILITY_AUDIENCE` names
|
|
265
|
+
* (members only, all four, 0.18.0); the server decides, and a client renders
|
|
266
|
+
* what arrives. Built field by field on the server
|
|
235
267
|
* (`toMemberPublic`), never by dropping fields from a fuller document.
|
|
236
268
|
*
|
|
237
269
|
* `viewer.canMessage` is the server's answer to "offer Message": the tap
|
|
@@ -296,30 +328,31 @@ const clearableText = (maxLength) => z.string().trim().min(1, VALIDATION_MESSAGE
|
|
|
296
328
|
*
|
|
297
329
|
* The rule this comes from: defaults belong on the READ side. A write body
|
|
298
330
|
* states what the caller sent and nothing else.
|
|
331
|
+
*
|
|
332
|
+
* ONE WRITER PER FIELD (0.18.0): the name and the city are the personal
|
|
333
|
+
* details' (`PATCH /profiles/me/personal`), the venture is the business
|
|
334
|
+
* editor's (`PUT /profiles/me/business`), the handle has its own route. They
|
|
335
|
+
* were taken here too, a second path with its own rules for Discover's order.
|
|
336
|
+
* STRICT: any other key is refused (a 400 naming it), never dropped without a
|
|
337
|
+
* word, so a client still sending a name here learns it at once instead of
|
|
338
|
+
* losing the member's edit.
|
|
299
339
|
*/
|
|
300
|
-
export const updateFounderProfileBodySchema = z
|
|
301
|
-
|
|
340
|
+
export const updateFounderProfileBodySchema = z
|
|
341
|
+
.object({
|
|
302
342
|
headline: clearableText(HEADLINE_MAX),
|
|
303
343
|
about: clearableText(ABOUT_MAX),
|
|
304
|
-
/**
|
|
305
|
-
* Carries the whole location; it has no default to fill a gap. `null`
|
|
306
|
-
* removes it (0.10.0): a member may stop showing their city on Discover, and
|
|
307
|
-
* nothing else would take it off once given. Written in India, one of its
|
|
308
|
-
* states (0.17.0, `indiaLocationSchema`); a stored location is still READ by
|
|
309
|
-
* `locationSchema`.
|
|
310
|
-
*/
|
|
311
|
-
location: indiaLocationSchema.nullable().optional(),
|
|
312
|
-
/**
|
|
313
|
-
* The whole venture, or `null` to remove it (0.10.0), its business type
|
|
314
|
-
* with it: a member whose venture closed is no longer building it.
|
|
315
|
-
*/
|
|
316
|
-
venture: ventureSchema.nullable().optional(),
|
|
317
344
|
/** The same list rules as onboarding's finish (each value once, 0.17.0). */
|
|
318
345
|
skills: skillListSchema.optional(),
|
|
319
|
-
|
|
346
|
+
/**
|
|
347
|
+
* The same rules as onboarding's finish, its minimum included (0.18.0, the
|
|
348
|
+
* user: "we already have at least one goal required"): Me cannot save a
|
|
349
|
+
* member down to no goals.
|
|
350
|
+
*/
|
|
351
|
+
goals: requiredGoalListSchema.optional(),
|
|
320
352
|
/** Sent whole. To drop one link, send the object without that key. */
|
|
321
353
|
links: founderLinksSchema.optional(),
|
|
322
|
-
})
|
|
354
|
+
})
|
|
355
|
+
.strict();
|
|
323
356
|
/**
|
|
324
357
|
* Username change. Separate from the profile patch because it needs a
|
|
325
358
|
* uniqueness check, and it refuses a reserved handle (`chosenUsernameSchema`).
|
|
@@ -327,12 +360,13 @@ export const updateFounderProfileBodySchema = z.object({
|
|
|
327
360
|
export const updateUsernameBodySchema = z.object({ username: chosenUsernameSchema });
|
|
328
361
|
/** `GET /api/v1/profiles/username-available?username=` (0.13.0): the handle asked about. */
|
|
329
362
|
export const usernameAvailabilityQuerySchema = z.object({ username: usernameSchema });
|
|
330
|
-
/**
|
|
363
|
+
/** Why a username is not available. */
|
|
364
|
+
export const USERNAME_UNAVAILABLE_REASON_VALUES = ['taken', 'reserved'];
|
|
331
365
|
export const usernameAvailabilitySchema = z.object({
|
|
332
366
|
username: usernameSchema,
|
|
333
367
|
available: z.boolean(),
|
|
334
|
-
/** Present when unavailable
|
|
335
|
-
reason: z.enum(
|
|
368
|
+
/** Present when unavailable. */
|
|
369
|
+
reason: z.enum(USERNAME_UNAVAILABLE_REASON_VALUES).optional(),
|
|
336
370
|
});
|
|
337
371
|
// ─── The member's own personal details ──────────────────────────────────────
|
|
338
372
|
/**
|
|
@@ -391,8 +425,8 @@ export const updateMyPersonalDetailsBodySchema = z.object({
|
|
|
391
425
|
/**
|
|
392
426
|
* The city, or `null` to remove it (0.11.0): it moved to the Personal
|
|
393
427
|
* Details card, so its editor saves it here, with the rest, in one
|
|
394
|
-
* transaction. `PATCH /profiles/me`
|
|
395
|
-
* one of its states (0.17.0, `indiaLocationSchema`).
|
|
428
|
+
* transaction; only here since 0.18.0 (`PATCH /profiles/me` took it too).
|
|
429
|
+
* Written in India, one of its states (0.17.0, `indiaLocationSchema`).
|
|
396
430
|
*/
|
|
397
431
|
location: indiaLocationSchema.nullable().optional(),
|
|
398
432
|
/** Only the switches that changed (0.11.0). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"founder-profile.js","sourceRoot":"","sources":["../../../src/domains/profile/founder-profile.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema — The founder profile\n *\n * One member, one profile. The central auth account owns identity (email,\n * verification, deletion); this owns everything Founder Circle shows.\n *\n * @module founder-schema/domains/profile\n */\n\nimport { z } from 'zod';\nimport {\n chosenUsernameSchema,\n displayNameSchema,\n GOAL_VALUES,\n genderSchema,\n goalListSchema,\n goalSchema,\n httpUrlSchema,\n industrySchema,\n locationSchema,\n objectIdSchema,\n optionalTrimmedString,\n phoneSchema,\n skillListSchema,\n skillSchema,\n startupStageSchema,\n usernameSchema,\n} from '../../core/common.js';\nimport {\n ABOUT_MAX,\n DISPLAY_NAME_MAX,\n FOUNDED_YEAR_MIN,\n HEADLINE_MAX,\n SKILLS_MAX,\n TEAM_SIZE_MAX,\n TEAM_SIZE_MIN,\n VENTURE_NAME_MAX,\n VENTURE_TAGLINE_MAX,\n} from '../../core/limits.js';\nimport { VALIDATION_MESSAGE } from '../../core/validation.js';\nimport { indiaLocationSchema } from '../location/india.js';\nimport { mediaImageSchema } from '../media/media.js';\n\n// ─── Venture ─────────────────────────────────────────────────────────────────\n\n/**\n * ⚠ NUMBERS ARE NUMBERS, never coerced. This is a BODY, which arrives as JSON\n * and already carries types. `z.coerce.number()` here accepted `teamSize: true`\n * as 1 and `\" \"` as 0, and made the schema's input type `unknown`, so a form\n * typed against it could put anything in the field. Coercion belongs to query\n * strings, where everything is text; a form converts its text input before it\n * builds the body.\n */\nexport const ventureSchema = z.object({\n name: z.string().trim().min(1).max(VENTURE_NAME_MAX),\n tagline: optionalTrimmedString(VENTURE_TAGLINE_MAX),\n website: httpUrlSchema.optional(),\n industry: industrySchema,\n stage: startupStageSchema,\n /** Team size including the founder. Optional: a solo pre-idea founder has none to report. */\n teamSize: z.number().int().min(TEAM_SIZE_MIN).max(TEAM_SIZE_MAX).optional(),\n /**\n * Not in the future. The bound is read per parse, never captured once.\n *\n * `.max(new Date().getUTCFullYear())` looks equivalent and is not: it is\n * evaluated when this module is imported, so a server process still up on\n * 1 January rejects the new year until somebody restarts it. The backend's\n * Mongoose model deliberately carries no upper bound for the same reason, a\n * `max` there being frozen at schema construction. This is where the ceiling\n * lives.\n */\n foundedYear: z\n .number()\n .int()\n .min(FOUNDED_YEAR_MIN)\n .refine((year) => year <= new Date().getUTCFullYear(), {\n error: VALIDATION_MESSAGE.foundedYearFuture,\n })\n .optional(),\n});\nexport type Venture = z.infer<typeof ventureSchema>;\n\n/**\n * A venture's fields, in order, and which of them may be left out (0.17.0):\n * what the backend lists when it saves or compares a venture, read from here.\n */\nexport const VENTURE_KEYS = ventureSchema.keyof().options;\nexport type VentureKey = (typeof VENTURE_KEYS)[number];\nexport const VENTURE_OPTIONAL_KEYS: readonly VentureKey[] = VENTURE_KEYS.filter(\n (key) => ventureSchema.shape[key].safeParse(undefined).success,\n);\n\n// ─── A member as other surfaces name them ────────────────────────────────────\n\n/**\n * A member as a byline renders them: on a post (`feedAuthorSchema` is this), a\n * product's team, a review, a notification, the top-helpers list.\n *\n * Every field is LIVE, joined against the profile at read time rather than\n * frozen onto the post or review at write time. So a renamed handle, a new\n * photo or an edited headline updates everywhere, the link always targets the\n * current username, and nothing personal survives in other people's rows once\n * an account is deleted.\n */\nexport const memberBylineSchema = z.object({\n userId: z.string(),\n username: usernameSchema,\n displayName: z.string(),\n /** Server-generated or from the login provider, never member-typed, so a\n * plain URL check. Same reasoning as `avatarUrl` on the profile. */\n avatarUrl: z.url().optional(),\n /** The member's one-line headline, live-joined. */\n headline: z.string().optional(),\n});\nexport type MemberByline = z.infer<typeof memberBylineSchema>;\n\n/**\n * What a member has given the community, on their profile and card.\n *\n * `score` is points earned from OTHER members' recognition only: a product's\n * team marking your feedback helpful, confirming you tested their product,\n * and members finding your review helpful (writing one earns nothing by\n * itself). Nothing a member\n * does to their own product counts, so the score cannot be bought by having a\n * popular one. `helpedCount` is how many different founders recognised that\n * help (\"Helped 12 founders\"). See `domains/contributions`.\n */\nexport const contributionSummarySchema = z.object({\n score: z.number().int().nonnegative(),\n helpedCount: z.number().int().nonnegative(),\n});\nexport type ContributionSummary = z.infer<typeof contributionSummarySchema>;\n\n// ─── Profile ─────────────────────────────────────────────────────────────────\n\n/**\n * The founder's off-platform links. ONE shape: onboarding's contact step picks\n * its three from this, so a link one side knows can never be missing on the\n * other. (It was written twice until 0.2.0, and the profile side lacked\n * Instagram, which onboarding collected.)\n *\n * Defined without a default, because the read side wants one and the write\n * side must not have one; each adds its own.\n */\nexport const founderLinksSchema = z.object({\n linkedin: httpUrlSchema.optional(),\n x: httpUrlSchema.optional(),\n instagram: httpUrlSchema.optional(),\n github: httpUrlSchema.optional(),\n website: httpUrlSchema.optional(),\n});\nexport type FounderLinks = z.infer<typeof founderLinksSchema>;\n\n/**\n * The profile's links, by key, in order (0.17.0): what the backend's model and\n * its projections list. Read from here, never written out again: a copy of\n * this list is how the profile model once lost Instagram.\n */\nexport const FOUNDER_LINK_KEYS = founderLinksSchema.keyof().options;\nexport type FounderLinkKey = (typeof FOUNDER_LINK_KEYS)[number];\n\n/**\n * The profile as the owner sees it. A peer sees the redacted projection\n * (`founderCardSchema`), which is built on the server and never by dropping\n * fields on the client.\n */\nexport const founderProfileSchema = z.object({\n id: z.string(),\n userId: z.string(),\n username: usernameSchema,\n displayName: z.string().trim().min(1).max(DISPLAY_NAME_MAX),\n headline: optionalTrimmedString(HEADLINE_MAX),\n about: optionalTrimmedString(ABOUT_MAX),\n /**\n * Plain `z.url()`, NOT `httpUrlSchema`, and the difference is deliberate.\n *\n * No member can write this field: it appears in no write body, and\n * `public-surface.test.ts` asserts that and will fail the day somebody adds it\n * to a form. So the scheme restriction would be defending a field that takes\n * no member input, while refusing hosts we legitimately serve avatars from,\n * our own dev host among them. A read schema that rejects our own response is\n * a self-inflicted outage.\n *\n * ⚠️ That argument holds only while nothing member-supplied reaches this\n * field. Two sources write an avatar today and NEITHER is a member typing a\n * URL: our own upload path, and the central-auth `user.created` /\n * `user.updated` webhook, which carries the Google OAuth picture. If a\n * \"paste a link to your photo\" control is ever added, this goes back to\n * `httpUrlSchema` and the ruling behind it is void.\n *\n * WHICH picture: the member's uploaded photo at its small size (192, see\n * `MEDIA_SIZES.avatar`), else their sign-in provider's picture, else absent.\n * Every surface that shows this member resolves it the same way.\n */\n avatarUrl: z.url().optional(),\n /**\n * The uploaded photo itself, every size, when the member has one. Absent\n * means `avatarUrl` (if any) is the sign-in provider's picture, so a client\n * offers \"Remove photo\" only when this is present. Not on `founderCardSchema`:\n * a card needs one small picture, not the set.\n */\n avatar: mediaImageSchema.optional(),\n location: locationSchema.optional(),\n venture: ventureSchema.optional(),\n skills: z.array(skillSchema).max(SKILLS_MAX).default([]),\n goals: z.array(goalSchema).max(GOAL_VALUES.length).default([]),\n links: founderLinksSchema.default({}),\n /**\n * Their contribution to other founders. Server-computed, never written by a\n * member. REQUIRED, no default: a read-side default would let a server that\n * forgot the field pass as a real zero.\n */\n contribution: contributionSummarySchema,\n /** Onboarding is finished and the member is visible in Discover. */\n isOnboarded: z.boolean().default(false),\n /** Member paused their own profile. Hidden from Discover, keeps their data. */\n isActive: z.boolean().default(true),\n createdAt: z.iso.datetime(),\n updatedAt: z.iso.datetime(),\n});\nexport type FounderProfile = z.infer<typeof founderProfileSchema>;\n\n/**\n * What one founder sees of another in Discover and on a profile page.\n *\n * Built server-side. Keep it a strict subset of `founderProfileSchema` — if a\n * private field ever needs to show here, that is a product decision, not an\n * accident of spreading the full document.\n */\n// ⚠ The web's Discover cache follows the member's own saves of exactly these\n// fields (`discoverChangedBy` in the web's features/profile/api.ts derives its\n// list from this shape): adding a field here is enough, there is no second list.\nexport const founderCardSchema = founderProfileSchema.pick({\n id: true,\n /**\n * The MEMBER's id (0.6.0): what a team invitation, a chat and every byline\n * key on. `id` is the profile's own id, which nothing takes as input.\n */\n userId: true,\n username: true,\n displayName: true,\n headline: true,\n avatarUrl: true,\n location: true,\n venture: true,\n skills: true,\n goals: true,\n contribution: true,\n});\nexport type FounderCard = z.infer<typeof founderCardSchema>;\n\n// ─── What a member chooses to show (0.11.0) ──────────────────────────────────\n\n/**\n * The four personal details a member MAY show on their profile page (the\n * user's ruling, 2026-10-11). Each is PRIVATE until its owner turns it on;\n * everything else on a profile is public as before. Changed only through\n * `PATCH /profiles/me/personal`.\n */\nexport const CONTACT_VISIBILITY_FIELDS = ['email', 'phone', 'gender', 'birthday'] as const;\nexport type ContactVisibilityField = (typeof CONTACT_VISIBILITY_FIELDS)[number];\n\n/** Which of the four the member shows. Every key present on a read: false is private. */\nexport const contactVisibilitySchema = z.object({\n email: z.boolean(),\n phone: z.boolean(),\n gender: z.boolean(),\n birthday: z.boolean(),\n});\nexport type ContactVisibility = z.infer<typeof contactVisibilitySchema>;\n\n/**\n * A birthday as OTHERS see it: day and month, NEVER the year (the user's\n * ruling). The year would give away the age and, with the name, the full date\n * of birth that identity checks ask for. The year is never on the wire.\n */\nexport const publicBirthdaySchema = z.object({\n day: z.number().int().min(1).max(31),\n month: z.number().int().min(1).max(12),\n});\nexport type PublicBirthday = z.infer<typeof publicBirthdaySchema>;\n\n/**\n * `GET /api/v1/founders/by-username/:username` (0.11.0): one member's profile\n * page, as a signed-in member (or the member themself) sees it.\n *\n * The card's fields, what only a page has room for (about, links, member\n * since), and `shown`: the personal details this member has CHOSEN to show,\n * and nothing else. Email and phone go to members only, never to an account\n * without membership (the coming viewer tier); the server decides, and a\n * client renders what arrives. Built field by field on the server\n * (`toMemberPublic`), never by dropping fields from a fuller document.\n *\n * `viewer.canMessage` is the server's answer to \"offer Message\": the tap\n * opens a conversation, or (0.13.0) the sheet that makes one possible (the\n * membership for a viewer, the plans for a member without one). Who may\n * message whom follows membership and plans, so no client works it out.\n */\nexport const memberProfileSchema = founderProfileSchema\n .pick({\n id: true,\n userId: true,\n username: true,\n displayName: true,\n headline: true,\n about: true,\n avatarUrl: true,\n location: true,\n venture: true,\n skills: true,\n goals: true,\n links: true,\n contribution: true,\n createdAt: true,\n })\n .extend({\n shown: z.object({\n email: z.string().optional(),\n phone: z.string().optional(),\n gender: genderSchema.optional(),\n birthday: publicBirthdaySchema.optional(),\n }),\n viewer: z.object({ isSelf: z.boolean(), canMessage: z.boolean() }),\n });\nexport type MemberProfile = z.infer<typeof memberProfileSchema>;\n\n/** The member page's address: `/discover/<username>`. */\nexport const memberHandleParamsSchema = z.object({ username: usernameSchema });\nexport type MemberHandleParams = z.infer<typeof memberHandleParamsSchema>;\n\n// ─── Write bodies ────────────────────────────────────────────────────────────\n\n/**\n * A text field the member is allowed to empty.\n *\n * Three states, and they are deliberately distinct:\n * absent do not touch this field\n * null clear it (the backend turns this into an `$unset`)\n * \"\" REJECTED\n *\n * The empty string is refused on purpose. A form that forgets to convert a\n * cleared input to `null` then fails loudly at the boundary, instead of being\n * silently dropped and leaving the member staring at text they just deleted.\n * `optionalTrimmedString` does the dropping, which is right for a create body\n * and wrong for a patch.\n */\nconst clearableText = (maxLength: number) =>\n z.string().trim().min(1, VALIDATION_MESSAGE.clearWithNull).max(maxLength).nullable().optional();\n\n/**\n * Patch body for the Me tab. Only the keys the member actually changed.\n *\n * ⚠️ Written as its own object ON PURPOSE. Do NOT rebuild it as\n * `founderProfileSchema.pick({...}).partial()`: `.partial()` does not switch\n * off `.default()`, so `skills`, `goals` and `links` would materialise as\n * `[]`, `[]` and `{}` on every request, including an empty one. Under a `$set`\n * that wipes a member's skills and every one of their links the moment they\n * edit their display name. `tsc` cannot see it and the request looks correct in\n * the network tab.\n *\n * The rule this comes from: defaults belong on the READ side. A write body\n * states what the caller sent and nothing else.\n */\nexport const updateFounderProfileBodySchema = z.object({\n displayName: displayNameSchema.optional(),\n headline: clearableText(HEADLINE_MAX),\n about: clearableText(ABOUT_MAX),\n /**\n * Carries the whole location; it has no default to fill a gap. `null`\n * removes it (0.10.0): a member may stop showing their city on Discover, and\n * nothing else would take it off once given. Written in India, one of its\n * states (0.17.0, `indiaLocationSchema`); a stored location is still READ by\n * `locationSchema`.\n */\n location: indiaLocationSchema.nullable().optional(),\n /**\n * The whole venture, or `null` to remove it (0.10.0), its business type\n * with it: a member whose venture closed is no longer building it.\n */\n venture: ventureSchema.nullable().optional(),\n /** The same list rules as onboarding's finish (each value once, 0.17.0). */\n skills: skillListSchema.optional(),\n goals: goalListSchema.optional(),\n /** Sent whole. To drop one link, send the object without that key. */\n links: founderLinksSchema.optional(),\n});\nexport type UpdateFounderProfileBody = z.infer<typeof updateFounderProfileBodySchema>;\n\n/**\n * Username change. Separate from the profile patch because it needs a\n * uniqueness check, and it refuses a reserved handle (`chosenUsernameSchema`).\n */\nexport const updateUsernameBodySchema = z.object({ username: chosenUsernameSchema });\nexport type UpdateUsernameBody = z.infer<typeof updateUsernameBodySchema>;\n\n/** `GET /api/v1/profiles/username-available?username=` (0.13.0): the handle asked about. */\nexport const usernameAvailabilityQuerySchema = z.object({ username: usernameSchema });\nexport type UsernameAvailabilityQuery = z.infer<typeof usernameAvailabilityQuerySchema>;\n\n/** Availability probe behind the username field. */\nexport const usernameAvailabilitySchema = z.object({\n username: usernameSchema,\n available: z.boolean(),\n /** Present when unavailable: `taken` or `reserved`. */\n reason: z.enum(['taken', 'reserved']).optional(),\n});\nexport type UsernameAvailability = z.infer<typeof usernameAvailabilitySchema>;\n\n// ─── The member's own personal details ──────────────────────────────────────\n\n/**\n * `GET /api/v1/profiles/me/personal` (0.10.0): what ONLY the member sees of\n * themselves, the \"Personal details\" card of the Me tab's Profile.\n *\n * ⚠ OWNER ONLY, and kept OFF `founderProfileSchema` on purpose. Email, phone,\n * gender and date of birth are personal data: they appear on no card, no\n * search, no team list, no byline and no audit detail. The profile is read on\n * every page by the app shell; these do not travel with it, they are asked\n * for only where they are shown.\n *\n * Read shapes are plain where the stored value may predate today's rule (a\n * phone seeded from the sign-in provider in another format): a read schema\n * that rejects our own stored value is a self-inflicted outage. The write\n * body holds the rules.\n */\nexport const myPersonalDetailsSchema = z.object({\n /** The profile's display name: the card's first row. */\n displayName: z.string(),\n /** The sign-in account's. Changed there (Account settings), never here. */\n email: z.string().optional(),\n /** Ten digits as stored on the account mirror; display adds the `+91`. */\n phone: z.string().optional(),\n gender: genderSchema.optional(),\n /**\n * `YYYY-MM-DD`. READ ONLY: it is the 18+ check's input, given once at\n * onboarding. A date of birth that could be edited later would be an age\n * gate anyone could walk around.\n */\n dateOfBirth: z.string().optional(),\n /** Which of email, phone, gender and birthday the member shows on their page (0.11.0). */\n visibility: contactVisibilitySchema,\n});\nexport type MyPersonalDetails = z.infer<typeof myPersonalDetailsSchema>;\n\n/**\n * `PATCH /api/v1/profiles/me/personal` (0.10.0): name, phone and gender. Only\n * the keys the member changed; answers with `myPersonalDetailsSchema`.\n *\n * ONE RULE PER FIELD: the very schemas onboarding finishes with (the display\n * name, the ten-digit phone, the gender enum with prefer-not-to-say), so a\n * value one accepts the other can never refuse.\n *\n * ⚠ NO `.default()` and no `.partial()` of a read shape (see\n * `updateFounderProfileBodySchema`): absent means unchanged, and a default\n * would overwrite what is stored. No `dateOfBirth`: read only, above. No\n * `email`: the sign-in account owns it.\n *\n * Name and phone are PRODUCT-OWNED on Founder Circle's account mirror: the\n * sign-in service seeds them once and never overwrites them, so this edits\n * Founder Circle's copy and never calls the sign-in service.\n */\nexport const updateMyPersonalDetailsBodySchema = z.object({\n displayName: displayNameSchema.optional(),\n phone: phoneSchema.optional(),\n gender: genderSchema.optional(),\n /**\n * The city, or `null` to remove it (0.11.0): it moved to the Personal\n * Details card, so its editor saves it here, with the rest, in one\n * transaction. `PATCH /profiles/me` still takes it too. Written in India,\n * one of its states (0.17.0, `indiaLocationSchema`).\n */\n location: indiaLocationSchema.nullable().optional(),\n /** Only the switches that changed (0.11.0). */\n visibility: contactVisibilitySchema.partial().optional(),\n});\nexport type UpdateMyPersonalDetailsBody = z.infer<typeof updateMyPersonalDetailsBodySchema>;\n\n// ─── The member's own photo ──────────────────────────────────────────────────\n\n/**\n * `PUT /api/v1/profiles/me/avatar`: use a completed upload (purpose `avatar`)\n * as the member's photo. Answers with the updated `founderProfileSchema`.\n *\n * The previous photo is deleted from storage, not merely unlinked, so its old\n * address stops working. `DELETE /api/v1/profiles/me/avatar` removes the photo\n * the same way and the member falls back to their sign-in provider's picture.\n */\nexport const setAvatarBodySchema = z.object({ mediaId: objectIdSchema });\nexport type SetAvatarBody = z.infer<typeof setAvatarBodySchema>;\n"]}
|
|
1
|
+
{"version":3,"file":"founder-profile.js","sourceRoot":"","sources":["../../../src/domains/profile/founder-profile.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema — The founder profile\n *\n * One member, one profile. The central auth account owns identity (email,\n * verification, deletion); this owns everything Founder Circle shows.\n *\n * @module founder-schema/domains/profile\n */\n\nimport { z } from 'zod';\nimport {\n chosenUsernameSchema,\n displayNameSchema,\n GOAL_VALUES,\n genderSchema,\n goalSchema,\n httpUrlSchema,\n industrySchema,\n locationSchema,\n objectIdSchema,\n optionalTrimmedString,\n phoneSchema,\n requiredGoalListSchema,\n skillListSchema,\n skillSchema,\n startupStageSchema,\n usernameSchema,\n} from '../../core/common.js';\nimport {\n ABOUT_MAX,\n DISPLAY_NAME_MAX,\n FOUNDED_YEAR_MIN,\n HEADLINE_MAX,\n SKILLS_MAX,\n TEAM_SIZE_MAX,\n TEAM_SIZE_MIN,\n VENTURE_NAME_MAX,\n VENTURE_TAGLINE_MAX,\n} from '../../core/limits.js';\nimport { VALIDATION_MESSAGE } from '../../core/validation.js';\nimport { indiaLocationSchema } from '../location/india.js';\nimport { mediaImageSchema } from '../media/media.js';\n\n// ─── Venture ─────────────────────────────────────────────────────────────────\n\n/**\n * ⚠ NUMBERS ARE NUMBERS, never coerced. This is a BODY, which arrives as JSON\n * and already carries types. `z.coerce.number()` here accepted `teamSize: true`\n * as 1 and `\" \"` as 0, and made the schema's input type `unknown`, so a form\n * typed against it could put anything in the field. Coercion belongs to query\n * strings, where everything is text; a form converts its text input before it\n * builds the body.\n */\nexport const ventureSchema = z.object({\n name: z.string().trim().min(1).max(VENTURE_NAME_MAX),\n tagline: optionalTrimmedString(VENTURE_TAGLINE_MAX),\n website: httpUrlSchema.optional(),\n industry: industrySchema,\n stage: startupStageSchema,\n /** Team size including the founder. Optional: a solo pre-idea founder has none to report. */\n teamSize: z.number().int().min(TEAM_SIZE_MIN).max(TEAM_SIZE_MAX).optional(),\n /**\n * Not in the future. The bound is read per parse, never captured once.\n *\n * `.max(new Date().getUTCFullYear())` looks equivalent and is not: it is\n * evaluated when this module is imported, so a server process still up on\n * 1 January rejects the new year until somebody restarts it. The backend's\n * Mongoose model deliberately carries no upper bound for the same reason, a\n * `max` there being frozen at schema construction. This is where the ceiling\n * lives.\n */\n foundedYear: z\n .number()\n .int()\n .min(FOUNDED_YEAR_MIN)\n .refine((year) => year <= new Date().getUTCFullYear(), {\n error: VALIDATION_MESSAGE.foundedYearFuture,\n })\n .optional(),\n});\nexport type Venture = z.infer<typeof ventureSchema>;\n\n/**\n * A venture's fields, in order, and which of them may be left out (0.17.0):\n * what the backend lists when it saves or compares a venture, read from here.\n */\nexport const VENTURE_KEYS = ventureSchema.keyof().options;\nexport type VentureKey = (typeof VENTURE_KEYS)[number];\nexport const VENTURE_OPTIONAL_KEYS: readonly VentureKey[] = VENTURE_KEYS.filter(\n (key) => ventureSchema.shape[key].safeParse(undefined).success,\n);\n\n// ─── A member as other surfaces name them ────────────────────────────────────\n\n/**\n * A member as a byline renders them: on a post (`feedAuthorSchema` is this), a\n * product's team, a review, a notification, the top-helpers list.\n *\n * Every field is LIVE, joined against the profile at read time rather than\n * frozen onto the post or review at write time. So a renamed handle, a new\n * photo or an edited headline updates everywhere, the link always targets the\n * current username, and nothing personal survives in other people's rows once\n * an account is deleted.\n */\nexport const memberBylineSchema = z.object({\n userId: z.string(),\n username: usernameSchema,\n displayName: z.string(),\n /** Server-generated or from the login provider, never member-typed, so a\n * plain URL check. Same reasoning as `avatarUrl` on the profile. */\n avatarUrl: z.url().optional(),\n /** The member's one-line headline, live-joined. */\n headline: z.string().optional(),\n});\nexport type MemberByline = z.infer<typeof memberBylineSchema>;\n\n/**\n * What a member has given the community, on their profile and card.\n *\n * `score` is points earned from OTHER members' recognition only: a product's\n * team marking your feedback helpful, confirming you tested their product,\n * and members finding your review helpful (writing one earns nothing by\n * itself). Nothing a member\n * does to their own product counts, so the score cannot be bought by having a\n * popular one. `helpedCount` is how many different founders recognised that\n * help (\"Helped 12 founders\"). See `domains/contributions`.\n */\nexport const contributionSummarySchema = z.object({\n score: z.number().int().nonnegative(),\n helpedCount: z.number().int().nonnegative(),\n});\nexport type ContributionSummary = z.infer<typeof contributionSummarySchema>;\n\n// ─── Profile ─────────────────────────────────────────────────────────────────\n\n/**\n * The founder's off-platform links. ONE shape: onboarding's contact step picks\n * its three from this, so a link one side knows can never be missing on the\n * other. (It was written twice until 0.2.0, and the profile side lacked\n * Instagram, which onboarding collected.)\n *\n * Defined without a default, because the read side wants one and the write\n * side must not have one; each adds its own.\n */\nexport const founderLinksSchema = z.object({\n linkedin: httpUrlSchema.optional(),\n x: httpUrlSchema.optional(),\n instagram: httpUrlSchema.optional(),\n github: httpUrlSchema.optional(),\n website: httpUrlSchema.optional(),\n});\nexport type FounderLinks = z.infer<typeof founderLinksSchema>;\n\n/**\n * The profile's links, by key, in order (0.17.0): what the backend's model and\n * its projections list. Read from here, never written out again: a copy of\n * this list is how the profile model once lost Instagram.\n */\nexport const FOUNDER_LINK_KEYS = founderLinksSchema.keyof().options;\nexport type FounderLinkKey = (typeof FOUNDER_LINK_KEYS)[number];\n\n/**\n * The profile as the owner sees it. A peer sees the redacted projection\n * (`founderCardSchema`), which is built on the server and never by dropping\n * fields on the client.\n */\nexport const founderProfileSchema = z.object({\n id: z.string(),\n userId: z.string(),\n username: usernameSchema,\n displayName: z.string().trim().min(1).max(DISPLAY_NAME_MAX),\n headline: optionalTrimmedString(HEADLINE_MAX),\n about: optionalTrimmedString(ABOUT_MAX),\n /**\n * Plain `z.url()`, NOT `httpUrlSchema`, and the difference is deliberate.\n *\n * No member can write this field: it appears in no write body, and\n * `public-surface.test.ts` asserts that and will fail the day somebody adds it\n * to a form. So the scheme restriction would be defending a field that takes\n * no member input, while refusing hosts we legitimately serve avatars from,\n * our own dev host among them. A read schema that rejects our own response is\n * a self-inflicted outage.\n *\n * ⚠️ That argument holds only while nothing member-supplied reaches this\n * field. Two sources write an avatar today and NEITHER is a member typing a\n * URL: our own upload path, and the central-auth `user.created` /\n * `user.updated` webhook, which carries the Google OAuth picture. If a\n * \"paste a link to your photo\" control is ever added, this goes back to\n * `httpUrlSchema` and the ruling behind it is void.\n *\n * WHICH picture: the member's uploaded photo at its small size (192, see\n * `MEDIA_SIZES.avatar`), else their sign-in provider's picture, else absent.\n * Every surface that shows this member resolves it the same way.\n */\n avatarUrl: z.url().optional(),\n /**\n * The uploaded photo itself, every size, when the member has one. Absent\n * means `avatarUrl` (if any) is the sign-in provider's picture, so a client\n * offers \"Remove photo\" only when this is present. Not on `founderCardSchema`:\n * a card needs one small picture, not the set.\n */\n avatar: mediaImageSchema.optional(),\n location: locationSchema.optional(),\n venture: ventureSchema.optional(),\n skills: z.array(skillSchema).max(SKILLS_MAX).default([]),\n goals: z.array(goalSchema).max(GOAL_VALUES.length).default([]),\n links: founderLinksSchema.default({}),\n /**\n * Their contribution to other founders. Server-computed, never written by a\n * member. REQUIRED, no default: a read-side default would let a server that\n * forgot the field pass as a real zero.\n */\n contribution: contributionSummarySchema,\n /** Onboarding is finished and the member is visible in Discover. */\n isOnboarded: z.boolean().default(false),\n /** Member paused their own profile. Hidden from Discover, keeps their data. */\n isActive: z.boolean().default(true),\n createdAt: z.iso.datetime(),\n updatedAt: z.iso.datetime(),\n});\nexport type FounderProfile = z.infer<typeof founderProfileSchema>;\n\n/**\n * What one founder sees of another in Discover and on a profile page.\n *\n * Built server-side. Keep it a strict subset of `founderProfileSchema` — if a\n * private field ever needs to show here, that is a product decision, not an\n * accident of spreading the full document.\n */\n// ⚠ The web's Discover cache follows the member's own saves of exactly these\n// fields (`discoverChangedBy` in the web's features/profile/api.ts derives its\n// list from this shape): adding a field here is enough, there is no second list.\nexport const founderCardSchema = founderProfileSchema.pick({\n id: true,\n /**\n * The MEMBER's id (0.6.0): what a team invitation, a chat and every byline\n * key on. `id` is the profile's own id, which nothing takes as input.\n */\n userId: true,\n username: true,\n displayName: true,\n headline: true,\n avatarUrl: true,\n location: true,\n venture: true,\n skills: true,\n goals: true,\n contribution: true,\n});\nexport type FounderCard = z.infer<typeof founderCardSchema>;\n\n/** The fields a Discover card shows. */\nexport const FOUNDER_CARD_FIELDS = founderCardSchema.keyof().options;\nexport type FounderCardField = (typeof FOUNDER_CARD_FIELDS)[number];\n\n/**\n * The card fields a member's own edit can change, the ones that move them up\n * Discover: every card field but the ids, which never change, and the points\n * (`contribution`), which other people's acts move all day and which would\n * otherwise reorder Discover with every helpful mark.\n */\nexport const FOUNDER_CARD_EDIT_FIELDS = FOUNDER_CARD_FIELDS.filter(\n (field) => field !== 'id' && field !== 'userId' && field !== 'contribution',\n);\n\n/**\n * Whether a write changed what a member's Discover card shows (0.18.0): ONE\n * rule for every profile writer, which decides whether the member moves up\n * Discover (its \"recently updated\" order) and whether a client reloads it.\n * `changed` names the fields the write actually changed: a body's keys, or\n * for the business editor `venture` only when its PUBLIC part changed. Before\n * 0.18.0 five writers decided it four ways: an edit to the About text alone\n * lifted a member to the top of everyone's Discover, a new username never did.\n * Points never count (`FOUNDER_CARD_EDIT_FIELDS`): they are not the member's\n * edit.\n */\nexport function changesDiscoverCard(changed: readonly string[]): boolean {\n return changed.some((field) => (FOUNDER_CARD_EDIT_FIELDS as readonly string[]).includes(field));\n}\n\n// ─── What a member chooses to show (0.11.0) ──────────────────────────────────\n\n/**\n * The four personal details a member MAY show on their profile page (the\n * user's ruling, 2026-10-11). Each is PRIVATE until its owner turns it on;\n * everything else on a profile is public as before. Changed only through\n * `PATCH /profiles/me/personal`.\n */\nexport const CONTACT_VISIBILITY_FIELDS = ['email', 'phone', 'gender', 'birthday'] as const;\nexport type ContactVisibilityField = (typeof CONTACT_VISIBILITY_FIELDS)[number];\n\n/**\n * WHO sees each of the four once its owner turns it on (0.18.0). The user,\n * 2026-10-20: \"any person who has the platform membership can view them if\n * they are public\", so all four go to members only (and to the member\n * themself), never to an account without membership. One table: the server\n * builds `shown` from it, and every screen words its switch from it.\n */\nexport const PROFILE_AUDIENCE_VALUES = ['members'] as const;\nexport type ProfileAudience = (typeof PROFILE_AUDIENCE_VALUES)[number];\nexport const CONTACT_VISIBILITY_AUDIENCE = {\n email: 'members',\n phone: 'members',\n gender: 'members',\n birthday: 'members',\n} as const satisfies Record<ContactVisibilityField, ProfileAudience>;\n\n/** Which of the four the member shows. Every key present on a read: false is private. */\nexport const contactVisibilitySchema = z.object(\n Object.fromEntries(CONTACT_VISIBILITY_FIELDS.map((field) => [field, z.boolean()])) as Record<\n ContactVisibilityField,\n z.ZodBoolean\n >,\n);\nexport type ContactVisibility = z.infer<typeof contactVisibilitySchema>;\n\n/**\n * A birthday as OTHERS see it: day and month, NEVER the year (the user's\n * ruling). The year would give away the age and, with the name, the full date\n * of birth that identity checks ask for. The year is never on the wire.\n */\nexport const publicBirthdaySchema = z.object({\n day: z.number().int().min(1).max(31),\n month: z.number().int().min(1).max(12),\n});\nexport type PublicBirthday = z.infer<typeof publicBirthdaySchema>;\n\n/**\n * `GET /api/v1/founders/by-username/:username` (0.11.0): one member's profile\n * page, as a signed-in member (or the member themself) sees it.\n *\n * The card's fields, what only a page has room for (about, links, member\n * since), and `shown`: the personal details this member has CHOSEN to show,\n * and nothing else, to the audience `CONTACT_VISIBILITY_AUDIENCE` names\n * (members only, all four, 0.18.0); the server decides, and a client renders\n * what arrives. Built field by field on the server\n * (`toMemberPublic`), never by dropping fields from a fuller document.\n *\n * `viewer.canMessage` is the server's answer to \"offer Message\": the tap\n * opens a conversation, or (0.13.0) the sheet that makes one possible (the\n * membership for a viewer, the plans for a member without one). Who may\n * message whom follows membership and plans, so no client works it out.\n */\nexport const memberProfileSchema = founderProfileSchema\n .pick({\n id: true,\n userId: true,\n username: true,\n displayName: true,\n headline: true,\n about: true,\n avatarUrl: true,\n location: true,\n venture: true,\n skills: true,\n goals: true,\n links: true,\n contribution: true,\n createdAt: true,\n })\n .extend({\n shown: z.object({\n email: z.string().optional(),\n phone: z.string().optional(),\n gender: genderSchema.optional(),\n birthday: publicBirthdaySchema.optional(),\n }),\n viewer: z.object({ isSelf: z.boolean(), canMessage: z.boolean() }),\n });\nexport type MemberProfile = z.infer<typeof memberProfileSchema>;\n\n/** The member page's address: `/discover/<username>`. */\nexport const memberHandleParamsSchema = z.object({ username: usernameSchema });\nexport type MemberHandleParams = z.infer<typeof memberHandleParamsSchema>;\n\n// ─── Write bodies ────────────────────────────────────────────────────────────\n\n/**\n * A text field the member is allowed to empty.\n *\n * Three states, and they are deliberately distinct:\n * absent do not touch this field\n * null clear it (the backend turns this into an `$unset`)\n * \"\" REJECTED\n *\n * The empty string is refused on purpose. A form that forgets to convert a\n * cleared input to `null` then fails loudly at the boundary, instead of being\n * silently dropped and leaving the member staring at text they just deleted.\n * `optionalTrimmedString` does the dropping, which is right for a create body\n * and wrong for a patch.\n */\nconst clearableText = (maxLength: number) =>\n z.string().trim().min(1, VALIDATION_MESSAGE.clearWithNull).max(maxLength).nullable().optional();\n\n/**\n * Patch body for the Me tab. Only the keys the member actually changed.\n *\n * ⚠️ Written as its own object ON PURPOSE. Do NOT rebuild it as\n * `founderProfileSchema.pick({...}).partial()`: `.partial()` does not switch\n * off `.default()`, so `skills`, `goals` and `links` would materialise as\n * `[]`, `[]` and `{}` on every request, including an empty one. Under a `$set`\n * that wipes a member's skills and every one of their links the moment they\n * edit their display name. `tsc` cannot see it and the request looks correct in\n * the network tab.\n *\n * The rule this comes from: defaults belong on the READ side. A write body\n * states what the caller sent and nothing else.\n *\n * ONE WRITER PER FIELD (0.18.0): the name and the city are the personal\n * details' (`PATCH /profiles/me/personal`), the venture is the business\n * editor's (`PUT /profiles/me/business`), the handle has its own route. They\n * were taken here too, a second path with its own rules for Discover's order.\n * STRICT: any other key is refused (a 400 naming it), never dropped without a\n * word, so a client still sending a name here learns it at once instead of\n * losing the member's edit.\n */\nexport const updateFounderProfileBodySchema = z\n .object({\n headline: clearableText(HEADLINE_MAX),\n about: clearableText(ABOUT_MAX),\n /** The same list rules as onboarding's finish (each value once, 0.17.0). */\n skills: skillListSchema.optional(),\n /**\n * The same rules as onboarding's finish, its minimum included (0.18.0, the\n * user: \"we already have at least one goal required\"): Me cannot save a\n * member down to no goals.\n */\n goals: requiredGoalListSchema.optional(),\n /** Sent whole. To drop one link, send the object without that key. */\n links: founderLinksSchema.optional(),\n })\n .strict();\nexport type UpdateFounderProfileBody = z.infer<typeof updateFounderProfileBodySchema>;\n\n/**\n * Username change. Separate from the profile patch because it needs a\n * uniqueness check, and it refuses a reserved handle (`chosenUsernameSchema`).\n */\nexport const updateUsernameBodySchema = z.object({ username: chosenUsernameSchema });\nexport type UpdateUsernameBody = z.infer<typeof updateUsernameBodySchema>;\n\n/** `GET /api/v1/profiles/username-available?username=` (0.13.0): the handle asked about. */\nexport const usernameAvailabilityQuerySchema = z.object({ username: usernameSchema });\nexport type UsernameAvailabilityQuery = z.infer<typeof usernameAvailabilityQuerySchema>;\n\n/** Why a username is not available. */\nexport const USERNAME_UNAVAILABLE_REASON_VALUES = ['taken', 'reserved'] as const;\nexport type UsernameUnavailableReason = (typeof USERNAME_UNAVAILABLE_REASON_VALUES)[number];\n\nexport const usernameAvailabilitySchema = z.object({\n username: usernameSchema,\n available: z.boolean(),\n /** Present when unavailable. */\n reason: z.enum(USERNAME_UNAVAILABLE_REASON_VALUES).optional(),\n});\nexport type UsernameAvailability = z.infer<typeof usernameAvailabilitySchema>;\n\n// ─── The member's own personal details ──────────────────────────────────────\n\n/**\n * `GET /api/v1/profiles/me/personal` (0.10.0): what ONLY the member sees of\n * themselves, the \"Personal details\" card of the Me tab's Profile.\n *\n * ⚠ OWNER ONLY, and kept OFF `founderProfileSchema` on purpose. Email, phone,\n * gender and date of birth are personal data: they appear on no card, no\n * search, no team list, no byline and no audit detail. The profile is read on\n * every page by the app shell; these do not travel with it, they are asked\n * for only where they are shown.\n *\n * Read shapes are plain where the stored value may predate today's rule (a\n * phone seeded from the sign-in provider in another format): a read schema\n * that rejects our own stored value is a self-inflicted outage. The write\n * body holds the rules.\n */\nexport const myPersonalDetailsSchema = z.object({\n /** The profile's display name: the card's first row. */\n displayName: z.string(),\n /** The sign-in account's. Changed there (Account settings), never here. */\n email: z.string().optional(),\n /** Ten digits as stored on the account mirror; display adds the `+91`. */\n phone: z.string().optional(),\n gender: genderSchema.optional(),\n /**\n * `YYYY-MM-DD`. READ ONLY: it is the 18+ check's input, given once at\n * onboarding. A date of birth that could be edited later would be an age\n * gate anyone could walk around.\n */\n dateOfBirth: z.string().optional(),\n /** Which of email, phone, gender and birthday the member shows on their page (0.11.0). */\n visibility: contactVisibilitySchema,\n});\nexport type MyPersonalDetails = z.infer<typeof myPersonalDetailsSchema>;\n\n/**\n * `PATCH /api/v1/profiles/me/personal` (0.10.0): name, phone and gender. Only\n * the keys the member changed; answers with `myPersonalDetailsSchema`.\n *\n * ONE RULE PER FIELD: the very schemas onboarding finishes with (the display\n * name, the ten-digit phone, the gender enum with prefer-not-to-say), so a\n * value one accepts the other can never refuse.\n *\n * ⚠ NO `.default()` and no `.partial()` of a read shape (see\n * `updateFounderProfileBodySchema`): absent means unchanged, and a default\n * would overwrite what is stored. No `dateOfBirth`: read only, above. No\n * `email`: the sign-in account owns it.\n *\n * Name and phone are PRODUCT-OWNED on Founder Circle's account mirror: the\n * sign-in service seeds them once and never overwrites them, so this edits\n * Founder Circle's copy and never calls the sign-in service.\n */\nexport const updateMyPersonalDetailsBodySchema = z.object({\n displayName: displayNameSchema.optional(),\n phone: phoneSchema.optional(),\n gender: genderSchema.optional(),\n /**\n * The city, or `null` to remove it (0.11.0): it moved to the Personal\n * Details card, so its editor saves it here, with the rest, in one\n * transaction; only here since 0.18.0 (`PATCH /profiles/me` took it too).\n * Written in India, one of its states (0.17.0, `indiaLocationSchema`).\n */\n location: indiaLocationSchema.nullable().optional(),\n /** Only the switches that changed (0.11.0). */\n visibility: contactVisibilitySchema.partial().optional(),\n});\nexport type UpdateMyPersonalDetailsBody = z.infer<typeof updateMyPersonalDetailsBodySchema>;\n\n// ─── The member's own photo ──────────────────────────────────────────────────\n\n/**\n * `PUT /api/v1/profiles/me/avatar`: use a completed upload (purpose `avatar`)\n * as the member's photo. Answers with the updated `founderProfileSchema`.\n *\n * The previous photo is deleted from storage, not merely unlinked, so its old\n * address stops working. `DELETE /api/v1/profiles/me/avatar` removes the photo\n * the same way and the member falls back to their sign-in provider's picture.\n */\nexport const setAvatarBodySchema = z.object({ mediaId: objectIdSchema });\nexport type SetAvatarBody = z.infer<typeof setAvatarBodySchema>;\n"]}
|
|
@@ -27,7 +27,7 @@ import { z } from 'zod';
|
|
|
27
27
|
*/
|
|
28
28
|
export declare const myVentureSchema: z.ZodObject<{
|
|
29
29
|
name: z.ZodString;
|
|
30
|
-
tagline: z.
|
|
30
|
+
tagline: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
|
|
31
31
|
website: z.ZodOptional<z.ZodURL>;
|
|
32
32
|
industry: z.ZodEnum<{
|
|
33
33
|
agritech: "agritech";
|
|
@@ -93,7 +93,7 @@ export type MyVenture = z.infer<typeof myVentureSchema>;
|
|
|
93
93
|
export declare const myBusinessSchema: z.ZodObject<{
|
|
94
94
|
venture: z.ZodOptional<z.ZodObject<{
|
|
95
95
|
name: z.ZodString;
|
|
96
|
-
tagline: z.
|
|
96
|
+
tagline: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
|
|
97
97
|
website: z.ZodOptional<z.ZodURL>;
|
|
98
98
|
industry: z.ZodEnum<{
|
|
99
99
|
agritech: "agritech";
|
|
@@ -165,7 +165,7 @@ export type MyBusiness = z.infer<typeof myBusinessSchema>;
|
|
|
165
165
|
export declare const putMyBusinessBodySchema: z.ZodObject<{
|
|
166
166
|
venture: z.ZodNullable<z.ZodObject<{
|
|
167
167
|
name: z.ZodString;
|
|
168
|
-
tagline: z.
|
|
168
|
+
tagline: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
|
|
169
169
|
website: z.ZodOptional<z.ZodURL>;
|
|
170
170
|
industry: z.ZodEnum<{
|
|
171
171
|
agritech: "agritech";
|
|
@@ -149,7 +149,7 @@ export declare const reviewPageSchema: z.ZodObject<{
|
|
|
149
149
|
export type ReviewPage = z.infer<typeof reviewPageSchema>;
|
|
150
150
|
export declare const reviewsQuerySchema: z.ZodObject<{
|
|
151
151
|
limit: z.ZodPreprocess<z.ZodDefault<z.ZodCoercedNumber<unknown>>, unknown>;
|
|
152
|
-
cursor: z.ZodOptional<z.ZodString>;
|
|
152
|
+
cursor: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
|
|
153
153
|
sort: z.ZodPreprocess<z.ZodDefault<z.ZodEnum<{
|
|
154
154
|
new: "new";
|
|
155
155
|
helpful: "helpful";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reviews.d.ts","sourceRoot":"","sources":["../../../src/domains/reviews/reviews.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"reviews.d.ts","sourceRoot":"","sources":["../../../src/domains/reviews/reviews.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgBxB,mBAAmB;AACnB,eAAO,MAAM,kBAAkB,aAAmD,CAAC;AACnF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,8CAA8C;AAC9C,eAAO,MAAM,kBAAkB,6BAA8B,CAAC;AAC9D,eAAO,MAAM,gBAAgB;;;EAA6B,CAAC;AAC3D,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,4CAA4C;AAC5C,eAAO,MAAM,iBAAiB;;;;;;;;;;;iBAM5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,oEAAoE;AACpE,eAAO,MAAM,kBAAkB;;;;;;;;;iBAe7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAa9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAuC,CAAC;AACrE,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,kBAAkB;;;;;;;iBAE7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9D,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEnE,eAAO,MAAM,kBAAkB;;iBAAyC,CAAC;AACzE,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,eAAO,MAAM,sBAAsB;;;iBAGjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,wFAAwF;AACxF,eAAO,MAAM,sBAAsB;;;iBAGjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,gFAAgF;AAChF,eAAO,MAAM,qBAAqB;;iBAEhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,sFAAsF;AACtF,eAAO,MAAM,2BAA2B;;;;iBAItC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC"}
|
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
*/
|
|
30
30
|
import { z } from 'zod';
|
|
31
31
|
import { emptyStringToUndefined, objectIdSchema, paginationQuerySchema, } from '../../core/common.js';
|
|
32
|
-
import { REVIEW_REPLY_MAX, REVIEW_TEXT_MAX, REVIEW_TEXT_MIN } from '../../core/limits.js';
|
|
32
|
+
import { RATING_MAX, RATING_MIN, REVIEW_REPLY_MAX, REVIEW_TEXT_MAX, REVIEW_TEXT_MIN, } from '../../core/limits.js';
|
|
33
33
|
import { paginatedSchema } from '../../core/responses.js';
|
|
34
34
|
import { memberBylineSchema } from '../profile/founder-profile.js';
|
|
35
35
|
/** Whole stars. */
|
|
36
|
-
export const reviewRatingSchema = z.number().int().min(
|
|
36
|
+
export const reviewRatingSchema = z.number().int().min(RATING_MIN).max(RATING_MAX);
|
|
37
37
|
/** `new` (the default) or `helpful` first. */
|
|
38
38
|
export const REVIEW_SORT_VALUES = ['new', 'helpful'];
|
|
39
39
|
export const reviewSortSchema = z.enum(REVIEW_SORT_VALUES);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reviews.js","sourceRoot":"","sources":["../../../src/domains/reviews/reviews.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,
|
|
1
|
+
{"version":3,"file":"reviews.js","sourceRoot":"","sources":["../../../src/domains/reviews/reviews.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema — Reviews: stars and words on a product\n *\n * One review per member per product: 1 to 5 stars AND text of at least\n * `REVIEW_TEXT_MIN` characters, because a star with no reason does not help\n * the founder. Nobody on a product's team reviews it. The writer may edit it\n * any time (it then shows as edited) or delete it. The team may answer each\n * review once, publicly; other members may find it Helpful.\n *\n * `testedIt` marks a review by a member the team confirmed as a tester of the\n * product, so a reader can tell a review from use from a review from a glance.\n *\n * The product's average shows from `REVIEW_AVERAGE_MIN_COUNT` reviews. Hidden\n * reviews (staff) are not counted.\n *\n * Routes (all signed in):\n * GET /api/v1/products/:productId/reviews reviewsQuerySchema -> reviewPageSchema\n * POST /api/v1/products/:productId/reviews createReviewBodySchema -> productReviewSchema\n * GET /api/v1/reviews/:reviewId -> productReviewSchema (yours, to edit it)\n * PATCH /api/v1/reviews/:reviewId updateReviewBodySchema -> productReviewSchema\n * DELETE /api/v1/reviews/:reviewId -> IdResponse\n * PUT /api/v1/reviews/:reviewId/reply reviewReplyBodySchema -> productReviewSchema\n * DELETE /api/v1/reviews/:reviewId/reply -> productReviewSchema\n * PUT /api/v1/reviews/:reviewId/helpful -> reviewHelpfulResponseSchema\n * DELETE /api/v1/reviews/:reviewId/helpful -> reviewHelpfulResponseSchema\n * POST /api/v1/reviews/:reviewId/report createReportBodySchema -> CreateReportResponse\n *\n * @module founder-schema/domains/reviews\n */\n\nimport { z } from 'zod';\nimport {\n emptyStringToUndefined,\n objectIdSchema,\n paginationQuerySchema,\n} from '../../core/common.js';\nimport {\n RATING_MAX,\n RATING_MIN,\n REVIEW_REPLY_MAX,\n REVIEW_TEXT_MAX,\n REVIEW_TEXT_MIN,\n} from '../../core/limits.js';\nimport { paginatedSchema } from '../../core/responses.js';\nimport { memberBylineSchema } from '../profile/founder-profile.js';\n\n/** Whole stars. */\nexport const reviewRatingSchema = z.number().int().min(RATING_MIN).max(RATING_MAX);\nexport type ReviewRating = z.infer<typeof reviewRatingSchema>;\n\n/** `new` (the default) or `helpful` first. */\nexport const REVIEW_SORT_VALUES = ['new', 'helpful'] as const;\nexport const reviewSortSchema = z.enum(REVIEW_SORT_VALUES);\nexport type ReviewSort = z.infer<typeof reviewSortSchema>;\n\n/** The team's public answer to a review. */\nexport const reviewReplySchema = z.object({\n text: z.string().max(REVIEW_REPLY_MAX),\n /** Who on the team wrote it; null when they have since deleted their account. */\n author: memberBylineSchema.nullable(),\n createdAt: z.iso.datetime(),\n editedAt: z.iso.datetime().nullable(),\n});\nexport type ReviewReply = z.infer<typeof reviewReplySchema>;\n\n/** What this viewer may do with a review, decided by the server. */\nexport const reviewViewerSchema = z.object({\n isAuthor: z.boolean(),\n votedHelpful: z.boolean(),\n canEdit: z.boolean(),\n canDelete: z.boolean(),\n /** On the product's team, taking part: write or edit the team's reply. */\n canReply: z.boolean(),\n /**\n * On the product's team, member or not, whatever the product's state:\n * take the team's reply back (0.12.0). Taking back is never paid.\n */\n canRemoveReply: z.boolean(),\n /** Not the writer. */\n canVoteHelpful: z.boolean(),\n canReport: z.boolean(),\n});\nexport type ReviewViewer = z.infer<typeof reviewViewerSchema>;\n\nexport const productReviewSchema = z.object({\n id: objectIdSchema,\n productId: objectIdSchema,\n author: memberBylineSchema,\n rating: reviewRatingSchema,\n text: z.string().max(REVIEW_TEXT_MAX),\n /** The team confirmed the writer as a tester of this product. */\n testedIt: z.boolean(),\n helpfulCount: z.number().int().nonnegative(),\n reply: reviewReplySchema.nullable(),\n createdAt: z.iso.datetime(),\n editedAt: z.iso.datetime().nullable(),\n viewer: reviewViewerSchema.optional(),\n});\nexport type ProductReview = z.infer<typeof productReviewSchema>;\n\nexport const reviewPageSchema = paginatedSchema(productReviewSchema);\nexport type ReviewPage = z.infer<typeof reviewPageSchema>;\n\nexport const reviewsQuerySchema = paginationQuerySchema.extend({\n sort: z.preprocess(emptyStringToUndefined, reviewSortSchema.default('new')),\n});\nexport type ReviewsQuery = z.infer<typeof reviewsQuerySchema>;\nexport type ReviewsQueryInput = z.input<typeof reviewsQuerySchema>;\n\nexport const reviewParamsSchema = z.object({ reviewId: objectIdSchema });\nexport type ReviewParams = z.infer<typeof reviewParamsSchema>;\n\nconst reviewTextSchema = z.string().trim().min(REVIEW_TEXT_MIN).max(REVIEW_TEXT_MAX);\n\nexport const createReviewBodySchema = z.object({\n rating: reviewRatingSchema,\n text: reviewTextSchema,\n});\nexport type CreateReviewBody = z.infer<typeof createReviewBodySchema>;\n\n/** Only what changed. An empty body changes nothing and answers the review as it is. */\nexport const updateReviewBodySchema = z.object({\n rating: reviewRatingSchema.optional(),\n text: reviewTextSchema.optional(),\n});\nexport type UpdateReviewBody = z.infer<typeof updateReviewBodySchema>;\n\n/** The team's reply: written once, then edited in place by sending it again. */\nexport const reviewReplyBodySchema = z.object({\n text: z.string().trim().min(1).max(REVIEW_REPLY_MAX),\n});\nexport type ReviewReplyBody = z.infer<typeof reviewReplyBodySchema>;\n\n/** `PUT|DELETE /reviews/:id/helpful`: the state it settled on and the fresh count. */\nexport const reviewHelpfulResponseSchema = z.object({\n reviewId: objectIdSchema,\n votedHelpful: z.boolean(),\n helpfulCount: z.number().int().nonnegative(),\n});\nexport type ReviewHelpfulResponse = z.infer<typeof reviewHelpfulResponseSchema>;\n"]}
|
|
@@ -17,8 +17,10 @@
|
|
|
17
17
|
* PUT /api/v1/support/saved-replies/:replyId { title, body }
|
|
18
18
|
* DELETE /api/v1/support/saved-replies/:replyId
|
|
19
19
|
*
|
|
20
|
-
* THE STATES
|
|
21
|
-
*
|
|
20
|
+
* THE STATES, and they ARE whose turn it is (0.18.0: a list worded the turn
|
|
21
|
+
* from `lastFrom`, so a reopened ticket read "Waiting on member" while it
|
|
22
|
+
* waited on support):
|
|
23
|
+
* open waiting on support: new, reopened, or the member wrote last
|
|
22
24
|
* waiting waiting on the member: an agent wrote last
|
|
23
25
|
* resolved closed; the member can reopen it, and writing reopens it
|
|
24
26
|
*
|
|
@@ -34,6 +36,13 @@
|
|
|
34
36
|
import { z } from 'zod';
|
|
35
37
|
/** What the problem is about: routes it, and lets the team see patterns. Append only. */
|
|
36
38
|
export declare const TICKET_CATEGORY_VALUES: readonly ["account", "profile", "feed", "chat", "safety", "bug", "other", "billing", "products", "communities"];
|
|
39
|
+
/**
|
|
40
|
+
* The category a refund ask is filed under (0.18.0): the backend finds a
|
|
41
|
+
* member's ask by it to decide whether the ask came in time, and Payments
|
|
42
|
+
* presets it. One value, typed, so a renamed category fails to compile rather
|
|
43
|
+
* than turning every ask into "not in time".
|
|
44
|
+
*/
|
|
45
|
+
export declare const REFUND_ASK_CATEGORY: "billing";
|
|
37
46
|
export declare const ticketCategorySchema: z.ZodEnum<{
|
|
38
47
|
chat: "chat";
|
|
39
48
|
feed: "feed";
|
|
@@ -48,6 +57,9 @@ export declare const ticketCategorySchema: z.ZodEnum<{
|
|
|
48
57
|
}>;
|
|
49
58
|
export type TicketCategory = z.infer<typeof ticketCategorySchema>;
|
|
50
59
|
export declare const TICKET_STATUS_VALUES: readonly ["open", "waiting", "resolved"];
|
|
60
|
+
/** The three changes a ticket takes, each its own route (`/claim`, `/resolve`, `/reopen`). */
|
|
61
|
+
export declare const TICKET_CHANGE_VALUES: readonly ["claim", "resolve", "reopen"];
|
|
62
|
+
export type TicketChange = (typeof TICKET_CHANGE_VALUES)[number];
|
|
51
63
|
export declare const ticketStatusSchema: z.ZodEnum<{
|
|
52
64
|
open: "open";
|
|
53
65
|
waiting: "waiting";
|
|
@@ -68,6 +80,13 @@ export declare const ticketAssigneeFilterSchema: z.ZodEnum<{
|
|
|
68
80
|
unassigned: "unassigned";
|
|
69
81
|
}>;
|
|
70
82
|
export type TicketAssigneeFilter = z.infer<typeof ticketAssigneeFilterSchema>;
|
|
83
|
+
/** Which side wrote a message, and so whose turn it is not (0.18.0: the values, for the backend's model). */
|
|
84
|
+
export declare const TICKET_SIDE_VALUES: readonly ["member", "agent"];
|
|
85
|
+
export declare const ticketSideSchema: z.ZodEnum<{
|
|
86
|
+
member: "member";
|
|
87
|
+
agent: "agent";
|
|
88
|
+
}>;
|
|
89
|
+
export type TicketSide = z.infer<typeof ticketSideSchema>;
|
|
71
90
|
/** One message. `from` says which side wrote it; the agent's name is shown to the member. */
|
|
72
91
|
export declare const ticketMessageSchema: z.ZodObject<{
|
|
73
92
|
id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
@@ -231,7 +250,7 @@ export type TicketParams = z.infer<typeof ticketParamsSchema>;
|
|
|
231
250
|
/** `GET /support/tickets`. Mine by default; status and assignee narrow it. */
|
|
232
251
|
export declare const ticketListQuerySchema: z.ZodObject<{
|
|
233
252
|
limit: z.ZodPreprocess<z.ZodDefault<z.ZodCoercedNumber<unknown>>, unknown>;
|
|
234
|
-
cursor: z.ZodOptional<z.ZodString>;
|
|
253
|
+
cursor: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
|
|
235
254
|
scope: z.ZodPreprocess<z.ZodDefault<z.ZodEnum<{
|
|
236
255
|
mine: "mine";
|
|
237
256
|
queue: "queue";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"support.d.ts","sourceRoot":"","sources":["../../../src/domains/support/support.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"support.d.ts","sourceRoot":"","sources":["../../../src/domains/support/support.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,8 +17,10 @@
|
|
|
17
17
|
* PUT /api/v1/support/saved-replies/:replyId { title, body }
|
|
18
18
|
* DELETE /api/v1/support/saved-replies/:replyId
|
|
19
19
|
*
|
|
20
|
-
* THE STATES
|
|
21
|
-
*
|
|
20
|
+
* THE STATES, and they ARE whose turn it is (0.18.0: a list worded the turn
|
|
21
|
+
* from `lastFrom`, so a reopened ticket read "Waiting on member" while it
|
|
22
|
+
* waited on support):
|
|
23
|
+
* open waiting on support: new, reopened, or the member wrote last
|
|
22
24
|
* waiting waiting on the member: an agent wrote last
|
|
23
25
|
* resolved closed; the member can reopen it, and writing reopens it
|
|
24
26
|
*
|
|
@@ -50,8 +52,17 @@ export const TICKET_CATEGORY_VALUES = [
|
|
|
50
52
|
'products',
|
|
51
53
|
'communities',
|
|
52
54
|
];
|
|
55
|
+
/**
|
|
56
|
+
* The category a refund ask is filed under (0.18.0): the backend finds a
|
|
57
|
+
* member's ask by it to decide whether the ask came in time, and Payments
|
|
58
|
+
* presets it. One value, typed, so a renamed category fails to compile rather
|
|
59
|
+
* than turning every ask into "not in time".
|
|
60
|
+
*/
|
|
61
|
+
export const REFUND_ASK_CATEGORY = 'billing';
|
|
53
62
|
export const ticketCategorySchema = z.enum(TICKET_CATEGORY_VALUES);
|
|
54
63
|
export const TICKET_STATUS_VALUES = ['open', 'waiting', 'resolved'];
|
|
64
|
+
/** The three changes a ticket takes, each its own route (`/claim`, `/resolve`, `/reopen`). */
|
|
65
|
+
export const TICKET_CHANGE_VALUES = ['claim', 'resolve', 'reopen'];
|
|
55
66
|
export const ticketStatusSchema = z.enum(TICKET_STATUS_VALUES);
|
|
56
67
|
export const TICKET_SCOPE_VALUES = ['mine', 'queue'];
|
|
57
68
|
export const ticketScopeSchema = z.enum(TICKET_SCOPE_VALUES);
|
|
@@ -59,10 +70,13 @@ export const ticketScopeSchema = z.enum(TICKET_SCOPE_VALUES);
|
|
|
59
70
|
export const TICKET_ASSIGNEE_FILTER_VALUES = ['me', 'unassigned', 'any'];
|
|
60
71
|
export const ticketAssigneeFilterSchema = z.enum(TICKET_ASSIGNEE_FILTER_VALUES);
|
|
61
72
|
const person = z.object({ id: objectIdSchema, displayName: z.string() });
|
|
73
|
+
/** Which side wrote a message, and so whose turn it is not (0.18.0: the values, for the backend's model). */
|
|
74
|
+
export const TICKET_SIDE_VALUES = ['member', 'agent'];
|
|
75
|
+
export const ticketSideSchema = z.enum(TICKET_SIDE_VALUES);
|
|
62
76
|
/** One message. `from` says which side wrote it; the agent's name is shown to the member. */
|
|
63
77
|
export const ticketMessageSchema = z.object({
|
|
64
78
|
id: objectIdSchema,
|
|
65
|
-
from:
|
|
79
|
+
from: ticketSideSchema,
|
|
66
80
|
author: person,
|
|
67
81
|
body: z.string(),
|
|
68
82
|
at: z.iso.datetime(),
|
|
@@ -78,8 +92,8 @@ export const ticketSummarySchema = z.object({
|
|
|
78
92
|
messageCount: z.number().int().min(1),
|
|
79
93
|
createdAt: z.iso.datetime(),
|
|
80
94
|
lastMessageAt: z.iso.datetime(),
|
|
81
|
-
/** Who wrote last
|
|
82
|
-
lastFrom:
|
|
95
|
+
/** Who wrote last. Whose turn it is comes from `status`, never from this. */
|
|
96
|
+
lastFrom: ticketSideSchema,
|
|
83
97
|
});
|
|
84
98
|
export const ticketPageSchema = paginatedSchema(ticketSummarySchema);
|
|
85
99
|
/** A ticket with its whole conversation and what the asker may do with it. */
|