jansathi-community-schema 0.3.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/feed.d.ts +62 -0
- package/dist/feed.d.ts.map +1 -1
- package/dist/feed.js +6 -0
- package/dist/feed.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/post.d.ts +58 -0
- package/dist/post.d.ts.map +1 -1
- package/dist/post.js +53 -2
- package/dist/post.js.map +1 -1
- package/dist/report.d.ts +2 -0
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +7 -1
- package/dist/report.js.map +1 -1
- package/dist/repost.d.ts +124 -0
- package/dist/repost.d.ts.map +1 -0
- package/dist/repost.js +47 -0
- package/dist/repost.js.map +1 -0
- package/package.json +1 -1
package/dist/feed.d.ts
CHANGED
|
@@ -45,12 +45,14 @@ 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>;
|
|
@@ -76,12 +78,14 @@ export declare const donateItemFeedItemSchema: z.ZodObject<{
|
|
|
76
78
|
status: z.ZodString;
|
|
77
79
|
upvoteCount: z.ZodNumber;
|
|
78
80
|
commentCount: z.ZodNumber;
|
|
81
|
+
repostCount: z.ZodNumber;
|
|
79
82
|
reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
80
83
|
createdAt: z.ZodString;
|
|
81
84
|
viewer: z.ZodOptional<z.ZodObject<{
|
|
82
85
|
upvoted: z.ZodBoolean;
|
|
83
86
|
reaction: z.ZodNullable<z.ZodString>;
|
|
84
87
|
isAuthor: z.ZodBoolean;
|
|
88
|
+
reposted: z.ZodBoolean;
|
|
85
89
|
}, z.core.$strip>>;
|
|
86
90
|
}, z.core.$strip>;
|
|
87
91
|
export type DonateItemFeedItem = z.infer<typeof donateItemFeedItemSchema>;
|
|
@@ -105,12 +109,14 @@ export declare const voiceBoxFeedItemSchema: z.ZodObject<{
|
|
|
105
109
|
description: z.ZodString;
|
|
106
110
|
upvoteCount: z.ZodNumber;
|
|
107
111
|
commentCount: z.ZodNumber;
|
|
112
|
+
repostCount: z.ZodNumber;
|
|
108
113
|
reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
109
114
|
createdAt: z.ZodString;
|
|
110
115
|
viewer: z.ZodOptional<z.ZodObject<{
|
|
111
116
|
upvoted: z.ZodBoolean;
|
|
112
117
|
reaction: z.ZodNullable<z.ZodString>;
|
|
113
118
|
isAuthor: z.ZodBoolean;
|
|
119
|
+
reposted: z.ZodBoolean;
|
|
114
120
|
}, z.core.$strip>>;
|
|
115
121
|
}, z.core.$strip>;
|
|
116
122
|
export type VoiceBoxFeedItem = z.infer<typeof voiceBoxFeedItemSchema>;
|
|
@@ -156,7 +162,28 @@ export declare const feedItemSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
156
162
|
}, z.core.$strip>;
|
|
157
163
|
upvoteCount: z.ZodNumber;
|
|
158
164
|
commentCount: z.ZodNumber;
|
|
165
|
+
repostCount: z.ZodNumber;
|
|
159
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>>;
|
|
160
187
|
createdAt: z.ZodString;
|
|
161
188
|
editedAt: z.ZodNullable<z.ZodString>;
|
|
162
189
|
deletedAt: z.ZodNullable<z.ZodString>;
|
|
@@ -164,6 +191,7 @@ export declare const feedItemSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
164
191
|
upvoted: z.ZodBoolean;
|
|
165
192
|
reaction: z.ZodNullable<z.ZodString>;
|
|
166
193
|
isAuthor: z.ZodBoolean;
|
|
194
|
+
reposted: z.ZodBoolean;
|
|
167
195
|
}, z.core.$strip>>;
|
|
168
196
|
}, z.core.$strip>, z.ZodObject<{
|
|
169
197
|
_id: z.ZodString;
|
|
@@ -183,12 +211,14 @@ export declare const feedItemSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
183
211
|
imageUrl: z.ZodOptional<z.ZodString>;
|
|
184
212
|
upvoteCount: z.ZodNumber;
|
|
185
213
|
commentCount: z.ZodNumber;
|
|
214
|
+
repostCount: z.ZodNumber;
|
|
186
215
|
reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
187
216
|
createdAt: z.ZodString;
|
|
188
217
|
viewer: z.ZodOptional<z.ZodObject<{
|
|
189
218
|
upvoted: z.ZodBoolean;
|
|
190
219
|
reaction: z.ZodNullable<z.ZodString>;
|
|
191
220
|
isAuthor: z.ZodBoolean;
|
|
221
|
+
reposted: z.ZodBoolean;
|
|
192
222
|
}, z.core.$strip>>;
|
|
193
223
|
}, z.core.$strip>, z.ZodObject<{
|
|
194
224
|
_id: z.ZodString;
|
|
@@ -207,12 +237,14 @@ export declare const feedItemSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
207
237
|
description: z.ZodString;
|
|
208
238
|
upvoteCount: z.ZodNumber;
|
|
209
239
|
commentCount: z.ZodNumber;
|
|
240
|
+
repostCount: z.ZodNumber;
|
|
210
241
|
reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
211
242
|
createdAt: z.ZodString;
|
|
212
243
|
viewer: z.ZodOptional<z.ZodObject<{
|
|
213
244
|
upvoted: z.ZodBoolean;
|
|
214
245
|
reaction: z.ZodNullable<z.ZodString>;
|
|
215
246
|
isAuthor: z.ZodBoolean;
|
|
247
|
+
reposted: z.ZodBoolean;
|
|
216
248
|
}, z.core.$strip>>;
|
|
217
249
|
}, z.core.$strip>, z.ZodObject<{
|
|
218
250
|
_id: z.ZodString;
|
|
@@ -231,12 +263,14 @@ export declare const feedItemSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
231
263
|
status: z.ZodString;
|
|
232
264
|
upvoteCount: z.ZodNumber;
|
|
233
265
|
commentCount: z.ZodNumber;
|
|
266
|
+
repostCount: z.ZodNumber;
|
|
234
267
|
reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
235
268
|
createdAt: z.ZodString;
|
|
236
269
|
viewer: z.ZodOptional<z.ZodObject<{
|
|
237
270
|
upvoted: z.ZodBoolean;
|
|
238
271
|
reaction: z.ZodNullable<z.ZodString>;
|
|
239
272
|
isAuthor: z.ZodBoolean;
|
|
273
|
+
reposted: z.ZodBoolean;
|
|
240
274
|
}, z.core.$strip>>;
|
|
241
275
|
}, z.core.$strip>], "contentKind">;
|
|
242
276
|
export type FeedItem = z.infer<typeof feedItemSchema>;
|
|
@@ -300,7 +334,28 @@ export declare const feedResponseSchema: z.ZodObject<{
|
|
|
300
334
|
}, z.core.$strip>;
|
|
301
335
|
upvoteCount: z.ZodNumber;
|
|
302
336
|
commentCount: z.ZodNumber;
|
|
337
|
+
repostCount: z.ZodNumber;
|
|
303
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>>;
|
|
304
359
|
createdAt: z.ZodString;
|
|
305
360
|
editedAt: z.ZodNullable<z.ZodString>;
|
|
306
361
|
deletedAt: z.ZodNullable<z.ZodString>;
|
|
@@ -308,6 +363,7 @@ export declare const feedResponseSchema: z.ZodObject<{
|
|
|
308
363
|
upvoted: z.ZodBoolean;
|
|
309
364
|
reaction: z.ZodNullable<z.ZodString>;
|
|
310
365
|
isAuthor: z.ZodBoolean;
|
|
366
|
+
reposted: z.ZodBoolean;
|
|
311
367
|
}, z.core.$strip>>;
|
|
312
368
|
}, z.core.$strip>, z.ZodObject<{
|
|
313
369
|
_id: z.ZodString;
|
|
@@ -327,12 +383,14 @@ export declare const feedResponseSchema: z.ZodObject<{
|
|
|
327
383
|
imageUrl: z.ZodOptional<z.ZodString>;
|
|
328
384
|
upvoteCount: z.ZodNumber;
|
|
329
385
|
commentCount: z.ZodNumber;
|
|
386
|
+
repostCount: z.ZodNumber;
|
|
330
387
|
reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
331
388
|
createdAt: z.ZodString;
|
|
332
389
|
viewer: z.ZodOptional<z.ZodObject<{
|
|
333
390
|
upvoted: z.ZodBoolean;
|
|
334
391
|
reaction: z.ZodNullable<z.ZodString>;
|
|
335
392
|
isAuthor: z.ZodBoolean;
|
|
393
|
+
reposted: z.ZodBoolean;
|
|
336
394
|
}, z.core.$strip>>;
|
|
337
395
|
}, z.core.$strip>, z.ZodObject<{
|
|
338
396
|
_id: z.ZodString;
|
|
@@ -351,12 +409,14 @@ export declare const feedResponseSchema: z.ZodObject<{
|
|
|
351
409
|
description: z.ZodString;
|
|
352
410
|
upvoteCount: z.ZodNumber;
|
|
353
411
|
commentCount: z.ZodNumber;
|
|
412
|
+
repostCount: z.ZodNumber;
|
|
354
413
|
reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
355
414
|
createdAt: z.ZodString;
|
|
356
415
|
viewer: z.ZodOptional<z.ZodObject<{
|
|
357
416
|
upvoted: z.ZodBoolean;
|
|
358
417
|
reaction: z.ZodNullable<z.ZodString>;
|
|
359
418
|
isAuthor: z.ZodBoolean;
|
|
419
|
+
reposted: z.ZodBoolean;
|
|
360
420
|
}, z.core.$strip>>;
|
|
361
421
|
}, z.core.$strip>, z.ZodObject<{
|
|
362
422
|
_id: z.ZodString;
|
|
@@ -375,12 +435,14 @@ export declare const feedResponseSchema: z.ZodObject<{
|
|
|
375
435
|
status: z.ZodString;
|
|
376
436
|
upvoteCount: z.ZodNumber;
|
|
377
437
|
commentCount: z.ZodNumber;
|
|
438
|
+
repostCount: z.ZodNumber;
|
|
378
439
|
reactionCounts: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
379
440
|
createdAt: z.ZodString;
|
|
380
441
|
viewer: z.ZodOptional<z.ZodObject<{
|
|
381
442
|
upvoted: z.ZodBoolean;
|
|
382
443
|
reaction: z.ZodNullable<z.ZodString>;
|
|
383
444
|
isAuthor: z.ZodBoolean;
|
|
445
|
+
reposted: z.ZodBoolean;
|
|
384
446
|
}, z.core.$strip>>;
|
|
385
447
|
}, z.core.$strip>], "contentKind">>;
|
|
386
448
|
nextCursor: z.ZodNullable<z.ZodString>;
|
package/dist/feed.d.ts.map
CHANGED
|
@@ -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
|
|
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,7 @@ 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(),
|
|
53
55
|
})
|
|
54
56
|
.optional(),
|
|
55
57
|
});
|
|
@@ -76,6 +78,7 @@ export const donateItemFeedItemSchema = z.object({
|
|
|
76
78
|
status: z.string(),
|
|
77
79
|
upvoteCount: z.number().int().nonnegative(),
|
|
78
80
|
commentCount: z.number().int().nonnegative(),
|
|
81
|
+
repostCount: z.number().int().nonnegative(),
|
|
79
82
|
reactionCounts: z.record(z.string(), z.number().int().nonnegative()),
|
|
80
83
|
createdAt: z.string().datetime(),
|
|
81
84
|
viewer: z
|
|
@@ -83,6 +86,7 @@ export const donateItemFeedItemSchema = z.object({
|
|
|
83
86
|
upvoted: z.boolean(),
|
|
84
87
|
reaction: z.string().nullable(),
|
|
85
88
|
isAuthor: z.boolean(),
|
|
89
|
+
reposted: z.boolean(),
|
|
86
90
|
})
|
|
87
91
|
.optional(),
|
|
88
92
|
});
|
|
@@ -99,6 +103,7 @@ export const voiceBoxFeedItemSchema = z.object({
|
|
|
99
103
|
description: z.string(),
|
|
100
104
|
upvoteCount: z.number().int().nonnegative(),
|
|
101
105
|
commentCount: z.number().int().nonnegative(),
|
|
106
|
+
repostCount: z.number().int().nonnegative(),
|
|
102
107
|
reactionCounts: z.record(z.string(), z.number().int().nonnegative()),
|
|
103
108
|
createdAt: z.string().datetime(),
|
|
104
109
|
viewer: z
|
|
@@ -106,6 +111,7 @@ export const voiceBoxFeedItemSchema = z.object({
|
|
|
106
111
|
upvoted: z.boolean(),
|
|
107
112
|
reaction: z.string().nullable(),
|
|
108
113
|
isAuthor: z.boolean(),
|
|
114
|
+
reposted: z.boolean(),
|
|
109
115
|
})
|
|
110
116
|
.optional(),
|
|
111
117
|
});
|
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 * 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 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 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 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 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"]}
|
|
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
|
@@ -22,7 +22,8 @@ export { CONNECTION_STATUS_VALUES, CONTENT_KIND_VALUES, type ConnectionStatus, t
|
|
|
22
22
|
export { type DonateItemFeedItem, donateItemFeedItemSchema, type FeedItem, type FeedQueryParams, type FeedResponse, feedItemSchema, feedQueryParamsSchema, feedResponseSchema, type LostFoundFeedItem, lostFoundFeedItemSchema, type VoiceBoxFeedItem, voiceBoxFeedItemSchema, } from './feed.js';
|
|
23
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
29
|
export { type BlockedUsersListResponse, type BlockedUserWire, blockedUsersListResponseSchema, blockedUserWireSchema, COMMUNITY_SETTINGS_DEFAULTS, type CommunitySettingsWire, communitySettingsWireSchema, type UpdateCommunitySettingsBody, updateCommunitySettingsBodySchema, } from './settings.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -31,7 +31,9 @@ export { addGroupMembersBodySchema, communityGroupWireSchema, communityGroupWith
|
|
|
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) ──────────────────────────────────────────────────
|
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,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,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,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 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 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"]}
|
|
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>;
|
package/dist/post.d.ts.map
CHANGED
|
@@ -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
|
|
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":"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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 { 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;
|
package/dist/report.d.ts.map
CHANGED
|
@@ -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
|
|
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([
|
|
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
|
package/dist/report.js.map
CHANGED
|
@@ -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,
|
|
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"]}
|
package/dist/repost.d.ts
ADDED
|
@@ -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.
|
|
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",
|