founder-schema 0.17.0 → 0.19.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 +186 -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 +54 -23
- package/dist/core/common.d.ts.map +1 -1
- package/dist/core/common.js +49 -24
- 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 +73 -17
- package/dist/core/limits.d.ts.map +1 -1
- package/dist/core/limits.js +77 -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 +19 -2
- package/dist/core/validation.d.ts.map +1 -1
- package/dist/core/validation.js +19 -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/areas.d.ts +331 -0
- package/dist/domains/location/areas.d.ts.map +1 -0
- package/dist/domains/location/areas.js +238 -0
- package/dist/domains/location/areas.js.map +1 -0
- package/dist/domains/location/india.d.ts +41 -123
- package/dist/domains/location/india.d.ts.map +1 -1
- package/dist/domains/location/india.js +121 -59
- 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 +42 -14
- package/dist/domains/onboarding/onboarding-answers.d.ts.map +1 -1
- package/dist/domains/onboarding/onboarding-answers.js +49 -33
- package/dist/domains/onboarding/onboarding-answers.js.map +1 -1
- package/dist/domains/onboarding/onboarding.d.ts +332 -138
- package/dist/domains/onboarding/onboarding.d.ts.map +1 -1
- package/dist/domains/onboarding/onboarding.js +47 -59
- 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 +123 -143
- package/dist/domains/profile/founder-profile.d.ts.map +1 -1
- package/dist/domains/profile/founder-profile.js +77 -35
- 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 +19 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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 { locationPickSchema, placeSchema } from '../location/areas.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 /**\n * The member's own location as picked (0.19.0): state, district and\n * locality with their area ids, for Me's location field and the drawer.\n * OWNER-ONLY: no card or member page carries it (they pick `location`).\n * Absent for a location stored as text before 0.19.0.\n */\n place: placeSchema.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 location, 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 * Area ids from the drawer or GPS (0.19.0, `locationPickSchema`): the server\n * names the place and its public city from `master_db`.\n */\n location: locationPickSchema.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. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"support.js","sourceRoot":"","sources":["../../../src/domains/support/support.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema: support\n *\n * A member asks for help; a support agent answers. One conversation per\n * problem, a ticket, with its messages in order.\n *\n * POST /api/v1/support/tickets { subject, category, body }\n * GET /api/v1/support/tickets ?scope&status&assignee&limit&cursor\n * GET /api/v1/support/tickets/:ticketId\n * POST /api/v1/support/tickets/:ticketId/messages { body } reply\n * POST /api/v1/support/tickets/:ticketId/claim an agent takes it\n * POST /api/v1/support/tickets/:ticketId/resolve closed as solved\n * POST /api/v1/support/tickets/:ticketId/reopen the member reopens\n *\n * GET /api/v1/support/saved-replies\n * POST /api/v1/support/saved-replies { title, body }\n * PUT /api/v1/support/saved-replies/:replyId { title, body }\n * DELETE /api/v1/support/saved-replies/:replyId\n *\n * THE STATES\n * open waiting on support: new, or the member wrote last\n * waiting waiting on the member: an agent wrote last\n * resolved closed; the member can reopen it, and writing reopens it\n *\n * `scope=mine` is the tickets the asker raised, whatever role they act as;\n * `scope=queue` is every ticket the active role may read (all of them for a\n * support agent or manager, only their own for a member). An agent replying to\n * an unclaimed ticket claims it.\n *\n * Every message is free text from a person: untrusted, shown as text.\n *\n * @module founder-schema/domains/support\n */\n\nimport { z } from 'zod';\nimport {\n emptyStringToUndefined,\n objectIdSchema,\n paginationQuerySchema,\n} from '../../core/common.js';\nimport {\n SAVED_REPLY_BODY_MAX,\n SAVED_REPLY_TITLE_MAX,\n TICKET_MESSAGE_MAX,\n TICKET_SUBJECT_MAX,\n TICKET_SUBJECT_MIN,\n} from '../../core/limits.js';\nimport { paginatedSchema } from '../../core/responses.js';\n\n/** What the problem is about: routes it, and lets the team see patterns. Append only. */\nexport const TICKET_CATEGORY_VALUES = [\n 'account',\n 'profile',\n 'feed',\n 'chat',\n 'safety',\n 'bug',\n 'other',\n // 0.13.0: payments and refunds (a refund is asked for through Help and\n // support), products, and communities.\n 'billing',\n 'products',\n 'communities',\n] as const;\nexport const ticketCategorySchema = z.enum(TICKET_CATEGORY_VALUES);\nexport type TicketCategory = z.infer<typeof ticketCategorySchema>;\n\nexport const TICKET_STATUS_VALUES = ['open', 'waiting', 'resolved'] as const;\nexport const ticketStatusSchema = z.enum(TICKET_STATUS_VALUES);\nexport type TicketStatus = z.infer<typeof ticketStatusSchema>;\n\nexport const TICKET_SCOPE_VALUES = ['mine', 'queue'] as const;\nexport const ticketScopeSchema = z.enum(TICKET_SCOPE_VALUES);\nexport type TicketScope = z.infer<typeof ticketScopeSchema>;\n\n/** The queue's assignee filter: mine, nobody's, or anyone's. */\nexport const TICKET_ASSIGNEE_FILTER_VALUES = ['me', 'unassigned', 'any'] as const;\nexport const ticketAssigneeFilterSchema = z.enum(TICKET_ASSIGNEE_FILTER_VALUES);\nexport type TicketAssigneeFilter = z.infer<typeof ticketAssigneeFilterSchema>;\n\nconst person = z.object({ id: objectIdSchema, displayName: z.string() });\n\n/** One message. `from` says which side wrote it; the agent's name is shown to the member. */\nexport const ticketMessageSchema = z.object({\n id: objectIdSchema,\n from: z.enum(['member', 'agent']),\n author: person,\n body: z.string(),\n at: z.iso.datetime(),\n});\nexport type TicketMessage = z.infer<typeof ticketMessageSchema>;\n\n/** A ticket in a list. */\nexport const ticketSummarySchema = z.object({\n id: objectIdSchema,\n subject: z.string(),\n category: ticketCategorySchema,\n status: ticketStatusSchema,\n member: person.extend({ avatarUrl: z.url().nullable() }),\n assignee: person.nullable(),\n messageCount: z.number().int().min(1),\n createdAt: z.iso.datetime(),\n lastMessageAt: z.iso.datetime(),\n /** Who wrote last, so a list can say \"Support replied\" or \"Waiting on support\". */\n lastFrom: z.enum(['member', 'agent']),\n});\nexport type TicketSummary = z.infer<typeof ticketSummarySchema>;\n\nexport const ticketPageSchema = paginatedSchema(ticketSummarySchema);\nexport type TicketPage = z.infer<typeof ticketPageSchema>;\n\n/** A ticket with its whole conversation and what the asker may do with it. */\nexport const ticketDetailSchema = ticketSummarySchema.extend({\n messages: z.array(ticketMessageSchema).min(1),\n resolvedAt: z.iso.datetime().nullable(),\n viewer: z.object({\n canReply: z.boolean(),\n canClaim: z.boolean(),\n canResolve: z.boolean(),\n canReopen: z.boolean(),\n /** Replying as the agent side (the reply is shown as Support's). */\n asAgent: z.boolean(),\n }),\n});\nexport type TicketDetail = z.infer<typeof ticketDetailSchema>;\n\n/** `:ticketId` in the paths above. */\nexport const ticketParamsSchema = z.object({ ticketId: objectIdSchema });\nexport type TicketParams = z.infer<typeof ticketParamsSchema>;\n\n/** `GET /support/tickets`. Mine by default; status and assignee narrow it. */\nexport const ticketListQuerySchema = paginationQuerySchema.extend({\n scope: z.preprocess(emptyStringToUndefined, ticketScopeSchema.default('mine')),\n status: z.preprocess(emptyStringToUndefined, ticketStatusSchema.optional()),\n assignee: z.preprocess(emptyStringToUndefined, ticketAssigneeFilterSchema.default('any')),\n});\nexport type TicketListQuery = z.infer<typeof ticketListQuerySchema>;\nexport type TicketListQueryInput = z.input<typeof ticketListQuerySchema>;\n\nconst messageBody = z.string().trim().min(1).max(TICKET_MESSAGE_MAX);\n\n/** `POST /support/tickets`: what went wrong, in the member's words. */\nexport const createTicketBodySchema = z.object({\n subject: z.string().trim().min(TICKET_SUBJECT_MIN).max(TICKET_SUBJECT_MAX),\n category: ticketCategorySchema,\n body: messageBody,\n});\nexport type CreateTicketBody = z.infer<typeof createTicketBodySchema>;\n\n/** `POST /support/tickets/:ticketId/messages`. */\nexport const replyTicketBodySchema = z.object({ body: messageBody });\nexport type ReplyTicketBody = z.infer<typeof replyTicketBodySchema>;\n\n// ─── Saved replies ───────────────────────────────────────────────────────────\n\n/** An answer the support team reuses. Inserted into a reply, then edited, never sent as is. */\nexport const savedReplySchema = z.object({\n id: objectIdSchema,\n title: z.string(),\n body: z.string(),\n author: person,\n updatedAt: z.iso.datetime(),\n viewer: z.object({ canEdit: z.boolean(), canDelete: z.boolean() }),\n});\nexport type SavedReply = z.infer<typeof savedReplySchema>;\n\n/** Every saved reply, by title. Bounded by `SAVED_REPLIES_MAX`, so not paged. */\nexport const savedReplyListSchema = z.object({ items: z.array(savedReplySchema) });\nexport type SavedReplyList = z.infer<typeof savedReplyListSchema>;\n\n/** `:replyId` in the paths above. */\nexport const savedReplyParamsSchema = z.object({ replyId: objectIdSchema });\nexport type SavedReplyParams = z.infer<typeof savedReplyParamsSchema>;\n\n/** `POST` and `PUT /support/saved-replies`. */\nexport const savedReplyBodySchema = z.object({\n title: z.string().trim().min(1).max(SAVED_REPLY_TITLE_MAX),\n body: z.string().trim().min(1).max(SAVED_REPLY_BODY_MAX),\n});\nexport type SavedReplyBody = z.infer<typeof savedReplyBodySchema>;\n"]}
|
|
1
|
+
{"version":3,"file":"support.js","sourceRoot":"","sources":["../../../src/domains/support/support.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema: support\n *\n * A member asks for help; a support agent answers. One conversation per\n * problem, a ticket, with its messages in order.\n *\n * POST /api/v1/support/tickets { subject, category, body }\n * GET /api/v1/support/tickets ?scope&status&assignee&limit&cursor\n * GET /api/v1/support/tickets/:ticketId\n * POST /api/v1/support/tickets/:ticketId/messages { body } reply\n * POST /api/v1/support/tickets/:ticketId/claim an agent takes it\n * POST /api/v1/support/tickets/:ticketId/resolve closed as solved\n * POST /api/v1/support/tickets/:ticketId/reopen the member reopens\n *\n * GET /api/v1/support/saved-replies\n * POST /api/v1/support/saved-replies { title, body }\n * PUT /api/v1/support/saved-replies/:replyId { title, body }\n * DELETE /api/v1/support/saved-replies/:replyId\n *\n * THE STATES, and they ARE whose turn it is (0.18.0: a list worded the turn\n * from `lastFrom`, so a reopened ticket read \"Waiting on member\" while it\n * waited on support):\n * open waiting on support: new, reopened, or the member wrote last\n * waiting waiting on the member: an agent wrote last\n * resolved closed; the member can reopen it, and writing reopens it\n *\n * `scope=mine` is the tickets the asker raised, whatever role they act as;\n * `scope=queue` is every ticket the active role may read (all of them for a\n * support agent or manager, only their own for a member). An agent replying to\n * an unclaimed ticket claims it.\n *\n * Every message is free text from a person: untrusted, shown as text.\n *\n * @module founder-schema/domains/support\n */\n\nimport { z } from 'zod';\nimport {\n emptyStringToUndefined,\n objectIdSchema,\n paginationQuerySchema,\n} from '../../core/common.js';\nimport {\n SAVED_REPLY_BODY_MAX,\n SAVED_REPLY_TITLE_MAX,\n TICKET_MESSAGE_MAX,\n TICKET_SUBJECT_MAX,\n TICKET_SUBJECT_MIN,\n} from '../../core/limits.js';\nimport { paginatedSchema } from '../../core/responses.js';\n\n/** What the problem is about: routes it, and lets the team see patterns. Append only. */\nexport const TICKET_CATEGORY_VALUES = [\n 'account',\n 'profile',\n 'feed',\n 'chat',\n 'safety',\n 'bug',\n 'other',\n // 0.13.0: payments and refunds (a refund is asked for through Help and\n // support), products, and communities.\n 'billing',\n 'products',\n 'communities',\n] as const;\n/**\n * The category a refund ask is filed under (0.18.0): the backend finds a\n * member's ask by it to decide whether the ask came in time, and Payments\n * presets it. One value, typed, so a renamed category fails to compile rather\n * than turning every ask into \"not in time\".\n */\nexport const REFUND_ASK_CATEGORY =\n 'billing' as const satisfies (typeof TICKET_CATEGORY_VALUES)[number];\nexport const ticketCategorySchema = z.enum(TICKET_CATEGORY_VALUES);\nexport type TicketCategory = z.infer<typeof ticketCategorySchema>;\n\nexport const TICKET_STATUS_VALUES = ['open', 'waiting', 'resolved'] as const;\n\n/** The three changes a ticket takes, each its own route (`/claim`, `/resolve`, `/reopen`). */\nexport const TICKET_CHANGE_VALUES = ['claim', 'resolve', 'reopen'] as const;\nexport type TicketChange = (typeof TICKET_CHANGE_VALUES)[number];\nexport const ticketStatusSchema = z.enum(TICKET_STATUS_VALUES);\nexport type TicketStatus = z.infer<typeof ticketStatusSchema>;\n\nexport const TICKET_SCOPE_VALUES = ['mine', 'queue'] as const;\nexport const ticketScopeSchema = z.enum(TICKET_SCOPE_VALUES);\nexport type TicketScope = z.infer<typeof ticketScopeSchema>;\n\n/** The queue's assignee filter: mine, nobody's, or anyone's. */\nexport const TICKET_ASSIGNEE_FILTER_VALUES = ['me', 'unassigned', 'any'] as const;\nexport const ticketAssigneeFilterSchema = z.enum(TICKET_ASSIGNEE_FILTER_VALUES);\nexport type TicketAssigneeFilter = z.infer<typeof ticketAssigneeFilterSchema>;\n\nconst person = z.object({ id: objectIdSchema, displayName: z.string() });\n\n/** Which side wrote a message, and so whose turn it is not (0.18.0: the values, for the backend's model). */\nexport const TICKET_SIDE_VALUES = ['member', 'agent'] as const;\nexport const ticketSideSchema = z.enum(TICKET_SIDE_VALUES);\nexport type TicketSide = z.infer<typeof ticketSideSchema>;\n\n/** One message. `from` says which side wrote it; the agent's name is shown to the member. */\nexport const ticketMessageSchema = z.object({\n id: objectIdSchema,\n from: ticketSideSchema,\n author: person,\n body: z.string(),\n at: z.iso.datetime(),\n});\nexport type TicketMessage = z.infer<typeof ticketMessageSchema>;\n\n/** A ticket in a list. */\nexport const ticketSummarySchema = z.object({\n id: objectIdSchema,\n subject: z.string(),\n category: ticketCategorySchema,\n status: ticketStatusSchema,\n member: person.extend({ avatarUrl: z.url().nullable() }),\n assignee: person.nullable(),\n messageCount: z.number().int().min(1),\n createdAt: z.iso.datetime(),\n lastMessageAt: z.iso.datetime(),\n /** Who wrote last. Whose turn it is comes from `status`, never from this. */\n lastFrom: ticketSideSchema,\n});\nexport type TicketSummary = z.infer<typeof ticketSummarySchema>;\n\nexport const ticketPageSchema = paginatedSchema(ticketSummarySchema);\nexport type TicketPage = z.infer<typeof ticketPageSchema>;\n\n/** A ticket with its whole conversation and what the asker may do with it. */\nexport const ticketDetailSchema = ticketSummarySchema.extend({\n messages: z.array(ticketMessageSchema).min(1),\n resolvedAt: z.iso.datetime().nullable(),\n viewer: z.object({\n canReply: z.boolean(),\n canClaim: z.boolean(),\n canResolve: z.boolean(),\n canReopen: z.boolean(),\n /** Replying as the agent side (the reply is shown as Support's). */\n asAgent: z.boolean(),\n }),\n});\nexport type TicketDetail = z.infer<typeof ticketDetailSchema>;\n\n/** `:ticketId` in the paths above. */\nexport const ticketParamsSchema = z.object({ ticketId: objectIdSchema });\nexport type TicketParams = z.infer<typeof ticketParamsSchema>;\n\n/** `GET /support/tickets`. Mine by default; status and assignee narrow it. */\nexport const ticketListQuerySchema = paginationQuerySchema.extend({\n scope: z.preprocess(emptyStringToUndefined, ticketScopeSchema.default('mine')),\n status: z.preprocess(emptyStringToUndefined, ticketStatusSchema.optional()),\n assignee: z.preprocess(emptyStringToUndefined, ticketAssigneeFilterSchema.default('any')),\n});\nexport type TicketListQuery = z.infer<typeof ticketListQuerySchema>;\nexport type TicketListQueryInput = z.input<typeof ticketListQuerySchema>;\n\nconst messageBody = z.string().trim().min(1).max(TICKET_MESSAGE_MAX);\n\n/** `POST /support/tickets`: what went wrong, in the member's words. */\nexport const createTicketBodySchema = z.object({\n subject: z.string().trim().min(TICKET_SUBJECT_MIN).max(TICKET_SUBJECT_MAX),\n category: ticketCategorySchema,\n body: messageBody,\n});\nexport type CreateTicketBody = z.infer<typeof createTicketBodySchema>;\n\n/** `POST /support/tickets/:ticketId/messages`. */\nexport const replyTicketBodySchema = z.object({ body: messageBody });\nexport type ReplyTicketBody = z.infer<typeof replyTicketBodySchema>;\n\n// ─── Saved replies ───────────────────────────────────────────────────────────\n\n/** An answer the support team reuses. Inserted into a reply, then edited, never sent as is. */\nexport const savedReplySchema = z.object({\n id: objectIdSchema,\n title: z.string(),\n body: z.string(),\n author: person,\n updatedAt: z.iso.datetime(),\n viewer: z.object({ canEdit: z.boolean(), canDelete: z.boolean() }),\n});\nexport type SavedReply = z.infer<typeof savedReplySchema>;\n\n/** Every saved reply, by title. Bounded by `SAVED_REPLIES_MAX`, so not paged. */\nexport const savedReplyListSchema = z.object({ items: z.array(savedReplySchema) });\nexport type SavedReplyList = z.infer<typeof savedReplyListSchema>;\n\n/** `:replyId` in the paths above. */\nexport const savedReplyParamsSchema = z.object({ replyId: objectIdSchema });\nexport type SavedReplyParams = z.infer<typeof savedReplyParamsSchema>;\n\n/** `POST` and `PUT /support/saved-replies`. */\nexport const savedReplyBodySchema = z.object({\n title: z.string().trim().min(1).max(SAVED_REPLY_TITLE_MAX),\n body: z.string().trim().min(1).max(SAVED_REPLY_BODY_MAX),\n});\nexport type SavedReplyBody = z.infer<typeof savedReplyBodySchema>;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"team.d.ts","sourceRoot":"","sources":["../../../src/domains/team/team.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,sCAAsC;AACtC,eAAO,MAAM,sBAAsB;;iBAAyC,CAAC;AAC7E,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,6DAA6D;AAC7D,eAAO,MAAM,gBAAgB;;;;;;;;;;;;iBAS3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;iBAAoC,CAAC;AAChE,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;;;;;iBAOnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,+FAA+F;AAC/F,eAAO,MAAM,4BAA4B;;;;;;;;iBAGvC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B;;;;
|
|
1
|
+
{"version":3,"file":"team.d.ts","sourceRoot":"","sources":["../../../src/domains/team/team.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,sCAAsC;AACtC,eAAO,MAAM,sBAAsB;;iBAAyC,CAAC;AAC7E,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,6DAA6D;AAC7D,eAAO,MAAM,gBAAgB;;;;;;;;;;;;iBAS3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;iBAAoC,CAAC;AAChE,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;;;;;iBAOnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,+FAA+F;AAC/F,eAAO,MAAM,4BAA4B;;;;;;;;iBAGvC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B;;;;iBAI1C,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAExF,8DAA8D;AAC9D,eAAO,MAAM,mCAAmC;;;;;iBAG9C,CAAC;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mCAAmC,CAAC,CAAC"}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { z } from 'zod';
|
|
19
19
|
import { CAPABILITY_VALUES, capabilitySchema } from '../../access/capabilities.js';
|
|
20
20
|
import { PLATFORM_ROLE_VALUES, platformRoleSchema } from '../../access/roles.js';
|
|
21
|
-
import { objectIdSchema } from '../../core/common.js';
|
|
21
|
+
import { eachOnce, objectIdSchema } from '../../core/common.js';
|
|
22
22
|
import { paginatedSchema } from '../../core/responses.js';
|
|
23
23
|
import { VALIDATION_MESSAGE } from '../../core/validation.js';
|
|
24
24
|
/** `:memberId` in the paths above. */
|
|
@@ -44,7 +44,7 @@ export const setMemberRolesBodySchema = z.object({
|
|
|
44
44
|
roles: z
|
|
45
45
|
.array(platformRoleSchema.exclude(['superadmin']))
|
|
46
46
|
.max(PLATFORM_ROLE_VALUES.length)
|
|
47
|
-
.refine(
|
|
47
|
+
.refine(eachOnce, {
|
|
48
48
|
error: VALIDATION_MESSAGE.rolesDuplicate,
|
|
49
49
|
}),
|
|
50
50
|
});
|
|
@@ -61,10 +61,7 @@ export const setMemberRolesResponseSchema = z.object({
|
|
|
61
61
|
* Sending what they already hold changes nothing and writes no audit entry.
|
|
62
62
|
*/
|
|
63
63
|
export const setMemberCapabilitiesBodySchema = z.object({
|
|
64
|
-
capabilities: z
|
|
65
|
-
.array(capabilitySchema)
|
|
66
|
-
.max(CAPABILITY_VALUES.length)
|
|
67
|
-
.refine((list) => new Set(list).size === list.length, {
|
|
64
|
+
capabilities: z.array(capabilitySchema).max(CAPABILITY_VALUES.length).refine(eachOnce, {
|
|
68
65
|
error: VALIDATION_MESSAGE.capabilitiesDuplicate,
|
|
69
66
|
}),
|
|
70
67
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"team.js","sourceRoot":"","sources":["../../../src/domains/team/team.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACnF,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"team.js","sourceRoot":"","sources":["../../../src/domains/team/team.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACnF,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,sCAAsC;AACtC,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAC;AAG7E,6DAA6D;AAC7D,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,EAAE,EAAE,cAAc;IAClB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,+FAA+F;IAC/F,SAAS,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC7B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,qEAAqE;IACrE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACzC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;CACrB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,cAAc,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAC;AAGhE;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,KAAK,EAAE,CAAC;SACL,KAAK,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;SACjD,GAAG,CAAC,oBAAoB,CAAC,MAAM,CAAC;SAChC,MAAM,CAAC,QAAQ,EAAE;QAChB,KAAK,EAAE,kBAAkB,CAAC,cAAc;KACzC,CAAC;CACL,CAAC,CAAC;AAGH,+FAA+F;AAC/F,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAAC;IACnD,EAAE,EAAE,cAAc;IAClB,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC;CACnC,CAAC,CAAC;AAGH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,CAAC,MAAM,CAAC;IACtD,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE;QACrF,KAAK,EAAE,kBAAkB,CAAC,qBAAqB;KAChD,CAAC;CACH,CAAC,CAAC;AAGH,8DAA8D;AAC9D,MAAM,CAAC,MAAM,mCAAmC,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1D,EAAE,EAAE,cAAc;IAClB,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC;CACxC,CAAC,CAAC","sourcesContent":["/**\n * founder-schema: the staff team\n *\n * GET /api/v1/team the people holding a platform role\n * PUT /api/v1/team/:memberId/roles set someone's platform roles\n * PUT /api/v1/team/:memberId/capabilities set someone's capabilities (0.9.0)\n * DELETE /api/v1/team/:memberId/mpin clear a forgotten MPIN (superadmin;\n * answers `IdResponse`)\n *\n * Setting roles sends the person's WHOLE set, so the request says what they\n * should end up with rather than a list of changes to replay. Every role that\n * changes must be one the actor's active role may grant (`ROLE_GRANTED_BY`);\n * nobody changes their own; superadmin is never in a request. Each change is\n * written to the audit log.\n *\n * @module founder-schema/domains/team\n */\n\nimport { z } from 'zod';\nimport { CAPABILITY_VALUES, capabilitySchema } from '../../access/capabilities.js';\nimport { PLATFORM_ROLE_VALUES, platformRoleSchema } from '../../access/roles.js';\nimport { eachOnce, objectIdSchema } from '../../core/common.js';\nimport { paginatedSchema } from '../../core/responses.js';\nimport { VALIDATION_MESSAGE } from '../../core/validation.js';\n\n/** `:memberId` in the paths above. */\nexport const teamMemberParamsSchema = z.object({ memberId: objectIdSchema });\nexport type TeamMemberParams = z.infer<typeof teamMemberParamsSchema>;\n\n/** One person on the team, as the Team screens list them. */\nexport const teamMemberSchema = z.object({\n id: objectIdSchema,\n displayName: z.string(),\n /** Server-set or from the login provider, like every avatar on the wire: a plain URL check. */\n avatarUrl: z.url().nullable(),\n email: z.string().nullable(),\n /** Their platform roles, highest first. Never empty in this list. */\n roles: z.array(platformRoleSchema).min(1),\n mpinSet: z.boolean(),\n});\nexport type TeamMember = z.infer<typeof teamMemberSchema>;\n\nexport const teamPageSchema = paginatedSchema(teamMemberSchema);\nexport type TeamPage = z.infer<typeof teamPageSchema>;\n\n/**\n * `PUT /team/:memberId/roles`: the platform roles the person should hold.\n * Empty takes every staff role away. No duplicates; no superadmin (a server\n * script grants it, never the product).\n */\nexport const setMemberRolesBodySchema = z.object({\n roles: z\n .array(platformRoleSchema.exclude(['superadmin']))\n .max(PLATFORM_ROLE_VALUES.length)\n .refine(eachOnce, {\n error: VALIDATION_MESSAGE.rolesDuplicate,\n }),\n});\nexport type SetMemberRolesBody = z.infer<typeof setMemberRolesBodySchema>;\n\n/** What `PUT /team/:memberId/roles` answers: the roles the person now holds, highest first. */\nexport const setMemberRolesResponseSchema = z.object({\n id: objectIdSchema,\n roles: z.array(platformRoleSchema),\n});\nexport type SetMemberRolesResponse = z.infer<typeof setMemberRolesResponseSchema>;\n\n/**\n * `PUT /team/:memberId/capabilities` (0.9.0): the capabilities the person\n * should hold, as a whole set. Every one that changes must be one the actor's\n * active role may grant (`CAPABILITY_GRANTED_BY`, else\n * `CAPABILITY_NOT_GRANTABLE`); never one's own (`CANNOT_CHANGE_OWN_CAPABILITIES`).\n * Sending what they already hold changes nothing and writes no audit entry.\n */\nexport const setMemberCapabilitiesBodySchema = z.object({\n capabilities: z.array(capabilitySchema).max(CAPABILITY_VALUES.length).refine(eachOnce, {\n error: VALIDATION_MESSAGE.capabilitiesDuplicate,\n }),\n});\nexport type SetMemberCapabilitiesBody = z.infer<typeof setMemberCapabilitiesBodySchema>;\n\n/** What it answers: the capabilities the person now holds. */\nexport const setMemberCapabilitiesResponseSchema = z.object({\n id: objectIdSchema,\n capabilities: z.array(capabilitySchema),\n});\nexport type SetMemberCapabilitiesResponse = z.infer<typeof setMemberCapabilitiesResponseSchema>;\n"]}
|
|
@@ -19,16 +19,13 @@ import { z } from 'zod';
|
|
|
19
19
|
* For a file input's `accept`, and for a file whose `type` the browser left
|
|
20
20
|
* empty. Images only: video is shared as a link in the post text, a product
|
|
21
21
|
* decision of 2026-09-21, not an unfinished uploader.
|
|
22
|
+
*
|
|
23
|
+
* 0.18.0: named for what they are (they were `UPLOAD_KEY_*`, from the 0.4.0
|
|
24
|
+
* storage-key format that no route has taken since 0.5.0, now removed).
|
|
22
25
|
*/
|
|
23
|
-
export declare const
|
|
24
|
-
export
|
|
25
|
-
|
|
26
|
-
jpeg: "jpeg";
|
|
27
|
-
png: "png";
|
|
28
|
-
webp: "webp";
|
|
29
|
-
gif: "gif";
|
|
30
|
-
}>;
|
|
31
|
-
export type UploadKeyExtension = z.infer<typeof uploadKeyExtensionSchema>;
|
|
26
|
+
export declare const UPLOAD_FILE_EXTENSIONS: readonly ["jpg", "jpeg", "png", "webp", "gif"];
|
|
27
|
+
export type UploadFileExtension = (typeof UPLOAD_FILE_EXTENSIONS)[number];
|
|
28
|
+
/** Each extension's content type: one of `MEDIA_UPLOAD_CONTENT_TYPES`, which a presign accepts. */
|
|
32
29
|
export declare const UPLOAD_CONTENT_TYPES: {
|
|
33
30
|
readonly jpg: "image/jpeg";
|
|
34
31
|
readonly jpeg: "image/jpeg";
|
|
@@ -36,18 +33,6 @@ export declare const UPLOAD_CONTENT_TYPES: {
|
|
|
36
33
|
readonly webp: "image/webp";
|
|
37
34
|
readonly gif: "image/gif";
|
|
38
35
|
};
|
|
39
|
-
/**
|
|
40
|
-
* @deprecated Since 0.5.0 no route takes or returns a storage key: a client
|
|
41
|
-
* carries a media id (`objectIdSchema`). Kept only because this package never
|
|
42
|
-
* removes an export without a ruling (TEAM.md 5a, `public-surface.test.ts`).
|
|
43
|
-
*/
|
|
44
|
-
export declare const UPLOAD_KEY_MAX_CHARS = 300;
|
|
45
|
-
/** @deprecated See `UPLOAD_KEY_MAX_CHARS`. The 0.4.0 key format, `media/<userId>/<uuid>.<ext>`. */
|
|
46
|
-
export declare const UPLOAD_KEY_PATTERN: RegExp;
|
|
47
|
-
/** @deprecated See `UPLOAD_KEY_MAX_CHARS`. */
|
|
48
|
-
export declare const uploadKeySchema: z.ZodString;
|
|
49
|
-
/** @deprecated See `UPLOAD_KEY_MAX_CHARS`. */
|
|
50
|
-
export type UploadKey = z.infer<typeof uploadKeySchema>;
|
|
51
36
|
/**
|
|
52
37
|
* Asking for an upload slot: `POST /api/v1/uploads/presign`.
|
|
53
38
|
*
|
|
@@ -89,7 +74,7 @@ export type UploadPresignBody = z.infer<typeof uploadPresignBodySchema>;
|
|
|
89
74
|
* processing the same upload; complete again in a moment.
|
|
90
75
|
*/
|
|
91
76
|
export declare const uploadPresignResponseSchema: z.ZodObject<{
|
|
92
|
-
mediaId: z.ZodString
|
|
77
|
+
mediaId: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
93
78
|
url: z.ZodURL;
|
|
94
79
|
method: z.ZodLiteral<"PUT">;
|
|
95
80
|
headers: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploads.d.ts","sourceRoot":"","sources":["../../../src/domains/uploads/uploads.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"uploads.d.ts","sourceRoot":"","sources":["../../../src/domains/uploads/uploads.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,gDAAiD,CAAC;AACrF,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1E,mGAAmG;AACnG,eAAO,MAAM,oBAAoB;;;;;;CAMuC,CAAC;AAEzE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;iBAIlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,2BAA2B;;;;;;iBAMtC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAEhF,iFAAiF;AACjF,eAAO,MAAM,kBAAkB;;iBAAwC,CAAC;AACxE,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
|