jansathi-community-schema 0.2.0 → 0.4.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/dist/comment.d.ts CHANGED
@@ -19,6 +19,7 @@ export declare const communityCommentWireSchema: z.ZodObject<{
19
19
  post: "post";
20
20
  lost_found: "lost_found";
21
21
  voice_box: "voice_box";
22
+ donate_item: "donate_item";
22
23
  }>;
23
24
  contentId: z.ZodString;
24
25
  parentCommentId: z.ZodNullable<z.ZodString>;
@@ -1 +1 @@
1
- {"version":3,"file":"comment.d.ts","sourceRoot":"","sources":["../src/comment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkCrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAI9E,eAAO,MAAM,uBAAuB;;;iBAMlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAIxE,eAAO,MAAM,uBAAuB;;iBAElC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC"}
1
+ {"version":3,"file":"comment.d.ts","sourceRoot":"","sources":["../src/comment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkCrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAI9E,eAAO,MAAM,uBAAuB;;;iBAMlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAIxE,eAAO,MAAM,uBAAuB;;iBAElC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC"}
@@ -22,6 +22,7 @@ export declare const toggleUpvoteResponseSchema: z.ZodObject<{
22
22
  post: "post";
23
23
  lost_found: "lost_found";
24
24
  voice_box: "voice_box";
25
+ donate_item: "donate_item";
25
26
  }>;
26
27
  contentId: z.ZodString;
27
28
  upvoted: z.ZodBoolean;
@@ -52,6 +53,7 @@ export declare const setReactionResponseSchema: z.ZodObject<{
52
53
  post: "post";
53
54
  lost_found: "lost_found";
54
55
  voice_box: "voice_box";
56
+ donate_item: "donate_item";
55
57
  }>;
56
58
  contentId: z.ZodString;
57
59
  reaction: z.ZodNullable<z.ZodEnum<{
@@ -1 +1 @@
1
- {"version":3,"file":"engagement.d.ts","sourceRoot":"","sources":["../src/engagement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;;;GAIG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;iBAQrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAI9E;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;iBAEhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;iBAOpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
1
+ {"version":3,"file":"engagement.d.ts","sourceRoot":"","sources":["../src/engagement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;;;GAIG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;iBAQrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAI9E;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;iBAEhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;iBAOpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC"}
package/dist/enums.d.ts CHANGED
@@ -40,17 +40,19 @@ export type VisibilityLevel = z.infer<typeof visibilityLevelSchema>;
40
40
  export declare const VISIBILITY_LEVEL_RANK: Record<VisibilityLevel, number>;
41
41
  /**
42
42
  * The kinds of items that show up in the feed. `post` is the new
43
- * community-native kind; `lost_found` and `voice_box` are existing
44
- * features surfaced into the feed via polymorphic union.
43
+ * community-native kind; `lost_found`, `voice_box`, and `donate_item`
44
+ * are existing legacy features surfaced into the feed via polymorphic
45
+ * union.
45
46
  *
46
- * Engagement (upvote / reaction / comment) is keyed by
47
+ * Engagement (upvote / reaction / comment / share) is keyed by
47
48
  * (contentKind, contentId) so every kind gets the same engagement bar.
48
49
  */
49
- export declare const CONTENT_KIND_VALUES: readonly ["post", "lost_found", "voice_box"];
50
+ export declare const CONTENT_KIND_VALUES: readonly ["post", "lost_found", "voice_box", "donate_item"];
50
51
  export declare const contentKindSchema: z.ZodEnum<{
51
52
  post: "post";
52
53
  lost_found: "lost_found";
53
54
  voice_box: "voice_box";
55
+ donate_item: "donate_item";
54
56
  }>;
55
57
  export type ContentKind = z.infer<typeof contentKindSchema>;
56
58
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"enums.d.ts","sourceRoot":"","sources":["../src/enums.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uBAAuB,+DAM1B,CAAC;AACX,eAAO,MAAM,qBAAqB;;;;;;EAAkC,CAAC;AACrE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAMjE,CAAC;AAIF;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,8CAA+C,CAAC;AAChF,eAAO,MAAM,iBAAiB;;;;EAA8B,CAAC;AAC7D,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAI5D;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,qEAQvB,CAAC;AACX,eAAO,MAAM,kBAAkB;;;;;;;;EAA+B,CAAC;AAC/D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,yDAA0D,CAAC;AAChG,eAAO,MAAM,sBAAsB;;;;;EAAmC,CAAC;AACvE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAItE;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,gCAAiC,CAAC;AACrE,eAAO,MAAM,oBAAoB;;;EAAiC,CAAC;AACnE,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAIlE;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,yIAUvB,CAAC;AACX,eAAO,MAAM,kBAAkB;;;;;;;;;;EAA+B,CAAC;AAC/D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,6CAA6C;AAC7C,eAAO,MAAM,oBAAoB,4DAA6D,CAAC;AAC/F,eAAO,MAAM,kBAAkB;;;;;EAA+B,CAAC;AAC/D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,gCAAiC,CAAC;AAC/D,eAAO,MAAM,cAAc;;;;EAA2B,CAAC;AACvD,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC"}
1
+ {"version":3,"file":"enums.d.ts","sourceRoot":"","sources":["../src/enums.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uBAAuB,+DAM1B,CAAC;AACX,eAAO,MAAM,qBAAqB;;;;;;EAAkC,CAAC;AACrE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAMjE,CAAC;AAIF;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,6DAA8D,CAAC;AAC/F,eAAO,MAAM,iBAAiB;;;;;EAA8B,CAAC;AAC7D,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAI5D;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,qEAQvB,CAAC;AACX,eAAO,MAAM,kBAAkB;;;;;;;;EAA+B,CAAC;AAC/D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,yDAA0D,CAAC;AAChG,eAAO,MAAM,sBAAsB;;;;;EAAmC,CAAC;AACvE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAItE;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,gCAAiC,CAAC;AACrE,eAAO,MAAM,oBAAoB;;;EAAiC,CAAC;AACnE,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAIlE;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,yIAUvB,CAAC;AACX,eAAO,MAAM,kBAAkB;;;;;;;;;;EAA+B,CAAC;AAC/D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,6CAA6C;AAC7C,eAAO,MAAM,oBAAoB,4DAA6D,CAAC;AAC/F,eAAO,MAAM,kBAAkB;;;;;EAA+B,CAAC;AAC/D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,gCAAiC,CAAC;AAC/D,eAAO,MAAM,cAAc;;;;EAA2B,CAAC;AACvD,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC"}
package/dist/enums.js CHANGED
@@ -47,13 +47,14 @@ export const VISIBILITY_LEVEL_RANK = {
47
47
  // ─── Polymorphic content kind ───────────────────────────────────────────────
48
48
  /**
49
49
  * The kinds of items that show up in the feed. `post` is the new
50
- * community-native kind; `lost_found` and `voice_box` are existing
51
- * features surfaced into the feed via polymorphic union.
50
+ * community-native kind; `lost_found`, `voice_box`, and `donate_item`
51
+ * are existing legacy features surfaced into the feed via polymorphic
52
+ * union.
52
53
  *
53
- * Engagement (upvote / reaction / comment) is keyed by
54
+ * Engagement (upvote / reaction / comment / share) is keyed by
54
55
  * (contentKind, contentId) so every kind gets the same engagement bar.
55
56
  */
56
- export const CONTENT_KIND_VALUES = ['post', 'lost_found', 'voice_box'];
57
+ export const CONTENT_KIND_VALUES = ['post', 'lost_found', 'voice_box', 'donate_item'];
57
58
  export const contentKindSchema = z.enum(CONTENT_KIND_VALUES);
58
59
  // ─── Reactions ──────────────────────────────────────────────────────────────
59
60
  /**
package/dist/enums.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"enums.js","sourceRoot":"","sources":["../src/enums.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,+EAA+E;AAE/E;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,OAAO;IACP,UAAU;IACV,OAAO;IACP,UAAU;IACV,QAAQ;CACA,CAAC;AACX,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;AAGrE;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAoC;IACpE,KAAK,EAAE,CAAC;IACR,QAAQ,EAAE,CAAC;IACX,KAAK,EAAE,CAAC;IACR,QAAQ,EAAE,CAAC;IACX,MAAM,EAAE,CAAC;CACV,CAAC;AAEF,+EAA+E;AAE/E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,CAAU,CAAC;AAChF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;AAG7D,+EAA+E;AAE/E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,MAAM;IACN,MAAM;IACN,MAAM;IACN,KAAK;IACL,KAAK;IACL,OAAO;IACP,SAAS;CACD,CAAC;AACX,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;AAG/D,8EAA8E;AAE9E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,CAAU,CAAC;AAChG,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;AAGvE,8EAA8E;AAE9E;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAU,CAAC;AACrE,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;AAGnE,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,MAAM;IACN,YAAY;IACZ,aAAa;IACb,gBAAgB;IAChB,gBAAgB;IAChB,UAAU;IACV,WAAW;IACX,iBAAiB;IACjB,OAAO;CACC,CAAC;AACX,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;AAG/D,6CAA6C;AAC7C,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,CAAU,CAAC;AAC/F,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;AAG/D,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU,CAAC;AAC/D,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC","sourcesContent":["/**\n * Enum value arrays shared across the community feature.\n *\n * Each enum is exported as both a `*_VALUES` tuple and the corresponding\n * Zod schema so backend Mongoose models and frontend forms validate\n * against the exact same set.\n *\n * @module community-schema/enums\n */\n\nimport { z } from 'zod';\n\n// ─── Visibility scope ───────────────────────────────────────────────────────\n\n/**\n * The geographic level at which a piece of content is posted.\n *\n * Cascade-upward semantics: a viewer whose feed filter is set to L\n * sees every post at L and every BROADER level that matches their\n * area lineage. So:\n * - `local` → matches local + district + state + national + global within the viewer's locality / village / urbanBody / etc.\n * - `district` → matches district + state + national + global within the viewer's district\n * - `state` → matches state + national + global within the viewer's state\n * - `national` → matches national + global within the viewer's country\n * - `global` → matches global only\n *\n * The full meaning of \"local\" is \"anything below district\" — i.e., the\n * deepest area-lineage match (locality, village, urbanWard, …).\n */\nexport const VISIBILITY_LEVEL_VALUES = [\n 'local',\n 'district',\n 'state',\n 'national',\n 'global',\n] as const;\nexport const visibilityLevelSchema = z.enum(VISIBILITY_LEVEL_VALUES);\nexport type VisibilityLevel = z.infer<typeof visibilityLevelSchema>;\n\n/**\n * Numeric ranking used for the cascade-upward query and for sorting\n * filter chips. `local` is the deepest (most specific), `global` the\n * shallowest. Higher rank = broader scope.\n */\nexport const VISIBILITY_LEVEL_RANK: Record<VisibilityLevel, number> = {\n local: 0,\n district: 1,\n state: 2,\n national: 3,\n global: 4,\n};\n\n// ─── Polymorphic content kind ───────────────────────────────────────────────\n\n/**\n * The kinds of items that show up in the feed. `post` is the new\n * community-native kind; `lost_found` and `voice_box` are existing\n * features surfaced into the feed via polymorphic union.\n *\n * Engagement (upvote / reaction / comment) is keyed by\n * (contentKind, contentId) so every kind gets the same engagement bar.\n */\nexport const CONTENT_KIND_VALUES = ['post', 'lost_found', 'voice_box'] as const;\nexport const contentKindSchema = z.enum(CONTENT_KIND_VALUES);\nexport type ContentKind = z.infer<typeof contentKindSchema>;\n\n// ─── Reactions ──────────────────────────────────────────────────────────────\n\n/**\n * Reaction types available on every feed item.\n *\n * `support` replaces Facebook's \"Care\" — better-suited to a civic /\n * hyperlocal context where reacting to a complaint or lost-and-found\n * post deserves a distinct \"I support you\" signal.\n */\nexport const REACTION_TYPE_VALUES = [\n 'like',\n 'love',\n 'haha',\n 'wow',\n 'sad',\n 'angry',\n 'support',\n] as const;\nexport const reactionTypeSchema = z.enum(REACTION_TYPE_VALUES);\nexport type ReactionType = z.infer<typeof reactionTypeSchema>;\n\n// ─── Social graph ──────────────────────────────────────────────────────────\n\n/**\n * Connection (mutual friend) lifecycle.\n *\n * `pending` — requester sent it, recipient hasn't acted yet.\n * `accepted` — both sides confirmed; full visibility regardless of level.\n * `declined` — recipient rejected; surfaces as \"you can't reconnect for X days\" on the requester side.\n * `blocked` — either side blocked; permanently prevents reconnection.\n */\nexport const CONNECTION_STATUS_VALUES = ['pending', 'accepted', 'declined', 'blocked'] as const;\nexport const connectionStatusSchema = z.enum(CONNECTION_STATUS_VALUES);\nexport type ConnectionStatus = z.infer<typeof connectionStatusSchema>;\n\n// ─── Profile privacy ───────────────────────────────────────────────────────\n\n/**\n * Profile-level privacy (single toggle in Settings).\n *\n * `public` — anyone in the matching geo scope sees this user's posts.\n * `private` — only accepted Connections see this user's posts. Visibility\n * level on each post still applies on top (a private post at\n * state level is visible only to Connections who are in the\n * same state).\n */\nexport const PROFILE_PRIVACY_VALUES = ['public', 'private'] as const;\nexport const profilePrivacySchema = z.enum(PROFILE_PRIVACY_VALUES);\nexport type ProfilePrivacy = z.infer<typeof profilePrivacySchema>;\n\n// ─── Moderation ────────────────────────────────────────────────────────────\n\n/**\n * Reasons a piece of content can be reported. The list is deliberately\n * short — finer categorization can come from the optional `details`\n * free-text field on a report.\n */\nexport const REPORT_REASON_VALUES = [\n 'spam',\n 'harassment',\n 'hate_speech',\n 'misinformation',\n 'sexual_content',\n 'violence',\n 'self_harm',\n 'illegal_content',\n 'other',\n] as const;\nexport const reportReasonSchema = z.enum(REPORT_REASON_VALUES);\nexport type ReportReason = z.infer<typeof reportReasonSchema>;\n\n/** Moderator decision states on a report. */\nexport const REPORT_STATUS_VALUES = ['pending', 'reviewing', 'actioned', 'dismissed'] as const;\nexport const reportStatusSchema = z.enum(REPORT_STATUS_VALUES);\nexport type ReportStatus = z.infer<typeof reportStatusSchema>;\n\n// ─── Feed sort ─────────────────────────────────────────────────────────────\n\n/**\n * Sort options exposed in the feed UI.\n *\n * `hot` — the default. Reddit-style score blending upvotes, comment count,\n * and recency. Best surface for civic / community engagement.\n * `new` — strict reverse-chronological. Useful when the viewer wants to\n * catch up on everything fresh.\n * `top` — highest upvote count over a configurable window (defaults to\n * 24 hours on the client).\n */\nexport const FEED_SORT_VALUES = ['hot', 'new', 'top'] as const;\nexport const feedSortSchema = z.enum(FEED_SORT_VALUES);\nexport type FeedSort = z.infer<typeof feedSortSchema>;\n"]}
1
+ {"version":3,"file":"enums.js","sourceRoot":"","sources":["../src/enums.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,+EAA+E;AAE/E;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,OAAO;IACP,UAAU;IACV,OAAO;IACP,UAAU;IACV,QAAQ;CACA,CAAC;AACX,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;AAGrE;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAoC;IACpE,KAAK,EAAE,CAAC;IACR,QAAQ,EAAE,CAAC;IACX,KAAK,EAAE,CAAC;IACR,QAAQ,EAAE,CAAC;IACX,MAAM,EAAE,CAAC;CACV,CAAC;AAEF,+EAA+E;AAE/E;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,CAAU,CAAC;AAC/F,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;AAG7D,+EAA+E;AAE/E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,MAAM;IACN,MAAM;IACN,MAAM;IACN,KAAK;IACL,KAAK;IACL,OAAO;IACP,SAAS;CACD,CAAC;AACX,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;AAG/D,8EAA8E;AAE9E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,CAAU,CAAC;AAChG,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;AAGvE,8EAA8E;AAE9E;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAU,CAAC;AACrE,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;AAGnE,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,MAAM;IACN,YAAY;IACZ,aAAa;IACb,gBAAgB;IAChB,gBAAgB;IAChB,UAAU;IACV,WAAW;IACX,iBAAiB;IACjB,OAAO;CACC,CAAC;AACX,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;AAG/D,6CAA6C;AAC7C,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,CAAU,CAAC;AAC/F,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;AAG/D,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU,CAAC;AAC/D,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC","sourcesContent":["/**\n * Enum value arrays shared across the community feature.\n *\n * Each enum is exported as both a `*_VALUES` tuple and the corresponding\n * Zod schema so backend Mongoose models and frontend forms validate\n * against the exact same set.\n *\n * @module community-schema/enums\n */\n\nimport { z } from 'zod';\n\n// ─── Visibility scope ───────────────────────────────────────────────────────\n\n/**\n * The geographic level at which a piece of content is posted.\n *\n * Cascade-upward semantics: a viewer whose feed filter is set to L\n * sees every post at L and every BROADER level that matches their\n * area lineage. So:\n * - `local` → matches local + district + state + national + global within the viewer's locality / village / urbanBody / etc.\n * - `district` → matches district + state + national + global within the viewer's district\n * - `state` → matches state + national + global within the viewer's state\n * - `national` → matches national + global within the viewer's country\n * - `global` → matches global only\n *\n * The full meaning of \"local\" is \"anything below district\" — i.e., the\n * deepest area-lineage match (locality, village, urbanWard, …).\n */\nexport const VISIBILITY_LEVEL_VALUES = [\n 'local',\n 'district',\n 'state',\n 'national',\n 'global',\n] as const;\nexport const visibilityLevelSchema = z.enum(VISIBILITY_LEVEL_VALUES);\nexport type VisibilityLevel = z.infer<typeof visibilityLevelSchema>;\n\n/**\n * Numeric ranking used for the cascade-upward query and for sorting\n * filter chips. `local` is the deepest (most specific), `global` the\n * shallowest. Higher rank = broader scope.\n */\nexport const VISIBILITY_LEVEL_RANK: Record<VisibilityLevel, number> = {\n local: 0,\n district: 1,\n state: 2,\n national: 3,\n global: 4,\n};\n\n// ─── Polymorphic content kind ───────────────────────────────────────────────\n\n/**\n * The kinds of items that show up in the feed. `post` is the new\n * community-native kind; `lost_found`, `voice_box`, and `donate_item`\n * are existing legacy features surfaced into the feed via polymorphic\n * union.\n *\n * Engagement (upvote / reaction / comment / share) is keyed by\n * (contentKind, contentId) so every kind gets the same engagement bar.\n */\nexport const CONTENT_KIND_VALUES = ['post', 'lost_found', 'voice_box', 'donate_item'] as const;\nexport const contentKindSchema = z.enum(CONTENT_KIND_VALUES);\nexport type ContentKind = z.infer<typeof contentKindSchema>;\n\n// ─── Reactions ──────────────────────────────────────────────────────────────\n\n/**\n * Reaction types available on every feed item.\n *\n * `support` replaces Facebook's \"Care\" — better-suited to a civic /\n * hyperlocal context where reacting to a complaint or lost-and-found\n * post deserves a distinct \"I support you\" signal.\n */\nexport const REACTION_TYPE_VALUES = [\n 'like',\n 'love',\n 'haha',\n 'wow',\n 'sad',\n 'angry',\n 'support',\n] as const;\nexport const reactionTypeSchema = z.enum(REACTION_TYPE_VALUES);\nexport type ReactionType = z.infer<typeof reactionTypeSchema>;\n\n// ─── Social graph ──────────────────────────────────────────────────────────\n\n/**\n * Connection (mutual friend) lifecycle.\n *\n * `pending` — requester sent it, recipient hasn't acted yet.\n * `accepted` — both sides confirmed; full visibility regardless of level.\n * `declined` — recipient rejected; surfaces as \"you can't reconnect for X days\" on the requester side.\n * `blocked` — either side blocked; permanently prevents reconnection.\n */\nexport const CONNECTION_STATUS_VALUES = ['pending', 'accepted', 'declined', 'blocked'] as const;\nexport const connectionStatusSchema = z.enum(CONNECTION_STATUS_VALUES);\nexport type ConnectionStatus = z.infer<typeof connectionStatusSchema>;\n\n// ─── Profile privacy ───────────────────────────────────────────────────────\n\n/**\n * Profile-level privacy (single toggle in Settings).\n *\n * `public` — anyone in the matching geo scope sees this user's posts.\n * `private` — only accepted Connections see this user's posts. Visibility\n * level on each post still applies on top (a private post at\n * state level is visible only to Connections who are in the\n * same state).\n */\nexport const PROFILE_PRIVACY_VALUES = ['public', 'private'] as const;\nexport const profilePrivacySchema = z.enum(PROFILE_PRIVACY_VALUES);\nexport type ProfilePrivacy = z.infer<typeof profilePrivacySchema>;\n\n// ─── Moderation ────────────────────────────────────────────────────────────\n\n/**\n * Reasons a piece of content can be reported. The list is deliberately\n * short — finer categorization can come from the optional `details`\n * free-text field on a report.\n */\nexport const REPORT_REASON_VALUES = [\n 'spam',\n 'harassment',\n 'hate_speech',\n 'misinformation',\n 'sexual_content',\n 'violence',\n 'self_harm',\n 'illegal_content',\n 'other',\n] as const;\nexport const reportReasonSchema = z.enum(REPORT_REASON_VALUES);\nexport type ReportReason = z.infer<typeof reportReasonSchema>;\n\n/** Moderator decision states on a report. */\nexport const REPORT_STATUS_VALUES = ['pending', 'reviewing', 'actioned', 'dismissed'] as const;\nexport const reportStatusSchema = z.enum(REPORT_STATUS_VALUES);\nexport type ReportStatus = z.infer<typeof reportStatusSchema>;\n\n// ─── Feed sort ─────────────────────────────────────────────────────────────\n\n/**\n * Sort options exposed in the feed UI.\n *\n * `hot` — the default. Reddit-style score blending upvotes, comment count,\n * and recency. Best surface for civic / community engagement.\n * `new` — strict reverse-chronological. Useful when the viewer wants to\n * catch up on everything fresh.\n * `top` — highest upvote count over a configurable window (defaults to\n * 24 hours on the client).\n */\nexport const FEED_SORT_VALUES = ['hot', 'new', 'top'] as const;\nexport const feedSortSchema = z.enum(FEED_SORT_VALUES);\nexport type FeedSort = z.infer<typeof feedSortSchema>;\n"]}
package/dist/feed.d.ts CHANGED
@@ -45,15 +45,50 @@ export declare const lostFoundFeedItemSchema: z.ZodObject<{
45
45
  imageUrl: z.ZodOptional<z.ZodString>;
46
46
  upvoteCount: z.ZodNumber;
47
47
  commentCount: z.ZodNumber;
48
+ repostCount: z.ZodNumber;
48
49
  reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
49
50
  createdAt: z.ZodString;
50
51
  viewer: z.ZodOptional<z.ZodObject<{
51
52
  upvoted: z.ZodBoolean;
52
53
  reaction: z.ZodNullable<z.ZodString>;
53
54
  isAuthor: z.ZodBoolean;
55
+ reposted: z.ZodBoolean;
54
56
  }, z.core.$strip>>;
55
57
  }, z.core.$strip>;
56
58
  export type LostFoundFeedItem = z.infer<typeof lostFoundFeedItemSchema>;
59
+ /**
60
+ * Donated-item listing projected into the feed. Mirrors the engagement
61
+ * + author shape every feed card needs, plus the donate-item-specific
62
+ * fields the card renders (item name, category, condition, status).
63
+ */
64
+ export declare const donateItemFeedItemSchema: z.ZodObject<{
65
+ _id: z.ZodString;
66
+ contentKind: z.ZodLiteral<"donate_item">;
67
+ author: z.ZodObject<{
68
+ userId: z.ZodString;
69
+ displayName: z.ZodString;
70
+ avatarUrl: z.ZodOptional<z.ZodString>;
71
+ localName: z.ZodOptional<z.ZodString>;
72
+ }, z.core.$strip>;
73
+ itemName: z.ZodString;
74
+ description: z.ZodOptional<z.ZodString>;
75
+ category: z.ZodString;
76
+ condition: z.ZodOptional<z.ZodString>;
77
+ imageUrl: z.ZodOptional<z.ZodString>;
78
+ status: z.ZodString;
79
+ upvoteCount: z.ZodNumber;
80
+ commentCount: z.ZodNumber;
81
+ repostCount: z.ZodNumber;
82
+ reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
83
+ createdAt: z.ZodString;
84
+ viewer: z.ZodOptional<z.ZodObject<{
85
+ upvoted: z.ZodBoolean;
86
+ reaction: z.ZodNullable<z.ZodString>;
87
+ isAuthor: z.ZodBoolean;
88
+ reposted: z.ZodBoolean;
89
+ }, z.core.$strip>>;
90
+ }, z.core.$strip>;
91
+ export type DonateItemFeedItem = z.infer<typeof donateItemFeedItemSchema>;
57
92
  /**
58
93
  * Voice-box (complaint or suggestion) item projected into the feed.
59
94
  */
@@ -74,12 +109,14 @@ export declare const voiceBoxFeedItemSchema: z.ZodObject<{
74
109
  description: z.ZodString;
75
110
  upvoteCount: z.ZodNumber;
76
111
  commentCount: z.ZodNumber;
112
+ repostCount: z.ZodNumber;
77
113
  reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
78
114
  createdAt: z.ZodString;
79
115
  viewer: z.ZodOptional<z.ZodObject<{
80
116
  upvoted: z.ZodBoolean;
81
117
  reaction: z.ZodNullable<z.ZodString>;
82
118
  isAuthor: z.ZodBoolean;
119
+ reposted: z.ZodBoolean;
83
120
  }, z.core.$strip>>;
84
121
  }, z.core.$strip>;
85
122
  export type VoiceBoxFeedItem = z.infer<typeof voiceBoxFeedItemSchema>;
@@ -125,7 +162,28 @@ export declare const feedItemSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
125
162
  }, z.core.$strip>;
126
163
  upvoteCount: z.ZodNumber;
127
164
  commentCount: z.ZodNumber;
165
+ repostCount: z.ZodNumber;
128
166
  reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
167
+ repostOf: z.ZodOptional<z.ZodObject<{
168
+ contentKind: z.ZodEnum<{
169
+ post: "post";
170
+ lost_found: "lost_found";
171
+ voice_box: "voice_box";
172
+ donate_item: "donate_item";
173
+ }>;
174
+ _id: z.ZodString;
175
+ author: z.ZodObject<{
176
+ userId: z.ZodString;
177
+ displayName: z.ZodString;
178
+ avatarUrl: z.ZodOptional<z.ZodString>;
179
+ localName: z.ZodOptional<z.ZodString>;
180
+ }, z.core.$strip>;
181
+ title: z.ZodString;
182
+ body: z.ZodOptional<z.ZodString>;
183
+ imageUrl: z.ZodOptional<z.ZodString>;
184
+ kindBadge: z.ZodOptional<z.ZodString>;
185
+ createdAt: z.ZodString;
186
+ }, z.core.$strip>>;
129
187
  createdAt: z.ZodString;
130
188
  editedAt: z.ZodNullable<z.ZodString>;
131
189
  deletedAt: z.ZodNullable<z.ZodString>;
@@ -133,6 +191,7 @@ export declare const feedItemSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
133
191
  upvoted: z.ZodBoolean;
134
192
  reaction: z.ZodNullable<z.ZodString>;
135
193
  isAuthor: z.ZodBoolean;
194
+ reposted: z.ZodBoolean;
136
195
  }, z.core.$strip>>;
137
196
  }, z.core.$strip>, z.ZodObject<{
138
197
  _id: z.ZodString;
@@ -152,12 +211,14 @@ export declare const feedItemSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
152
211
  imageUrl: z.ZodOptional<z.ZodString>;
153
212
  upvoteCount: z.ZodNumber;
154
213
  commentCount: z.ZodNumber;
214
+ repostCount: z.ZodNumber;
155
215
  reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
156
216
  createdAt: z.ZodString;
157
217
  viewer: z.ZodOptional<z.ZodObject<{
158
218
  upvoted: z.ZodBoolean;
159
219
  reaction: z.ZodNullable<z.ZodString>;
160
220
  isAuthor: z.ZodBoolean;
221
+ reposted: z.ZodBoolean;
161
222
  }, z.core.$strip>>;
162
223
  }, z.core.$strip>, z.ZodObject<{
163
224
  _id: z.ZodString;
@@ -176,12 +237,40 @@ export declare const feedItemSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
176
237
  description: z.ZodString;
177
238
  upvoteCount: z.ZodNumber;
178
239
  commentCount: z.ZodNumber;
240
+ repostCount: z.ZodNumber;
179
241
  reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
180
242
  createdAt: z.ZodString;
181
243
  viewer: z.ZodOptional<z.ZodObject<{
182
244
  upvoted: z.ZodBoolean;
183
245
  reaction: z.ZodNullable<z.ZodString>;
184
246
  isAuthor: z.ZodBoolean;
247
+ reposted: z.ZodBoolean;
248
+ }, z.core.$strip>>;
249
+ }, z.core.$strip>, z.ZodObject<{
250
+ _id: z.ZodString;
251
+ contentKind: z.ZodLiteral<"donate_item">;
252
+ author: z.ZodObject<{
253
+ userId: z.ZodString;
254
+ displayName: z.ZodString;
255
+ avatarUrl: z.ZodOptional<z.ZodString>;
256
+ localName: z.ZodOptional<z.ZodString>;
257
+ }, z.core.$strip>;
258
+ itemName: z.ZodString;
259
+ description: z.ZodOptional<z.ZodString>;
260
+ category: z.ZodString;
261
+ condition: z.ZodOptional<z.ZodString>;
262
+ imageUrl: z.ZodOptional<z.ZodString>;
263
+ status: z.ZodString;
264
+ upvoteCount: z.ZodNumber;
265
+ commentCount: z.ZodNumber;
266
+ repostCount: z.ZodNumber;
267
+ reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
268
+ createdAt: z.ZodString;
269
+ viewer: z.ZodOptional<z.ZodObject<{
270
+ upvoted: z.ZodBoolean;
271
+ reaction: z.ZodNullable<z.ZodString>;
272
+ isAuthor: z.ZodBoolean;
273
+ reposted: z.ZodBoolean;
185
274
  }, z.core.$strip>>;
186
275
  }, z.core.$strip>], "contentKind">;
187
276
  export type FeedItem = z.infer<typeof feedItemSchema>;
@@ -245,7 +334,28 @@ export declare const feedResponseSchema: z.ZodObject<{
245
334
  }, z.core.$strip>;
246
335
  upvoteCount: z.ZodNumber;
247
336
  commentCount: z.ZodNumber;
337
+ repostCount: z.ZodNumber;
248
338
  reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
339
+ repostOf: z.ZodOptional<z.ZodObject<{
340
+ contentKind: z.ZodEnum<{
341
+ post: "post";
342
+ lost_found: "lost_found";
343
+ voice_box: "voice_box";
344
+ donate_item: "donate_item";
345
+ }>;
346
+ _id: z.ZodString;
347
+ author: z.ZodObject<{
348
+ userId: z.ZodString;
349
+ displayName: z.ZodString;
350
+ avatarUrl: z.ZodOptional<z.ZodString>;
351
+ localName: z.ZodOptional<z.ZodString>;
352
+ }, z.core.$strip>;
353
+ title: z.ZodString;
354
+ body: z.ZodOptional<z.ZodString>;
355
+ imageUrl: z.ZodOptional<z.ZodString>;
356
+ kindBadge: z.ZodOptional<z.ZodString>;
357
+ createdAt: z.ZodString;
358
+ }, z.core.$strip>>;
249
359
  createdAt: z.ZodString;
250
360
  editedAt: z.ZodNullable<z.ZodString>;
251
361
  deletedAt: z.ZodNullable<z.ZodString>;
@@ -253,6 +363,7 @@ export declare const feedResponseSchema: z.ZodObject<{
253
363
  upvoted: z.ZodBoolean;
254
364
  reaction: z.ZodNullable<z.ZodString>;
255
365
  isAuthor: z.ZodBoolean;
366
+ reposted: z.ZodBoolean;
256
367
  }, z.core.$strip>>;
257
368
  }, z.core.$strip>, z.ZodObject<{
258
369
  _id: z.ZodString;
@@ -272,12 +383,14 @@ export declare const feedResponseSchema: z.ZodObject<{
272
383
  imageUrl: z.ZodOptional<z.ZodString>;
273
384
  upvoteCount: z.ZodNumber;
274
385
  commentCount: z.ZodNumber;
386
+ repostCount: z.ZodNumber;
275
387
  reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
276
388
  createdAt: z.ZodString;
277
389
  viewer: z.ZodOptional<z.ZodObject<{
278
390
  upvoted: z.ZodBoolean;
279
391
  reaction: z.ZodNullable<z.ZodString>;
280
392
  isAuthor: z.ZodBoolean;
393
+ reposted: z.ZodBoolean;
281
394
  }, z.core.$strip>>;
282
395
  }, z.core.$strip>, z.ZodObject<{
283
396
  _id: z.ZodString;
@@ -296,12 +409,40 @@ export declare const feedResponseSchema: z.ZodObject<{
296
409
  description: z.ZodString;
297
410
  upvoteCount: z.ZodNumber;
298
411
  commentCount: z.ZodNumber;
412
+ repostCount: z.ZodNumber;
413
+ reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
414
+ createdAt: z.ZodString;
415
+ viewer: z.ZodOptional<z.ZodObject<{
416
+ upvoted: z.ZodBoolean;
417
+ reaction: z.ZodNullable<z.ZodString>;
418
+ isAuthor: z.ZodBoolean;
419
+ reposted: z.ZodBoolean;
420
+ }, z.core.$strip>>;
421
+ }, z.core.$strip>, z.ZodObject<{
422
+ _id: z.ZodString;
423
+ contentKind: z.ZodLiteral<"donate_item">;
424
+ author: z.ZodObject<{
425
+ userId: z.ZodString;
426
+ displayName: z.ZodString;
427
+ avatarUrl: z.ZodOptional<z.ZodString>;
428
+ localName: z.ZodOptional<z.ZodString>;
429
+ }, z.core.$strip>;
430
+ itemName: z.ZodString;
431
+ description: z.ZodOptional<z.ZodString>;
432
+ category: z.ZodString;
433
+ condition: z.ZodOptional<z.ZodString>;
434
+ imageUrl: z.ZodOptional<z.ZodString>;
435
+ status: z.ZodString;
436
+ upvoteCount: z.ZodNumber;
437
+ commentCount: z.ZodNumber;
438
+ repostCount: z.ZodNumber;
299
439
  reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
300
440
  createdAt: z.ZodString;
301
441
  viewer: z.ZodOptional<z.ZodObject<{
302
442
  upvoted: z.ZodBoolean;
303
443
  reaction: z.ZodNullable<z.ZodString>;
304
444
  isAuthor: z.ZodBoolean;
445
+ reposted: z.ZodBoolean;
305
446
  }, z.core.$strip>>;
306
447
  }, z.core.$strip>], "contentKind">>;
307
448
  nextCursor: z.ZodNullable<z.ZodString>;
@@ -1 +1 @@
1
- {"version":3,"file":"feed.d.ts","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAMhD;;;;GAIG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;iBA0BlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;iBAuBjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAItE,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCAIzB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAItD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;iBAShC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAG7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAG9D,OAAO,EAAE,cAAc,EAAE,CAAC"}
1
+ {"version":3,"file":"feed.d.ts","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAMhD;;;;GAIG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4BlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiCnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E;;GAEG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyBjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAItE,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCAKzB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAItD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;iBAShC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAG7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAG9D,OAAO,EAAE,cAAc,EAAE,CAAC"}
package/dist/feed.js CHANGED
@@ -43,6 +43,7 @@ export const lostFoundFeedItemSchema = z.object({
43
43
  /** Inherited engagement axis — same shape as posts. */
44
44
  upvoteCount: z.number().int().nonnegative(),
45
45
  commentCount: z.number().int().nonnegative(),
46
+ repostCount: z.number().int().nonnegative(),
46
47
  reactionCounts: z.record(z.string(), z.number().int().nonnegative()),
47
48
  createdAt: z.string().datetime(),
48
49
  viewer: z
@@ -50,6 +51,42 @@ export const lostFoundFeedItemSchema = z.object({
50
51
  upvoted: z.boolean(),
51
52
  reaction: z.string().nullable(),
52
53
  isAuthor: z.boolean(),
54
+ reposted: z.boolean(),
55
+ })
56
+ .optional(),
57
+ });
58
+ /**
59
+ * Donated-item listing projected into the feed. Mirrors the engagement
60
+ * + author shape every feed card needs, plus the donate-item-specific
61
+ * fields the card renders (item name, category, condition, status).
62
+ */
63
+ export const donateItemFeedItemSchema = z.object({
64
+ _id: z.string(),
65
+ contentKind: z.literal('donate_item'),
66
+ author: postAuthorSnapshotSchema,
67
+ /** Display name of the item (maps to ReformItemDonation.itemName). */
68
+ itemName: z.string(),
69
+ description: z.string().optional(),
70
+ /** Free-text category (clothing / books / electronics / etc.). */
71
+ category: z.string(),
72
+ /** Free-text condition label (new / good / used / etc.). Optional. */
73
+ condition: z.string().optional(),
74
+ /** First photo if present — used as the card's hero image. */
75
+ imageUrl: z.string().url().optional(),
76
+ /** Donation lifecycle status (pending / verified / picked / etc.) so
77
+ * the card can show an appropriate badge. */
78
+ status: z.string(),
79
+ upvoteCount: z.number().int().nonnegative(),
80
+ commentCount: z.number().int().nonnegative(),
81
+ repostCount: z.number().int().nonnegative(),
82
+ reactionCounts: z.record(z.string(), z.number().int().nonnegative()),
83
+ createdAt: z.string().datetime(),
84
+ viewer: z
85
+ .object({
86
+ upvoted: z.boolean(),
87
+ reaction: z.string().nullable(),
88
+ isAuthor: z.boolean(),
89
+ reposted: z.boolean(),
53
90
  })
54
91
  .optional(),
55
92
  });
@@ -66,6 +103,7 @@ export const voiceBoxFeedItemSchema = z.object({
66
103
  description: z.string(),
67
104
  upvoteCount: z.number().int().nonnegative(),
68
105
  commentCount: z.number().int().nonnegative(),
106
+ repostCount: z.number().int().nonnegative(),
69
107
  reactionCounts: z.record(z.string(), z.number().int().nonnegative()),
70
108
  createdAt: z.string().datetime(),
71
109
  viewer: z
@@ -73,6 +111,7 @@ export const voiceBoxFeedItemSchema = z.object({
73
111
  upvoted: z.boolean(),
74
112
  reaction: z.string().nullable(),
75
113
  isAuthor: z.boolean(),
114
+ reposted: z.boolean(),
76
115
  })
77
116
  .optional(),
78
117
  });
@@ -81,6 +120,7 @@ export const feedItemSchema = z.discriminatedUnion('contentKind', [
81
120
  communityPostWireSchema,
82
121
  lostFoundFeedItemSchema,
83
122
  voiceBoxFeedItemSchema,
123
+ donateItemFeedItemSchema,
84
124
  ]);
85
125
  // ─── Query + response ──────────────────────────────────────────────────────
86
126
  export const feedQueryParamsSchema = z.object({
package/dist/feed.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"feed.js","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Feed wire shapes — polymorphic union of community posts, lost-and-\n * found reports, and voice-box (complaints + suggestions).\n *\n * The feed is a SINGLE list typed as `FeedItem = Post | LostFound | VoiceBox`.\n * The client discriminates on `contentKind` and renders the matching\n * card component.\n *\n * Strategy notes (server-side):\n * - Read-time fanout. The server runs ONE parallel query per kind\n * filtered by the same area-lineage match and the same cascade-\n * upward visibility rule, then merges and sorts by `hot` score.\n * - Cursor-based pagination. The cursor is opaque (base64 of\n * `<sortKey>|<_id>`) so we can swap sort algorithms without a\n * breaking change.\n * - LostFound / VoiceBox shapes are pruned to the fields the feed\n * card needs — they keep their richer detail-page shape in their\n * own schemas (lost-found-schema, voice-box-schema if/when those\n * get factored out).\n *\n * @module community-schema/feed\n */\n\nimport { z } from 'zod';\nimport { FEED_PAGE_SIZE } from './constants.js';\nimport { feedSortSchema, visibilityLevelSchema } from './enums.js';\nimport { communityPostWireSchema, postAuthorSnapshotSchema } from './post.js';\n\n// ─── Per-kind feed envelopes ───────────────────────────────────────────────\n\n/**\n * Lost-and-found item projected into the feed. Mirrors the engagement\n * + author shape every feed card needs, plus a small handful of\n * lost-found-specific fields used by the card.\n */\nexport const lostFoundFeedItemSchema = z.object({\n _id: z.string(),\n contentKind: z.literal('lost_found'),\n author: postAuthorSnapshotSchema,\n\n /** `'lost'` or `'found'` — drives the card chip colour. */\n kind: z.enum(['lost', 'found']),\n title: z.string(),\n description: z.string(),\n /** Single primary image rendered on the card. */\n imageUrl: z.string().url().optional(),\n\n /** Inherited engagement axis — same shape as posts. */\n upvoteCount: z.number().int().nonnegative(),\n commentCount: z.number().int().nonnegative(),\n reactionCounts: z.record(z.string(), z.number().int().nonnegative()),\n\n createdAt: z.string().datetime(),\n\n viewer: z\n .object({\n upvoted: z.boolean(),\n reaction: z.string().nullable(),\n isAuthor: z.boolean(),\n })\n .optional(),\n});\nexport type LostFoundFeedItem = z.infer<typeof lostFoundFeedItemSchema>;\n\n/**\n * Voice-box (complaint or suggestion) item projected into the feed.\n */\nexport const voiceBoxFeedItemSchema = z.object({\n _id: z.string(),\n contentKind: z.literal('voice_box'),\n author: postAuthorSnapshotSchema,\n\n /** `'complaint'` or `'suggestion'` — drives the card chip colour. */\n kind: z.enum(['complaint', 'suggestion']),\n title: z.string(),\n description: z.string(),\n\n upvoteCount: z.number().int().nonnegative(),\n commentCount: z.number().int().nonnegative(),\n reactionCounts: z.record(z.string(), z.number().int().nonnegative()),\n\n createdAt: z.string().datetime(),\n\n viewer: z\n .object({\n upvoted: z.boolean(),\n reaction: z.string().nullable(),\n isAuthor: z.boolean(),\n })\n .optional(),\n});\nexport type VoiceBoxFeedItem = z.infer<typeof voiceBoxFeedItemSchema>;\n\n// ─── Polymorphic union ─────────────────────────────────────────────────────\n\nexport const feedItemSchema = z.discriminatedUnion('contentKind', [\n communityPostWireSchema,\n lostFoundFeedItemSchema,\n voiceBoxFeedItemSchema,\n]);\nexport type FeedItem = z.infer<typeof feedItemSchema>;\n\n// ─── Query + response ──────────────────────────────────────────────────────\n\nexport const feedQueryParamsSchema = z.object({\n /** The viewer's chosen feed level. Cascade-upward semantics apply. */\n level: visibilityLevelSchema,\n /** Sort algorithm. Defaults to `hot` server-side when omitted. */\n sort: feedSortSchema.optional(),\n /** Opaque pagination cursor returned by the previous page. */\n cursor: z.string().optional(),\n /** Page size; defaults to `FEED_PAGE_SIZE` server-side. */\n pageSize: z.number().int().positive().max(50).optional(),\n});\nexport type FeedQueryParams = z.infer<typeof feedQueryParamsSchema>;\n\nexport const feedResponseSchema = z.object({\n items: z.array(feedItemSchema),\n nextCursor: z.string().nullable(),\n});\nexport type FeedResponse = z.infer<typeof feedResponseSchema>;\n\n// Re-export so consumers don't have to also import constants.ts.\nexport { FEED_PAGE_SIZE };\n"]}
1
+ {"version":3,"file":"feed.js","sourceRoot":"","sources":["../src/feed.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Feed wire shapes — polymorphic union of community posts, lost-and-\n * found reports, and voice-box (complaints + suggestions).\n *\n * The feed is a SINGLE list typed as `FeedItem = Post | LostFound | VoiceBox`.\n * The client discriminates on `contentKind` and renders the matching\n * card component.\n *\n * Strategy notes (server-side):\n * - Read-time fanout. The server runs ONE parallel query per kind\n * filtered by the same area-lineage match and the same cascade-\n * upward visibility rule, then merges and sorts by `hot` score.\n * - Cursor-based pagination. The cursor is opaque (base64 of\n * `<sortKey>|<_id>`) so we can swap sort algorithms without a\n * breaking change.\n * - LostFound / VoiceBox shapes are pruned to the fields the feed\n * card needs — they keep their richer detail-page shape in their\n * own schemas (lost-found-schema, voice-box-schema if/when those\n * get factored out).\n *\n * @module community-schema/feed\n */\n\nimport { z } from 'zod';\nimport { FEED_PAGE_SIZE } from './constants.js';\nimport { feedSortSchema, visibilityLevelSchema } from './enums.js';\nimport { communityPostWireSchema, postAuthorSnapshotSchema } from './post.js';\n\n// ─── Per-kind feed envelopes ───────────────────────────────────────────────\n\n/**\n * Lost-and-found item projected into the feed. Mirrors the engagement\n * + author shape every feed card needs, plus a small handful of\n * lost-found-specific fields used by the card.\n */\nexport const lostFoundFeedItemSchema = z.object({\n _id: z.string(),\n contentKind: z.literal('lost_found'),\n author: postAuthorSnapshotSchema,\n\n /** `'lost'` or `'found'` — drives the card chip colour. */\n kind: z.enum(['lost', 'found']),\n title: z.string(),\n description: z.string(),\n /** Single primary image rendered on the card. */\n imageUrl: z.string().url().optional(),\n\n /** Inherited engagement axis — same shape as posts. */\n upvoteCount: z.number().int().nonnegative(),\n commentCount: z.number().int().nonnegative(),\n repostCount: z.number().int().nonnegative(),\n reactionCounts: z.record(z.string(), z.number().int().nonnegative()),\n\n createdAt: z.string().datetime(),\n\n viewer: z\n .object({\n upvoted: z.boolean(),\n reaction: z.string().nullable(),\n isAuthor: z.boolean(),\n reposted: z.boolean(),\n })\n .optional(),\n});\nexport type LostFoundFeedItem = z.infer<typeof lostFoundFeedItemSchema>;\n\n/**\n * Donated-item listing projected into the feed. Mirrors the engagement\n * + author shape every feed card needs, plus the donate-item-specific\n * fields the card renders (item name, category, condition, status).\n */\nexport const donateItemFeedItemSchema = z.object({\n _id: z.string(),\n contentKind: z.literal('donate_item'),\n author: postAuthorSnapshotSchema,\n\n /** Display name of the item (maps to ReformItemDonation.itemName). */\n itemName: z.string(),\n description: z.string().optional(),\n /** Free-text category (clothing / books / electronics / etc.). */\n category: z.string(),\n /** Free-text condition label (new / good / used / etc.). Optional. */\n condition: z.string().optional(),\n /** First photo if present — used as the card's hero image. */\n imageUrl: z.string().url().optional(),\n /** Donation lifecycle status (pending / verified / picked / etc.) so\n * the card can show an appropriate badge. */\n status: z.string(),\n\n upvoteCount: z.number().int().nonnegative(),\n commentCount: z.number().int().nonnegative(),\n repostCount: z.number().int().nonnegative(),\n reactionCounts: z.record(z.string(), z.number().int().nonnegative()),\n\n createdAt: z.string().datetime(),\n\n viewer: z\n .object({\n upvoted: z.boolean(),\n reaction: z.string().nullable(),\n isAuthor: z.boolean(),\n reposted: z.boolean(),\n })\n .optional(),\n});\nexport type DonateItemFeedItem = z.infer<typeof donateItemFeedItemSchema>;\n\n/**\n * Voice-box (complaint or suggestion) item projected into the feed.\n */\nexport const voiceBoxFeedItemSchema = z.object({\n _id: z.string(),\n contentKind: z.literal('voice_box'),\n author: postAuthorSnapshotSchema,\n\n /** `'complaint'` or `'suggestion'` — drives the card chip colour. */\n kind: z.enum(['complaint', 'suggestion']),\n title: z.string(),\n description: z.string(),\n\n upvoteCount: z.number().int().nonnegative(),\n commentCount: z.number().int().nonnegative(),\n repostCount: z.number().int().nonnegative(),\n reactionCounts: z.record(z.string(), z.number().int().nonnegative()),\n\n createdAt: z.string().datetime(),\n\n viewer: z\n .object({\n upvoted: z.boolean(),\n reaction: z.string().nullable(),\n isAuthor: z.boolean(),\n reposted: z.boolean(),\n })\n .optional(),\n});\nexport type VoiceBoxFeedItem = z.infer<typeof voiceBoxFeedItemSchema>;\n\n// ─── Polymorphic union ─────────────────────────────────────────────────────\n\nexport const feedItemSchema = z.discriminatedUnion('contentKind', [\n communityPostWireSchema,\n lostFoundFeedItemSchema,\n voiceBoxFeedItemSchema,\n donateItemFeedItemSchema,\n]);\nexport type FeedItem = z.infer<typeof feedItemSchema>;\n\n// ─── Query + response ──────────────────────────────────────────────────────\n\nexport const feedQueryParamsSchema = z.object({\n /** The viewer's chosen feed level. Cascade-upward semantics apply. */\n level: visibilityLevelSchema,\n /** Sort algorithm. Defaults to `hot` server-side when omitted. */\n sort: feedSortSchema.optional(),\n /** Opaque pagination cursor returned by the previous page. */\n cursor: z.string().optional(),\n /** Page size; defaults to `FEED_PAGE_SIZE` server-side. */\n pageSize: z.number().int().positive().max(50).optional(),\n});\nexport type FeedQueryParams = z.infer<typeof feedQueryParamsSchema>;\n\nexport const feedResponseSchema = z.object({\n items: z.array(feedItemSchema),\n nextCursor: z.string().nullable(),\n});\nexport type FeedResponse = z.infer<typeof feedResponseSchema>;\n\n// Re-export so consumers don't have to also import constants.ts.\nexport { FEED_PAGE_SIZE };\n"]}
package/dist/index.d.ts CHANGED
@@ -19,12 +19,13 @@ export { type CommunityCommentWire, type CreateCommentBody, communityCommentWire
19
19
  export { COMMENT_MAX_BODY_CHARS, COMMENTS_PAGE_SIZE, EDIT_GRACE_WINDOW_MS, FEED_PAGE_SIZE, GROUP_MAX_MEMBERS, GROUP_NAME_MAX_CHARS, GROUP_NAME_MIN_CHARS, PEOPLE_PAGE_SIZE, POST_MAX_BODY_CHARS, POST_MAX_IMAGE_BYTES, POST_MAX_IMAGES, POST_MAX_VIDEO_BYTES, POST_MAX_VIDEO_SECONDS, POST_MAX_VIDEOS, PROFILE_MAX_BIO_CHARS, REPLIES_PRELOAD_COUNT, } from './constants.js';
20
20
  export { type SetReactionBody, type SetReactionResponse, setReactionBodySchema, setReactionResponseSchema, type ToggleUpvoteResponse, toggleUpvoteResponseSchema, } from './engagement.js';
21
21
  export { CONNECTION_STATUS_VALUES, CONTENT_KIND_VALUES, type ConnectionStatus, type ContentKind, connectionStatusSchema, contentKindSchema, FEED_SORT_VALUES, type FeedSort, feedSortSchema, PROFILE_PRIVACY_VALUES, type ProfilePrivacy, profilePrivacySchema, REACTION_TYPE_VALUES, REPORT_REASON_VALUES, REPORT_STATUS_VALUES, type ReactionType, type ReportReason, type ReportStatus, reactionTypeSchema, reportReasonSchema, reportStatusSchema, VISIBILITY_LEVEL_RANK, VISIBILITY_LEVEL_VALUES, type VisibilityLevel, visibilityLevelSchema, } from './enums.js';
22
- export { type FeedItem, type FeedQueryParams, type FeedResponse, feedItemSchema, feedQueryParamsSchema, feedResponseSchema, type LostFoundFeedItem, lostFoundFeedItemSchema, type VoiceBoxFeedItem, voiceBoxFeedItemSchema, } from './feed.js';
23
- export { type AddGroupMembersBody, addGroupMembersBodySchema, type CommunityGroupWire, type CommunityGroupWithMembers, communityGroupWireSchema, communityGroupWithMembersSchema, type CreateGroupBody, createGroupBodySchema, type GroupMemberWire, type GroupMembersListResponse, groupMemberWireSchema, groupMembersListResponseSchema, type GroupRole, groupRoleSchema, type GroupsListResponse, groupsListResponseSchema, type UpdateGroupBody, updateGroupBodySchema, } from './group.js';
22
+ export { type DonateItemFeedItem, donateItemFeedItemSchema, type FeedItem, type FeedQueryParams, type FeedResponse, feedItemSchema, feedQueryParamsSchema, feedResponseSchema, type LostFoundFeedItem, lostFoundFeedItemSchema, type VoiceBoxFeedItem, voiceBoxFeedItemSchema, } from './feed.js';
23
+ export { type AddGroupMembersBody, addGroupMembersBodySchema, type CommunityGroupWire, type CommunityGroupWithMembers, type CreateGroupBody, communityGroupWireSchema, communityGroupWithMembersSchema, createGroupBodySchema, type GroupMembersListResponse, type GroupMemberWire, type GroupRole, type GroupsListResponse, groupMembersListResponseSchema, groupMemberWireSchema, groupRoleSchema, groupsListResponseSchema, type UpdateGroupBody, updateGroupBodySchema, } from './group.js';
24
24
  export { type PostImage, type PostVideo, postImageSchema, postVideoSchema, } from './media.js';
25
- export { type CommunityPostWire, type CreatePostBody, communityPostWireSchema, createPostBodySchema, type PostAuthorSnapshot, postAuthorSnapshotSchema, type UpdatePostBody, updatePostBodySchema, } from './post.js';
25
+ export { type CommunityPostWire, type CreatePostBody, communityPostWireSchema, createPostBodySchema, type PostAuthorSnapshot, postAuthorSnapshotSchema, type RepostedSource, repostedSourceSchema, type UpdatePostBody, updatePostBodySchema, } from './post.js';
26
+ export { type CreateRepostBody, type CreateRepostResponse, createRepostBodySchema, createRepostResponseSchema, } from './repost.js';
26
27
  export { type PeopleListItem, type PeopleListResponse, type PeopleQueryParams, type PublicProfileWire, peopleListItemSchema, peopleListResponseSchema, peopleQueryParamsSchema, publicProfileWireSchema, } from './profile.js';
27
28
  export { type ReportContentBody, type ReportContentKind, type ReportRecordWire, type ReportSubmissionResponse, type ReportUserBody, reportContentBodySchema, reportContentKindSchema, reportRecordWireSchema, reportSubmissionResponseSchema, reportUserBodySchema, } from './report.js';
28
- export { type BlockedUserWire, type BlockedUsersListResponse, blockedUserWireSchema, blockedUsersListResponseSchema, COMMUNITY_SETTINGS_DEFAULTS, type CommunitySettingsWire, communitySettingsWireSchema, type UpdateCommunitySettingsBody, updateCommunitySettingsBodySchema, } from './settings.js';
29
+ export { type BlockedUsersListResponse, type BlockedUserWire, blockedUsersListResponseSchema, blockedUserWireSchema, COMMUNITY_SETTINGS_DEFAULTS, type CommunitySettingsWire, communitySettingsWireSchema, type UpdateCommunitySettingsBody, updateCommunitySettingsBodySchema, } from './settings.js';
29
30
  export { type ConnectionDecisionBody, type ConnectionRecordWire, connectionDecisionBodySchema, connectionRecordWireSchema, type SendConnectionRequestResponse, sendConnectionRequestResponseSchema, type ToggleFollowResponse, toggleFollowResponseSchema, type ViewerSocialState, viewerSocialStateSchema, } from './social.js';
30
31
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EACL,KAAK,mBAAmB,EACxB,yBAAyB,GAC1B,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,0BAA0B,EAC1B,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,qBAAqB,EACrB,yBAAyB,EACzB,KAAK,oBAAoB,EACzB,0BAA0B,GAC3B,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,sBAAsB,EACtB,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,QAAQ,EACb,cAAc,EACd,sBAAsB,EACtB,KAAK,cAAc,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,KAAK,eAAe,EACpB,qBAAqB,GACtB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,cAAc,EACd,qBAAqB,EACrB,kBAAkB,EAClB,KAAK,iBAAiB,EACtB,uBAAuB,EACvB,KAAK,gBAAgB,EACrB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,KAAK,mBAAmB,EACxB,yBAAyB,EACzB,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,wBAAwB,EACxB,+BAA+B,EAC/B,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,wBAAwB,EAC7B,qBAAqB,EACrB,8BAA8B,EAC9B,KAAK,SAAS,EACd,eAAe,EACf,KAAK,kBAAkB,EACvB,wBAAwB,EACxB,KAAK,eAAe,EACpB,qBAAqB,GACtB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,KAAK,SAAS,EACd,KAAK,SAAS,EACd,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,uBAAuB,EACvB,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,wBAAwB,EACxB,KAAK,cAAc,EACnB,oBAAoB,GACrB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,oBAAoB,EACpB,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,cAAc,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,8BAA8B,EAC9B,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,wBAAwB,EAC7B,qBAAqB,EACrB,8BAA8B,EAC9B,2BAA2B,EAC3B,KAAK,qBAAqB,EAC1B,2BAA2B,EAC3B,KAAK,2BAA2B,EAChC,iCAAiC,GAClC,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,4BAA4B,EAC5B,0BAA0B,EAC1B,KAAK,6BAA6B,EAClC,mCAAmC,EACnC,KAAK,oBAAoB,EACzB,0BAA0B,EAC1B,KAAK,iBAAiB,EACtB,uBAAuB,GACxB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -25,19 +25,21 @@ export { setReactionBodySchema, setReactionResponseSchema, toggleUpvoteResponseS
25
25
  // ─── Enums ─────────────────────────────────────────────────────────────────
26
26
  export { CONNECTION_STATUS_VALUES, CONTENT_KIND_VALUES, connectionStatusSchema, contentKindSchema, FEED_SORT_VALUES, feedSortSchema, PROFILE_PRIVACY_VALUES, profilePrivacySchema, REACTION_TYPE_VALUES, REPORT_REASON_VALUES, REPORT_STATUS_VALUES, reactionTypeSchema, reportReasonSchema, reportStatusSchema, VISIBILITY_LEVEL_RANK, VISIBILITY_LEVEL_VALUES, visibilityLevelSchema, } from './enums.js';
27
27
  // ─── Feed (polymorphic union) ──────────────────────────────────────────────
28
- export { feedItemSchema, feedQueryParamsSchema, feedResponseSchema, lostFoundFeedItemSchema, voiceBoxFeedItemSchema, } from './feed.js';
28
+ export { donateItemFeedItemSchema, feedItemSchema, feedQueryParamsSchema, feedResponseSchema, lostFoundFeedItemSchema, voiceBoxFeedItemSchema, } from './feed.js';
29
29
  // ─── Groups ────────────────────────────────────────────────────────────────
30
- export { addGroupMembersBodySchema, communityGroupWireSchema, communityGroupWithMembersSchema, createGroupBodySchema, groupMemberWireSchema, groupMembersListResponseSchema, groupRoleSchema, groupsListResponseSchema, updateGroupBodySchema, } from './group.js';
30
+ export { addGroupMembersBodySchema, communityGroupWireSchema, communityGroupWithMembersSchema, createGroupBodySchema, groupMembersListResponseSchema, groupMemberWireSchema, groupRoleSchema, groupsListResponseSchema, updateGroupBodySchema, } from './group.js';
31
31
  // ─── Media ─────────────────────────────────────────────────────────────────
32
32
  export { postImageSchema, postVideoSchema, } from './media.js';
33
33
  // ─── Post ──────────────────────────────────────────────────────────────────
34
- export { communityPostWireSchema, createPostBodySchema, postAuthorSnapshotSchema, updatePostBodySchema, } from './post.js';
34
+ export { communityPostWireSchema, createPostBodySchema, postAuthorSnapshotSchema, repostedSourceSchema, updatePostBodySchema, } from './post.js';
35
+ // ─── Repost (quote / amplify) ──────────────────────────────────────────────
36
+ export { createRepostBodySchema, createRepostResponseSchema, } from './repost.js';
35
37
  // ─── Profile + People list ─────────────────────────────────────────────────
36
38
  export { peopleListItemSchema, peopleListResponseSchema, peopleQueryParamsSchema, publicProfileWireSchema, } from './profile.js';
37
39
  // ─── Reports (moderation) ──────────────────────────────────────────────────
38
40
  export { reportContentBodySchema, reportContentKindSchema, reportRecordWireSchema, reportSubmissionResponseSchema, reportUserBodySchema, } from './report.js';
39
41
  // ─── Settings ──────────────────────────────────────────────────────────────
40
- export { blockedUserWireSchema, blockedUsersListResponseSchema, COMMUNITY_SETTINGS_DEFAULTS, communitySettingsWireSchema, updateCommunitySettingsBodySchema, } from './settings.js';
42
+ export { blockedUsersListResponseSchema, blockedUserWireSchema, COMMUNITY_SETTINGS_DEFAULTS, communitySettingsWireSchema, updateCommunitySettingsBodySchema, } from './settings.js';
41
43
  // ─── Social (follow + connection) ──────────────────────────────────────────
42
44
  export { connectionDecisionBodySchema, connectionRecordWireSchema, sendConnectionRequestResponseSchema, toggleFollowResponseSchema, viewerSocialStateSchema, } from './social.js';
43
45
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,8EAA8E;AAC9E,OAAO,EAEL,yBAAyB,GAC1B,MAAM,WAAW,CAAC;AAEnB,8EAA8E;AAC9E,OAAO,EAGL,0BAA0B,EAC1B,uBAAuB,EAEvB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AAEtB,8EAA8E;AAC9E,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AAExB,8EAA8E;AAC9E,OAAO,EAGL,qBAAqB,EACrB,yBAAyB,EAEzB,0BAA0B,GAC3B,MAAM,iBAAiB,CAAC;AAEzB,8EAA8E;AAC9E,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EAGnB,sBAAsB,EACtB,iBAAiB,EACjB,gBAAgB,EAEhB,cAAc,EACd,sBAAsB,EAEtB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EAIpB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EAEvB,qBAAqB,GACtB,MAAM,YAAY,CAAC;AAEpB,8EAA8E;AAC9E,OAAO,EAIL,cAAc,EACd,qBAAqB,EACrB,kBAAkB,EAElB,uBAAuB,EAEvB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AAEnB,8EAA8E;AAC9E,OAAO,EAEL,yBAAyB,EAGzB,wBAAwB,EACxB,+BAA+B,EAE/B,qBAAqB,EAGrB,qBAAqB,EACrB,8BAA8B,EAE9B,eAAe,EAEf,wBAAwB,EAExB,qBAAqB,GACtB,MAAM,YAAY,CAAC;AAEpB,8EAA8E;AAC9E,OAAO,EAGL,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,8EAA8E;AAC9E,OAAO,EAGL,uBAAuB,EACvB,oBAAoB,EAEpB,wBAAwB,EAExB,oBAAoB,GACrB,MAAM,WAAW,CAAC;AAEnB,8EAA8E;AAC9E,OAAO,EAKL,oBAAoB,EACpB,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AAEtB,8EAA8E;AAC9E,OAAO,EAML,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,8BAA8B,EAC9B,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAErB,8EAA8E;AAC9E,OAAO,EAGL,qBAAqB,EACrB,8BAA8B,EAC9B,2BAA2B,EAE3B,2BAA2B,EAE3B,iCAAiC,GAClC,MAAM,eAAe,CAAC;AAEvB,8EAA8E;AAC9E,OAAO,EAGL,4BAA4B,EAC5B,0BAA0B,EAE1B,mCAAmC,EAEnC,0BAA0B,EAE1B,uBAAuB,GACxB,MAAM,aAAa,CAAC","sourcesContent":["/**\n * community-schema — Shared Zod schemas + TypeScript types for the\n * Jansathi hyperlocal community feature (feed, posts, engagement,\n * social graph, groups, settings).\n *\n * Single source of truth consumed by reform-backend and every product\n * frontend. The schemas mirror MongoDB / Mongoose document shapes\n * 1:1 so the same Zod validation runs at the controller boundary on\n * the backend and in the form handlers on the client.\n *\n * Cascade-upward visibility semantics: a viewer whose feed level is L\n * sees posts at L and every BROADER level matching their area lineage.\n * See `./enums.ts#VISIBILITY_LEVEL_VALUES` for the level rank table.\n *\n * @module community-schema\n */\n\n// ─── Area lineage snapshot ─────────────────────────────────────────────────\nexport {\n type AreaLineageSnapshot,\n areaLineageSnapshotSchema,\n} from './area.js';\n\n// ─── Comment ───────────────────────────────────────────────────────────────\nexport {\n type CommunityCommentWire,\n type CreateCommentBody,\n communityCommentWireSchema,\n createCommentBodySchema,\n type UpdateCommentBody,\n updateCommentBodySchema,\n} from './comment.js';\n\n// ─── Constants ─────────────────────────────────────────────────────────────\nexport {\n COMMENT_MAX_BODY_CHARS,\n COMMENTS_PAGE_SIZE,\n EDIT_GRACE_WINDOW_MS,\n FEED_PAGE_SIZE,\n GROUP_MAX_MEMBERS,\n GROUP_NAME_MAX_CHARS,\n GROUP_NAME_MIN_CHARS,\n PEOPLE_PAGE_SIZE,\n POST_MAX_BODY_CHARS,\n POST_MAX_IMAGE_BYTES,\n POST_MAX_IMAGES,\n POST_MAX_VIDEO_BYTES,\n POST_MAX_VIDEO_SECONDS,\n POST_MAX_VIDEOS,\n PROFILE_MAX_BIO_CHARS,\n REPLIES_PRELOAD_COUNT,\n} from './constants.js';\n\n// ─── Engagement (upvote + reactions) ───────────────────────────────────────\nexport {\n type SetReactionBody,\n type SetReactionResponse,\n setReactionBodySchema,\n setReactionResponseSchema,\n type ToggleUpvoteResponse,\n toggleUpvoteResponseSchema,\n} from './engagement.js';\n\n// ─── Enums ─────────────────────────────────────────────────────────────────\nexport {\n CONNECTION_STATUS_VALUES,\n CONTENT_KIND_VALUES,\n type ConnectionStatus,\n type ContentKind,\n connectionStatusSchema,\n contentKindSchema,\n FEED_SORT_VALUES,\n type FeedSort,\n feedSortSchema,\n PROFILE_PRIVACY_VALUES,\n type ProfilePrivacy,\n profilePrivacySchema,\n REACTION_TYPE_VALUES,\n REPORT_REASON_VALUES,\n REPORT_STATUS_VALUES,\n type ReactionType,\n type ReportReason,\n type ReportStatus,\n reactionTypeSchema,\n reportReasonSchema,\n reportStatusSchema,\n VISIBILITY_LEVEL_RANK,\n VISIBILITY_LEVEL_VALUES,\n type VisibilityLevel,\n visibilityLevelSchema,\n} from './enums.js';\n\n// ─── Feed (polymorphic union) ──────────────────────────────────────────────\nexport {\n type FeedItem,\n type FeedQueryParams,\n type FeedResponse,\n feedItemSchema,\n feedQueryParamsSchema,\n feedResponseSchema,\n type LostFoundFeedItem,\n lostFoundFeedItemSchema,\n type VoiceBoxFeedItem,\n voiceBoxFeedItemSchema,\n} from './feed.js';\n\n// ─── Groups ────────────────────────────────────────────────────────────────\nexport {\n type AddGroupMembersBody,\n addGroupMembersBodySchema,\n type CommunityGroupWire,\n type CommunityGroupWithMembers,\n communityGroupWireSchema,\n communityGroupWithMembersSchema,\n type CreateGroupBody,\n createGroupBodySchema,\n type GroupMemberWire,\n type GroupMembersListResponse,\n groupMemberWireSchema,\n groupMembersListResponseSchema,\n type GroupRole,\n groupRoleSchema,\n type GroupsListResponse,\n groupsListResponseSchema,\n type UpdateGroupBody,\n updateGroupBodySchema,\n} from './group.js';\n\n// ─── Media ─────────────────────────────────────────────────────────────────\nexport {\n type PostImage,\n type PostVideo,\n postImageSchema,\n postVideoSchema,\n} from './media.js';\n\n// ─── Post ──────────────────────────────────────────────────────────────────\nexport {\n type CommunityPostWire,\n type CreatePostBody,\n communityPostWireSchema,\n createPostBodySchema,\n type PostAuthorSnapshot,\n postAuthorSnapshotSchema,\n type UpdatePostBody,\n updatePostBodySchema,\n} from './post.js';\n\n// ─── Profile + People list ─────────────────────────────────────────────────\nexport {\n type PeopleListItem,\n type PeopleListResponse,\n type PeopleQueryParams,\n type PublicProfileWire,\n peopleListItemSchema,\n peopleListResponseSchema,\n peopleQueryParamsSchema,\n publicProfileWireSchema,\n} from './profile.js';\n\n// ─── Reports (moderation) ──────────────────────────────────────────────────\nexport {\n type ReportContentBody,\n type ReportContentKind,\n type ReportRecordWire,\n type ReportSubmissionResponse,\n type ReportUserBody,\n reportContentBodySchema,\n reportContentKindSchema,\n reportRecordWireSchema,\n reportSubmissionResponseSchema,\n reportUserBodySchema,\n} from './report.js';\n\n// ─── Settings ──────────────────────────────────────────────────────────────\nexport {\n type BlockedUserWire,\n type BlockedUsersListResponse,\n blockedUserWireSchema,\n blockedUsersListResponseSchema,\n COMMUNITY_SETTINGS_DEFAULTS,\n type CommunitySettingsWire,\n communitySettingsWireSchema,\n type UpdateCommunitySettingsBody,\n updateCommunitySettingsBodySchema,\n} from './settings.js';\n\n// ─── Social (follow + connection) ──────────────────────────────────────────\nexport {\n type ConnectionDecisionBody,\n type ConnectionRecordWire,\n connectionDecisionBodySchema,\n connectionRecordWireSchema,\n type SendConnectionRequestResponse,\n sendConnectionRequestResponseSchema,\n type ToggleFollowResponse,\n toggleFollowResponseSchema,\n type ViewerSocialState,\n viewerSocialStateSchema,\n} from './social.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,8EAA8E;AAC9E,OAAO,EAEL,yBAAyB,GAC1B,MAAM,WAAW,CAAC;AAEnB,8EAA8E;AAC9E,OAAO,EAGL,0BAA0B,EAC1B,uBAAuB,EAEvB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AAEtB,8EAA8E;AAC9E,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AAExB,8EAA8E;AAC9E,OAAO,EAGL,qBAAqB,EACrB,yBAAyB,EAEzB,0BAA0B,GAC3B,MAAM,iBAAiB,CAAC;AAEzB,8EAA8E;AAC9E,OAAO,EACL,wBAAwB,EACxB,mBAAmB,EAGnB,sBAAsB,EACtB,iBAAiB,EACjB,gBAAgB,EAEhB,cAAc,EACd,sBAAsB,EAEtB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EAIpB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EAEvB,qBAAqB,GACtB,MAAM,YAAY,CAAC;AAEpB,8EAA8E;AAC9E,OAAO,EAEL,wBAAwB,EAIxB,cAAc,EACd,qBAAqB,EACrB,kBAAkB,EAElB,uBAAuB,EAEvB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AAEnB,8EAA8E;AAC9E,OAAO,EAEL,yBAAyB,EAIzB,wBAAwB,EACxB,+BAA+B,EAC/B,qBAAqB,EAKrB,8BAA8B,EAC9B,qBAAqB,EACrB,eAAe,EACf,wBAAwB,EAExB,qBAAqB,GACtB,MAAM,YAAY,CAAC;AAEpB,8EAA8E;AAC9E,OAAO,EAGL,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,8EAA8E;AAC9E,OAAO,EAGL,uBAAuB,EACvB,oBAAoB,EAEpB,wBAAwB,EAExB,oBAAoB,EAEpB,oBAAoB,GACrB,MAAM,WAAW,CAAC;AAEnB,8EAA8E;AAC9E,OAAO,EAGL,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,aAAa,CAAC;AAErB,8EAA8E;AAC9E,OAAO,EAKL,oBAAoB,EACpB,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AAEtB,8EAA8E;AAC9E,OAAO,EAML,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,8BAA8B,EAC9B,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAErB,8EAA8E;AAC9E,OAAO,EAGL,8BAA8B,EAC9B,qBAAqB,EACrB,2BAA2B,EAE3B,2BAA2B,EAE3B,iCAAiC,GAClC,MAAM,eAAe,CAAC;AAEvB,8EAA8E;AAC9E,OAAO,EAGL,4BAA4B,EAC5B,0BAA0B,EAE1B,mCAAmC,EAEnC,0BAA0B,EAE1B,uBAAuB,GACxB,MAAM,aAAa,CAAC","sourcesContent":["/**\n * community-schema — Shared Zod schemas + TypeScript types for the\n * Jansathi hyperlocal community feature (feed, posts, engagement,\n * social graph, groups, settings).\n *\n * Single source of truth consumed by reform-backend and every product\n * frontend. The schemas mirror MongoDB / Mongoose document shapes\n * 1:1 so the same Zod validation runs at the controller boundary on\n * the backend and in the form handlers on the client.\n *\n * Cascade-upward visibility semantics: a viewer whose feed level is L\n * sees posts at L and every BROADER level matching their area lineage.\n * See `./enums.ts#VISIBILITY_LEVEL_VALUES` for the level rank table.\n *\n * @module community-schema\n */\n\n// ─── Area lineage snapshot ─────────────────────────────────────────────────\nexport {\n type AreaLineageSnapshot,\n areaLineageSnapshotSchema,\n} from './area.js';\n\n// ─── Comment ───────────────────────────────────────────────────────────────\nexport {\n type CommunityCommentWire,\n type CreateCommentBody,\n communityCommentWireSchema,\n createCommentBodySchema,\n type UpdateCommentBody,\n updateCommentBodySchema,\n} from './comment.js';\n\n// ─── Constants ─────────────────────────────────────────────────────────────\nexport {\n COMMENT_MAX_BODY_CHARS,\n COMMENTS_PAGE_SIZE,\n EDIT_GRACE_WINDOW_MS,\n FEED_PAGE_SIZE,\n GROUP_MAX_MEMBERS,\n GROUP_NAME_MAX_CHARS,\n GROUP_NAME_MIN_CHARS,\n PEOPLE_PAGE_SIZE,\n POST_MAX_BODY_CHARS,\n POST_MAX_IMAGE_BYTES,\n POST_MAX_IMAGES,\n POST_MAX_VIDEO_BYTES,\n POST_MAX_VIDEO_SECONDS,\n POST_MAX_VIDEOS,\n PROFILE_MAX_BIO_CHARS,\n REPLIES_PRELOAD_COUNT,\n} from './constants.js';\n\n// ─── Engagement (upvote + reactions) ───────────────────────────────────────\nexport {\n type SetReactionBody,\n type SetReactionResponse,\n setReactionBodySchema,\n setReactionResponseSchema,\n type ToggleUpvoteResponse,\n toggleUpvoteResponseSchema,\n} from './engagement.js';\n\n// ─── Enums ─────────────────────────────────────────────────────────────────\nexport {\n CONNECTION_STATUS_VALUES,\n CONTENT_KIND_VALUES,\n type ConnectionStatus,\n type ContentKind,\n connectionStatusSchema,\n contentKindSchema,\n FEED_SORT_VALUES,\n type FeedSort,\n feedSortSchema,\n PROFILE_PRIVACY_VALUES,\n type ProfilePrivacy,\n profilePrivacySchema,\n REACTION_TYPE_VALUES,\n REPORT_REASON_VALUES,\n REPORT_STATUS_VALUES,\n type ReactionType,\n type ReportReason,\n type ReportStatus,\n reactionTypeSchema,\n reportReasonSchema,\n reportStatusSchema,\n VISIBILITY_LEVEL_RANK,\n VISIBILITY_LEVEL_VALUES,\n type VisibilityLevel,\n visibilityLevelSchema,\n} from './enums.js';\n\n// ─── Feed (polymorphic union) ──────────────────────────────────────────────\nexport {\n type DonateItemFeedItem,\n donateItemFeedItemSchema,\n type FeedItem,\n type FeedQueryParams,\n type FeedResponse,\n feedItemSchema,\n feedQueryParamsSchema,\n feedResponseSchema,\n type LostFoundFeedItem,\n lostFoundFeedItemSchema,\n type VoiceBoxFeedItem,\n voiceBoxFeedItemSchema,\n} from './feed.js';\n\n// ─── Groups ────────────────────────────────────────────────────────────────\nexport {\n type AddGroupMembersBody,\n addGroupMembersBodySchema,\n type CommunityGroupWire,\n type CommunityGroupWithMembers,\n type CreateGroupBody,\n communityGroupWireSchema,\n communityGroupWithMembersSchema,\n createGroupBodySchema,\n type GroupMembersListResponse,\n type GroupMemberWire,\n type GroupRole,\n type GroupsListResponse,\n groupMembersListResponseSchema,\n groupMemberWireSchema,\n groupRoleSchema,\n groupsListResponseSchema,\n type UpdateGroupBody,\n updateGroupBodySchema,\n} from './group.js';\n\n// ─── Media ─────────────────────────────────────────────────────────────────\nexport {\n type PostImage,\n type PostVideo,\n postImageSchema,\n postVideoSchema,\n} from './media.js';\n\n// ─── Post ──────────────────────────────────────────────────────────────────\nexport {\n type CommunityPostWire,\n type CreatePostBody,\n communityPostWireSchema,\n createPostBodySchema,\n type PostAuthorSnapshot,\n postAuthorSnapshotSchema,\n type RepostedSource,\n repostedSourceSchema,\n type UpdatePostBody,\n updatePostBodySchema,\n} from './post.js';\n\n// ─── Repost (quote / amplify) ──────────────────────────────────────────────\nexport {\n type CreateRepostBody,\n type CreateRepostResponse,\n createRepostBodySchema,\n createRepostResponseSchema,\n} from './repost.js';\n\n// ─── Profile + People list ─────────────────────────────────────────────────\nexport {\n type PeopleListItem,\n type PeopleListResponse,\n type PeopleQueryParams,\n type PublicProfileWire,\n peopleListItemSchema,\n peopleListResponseSchema,\n peopleQueryParamsSchema,\n publicProfileWireSchema,\n} from './profile.js';\n\n// ─── Reports (moderation) ──────────────────────────────────────────────────\nexport {\n type ReportContentBody,\n type ReportContentKind,\n type ReportRecordWire,\n type ReportSubmissionResponse,\n type ReportUserBody,\n reportContentBodySchema,\n reportContentKindSchema,\n reportRecordWireSchema,\n reportSubmissionResponseSchema,\n reportUserBodySchema,\n} from './report.js';\n\n// ─── Settings ──────────────────────────────────────────────────────────────\nexport {\n type BlockedUsersListResponse,\n type BlockedUserWire,\n blockedUsersListResponseSchema,\n blockedUserWireSchema,\n COMMUNITY_SETTINGS_DEFAULTS,\n type CommunitySettingsWire,\n communitySettingsWireSchema,\n type UpdateCommunitySettingsBody,\n updateCommunitySettingsBodySchema,\n} from './settings.js';\n\n// ─── Social (follow + connection) ──────────────────────────────────────────\nexport {\n type ConnectionDecisionBody,\n type ConnectionRecordWire,\n connectionDecisionBodySchema,\n connectionRecordWireSchema,\n type SendConnectionRequestResponse,\n sendConnectionRequestResponseSchema,\n type ToggleFollowResponse,\n toggleFollowResponseSchema,\n type ViewerSocialState,\n viewerSocialStateSchema,\n} from './social.js';\n"]}
package/dist/post.d.ts CHANGED
@@ -26,6 +26,42 @@ export declare const postAuthorSnapshotSchema: z.ZodObject<{
26
26
  localName: z.ZodOptional<z.ZodString>;
27
27
  }, z.core.$strip>;
28
28
  export type PostAuthorSnapshot = z.infer<typeof postAuthorSnapshotSchema>;
29
+ /**
30
+ * Compact embed shape used when a CommunityPost is a repost (caption +
31
+ * optional quote of another item). Modelled after Twitter's quote-tweet
32
+ * embed: byline + truncated text/title + first image + kind badge + the
33
+ * moment-of-creation timestamp.
34
+ *
35
+ * Engagement counts are intentionally NOT included — they'd drift
36
+ * relative to the live source. The card embeds this as a static teaser
37
+ * that links to the source for the live numbers.
38
+ *
39
+ * Backend snapshots these fields at repost-create time from the source
40
+ * row (post text / item title / first photo / status badge), so the
41
+ * embed survives source edits / soft-deletes / hides — the user's
42
+ * reaction at repost time is preserved.
43
+ */
44
+ export declare const repostedSourceSchema: z.ZodObject<{
45
+ contentKind: z.ZodEnum<{
46
+ post: "post";
47
+ lost_found: "lost_found";
48
+ voice_box: "voice_box";
49
+ donate_item: "donate_item";
50
+ }>;
51
+ _id: z.ZodString;
52
+ author: z.ZodObject<{
53
+ userId: z.ZodString;
54
+ displayName: z.ZodString;
55
+ avatarUrl: z.ZodOptional<z.ZodString>;
56
+ localName: z.ZodOptional<z.ZodString>;
57
+ }, z.core.$strip>;
58
+ title: z.ZodString;
59
+ body: z.ZodOptional<z.ZodString>;
60
+ imageUrl: z.ZodOptional<z.ZodString>;
61
+ kindBadge: z.ZodOptional<z.ZodString>;
62
+ createdAt: z.ZodString;
63
+ }, z.core.$strip>;
64
+ export type RepostedSource = z.infer<typeof repostedSourceSchema>;
29
65
  /**
30
66
  * A community post as it appears in the feed and on the single-post
31
67
  * page. Engagement counts are denormalised onto the document for
@@ -74,7 +110,28 @@ export declare const communityPostWireSchema: z.ZodObject<{
74
110
  }, z.core.$strip>;
75
111
  upvoteCount: z.ZodNumber;
76
112
  commentCount: z.ZodNumber;
113
+ repostCount: z.ZodNumber;
77
114
  reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
115
+ repostOf: z.ZodOptional<z.ZodObject<{
116
+ contentKind: z.ZodEnum<{
117
+ post: "post";
118
+ lost_found: "lost_found";
119
+ voice_box: "voice_box";
120
+ donate_item: "donate_item";
121
+ }>;
122
+ _id: z.ZodString;
123
+ author: z.ZodObject<{
124
+ userId: z.ZodString;
125
+ displayName: z.ZodString;
126
+ avatarUrl: z.ZodOptional<z.ZodString>;
127
+ localName: z.ZodOptional<z.ZodString>;
128
+ }, z.core.$strip>;
129
+ title: z.ZodString;
130
+ body: z.ZodOptional<z.ZodString>;
131
+ imageUrl: z.ZodOptional<z.ZodString>;
132
+ kindBadge: z.ZodOptional<z.ZodString>;
133
+ createdAt: z.ZodString;
134
+ }, z.core.$strip>>;
78
135
  createdAt: z.ZodString;
79
136
  editedAt: z.ZodNullable<z.ZodString>;
80
137
  deletedAt: z.ZodNullable<z.ZodString>;
@@ -82,6 +139,7 @@ export declare const communityPostWireSchema: z.ZodObject<{
82
139
  upvoted: z.ZodBoolean;
83
140
  reaction: z.ZodNullable<z.ZodString>;
84
141
  isAuthor: z.ZodBoolean;
142
+ reposted: z.ZodBoolean;
85
143
  }, z.core.$strip>>;
86
144
  }, z.core.$strip>;
87
145
  export type CommunityPostWire = z.infer<typeof communityPostWireSchema>;
@@ -1 +1 @@
1
- {"version":3,"file":"post.d.ts","sourceRoot":"","sources":["../src/post.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB;;;;;;;;;GASG;AACH,eAAO,MAAM,wBAAwB;;;;;iBAWnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAI1E;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuClC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAIxE;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;iBAM/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;iBAE/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
1
+ {"version":3,"file":"post.d.ts","sourceRoot":"","sources":["../src/post.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB;;;;;;;;;GASG;AACH,eAAO,MAAM,wBAAwB;;;;;iBAWnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAI1E;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;iBAuB/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAIlE;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmDlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAIxE;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;iBAM/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;iBAE/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
package/dist/post.js CHANGED
@@ -11,7 +11,7 @@
11
11
  import { z } from 'zod';
12
12
  import { areaLineageSnapshotSchema } from './area.js';
13
13
  import { POST_MAX_BODY_CHARS, POST_MAX_IMAGES, POST_MAX_VIDEOS } from './constants.js';
14
- import { visibilityLevelSchema } from './enums.js';
14
+ import { contentKindSchema, visibilityLevelSchema } from './enums.js';
15
15
  import { postImageSchema, postVideoSchema } from './media.js';
16
16
  // ─── Author snapshot (denormalised on every post) ──────────────────────────
17
17
  /**
@@ -36,6 +36,46 @@ export const postAuthorSnapshotSchema = z.object({
36
36
  */
37
37
  localName: z.string().optional(),
38
38
  });
39
+ // ─── Reposted source snapshot ──────────────────────────────────────────────
40
+ /**
41
+ * Compact embed shape used when a CommunityPost is a repost (caption +
42
+ * optional quote of another item). Modelled after Twitter's quote-tweet
43
+ * embed: byline + truncated text/title + first image + kind badge + the
44
+ * moment-of-creation timestamp.
45
+ *
46
+ * Engagement counts are intentionally NOT included — they'd drift
47
+ * relative to the live source. The card embeds this as a static teaser
48
+ * that links to the source for the live numbers.
49
+ *
50
+ * Backend snapshots these fields at repost-create time from the source
51
+ * row (post text / item title / first photo / status badge), so the
52
+ * embed survives source edits / soft-deletes / hides — the user's
53
+ * reaction at repost time is preserved.
54
+ */
55
+ export const repostedSourceSchema = z.object({
56
+ /** Which kind was reposted. Lets the embed render the right icon. */
57
+ contentKind: contentKindSchema,
58
+ /** Source row's id — tap-embed navigates to its thread / detail page. */
59
+ _id: z.string(),
60
+ /** Author snapshot of the source at repost time. */
61
+ author: postAuthorSnapshotSchema,
62
+ /** Primary text. For posts: the body text (truncated). For lost_found /
63
+ * voice_box / donate_item: the title. */
64
+ title: z.string(),
65
+ /** Optional secondary line. For posts: unused. For legacy kinds: the
66
+ * description (truncated server-side). */
67
+ body: z.string().optional(),
68
+ /** First image if any. */
69
+ imageUrl: z.string().url().optional(),
70
+ /** Small kind-specific badge:
71
+ * - posts → undefined
72
+ * - lost_found → 'lost' | 'found'
73
+ * - voice_box → 'complaint' | 'suggestion'
74
+ * - donate_item → 'PENDING' | 'VERIFIED' | 'AT_CENTER' | 'DONATED'
75
+ */
76
+ kindBadge: z.string().optional(),
77
+ createdAt: z.string().datetime(),
78
+ });
39
79
  // ─── Wire shape — what the server returns ──────────────────────────────────
40
80
  /**
41
81
  * A community post as it appears in the feed and on the single-post
@@ -47,7 +87,7 @@ export const communityPostWireSchema = z.object({
47
87
  _id: z.string(),
48
88
  contentKind: z.literal('post'),
49
89
  author: postAuthorSnapshotSchema,
50
- // Body
90
+ // Body — for a repost, this carries the caption (may be empty).
51
91
  text: z.string().max(POST_MAX_BODY_CHARS),
52
92
  images: z.array(postImageSchema).max(POST_MAX_IMAGES),
53
93
  videos: z.array(postVideoSchema).max(POST_MAX_VIDEOS),
@@ -57,8 +97,15 @@ export const communityPostWireSchema = z.object({
57
97
  // Denormalised engagement counters (drive feed sort + card render)
58
98
  upvoteCount: z.number().int().nonnegative(),
59
99
  commentCount: z.number().int().nonnegative(),
100
+ /** How many times this post has been reposted. Incremented by the
101
+ * repost endpoint; reposts of reposts are blocked server-side. */
102
+ repostCount: z.number().int().nonnegative(),
60
103
  /** Map of `ReactionType` → count, e.g. { like: 4, love: 1 }. */
61
104
  reactionCounts: z.record(z.string(), z.number().int().nonnegative()),
105
+ /** Source-snapshot when this post is a repost. Absent on regular posts.
106
+ * The wrapper post owns its own engagement counters; this embed is
107
+ * a static teaser of the original at repost time. */
108
+ repostOf: repostedSourceSchema.optional(),
62
109
  // Lifecycle
63
110
  createdAt: z.string().datetime(),
64
111
  /** Last edit timestamp, or null if never edited. Edit window is
@@ -75,6 +122,10 @@ export const communityPostWireSchema = z.object({
75
122
  reaction: z.string().nullable(),
76
123
  /** True when the viewer authored the post (controls edit / delete buttons). */
77
124
  isAuthor: z.boolean(),
125
+ /** True when the viewer has already reposted this source. The
126
+ * client uses this to render the repost button as "already
127
+ * reposted" rather than as a fresh CTA. */
128
+ reposted: z.boolean(),
78
129
  })
79
130
  .optional(),
80
131
  });
package/dist/post.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"post.js","sourceRoot":"","sources":["../src/post.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Community post wire shapes.\n *\n * A \"post\" is the native community content kind (text-primary + media).\n * Lost-and-found and voice-box items show up in the same feed via\n * polymorphic union; their shapes live in their own schema packages\n * and are projected into a `FeedItem` envelope (see `./feed.ts`).\n *\n * @module community-schema/post\n */\n\nimport { z } from 'zod';\nimport { areaLineageSnapshotSchema } from './area.js';\nimport { POST_MAX_BODY_CHARS, POST_MAX_IMAGES, POST_MAX_VIDEOS } from './constants.js';\nimport { visibilityLevelSchema } from './enums.js';\nimport { postImageSchema, postVideoSchema } from './media.js';\n\n// ─── Author snapshot (denormalised on every post) ──────────────────────────\n\n/**\n * The author's display info captured at post time. Denormalising\n * `displayName` / `avatarUrl` / `localName` here avoids a $lookup\n * against the User collection on every feed read — feed scans 20–40\n * posts per page and population would be a hot path.\n *\n * The User document remains the source of truth; a background job\n * (or a hook on User.findOneAndUpdate) refreshes recent posts when\n * a user changes their display name or avatar.\n */\nexport const postAuthorSnapshotSchema = z.object({\n userId: z.string(),\n displayName: z.string(),\n avatarUrl: z.string().url().optional(),\n /**\n * The author's \"local\" area label at post time, e.g. \"Rohini Sector 15\".\n * Powers the byline \"<DisplayName> · <localName> · <relativeTime>\".\n * Hidden when the author has a private profile and the viewer is\n * not an accepted Connection.\n */\n localName: z.string().optional(),\n});\nexport type PostAuthorSnapshot = z.infer<typeof postAuthorSnapshotSchema>;\n\n// ─── Wire shape — what the server returns ──────────────────────────────────\n\n/**\n * A community post as it appears in the feed and on the single-post\n * page. Engagement counts are denormalised onto the document for\n * fast feed sorting; the per-user reaction state (\"did I upvote this?\")\n * is computed at read time and returned in the `viewer` block.\n */\nexport const communityPostWireSchema = z.object({\n _id: z.string(),\n contentKind: z.literal('post'),\n author: postAuthorSnapshotSchema,\n\n // Body\n text: z.string().max(POST_MAX_BODY_CHARS),\n images: z.array(postImageSchema).max(POST_MAX_IMAGES),\n videos: z.array(postVideoSchema).max(POST_MAX_VIDEOS),\n\n // Audience\n visibilityLevel: visibilityLevelSchema,\n areaLineage: areaLineageSnapshotSchema,\n\n // Denormalised engagement counters (drive feed sort + card render)\n upvoteCount: z.number().int().nonnegative(),\n commentCount: z.number().int().nonnegative(),\n /** Map of `ReactionType` → count, e.g. { like: 4, love: 1 }. */\n reactionCounts: z.record(z.string(), z.number().int().nonnegative()),\n\n // Lifecycle\n createdAt: z.string().datetime(),\n /** Last edit timestamp, or null if never edited. Edit window is\n * enforced by the server at write time, not by this schema. */\n editedAt: z.string().datetime().nullable(),\n deletedAt: z.string().datetime().nullable(),\n\n // Per-viewer state (filled by the server at read time)\n viewer: z\n .object({\n upvoted: z.boolean(),\n /** The reaction the viewer chose, or null. Mutually exclusive\n * with `upvoted` is intentionally NOT enforced — they're two\n * separate axes per the product decision. */\n reaction: z.string().nullable(),\n /** True when the viewer authored the post (controls edit / delete buttons). */\n isAuthor: z.boolean(),\n })\n .optional(),\n});\nexport type CommunityPostWire = z.infer<typeof communityPostWireSchema>;\n\n// ─── Create / update bodies ────────────────────────────────────────────────\n\n/**\n * What the client sends to `POST /api/v1/community/posts`. The author\n * is taken from the authenticated request; visibility area is resolved\n * server-side from the user's primary area lineage (the client cannot\n * spoof a different area).\n */\nexport const createPostBodySchema = z.object({\n text: z.string().min(1).max(POST_MAX_BODY_CHARS),\n images: z.array(postImageSchema).max(POST_MAX_IMAGES).optional(),\n videos: z.array(postVideoSchema).max(POST_MAX_VIDEOS).optional(),\n /** Default chosen by the client is `local`; the server still validates. */\n visibilityLevel: visibilityLevelSchema,\n});\nexport type CreatePostBody = z.infer<typeof createPostBodySchema>;\n\n/**\n * What the client sends to `PATCH /api/v1/community/posts/:id`. Only\n * the text body is editable — media additions / removals require a new\n * post (matches Twitter / FB convention).\n */\nexport const updatePostBodySchema = z.object({\n text: z.string().min(1).max(POST_MAX_BODY_CHARS),\n});\nexport type UpdatePostBody = z.infer<typeof updatePostBodySchema>;\n"]}
1
+ {"version":3,"file":"post.js","sourceRoot":"","sources":["../src/post.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACvF,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE9D,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACtC;;;;;OAKG;IACH,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAC;AAGH,8EAA8E;AAE9E;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,qEAAqE;IACrE,WAAW,EAAE,iBAAiB;IAC9B,yEAAyE;IACzE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,oDAAoD;IACpD,MAAM,EAAE,wBAAwB;IAChC;8CAC0C;IAC1C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB;+CAC2C;IAC3C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,0BAA0B;IAC1B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACrC;;;;;OAKG;IACH,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAC;AAGH,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;IAC9B,MAAM,EAAE,wBAAwB;IAEhC,gEAAgE;IAChE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,mBAAmB,CAAC;IACzC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC;IACrD,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC;IAErD,WAAW;IACX,eAAe,EAAE,qBAAqB;IACtC,WAAW,EAAE,yBAAyB;IAEtC,mEAAmE;IACnE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IAC3C,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IAC5C;uEACmE;IACnE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IAC3C,gEAAgE;IAChE,cAAc,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC;IAEpE;;0DAEsD;IACtD,QAAQ,EAAE,oBAAoB,CAAC,QAAQ,EAAE;IAEzC,YAAY;IACZ,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC;oEACgE;IAChE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC1C,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAE3C,uDAAuD;IACvD,MAAM,EAAE,CAAC;SACN,MAAM,CAAC;QACN,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;QACpB;;sDAE8C;QAC9C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC/B,+EAA+E;QAC/E,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE;QACrB;;oDAE4C;QAC5C,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE;KACtB,CAAC;SACD,QAAQ,EAAE;CACd,CAAC,CAAC;AAGH,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,mBAAmB,CAAC;IAChD,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,QAAQ,EAAE;IAChE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,QAAQ,EAAE;IAChE,2EAA2E;IAC3E,eAAe,EAAE,qBAAqB;CACvC,CAAC,CAAC;AAGH;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,mBAAmB,CAAC;CACjD,CAAC,CAAC","sourcesContent":["/**\n * Community post wire shapes.\n *\n * A \"post\" is the native community content kind (text-primary + media).\n * Lost-and-found and voice-box items show up in the same feed via\n * polymorphic union; their shapes live in their own schema packages\n * and are projected into a `FeedItem` envelope (see `./feed.ts`).\n *\n * @module community-schema/post\n */\n\nimport { z } from 'zod';\nimport { areaLineageSnapshotSchema } from './area.js';\nimport { POST_MAX_BODY_CHARS, POST_MAX_IMAGES, POST_MAX_VIDEOS } from './constants.js';\nimport { contentKindSchema, visibilityLevelSchema } from './enums.js';\nimport { postImageSchema, postVideoSchema } from './media.js';\n\n// ─── Author snapshot (denormalised on every post) ──────────────────────────\n\n/**\n * The author's display info captured at post time. Denormalising\n * `displayName` / `avatarUrl` / `localName` here avoids a $lookup\n * against the User collection on every feed read — feed scans 20–40\n * posts per page and population would be a hot path.\n *\n * The User document remains the source of truth; a background job\n * (or a hook on User.findOneAndUpdate) refreshes recent posts when\n * a user changes their display name or avatar.\n */\nexport const postAuthorSnapshotSchema = z.object({\n userId: z.string(),\n displayName: z.string(),\n avatarUrl: z.string().url().optional(),\n /**\n * The author's \"local\" area label at post time, e.g. \"Rohini Sector 15\".\n * Powers the byline \"<DisplayName> · <localName> · <relativeTime>\".\n * Hidden when the author has a private profile and the viewer is\n * not an accepted Connection.\n */\n localName: z.string().optional(),\n});\nexport type PostAuthorSnapshot = z.infer<typeof postAuthorSnapshotSchema>;\n\n// ─── Reposted source snapshot ──────────────────────────────────────────────\n\n/**\n * Compact embed shape used when a CommunityPost is a repost (caption +\n * optional quote of another item). Modelled after Twitter's quote-tweet\n * embed: byline + truncated text/title + first image + kind badge + the\n * moment-of-creation timestamp.\n *\n * Engagement counts are intentionally NOT included — they'd drift\n * relative to the live source. The card embeds this as a static teaser\n * that links to the source for the live numbers.\n *\n * Backend snapshots these fields at repost-create time from the source\n * row (post text / item title / first photo / status badge), so the\n * embed survives source edits / soft-deletes / hides — the user's\n * reaction at repost time is preserved.\n */\nexport const repostedSourceSchema = z.object({\n /** Which kind was reposted. Lets the embed render the right icon. */\n contentKind: contentKindSchema,\n /** Source row's id — tap-embed navigates to its thread / detail page. */\n _id: z.string(),\n /** Author snapshot of the source at repost time. */\n author: postAuthorSnapshotSchema,\n /** Primary text. For posts: the body text (truncated). For lost_found /\n * voice_box / donate_item: the title. */\n title: z.string(),\n /** Optional secondary line. For posts: unused. For legacy kinds: the\n * description (truncated server-side). */\n body: z.string().optional(),\n /** First image if any. */\n imageUrl: z.string().url().optional(),\n /** Small kind-specific badge:\n * - posts → undefined\n * - lost_found → 'lost' | 'found'\n * - voice_box → 'complaint' | 'suggestion'\n * - donate_item → 'PENDING' | 'VERIFIED' | 'AT_CENTER' | 'DONATED'\n */\n kindBadge: z.string().optional(),\n createdAt: z.string().datetime(),\n});\nexport type RepostedSource = z.infer<typeof repostedSourceSchema>;\n\n// ─── Wire shape — what the server returns ──────────────────────────────────\n\n/**\n * A community post as it appears in the feed and on the single-post\n * page. Engagement counts are denormalised onto the document for\n * fast feed sorting; the per-user reaction state (\"did I upvote this?\")\n * is computed at read time and returned in the `viewer` block.\n */\nexport const communityPostWireSchema = z.object({\n _id: z.string(),\n contentKind: z.literal('post'),\n author: postAuthorSnapshotSchema,\n\n // Body — for a repost, this carries the caption (may be empty).\n text: z.string().max(POST_MAX_BODY_CHARS),\n images: z.array(postImageSchema).max(POST_MAX_IMAGES),\n videos: z.array(postVideoSchema).max(POST_MAX_VIDEOS),\n\n // Audience\n visibilityLevel: visibilityLevelSchema,\n areaLineage: areaLineageSnapshotSchema,\n\n // Denormalised engagement counters (drive feed sort + card render)\n upvoteCount: z.number().int().nonnegative(),\n commentCount: z.number().int().nonnegative(),\n /** How many times this post has been reposted. Incremented by the\n * repost endpoint; reposts of reposts are blocked server-side. */\n repostCount: z.number().int().nonnegative(),\n /** Map of `ReactionType` → count, e.g. { like: 4, love: 1 }. */\n reactionCounts: z.record(z.string(), z.number().int().nonnegative()),\n\n /** Source-snapshot when this post is a repost. Absent on regular posts.\n * The wrapper post owns its own engagement counters; this embed is\n * a static teaser of the original at repost time. */\n repostOf: repostedSourceSchema.optional(),\n\n // Lifecycle\n createdAt: z.string().datetime(),\n /** Last edit timestamp, or null if never edited. Edit window is\n * enforced by the server at write time, not by this schema. */\n editedAt: z.string().datetime().nullable(),\n deletedAt: z.string().datetime().nullable(),\n\n // Per-viewer state (filled by the server at read time)\n viewer: z\n .object({\n upvoted: z.boolean(),\n /** The reaction the viewer chose, or null. Mutually exclusive\n * with `upvoted` is intentionally NOT enforced — they're two\n * separate axes per the product decision. */\n reaction: z.string().nullable(),\n /** True when the viewer authored the post (controls edit / delete buttons). */\n isAuthor: z.boolean(),\n /** True when the viewer has already reposted this source. The\n * client uses this to render the repost button as \"already\n * reposted\" rather than as a fresh CTA. */\n reposted: z.boolean(),\n })\n .optional(),\n});\nexport type CommunityPostWire = z.infer<typeof communityPostWireSchema>;\n\n// ─── Create / update bodies ────────────────────────────────────────────────\n\n/**\n * What the client sends to `POST /api/v1/community/posts`. The author\n * is taken from the authenticated request; visibility area is resolved\n * server-side from the user's primary area lineage (the client cannot\n * spoof a different area).\n */\nexport const createPostBodySchema = z.object({\n text: z.string().min(1).max(POST_MAX_BODY_CHARS),\n images: z.array(postImageSchema).max(POST_MAX_IMAGES).optional(),\n videos: z.array(postVideoSchema).max(POST_MAX_VIDEOS).optional(),\n /** Default chosen by the client is `local`; the server still validates. */\n visibilityLevel: visibilityLevelSchema,\n});\nexport type CreatePostBody = z.infer<typeof createPostBodySchema>;\n\n/**\n * What the client sends to `PATCH /api/v1/community/posts/:id`. Only\n * the text body is editable — media additions / removals require a new\n * post (matches Twitter / FB convention).\n */\nexport const updatePostBodySchema = z.object({\n text: z.string().min(1).max(POST_MAX_BODY_CHARS),\n});\nexport type UpdatePostBody = z.infer<typeof updatePostBodySchema>;\n"]}
package/dist/report.d.ts CHANGED
@@ -30,6 +30,7 @@ export declare const reportContentKindSchema: z.ZodEnum<{
30
30
  post: "post";
31
31
  lost_found: "lost_found";
32
32
  voice_box: "voice_box";
33
+ donate_item: "donate_item";
33
34
  comment: "comment";
34
35
  }>;
35
36
  export type ReportContentKind = z.infer<typeof reportContentKindSchema>;
@@ -42,6 +43,7 @@ export declare const reportContentBodySchema: z.ZodObject<{
42
43
  post: "post";
43
44
  lost_found: "lost_found";
44
45
  voice_box: "voice_box";
46
+ donate_item: "donate_item";
45
47
  comment: "comment";
46
48
  }>;
47
49
  contentId: z.ZodString;
@@ -1 +1 @@
1
- {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB;;;;;EAAyD,CAAC;AAC9F,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAIxE;;;GAGG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;iBASlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;;GAGG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;iBAI/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAIlE;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBASjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAGzC,CAAC;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC"}
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB;;;;;;EAMlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAIxE;;;GAGG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;iBASlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;;GAGG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;iBAI/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAIlE;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBASjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAGzC,CAAC;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC"}
package/dist/report.js CHANGED
@@ -27,7 +27,13 @@ import { reportReasonSchema, reportStatusSchema } from './enums.js';
27
27
  * are also reportable. Lives here — not in `enums.ts` — to keep the
28
28
  * feed/post schemas free of the comment value.
29
29
  */
30
- export const reportContentKindSchema = z.enum(['post', 'lost_found', 'voice_box', 'comment']);
30
+ export const reportContentKindSchema = z.enum([
31
+ 'post',
32
+ 'lost_found',
33
+ 'voice_box',
34
+ 'donate_item',
35
+ 'comment',
36
+ ]);
31
37
  // ─── Body shapes ───────────────────────────────────────────────────────────
32
38
  /**
33
39
  * Body for `POST /api/v1/community/reports`. Reports a piece of
@@ -1 +1 @@
1
- {"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC;AAG9F,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C;uDACmD;IACnD,WAAW,EAAE,uBAAuB;IACpC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,MAAM,EAAE,kBAAkB;IAC1B;2DACuD;IACvD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;CACzC,CAAC,CAAC;AAGH;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,MAAM,EAAE,kBAAkB;IAC1B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;CACzC,CAAC,CAAC;AAGH,8EAA8E;AAE9E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IAC1E,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACnC,MAAM,EAAE,kBAAkB;IAC1B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,MAAM,EAAE,kBAAkB;IAC1B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAC;AAGH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC,MAAM,CAAC;IACrD,MAAM,EAAE,sBAAsB;IAC9B,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;CACrB,CAAC,CAAC","sourcesContent":["/**\n * Content + user reporting wire shapes for moderation.\n *\n * Any signed-in viewer can report:\n * - feed content (post / lost-found / voice-box)\n * - comments (one-level thread under a feed item)\n * - users (the profile + everything they author)\n *\n * Reports land in a moderation queue surfaced to admins; admin actions\n * are out of scope for this schema package.\n *\n * Endpoints:\n * POST /reports file a content report\n * POST /reports/user file a user report\n *\n * Both return a `ReportSubmissionResponse` carrying the resulting\n * record + a `created` flag so the UI can distinguish first-submission\n * from idempotent repeat (and toast accordingly).\n *\n * @module community-schema/report\n */\n\nimport { z } from 'zod';\nimport { reportReasonSchema, reportStatusSchema } from './enums.js';\n\n/**\n * The four kinds of community CONTENT that can be reported. Wider than\n * `contentKindSchema` (which covers only feed items) because comments\n * are also reportable. Lives here — not in `enums.ts` — to keep the\n * feed/post schemas free of the comment value.\n */\nexport const reportContentKindSchema = z.enum(['post', 'lost_found', 'voice_box', 'comment']);\nexport type ReportContentKind = z.infer<typeof reportContentKindSchema>;\n\n// ─── Body shapes ───────────────────────────────────────────────────────────\n\n/**\n * Body for `POST /api/v1/community/reports`. Reports a piece of\n * content (post / lost-found / voice-box / comment).\n */\nexport const reportContentBodySchema = z.object({\n /** Still named `contentKind` for backward compat with 0.1.x callers;\n * semantically it now also includes 'comment'. */\n contentKind: reportContentKindSchema,\n contentId: z.string(),\n reason: reportReasonSchema,\n /** Free-text detail. Required when `reason === 'other'`, optional\n * otherwise (the server enforces the conditional). */\n details: z.string().max(2000).optional(),\n});\nexport type ReportContentBody = z.infer<typeof reportContentBodySchema>;\n\n/**\n * Body for `POST /api/v1/community/reports/user`. Reports a user's\n * profile / general behaviour rather than a specific piece of content.\n */\nexport const reportUserBodySchema = z.object({\n targetUserId: z.string(),\n reason: reportReasonSchema,\n details: z.string().max(2000).optional(),\n});\nexport type ReportUserBody = z.infer<typeof reportUserBodySchema>;\n\n// ─── Record + response shapes ──────────────────────────────────────────────\n\n/**\n * Mongo-side record of a single report, projected for wire.\n *\n * For content reports `targetKind` is one of the content kinds and\n * `contentId` holds the target; `targetUserId` is undefined. For user\n * reports `targetKind === 'user'`, `targetUserId` is set, and\n * `contentId` is an empty string (no content target).\n */\nexport const reportRecordWireSchema = z.object({\n _id: z.string(),\n targetKind: z.enum(['post', 'lost_found', 'voice_box', 'comment', 'user']),\n contentId: z.string(),\n targetUserId: z.string().optional(),\n reason: reportReasonSchema,\n details: z.string().nullable(),\n status: reportStatusSchema,\n createdAt: z.string().datetime(),\n});\nexport type ReportRecordWire = z.infer<typeof reportRecordWireSchema>;\n\n/**\n * Common response envelope for both `POST /reports` and\n * `POST /reports/user`. `created` is `true` on first submission and\n * `false` when the backend's idempotency check returned an existing\n * row — the client uses this to distinguish \"thanks for reporting\"\n * from \"you've already reported this\".\n */\nexport const reportSubmissionResponseSchema = z.object({\n record: reportRecordWireSchema,\n created: z.boolean(),\n});\nexport type ReportSubmissionResponse = z.infer<typeof reportSubmissionResponseSchema>;\n"]}
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,IAAI,CAAC;IAC5C,MAAM;IACN,YAAY;IACZ,WAAW;IACX,aAAa;IACb,SAAS;CACV,CAAC,CAAC;AAGH,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C;uDACmD;IACnD,WAAW,EAAE,uBAAuB;IACpC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,MAAM,EAAE,kBAAkB;IAC1B;2DACuD;IACvD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;CACzC,CAAC,CAAC;AAGH;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,MAAM,EAAE,kBAAkB;IAC1B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;CACzC,CAAC,CAAC;AAGH,8EAA8E;AAE9E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IAC1E,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACnC,MAAM,EAAE,kBAAkB;IAC1B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,MAAM,EAAE,kBAAkB;IAC1B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAC;AAGH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,CAAC,MAAM,CAAC;IACrD,MAAM,EAAE,sBAAsB;IAC9B,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;CACrB,CAAC,CAAC","sourcesContent":["/**\n * Content + user reporting wire shapes for moderation.\n *\n * Any signed-in viewer can report:\n * - feed content (post / lost-found / voice-box)\n * - comments (one-level thread under a feed item)\n * - users (the profile + everything they author)\n *\n * Reports land in a moderation queue surfaced to admins; admin actions\n * are out of scope for this schema package.\n *\n * Endpoints:\n * POST /reports file a content report\n * POST /reports/user file a user report\n *\n * Both return a `ReportSubmissionResponse` carrying the resulting\n * record + a `created` flag so the UI can distinguish first-submission\n * from idempotent repeat (and toast accordingly).\n *\n * @module community-schema/report\n */\n\nimport { z } from 'zod';\nimport { reportReasonSchema, reportStatusSchema } from './enums.js';\n\n/**\n * The four kinds of community CONTENT that can be reported. Wider than\n * `contentKindSchema` (which covers only feed items) because comments\n * are also reportable. Lives here — not in `enums.ts` — to keep the\n * feed/post schemas free of the comment value.\n */\nexport const reportContentKindSchema = z.enum([\n 'post',\n 'lost_found',\n 'voice_box',\n 'donate_item',\n 'comment',\n]);\nexport type ReportContentKind = z.infer<typeof reportContentKindSchema>;\n\n// ─── Body shapes ───────────────────────────────────────────────────────────\n\n/**\n * Body for `POST /api/v1/community/reports`. Reports a piece of\n * content (post / lost-found / voice-box / comment).\n */\nexport const reportContentBodySchema = z.object({\n /** Still named `contentKind` for backward compat with 0.1.x callers;\n * semantically it now also includes 'comment'. */\n contentKind: reportContentKindSchema,\n contentId: z.string(),\n reason: reportReasonSchema,\n /** Free-text detail. Required when `reason === 'other'`, optional\n * otherwise (the server enforces the conditional). */\n details: z.string().max(2000).optional(),\n});\nexport type ReportContentBody = z.infer<typeof reportContentBodySchema>;\n\n/**\n * Body for `POST /api/v1/community/reports/user`. Reports a user's\n * profile / general behaviour rather than a specific piece of content.\n */\nexport const reportUserBodySchema = z.object({\n targetUserId: z.string(),\n reason: reportReasonSchema,\n details: z.string().max(2000).optional(),\n});\nexport type ReportUserBody = z.infer<typeof reportUserBodySchema>;\n\n// ─── Record + response shapes ──────────────────────────────────────────────\n\n/**\n * Mongo-side record of a single report, projected for wire.\n *\n * For content reports `targetKind` is one of the content kinds and\n * `contentId` holds the target; `targetUserId` is undefined. For user\n * reports `targetKind === 'user'`, `targetUserId` is set, and\n * `contentId` is an empty string (no content target).\n */\nexport const reportRecordWireSchema = z.object({\n _id: z.string(),\n targetKind: z.enum(['post', 'lost_found', 'voice_box', 'comment', 'user']),\n contentId: z.string(),\n targetUserId: z.string().optional(),\n reason: reportReasonSchema,\n details: z.string().nullable(),\n status: reportStatusSchema,\n createdAt: z.string().datetime(),\n});\nexport type ReportRecordWire = z.infer<typeof reportRecordWireSchema>;\n\n/**\n * Common response envelope for both `POST /reports` and\n * `POST /reports/user`. `created` is `true` on first submission and\n * `false` when the backend's idempotency check returned an existing\n * row — the client uses this to distinguish \"thanks for reporting\"\n * from \"you've already reported this\".\n */\nexport const reportSubmissionResponseSchema = z.object({\n record: reportRecordWireSchema,\n created: z.boolean(),\n});\nexport type ReportSubmissionResponse = z.infer<typeof reportSubmissionResponseSchema>;\n"]}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Repost (quote / amplify) wire shapes.
3
+ *
4
+ * A repost is a CommunityPost with an optional caption that embeds the
5
+ * source via a snapshot. Modelled after Twitter's quote-tweet: the
6
+ * wrapper is its own post with own engagement; tapping the embed
7
+ * navigates to the source for live counts.
8
+ *
9
+ * Constraints enforced server-side:
10
+ * - At most one active repost per (viewer, sourceKind, sourceId).
11
+ * - Reposting a repost is blocked (no chains).
12
+ * - Caption may be empty (amplify-only) but the wrapper is still a
13
+ * full CommunityPost.
14
+ *
15
+ * @module community-schema/repost
16
+ */
17
+ import { z } from 'zod';
18
+ /**
19
+ * Body for `POST /api/v1/community/reposts`. The wrapper post's text is
20
+ * the optional caption; visibility level is chosen by the reposter and
21
+ * may differ from the source's level (e.g. amplify a national post
22
+ * locally to flag it for your neighbours).
23
+ */
24
+ export declare const createRepostBodySchema: z.ZodObject<{
25
+ sourceKind: z.ZodEnum<{
26
+ post: "post";
27
+ lost_found: "lost_found";
28
+ voice_box: "voice_box";
29
+ donate_item: "donate_item";
30
+ }>;
31
+ sourceId: z.ZodString;
32
+ caption: z.ZodOptional<z.ZodString>;
33
+ visibilityLevel: z.ZodEnum<{
34
+ local: "local";
35
+ district: "district";
36
+ state: "state";
37
+ national: "national";
38
+ global: "global";
39
+ }>;
40
+ }, z.core.$strip>;
41
+ export type CreateRepostBody = z.infer<typeof createRepostBodySchema>;
42
+ /**
43
+ * Response from a successful repost. The wrapper post is returned in
44
+ * full (same wire shape as a normal post, but with `repostOf` set) so
45
+ * the client can prepend it to the feed without a follow-up read.
46
+ */
47
+ export declare const createRepostResponseSchema: z.ZodObject<{
48
+ post: z.ZodObject<{
49
+ _id: z.ZodString;
50
+ contentKind: z.ZodLiteral<"post">;
51
+ author: z.ZodObject<{
52
+ userId: z.ZodString;
53
+ displayName: z.ZodString;
54
+ avatarUrl: z.ZodOptional<z.ZodString>;
55
+ localName: z.ZodOptional<z.ZodString>;
56
+ }, z.core.$strip>;
57
+ text: z.ZodString;
58
+ images: z.ZodArray<z.ZodObject<{
59
+ url: z.ZodString;
60
+ thumbnailUrl: z.ZodOptional<z.ZodString>;
61
+ width: z.ZodNumber;
62
+ height: z.ZodNumber;
63
+ bytes: z.ZodNumber;
64
+ alt: z.ZodOptional<z.ZodString>;
65
+ }, z.core.$strip>>;
66
+ videos: z.ZodArray<z.ZodObject<{
67
+ url: z.ZodString;
68
+ thumbnailUrl: z.ZodString;
69
+ width: z.ZodNumber;
70
+ height: z.ZodNumber;
71
+ bytes: z.ZodNumber;
72
+ duration: z.ZodNumber;
73
+ }, z.core.$strip>>;
74
+ visibilityLevel: z.ZodEnum<{
75
+ local: "local";
76
+ district: "district";
77
+ state: "state";
78
+ national: "national";
79
+ global: "global";
80
+ }>;
81
+ areaLineage: z.ZodObject<{
82
+ countryId: z.ZodOptional<z.ZodString>;
83
+ stateId: z.ZodOptional<z.ZodString>;
84
+ districtId: z.ZodOptional<z.ZodString>;
85
+ localId: z.ZodOptional<z.ZodString>;
86
+ localName: z.ZodOptional<z.ZodString>;
87
+ }, z.core.$strip>;
88
+ upvoteCount: z.ZodNumber;
89
+ commentCount: z.ZodNumber;
90
+ repostCount: z.ZodNumber;
91
+ reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
92
+ repostOf: z.ZodOptional<z.ZodObject<{
93
+ contentKind: z.ZodEnum<{
94
+ post: "post";
95
+ lost_found: "lost_found";
96
+ voice_box: "voice_box";
97
+ donate_item: "donate_item";
98
+ }>;
99
+ _id: z.ZodString;
100
+ author: z.ZodObject<{
101
+ userId: z.ZodString;
102
+ displayName: z.ZodString;
103
+ avatarUrl: z.ZodOptional<z.ZodString>;
104
+ localName: z.ZodOptional<z.ZodString>;
105
+ }, z.core.$strip>;
106
+ title: z.ZodString;
107
+ body: z.ZodOptional<z.ZodString>;
108
+ imageUrl: z.ZodOptional<z.ZodString>;
109
+ kindBadge: z.ZodOptional<z.ZodString>;
110
+ createdAt: z.ZodString;
111
+ }, z.core.$strip>>;
112
+ createdAt: z.ZodString;
113
+ editedAt: z.ZodNullable<z.ZodString>;
114
+ deletedAt: z.ZodNullable<z.ZodString>;
115
+ viewer: z.ZodOptional<z.ZodObject<{
116
+ upvoted: z.ZodBoolean;
117
+ reaction: z.ZodNullable<z.ZodString>;
118
+ isAuthor: z.ZodBoolean;
119
+ reposted: z.ZodBoolean;
120
+ }, z.core.$strip>>;
121
+ }, z.core.$strip>;
122
+ }, z.core.$strip>;
123
+ export type CreateRepostResponse = z.infer<typeof createRepostResponseSchema>;
124
+ //# sourceMappingURL=repost.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repost.d.ts","sourceRoot":"","sources":["../src/repost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;iBAWjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;;GAIG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAErC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC"}
package/dist/repost.js ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Repost (quote / amplify) wire shapes.
3
+ *
4
+ * A repost is a CommunityPost with an optional caption that embeds the
5
+ * source via a snapshot. Modelled after Twitter's quote-tweet: the
6
+ * wrapper is its own post with own engagement; tapping the embed
7
+ * navigates to the source for live counts.
8
+ *
9
+ * Constraints enforced server-side:
10
+ * - At most one active repost per (viewer, sourceKind, sourceId).
11
+ * - Reposting a repost is blocked (no chains).
12
+ * - Caption may be empty (amplify-only) but the wrapper is still a
13
+ * full CommunityPost.
14
+ *
15
+ * @module community-schema/repost
16
+ */
17
+ import { z } from 'zod';
18
+ import { POST_MAX_BODY_CHARS } from './constants.js';
19
+ import { contentKindSchema, visibilityLevelSchema } from './enums.js';
20
+ import { communityPostWireSchema } from './post.js';
21
+ /**
22
+ * Body for `POST /api/v1/community/reposts`. The wrapper post's text is
23
+ * the optional caption; visibility level is chosen by the reposter and
24
+ * may differ from the source's level (e.g. amplify a national post
25
+ * locally to flag it for your neighbours).
26
+ */
27
+ export const createRepostBodySchema = z.object({
28
+ /** Which kind is being reposted. */
29
+ sourceKind: contentKindSchema,
30
+ /** Source row's id. Server validates existence + not-already-a-repost
31
+ * + not-already-reposted-by-this-viewer. */
32
+ sourceId: z.string(),
33
+ /** Optional caption. Empty string = amplify only (still creates a
34
+ * wrapper post, just with no caption shown on the card). */
35
+ caption: z.string().max(POST_MAX_BODY_CHARS).optional(),
36
+ /** Visibility for the wrapper post. */
37
+ visibilityLevel: visibilityLevelSchema,
38
+ });
39
+ /**
40
+ * Response from a successful repost. The wrapper post is returned in
41
+ * full (same wire shape as a normal post, but with `repostOf` set) so
42
+ * the client can prepend it to the feed without a follow-up read.
43
+ */
44
+ export const createRepostResponseSchema = z.object({
45
+ post: communityPostWireSchema,
46
+ });
47
+ //# sourceMappingURL=repost.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repost.js","sourceRoot":"","sources":["../src/repost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,oCAAoC;IACpC,UAAU,EAAE,iBAAiB;IAC7B;iDAC6C;IAC7C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;IACpB;iEAC6D;IAC7D,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC,QAAQ,EAAE;IACvD,uCAAuC;IACvC,eAAe,EAAE,qBAAqB;CACvC,CAAC,CAAC;AAGH;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,IAAI,EAAE,uBAAuB;CAC9B,CAAC,CAAC","sourcesContent":["/**\n * Repost (quote / amplify) wire shapes.\n *\n * A repost is a CommunityPost with an optional caption that embeds the\n * source via a snapshot. Modelled after Twitter's quote-tweet: the\n * wrapper is its own post with own engagement; tapping the embed\n * navigates to the source for live counts.\n *\n * Constraints enforced server-side:\n * - At most one active repost per (viewer, sourceKind, sourceId).\n * - Reposting a repost is blocked (no chains).\n * - Caption may be empty (amplify-only) but the wrapper is still a\n * full CommunityPost.\n *\n * @module community-schema/repost\n */\n\nimport { z } from 'zod';\nimport { POST_MAX_BODY_CHARS } from './constants.js';\nimport { contentKindSchema, visibilityLevelSchema } from './enums.js';\nimport { communityPostWireSchema } from './post.js';\n\n/**\n * Body for `POST /api/v1/community/reposts`. The wrapper post's text is\n * the optional caption; visibility level is chosen by the reposter and\n * may differ from the source's level (e.g. amplify a national post\n * locally to flag it for your neighbours).\n */\nexport const createRepostBodySchema = z.object({\n /** Which kind is being reposted. */\n sourceKind: contentKindSchema,\n /** Source row's id. Server validates existence + not-already-a-repost\n * + not-already-reposted-by-this-viewer. */\n sourceId: z.string(),\n /** Optional caption. Empty string = amplify only (still creates a\n * wrapper post, just with no caption shown on the card). */\n caption: z.string().max(POST_MAX_BODY_CHARS).optional(),\n /** Visibility for the wrapper post. */\n visibilityLevel: visibilityLevelSchema,\n});\nexport type CreateRepostBody = z.infer<typeof createRepostBodySchema>;\n\n/**\n * Response from a successful repost. The wrapper post is returned in\n * full (same wire shape as a normal post, but with `repostOf` set) so\n * the client can prepend it to the feed without a follow-up read.\n */\nexport const createRepostResponseSchema = z.object({\n post: communityPostWireSchema,\n});\nexport type CreateRepostResponse = z.infer<typeof createRepostResponseSchema>;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jansathi-community-schema",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Shared Zod schemas + TypeScript types for the Jansathi hyperlocal community feature (feed, posts, engagement, social graph, groups).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",