founder-schema 0.8.0 → 0.10.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.
Files changed (77) hide show
  1. package/README.md +68 -0
  2. package/dist/access/capabilities.d.ts +34 -0
  3. package/dist/access/capabilities.d.ts.map +1 -0
  4. package/dist/access/capabilities.js +35 -0
  5. package/dist/access/capabilities.js.map +1 -0
  6. package/dist/access/permissions.d.ts +20 -1
  7. package/dist/access/permissions.d.ts.map +1 -1
  8. package/dist/access/permissions.js +4 -0
  9. package/dist/access/permissions.js.map +1 -1
  10. package/dist/core/common.d.ts +8 -0
  11. package/dist/core/common.d.ts.map +1 -1
  12. package/dist/core/common.js +10 -1
  13. package/dist/core/common.js.map +1 -1
  14. package/dist/core/errors.d.ts +33 -1
  15. package/dist/core/errors.d.ts.map +1 -1
  16. package/dist/core/errors.js +57 -0
  17. package/dist/core/errors.js.map +1 -1
  18. package/dist/core/limits.d.ts +12 -0
  19. package/dist/core/limits.d.ts.map +1 -1
  20. package/dist/core/limits.js +13 -0
  21. package/dist/core/limits.js.map +1 -1
  22. package/dist/core/responses.d.ts +47 -0
  23. package/dist/core/responses.d.ts.map +1 -1
  24. package/dist/core/responses.js +7 -0
  25. package/dist/core/responses.js.map +1 -1
  26. package/dist/core/validation.d.ts +6 -0
  27. package/dist/core/validation.d.ts.map +1 -1
  28. package/dist/core/validation.js +6 -0
  29. package/dist/core/validation.js.map +1 -1
  30. package/dist/domains/audit/audit.d.ts +47 -3
  31. package/dist/domains/audit/audit.d.ts.map +1 -1
  32. package/dist/domains/audit/audit.js +40 -4
  33. package/dist/domains/audit/audit.js.map +1 -1
  34. package/dist/domains/communities/communities.d.ts +1537 -0
  35. package/dist/domains/communities/communities.d.ts.map +1 -0
  36. package/dist/domains/communities/communities.js +464 -0
  37. package/dist/domains/communities/communities.js.map +1 -0
  38. package/dist/domains/contributions/contributions.d.ts +53 -4
  39. package/dist/domains/contributions/contributions.d.ts.map +1 -1
  40. package/dist/domains/contributions/contributions.js +39 -5
  41. package/dist/domains/contributions/contributions.js.map +1 -1
  42. package/dist/domains/feed/feed.d.ts +479 -0
  43. package/dist/domains/feed/feed.d.ts.map +1 -1
  44. package/dist/domains/feed/feed.js +45 -1
  45. package/dist/domains/feed/feed.js.map +1 -1
  46. package/dist/domains/media/media.d.ts +7 -1
  47. package/dist/domains/media/media.d.ts.map +1 -1
  48. package/dist/domains/media/media.js +5 -0
  49. package/dist/domains/media/media.js.map +1 -1
  50. package/dist/domains/members/members.d.ts +6 -0
  51. package/dist/domains/members/members.d.ts.map +1 -1
  52. package/dist/domains/members/members.js +5 -0
  53. package/dist/domains/members/members.js.map +1 -1
  54. package/dist/domains/moderation/moderation.d.ts +4 -4
  55. package/dist/domains/notifications/notifications.d.ts +82 -2
  56. package/dist/domains/notifications/notifications.d.ts.map +1 -1
  57. package/dist/domains/notifications/notifications.js +45 -0
  58. package/dist/domains/notifications/notifications.js.map +1 -1
  59. package/dist/domains/onboarding/onboarding.d.ts +11 -11
  60. package/dist/domains/onboarding/onboarding.d.ts.map +1 -1
  61. package/dist/domains/onboarding/onboarding.js +2 -2
  62. package/dist/domains/onboarding/onboarding.js.map +1 -1
  63. package/dist/domains/profile/founder-profile.d.ts +60 -4
  64. package/dist/domains/profile/founder-profile.d.ts.map +1 -1
  65. package/dist/domains/profile/founder-profile.js +66 -5
  66. package/dist/domains/profile/founder-profile.js.map +1 -1
  67. package/dist/domains/team/team.d.ts +22 -0
  68. package/dist/domains/team/team.d.ts.map +1 -1
  69. package/dist/domains/team/team.js +22 -0
  70. package/dist/domains/team/team.js.map +1 -1
  71. package/dist/domains/uploads/uploads.d.ts +1 -0
  72. package/dist/domains/uploads/uploads.d.ts.map +1 -1
  73. package/dist/index.d.ts +8 -6
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +9 -6
  76. package/dist/index.js.map +1 -1
  77. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"communities.d.ts","sourceRoot":"","sources":["../../../src/domains/communities/communities.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,464 @@
1
+ /**
2
+ * founder-schema — Communities: official spaces inside Founder Circle (0.9.0)
3
+ *
4
+ * THE USER'S DECISIONS (2026-10-08), each asked on its own:
5
+ *
6
+ * - OFFICIAL: managers, super admins, and members given the
7
+ * `create_communities` capability create them. Everyone else SUGGESTS one;
8
+ * managers and super admins review the suggestions.
9
+ * - THREE TYPES, three separate questions answered per type:
10
+ *
11
+ * public private hidden
12
+ * listed in Discover yes yes no
13
+ * header (name, about, signed-in signed-in members and
14
+ * rules, admins, count) members members invitees (404 otherwise)
15
+ * posts, polls, members signed-in members only members only
16
+ * joining one tap request, then invite only,
17
+ * approval then accept
18
+ *
19
+ * Taking part (post, poll, comment, react, vote, save, report) is for
20
+ * members of every type. "Members" means ACTIVE members: someone who
21
+ * requested, was invited or is banned is a non-member for every rule.
22
+ * - PUBLIC MEANS PUBLIC INSIDE FOUNDER CIRCLE: a signed-out visitor sees no
23
+ * community content of any type, even by a shared link.
24
+ * - THE HOME FEED adds only the communities the member joined.
25
+ * - NO CHAT IN 0.9.0 (feed-first). Announcements are pinned posts
26
+ * (`COMMUNITY_PINS_MAX`), optionally notifying every member.
27
+ *
28
+ * A banned member sees the community as hidden (404 everywhere) until
29
+ * unbanned. Platform staff acting as moderator, manager or super admin READ
30
+ * every community, as they read every post, and moderate its content through
31
+ * the staff moderation queue; to take part they join like anyone else.
32
+ *
33
+ * Refusals follow what the caller can SEE: 404 for something they may not
34
+ * see (the same as for something that does not exist, so nothing is
35
+ * probed), 403 `COMMUNITY_JOIN_REQUIRED` for something they see but must join
36
+ * to read or to take part in.
37
+ *
38
+ * Content in a community carries no product and no ask, and cannot be
39
+ * reposted (a repost is general content, readable signed out): two leak paths
40
+ * closed by construction. Design: founder-circle-backend `docs/communities.md`.
41
+ *
42
+ * Inside a community:
43
+ *
44
+ * member moderator admin manager,
45
+ * super admin
46
+ * post, poll, comment, react, vote ✓ ✓ ✓ ✓ (a member)
47
+ * approve requests, invite, pin, hide ✓ ✓ ✓
48
+ * remove a member (not a moderator) ✓ ✓ ✓
49
+ * remove a moderator ✓ ✓
50
+ * remove or ban an admin ✓
51
+ * notify all, ban, roles, edit, type ✓ ✓
52
+ * archive, delete ✓
53
+ *
54
+ * An admin may unmake another admin (then act on them as on a member). Each
55
+ * member row says what this viewer may do to it (`communityMemberSchema.viewer`):
56
+ * a client never works it out from roles.
57
+ *
58
+ * A community always keeps an admin while anyone can be one
59
+ * (`COMMUNITY_LAST_ADMIN`): an admin leaves, steps down, or deletes their
60
+ * account only once someone else is admin (the user's rule, 2026-10-08). When
61
+ * nobody else is active, or the account was deleted where it cannot be
62
+ * refused (central sign-in), the community is ORPHANED: managers and super
63
+ * admins run it (they can act on any community), it is flagged in the audit
64
+ * log (`community.orphaned`), and the page says so to them (`orphaned`).
65
+ *
66
+ * Routes (all signed in):
67
+ * GET /api/v1/communities communityDiscoverQuerySchema -> communityCardPageSchema
68
+ * GET /api/v1/communities/mine paginationQuerySchema -> communityCardPageSchema
69
+ * GET /api/v1/communities/invites communityInvitesSchema (mine, waiting)
70
+ * GET /api/v1/communities/orphaned paginationQuerySchema -> communityCardPageSchema
71
+ * (managers, super admins: communities left with no admin, newest first)
72
+ * POST /api/v1/communities createCommunityBodySchema -> IdResponse
73
+ * GET /api/v1/communities/:communityId communitySchema
74
+ * PATCH /api/v1/communities/:communityId updateCommunityBodySchema -> communitySchema
75
+ * PUT /api/v1/communities/:communityId/icon setCommunityIconBodySchema -> communitySchema
76
+ * DELETE /api/v1/communities/:communityId/icon -> communitySchema
77
+ * PUT /api/v1/communities/:communityId/visibility setCommunityVisibilityBodySchema -> communitySchema
78
+ * PUT /api/v1/communities/:communityId/status setCommunityStatusBodySchema -> communitySchema (staff)
79
+ * DELETE /api/v1/communities/:communityId -> IdResponse (staff)
80
+ * GET /api/v1/communities/:communityId/feed paginationQuerySchema -> FeedPage (newest first;
81
+ * pinned items are in it too, in their place)
82
+ * GET /api/v1/communities/:communityId/pinned PinnedItems (feed)
83
+ * POST /api/v1/communities/:communityId/pins pinCommunityContentBodySchema -> PinnedItems
84
+ * DELETE /api/v1/communities/:communityId/pins/:contentId -> PinnedItems
85
+ * POST /api/v1/communities/:communityId/join -> communitySchema (public: joined; private: requested)
86
+ * DELETE /api/v1/communities/:communityId/join cancel a request -> communitySchema
87
+ * POST /api/v1/communities/:communityId/leave -> IdResponse
88
+ * GET /api/v1/communities/:communityId/members communityMembersQuerySchema -> communityMemberPageSchema
89
+ * GET /api/v1/communities/:communityId/requests paginationQuerySchema -> communityRequestPageSchema
90
+ * PUT /api/v1/communities/:communityId/requests/:userId approve -> IdResponse
91
+ * DELETE /api/v1/communities/:communityId/requests/:userId decline -> IdResponse
92
+ * GET /api/v1/communities/:communityId/invites paginationQuerySchema -> communityPendingInvitePageSchema
93
+ * POST /api/v1/communities/:communityId/invites inviteCommunityMemberBodySchema -> communityPendingInviteSchema
94
+ * DELETE /api/v1/communities/:communityId/invites/:userId cancel -> IdResponse
95
+ * PUT /api/v1/communities/:communityId/invite accept (the invitee) -> communitySchema
96
+ * DELETE /api/v1/communities/:communityId/invite decline (the invitee) -> IdResponse
97
+ * PUT /api/v1/communities/:communityId/members/:userId/role setCommunityMemberRoleBodySchema -> IdResponse
98
+ * DELETE /api/v1/communities/:communityId/members/:userId remove -> IdResponse
99
+ * GET /api/v1/communities/:communityId/bans paginationQuerySchema -> communityBanPageSchema
100
+ * POST /api/v1/communities/:communityId/bans banCommunityMemberBodySchema -> IdResponse
101
+ * DELETE /api/v1/communities/:communityId/bans/:userId unban -> IdResponse
102
+ * PUT /api/v1/communities/:communityId/content/:contentId/hidden hide in the community -> IdResponse
103
+ * DELETE /api/v1/communities/:communityId/content/:contentId/hidden restore -> IdResponse
104
+ * PUT /api/v1/communities/:communityId/comments/:commentId/hidden hide a comment in it -> IdResponse
105
+ * DELETE /api/v1/communities/:communityId/comments/:commentId/hidden restore -> IdResponse
106
+ * GET /api/v1/communities/:communityId/hidden paginationQuerySchema -> FeedPage (moderators:
107
+ * what ITS moderators hid, newest first; a staff hide is staff's to undo and is not listed)
108
+ * GET /api/v1/communities/:communityId/hidden/comments paginationQuerySchema -> commentPageSchema
109
+ * POST /api/v1/community-suggestions createCommunitySuggestionBodySchema -> communitySuggestionSchema
110
+ * GET /api/v1/community-suggestions/mine paginationQuerySchema -> communitySuggestionPageSchema
111
+ * GET /api/v1/community-suggestions communitySuggestionsQuerySchema -> communitySuggestionPageSchema (staff)
112
+ * POST /api/v1/community-suggestions/:suggestionId/approve createCommunityBodySchema -> IdResponse (the community)
113
+ * POST /api/v1/community-suggestions/:suggestionId/decline declineCommunitySuggestionBodySchema -> communitySuggestionSchema
114
+ * (posting into a community: `communityId` on the feed's create bodies)
115
+ *
116
+ * @module founder-schema/domains/communities
117
+ */
118
+ import { z } from 'zod';
119
+ import { emptyStringToUndefined, multiValueQuery, objectIdSchema, optionalTrimmedString, paginationQuerySchema, } from '../../core/common.js';
120
+ import { COMMUNITY_BAN_REASON_MAX, COMMUNITY_DECLINE_REASON_MAX, COMMUNITY_DECLINE_REASON_MIN, COMMUNITY_DESCRIPTION_MAX, COMMUNITY_NAME_MAX, COMMUNITY_NAME_MIN, COMMUNITY_RULE_MAX, COMMUNITY_RULES_MAX, COMMUNITY_SUGGESTION_WHY_MAX, DISCOVER_FILTER_VALUES_MAX, DISCOVER_TEXT_MAX, } from '../../core/limits.js';
121
+ import { paginatedSchema } from '../../core/responses.js';
122
+ import { VALIDATION_MESSAGE } from '../../core/validation.js';
123
+ import { mediaImageSchema } from '../media/media.js';
124
+ import { topicSchema } from '../onboarding/onboarding.js';
125
+ import { memberBylineSchema } from '../profile/founder-profile.js';
126
+ /** Topics a community carries: what Discover filters by. */
127
+ export const COMMUNITY_TOPICS_MAX = 3;
128
+ /** Announcements pinned to the top of one community at once. */
129
+ export const COMMUNITY_PINS_MAX = 3;
130
+ /** Suggestions one member may have waiting for review at once. */
131
+ export const COMMUNITY_SUGGESTIONS_OPEN_MAX = 3;
132
+ /**
133
+ * The shortest time between two notify-everyone announcements in one
134
+ * community, so members are not spammed (`COMMUNITY_ANNOUNCE_TOO_SOON`).
135
+ */
136
+ export const COMMUNITY_ANNOUNCE_INTERVAL_MS = 24 * 60 * 60 * 1000;
137
+ // ─── Vocabulary ──────────────────────────────────────────────────────────────
138
+ // ⚠ Every list below is STORED. Append only; removing a value orphans rows.
139
+ /** See the module note's table. */
140
+ export const COMMUNITY_VISIBILITY_VALUES = ['public', 'private', 'hidden'];
141
+ export const communityVisibilitySchema = z.enum(COMMUNITY_VISIBILITY_VALUES);
142
+ /**
143
+ * How private each type is, least first. A change to a LOWER rank exposes what
144
+ * was already posted, so it needs `confirmExposure` (and is audited).
145
+ */
146
+ export const COMMUNITY_VISIBILITY_RANK = {
147
+ public: 0,
148
+ private: 1,
149
+ hidden: 2,
150
+ };
151
+ /** Whether moving from one type to another exposes what was already there. */
152
+ export function communityChangeExposes(from, to) {
153
+ return COMMUNITY_VISIBILITY_RANK[to] < COMMUNITY_VISIBILITY_RANK[from];
154
+ }
155
+ /** A member's role inside one community. */
156
+ export const COMMUNITY_ROLE_VALUES = ['admin', 'moderator', 'member'];
157
+ export const communityRoleSchema = z.enum(COMMUNITY_ROLE_VALUES);
158
+ /**
159
+ * Where someone stands with one community. `requested` (private: waiting for
160
+ * approval), `invited` (waiting for their answer), `banned` (the community is
161
+ * hidden from them). Leaving or being removed deletes the row.
162
+ */
163
+ export const COMMUNITY_MEMBER_STATUS_VALUES = ['active', 'requested', 'invited', 'banned'];
164
+ export const communityMemberStatusSchema = z.enum(COMMUNITY_MEMBER_STATUS_VALUES);
165
+ /** `archived`: read-only and out of Discover; members still read it. Staff only. */
166
+ export const COMMUNITY_STATUS_VALUES = ['active', 'archived'];
167
+ export const communityStatusSchema = z.enum(COMMUNITY_STATUS_VALUES);
168
+ export const COMMUNITY_SUGGESTION_STATUS_VALUES = ['open', 'approved', 'declined'];
169
+ export const communitySuggestionStatusSchema = z.enum(COMMUNITY_SUGGESTION_STATUS_VALUES);
170
+ // ─── Read shapes ─────────────────────────────────────────────────────────────
171
+ /**
172
+ * A community named on something else: "in AI Builders" on a post or poll, a
173
+ * notification. `iconUrl` is the icon's smallest size.
174
+ */
175
+ export const communityChipSchema = z.object({
176
+ id: objectIdSchema,
177
+ /** For a readable URL, `/communities/<id>/<slug>`. The id resolves; a renamed community keeps its links. */
178
+ slug: z.string(),
179
+ name: z.string(),
180
+ iconUrl: z.url().optional(),
181
+ visibility: communityVisibilitySchema,
182
+ /** Present (true) once it is archived: read only, nobody takes part. */
183
+ archived: z.boolean().optional(),
184
+ });
185
+ /** Where THIS viewer stands with a community, on a card. Banned viewers never see it. */
186
+ export const communityCardViewerSchema = z.object({
187
+ /** Null: not a member and nothing pending. Never `banned` (they get 404). */
188
+ status: communityMemberStatusSchema.exclude(['banned']).nullable(),
189
+ /** Their role while active, else null. */
190
+ role: communityRoleSchema.nullable(),
191
+ });
192
+ /** A community on Discover and in "your communities". */
193
+ export const communityCardSchema = z.object({
194
+ id: objectIdSchema,
195
+ slug: z.string(),
196
+ name: z.string(),
197
+ description: z.string(),
198
+ icon: mediaImageSchema.optional(),
199
+ visibility: communityVisibilitySchema,
200
+ status: communityStatusSchema,
201
+ topics: z.array(topicSchema).max(COMMUNITY_TOPICS_MAX),
202
+ /** Active members. */
203
+ memberCount: z.number().int().nonnegative(),
204
+ /** Visible posts and polls in it. */
205
+ postCount: z.number().int().nonnegative(),
206
+ viewer: communityCardViewerSchema.optional(),
207
+ });
208
+ /**
209
+ * What THIS viewer may do on the community page, decided by the server and
210
+ * sent as plain answers (the rule of `viewerStateSchema` on a post): a client
211
+ * never derives a power from `role`.
212
+ */
213
+ export const communityViewerSchema = communityCardViewerSchema.extend({
214
+ /** Read its posts, polls and comments now. False on a private one before joining. */
215
+ canRead: z.boolean(),
216
+ /** Read its member list. */
217
+ canSeeMembers: z.boolean(),
218
+ /** Public, active, and the viewer has no row (nothing pending): offer Join. */
219
+ canJoin: z.boolean(),
220
+ /** Private, active, and the viewer has no row: offer Request to join. */
221
+ canRequest: z.boolean(),
222
+ /** Active member, community not archived: the composer. */
223
+ canPost: z.boolean(),
224
+ /** Active member who is not its last admin. */
225
+ canLeave: z.boolean(),
226
+ /**
227
+ * Moderators and admins (and managers, super admins): requests, invites,
228
+ * pins, hiding, removing members. On an ARCHIVED community approving,
229
+ * inviting, accepting and pinning answer `COMMUNITY_ARCHIVED`; declining,
230
+ * cancelling, removing, banning, roles and hiding still work.
231
+ */
232
+ canModerate: z.boolean(),
233
+ /** Admins (and managers, super admins): details, icon, type, roles, bans. */
234
+ canManage: z.boolean(),
235
+ /** May notify every member with an announcement now (admins, at most once per `COMMUNITY_ANNOUNCE_INTERVAL_MS`). */
236
+ canAnnounce: z.boolean(),
237
+ /** Managers and super admins: archive, restore, delete. */
238
+ canArchive: z.boolean(),
239
+ });
240
+ /**
241
+ * One person in a community, live-joined like every byline. In the member
242
+ * list, admins first, then moderators, then members, each by when they joined.
243
+ * `viewer` (0.9.0, on the member list only) is what THIS viewer may do to
244
+ * them, the server's own rules (who outranks whom, never oneself, never the
245
+ * last admin).
246
+ */
247
+ export const communityMemberSchema = memberBylineSchema.extend({
248
+ role: communityRoleSchema,
249
+ joinedAt: z.iso.datetime(),
250
+ viewer: z
251
+ .object({
252
+ canSetRole: z.boolean(),
253
+ canRemove: z.boolean(),
254
+ canBan: z.boolean(),
255
+ })
256
+ .optional(),
257
+ });
258
+ /** The community page. The header is what a non-member of a private one may see too. */
259
+ export const communitySchema = communityCardSchema.extend({
260
+ rules: z.array(z.string()).max(COMMUNITY_RULES_MAX),
261
+ /**
262
+ * Its admins and moderators, admins first, by when they joined: who to ask.
263
+ * EMPTY for a viewer who may not read it (a private community before
264
+ * joining, a hidden one's invitee): who is in it is its members' to see.
265
+ */
266
+ team: z.array(communityMemberSchema),
267
+ createdAt: z.iso.datetime(),
268
+ updatedAt: z.iso.datetime(),
269
+ archivedAt: z.iso.datetime().nullable(),
270
+ /**
271
+ * Only for managers and super admins: it has no admin left (its last one's
272
+ * account was deleted where that cannot be refused), so staff run it until
273
+ * they make a member admin, which clears it.
274
+ */
275
+ orphaned: z.boolean().optional(),
276
+ viewer: communityViewerSchema.optional(),
277
+ });
278
+ /** A request to join a private community, as its moderators see it. */
279
+ export const communityRequestSchema = z.object({
280
+ member: memberBylineSchema,
281
+ requestedAt: z.iso.datetime(),
282
+ });
283
+ /** An invitation nobody has answered yet, as its moderators see it. */
284
+ export const communityPendingInviteSchema = z.object({
285
+ member: memberBylineSchema,
286
+ /** Null when whoever invited has since deleted their account. */
287
+ invitedBy: memberBylineSchema.nullable(),
288
+ invitedAt: z.iso.datetime(),
289
+ });
290
+ /** An invitation waiting for the viewer's answer. */
291
+ export const communityInviteSchema = z.object({
292
+ community: communityChipSchema.extend({ description: z.string() }),
293
+ invitedBy: memberBylineSchema.nullable(),
294
+ invitedAt: z.iso.datetime(),
295
+ });
296
+ /** `GET /communities/invites`. Never paged: invitations are few, and each needs an answer. */
297
+ export const communityInvitesSchema = z.object({ items: z.array(communityInviteSchema) });
298
+ /** Someone banned from a community, as its admins see it. */
299
+ export const communityBanSchema = z.object({
300
+ member: memberBylineSchema,
301
+ bannedBy: memberBylineSchema.nullable(),
302
+ bannedAt: z.iso.datetime(),
303
+ /** For the admins' own record; never shown to the banned member. */
304
+ reason: z.string().max(COMMUNITY_BAN_REASON_MAX).optional(),
305
+ });
306
+ /** A suggestion, as its author and the reviewers see it. */
307
+ export const communitySuggestionSchema = z.object({
308
+ id: objectIdSchema,
309
+ /** Null when its author has since deleted their account. */
310
+ suggestedBy: memberBylineSchema.nullable(),
311
+ name: z.string(),
312
+ description: z.string(),
313
+ why: z.string().optional(),
314
+ visibility: communityVisibilitySchema,
315
+ topics: z.array(topicSchema).max(COMMUNITY_TOPICS_MAX),
316
+ status: communitySuggestionStatusSchema,
317
+ decidedBy: memberBylineSchema.nullable(),
318
+ decidedAt: z.iso.datetime().nullable(),
319
+ /** Why it was declined, shown to its author. */
320
+ declineReason: z.string().optional(),
321
+ /**
322
+ * The community it became, once approved. Absent if that community is since
323
+ * deleted, or the viewer may no longer see it (banned from a hidden one).
324
+ */
325
+ community: communityChipSchema.optional(),
326
+ createdAt: z.iso.datetime(),
327
+ });
328
+ export const communityCardPageSchema = paginatedSchema(communityCardSchema);
329
+ export const communityMemberPageSchema = paginatedSchema(communityMemberSchema);
330
+ export const communityRequestPageSchema = paginatedSchema(communityRequestSchema);
331
+ export const communityPendingInvitePageSchema = paginatedSchema(communityPendingInviteSchema);
332
+ export const communityBanPageSchema = paginatedSchema(communityBanSchema);
333
+ export const communitySuggestionPageSchema = paginatedSchema(communitySuggestionSchema);
334
+ // ─── Queries and params ──────────────────────────────────────────────────────
335
+ /**
336
+ * `GET /communities`: public and private communities that are not archived
337
+ * (hidden ones never), most members first. `q` matches the start of the
338
+ * name; an emptied box (`?q=`) is no search, never a 400. `topic` means "any of".
339
+ */
340
+ export const communityDiscoverQuerySchema = paginationQuerySchema.extend({
341
+ q: optionalTrimmedString(DISCOVER_TEXT_MAX),
342
+ topic: multiValueQuery(topicSchema, DISCOVER_FILTER_VALUES_MAX),
343
+ });
344
+ /** `GET /communities/:id/members`: everyone, or one role (`?role=admin`). */
345
+ export const communityMembersQuerySchema = paginationQuerySchema.extend({
346
+ role: z.preprocess(emptyStringToUndefined, communityRoleSchema.optional()),
347
+ });
348
+ /** `GET /community-suggestions` (staff): waiting ones by default, oldest first. */
349
+ export const communitySuggestionsQuerySchema = paginationQuerySchema.extend({
350
+ status: z.preprocess(emptyStringToUndefined, communitySuggestionStatusSchema.default('open')),
351
+ });
352
+ export const communityParamsSchema = z.object({ communityId: objectIdSchema });
353
+ /** `/communities/:communityId/{members,requests,invites,bans}/:userId`. */
354
+ export const communityMemberParamsSchema = z.object({
355
+ communityId: objectIdSchema,
356
+ userId: objectIdSchema,
357
+ });
358
+ /** `/communities/:communityId/{pins,content}/:contentId`. */
359
+ export const communityContentParamsSchema = z.object({
360
+ communityId: objectIdSchema,
361
+ contentId: objectIdSchema,
362
+ });
363
+ /** `/communities/:communityId/comments/:commentId/hidden` (0.9.0). */
364
+ export const communityCommentParamsSchema = z.object({
365
+ communityId: objectIdSchema,
366
+ commentId: objectIdSchema,
367
+ });
368
+ export const communitySuggestionParamsSchema = z.object({ suggestionId: objectIdSchema });
369
+ // ─── Write bodies ────────────────────────────────────────────────────────────
370
+ // Each its own object with NO defaults (see `updateFounderProfileBodySchema`).
371
+ const nameSchema = z.string().trim().min(COMMUNITY_NAME_MIN).max(COMMUNITY_NAME_MAX);
372
+ const descriptionSchema = z.string().trim().min(1).max(COMMUNITY_DESCRIPTION_MAX);
373
+ const rulesSchema = z
374
+ .array(z.string().trim().min(1).max(COMMUNITY_RULE_MAX))
375
+ .max(COMMUNITY_RULES_MAX);
376
+ const topicsSchema = z
377
+ .array(topicSchema)
378
+ .max(COMMUNITY_TOPICS_MAX)
379
+ .refine((topics) => new Set(topics).size === topics.length, {
380
+ error: VALIDATION_MESSAGE.topicsDuplicate,
381
+ });
382
+ /**
383
+ * `POST /communities` (managers, super admins and capability holders), and
384
+ * the reviewer's edited version when approving a suggestion. The creator
385
+ * becomes its first admin. A name a live PUBLIC or PRIVATE community has is
386
+ * `COMMUNITY_NAME_TAKEN` (letter case ignored, runs of spaces read as one);
387
+ * hidden communities' names are never compared, so no name probes for one.
388
+ * Approving a private or hidden one invites its suggester. The icon is a
389
+ * processed `community_icon` upload.
390
+ */
391
+ export const createCommunityBodySchema = z.object({
392
+ name: nameSchema,
393
+ description: descriptionSchema,
394
+ visibility: communityVisibilitySchema,
395
+ rules: rulesSchema.optional(),
396
+ topics: topicsSchema.optional(),
397
+ iconMediaId: objectIdSchema.optional(),
398
+ });
399
+ /** `PATCH /communities/:id` (admins): only the keys that changed. Type and icon have their own routes. */
400
+ export const updateCommunityBodySchema = z.object({
401
+ name: nameSchema.optional(),
402
+ description: descriptionSchema.optional(),
403
+ /** Sent whole; empty clears them. */
404
+ rules: rulesSchema.optional(),
405
+ topics: topicsSchema.optional(),
406
+ });
407
+ /** `PUT /communities/:id/icon`: a processed `community_icon` upload. The old icon's files are deleted. */
408
+ export const setCommunityIconBodySchema = z.object({ mediaId: objectIdSchema });
409
+ /**
410
+ * `PUT /communities/:id/visibility` (admins). A change that exposes what was
411
+ * already posted (`communityChangeExposes`: toward public, or hidden to
412
+ * private) needs `confirmExposure: true`, else `COMMUNITY_EXPOSURE_UNCONFIRMED`:
413
+ * the web asks first, and a client cannot skip the question. Audited.
414
+ */
415
+ export const setCommunityVisibilityBodySchema = z.object({
416
+ visibility: communityVisibilitySchema,
417
+ confirmExposure: z.boolean().optional(),
418
+ });
419
+ /** `PUT /communities/:id/status` (staff). Audited. */
420
+ export const setCommunityStatusBodySchema = z.object({ status: communityStatusSchema });
421
+ /**
422
+ * `POST /communities/:id/invites` (moderators, private and hidden only;
423
+ * a public community needs none: `COMMUNITY_INVITE_NOT_NEEDED`). Someone who
424
+ * cannot join (not onboarded, paused, suspended, deleted) is
425
+ * `COMMUNITY_MEMBER_NOT_INVITABLE`; banned, `COMMUNITY_MEMBER_BANNED`; already
426
+ * a member, requested or invited, `COMMUNITY_ALREADY_MEMBER`.
427
+ */
428
+ export const inviteCommunityMemberBodySchema = z.object({ userId: objectIdSchema });
429
+ /** `PUT /communities/:id/members/:userId/role` (admins). Never the last admin's own step down. */
430
+ export const setCommunityMemberRoleBodySchema = z.object({ role: communityRoleSchema });
431
+ /** `POST /communities/:id/bans` (admins): removes them if active, and keeps them out. */
432
+ export const banCommunityMemberBodySchema = z.object({
433
+ userId: objectIdSchema,
434
+ reason: z.string().trim().min(1).max(COMMUNITY_BAN_REASON_MAX).optional(),
435
+ });
436
+ /**
437
+ * `POST /communities/:id/pins` (moderators): pin one of the community's posts
438
+ * or polls as an announcement (`COMMUNITY_PINS_FULL` past `COMMUNITY_PINS_MAX`).
439
+ * `notifyMembers` (admins only) rings every active member's bell, at most once
440
+ * per `COMMUNITY_ANNOUNCE_INTERVAL_MS` (`COMMUNITY_ANNOUNCE_TOO_SOON`).
441
+ */
442
+ export const pinCommunityContentBodySchema = z.object({
443
+ contentId: objectIdSchema,
444
+ notifyMembers: z.boolean(),
445
+ });
446
+ /**
447
+ * `POST /community-suggestions`: any onboarded member, at most
448
+ * `COMMUNITY_SUGGESTIONS_OPEN_MAX` waiting at once (`COMMUNITY_SUGGESTIONS_FULL`).
449
+ */
450
+ export const createCommunitySuggestionBodySchema = z.object({
451
+ name: nameSchema,
452
+ description: descriptionSchema,
453
+ why: z.string().trim().min(1).max(COMMUNITY_SUGGESTION_WHY_MAX).optional(),
454
+ visibility: communityVisibilitySchema,
455
+ topics: topicsSchema.optional(),
456
+ });
457
+ /**
458
+ * `POST /community-suggestions/:id/decline` (staff): the reason is shown to the
459
+ * suggester. A decided suggestion is `COMMUNITY_SUGGESTION_DECIDED`.
460
+ */
461
+ export const declineCommunitySuggestionBodySchema = z.object({
462
+ reason: z.string().trim().min(COMMUNITY_DECLINE_REASON_MIN).max(COMMUNITY_DECLINE_REASON_MAX),
463
+ });
464
+ //# sourceMappingURL=communities.js.map
@@ -0,0 +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"]}