founder-schema 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +186 -17
- package/dist/access/access.d.ts +1 -1
- package/dist/access/access.d.ts.map +1 -1
- package/dist/access/access.js +4 -1
- package/dist/access/access.js.map +1 -1
- package/dist/access/permissions.d.ts +27 -29
- package/dist/access/permissions.d.ts.map +1 -1
- package/dist/access/permissions.js +39 -3
- package/dist/access/permissions.js.map +1 -1
- package/dist/core/common.d.ts +54 -23
- package/dist/core/common.d.ts.map +1 -1
- package/dist/core/common.js +49 -24
- package/dist/core/common.js.map +1 -1
- package/dist/core/errors.d.ts +7 -5
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +17 -11
- package/dist/core/errors.js.map +1 -1
- package/dist/core/limits.d.ts +73 -17
- package/dist/core/limits.d.ts.map +1 -1
- package/dist/core/limits.js +77 -22
- package/dist/core/limits.js.map +1 -1
- package/dist/core/money.d.ts +33 -0
- package/dist/core/money.d.ts.map +1 -0
- package/dist/core/money.js +36 -0
- package/dist/core/money.js.map +1 -0
- package/dist/core/responses.d.ts +14 -4
- package/dist/core/responses.d.ts.map +1 -1
- package/dist/core/responses.js.map +1 -1
- package/dist/core/validation.d.ts +19 -2
- package/dist/core/validation.d.ts.map +1 -1
- package/dist/core/validation.js +19 -2
- package/dist/core/validation.js.map +1 -1
- package/dist/domains/billing/billing.d.ts +61 -8
- package/dist/domains/billing/billing.d.ts.map +1 -1
- package/dist/domains/billing/billing.js +58 -22
- package/dist/domains/billing/billing.js.map +1 -1
- package/dist/domains/billing/gst.d.ts.map +1 -1
- package/dist/domains/billing/gst.js +4 -3
- package/dist/domains/billing/gst.js.map +1 -1
- package/dist/domains/chat/chat.d.ts +0 -21
- package/dist/domains/chat/chat.d.ts.map +1 -1
- package/dist/domains/chat/chat.js +0 -20
- package/dist/domains/chat/chat.js.map +1 -1
- package/dist/domains/communities/communities.d.ts +30 -12
- package/dist/domains/communities/communities.d.ts.map +1 -1
- package/dist/domains/communities/communities.js +43 -15
- package/dist/domains/communities/communities.js.map +1 -1
- package/dist/domains/contributions/contributions.d.ts +1 -1
- package/dist/domains/contributions/contributions.js +1 -1
- package/dist/domains/contributions/contributions.js.map +1 -1
- package/dist/domains/dashboard/dashboard.d.ts +3 -1
- package/dist/domains/dashboard/dashboard.d.ts.map +1 -1
- package/dist/domains/dashboard/dashboard.js +23 -7
- package/dist/domains/dashboard/dashboard.js.map +1 -1
- package/dist/domains/discover/discover.d.ts +8 -8
- package/dist/domains/feed/feed.d.ts +55 -153
- package/dist/domains/feed/feed.d.ts.map +1 -1
- package/dist/domains/feed/feed.js +53 -58
- package/dist/domains/feed/feed.js.map +1 -1
- package/dist/domains/location/areas.d.ts +331 -0
- package/dist/domains/location/areas.d.ts.map +1 -0
- package/dist/domains/location/areas.js +238 -0
- package/dist/domains/location/areas.js.map +1 -0
- package/dist/domains/location/india.d.ts +41 -123
- package/dist/domains/location/india.d.ts.map +1 -1
- package/dist/domains/location/india.js +121 -59
- package/dist/domains/location/india.js.map +1 -1
- package/dist/domains/members/members.d.ts +2 -2
- package/dist/domains/membership/membership.d.ts +26 -0
- package/dist/domains/membership/membership.d.ts.map +1 -1
- package/dist/domains/membership/membership.js +55 -1
- package/dist/domains/membership/membership.js.map +1 -1
- package/dist/domains/moderation/moderation.d.ts +2 -2
- package/dist/domains/moderation/moderation.js +3 -3
- package/dist/domains/moderation/moderation.js.map +1 -1
- package/dist/domains/notifications/notifications.d.ts +1 -10
- package/dist/domains/notifications/notifications.d.ts.map +1 -1
- package/dist/domains/notifications/notifications.js +2 -3
- package/dist/domains/notifications/notifications.js.map +1 -1
- package/dist/domains/onboarding/onboarding-answers.d.ts +42 -14
- package/dist/domains/onboarding/onboarding-answers.d.ts.map +1 -1
- package/dist/domains/onboarding/onboarding-answers.js +49 -33
- package/dist/domains/onboarding/onboarding-answers.js.map +1 -1
- package/dist/domains/onboarding/onboarding.d.ts +332 -138
- package/dist/domains/onboarding/onboarding.d.ts.map +1 -1
- package/dist/domains/onboarding/onboarding.js +47 -59
- package/dist/domains/onboarding/onboarding.js.map +1 -1
- package/dist/domains/products/products.d.ts +43 -8
- package/dist/domains/products/products.d.ts.map +1 -1
- package/dist/domains/products/products.js +57 -11
- package/dist/domains/products/products.js.map +1 -1
- package/dist/domains/profile/founder-profile.d.ts +123 -143
- package/dist/domains/profile/founder-profile.d.ts.map +1 -1
- package/dist/domains/profile/founder-profile.js +77 -35
- package/dist/domains/profile/founder-profile.js.map +1 -1
- package/dist/domains/profile/my-profile.d.ts +3 -3
- package/dist/domains/reviews/reviews.d.ts +1 -1
- package/dist/domains/reviews/reviews.d.ts.map +1 -1
- package/dist/domains/reviews/reviews.js +2 -2
- package/dist/domains/reviews/reviews.js.map +1 -1
- package/dist/domains/support/support.d.ts +22 -3
- package/dist/domains/support/support.d.ts.map +1 -1
- package/dist/domains/support/support.js +19 -5
- package/dist/domains/support/support.js.map +1 -1
- package/dist/domains/team/team.d.ts.map +1 -1
- package/dist/domains/team/team.js +3 -6
- package/dist/domains/team/team.js.map +1 -1
- package/dist/domains/uploads/uploads.d.ts +7 -22
- package/dist/domains/uploads/uploads.d.ts.map +1 -1
- package/dist/domains/uploads/uploads.js +7 -21
- package/dist/domains/uploads/uploads.js.map +1 -1
- package/dist/env.d.ts +26 -13
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +58 -23
- package/dist/env.js.map +1 -1
- package/dist/index.d.ts +19 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -16,9 +16,11 @@
|
|
|
16
16
|
* joining one tap request, then invite only,
|
|
17
17
|
* approval then accept
|
|
18
18
|
*
|
|
19
|
-
* Taking part (post, poll, comment, react, vote, save
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* Taking part (post, poll, comment, react, vote, save) is for members of
|
|
20
|
+
* every type. "Members" means ACTIVE members: someone who requested, was
|
|
21
|
+
* invited or is banned is a non-member for every rule. Taking a reaction,
|
|
22
|
+
* a vote or a save BACK needs only read access, and reporting is open to
|
|
23
|
+
* any signed-in reader (safety, 0.13.0).
|
|
22
24
|
* - PUBLIC MEANS PUBLIC INSIDE FOUNDER CIRCLE: a signed-out visitor sees no
|
|
23
25
|
* community content of any type, even by a shared link.
|
|
24
26
|
* - THE HOME FEED adds only the communities the member joined.
|
|
@@ -174,11 +176,25 @@ export declare const communityStatusSchema: z.ZodEnum<{
|
|
|
174
176
|
archived: "archived";
|
|
175
177
|
}>;
|
|
176
178
|
export type CommunityStatus = z.infer<typeof communityStatusSchema>;
|
|
179
|
+
/**
|
|
180
|
+
* Whether a community may take type `to` now (0.18.0): ONE rule for the
|
|
181
|
+
* server's refusal (`COMMUNITY_ARCHIVED`) and the screen's choice. An
|
|
182
|
+
* archived community lets nobody new in, so it may be narrowed (private,
|
|
183
|
+
* hidden) but never opened to public, which would let its waiting requests in.
|
|
184
|
+
*/
|
|
185
|
+
export declare function communityTypeChangeAllowed(status: CommunityStatus, to: CommunityVisibility): boolean;
|
|
186
|
+
/** How staff decided a suggestion (0.18.0: named, for the bell and the backend's model). */
|
|
187
|
+
export declare const COMMUNITY_SUGGESTION_DECISION_VALUES: readonly ["approved", "declined"];
|
|
188
|
+
export declare const communitySuggestionDecisionSchema: z.ZodEnum<{
|
|
189
|
+
approved: "approved";
|
|
190
|
+
declined: "declined";
|
|
191
|
+
}>;
|
|
192
|
+
export type CommunitySuggestionDecision = z.infer<typeof communitySuggestionDecisionSchema>;
|
|
177
193
|
export declare const COMMUNITY_SUGGESTION_STATUS_VALUES: readonly ["open", "approved", "declined"];
|
|
178
194
|
export declare const communitySuggestionStatusSchema: z.ZodEnum<{
|
|
179
|
-
open: "open";
|
|
180
195
|
approved: "approved";
|
|
181
196
|
declined: "declined";
|
|
197
|
+
open: "open";
|
|
182
198
|
}>;
|
|
183
199
|
export type CommunitySuggestionStatus = z.infer<typeof communitySuggestionStatusSchema>;
|
|
184
200
|
/**
|
|
@@ -349,6 +365,7 @@ export declare const communityViewerSchema: z.ZodObject<{
|
|
|
349
365
|
canRequest: z.ZodBoolean;
|
|
350
366
|
canPost: z.ZodBoolean;
|
|
351
367
|
canLeave: z.ZodBoolean;
|
|
368
|
+
leaveArchives: z.ZodBoolean;
|
|
352
369
|
canModerate: z.ZodBoolean;
|
|
353
370
|
canManage: z.ZodBoolean;
|
|
354
371
|
canAssign: z.ZodBoolean;
|
|
@@ -523,6 +540,7 @@ export declare const communitySchema: z.ZodObject<{
|
|
|
523
540
|
canRequest: z.ZodBoolean;
|
|
524
541
|
canPost: z.ZodBoolean;
|
|
525
542
|
canLeave: z.ZodBoolean;
|
|
543
|
+
leaveArchives: z.ZodBoolean;
|
|
526
544
|
canModerate: z.ZodBoolean;
|
|
527
545
|
canManage: z.ZodBoolean;
|
|
528
546
|
canAssign: z.ZodBoolean;
|
|
@@ -725,9 +743,9 @@ export declare const communitySuggestionSchema: z.ZodObject<{
|
|
|
725
743
|
education_access: "education_access";
|
|
726
744
|
}>>;
|
|
727
745
|
status: z.ZodEnum<{
|
|
728
|
-
open: "open";
|
|
729
746
|
approved: "approved";
|
|
730
747
|
declined: "declined";
|
|
748
|
+
open: "open";
|
|
731
749
|
}>;
|
|
732
750
|
decidedBy: z.ZodNullable<z.ZodObject<{
|
|
733
751
|
userId: z.ZodString;
|
|
@@ -1040,9 +1058,9 @@ export declare const communitySuggestionPageSchema: z.ZodObject<{
|
|
|
1040
1058
|
education_access: "education_access";
|
|
1041
1059
|
}>>;
|
|
1042
1060
|
status: z.ZodEnum<{
|
|
1043
|
-
open: "open";
|
|
1044
1061
|
approved: "approved";
|
|
1045
1062
|
declined: "declined";
|
|
1063
|
+
open: "open";
|
|
1046
1064
|
}>;
|
|
1047
1065
|
decidedBy: z.ZodNullable<z.ZodObject<{
|
|
1048
1066
|
userId: z.ZodString;
|
|
@@ -1077,8 +1095,8 @@ export type CommunitySuggestionPage = z.infer<typeof communitySuggestionPageSche
|
|
|
1077
1095
|
*/
|
|
1078
1096
|
export declare const communityDiscoverQuerySchema: z.ZodObject<{
|
|
1079
1097
|
limit: z.ZodPreprocess<z.ZodDefault<z.ZodCoercedNumber<unknown>>, unknown>;
|
|
1080
|
-
cursor: z.ZodOptional<z.ZodString>;
|
|
1081
|
-
q: z.
|
|
1098
|
+
cursor: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
|
|
1099
|
+
q: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
|
|
1082
1100
|
topic: z.ZodPreprocess<z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
1083
1101
|
ai_ml: "ai_ml";
|
|
1084
1102
|
b2b_saas: "b2b_saas";
|
|
@@ -1157,7 +1175,7 @@ export type CommunityDiscoverQueryInput = z.input<typeof communityDiscoverQueryS
|
|
|
1157
1175
|
/** `GET /communities/:id/members`: everyone, or one role (`?role=admin`). */
|
|
1158
1176
|
export declare const communityMembersQuerySchema: z.ZodObject<{
|
|
1159
1177
|
limit: z.ZodPreprocess<z.ZodDefault<z.ZodCoercedNumber<unknown>>, unknown>;
|
|
1160
|
-
cursor: z.ZodOptional<z.ZodString>;
|
|
1178
|
+
cursor: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
|
|
1161
1179
|
role: z.ZodPreprocess<z.ZodOptional<z.ZodEnum<{
|
|
1162
1180
|
admin: "admin";
|
|
1163
1181
|
moderator: "moderator";
|
|
@@ -1169,11 +1187,11 @@ export type CommunityMembersQueryInput = z.input<typeof communityMembersQuerySch
|
|
|
1169
1187
|
/** `GET /community-suggestions` (staff): waiting ones by default, oldest first. */
|
|
1170
1188
|
export declare const communitySuggestionsQuerySchema: z.ZodObject<{
|
|
1171
1189
|
limit: z.ZodPreprocess<z.ZodDefault<z.ZodCoercedNumber<unknown>>, unknown>;
|
|
1172
|
-
cursor: z.ZodOptional<z.ZodString>;
|
|
1190
|
+
cursor: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
|
|
1173
1191
|
status: z.ZodPreprocess<z.ZodDefault<z.ZodEnum<{
|
|
1174
|
-
open: "open";
|
|
1175
1192
|
approved: "approved";
|
|
1176
1193
|
declined: "declined";
|
|
1194
|
+
open: "open";
|
|
1177
1195
|
}>>, unknown>;
|
|
1178
1196
|
}, z.core.$strip>;
|
|
1179
1197
|
export type CommunitySuggestionsQuery = z.infer<typeof communitySuggestionsQuerySchema>;
|
|
@@ -1427,7 +1445,7 @@ export type SetCommunityMemberRoleBody = z.infer<typeof setCommunityMemberRoleBo
|
|
|
1427
1445
|
/** `POST /communities/:id/bans` (admins): removes them if active, and keeps them out. */
|
|
1428
1446
|
export declare const banCommunityMemberBodySchema: z.ZodObject<{
|
|
1429
1447
|
userId: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
1430
|
-
reason: z.ZodOptional<z.ZodString
|
|
1448
|
+
reason: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
|
|
1431
1449
|
}, z.core.$strip>;
|
|
1432
1450
|
export type BanCommunityMemberBody = z.infer<typeof banCommunityMemberBodySchema>;
|
|
1433
1451
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"communities.d.ts","sourceRoot":"","sources":["../../../src/domains/communities/communities.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"communities.d.ts","sourceRoot":"","sources":["../../../src/domains/communities/communities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsHG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA6BxB,4DAA4D;AAC5D,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,gEAAgE;AAChE,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,kEAAkE;AAClE,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD;;;GAGG;AACH,eAAO,MAAM,8BAA8B,QAAS,CAAC;AAKrD,mCAAmC;AACnC,eAAO,MAAM,2BAA2B,0CAA2C,CAAC;AACpF,eAAO,MAAM,yBAAyB;;;;EAAsC,CAAC;AAC7E,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E;;;GAGG;AACH,eAAO,MAAM,yBAAyB;;;;CAIkB,CAAC;AAEzD,8EAA8E;AAC9E,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,mBAAmB,EACzB,EAAE,EAAE,mBAAmB,GACtB,OAAO,CAET;AAED,4CAA4C;AAC5C,eAAO,MAAM,qBAAqB,2CAA4C,CAAC;AAC/E,eAAO,MAAM,mBAAmB;;;;EAAgC,CAAC;AACjE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,uDAAwD,CAAC;AACpG,eAAO,MAAM,2BAA2B;;;;;EAAyC,CAAC;AAClF,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAEhF,oFAAoF;AACpF,eAAO,MAAM,uBAAuB,iCAAkC,CAAC;AACvE,eAAO,MAAM,qBAAqB;;;EAAkC,CAAC;AACrE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,eAAe,EACvB,EAAE,EAAE,mBAAmB,GACtB,OAAO,CAET;AAED,4FAA4F;AAC5F,eAAO,MAAM,oCAAoC,mCAAoC,CAAC;AACtF,eAAO,MAAM,iCAAiC;;;EAA+C,CAAC;AAC9F,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAC5F,eAAO,MAAM,kCAAkC,2CAGrC,CAAC;AACX,eAAO,MAAM,+BAA+B;;;;EAA6C,CAAC;AAC1F,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAIxF;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;iBAS9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,yFAAyF;AACzF,eAAO,MAAM,yBAAyB;;;;;;;;;;;iBAKpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,yDAAyD;AACzD,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAc9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;GAIG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;iBA8ChC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;iBAUhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,wFAAwF;AACxF,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkB1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,uEAAuE;AACvE,eAAO,MAAM,sBAAsB;;;;;;;;;iBAGjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,uEAAuE;AACvE,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;iBAKvC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF,qDAAqD;AACrD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;iBAIhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,8FAA8F;AAC9F,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;iBAAsD,CAAC;AAC1F,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,6DAA6D;AAC7D,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;iBAM7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,4DAA4D;AAC5D,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoBpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAuC,CAAC;AAC5E,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AACxE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;iBAAyC,CAAC;AAChF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAC5E,eAAO,MAAM,0BAA0B;;;;;;;;;;;;iBAA0C,CAAC;AAClF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC9E,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;iBAAgD,CAAC;AAC9F,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAC;AAC1F,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;iBAAsC,CAAC;AAC1E,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACtE,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAA6C,CAAC;AACxF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAIpF;;;;GAIG;AACH,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAGvC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAClF,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAEvF,6EAA6E;AAC7E,eAAO,MAAM,2BAA2B;;;;;;;;iBAEtC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAChF,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAErF,mFAAmF;AACnF,eAAO,MAAM,+BAA+B;;;;;;;;iBAE1C,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AACxF,MAAM,MAAM,8BAA8B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAE7F,eAAO,MAAM,qBAAqB;;iBAA4C,CAAC;AAC/E,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,2EAA2E;AAC3E,eAAO,MAAM,2BAA2B;;;iBAGtC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAChF,6DAA6D;AAC7D,eAAO,MAAM,4BAA4B;;;iBAGvC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAClF,sEAAsE;AACtE,eAAO,MAAM,4BAA4B;;;iBAGvC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAClF,eAAO,MAAM,+BAA+B;;iBAA6C,CAAC;AAC1F,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAcxF;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAOpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,0GAA0G;AAC1G,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAMpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,0GAA0G;AAC1G,eAAO,MAAM,0BAA0B;;iBAAwC,CAAC;AAChF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC;;;;;;;iBAG3C,CAAC;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAC;AAE1F,sDAAsD;AACtD,eAAO,MAAM,4BAA4B;;;;;iBAA8C,CAAC;AACxF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B;;iBAAuC,CAAC;AACpF,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAExF,kGAAkG;AAClG,eAAO,MAAM,gCAAgC;;;;;;iBAA0C,CAAC;AACxF,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAC;AAE1F,yFAAyF;AACzF,eAAO,MAAM,4BAA4B;;;iBAIvC,CAAC;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAElF;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B;;;iBAGxC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF;;;GAGG;AACH,eAAO,MAAM,mCAAmC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAM9C,CAAC;AACH,MAAM,MAAM,6BAA6B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mCAAmC,CAAC,CAAC;AAEhG;;;GAGG;AACH,eAAO,MAAM,oCAAoC;;iBAE/C,CAAC;AACH,MAAM,MAAM,8BAA8B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oCAAoC,CAAC,CAAC"}
|
|
@@ -16,9 +16,11 @@
|
|
|
16
16
|
* joining one tap request, then invite only,
|
|
17
17
|
* approval then accept
|
|
18
18
|
*
|
|
19
|
-
* Taking part (post, poll, comment, react, vote, save
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* Taking part (post, poll, comment, react, vote, save) is for members of
|
|
20
|
+
* every type. "Members" means ACTIVE members: someone who requested, was
|
|
21
|
+
* invited or is banned is a non-member for every rule. Taking a reaction,
|
|
22
|
+
* a vote or a save BACK needs only read access, and reporting is open to
|
|
23
|
+
* any signed-in reader (safety, 0.13.0).
|
|
22
24
|
* - PUBLIC MEANS PUBLIC INSIDE FOUNDER CIRCLE: a signed-out visitor sees no
|
|
23
25
|
* community content of any type, even by a shared link.
|
|
24
26
|
* - THE HOME FEED adds only the communities the member joined.
|
|
@@ -116,8 +118,8 @@
|
|
|
116
118
|
* @module founder-schema/domains/communities
|
|
117
119
|
*/
|
|
118
120
|
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 { eachOnce, emptyStringToUndefined, multiValueQuery, objectIdSchema, optionalTrimmedString, paginationQuerySchema, } from '../../core/common.js';
|
|
122
|
+
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, DAY_MS, DISCOVER_FILTER_VALUES_MAX, DISCOVER_TEXT_MAX, } from '../../core/limits.js';
|
|
121
123
|
import { paginatedSchema } from '../../core/responses.js';
|
|
122
124
|
import { VALIDATION_MESSAGE } from '../../core/validation.js';
|
|
123
125
|
import { mediaImageSchema } from '../media/media.js';
|
|
@@ -133,7 +135,7 @@ export const COMMUNITY_SUGGESTIONS_OPEN_MAX = 3;
|
|
|
133
135
|
* The shortest time between two notify-everyone announcements in one
|
|
134
136
|
* community, so members are not spammed (`COMMUNITY_ANNOUNCE_TOO_SOON`).
|
|
135
137
|
*/
|
|
136
|
-
export const COMMUNITY_ANNOUNCE_INTERVAL_MS =
|
|
138
|
+
export const COMMUNITY_ANNOUNCE_INTERVAL_MS = DAY_MS;
|
|
137
139
|
// ─── Vocabulary ──────────────────────────────────────────────────────────────
|
|
138
140
|
// ⚠ Every list below is STORED. Append only; removing a value orphans rows.
|
|
139
141
|
/** See the module note's table. */
|
|
@@ -165,7 +167,22 @@ export const communityMemberStatusSchema = z.enum(COMMUNITY_MEMBER_STATUS_VALUES
|
|
|
165
167
|
/** `archived`: read-only and out of Discover; members still read it. Staff only. */
|
|
166
168
|
export const COMMUNITY_STATUS_VALUES = ['active', 'archived'];
|
|
167
169
|
export const communityStatusSchema = z.enum(COMMUNITY_STATUS_VALUES);
|
|
168
|
-
|
|
170
|
+
/**
|
|
171
|
+
* Whether a community may take type `to` now (0.18.0): ONE rule for the
|
|
172
|
+
* server's refusal (`COMMUNITY_ARCHIVED`) and the screen's choice. An
|
|
173
|
+
* archived community lets nobody new in, so it may be narrowed (private,
|
|
174
|
+
* hidden) but never opened to public, which would let its waiting requests in.
|
|
175
|
+
*/
|
|
176
|
+
export function communityTypeChangeAllowed(status, to) {
|
|
177
|
+
return !(status === 'archived' && to === 'public');
|
|
178
|
+
}
|
|
179
|
+
/** How staff decided a suggestion (0.18.0: named, for the bell and the backend's model). */
|
|
180
|
+
export const COMMUNITY_SUGGESTION_DECISION_VALUES = ['approved', 'declined'];
|
|
181
|
+
export const communitySuggestionDecisionSchema = z.enum(COMMUNITY_SUGGESTION_DECISION_VALUES);
|
|
182
|
+
export const COMMUNITY_SUGGESTION_STATUS_VALUES = [
|
|
183
|
+
'open',
|
|
184
|
+
...COMMUNITY_SUGGESTION_DECISION_VALUES,
|
|
185
|
+
];
|
|
169
186
|
export const communitySuggestionStatusSchema = z.enum(COMMUNITY_SUGGESTION_STATUS_VALUES);
|
|
170
187
|
// ─── Read shapes ─────────────────────────────────────────────────────────────
|
|
171
188
|
/**
|
|
@@ -221,12 +238,25 @@ export const communityViewerSchema = communityCardViewerSchema.extend({
|
|
|
221
238
|
canRequest: z.boolean(),
|
|
222
239
|
/** Active member, community not archived: the composer. */
|
|
223
240
|
canPost: z.boolean(),
|
|
224
|
-
/**
|
|
241
|
+
/**
|
|
242
|
+
* Active member who is not its last admin, or the last admin of a community
|
|
243
|
+
* nobody else is in (0.18.0): their leaving ARCHIVES it, nothing deleted,
|
|
244
|
+
* since there is no one to hand it to. A last admin with members hands it
|
|
245
|
+
* over first (`COMMUNITY_LAST_ADMIN`).
|
|
246
|
+
*/
|
|
225
247
|
canLeave: z.boolean(),
|
|
248
|
+
/**
|
|
249
|
+
* Leaving ARCHIVES it (0.18.0): the viewer is its last admin and nobody else
|
|
250
|
+
* is in it, counted as leaving counts. The server's answer, so the dialog
|
|
251
|
+
* never guesses from a stored count.
|
|
252
|
+
*/
|
|
253
|
+
leaveArchives: z.boolean(),
|
|
226
254
|
/**
|
|
227
255
|
* Moderators and admins (and managers, super admins): requests, invites,
|
|
228
|
-
* pins, hiding, removing members. On an ARCHIVED community
|
|
229
|
-
* inviting, accepting
|
|
256
|
+
* pins, hiding, removing members. On an ARCHIVED community nobody new comes
|
|
257
|
+
* in: approving, inviting, accepting, pinning AND opening it to public
|
|
258
|
+
* (which would let waiting requests in, `communityTypeChangeAllowed`,
|
|
259
|
+
* 0.18.0) answer `COMMUNITY_ARCHIVED`; narrowing its type, declining,
|
|
230
260
|
* cancelling, removing, banning, roles and hiding still work.
|
|
231
261
|
*/
|
|
232
262
|
canModerate: z.boolean(),
|
|
@@ -380,10 +410,7 @@ const descriptionSchema = z.string().trim().min(1).max(COMMUNITY_DESCRIPTION_MAX
|
|
|
380
410
|
const rulesSchema = z
|
|
381
411
|
.array(z.string().trim().min(1).max(COMMUNITY_RULE_MAX))
|
|
382
412
|
.max(COMMUNITY_RULES_MAX);
|
|
383
|
-
const topicsSchema = z
|
|
384
|
-
.array(topicSchema)
|
|
385
|
-
.max(COMMUNITY_TOPICS_MAX)
|
|
386
|
-
.refine((topics) => new Set(topics).size === topics.length, {
|
|
413
|
+
const topicsSchema = z.array(topicSchema).max(COMMUNITY_TOPICS_MAX).refine(eachOnce, {
|
|
387
414
|
error: VALIDATION_MESSAGE.topicsDuplicate,
|
|
388
415
|
});
|
|
389
416
|
/**
|
|
@@ -438,7 +465,8 @@ export const setCommunityMemberRoleBodySchema = z.object({ role: communityRoleSc
|
|
|
438
465
|
/** `POST /communities/:id/bans` (admins): removes them if active, and keeps them out. */
|
|
439
466
|
export const banCommunityMemberBodySchema = z.object({
|
|
440
467
|
userId: objectIdSchema,
|
|
441
|
-
reason
|
|
468
|
+
/** Why, for the record. Blank means no reason (0.18.0: a blank box was refused). */
|
|
469
|
+
reason: optionalTrimmedString(COMMUNITY_BAN_REASON_MAX),
|
|
442
470
|
});
|
|
443
471
|
/**
|
|
444
472
|
* `POST /communities/:id/pins` (moderators): pin one of the community's posts
|
|
@@ -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 /**\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"]}
|
|
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) is for members of\n * every type. \"Members\" means ACTIVE members: someone who requested, was\n * invited or is banned is a non-member for every rule. Taking a reaction,\n * a vote or a save BACK needs only read access, and reporting is open to\n * any signed-in reader (safety, 0.13.0).\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 eachOnce,\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 DAY_MS,\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 = DAY_MS;\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\n/**\n * Whether a community may take type `to` now (0.18.0): ONE rule for the\n * server's refusal (`COMMUNITY_ARCHIVED`) and the screen's choice. An\n * archived community lets nobody new in, so it may be narrowed (private,\n * hidden) but never opened to public, which would let its waiting requests in.\n */\nexport function communityTypeChangeAllowed(\n status: CommunityStatus,\n to: CommunityVisibility,\n): boolean {\n return !(status === 'archived' && to === 'public');\n}\n\n/** How staff decided a suggestion (0.18.0: named, for the bell and the backend's model). */\nexport const COMMUNITY_SUGGESTION_DECISION_VALUES = ['approved', 'declined'] as const;\nexport const communitySuggestionDecisionSchema = z.enum(COMMUNITY_SUGGESTION_DECISION_VALUES);\nexport type CommunitySuggestionDecision = z.infer<typeof communitySuggestionDecisionSchema>;\nexport const COMMUNITY_SUGGESTION_STATUS_VALUES = [\n 'open',\n ...COMMUNITY_SUGGESTION_DECISION_VALUES,\n] 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 /**\n * Active member who is not its last admin, or the last admin of a community\n * nobody else is in (0.18.0): their leaving ARCHIVES it, nothing deleted,\n * since there is no one to hand it to. A last admin with members hands it\n * over first (`COMMUNITY_LAST_ADMIN`).\n */\n canLeave: z.boolean(),\n /**\n * Leaving ARCHIVES it (0.18.0): the viewer is its last admin and nobody else\n * is in it, counted as leaving counts. The server's answer, so the dialog\n * never guesses from a stored count.\n */\n leaveArchives: z.boolean(),\n /**\n * Moderators and admins (and managers, super admins): requests, invites,\n * pins, hiding, removing members. On an ARCHIVED community nobody new comes\n * in: approving, inviting, accepting, pinning AND opening it to public\n * (which would let waiting requests in, `communityTypeChangeAllowed`,\n * 0.18.0) answer `COMMUNITY_ARCHIVED`; narrowing its type, 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.array(topicSchema).max(COMMUNITY_TOPICS_MAX).refine(eachOnce, {\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 /** Why, for the record. Blank means no reason (0.18.0: a blank box was refused). */\n reason: optionalTrimmedString(COMMUNITY_BAN_REASON_MAX),\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"]}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* product receives, so the score cannot be bought with a popular product.
|
|
17
17
|
*
|
|
18
18
|
* Against farming: one member can earn at most `CONTRIBUTION_PAIR_POINTS_MAX`
|
|
19
|
-
* points from any ONE other member over
|
|
19
|
+
* points from any ONE other member over `CONTRIBUTION_PAIR_WINDOW_DAYS`, so two founders cannot trade
|
|
20
20
|
* Helpful marks into a high score.
|
|
21
21
|
*
|
|
22
22
|
* EVERY POINT IS A ROW (the ledger), so a score can be explained ("how did I
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* product receives, so the score cannot be bought with a popular product.
|
|
17
17
|
*
|
|
18
18
|
* Against farming: one member can earn at most `CONTRIBUTION_PAIR_POINTS_MAX`
|
|
19
|
-
* points from any ONE other member over
|
|
19
|
+
* points from any ONE other member over `CONTRIBUTION_PAIR_WINDOW_DAYS`, so two founders cannot trade
|
|
20
20
|
* Helpful marks into a high score.
|
|
21
21
|
*
|
|
22
22
|
* EVERY POINT IS A ROW (the ledger), so a score can be explained ("how did I
|
|
@@ -1 +1 @@
|
|
|
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
|
|
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 `CONTRIBUTION_PAIR_WINDOW_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"]}
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* screen can be glanced at over a shoulder. Each block answers one question:
|
|
8
8
|
* is the community growing, is it talking, is anything waiting on staff.
|
|
9
9
|
*
|
|
10
|
-
* "Last 7 days" is the
|
|
10
|
+
* "Last 7 days" is the `DASHBOARD_RECENT_DAYS` days up to `generatedAt`, by the
|
|
11
|
+
* server's clock (the field names say 7: changing the constant renames them).
|
|
11
12
|
*
|
|
12
13
|
* @module founder-schema/domains/dashboard
|
|
13
14
|
*/
|
|
@@ -31,6 +32,7 @@ export declare const dashboardSchema: z.ZodObject<{
|
|
|
31
32
|
}, z.core.$strip>;
|
|
32
33
|
support: z.ZodObject<{
|
|
33
34
|
openTickets: z.ZodNumber;
|
|
35
|
+
waitingTickets: z.ZodNumber;
|
|
34
36
|
unassignedTickets: z.ZodNumber;
|
|
35
37
|
}, z.core.$strip>;
|
|
36
38
|
billing: z.ZodObject<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../../src/domains/dashboard/dashboard.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../../src/domains/dashboard/dashboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsE1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC"}
|