founder-schema 0.12.0 → 0.14.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 +40 -0
- package/dist/core/errors.d.ts +7 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +13 -0
- package/dist/core/errors.js.map +1 -1
- package/dist/core/responses.d.ts +6 -0
- package/dist/core/responses.d.ts.map +1 -1
- package/dist/domains/billing/billing.d.ts +5 -1
- package/dist/domains/billing/billing.d.ts.map +1 -1
- package/dist/domains/billing/billing.js +15 -2
- package/dist/domains/billing/billing.js.map +1 -1
- package/dist/domains/communities/communities.d.ts +2 -0
- package/dist/domains/communities/communities.d.ts.map +1 -1
- package/dist/domains/communities/communities.js +7 -0
- package/dist/domains/communities/communities.js.map +1 -1
- package/dist/domains/contributions/contributions.d.ts +2 -2
- package/dist/domains/contributions/contributions.d.ts.map +1 -1
- package/dist/domains/contributions/contributions.js +3 -2
- package/dist/domains/contributions/contributions.js.map +1 -1
- package/dist/domains/membership/membership.d.ts +11 -2
- package/dist/domains/membership/membership.d.ts.map +1 -1
- package/dist/domains/membership/membership.js +16 -2
- package/dist/domains/membership/membership.js.map +1 -1
- package/dist/domains/onboarding/onboarding.d.ts +0 -10
- package/dist/domains/onboarding/onboarding.d.ts.map +1 -1
- package/dist/domains/onboarding/onboarding.js +9 -3
- package/dist/domains/onboarding/onboarding.js.map +1 -1
- package/dist/domains/profile/founder-profile.d.ts +9 -3
- package/dist/domains/profile/founder-profile.d.ts.map +1 -1
- package/dist/domains/profile/founder-profile.js +6 -3
- package/dist/domains/profile/founder-profile.js.map +1 -1
- package/dist/domains/support/support.d.ts +16 -1
- package/dist/domains/support/support.d.ts.map +1 -1
- package/dist/domains/support/support.js +5 -0
- package/dist/domains/support/support.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"communities.js","sourceRoot":"","sources":["../../../src/domains/communities/communities.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema — Communities: official spaces inside Founder Circle (0.9.0)\n *\n * THE USER'S DECISIONS (2026-10-08), each asked on its own:\n *\n * - OFFICIAL: managers, super admins, and members given the\n * `create_communities` capability create them. Everyone else SUGGESTS one;\n * managers and super admins review the suggestions.\n * - THREE TYPES, three separate questions answered per type:\n *\n * public private hidden\n * listed in Discover yes yes no\n * header (name, about, signed-in signed-in members and\n * rules, admins, count) members members invitees (404 otherwise)\n * posts, polls, members signed-in members only members only\n * joining one tap request, then invite only,\n * approval then accept\n *\n * Taking part (post, poll, comment, react, vote, save, report) is for\n * members of every type. \"Members\" means ACTIVE members: someone who\n * requested, was invited or is banned is a non-member for every rule.\n * - PUBLIC MEANS PUBLIC INSIDE FOUNDER CIRCLE: a signed-out visitor sees no\n * community content of any type, even by a shared link.\n * - THE HOME FEED adds only the communities the member joined.\n * - NO CHAT IN 0.9.0 (feed-first). Announcements are pinned posts\n * (`COMMUNITY_PINS_MAX`), optionally notifying every member.\n *\n * A banned member sees the community as hidden (404 everywhere) until\n * unbanned. Platform staff acting as moderator, manager or super admin READ\n * every community, as they read every post, and moderate its content through\n * the staff moderation queue; to take part they join like anyone else.\n *\n * Refusals follow what the caller can SEE: 404 for something they may not\n * see (the same as for something that does not exist, so nothing is\n * probed), 403 `COMMUNITY_JOIN_REQUIRED` for something they see but must join\n * to read or to take part in.\n *\n * Content in a community carries no product and no ask, and cannot be\n * reposted (a repost is general content, readable signed out): two leak paths\n * closed by construction. Design: founder-circle-backend `docs/communities.md`.\n *\n * Inside a community:\n *\n * member moderator admin manager,\n * super admin\n * post, poll, comment, react, vote ✓ ✓ ✓ ✓ (a member)\n * approve requests, invite, pin, hide ✓ ✓ ✓\n * remove a member (not a moderator) ✓ ✓ ✓\n * remove a moderator ✓ ✓\n * remove or ban an admin ✓\n * notify all, ban, roles, edit, type ✓ ✓\n * archive, delete ✓\n *\n * An admin may unmake another admin (then act on them as on a member). Each\n * member row says what this viewer may do to it (`communityMemberSchema.viewer`):\n * a client never works it out from roles.\n *\n * A community always keeps an admin while anyone can be one\n * (`COMMUNITY_LAST_ADMIN`): an admin leaves, steps down, or deletes their\n * account only once someone else is admin (the user's rule, 2026-10-08). When\n * nobody else is active, or the account was deleted where it cannot be\n * refused (central sign-in), the community is ORPHANED: managers and super\n * admins run it (they can act on any community), it is flagged in the audit\n * log (`community.orphaned`), and the page says so to them (`orphaned`).\n *\n * Routes (all signed in):\n * GET /api/v1/communities communityDiscoverQuerySchema -> communityCardPageSchema\n * GET /api/v1/communities/mine paginationQuerySchema -> communityCardPageSchema\n * GET /api/v1/communities/invites communityInvitesSchema (mine, waiting)\n * GET /api/v1/communities/orphaned paginationQuerySchema -> communityCardPageSchema\n * (managers, super admins: communities left with no admin, newest first)\n * POST /api/v1/communities createCommunityBodySchema -> IdResponse\n * GET /api/v1/communities/:communityId communitySchema\n * PATCH /api/v1/communities/:communityId updateCommunityBodySchema -> communitySchema\n * PUT /api/v1/communities/:communityId/icon setCommunityIconBodySchema -> communitySchema\n * DELETE /api/v1/communities/:communityId/icon -> communitySchema\n * PUT /api/v1/communities/:communityId/visibility setCommunityVisibilityBodySchema -> communitySchema\n * PUT /api/v1/communities/:communityId/status setCommunityStatusBodySchema -> communitySchema (staff)\n * DELETE /api/v1/communities/:communityId -> IdResponse (staff)\n * GET /api/v1/communities/:communityId/feed paginationQuerySchema -> FeedPage (newest first;\n * pinned items are in it too, in their place)\n * GET /api/v1/communities/:communityId/pinned PinnedItems (feed)\n * POST /api/v1/communities/:communityId/pins pinCommunityContentBodySchema -> PinnedItems\n * DELETE /api/v1/communities/:communityId/pins/:contentId -> PinnedItems\n * POST /api/v1/communities/:communityId/join -> communitySchema (public: joined; private: requested)\n * DELETE /api/v1/communities/:communityId/join cancel a request -> communitySchema\n * POST /api/v1/communities/:communityId/leave -> IdResponse\n * GET /api/v1/communities/:communityId/members communityMembersQuerySchema -> communityMemberPageSchema\n * GET /api/v1/communities/:communityId/requests paginationQuerySchema -> communityRequestPageSchema\n * PUT /api/v1/communities/:communityId/requests/:userId approve -> IdResponse\n * DELETE /api/v1/communities/:communityId/requests/:userId decline -> IdResponse\n * GET /api/v1/communities/:communityId/invites paginationQuerySchema -> communityPendingInvitePageSchema\n * POST /api/v1/communities/:communityId/invites inviteCommunityMemberBodySchema -> communityPendingInviteSchema\n * DELETE /api/v1/communities/:communityId/invites/:userId cancel -> IdResponse\n * PUT /api/v1/communities/:communityId/invite accept (the invitee) -> communitySchema\n * DELETE /api/v1/communities/:communityId/invite decline (the invitee) -> IdResponse\n * PUT /api/v1/communities/:communityId/members/:userId/role setCommunityMemberRoleBodySchema -> IdResponse\n * DELETE /api/v1/communities/:communityId/members/:userId remove -> IdResponse\n * GET /api/v1/communities/:communityId/bans paginationQuerySchema -> communityBanPageSchema\n * POST /api/v1/communities/:communityId/bans banCommunityMemberBodySchema -> IdResponse\n * DELETE /api/v1/communities/:communityId/bans/:userId unban -> IdResponse\n * PUT /api/v1/communities/:communityId/content/:contentId/hidden hide in the community -> IdResponse\n * DELETE /api/v1/communities/:communityId/content/:contentId/hidden restore -> IdResponse\n * PUT /api/v1/communities/:communityId/comments/:commentId/hidden hide a comment in it -> IdResponse\n * DELETE /api/v1/communities/:communityId/comments/:commentId/hidden restore -> IdResponse\n * GET /api/v1/communities/:communityId/hidden paginationQuerySchema -> FeedPage (moderators:\n * what ITS moderators hid, newest first; a staff hide is staff's to undo and is not listed)\n * GET /api/v1/communities/:communityId/hidden/comments paginationQuerySchema -> commentPageSchema\n * POST /api/v1/community-suggestions createCommunitySuggestionBodySchema -> communitySuggestionSchema\n * GET /api/v1/community-suggestions/mine paginationQuerySchema -> communitySuggestionPageSchema\n * GET /api/v1/community-suggestions communitySuggestionsQuerySchema -> communitySuggestionPageSchema (staff)\n * POST /api/v1/community-suggestions/:suggestionId/approve createCommunityBodySchema -> IdResponse (the community)\n * POST /api/v1/community-suggestions/:suggestionId/decline declineCommunitySuggestionBodySchema -> communitySuggestionSchema\n * (posting into a community: `communityId` on the feed's create bodies)\n *\n * @module founder-schema/domains/communities\n */\n\nimport { z } from 'zod';\nimport {\n emptyStringToUndefined,\n multiValueQuery,\n objectIdSchema,\n optionalTrimmedString,\n paginationQuerySchema,\n} from '../../core/common.js';\nimport {\n COMMUNITY_BAN_REASON_MAX,\n COMMUNITY_DECLINE_REASON_MAX,\n COMMUNITY_DECLINE_REASON_MIN,\n COMMUNITY_DESCRIPTION_MAX,\n COMMUNITY_NAME_MAX,\n COMMUNITY_NAME_MIN,\n COMMUNITY_RULE_MAX,\n COMMUNITY_RULES_MAX,\n COMMUNITY_SUGGESTION_WHY_MAX,\n DISCOVER_FILTER_VALUES_MAX,\n DISCOVER_TEXT_MAX,\n} from '../../core/limits.js';\nimport { paginatedSchema } from '../../core/responses.js';\nimport { VALIDATION_MESSAGE } from '../../core/validation.js';\nimport { mediaImageSchema } from '../media/media.js';\nimport { topicSchema } from '../onboarding/onboarding.js';\nimport { memberBylineSchema } from '../profile/founder-profile.js';\n\n/** Topics a community carries: what Discover filters by. */\nexport const COMMUNITY_TOPICS_MAX = 3;\n/** Announcements pinned to the top of one community at once. */\nexport const COMMUNITY_PINS_MAX = 3;\n/** Suggestions one member may have waiting for review at once. */\nexport const COMMUNITY_SUGGESTIONS_OPEN_MAX = 3;\n/**\n * The shortest time between two notify-everyone announcements in one\n * community, so members are not spammed (`COMMUNITY_ANNOUNCE_TOO_SOON`).\n */\nexport const COMMUNITY_ANNOUNCE_INTERVAL_MS = 24 * 60 * 60 * 1000;\n\n// ─── Vocabulary ──────────────────────────────────────────────────────────────\n// ⚠ Every list below is STORED. Append only; removing a value orphans rows.\n\n/** See the module note's table. */\nexport const COMMUNITY_VISIBILITY_VALUES = ['public', 'private', 'hidden'] as const;\nexport const communityVisibilitySchema = z.enum(COMMUNITY_VISIBILITY_VALUES);\nexport type CommunityVisibility = z.infer<typeof communityVisibilitySchema>;\n\n/**\n * How private each type is, least first. A change to a LOWER rank exposes what\n * was already posted, so it needs `confirmExposure` (and is audited).\n */\nexport const COMMUNITY_VISIBILITY_RANK = {\n public: 0,\n private: 1,\n hidden: 2,\n} as const satisfies Record<CommunityVisibility, number>;\n\n/** Whether moving from one type to another exposes what was already there. */\nexport function communityChangeExposes(\n from: CommunityVisibility,\n to: CommunityVisibility,\n): boolean {\n return COMMUNITY_VISIBILITY_RANK[to] < COMMUNITY_VISIBILITY_RANK[from];\n}\n\n/** A member's role inside one community. */\nexport const COMMUNITY_ROLE_VALUES = ['admin', 'moderator', 'member'] as const;\nexport const communityRoleSchema = z.enum(COMMUNITY_ROLE_VALUES);\nexport type CommunityRole = z.infer<typeof communityRoleSchema>;\n\n/**\n * Where someone stands with one community. `requested` (private: waiting for\n * approval), `invited` (waiting for their answer), `banned` (the community is\n * hidden from them). Leaving or being removed deletes the row.\n */\nexport const COMMUNITY_MEMBER_STATUS_VALUES = ['active', 'requested', 'invited', 'banned'] as const;\nexport const communityMemberStatusSchema = z.enum(COMMUNITY_MEMBER_STATUS_VALUES);\nexport type CommunityMemberStatus = z.infer<typeof communityMemberStatusSchema>;\n\n/** `archived`: read-only and out of Discover; members still read it. Staff only. */\nexport const COMMUNITY_STATUS_VALUES = ['active', 'archived'] as const;\nexport const communityStatusSchema = z.enum(COMMUNITY_STATUS_VALUES);\nexport type CommunityStatus = z.infer<typeof communityStatusSchema>;\n\nexport const COMMUNITY_SUGGESTION_STATUS_VALUES = ['open', 'approved', 'declined'] as const;\nexport const communitySuggestionStatusSchema = z.enum(COMMUNITY_SUGGESTION_STATUS_VALUES);\nexport type CommunitySuggestionStatus = z.infer<typeof communitySuggestionStatusSchema>;\n\n// ─── Read shapes ─────────────────────────────────────────────────────────────\n\n/**\n * A community named on something else: \"in AI Builders\" on a post or poll, a\n * notification. `iconUrl` is the icon's smallest size.\n */\nexport const communityChipSchema = z.object({\n id: objectIdSchema,\n /** For a readable URL, `/communities/<id>/<slug>`. The id resolves; a renamed community keeps its links. */\n slug: z.string(),\n name: z.string(),\n iconUrl: z.url().optional(),\n visibility: communityVisibilitySchema,\n /** Present (true) once it is archived: read only, nobody takes part. */\n archived: z.boolean().optional(),\n});\nexport type CommunityChip = z.infer<typeof communityChipSchema>;\n\n/** Where THIS viewer stands with a community, on a card. Banned viewers never see it. */\nexport const communityCardViewerSchema = z.object({\n /** Null: not a member and nothing pending. Never `banned` (they get 404). */\n status: communityMemberStatusSchema.exclude(['banned']).nullable(),\n /** Their role while active, else null. */\n role: communityRoleSchema.nullable(),\n});\nexport type CommunityCardViewer = z.infer<typeof communityCardViewerSchema>;\n\n/** A community on Discover and in \"your communities\". */\nexport const communityCardSchema = z.object({\n id: objectIdSchema,\n slug: z.string(),\n name: z.string(),\n description: z.string(),\n icon: mediaImageSchema.optional(),\n visibility: communityVisibilitySchema,\n status: communityStatusSchema,\n topics: z.array(topicSchema).max(COMMUNITY_TOPICS_MAX),\n /** Active members. */\n memberCount: z.number().int().nonnegative(),\n /** Visible posts and polls in it. */\n postCount: z.number().int().nonnegative(),\n viewer: communityCardViewerSchema.optional(),\n});\nexport type CommunityCard = z.infer<typeof communityCardSchema>;\n\n/**\n * What THIS viewer may do on the community page, decided by the server and\n * sent as plain answers (the rule of `viewerStateSchema` on a post): a client\n * never derives a power from `role`.\n */\nexport const communityViewerSchema = communityCardViewerSchema.extend({\n /** Read its posts, polls and comments now. False on a private one before joining. */\n canRead: z.boolean(),\n /** Read its member list. */\n canSeeMembers: z.boolean(),\n /** Public, active, and the viewer has no row (nothing pending): offer Join. */\n canJoin: z.boolean(),\n /** Private, active, and the viewer has no row: offer Request to join. */\n canRequest: z.boolean(),\n /** Active member, community not archived: the composer. */\n canPost: z.boolean(),\n /** Active member who is not its last admin. */\n canLeave: z.boolean(),\n /**\n * Moderators and admins (and managers, super admins): requests, invites,\n * pins, hiding, removing members. On an ARCHIVED community approving,\n * inviting, accepting and pinning answer `COMMUNITY_ARCHIVED`; declining,\n * cancelling, removing, banning, roles and hiding still work.\n */\n canModerate: z.boolean(),\n /** Admins (and managers, super admins): details, icon, type, roles, bans. */\n canManage: z.boolean(),\n /** May notify every member with an announcement now (admins, at most once per `COMMUNITY_ANNOUNCE_INTERVAL_MS`). */\n canAnnounce: z.boolean(),\n /** Managers and super admins: archive, restore, delete. */\n canArchive: z.boolean(),\n});\nexport type CommunityViewer = z.infer<typeof communityViewerSchema>;\n\n/**\n * One person in a community, live-joined like every byline. In the member\n * list, admins first, then moderators, then members, each by when they joined.\n * `viewer` (0.9.0, on the member list only) is what THIS viewer may do to\n * them, the server's own rules (who outranks whom, never oneself, never the\n * last admin).\n */\nexport const communityMemberSchema = memberBylineSchema.extend({\n role: communityRoleSchema,\n joinedAt: z.iso.datetime(),\n viewer: z\n .object({\n canSetRole: z.boolean(),\n canRemove: z.boolean(),\n canBan: z.boolean(),\n })\n .optional(),\n});\nexport type CommunityMember = z.infer<typeof communityMemberSchema>;\n\n/** The community page. The header is what a non-member of a private one may see too. */\nexport const communitySchema = communityCardSchema.extend({\n rules: z.array(z.string()).max(COMMUNITY_RULES_MAX),\n /**\n * Its admins and moderators, admins first, by when they joined: who to ask.\n * EMPTY for a viewer who may not read it (a private community before\n * joining, a hidden one's invitee): who is in it is its members' to see.\n */\n team: z.array(communityMemberSchema),\n createdAt: z.iso.datetime(),\n updatedAt: z.iso.datetime(),\n archivedAt: z.iso.datetime().nullable(),\n /**\n * Only for managers and super admins: it has no admin left (its last one's\n * account was deleted where that cannot be refused), so staff run it until\n * they make a member admin, which clears it.\n */\n orphaned: z.boolean().optional(),\n viewer: communityViewerSchema.optional(),\n});\nexport type Community = z.infer<typeof communitySchema>;\n\n/** A request to join a private community, as its moderators see it. */\nexport const communityRequestSchema = z.object({\n member: memberBylineSchema,\n requestedAt: z.iso.datetime(),\n});\nexport type CommunityRequest = z.infer<typeof communityRequestSchema>;\n\n/** An invitation nobody has answered yet, as its moderators see it. */\nexport const communityPendingInviteSchema = z.object({\n member: memberBylineSchema,\n /** Null when whoever invited has since deleted their account. */\n invitedBy: memberBylineSchema.nullable(),\n invitedAt: z.iso.datetime(),\n});\nexport type CommunityPendingInvite = z.infer<typeof communityPendingInviteSchema>;\n\n/** An invitation waiting for the viewer's answer. */\nexport const communityInviteSchema = z.object({\n community: communityChipSchema.extend({ description: z.string() }),\n invitedBy: memberBylineSchema.nullable(),\n invitedAt: z.iso.datetime(),\n});\nexport type CommunityInvite = z.infer<typeof communityInviteSchema>;\n\n/** `GET /communities/invites`. Never paged: invitations are few, and each needs an answer. */\nexport const communityInvitesSchema = z.object({ items: z.array(communityInviteSchema) });\nexport type CommunityInvites = z.infer<typeof communityInvitesSchema>;\n\n/** Someone banned from a community, as its admins see it. */\nexport const communityBanSchema = z.object({\n member: memberBylineSchema,\n bannedBy: memberBylineSchema.nullable(),\n bannedAt: z.iso.datetime(),\n /** For the admins' own record; never shown to the banned member. */\n reason: z.string().max(COMMUNITY_BAN_REASON_MAX).optional(),\n});\nexport type CommunityBan = z.infer<typeof communityBanSchema>;\n\n/** A suggestion, as its author and the reviewers see it. */\nexport const communitySuggestionSchema = z.object({\n id: objectIdSchema,\n /** Null when its author has since deleted their account. */\n suggestedBy: memberBylineSchema.nullable(),\n name: z.string(),\n description: z.string(),\n why: z.string().optional(),\n visibility: communityVisibilitySchema,\n topics: z.array(topicSchema).max(COMMUNITY_TOPICS_MAX),\n status: communitySuggestionStatusSchema,\n decidedBy: memberBylineSchema.nullable(),\n decidedAt: z.iso.datetime().nullable(),\n /** Why it was declined, shown to its author. */\n declineReason: z.string().optional(),\n /**\n * The community it became, once approved. Absent if that community is since\n * deleted, or the viewer may no longer see it (banned from a hidden one).\n */\n community: communityChipSchema.optional(),\n createdAt: z.iso.datetime(),\n});\nexport type CommunitySuggestion = z.infer<typeof communitySuggestionSchema>;\n\nexport const communityCardPageSchema = paginatedSchema(communityCardSchema);\nexport type CommunityCardPage = z.infer<typeof communityCardPageSchema>;\nexport const communityMemberPageSchema = paginatedSchema(communityMemberSchema);\nexport type CommunityMemberPage = z.infer<typeof communityMemberPageSchema>;\nexport const communityRequestPageSchema = paginatedSchema(communityRequestSchema);\nexport type CommunityRequestPage = z.infer<typeof communityRequestPageSchema>;\nexport const communityPendingInvitePageSchema = paginatedSchema(communityPendingInviteSchema);\nexport type CommunityPendingInvitePage = z.infer<typeof communityPendingInvitePageSchema>;\nexport const communityBanPageSchema = paginatedSchema(communityBanSchema);\nexport type CommunityBanPage = z.infer<typeof communityBanPageSchema>;\nexport const communitySuggestionPageSchema = paginatedSchema(communitySuggestionSchema);\nexport type CommunitySuggestionPage = z.infer<typeof communitySuggestionPageSchema>;\n\n// ─── Queries and params ──────────────────────────────────────────────────────\n\n/**\n * `GET /communities`: public and private communities that are not archived\n * (hidden ones never), most members first. `q` matches the start of the\n * name; an emptied box (`?q=`) is no search, never a 400. `topic` means \"any of\".\n */\nexport const communityDiscoverQuerySchema = paginationQuerySchema.extend({\n q: optionalTrimmedString(DISCOVER_TEXT_MAX),\n topic: multiValueQuery(topicSchema, DISCOVER_FILTER_VALUES_MAX),\n});\nexport type CommunityDiscoverQuery = z.infer<typeof communityDiscoverQuerySchema>;\nexport type CommunityDiscoverQueryInput = z.input<typeof communityDiscoverQuerySchema>;\n\n/** `GET /communities/:id/members`: everyone, or one role (`?role=admin`). */\nexport const communityMembersQuerySchema = paginationQuerySchema.extend({\n role: z.preprocess(emptyStringToUndefined, communityRoleSchema.optional()),\n});\nexport type CommunityMembersQuery = z.infer<typeof communityMembersQuerySchema>;\nexport type CommunityMembersQueryInput = z.input<typeof communityMembersQuerySchema>;\n\n/** `GET /community-suggestions` (staff): waiting ones by default, oldest first. */\nexport const communitySuggestionsQuerySchema = paginationQuerySchema.extend({\n status: z.preprocess(emptyStringToUndefined, communitySuggestionStatusSchema.default('open')),\n});\nexport type CommunitySuggestionsQuery = z.infer<typeof communitySuggestionsQuerySchema>;\nexport type CommunitySuggestionsQueryInput = z.input<typeof communitySuggestionsQuerySchema>;\n\nexport const communityParamsSchema = z.object({ communityId: objectIdSchema });\nexport type CommunityParams = z.infer<typeof communityParamsSchema>;\n/** `/communities/:communityId/{members,requests,invites,bans}/:userId`. */\nexport const communityMemberParamsSchema = z.object({\n communityId: objectIdSchema,\n userId: objectIdSchema,\n});\nexport type CommunityMemberParams = z.infer<typeof communityMemberParamsSchema>;\n/** `/communities/:communityId/{pins,content}/:contentId`. */\nexport const communityContentParamsSchema = z.object({\n communityId: objectIdSchema,\n contentId: objectIdSchema,\n});\nexport type CommunityContentParams = z.infer<typeof communityContentParamsSchema>;\n/** `/communities/:communityId/comments/:commentId/hidden` (0.9.0). */\nexport const communityCommentParamsSchema = z.object({\n communityId: objectIdSchema,\n commentId: objectIdSchema,\n});\nexport type CommunityCommentParams = z.infer<typeof communityCommentParamsSchema>;\nexport const communitySuggestionParamsSchema = z.object({ suggestionId: objectIdSchema });\nexport type CommunitySuggestionParams = z.infer<typeof communitySuggestionParamsSchema>;\n\n// ─── Write bodies ────────────────────────────────────────────────────────────\n// Each its own object with NO defaults (see `updateFounderProfileBodySchema`).\n\nconst nameSchema = z.string().trim().min(COMMUNITY_NAME_MIN).max(COMMUNITY_NAME_MAX);\nconst descriptionSchema = z.string().trim().min(1).max(COMMUNITY_DESCRIPTION_MAX);\nconst rulesSchema = z\n .array(z.string().trim().min(1).max(COMMUNITY_RULE_MAX))\n .max(COMMUNITY_RULES_MAX);\nconst topicsSchema = z\n .array(topicSchema)\n .max(COMMUNITY_TOPICS_MAX)\n .refine((topics) => new Set(topics).size === topics.length, {\n error: VALIDATION_MESSAGE.topicsDuplicate,\n });\n\n/**\n * `POST /communities` (managers, super admins and capability holders), and\n * the reviewer's edited version when approving a suggestion. The creator\n * becomes its first admin. A name a live PUBLIC or PRIVATE community has is\n * `COMMUNITY_NAME_TAKEN` (letter case ignored, runs of spaces read as one);\n * hidden communities' names are never compared, so no name probes for one.\n * Approving a private or hidden one invites its suggester. The icon is a\n * processed `community_icon` upload.\n */\nexport const createCommunityBodySchema = z.object({\n name: nameSchema,\n description: descriptionSchema,\n visibility: communityVisibilitySchema,\n rules: rulesSchema.optional(),\n topics: topicsSchema.optional(),\n iconMediaId: objectIdSchema.optional(),\n});\nexport type CreateCommunityBody = z.infer<typeof createCommunityBodySchema>;\n\n/** `PATCH /communities/:id` (admins): only the keys that changed. Type and icon have their own routes. */\nexport const updateCommunityBodySchema = z.object({\n name: nameSchema.optional(),\n description: descriptionSchema.optional(),\n /** Sent whole; empty clears them. */\n rules: rulesSchema.optional(),\n topics: topicsSchema.optional(),\n});\nexport type UpdateCommunityBody = z.infer<typeof updateCommunityBodySchema>;\n\n/** `PUT /communities/:id/icon`: a processed `community_icon` upload. The old icon's files are deleted. */\nexport const setCommunityIconBodySchema = z.object({ mediaId: objectIdSchema });\nexport type SetCommunityIconBody = z.infer<typeof setCommunityIconBodySchema>;\n\n/**\n * `PUT /communities/:id/visibility` (admins). A change that exposes what was\n * already posted (`communityChangeExposes`: toward public, or hidden to\n * private) needs `confirmExposure: true`, else `COMMUNITY_EXPOSURE_UNCONFIRMED`:\n * the web asks first, and a client cannot skip the question. Audited.\n */\nexport const setCommunityVisibilityBodySchema = z.object({\n visibility: communityVisibilitySchema,\n confirmExposure: z.boolean().optional(),\n});\nexport type SetCommunityVisibilityBody = z.infer<typeof setCommunityVisibilityBodySchema>;\n\n/** `PUT /communities/:id/status` (staff). Audited. */\nexport const setCommunityStatusBodySchema = z.object({ status: communityStatusSchema });\nexport type SetCommunityStatusBody = z.infer<typeof setCommunityStatusBodySchema>;\n\n/**\n * `POST /communities/:id/invites` (moderators, private and hidden only;\n * a public community needs none: `COMMUNITY_INVITE_NOT_NEEDED`). Someone who\n * cannot join (not onboarded, paused, suspended, deleted) is\n * `COMMUNITY_MEMBER_NOT_INVITABLE`; banned, `COMMUNITY_MEMBER_BANNED`; already\n * a member, requested or invited, `COMMUNITY_ALREADY_MEMBER`.\n */\nexport const inviteCommunityMemberBodySchema = z.object({ userId: objectIdSchema });\nexport type InviteCommunityMemberBody = z.infer<typeof inviteCommunityMemberBodySchema>;\n\n/** `PUT /communities/:id/members/:userId/role` (admins). Never the last admin's own step down. */\nexport const setCommunityMemberRoleBodySchema = z.object({ role: communityRoleSchema });\nexport type SetCommunityMemberRoleBody = z.infer<typeof setCommunityMemberRoleBodySchema>;\n\n/** `POST /communities/:id/bans` (admins): removes them if active, and keeps them out. */\nexport const banCommunityMemberBodySchema = z.object({\n userId: objectIdSchema,\n reason: z.string().trim().min(1).max(COMMUNITY_BAN_REASON_MAX).optional(),\n});\nexport type BanCommunityMemberBody = z.infer<typeof banCommunityMemberBodySchema>;\n\n/**\n * `POST /communities/:id/pins` (moderators): pin one of the community's posts\n * or polls as an announcement (`COMMUNITY_PINS_FULL` past `COMMUNITY_PINS_MAX`).\n * `notifyMembers` (admins only) rings every active member's bell, at most once\n * per `COMMUNITY_ANNOUNCE_INTERVAL_MS` (`COMMUNITY_ANNOUNCE_TOO_SOON`).\n */\nexport const pinCommunityContentBodySchema = z.object({\n contentId: objectIdSchema,\n notifyMembers: z.boolean(),\n});\nexport type PinCommunityContentBody = z.infer<typeof pinCommunityContentBodySchema>;\n\n/**\n * `POST /community-suggestions`: any onboarded member, at most\n * `COMMUNITY_SUGGESTIONS_OPEN_MAX` waiting at once (`COMMUNITY_SUGGESTIONS_FULL`).\n */\nexport const createCommunitySuggestionBodySchema = z.object({\n name: nameSchema,\n description: descriptionSchema,\n why: z.string().trim().min(1).max(COMMUNITY_SUGGESTION_WHY_MAX).optional(),\n visibility: communityVisibilitySchema,\n topics: topicsSchema.optional(),\n});\nexport type CreateCommunitySuggestionBody = z.infer<typeof createCommunitySuggestionBodySchema>;\n\n/**\n * `POST /community-suggestions/:id/decline` (staff): the reason is shown to the\n * suggester. A decided suggestion is `COMMUNITY_SUGGESTION_DECIDED`.\n */\nexport const declineCommunitySuggestionBodySchema = z.object({\n reason: z.string().trim().min(COMMUNITY_DECLINE_REASON_MIN).max(COMMUNITY_DECLINE_REASON_MAX),\n});\nexport type DeclineCommunitySuggestionBody = z.infer<typeof declineCommunitySuggestionBodySchema>;\n"]}
|
|
1
|
+
{"version":3,"file":"communities.js","sourceRoot":"","sources":["../../../src/domains/communities/communities.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema — Communities: official spaces inside Founder Circle (0.9.0)\n *\n * THE USER'S DECISIONS (2026-10-08), each asked on its own:\n *\n * - OFFICIAL: managers, super admins, and members given the\n * `create_communities` capability create them. Everyone else SUGGESTS one;\n * managers and super admins review the suggestions.\n * - THREE TYPES, three separate questions answered per type:\n *\n * public private hidden\n * listed in Discover yes yes no\n * header (name, about, signed-in signed-in members and\n * rules, admins, count) members members invitees (404 otherwise)\n * posts, polls, members signed-in members only members only\n * joining one tap request, then invite only,\n * approval then accept\n *\n * Taking part (post, poll, comment, react, vote, save, report) is for\n * members of every type. \"Members\" means ACTIVE members: someone who\n * requested, was invited or is banned is a non-member for every rule.\n * - PUBLIC MEANS PUBLIC INSIDE FOUNDER CIRCLE: a signed-out visitor sees no\n * community content of any type, even by a shared link.\n * - THE HOME FEED adds only the communities the member joined.\n * - NO CHAT IN 0.9.0 (feed-first). Announcements are pinned posts\n * (`COMMUNITY_PINS_MAX`), optionally notifying every member.\n *\n * A banned member sees the community as hidden (404 everywhere) until\n * unbanned. Platform staff acting as moderator, manager or super admin READ\n * every community, as they read every post, and moderate its content through\n * the staff moderation queue; to take part they join like anyone else.\n *\n * Refusals follow what the caller can SEE: 404 for something they may not\n * see (the same as for something that does not exist, so nothing is\n * probed), 403 `COMMUNITY_JOIN_REQUIRED` for something they see but must join\n * to read or to take part in.\n *\n * Content in a community carries no product and no ask, and cannot be\n * reposted (a repost is general content, readable signed out): two leak paths\n * closed by construction. Design: founder-circle-backend `docs/communities.md`.\n *\n * Inside a community:\n *\n * member moderator admin manager,\n * super admin\n * post, poll, comment, react, vote ✓ ✓ ✓ ✓ (a member)\n * approve requests, invite, pin, hide ✓ ✓ ✓\n * remove a member (not a moderator) ✓ ✓ ✓\n * remove a moderator ✓ ✓\n * remove or ban an admin ✓\n * notify all, ban, roles, edit, type ✓ ✓\n * archive, delete ✓\n *\n * An admin may unmake another admin (then act on them as on a member). Each\n * member row says what this viewer may do to it (`communityMemberSchema.viewer`):\n * a client never works it out from roles.\n *\n * A community always keeps an admin while anyone can be one\n * (`COMMUNITY_LAST_ADMIN`): an admin leaves, steps down, or deletes their\n * account only once someone else is admin (the user's rule, 2026-10-08). When\n * nobody else is active, or the account was deleted where it cannot be\n * refused (central sign-in), the community is ORPHANED: managers and super\n * admins run it (they can act on any community), it is flagged in the audit\n * log (`community.orphaned`), and the page says so to them (`orphaned`).\n *\n * Routes (all signed in):\n * GET /api/v1/communities communityDiscoverQuerySchema -> communityCardPageSchema\n * GET /api/v1/communities/mine paginationQuerySchema -> communityCardPageSchema\n * GET /api/v1/communities/invites communityInvitesSchema (mine, waiting)\n * GET /api/v1/communities/orphaned paginationQuerySchema -> communityCardPageSchema\n * (managers, super admins: communities left with no admin, newest first)\n * POST /api/v1/communities createCommunityBodySchema -> IdResponse\n * GET /api/v1/communities/:communityId communitySchema\n * PATCH /api/v1/communities/:communityId updateCommunityBodySchema -> communitySchema\n * PUT /api/v1/communities/:communityId/icon setCommunityIconBodySchema -> communitySchema\n * DELETE /api/v1/communities/:communityId/icon -> communitySchema\n * PUT /api/v1/communities/:communityId/visibility setCommunityVisibilityBodySchema -> communitySchema\n * PUT /api/v1/communities/:communityId/status setCommunityStatusBodySchema -> communitySchema (staff)\n * DELETE /api/v1/communities/:communityId -> IdResponse (staff)\n * GET /api/v1/communities/:communityId/feed paginationQuerySchema -> FeedPage (newest first;\n * pinned items are in it too, in their place)\n * GET /api/v1/communities/:communityId/pinned PinnedItems (feed)\n * POST /api/v1/communities/:communityId/pins pinCommunityContentBodySchema -> PinnedItems\n * DELETE /api/v1/communities/:communityId/pins/:contentId -> PinnedItems\n * POST /api/v1/communities/:communityId/join -> communitySchema (public: joined; private: requested)\n * DELETE /api/v1/communities/:communityId/join cancel a request -> communitySchema\n * POST /api/v1/communities/:communityId/leave -> IdResponse\n * GET /api/v1/communities/:communityId/members communityMembersQuerySchema -> communityMemberPageSchema\n * GET /api/v1/communities/:communityId/requests paginationQuerySchema -> communityRequestPageSchema\n * PUT /api/v1/communities/:communityId/requests/:userId approve -> IdResponse\n * DELETE /api/v1/communities/:communityId/requests/:userId decline -> IdResponse\n * GET /api/v1/communities/:communityId/invites paginationQuerySchema -> communityPendingInvitePageSchema\n * POST /api/v1/communities/:communityId/invites inviteCommunityMemberBodySchema -> communityPendingInviteSchema\n * DELETE /api/v1/communities/:communityId/invites/:userId cancel -> IdResponse\n * PUT /api/v1/communities/:communityId/invite accept (the invitee) -> communitySchema\n * DELETE /api/v1/communities/:communityId/invite decline (the invitee) -> IdResponse\n * PUT /api/v1/communities/:communityId/members/:userId/role setCommunityMemberRoleBodySchema -> IdResponse\n * DELETE /api/v1/communities/:communityId/members/:userId remove -> IdResponse\n * GET /api/v1/communities/:communityId/bans paginationQuerySchema -> communityBanPageSchema\n * POST /api/v1/communities/:communityId/bans banCommunityMemberBodySchema -> IdResponse\n * DELETE /api/v1/communities/:communityId/bans/:userId unban -> IdResponse\n * PUT /api/v1/communities/:communityId/content/:contentId/hidden hide in the community -> IdResponse\n * DELETE /api/v1/communities/:communityId/content/:contentId/hidden restore -> IdResponse\n * PUT /api/v1/communities/:communityId/comments/:commentId/hidden hide a comment in it -> IdResponse\n * DELETE /api/v1/communities/:communityId/comments/:commentId/hidden restore -> IdResponse\n * GET /api/v1/communities/:communityId/hidden paginationQuerySchema -> FeedPage (moderators:\n * what ITS moderators hid, newest first; a staff hide is staff's to undo and is not listed)\n * GET /api/v1/communities/:communityId/hidden/comments paginationQuerySchema -> commentPageSchema\n * POST /api/v1/community-suggestions createCommunitySuggestionBodySchema -> communitySuggestionSchema\n * GET /api/v1/community-suggestions/mine paginationQuerySchema -> communitySuggestionPageSchema\n * GET /api/v1/community-suggestions communitySuggestionsQuerySchema -> communitySuggestionPageSchema (staff)\n * POST /api/v1/community-suggestions/:suggestionId/approve createCommunityBodySchema -> IdResponse (the community)\n * POST /api/v1/community-suggestions/:suggestionId/decline declineCommunitySuggestionBodySchema -> communitySuggestionSchema\n * (posting into a community: `communityId` on the feed's create bodies)\n *\n * @module founder-schema/domains/communities\n */\n\nimport { z } from 'zod';\nimport {\n emptyStringToUndefined,\n multiValueQuery,\n objectIdSchema,\n optionalTrimmedString,\n paginationQuerySchema,\n} from '../../core/common.js';\nimport {\n COMMUNITY_BAN_REASON_MAX,\n COMMUNITY_DECLINE_REASON_MAX,\n COMMUNITY_DECLINE_REASON_MIN,\n COMMUNITY_DESCRIPTION_MAX,\n COMMUNITY_NAME_MAX,\n COMMUNITY_NAME_MIN,\n COMMUNITY_RULE_MAX,\n COMMUNITY_RULES_MAX,\n COMMUNITY_SUGGESTION_WHY_MAX,\n DISCOVER_FILTER_VALUES_MAX,\n DISCOVER_TEXT_MAX,\n} from '../../core/limits.js';\nimport { paginatedSchema } from '../../core/responses.js';\nimport { VALIDATION_MESSAGE } from '../../core/validation.js';\nimport { mediaImageSchema } from '../media/media.js';\nimport { topicSchema } from '../onboarding/onboarding.js';\nimport { memberBylineSchema } from '../profile/founder-profile.js';\n\n/** Topics a community carries: what Discover filters by. */\nexport const COMMUNITY_TOPICS_MAX = 3;\n/** Announcements pinned to the top of one community at once. */\nexport const COMMUNITY_PINS_MAX = 3;\n/** Suggestions one member may have waiting for review at once. */\nexport const COMMUNITY_SUGGESTIONS_OPEN_MAX = 3;\n/**\n * The shortest time between two notify-everyone announcements in one\n * community, so members are not spammed (`COMMUNITY_ANNOUNCE_TOO_SOON`).\n */\nexport const COMMUNITY_ANNOUNCE_INTERVAL_MS = 24 * 60 * 60 * 1000;\n\n// ─── Vocabulary ──────────────────────────────────────────────────────────────\n// ⚠ Every list below is STORED. Append only; removing a value orphans rows.\n\n/** See the module note's table. */\nexport const COMMUNITY_VISIBILITY_VALUES = ['public', 'private', 'hidden'] as const;\nexport const communityVisibilitySchema = z.enum(COMMUNITY_VISIBILITY_VALUES);\nexport type CommunityVisibility = z.infer<typeof communityVisibilitySchema>;\n\n/**\n * How private each type is, least first. A change to a LOWER rank exposes what\n * was already posted, so it needs `confirmExposure` (and is audited).\n */\nexport const COMMUNITY_VISIBILITY_RANK = {\n public: 0,\n private: 1,\n hidden: 2,\n} as const satisfies Record<CommunityVisibility, number>;\n\n/** Whether moving from one type to another exposes what was already there. */\nexport function communityChangeExposes(\n from: CommunityVisibility,\n to: CommunityVisibility,\n): boolean {\n return COMMUNITY_VISIBILITY_RANK[to] < COMMUNITY_VISIBILITY_RANK[from];\n}\n\n/** A member's role inside one community. */\nexport const COMMUNITY_ROLE_VALUES = ['admin', 'moderator', 'member'] as const;\nexport const communityRoleSchema = z.enum(COMMUNITY_ROLE_VALUES);\nexport type CommunityRole = z.infer<typeof communityRoleSchema>;\n\n/**\n * Where someone stands with one community. `requested` (private: waiting for\n * approval), `invited` (waiting for their answer), `banned` (the community is\n * hidden from them). Leaving or being removed deletes the row.\n */\nexport const COMMUNITY_MEMBER_STATUS_VALUES = ['active', 'requested', 'invited', 'banned'] as const;\nexport const communityMemberStatusSchema = z.enum(COMMUNITY_MEMBER_STATUS_VALUES);\nexport type CommunityMemberStatus = z.infer<typeof communityMemberStatusSchema>;\n\n/** `archived`: read-only and out of Discover; members still read it. Staff only. */\nexport const COMMUNITY_STATUS_VALUES = ['active', 'archived'] as const;\nexport const communityStatusSchema = z.enum(COMMUNITY_STATUS_VALUES);\nexport type CommunityStatus = z.infer<typeof communityStatusSchema>;\n\nexport const COMMUNITY_SUGGESTION_STATUS_VALUES = ['open', 'approved', 'declined'] as const;\nexport const communitySuggestionStatusSchema = z.enum(COMMUNITY_SUGGESTION_STATUS_VALUES);\nexport type CommunitySuggestionStatus = z.infer<typeof communitySuggestionStatusSchema>;\n\n// ─── Read shapes ─────────────────────────────────────────────────────────────\n\n/**\n * A community named on something else: \"in AI Builders\" on a post or poll, a\n * notification. `iconUrl` is the icon's smallest size.\n */\nexport const communityChipSchema = z.object({\n id: objectIdSchema,\n /** For a readable URL, `/communities/<id>/<slug>`. The id resolves; a renamed community keeps its links. */\n slug: z.string(),\n name: z.string(),\n iconUrl: z.url().optional(),\n visibility: communityVisibilitySchema,\n /** Present (true) once it is archived: read only, nobody takes part. */\n archived: z.boolean().optional(),\n});\nexport type CommunityChip = z.infer<typeof communityChipSchema>;\n\n/** Where THIS viewer stands with a community, on a card. Banned viewers never see it. */\nexport const communityCardViewerSchema = z.object({\n /** Null: not a member and nothing pending. Never `banned` (they get 404). */\n status: communityMemberStatusSchema.exclude(['banned']).nullable(),\n /** Their role while active, else null. */\n role: communityRoleSchema.nullable(),\n});\nexport type CommunityCardViewer = z.infer<typeof communityCardViewerSchema>;\n\n/** A community on Discover and in \"your communities\". */\nexport const communityCardSchema = z.object({\n id: objectIdSchema,\n slug: z.string(),\n name: z.string(),\n description: z.string(),\n icon: mediaImageSchema.optional(),\n visibility: communityVisibilitySchema,\n status: communityStatusSchema,\n topics: z.array(topicSchema).max(COMMUNITY_TOPICS_MAX),\n /** Active members. */\n memberCount: z.number().int().nonnegative(),\n /** Visible posts and polls in it. */\n postCount: z.number().int().nonnegative(),\n viewer: communityCardViewerSchema.optional(),\n});\nexport type CommunityCard = z.infer<typeof communityCardSchema>;\n\n/**\n * What THIS viewer may do on the community page, decided by the server and\n * sent as plain answers (the rule of `viewerStateSchema` on a post): a client\n * never derives a power from `role`.\n */\nexport const communityViewerSchema = communityCardViewerSchema.extend({\n /** Read its posts, polls and comments now. False on a private one before joining. */\n canRead: z.boolean(),\n /** Read its member list. */\n canSeeMembers: z.boolean(),\n /** Public, active, and the viewer has no row (nothing pending): offer Join. */\n canJoin: z.boolean(),\n /** Private, active, and the viewer has no row: offer Request to join. */\n canRequest: z.boolean(),\n /** Active member, community not archived: the composer. */\n canPost: z.boolean(),\n /** Active member who is not its last admin. */\n canLeave: z.boolean(),\n /**\n * Moderators and admins (and managers, super admins): requests, invites,\n * pins, hiding, removing members. On an ARCHIVED community approving,\n * inviting, accepting and pinning answer `COMMUNITY_ARCHIVED`; declining,\n * cancelling, removing, banning, roles and hiding still work.\n */\n canModerate: z.boolean(),\n /** Admins (and managers, super admins): details, icon, type, roles, bans. */\n canManage: z.boolean(),\n /**\n * Change members' roles (hand it over) and lift bans (0.13.0): every admin,\n * INCLUDING one whose membership ended and so cannot manage or moderate any\n * more. They keep this so a sole admin is never trapped: Manage shows them\n * Members (roles only) and Bans (unban only).\n */\n canAssign: z.boolean(),\n /** May notify every member with an announcement now (admins, at most once per `COMMUNITY_ANNOUNCE_INTERVAL_MS`). */\n canAnnounce: z.boolean(),\n /** Managers and super admins: archive, restore, delete. */\n canArchive: z.boolean(),\n});\nexport type CommunityViewer = z.infer<typeof communityViewerSchema>;\n\n/**\n * One person in a community, live-joined like every byline. In the member\n * list, admins first, then moderators, then members, each by when they joined.\n * `viewer` (0.9.0, on the member list only) is what THIS viewer may do to\n * them, the server's own rules (who outranks whom, never oneself, never the\n * last admin).\n */\nexport const communityMemberSchema = memberBylineSchema.extend({\n role: communityRoleSchema,\n joinedAt: z.iso.datetime(),\n viewer: z\n .object({\n canSetRole: z.boolean(),\n canRemove: z.boolean(),\n canBan: z.boolean(),\n })\n .optional(),\n});\nexport type CommunityMember = z.infer<typeof communityMemberSchema>;\n\n/** The community page. The header is what a non-member of a private one may see too. */\nexport const communitySchema = communityCardSchema.extend({\n rules: z.array(z.string()).max(COMMUNITY_RULES_MAX),\n /**\n * Its admins and moderators, admins first, by when they joined: who to ask.\n * EMPTY for a viewer who may not read it (a private community before\n * joining, a hidden one's invitee): who is in it is its members' to see.\n */\n team: z.array(communityMemberSchema),\n createdAt: z.iso.datetime(),\n updatedAt: z.iso.datetime(),\n archivedAt: z.iso.datetime().nullable(),\n /**\n * Only for managers and super admins: it has no admin left (its last one's\n * account was deleted where that cannot be refused), so staff run it until\n * they make a member admin, which clears it.\n */\n orphaned: z.boolean().optional(),\n viewer: communityViewerSchema.optional(),\n});\nexport type Community = z.infer<typeof communitySchema>;\n\n/** A request to join a private community, as its moderators see it. */\nexport const communityRequestSchema = z.object({\n member: memberBylineSchema,\n requestedAt: z.iso.datetime(),\n});\nexport type CommunityRequest = z.infer<typeof communityRequestSchema>;\n\n/** An invitation nobody has answered yet, as its moderators see it. */\nexport const communityPendingInviteSchema = z.object({\n member: memberBylineSchema,\n /** Null when whoever invited has since deleted their account. */\n invitedBy: memberBylineSchema.nullable(),\n invitedAt: z.iso.datetime(),\n});\nexport type CommunityPendingInvite = z.infer<typeof communityPendingInviteSchema>;\n\n/** An invitation waiting for the viewer's answer. */\nexport const communityInviteSchema = z.object({\n community: communityChipSchema.extend({ description: z.string() }),\n invitedBy: memberBylineSchema.nullable(),\n invitedAt: z.iso.datetime(),\n});\nexport type CommunityInvite = z.infer<typeof communityInviteSchema>;\n\n/** `GET /communities/invites`. Never paged: invitations are few, and each needs an answer. */\nexport const communityInvitesSchema = z.object({ items: z.array(communityInviteSchema) });\nexport type CommunityInvites = z.infer<typeof communityInvitesSchema>;\n\n/** Someone banned from a community, as its admins see it. */\nexport const communityBanSchema = z.object({\n member: memberBylineSchema,\n bannedBy: memberBylineSchema.nullable(),\n bannedAt: z.iso.datetime(),\n /** For the admins' own record; never shown to the banned member. */\n reason: z.string().max(COMMUNITY_BAN_REASON_MAX).optional(),\n});\nexport type CommunityBan = z.infer<typeof communityBanSchema>;\n\n/** A suggestion, as its author and the reviewers see it. */\nexport const communitySuggestionSchema = z.object({\n id: objectIdSchema,\n /** Null when its author has since deleted their account. */\n suggestedBy: memberBylineSchema.nullable(),\n name: z.string(),\n description: z.string(),\n why: z.string().optional(),\n visibility: communityVisibilitySchema,\n topics: z.array(topicSchema).max(COMMUNITY_TOPICS_MAX),\n status: communitySuggestionStatusSchema,\n decidedBy: memberBylineSchema.nullable(),\n decidedAt: z.iso.datetime().nullable(),\n /** Why it was declined, shown to its author. */\n declineReason: z.string().optional(),\n /**\n * The community it became, once approved. Absent if that community is since\n * deleted, or the viewer may no longer see it (banned from a hidden one).\n */\n community: communityChipSchema.optional(),\n createdAt: z.iso.datetime(),\n});\nexport type CommunitySuggestion = z.infer<typeof communitySuggestionSchema>;\n\nexport const communityCardPageSchema = paginatedSchema(communityCardSchema);\nexport type CommunityCardPage = z.infer<typeof communityCardPageSchema>;\nexport const communityMemberPageSchema = paginatedSchema(communityMemberSchema);\nexport type CommunityMemberPage = z.infer<typeof communityMemberPageSchema>;\nexport const communityRequestPageSchema = paginatedSchema(communityRequestSchema);\nexport type CommunityRequestPage = z.infer<typeof communityRequestPageSchema>;\nexport const communityPendingInvitePageSchema = paginatedSchema(communityPendingInviteSchema);\nexport type CommunityPendingInvitePage = z.infer<typeof communityPendingInvitePageSchema>;\nexport const communityBanPageSchema = paginatedSchema(communityBanSchema);\nexport type CommunityBanPage = z.infer<typeof communityBanPageSchema>;\nexport const communitySuggestionPageSchema = paginatedSchema(communitySuggestionSchema);\nexport type CommunitySuggestionPage = z.infer<typeof communitySuggestionPageSchema>;\n\n// ─── Queries and params ──────────────────────────────────────────────────────\n\n/**\n * `GET /communities`: public and private communities that are not archived\n * (hidden ones never), most members first. `q` matches the start of the\n * name; an emptied box (`?q=`) is no search, never a 400. `topic` means \"any of\".\n */\nexport const communityDiscoverQuerySchema = paginationQuerySchema.extend({\n q: optionalTrimmedString(DISCOVER_TEXT_MAX),\n topic: multiValueQuery(topicSchema, DISCOVER_FILTER_VALUES_MAX),\n});\nexport type CommunityDiscoverQuery = z.infer<typeof communityDiscoverQuerySchema>;\nexport type CommunityDiscoverQueryInput = z.input<typeof communityDiscoverQuerySchema>;\n\n/** `GET /communities/:id/members`: everyone, or one role (`?role=admin`). */\nexport const communityMembersQuerySchema = paginationQuerySchema.extend({\n role: z.preprocess(emptyStringToUndefined, communityRoleSchema.optional()),\n});\nexport type CommunityMembersQuery = z.infer<typeof communityMembersQuerySchema>;\nexport type CommunityMembersQueryInput = z.input<typeof communityMembersQuerySchema>;\n\n/** `GET /community-suggestions` (staff): waiting ones by default, oldest first. */\nexport const communitySuggestionsQuerySchema = paginationQuerySchema.extend({\n status: z.preprocess(emptyStringToUndefined, communitySuggestionStatusSchema.default('open')),\n});\nexport type CommunitySuggestionsQuery = z.infer<typeof communitySuggestionsQuerySchema>;\nexport type CommunitySuggestionsQueryInput = z.input<typeof communitySuggestionsQuerySchema>;\n\nexport const communityParamsSchema = z.object({ communityId: objectIdSchema });\nexport type CommunityParams = z.infer<typeof communityParamsSchema>;\n/** `/communities/:communityId/{members,requests,invites,bans}/:userId`. */\nexport const communityMemberParamsSchema = z.object({\n communityId: objectIdSchema,\n userId: objectIdSchema,\n});\nexport type CommunityMemberParams = z.infer<typeof communityMemberParamsSchema>;\n/** `/communities/:communityId/{pins,content}/:contentId`. */\nexport const communityContentParamsSchema = z.object({\n communityId: objectIdSchema,\n contentId: objectIdSchema,\n});\nexport type CommunityContentParams = z.infer<typeof communityContentParamsSchema>;\n/** `/communities/:communityId/comments/:commentId/hidden` (0.9.0). */\nexport const communityCommentParamsSchema = z.object({\n communityId: objectIdSchema,\n commentId: objectIdSchema,\n});\nexport type CommunityCommentParams = z.infer<typeof communityCommentParamsSchema>;\nexport const communitySuggestionParamsSchema = z.object({ suggestionId: objectIdSchema });\nexport type CommunitySuggestionParams = z.infer<typeof communitySuggestionParamsSchema>;\n\n// ─── Write bodies ────────────────────────────────────────────────────────────\n// Each its own object with NO defaults (see `updateFounderProfileBodySchema`).\n\nconst nameSchema = z.string().trim().min(COMMUNITY_NAME_MIN).max(COMMUNITY_NAME_MAX);\nconst descriptionSchema = z.string().trim().min(1).max(COMMUNITY_DESCRIPTION_MAX);\nconst rulesSchema = z\n .array(z.string().trim().min(1).max(COMMUNITY_RULE_MAX))\n .max(COMMUNITY_RULES_MAX);\nconst topicsSchema = z\n .array(topicSchema)\n .max(COMMUNITY_TOPICS_MAX)\n .refine((topics) => new Set(topics).size === topics.length, {\n error: VALIDATION_MESSAGE.topicsDuplicate,\n });\n\n/**\n * `POST /communities` (managers, super admins and capability holders), and\n * the reviewer's edited version when approving a suggestion. The creator\n * becomes its first admin. A name a live PUBLIC or PRIVATE community has is\n * `COMMUNITY_NAME_TAKEN` (letter case ignored, runs of spaces read as one);\n * hidden communities' names are never compared, so no name probes for one.\n * Approving a private or hidden one invites its suggester. The icon is a\n * processed `community_icon` upload.\n */\nexport const createCommunityBodySchema = z.object({\n name: nameSchema,\n description: descriptionSchema,\n visibility: communityVisibilitySchema,\n rules: rulesSchema.optional(),\n topics: topicsSchema.optional(),\n iconMediaId: objectIdSchema.optional(),\n});\nexport type CreateCommunityBody = z.infer<typeof createCommunityBodySchema>;\n\n/** `PATCH /communities/:id` (admins): only the keys that changed. Type and icon have their own routes. */\nexport const updateCommunityBodySchema = z.object({\n name: nameSchema.optional(),\n description: descriptionSchema.optional(),\n /** Sent whole; empty clears them. */\n rules: rulesSchema.optional(),\n topics: topicsSchema.optional(),\n});\nexport type UpdateCommunityBody = z.infer<typeof updateCommunityBodySchema>;\n\n/** `PUT /communities/:id/icon`: a processed `community_icon` upload. The old icon's files are deleted. */\nexport const setCommunityIconBodySchema = z.object({ mediaId: objectIdSchema });\nexport type SetCommunityIconBody = z.infer<typeof setCommunityIconBodySchema>;\n\n/**\n * `PUT /communities/:id/visibility` (admins). A change that exposes what was\n * already posted (`communityChangeExposes`: toward public, or hidden to\n * private) needs `confirmExposure: true`, else `COMMUNITY_EXPOSURE_UNCONFIRMED`:\n * the web asks first, and a client cannot skip the question. Audited.\n */\nexport const setCommunityVisibilityBodySchema = z.object({\n visibility: communityVisibilitySchema,\n confirmExposure: z.boolean().optional(),\n});\nexport type SetCommunityVisibilityBody = z.infer<typeof setCommunityVisibilityBodySchema>;\n\n/** `PUT /communities/:id/status` (staff). Audited. */\nexport const setCommunityStatusBodySchema = z.object({ status: communityStatusSchema });\nexport type SetCommunityStatusBody = z.infer<typeof setCommunityStatusBodySchema>;\n\n/**\n * `POST /communities/:id/invites` (moderators, private and hidden only;\n * a public community needs none: `COMMUNITY_INVITE_NOT_NEEDED`). Someone who\n * cannot join (not onboarded, paused, suspended, deleted) is\n * `COMMUNITY_MEMBER_NOT_INVITABLE`; banned, `COMMUNITY_MEMBER_BANNED`; already\n * a member, requested or invited, `COMMUNITY_ALREADY_MEMBER`.\n */\nexport const inviteCommunityMemberBodySchema = z.object({ userId: objectIdSchema });\nexport type InviteCommunityMemberBody = z.infer<typeof inviteCommunityMemberBodySchema>;\n\n/** `PUT /communities/:id/members/:userId/role` (admins). Never the last admin's own step down. */\nexport const setCommunityMemberRoleBodySchema = z.object({ role: communityRoleSchema });\nexport type SetCommunityMemberRoleBody = z.infer<typeof setCommunityMemberRoleBodySchema>;\n\n/** `POST /communities/:id/bans` (admins): removes them if active, and keeps them out. */\nexport const banCommunityMemberBodySchema = z.object({\n userId: objectIdSchema,\n reason: z.string().trim().min(1).max(COMMUNITY_BAN_REASON_MAX).optional(),\n});\nexport type BanCommunityMemberBody = z.infer<typeof banCommunityMemberBodySchema>;\n\n/**\n * `POST /communities/:id/pins` (moderators): pin one of the community's posts\n * or polls as an announcement (`COMMUNITY_PINS_FULL` past `COMMUNITY_PINS_MAX`).\n * `notifyMembers` (admins only) rings every active member's bell, at most once\n * per `COMMUNITY_ANNOUNCE_INTERVAL_MS` (`COMMUNITY_ANNOUNCE_TOO_SOON`).\n */\nexport const pinCommunityContentBodySchema = z.object({\n contentId: objectIdSchema,\n notifyMembers: z.boolean(),\n});\nexport type PinCommunityContentBody = z.infer<typeof pinCommunityContentBodySchema>;\n\n/**\n * `POST /community-suggestions`: any onboarded member, at most\n * `COMMUNITY_SUGGESTIONS_OPEN_MAX` waiting at once (`COMMUNITY_SUGGESTIONS_FULL`).\n */\nexport const createCommunitySuggestionBodySchema = z.object({\n name: nameSchema,\n description: descriptionSchema,\n why: z.string().trim().min(1).max(COMMUNITY_SUGGESTION_WHY_MAX).optional(),\n visibility: communityVisibilitySchema,\n topics: topicsSchema.optional(),\n});\nexport type CreateCommunitySuggestionBody = z.infer<typeof createCommunitySuggestionBodySchema>;\n\n/**\n * `POST /community-suggestions/:id/decline` (staff): the reason is shown to the\n * suggester. A decided suggestion is `COMMUNITY_SUGGESTION_DECIDED`.\n */\nexport const declineCommunitySuggestionBodySchema = z.object({\n reason: z.string().trim().min(COMMUNITY_DECLINE_REASON_MIN).max(COMMUNITY_DECLINE_REASON_MAX),\n});\nexport type DeclineCommunitySuggestionBody = z.infer<typeof declineCommunitySuggestionBodySchema>;\n"]}
|
|
@@ -145,10 +145,10 @@ export declare const contributionPeriodSchema: z.ZodEnum<{
|
|
|
145
145
|
export type ContributionPeriod = z.infer<typeof contributionPeriodSchema>;
|
|
146
146
|
/** `GET /api/v1/contributions/leaderboard?period=` (0.10.0). A query string, so the default is the server's. */
|
|
147
147
|
export declare const contributionLeaderboardQuerySchema: z.ZodObject<{
|
|
148
|
-
period: z.ZodDefault<z.ZodEnum<{
|
|
148
|
+
period: z.ZodPreprocess<z.ZodDefault<z.ZodEnum<{
|
|
149
149
|
all: "all";
|
|
150
150
|
recent: "recent";
|
|
151
|
-
}
|
|
151
|
+
}>>, unknown>;
|
|
152
152
|
}, z.core.$strip>;
|
|
153
153
|
export type ContributionLeaderboardQuery = z.infer<typeof contributionLeaderboardQuerySchema>;
|
|
154
154
|
/** One row of a board: its rank, who, and their points in the period. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contributions.d.ts","sourceRoot":"","sources":["../../../src/domains/contributions/contributions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,4CAA4C;AAC5C,eAAO,MAAM,wBAAwB,uFAK3B,CAAC;AACX,eAAO,MAAM,sBAAsB;;;;;EAAmC,CAAC;AACvE;;;GAGG;AACH,eAAO,MAAM,gCAAgC,gEAG5C,CAAC;AACF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;CAKqB,CAAC;AAEtD,4CAA4C;AAC5C,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoBlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAA2C,CAAC;AAC/E,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,4BAA6B,CAAC;AACrE,eAAO,MAAM,wBAAwB;;;EAAqC,CAAC;AAC3E,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,gHAAgH;AAChH,eAAO,MAAM,kCAAkC;;;;;
|
|
1
|
+
{"version":3,"file":"contributions.d.ts","sourceRoot":"","sources":["../../../src/domains/contributions/contributions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,4CAA4C;AAC5C,eAAO,MAAM,wBAAwB,uFAK3B,CAAC;AACX,eAAO,MAAM,sBAAsB;;;;;EAAmC,CAAC;AACvE;;;GAGG;AACH,eAAO,MAAM,gCAAgC,gEAG5C,CAAC;AACF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;CAKqB,CAAC;AAEtD,4CAA4C;AAC5C,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoBlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAA2C,CAAC;AAC/E,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,4BAA6B,CAAC;AACrE,eAAO,MAAM,wBAAwB;;;EAAqC,CAAC;AAC3E,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,gHAAgH;AAChH,eAAO,MAAM,kCAAkC;;;;;iBAG7C,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAE9F,yEAAyE;AACzE,eAAO,MAAM,6BAA6B;;;;;;;;;;iBAKxC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB;;;iBAGhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;iBAKxC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC"}
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
* @module founder-schema/domains/contributions
|
|
36
36
|
*/
|
|
37
37
|
import { z } from 'zod';
|
|
38
|
-
import { objectIdSchema } from '../../core/common.js';
|
|
38
|
+
import { emptyStringToUndefined, objectIdSchema } from '../../core/common.js';
|
|
39
39
|
import { paginatedSchema } from '../../core/responses.js';
|
|
40
40
|
import { contentKindSchema } from '../feed/feed.js';
|
|
41
41
|
import { productChipSchema } from '../products/products.js';
|
|
@@ -96,7 +96,8 @@ export const CONTRIBUTION_PERIOD_VALUES = ['recent', 'all'];
|
|
|
96
96
|
export const contributionPeriodSchema = z.enum(CONTRIBUTION_PERIOD_VALUES);
|
|
97
97
|
/** `GET /api/v1/contributions/leaderboard?period=` (0.10.0). A query string, so the default is the server's. */
|
|
98
98
|
export const contributionLeaderboardQuerySchema = z.object({
|
|
99
|
-
period
|
|
99
|
+
// `?period=` with nothing after it is no period, as on every other query.
|
|
100
|
+
period: z.preprocess(emptyStringToUndefined, contributionPeriodSchema.default('recent')),
|
|
100
101
|
});
|
|
101
102
|
/** One row of a board: its rank, who, and their points in the period. */
|
|
102
103
|
export const contributionLeaderEntrySchema = z.object({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contributions.js","sourceRoot":"","sources":["../../../src/domains/contributions/contributions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"contributions.js","sourceRoot":"","sources":["../../../src/domains/contributions/contributions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema — Contribution: the help a member gives, recognised\n *\n * The score counts HELP GIVEN, and only what OTHER members recognise:\n *\n * feedback_helpful a product's team marks your comment \"this helped\" +10\n * tester_confirmed a product's team confirms you tested their product +15\n * review_helpful another member finds your review helpful +2\n * (at most `REVIEW_HELPFUL_POINTS_MAX` per review)\n *\n * `review_written` is still listed so older rows and clients parse, but it is\n * NEVER awarded (0.7.0): writing a review is your own act, and the score\n * counts only what others recognise. A review earns through Helpful votes.\n *\n * Nothing a member does for their OWN product counts, and nothing their own\n * product receives, so the score cannot be bought with a popular product.\n *\n * Against farming: one member can earn at most `CONTRIBUTION_PAIR_POINTS_MAX`\n * points from any ONE other member over 30 days, so two founders cannot trade\n * Helpful marks into a high score.\n *\n * EVERY POINT IS A ROW (the ledger), so a score can be explained (\"how did I\n * earn this?\"), checked and re-tuned. A point stands while what earned it\n * stands: unmarking, withdrawing a vote, deleting the review or comment, or\n * staff removing it takes the point back. An hourly job recounts every score\n * from the ledger, so a total can never drift for long.\n *\n * Routes (all signed in):\n * GET /api/v1/contributions/me paginationQuerySchema -> contributionPageSchema\n * GET /api/v1/contributions/leaderboard contributionLeaderboardQuerySchema\n * -> contributionLeaderboardSchema\n *\n * The score itself is on the profile and the card (`contributionSummarySchema`).\n *\n * @module founder-schema/domains/contributions\n */\n\nimport { z } from 'zod';\nimport { emptyStringToUndefined, objectIdSchema } from '../../core/common.js';\nimport { paginatedSchema } from '../../core/responses.js';\nimport { contentKindSchema } from '../feed/feed.js';\nimport { productChipSchema } from '../products/products.js';\nimport { memberBylineSchema } from '../profile/founder-profile.js';\n\n/** ⚠ Stored per ledger row: append only. */\nexport const CONTRIBUTION_KIND_VALUES = [\n 'feedback_helpful',\n 'tester_confirmed',\n 'review_helpful',\n 'review_written',\n] as const;\nexport const contributionKindSchema = z.enum(CONTRIBUTION_KIND_VALUES);\n/**\n * The kinds that are actually awarded (0.7.0): every kind but `review_written`,\n * which is never awarded any more. \"How points are earned\" lists these.\n */\nexport const CONTRIBUTION_AWARDED_KIND_VALUES = CONTRIBUTION_KIND_VALUES.filter(\n (k): k is Exclude<(typeof CONTRIBUTION_KIND_VALUES)[number], 'review_written'> =>\n k !== 'review_written',\n);\nexport type ContributionKind = z.infer<typeof contributionKindSchema>;\n\n/**\n * Points per kind. Exhaustive by type. Changing a number changes FUTURE rows;\n * the hourly recount sums what each row was actually awarded.\n */\nexport const CONTRIBUTION_POINTS = {\n feedback_helpful: 10,\n tester_confirmed: 15,\n review_helpful: 2,\n review_written: 3,\n} as const satisfies Record<ContributionKind, number>;\n\n/** One line of \"how I earned my points\". */\nexport const contributionEntrySchema = z.object({\n id: objectIdSchema,\n kind: contributionKindSchema,\n /** What it was actually awarded: below the kind's points when a cap applied. */\n points: z.number().int().positive(),\n /**\n * Who recognised it; null for a deleted account, and for `review_helpful`\n * (a Helpful vote is anonymous: the review shows only a count).\n */\n actor: memberBylineSchema.nullable(),\n /** The product it was about, while it exists. */\n product: productChipSchema.optional(),\n /**\n * What earned it, so the row opens it: the post or poll and the comment for\n * `feedback_helpful`, the review for `review_helpful`.\n */\n content: z.object({ kind: contentKindSchema, id: objectIdSchema }).optional(),\n commentId: objectIdSchema.optional(),\n reviewId: objectIdSchema.optional(),\n createdAt: z.iso.datetime(),\n});\nexport type ContributionEntry = z.infer<typeof contributionEntrySchema>;\n\nexport const contributionPageSchema = paginatedSchema(contributionEntrySchema);\nexport type ContributionPage = z.infer<typeof contributionPageSchema>;\n\n/**\n * Which points a board ranks by (0.10.0): `recent`, the last\n * `CONTRIBUTION_RECENT_DAYS` days (the default, so a newer member can still\n * climb), or `all`, every point ever earned. Every board is GLOBAL: one list\n * for the whole circle (the user's ruling, 2026-10-11).\n */\nexport const CONTRIBUTION_PERIOD_VALUES = ['recent', 'all'] as const;\nexport const contributionPeriodSchema = z.enum(CONTRIBUTION_PERIOD_VALUES);\nexport type ContributionPeriod = z.infer<typeof contributionPeriodSchema>;\n\n/** `GET /api/v1/contributions/leaderboard?period=` (0.10.0). A query string, so the default is the server's. */\nexport const contributionLeaderboardQuerySchema = z.object({\n // `?period=` with nothing after it is no period, as on every other query.\n period: z.preprocess(emptyStringToUndefined, contributionPeriodSchema.default('recent')),\n});\nexport type ContributionLeaderboardQuery = z.infer<typeof contributionLeaderboardQuerySchema>;\n\n/** One row of a board: its rank, who, and their points in the period. */\nexport const contributionLeaderEntrySchema = z.object({\n rank: z.number().int().positive(),\n member: memberBylineSchema,\n /** Points earned in the period. */\n points: z.number().int().positive(),\n});\nexport type ContributionLeaderEntry = z.infer<typeof contributionLeaderEntrySchema>;\n\n/**\n * The signed-in member's own place on the board (0.10.0), on its first page or\n * not. Ranked in the SAME order, among the SAME members, as the list, so their\n * rank is always their position. `rank` is null while they are not on the\n * board: no points in the period (a board ranks help given, and nobody is\n * 40th for nothing), or a profile no longer listed. `points` are theirs either\n * way.\n */\nexport const contributionYouSchema = z.object({\n rank: z.number().int().positive().nullable(),\n points: z.number().int().nonnegative(),\n});\nexport type ContributionYou = z.infer<typeof contributionYouSchema>;\n\n/**\n * Top helpers, best first, at most `CONTRIBUTION_LEADERBOARD_SIZE`. Read from\n * the live counters, which move the moment help is recognised; only points\n * leaving the `recent` window wait for the hourly recount. `computedAt` is the\n * latest recount among the rows shown (null before the first run, and for an\n * empty board).\n *\n * `period` and `you` (0.10.0) are OPTIONAL on read, so this client keeps\n * parsing a server from before them: without `you` the board simply shows no\n * row of the member's own.\n */\nexport const contributionLeaderboardSchema = z.object({\n period: contributionPeriodSchema.optional(),\n items: z.array(contributionLeaderEntrySchema),\n you: contributionYouSchema.optional(),\n computedAt: z.iso.datetime().nullable(),\n});\nexport type ContributionLeaderboard = z.infer<typeof contributionLeaderboardSchema>;\n"]}
|
|
@@ -24,7 +24,10 @@ import { z } from 'zod';
|
|
|
24
24
|
/**
|
|
25
25
|
* How someone came to hold a membership. ⚠ Stored: append only.
|
|
26
26
|
*
|
|
27
|
-
* founding
|
|
27
|
+
* founding free for life, numbered "Founding member #N": one of the first
|
|
28
|
+
* `FOUNDING_PLACES` people to join the launch WAITLIST (0.14.0,
|
|
29
|
+
* the user's rule of 2026-10-16). Before that, the founding
|
|
30
|
+
* coupon (`FIRST100`), which no longer exists on main.
|
|
28
31
|
* coupon another free-membership coupon
|
|
29
32
|
* paid Rs 99, captured
|
|
30
33
|
* granted staff gave it (a partner, a press account), with a reason on record
|
|
@@ -79,7 +82,12 @@ export declare const PLAN_SPECS: Readonly<Record<PlanTier, {
|
|
|
79
82
|
prices: Readonly<Record<PlanPeriod, number>>;
|
|
80
83
|
limits: PlanLimits;
|
|
81
84
|
}>>;
|
|
82
|
-
/**
|
|
85
|
+
/**
|
|
86
|
+
* Free founding memberships (0.14.0): the first 100 people to join the launch
|
|
87
|
+
* waitlist get their membership free, numbered #1 to #100 by their place
|
|
88
|
+
* (the user, 2026-10-16). Everyone else pays `MEMBERSHIP_PRICE_PAISE` once.
|
|
89
|
+
* No code gives a founding place any more.
|
|
90
|
+
*/
|
|
83
91
|
export declare const FOUNDING_PLACES = 100;
|
|
84
92
|
export declare const planOfferSchema: z.ZodObject<{
|
|
85
93
|
tier: z.ZodEnum<{
|
|
@@ -186,6 +194,7 @@ export declare const meMembershipSchema: z.ZodObject<{
|
|
|
186
194
|
products: z.ZodObject<{
|
|
187
195
|
live: z.ZodNumber;
|
|
188
196
|
owned: z.ZodNumber;
|
|
197
|
+
counted: z.ZodNumber;
|
|
189
198
|
}, z.core.$strip>;
|
|
190
199
|
messages: z.ZodObject<{
|
|
191
200
|
used: z.ZodNumber;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"membership.d.ts","sourceRoot":"","sources":["../../../src/domains/membership/membership.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB
|
|
1
|
+
{"version":3,"file":"membership.d.ts","sourceRoot":"","sources":["../../../src/domains/membership/membership.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,oDAAqD,CAAC;AACzF,eAAO,MAAM,oBAAoB;;;;;EAAiC,CAAC;AACnE,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,yFAAyF;AACzF,eAAO,MAAM,gBAAgB,kCAAmC,CAAC;AACjE,eAAO,MAAM,cAAc;;;EAA2B,CAAC;AACvD,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD,yDAAyD;AACzD,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAA+B,CAAC;AAE9F,mFAAmF;AACnF,eAAO,MAAM,kBAAkB,4BAA6B,CAAC;AAC7D,eAAO,MAAM,gBAAgB;;;EAA6B,CAAC;AAC3D,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,sDAAsD;AACtD,eAAO,MAAM,WAAW,aAAiC,CAAC;AAC1D,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB;;;iBAG3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAW1D,sCAAsC;AACtC,eAAO,MAAM,sBAAsB,OAAQ,CAAC;AAE5C,qGAAqG;AACrG,eAAO,MAAM,mBAAmB,EAAE,UAAsD,CAAC;AAEzF,+DAA+D;AAC/D,eAAO,MAAM,UAAU,EAAE,QAAQ,CAC/B,MAAM,CACJ,QAAQ,EACR;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CACnF,CAYF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,MAAM,CAAC;AAInC,eAAO,MAAM,eAAe;;;;;;;;;;;;;;iBAO1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiB5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAI5D,eAAO,MAAM,gBAAgB;;;;;;;;;iBAK3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,qFAAqF;AACrF,eAAO,MAAM,eAAe;;;;;;;iBAI1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiC7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
|
|
@@ -26,7 +26,10 @@ import { billingDetailsSchema } from '../billing/gst.js';
|
|
|
26
26
|
/**
|
|
27
27
|
* How someone came to hold a membership. ⚠ Stored: append only.
|
|
28
28
|
*
|
|
29
|
-
* founding
|
|
29
|
+
* founding free for life, numbered "Founding member #N": one of the first
|
|
30
|
+
* `FOUNDING_PLACES` people to join the launch WAITLIST (0.14.0,
|
|
31
|
+
* the user's rule of 2026-10-16). Before that, the founding
|
|
32
|
+
* coupon (`FIRST100`), which no longer exists on main.
|
|
30
33
|
* coupon another free-membership coupon
|
|
31
34
|
* paid Rs 99, captured
|
|
32
35
|
* granted staff gave it (a partner, a press account), with a reason on record
|
|
@@ -80,7 +83,12 @@ export const PLAN_SPECS = {
|
|
|
80
83
|
limits: { productsLive: null, newPeoplePerMonth: 50 },
|
|
81
84
|
},
|
|
82
85
|
};
|
|
83
|
-
/**
|
|
86
|
+
/**
|
|
87
|
+
* Free founding memberships (0.14.0): the first 100 people to join the launch
|
|
88
|
+
* waitlist get their membership free, numbered #1 to #100 by their place
|
|
89
|
+
* (the user, 2026-10-16). Everyone else pays `MEMBERSHIP_PRICE_PAISE` once.
|
|
90
|
+
* No code gives a founding place any more.
|
|
91
|
+
*/
|
|
84
92
|
export const FOUNDING_PLACES = 100;
|
|
85
93
|
// ─── GET /plans ──────────────────────────────────────────────────────────────
|
|
86
94
|
export const planOfferSchema = z.object({
|
|
@@ -140,6 +148,12 @@ export const meMembershipSchema = z.object({
|
|
|
140
148
|
live: z.number().int().nonnegative(),
|
|
141
149
|
/** Products they own, paused ones included. */
|
|
142
150
|
owned: z.number().int().nonnegative(),
|
|
151
|
+
/**
|
|
152
|
+
* What adding another counts against (0.13.0): live ones and the ones
|
|
153
|
+
* they paused themselves, not those a plan's end paused. "Add a product"
|
|
154
|
+
* stops when this reaches `limits.productsLive`: pausing never makes room.
|
|
155
|
+
*/
|
|
156
|
+
counted: z.number().int().nonnegative(),
|
|
143
157
|
}),
|
|
144
158
|
messages: z.object({
|
|
145
159
|
/** New people they started a conversation with this month (India time). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"membership.js","sourceRoot":"","sources":["../../../src/domains/membership/membership.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema: membership and plans, as a member holds them (0.12.0)\n *\n * GET /api/v1/plans planCatalogSchema (anyone: prices and what each includes)\n * GET /api/v1/me/membership meMembershipSchema (signed in: what I hold, what I have used)\n *\n * THE USER'S RULING (2026-10-11): looking is free; taking part needs a\n * MEMBERSHIP, Rs 99 once, for life (the first 100 free with a coupon). On top,\n * two PLANS: Premium (5 live products, 15 new people a month to message) and\n * Business (unlimited products, 50 new people). Prices and plan contents come\n * from the server, so a change needs no client release.\n *\n * A PLAN IS TIME, PREPAID, IN TWO BLOCKS: Business time first, then Premium\n * time. Buying more APPENDS; buying Premium while on Business waits its turn;\n * buying Business while on Premium is an upgrade that turns the Premium time\n * left into Business for the difference (`domains/billing`). `blocks` says it\n * as it stands, from now on.\n *\n * Money is integer PAISE everywhere in this package: never a float of rupees.\n *\n * @module founder-schema/domains/membership\n */\n\nimport { z } from 'zod';\nimport { standingSchema } from '../../access/standing.js';\nimport { billingDetailsSchema } from '../billing/gst.js';\n\n/**\n * How someone came to hold a membership. ⚠ Stored: append only.\n *\n * founding the founding coupon (`FIRST100`): free for life, numbered\n * coupon another free-membership coupon\n * paid Rs 99, captured\n * granted staff gave it (a partner, a press account), with a reason on record\n */\nexport const MEMBERSHIP_KIND_VALUES = ['founding', 'coupon', 'paid', 'granted'] as const;\nexport const membershipKindSchema = z.enum(MEMBERSHIP_KIND_VALUES);\nexport type MembershipKind = z.infer<typeof membershipKindSchema>;\n\n/** ⚠ Stored: append only. Highest last is NOT implied: compare with `PLAN_TIER_RANK`. */\nexport const PLAN_TIER_VALUES = ['premium', 'business'] as const;\nexport const planTierSchema = z.enum(PLAN_TIER_VALUES);\nexport type PlanTier = z.infer<typeof planTierSchema>;\n\n/** Which tier outranks which: Business above Premium. */\nexport const PLAN_TIER_RANK: Readonly<Record<PlanTier, number>> = { premium: 1, business: 2 };\n\n/** One payment buys a calendar month or a calendar year. ⚠ Stored: append only. */\nexport const PLAN_PERIOD_VALUES = ['month', 'year'] as const;\nexport const planPeriodSchema = z.enum(PLAN_PERIOD_VALUES);\nexport type PlanPeriod = z.infer<typeof planPeriodSchema>;\n\n/** Integer paise, never negative. Rs 399 is 39900. */\nexport const paiseSchema = z.number().int().nonnegative();\nexport type Paise = z.infer<typeof paiseSchema>;\n\n/**\n * What a standing or a plan allows that is COUNTED (the ability says whether;\n * these say how many).\n *\n * productsLive products live at once; null is unlimited (the server\n * still holds a safety ceiling, `PRODUCTS_LIVE_SAFETY_MAX`)\n * newPeoplePerMonth conversations a month with people you do not know\n */\nexport const planLimitsSchema = z.object({\n productsLive: z.number().int().nonnegative().nullable(),\n newPeoplePerMonth: z.number().int().nonnegative(),\n});\nexport type PlanLimits = z.infer<typeof planLimitsSchema>;\n\n// ─── The price list ──────────────────────────────────────────────────────────\n//\n// THE ONE PLACE a price or a plan's limit is written (0.12.0). The backend\n// prices and counts from these (`modules/plans/catalog.ts`), `GET /plans`\n// serves them, and the web's Terms of Service are written FROM them, so the\n// Terms can never state a price the server does not charge. A change here is\n// a schema release, then the backend, then the web. Integer PAISE, GST\n// included. hary's India-first prices (the user: \"you decide, India first\").\n\n/** The membership: once, for life. */\nexport const MEMBERSHIP_PRICE_PAISE = 9_900;\n\n/** What a member with no plan may: 1 live product, no conversations with people they do not know. */\nexport const MEMBER_LIMIT_VALUES: PlanLimits = { productsLive: 1, newPeoplePerMonth: 0 };\n\n/** Each plan's name, prices and limits. Exhaustive by type. */\nexport const PLAN_SPECS: Readonly<\n Record<\n PlanTier,\n { name: string; prices: Readonly<Record<PlanPeriod, number>>; limits: PlanLimits }\n >\n> = {\n premium: {\n name: 'Premium',\n prices: { month: 39_900, year: 399_900 },\n limits: { productsLive: 5, newPeoplePerMonth: 15 },\n },\n business: {\n name: 'Business',\n prices: { month: 99_900, year: 999_900 },\n limits: { productsLive: null, newPeoplePerMonth: 50 },\n },\n};\n\n/** Free founding memberships on the seeded `FIRST100` code. */\nexport const FOUNDING_PLACES = 100;\n\n// ─── GET /plans ──────────────────────────────────────────────────────────────\n\nexport const planOfferSchema = z.object({\n tier: planTierSchema,\n /** \"Premium\", \"Business\": the server's word, so a rename needs no client release. */\n name: z.string(),\n /** GST included. */\n prices: z.object({ month: paiseSchema, year: paiseSchema }),\n limits: planLimitsSchema,\n});\nexport type PlanOffer = z.infer<typeof planOfferSchema>;\n\nexport const planCatalogSchema = z.object({\n currency: z.literal('INR'),\n membership: z.object({\n /** Once, for life, GST included. */\n price: paiseSchema,\n limits: planLimitsSchema,\n }),\n /** Lowest first. */\n plans: z.array(planOfferSchema),\n /** Every purchase is refunded in full within this many days of paying. */\n refundWindowDays: z.number().int().positive(),\n /**\n * Whether a payment can be taken here now. False when the gateway is not\n * configured: a free coupon still works, a paid purchase answers\n * `PAYMENTS_UNAVAILABLE`.\n */\n paymentsEnabled: z.boolean(),\n});\nexport type PlanCatalog = z.infer<typeof planCatalogSchema>;\n\n// ─── GET /me/membership ──────────────────────────────────────────────────────\n\nexport const membershipSchema = z.object({\n kind: membershipKindSchema,\n since: z.iso.datetime(),\n /** \"Founding member #N\": set only for `founding`. */\n foundingNumber: z.number().int().positive().nullable(),\n});\nexport type Membership = z.infer<typeof membershipSchema>;\n\n/** A stretch of one tier. Blocks never overlap and follow each other with no gap. */\nexport const planBlockSchema = z.object({\n tier: planTierSchema,\n startsAt: z.iso.datetime(),\n endsAt: z.iso.datetime(),\n});\nexport type PlanBlock = z.infer<typeof planBlockSchema>;\n\nexport const meMembershipSchema = z.object({\n standing: standingSchema,\n /** Null for a viewer, and for staff who hold none (they take part through their role). */\n membership: membershipSchema.nullable(),\n /** Why they take part: their membership, or a staff role (staff are members). Null for a viewer. */\n through: z.enum(['membership', 'staff']).nullable(),\n plan: z.object({\n /** The tier in force now; null with no plan. */\n tier: planTierSchema.nullable(),\n /** From now on, Business first, then Premium. Empty with no plan. */\n blocks: z.array(planBlockSchema),\n }),\n limits: planLimitsSchema,\n products: z.object({\n /** Products they own that are live now. */\n live: z.number().int().nonnegative(),\n /** Products they own, paused ones included. */\n owned: z.number().int().nonnegative(),\n }),\n messages: z.object({\n /** New people they started a conversation with this month (India time). */\n used: z.number().int().nonnegative(),\n /** When this month's count starts again. */\n resetsAt: z.iso.datetime(),\n }),\n /** The billing details they last used, to prefill the next checkout; null before the first. */\n billing: billingDetailsSchema.nullable(),\n});\nexport type MeMembership = z.infer<typeof meMembershipSchema>;\n"]}
|
|
1
|
+
{"version":3,"file":"membership.js","sourceRoot":"","sources":["../../../src/domains/membership/membership.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema: membership and plans, as a member holds them (0.12.0)\n *\n * GET /api/v1/plans planCatalogSchema (anyone: prices and what each includes)\n * GET /api/v1/me/membership meMembershipSchema (signed in: what I hold, what I have used)\n *\n * THE USER'S RULING (2026-10-11): looking is free; taking part needs a\n * MEMBERSHIP, Rs 99 once, for life (the first 100 free with a coupon). On top,\n * two PLANS: Premium (5 live products, 15 new people a month to message) and\n * Business (unlimited products, 50 new people). Prices and plan contents come\n * from the server, so a change needs no client release.\n *\n * A PLAN IS TIME, PREPAID, IN TWO BLOCKS: Business time first, then Premium\n * time. Buying more APPENDS; buying Premium while on Business waits its turn;\n * buying Business while on Premium is an upgrade that turns the Premium time\n * left into Business for the difference (`domains/billing`). `blocks` says it\n * as it stands, from now on.\n *\n * Money is integer PAISE everywhere in this package: never a float of rupees.\n *\n * @module founder-schema/domains/membership\n */\n\nimport { z } from 'zod';\nimport { standingSchema } from '../../access/standing.js';\nimport { billingDetailsSchema } from '../billing/gst.js';\n\n/**\n * How someone came to hold a membership. ⚠ Stored: append only.\n *\n * founding free for life, numbered \"Founding member #N\": one of the first\n * `FOUNDING_PLACES` people to join the launch WAITLIST (0.14.0,\n * the user's rule of 2026-10-16). Before that, the founding\n * coupon (`FIRST100`), which no longer exists on main.\n * coupon another free-membership coupon\n * paid Rs 99, captured\n * granted staff gave it (a partner, a press account), with a reason on record\n */\nexport const MEMBERSHIP_KIND_VALUES = ['founding', 'coupon', 'paid', 'granted'] as const;\nexport const membershipKindSchema = z.enum(MEMBERSHIP_KIND_VALUES);\nexport type MembershipKind = z.infer<typeof membershipKindSchema>;\n\n/** ⚠ Stored: append only. Highest last is NOT implied: compare with `PLAN_TIER_RANK`. */\nexport const PLAN_TIER_VALUES = ['premium', 'business'] as const;\nexport const planTierSchema = z.enum(PLAN_TIER_VALUES);\nexport type PlanTier = z.infer<typeof planTierSchema>;\n\n/** Which tier outranks which: Business above Premium. */\nexport const PLAN_TIER_RANK: Readonly<Record<PlanTier, number>> = { premium: 1, business: 2 };\n\n/** One payment buys a calendar month or a calendar year. ⚠ Stored: append only. */\nexport const PLAN_PERIOD_VALUES = ['month', 'year'] as const;\nexport const planPeriodSchema = z.enum(PLAN_PERIOD_VALUES);\nexport type PlanPeriod = z.infer<typeof planPeriodSchema>;\n\n/** Integer paise, never negative. Rs 399 is 39900. */\nexport const paiseSchema = z.number().int().nonnegative();\nexport type Paise = z.infer<typeof paiseSchema>;\n\n/**\n * What a standing or a plan allows that is COUNTED (the ability says whether;\n * these say how many).\n *\n * productsLive products live at once; null is unlimited (the server\n * still holds a safety ceiling, `PRODUCTS_LIVE_SAFETY_MAX`)\n * newPeoplePerMonth conversations a month with people you do not know\n */\nexport const planLimitsSchema = z.object({\n productsLive: z.number().int().nonnegative().nullable(),\n newPeoplePerMonth: z.number().int().nonnegative(),\n});\nexport type PlanLimits = z.infer<typeof planLimitsSchema>;\n\n// ─── The price list ──────────────────────────────────────────────────────────\n//\n// THE ONE PLACE a price or a plan's limit is written (0.12.0). The backend\n// prices and counts from these (`modules/plans/catalog.ts`), `GET /plans`\n// serves them, and the web's Terms of Service are written FROM them, so the\n// Terms can never state a price the server does not charge. A change here is\n// a schema release, then the backend, then the web. Integer PAISE, GST\n// included. hary's India-first prices (the user: \"you decide, India first\").\n\n/** The membership: once, for life. */\nexport const MEMBERSHIP_PRICE_PAISE = 9_900;\n\n/** What a member with no plan may: 1 live product, no conversations with people they do not know. */\nexport const MEMBER_LIMIT_VALUES: PlanLimits = { productsLive: 1, newPeoplePerMonth: 0 };\n\n/** Each plan's name, prices and limits. Exhaustive by type. */\nexport const PLAN_SPECS: Readonly<\n Record<\n PlanTier,\n { name: string; prices: Readonly<Record<PlanPeriod, number>>; limits: PlanLimits }\n >\n> = {\n premium: {\n name: 'Premium',\n prices: { month: 39_900, year: 399_900 },\n limits: { productsLive: 5, newPeoplePerMonth: 15 },\n },\n business: {\n name: 'Business',\n prices: { month: 99_900, year: 999_900 },\n limits: { productsLive: null, newPeoplePerMonth: 50 },\n },\n};\n\n/**\n * Free founding memberships (0.14.0): the first 100 people to join the launch\n * waitlist get their membership free, numbered #1 to #100 by their place\n * (the user, 2026-10-16). Everyone else pays `MEMBERSHIP_PRICE_PAISE` once.\n * No code gives a founding place any more.\n */\nexport const FOUNDING_PLACES = 100;\n\n// ─── GET /plans ──────────────────────────────────────────────────────────────\n\nexport const planOfferSchema = z.object({\n tier: planTierSchema,\n /** \"Premium\", \"Business\": the server's word, so a rename needs no client release. */\n name: z.string(),\n /** GST included. */\n prices: z.object({ month: paiseSchema, year: paiseSchema }),\n limits: planLimitsSchema,\n});\nexport type PlanOffer = z.infer<typeof planOfferSchema>;\n\nexport const planCatalogSchema = z.object({\n currency: z.literal('INR'),\n membership: z.object({\n /** Once, for life, GST included. */\n price: paiseSchema,\n limits: planLimitsSchema,\n }),\n /** Lowest first. */\n plans: z.array(planOfferSchema),\n /** Every purchase is refunded in full within this many days of paying. */\n refundWindowDays: z.number().int().positive(),\n /**\n * Whether a payment can be taken here now. False when the gateway is not\n * configured: a free coupon still works, a paid purchase answers\n * `PAYMENTS_UNAVAILABLE`.\n */\n paymentsEnabled: z.boolean(),\n});\nexport type PlanCatalog = z.infer<typeof planCatalogSchema>;\n\n// ─── GET /me/membership ──────────────────────────────────────────────────────\n\nexport const membershipSchema = z.object({\n kind: membershipKindSchema,\n since: z.iso.datetime(),\n /** \"Founding member #N\": set only for `founding`. */\n foundingNumber: z.number().int().positive().nullable(),\n});\nexport type Membership = z.infer<typeof membershipSchema>;\n\n/** A stretch of one tier. Blocks never overlap and follow each other with no gap. */\nexport const planBlockSchema = z.object({\n tier: planTierSchema,\n startsAt: z.iso.datetime(),\n endsAt: z.iso.datetime(),\n});\nexport type PlanBlock = z.infer<typeof planBlockSchema>;\n\nexport const meMembershipSchema = z.object({\n standing: standingSchema,\n /** Null for a viewer, and for staff who hold none (they take part through their role). */\n membership: membershipSchema.nullable(),\n /** Why they take part: their membership, or a staff role (staff are members). Null for a viewer. */\n through: z.enum(['membership', 'staff']).nullable(),\n plan: z.object({\n /** The tier in force now; null with no plan. */\n tier: planTierSchema.nullable(),\n /** From now on, Business first, then Premium. Empty with no plan. */\n blocks: z.array(planBlockSchema),\n }),\n limits: planLimitsSchema,\n products: z.object({\n /** Products they own that are live now. */\n live: z.number().int().nonnegative(),\n /** Products they own, paused ones included. */\n owned: z.number().int().nonnegative(),\n /**\n * What adding another counts against (0.13.0): live ones and the ones\n * they paused themselves, not those a plan's end paused. \"Add a product\"\n * stops when this reaches `limits.productsLive`: pausing never makes room.\n */\n counted: z.number().int().nonnegative(),\n }),\n messages: z.object({\n /** New people they started a conversation with this month (India time). */\n used: z.number().int().nonnegative(),\n /** When this month's count starts again. */\n resetsAt: z.iso.datetime(),\n }),\n /** The billing details they last used, to prefill the next checkout; null before the first. */\n billing: billingDetailsSchema.nullable(),\n});\nexport type MeMembership = z.infer<typeof meMembershipSchema>;\n"]}
|
|
@@ -37,16 +37,6 @@ import { z } from 'zod';
|
|
|
37
37
|
export declare const MIN_AGE_YEARS = 18;
|
|
38
38
|
/** A sanity ceiling, not a product rule. Catches a mistyped year. */
|
|
39
39
|
export declare const MAX_AGE_YEARS = 120;
|
|
40
|
-
/**
|
|
41
|
-
* Whole years completed, by the calendar.
|
|
42
|
-
*
|
|
43
|
-
* ⚠️ NOT `elapsedMs / (365.25 * day)`, which is what the sibling product uses.
|
|
44
|
-
* That approximation reads 17.9986 at the start of somebody's eighteenth
|
|
45
|
-
* birthday and only crosses 18 about halfway through the day, so it refuses
|
|
46
|
-
* them for the half of their birthday they are most likely to be signing up on.
|
|
47
|
-
* Measured: it disagrees with the calendar on all 400 of the next 400 days.
|
|
48
|
-
* An age gate is exactly the place an approximation is not good enough.
|
|
49
|
-
*/
|
|
50
40
|
export declare function ageInYears(isoDate: string, now?: Date): number;
|
|
51
41
|
/** A calendar date with no time component, `YYYY-MM-DD`. */
|
|
52
42
|
export declare const isoDateSchema: z.ZodString;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../../../src/domains/onboarding/onboarding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAoCxB;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,qEAAqE;AACrE,eAAO,MAAM,aAAa,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../../../src/domains/onboarding/onboarding.ts"],"names":[],"mappings":"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"}
|
|
@@ -52,6 +52,8 @@ export const MAX_AGE_YEARS = 120;
|
|
|
52
52
|
* Measured: it disagrees with the calendar on all 400 of the next 400 days.
|
|
53
53
|
* An age gate is exactly the place an approximation is not good enough.
|
|
54
54
|
*/
|
|
55
|
+
/** India Standard Time, UTC+05:30, with no daylight saving. */
|
|
56
|
+
const INDIA_OFFSET_MS = (5 * 60 + 30) * 60 * 1000;
|
|
55
57
|
export function ageInYears(isoDate, now = new Date()) {
|
|
56
58
|
// ⚠️ A STRING, `YYYY-MM-DD`, not a `Date`. This is a wire contract, so it
|
|
57
59
|
// takes the wire type. The backend is the obvious caller and the backend
|
|
@@ -62,9 +64,13 @@ export function ageInYears(isoDate, now = new Date()) {
|
|
|
62
64
|
throw new TypeError('ageInYears expects a YYYY-MM-DD string, not a Date. Format it first, e.g. date.toISOString().slice(0, 10).');
|
|
63
65
|
}
|
|
64
66
|
const [y, m, d] = isoDate.split('-').map(Number);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
// The date TODAY IS IN INDIA (0.13.0), where the members are: a birthday
|
|
68
|
+
// begins at midnight there, not five and a half hours later at UTC's
|
|
69
|
+
// midnight. India keeps one offset all year.
|
|
70
|
+
const today = new Date(now.getTime() + INDIA_OFFSET_MS);
|
|
71
|
+
let age = today.getUTCFullYear() - y;
|
|
72
|
+
const monthNow = today.getUTCMonth() + 1;
|
|
73
|
+
if (monthNow < m || (monthNow === m && today.getUTCDate() < d))
|
|
68
74
|
age -= 1;
|
|
69
75
|
return age;
|
|
70
76
|
}
|