@kids-reporter/api-types 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/index.d.ts +9 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +9 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/openapi.d.ts +43 -0
  6. package/dist/openapi.d.ts.map +1 -0
  7. package/dist/openapi.js +50 -0
  8. package/dist/openapi.js.map +1 -0
  9. package/dist/rest.d.ts +57 -0
  10. package/dist/rest.d.ts.map +1 -0
  11. package/dist/rest.js +26 -0
  12. package/dist/rest.js.map +1 -0
  13. package/dist/schemas/content-feed-queries.d.ts +34 -0
  14. package/dist/schemas/content-feed-queries.d.ts.map +1 -0
  15. package/dist/schemas/content-feed-queries.js +21 -0
  16. package/dist/schemas/content-feed-queries.js.map +1 -0
  17. package/dist/schemas/content-feed-responses.d.ts +192 -0
  18. package/dist/schemas/content-feed-responses.d.ts.map +1 -0
  19. package/dist/schemas/content-feed-responses.js +87 -0
  20. package/dist/schemas/content-feed-responses.js.map +1 -0
  21. package/dist/schemas/content-post-detail.d.ts +313 -0
  22. package/dist/schemas/content-post-detail.d.ts.map +1 -0
  23. package/dist/schemas/content-post-detail.js +196 -0
  24. package/dist/schemas/content-post-detail.js.map +1 -0
  25. package/dist/schemas/content.d.ts +438 -0
  26. package/dist/schemas/content.d.ts.map +1 -0
  27. package/dist/schemas/content.js +722 -0
  28. package/dist/schemas/content.js.map +1 -0
  29. package/dist/schemas/extra-openapi-paths.d.ts +53 -0
  30. package/dist/schemas/extra-openapi-paths.d.ts.map +1 -0
  31. package/dist/schemas/extra-openapi-paths.js +674 -0
  32. package/dist/schemas/extra-openapi-paths.js.map +1 -0
  33. package/dist/schemas/member-essay-answers-has-liked.d.ts +16 -0
  34. package/dist/schemas/member-essay-answers-has-liked.d.ts.map +1 -0
  35. package/dist/schemas/member-essay-answers-has-liked.js +30 -0
  36. package/dist/schemas/member-essay-answers-has-liked.js.map +1 -0
  37. package/dist/schemas/qna.d.ts +145 -0
  38. package/dist/schemas/qna.d.ts.map +1 -0
  39. package/dist/schemas/qna.js +632 -0
  40. package/dist/schemas/qna.js.map +1 -0
  41. package/package.json +36 -0
@@ -0,0 +1,674 @@
1
+ import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
2
+ import { z } from 'zod';
3
+ import { RestErrorBodySchema } from '../rest.js';
4
+ import { registry, V1AccessTokenResponseSchema, V1AuthorPostsCountResponseSchema, V1MemberPostsWithAnswersQuerySchema } from './content.js';
5
+ import { V1AuthorBySlugPostsQuerySchema, V1CategoryBySlugMetadataQuerySchema, V1CategoryPostsQuerySchema, V1FeedSlugPathParamsSchema, V1SubSubcategoryBySlugPostsQuerySchema, V1TagBySlugPostsQuerySchema } from './content-feed-queries.js';
6
+ import { V1AuthorBySlugMetaResponseSchema, V1AuthorBySlugPostsResponseSchema, V1CategoryBySlugCategoryPostsResponseSchema, V1CategoryBySlugMetadataResponseSchema, V1CategoryBySlugSubcategoriesThemeResponseSchema, V1SubcategoryBySlugPostsResponseSchema, V1SubSubcategoryBySlugPostsResponseSchema, V1TagBySlugMetaResponseSchema, V1TagBySlugPostsResponseSchema } from './content-feed-responses.js';
7
+ import { V1EssayAnswersHasLikedQuerySchema, V1EssayAnswersHasLikedResponseSchema } from './member-essay-answers-has-liked.js';
8
+ extendZodWithOpenApi(z);
9
+ const HealthOkWithTimestampSchema = z.object({
10
+ timestamp: z.string()
11
+ });
12
+ const MemberPostEssayAnswerInListSchema = z.strictObject({
13
+ id: z.string(),
14
+ content: z.string(),
15
+ likesCount: z.number().int(),
16
+ createdAt: z.string(),
17
+ updatedAt: z.string(),
18
+ question: z.strictObject({
19
+ id: z.string(),
20
+ title: z.string(),
21
+ hint: z.string(),
22
+ post: z.strictObject({
23
+ id: z.string()
24
+ })
25
+ })
26
+ }).openapi('MemberPostEssayAnswerInList');
27
+ const MemberPostChoiceOptionSchema = z.strictObject({
28
+ content: z.string(),
29
+ isCorrectAnswer: z.boolean()
30
+ });
31
+ const MemberPostChoiceAnswerInListSchema = z.strictObject({
32
+ id: z.string(),
33
+ choiceIndex: z.number().int(),
34
+ correct: z.boolean(),
35
+ createdAt: z.string(),
36
+ updatedAt: z.string(),
37
+ question: z.strictObject({
38
+ id: z.string(),
39
+ title: z.string(),
40
+ options: z.array(MemberPostChoiceOptionSchema),
41
+ reason: z.unknown().nullable(),
42
+ post: z.strictObject({
43
+ id: z.string()
44
+ })
45
+ })
46
+ }).openapi('MemberPostChoiceAnswerInList');
47
+ const V1MemberPostsWithAnswersItemSchema = z.object({
48
+ id: z.string(),
49
+ title: z.string(),
50
+ slug: z.string(),
51
+ publishedDate: z.string(),
52
+ lastAnsweredTime: z.string(),
53
+ essayAnswers: z.array(MemberPostEssayAnswerInListSchema),
54
+ choiceAnswers: z.array(MemberPostChoiceAnswerInListSchema)
55
+ }).openapi('MemberPostWithAnswersItem');
56
+ export const V1MemberPostsWithAnswersResponseSchema = z.object({
57
+ posts: z.array(V1MemberPostsWithAnswersItemSchema),
58
+ nextCursor: z.string().nullable()
59
+ });
60
+ export const V1MemberAvatarUploadResponseSchema = z.object({
61
+ id: z.string(),
62
+ name: z.string()
63
+ });
64
+ export const V1MemberAvatarDeleteResponseSchema = z.object({
65
+ id: z.string()
66
+ });
67
+
68
+ /* --- Infra: health, OpenAPI, auth */
69
+
70
+ registry.registerPath({
71
+ method: 'get',
72
+ path: '/health',
73
+ tags: ['Infra'],
74
+ description: 'Health: DB connectivity plus timestamp in success payload.',
75
+ responses: {
76
+ 200: {
77
+ description: 'OK',
78
+ content: {
79
+ 'application/json': {
80
+ schema: HealthOkWithTimestampSchema
81
+ }
82
+ }
83
+ },
84
+ 503: {
85
+ description: 'Unhealthy',
86
+ content: {
87
+ 'application/json': {
88
+ schema: RestErrorBodySchema
89
+ }
90
+ }
91
+ }
92
+ }
93
+ });
94
+ registry.registerPath({
95
+ method: 'options',
96
+ path: '/auth/access-token',
97
+ tags: ['Auth'],
98
+ description: 'CORS preflight for the access token exchange (browser + cookies).',
99
+ responses: {
100
+ 204: {
101
+ description: 'No content'
102
+ }
103
+ }
104
+ });
105
+ registry.registerPath({
106
+ method: 'post',
107
+ path: '/auth/access-token',
108
+ tags: ['Auth'],
109
+ description: 'Exchanges the `id_token` **cookie** (forwarded to Go API) for a **Bearer** JWT. Requires `Origin` or `Referer` from an allowlisted origin. Used by the frontend; not for unauthenticated server-to-server calls.',
110
+ responses: {
111
+ 200: {
112
+ description: 'Access token issued',
113
+ content: {
114
+ 'application/json': {
115
+ schema: V1AccessTokenResponseSchema
116
+ }
117
+ }
118
+ },
119
+ 400: {
120
+ description: 'Missing id_token cookie',
121
+ content: {
122
+ 'application/json': {
123
+ schema: RestErrorBodySchema
124
+ }
125
+ }
126
+ },
127
+ 401: {
128
+ description: 'Unauthorized: invalid id_token when exchanging upstream (Go API **400**/**401**), **or** upstream returned a JWT body that failed local verification (**iss** / **aud** / **user_id**).',
129
+ content: {
130
+ 'application/json': {
131
+ schema: RestErrorBodySchema
132
+ }
133
+ }
134
+ },
135
+ 403: {
136
+ description: 'Origin not allowed, missing, or invalid',
137
+ content: {
138
+ 'application/json': {
139
+ schema: RestErrorBodySchema
140
+ }
141
+ }
142
+ },
143
+ 500: {
144
+ description: 'Internal error or upstream issue',
145
+ content: {
146
+ 'application/json': {
147
+ schema: RestErrorBodySchema
148
+ }
149
+ }
150
+ }
151
+ }
152
+ });
153
+
154
+ /* --- Public: categories, tags, authors */
155
+
156
+ registry.registerPath({
157
+ method: 'get',
158
+ path: '/v1/categories/by-slug/{slug}/posts',
159
+ tags: ['Taxonomy'],
160
+ description: 'Posts for a top-level **category** slug. Aggregates all sub-subcategories under that category. Empty list if slug is unknown (no 404).',
161
+ request: {
162
+ params: V1FeedSlugPathParamsSchema,
163
+ query: V1CategoryPostsQuerySchema
164
+ },
165
+ responses: {
166
+ 200: {
167
+ description: 'Success',
168
+ content: {
169
+ 'application/json': {
170
+ schema: V1CategoryBySlugCategoryPostsResponseSchema
171
+ }
172
+ }
173
+ },
174
+ 400: {
175
+ description: 'Bad request',
176
+ content: {
177
+ 'application/json': {
178
+ schema: RestErrorBodySchema
179
+ }
180
+ }
181
+ }
182
+ }
183
+ });
184
+ registry.registerPath({
185
+ method: 'get',
186
+ path: '/v1/categories/by-slug/{slug}/metadata',
187
+ tags: ['Taxonomy'],
188
+ description: 'OG metadata for a category, optionally filtered to one subcategory by `subcategorySlug`.',
189
+ request: {
190
+ params: V1FeedSlugPathParamsSchema,
191
+ query: V1CategoryBySlugMetadataQuerySchema
192
+ },
193
+ responses: {
194
+ 200: {
195
+ description: 'Success',
196
+ content: {
197
+ 'application/json': {
198
+ schema: V1CategoryBySlugMetadataResponseSchema
199
+ }
200
+ }
201
+ },
202
+ 404: {
203
+ description: 'Category not found',
204
+ content: {
205
+ 'application/json': {
206
+ schema: RestErrorBodySchema
207
+ }
208
+ }
209
+ },
210
+ 400: {
211
+ description: 'Bad request',
212
+ content: {
213
+ 'application/json': {
214
+ schema: RestErrorBodySchema
215
+ }
216
+ }
217
+ }
218
+ }
219
+ });
220
+ registry.registerPath({
221
+ method: 'get',
222
+ path: '/v1/categories/by-slug/{slug}/subcategories-theme',
223
+ tags: ['Taxonomy'],
224
+ description: 'Display name, theme color, and subcategory list for a category tab UI.',
225
+ request: {
226
+ params: V1FeedSlugPathParamsSchema
227
+ },
228
+ responses: {
229
+ 200: {
230
+ description: 'Success',
231
+ content: {
232
+ 'application/json': {
233
+ schema: V1CategoryBySlugSubcategoriesThemeResponseSchema
234
+ }
235
+ }
236
+ },
237
+ 404: {
238
+ description: 'Category not found',
239
+ content: {
240
+ 'application/json': {
241
+ schema: RestErrorBodySchema
242
+ }
243
+ }
244
+ }
245
+ }
246
+ });
247
+ registry.registerPath({
248
+ method: 'get',
249
+ path: '/v1/subcategories/by-slug/{slug}/posts',
250
+ tags: ['Taxonomy'],
251
+ description: 'Feed posts for a **subcategory** slug (published/archived, sub-subcategory membership).',
252
+ request: {
253
+ params: V1FeedSlugPathParamsSchema,
254
+ query: V1CategoryPostsQuerySchema
255
+ },
256
+ responses: {
257
+ 200: {
258
+ description: 'Success',
259
+ content: {
260
+ 'application/json': {
261
+ schema: V1SubcategoryBySlugPostsResponseSchema
262
+ }
263
+ }
264
+ },
265
+ 404: {
266
+ description: 'Subcategory not found',
267
+ content: {
268
+ 'application/json': {
269
+ schema: RestErrorBodySchema
270
+ }
271
+ }
272
+ },
273
+ 400: {
274
+ description: 'Bad request',
275
+ content: {
276
+ 'application/json': {
277
+ schema: RestErrorBodySchema
278
+ }
279
+ }
280
+ }
281
+ }
282
+ });
283
+ registry.registerPath({
284
+ method: 'get',
285
+ path: '/v1/sub-subcategories/by-slug/{slug}/posts',
286
+ tags: ['Taxonomy'],
287
+ description: 'Public posts for a **sub-subcategory** slug, latest-first.',
288
+ request: {
289
+ params: V1FeedSlugPathParamsSchema,
290
+ query: V1SubSubcategoryBySlugPostsQuerySchema
291
+ },
292
+ responses: {
293
+ 200: {
294
+ description: 'Success',
295
+ content: {
296
+ 'application/json': {
297
+ schema: V1SubSubcategoryBySlugPostsResponseSchema
298
+ }
299
+ }
300
+ },
301
+ 404: {
302
+ description: 'Sub-subcategory not found',
303
+ content: {
304
+ 'application/json': {
305
+ schema: RestErrorBodySchema
306
+ }
307
+ }
308
+ },
309
+ 400: {
310
+ description: 'Bad request',
311
+ content: {
312
+ 'application/json': {
313
+ schema: RestErrorBodySchema
314
+ }
315
+ }
316
+ }
317
+ }
318
+ });
319
+ registry.registerPath({
320
+ method: 'get',
321
+ path: '/v1/tags/by-slug/{slug}/meta',
322
+ tags: ['Taxonomy'],
323
+ description: 'OG metadata for a tag page.',
324
+ request: {
325
+ params: V1FeedSlugPathParamsSchema
326
+ },
327
+ responses: {
328
+ 200: {
329
+ description: 'Success',
330
+ content: {
331
+ 'application/json': {
332
+ schema: V1TagBySlugMetaResponseSchema
333
+ }
334
+ }
335
+ },
336
+ 404: {
337
+ description: 'Tag not found',
338
+ content: {
339
+ 'application/json': {
340
+ schema: RestErrorBodySchema
341
+ }
342
+ }
343
+ }
344
+ }
345
+ });
346
+ registry.registerPath({
347
+ method: 'get',
348
+ path: '/v1/tags/by-slug/{slug}/posts',
349
+ tags: ['Taxonomy'],
350
+ description: 'Public posts for a tag (with count).',
351
+ request: {
352
+ params: V1FeedSlugPathParamsSchema,
353
+ query: V1TagBySlugPostsQuerySchema
354
+ },
355
+ responses: {
356
+ 200: {
357
+ description: 'Success',
358
+ content: {
359
+ 'application/json': {
360
+ schema: V1TagBySlugPostsResponseSchema
361
+ }
362
+ }
363
+ },
364
+ 404: {
365
+ description: 'Tag not found',
366
+ content: {
367
+ 'application/json': {
368
+ schema: RestErrorBodySchema
369
+ }
370
+ }
371
+ },
372
+ 400: {
373
+ description: 'Bad request',
374
+ content: {
375
+ 'application/json': {
376
+ schema: RestErrorBodySchema
377
+ }
378
+ }
379
+ }
380
+ }
381
+ });
382
+ registry.registerPath({
383
+ method: 'get',
384
+ path: '/v1/authors/by-slug/{slug}/meta',
385
+ tags: ['Taxonomy'],
386
+ description: 'Author bio, name, and small image for a profile/SEO surface.',
387
+ request: {
388
+ params: V1FeedSlugPathParamsSchema
389
+ },
390
+ responses: {
391
+ 200: {
392
+ description: 'Success',
393
+ content: {
394
+ 'application/json': {
395
+ schema: V1AuthorBySlugMetaResponseSchema
396
+ }
397
+ }
398
+ },
399
+ 404: {
400
+ description: 'Author not found',
401
+ content: {
402
+ 'application/json': {
403
+ schema: RestErrorBodySchema
404
+ }
405
+ }
406
+ }
407
+ }
408
+ });
409
+ registry.registerPath({
410
+ method: 'get',
411
+ path: '/v1/authors/by-slug/{slug}/posts',
412
+ tags: ['Taxonomy'],
413
+ description: 'Author posts and profile fields for the list view.',
414
+ request: {
415
+ params: V1FeedSlugPathParamsSchema,
416
+ query: V1AuthorBySlugPostsQuerySchema
417
+ },
418
+ responses: {
419
+ 200: {
420
+ description: 'Success',
421
+ content: {
422
+ 'application/json': {
423
+ schema: V1AuthorBySlugPostsResponseSchema
424
+ }
425
+ }
426
+ },
427
+ 404: {
428
+ description: 'Author not found',
429
+ content: {
430
+ 'application/json': {
431
+ schema: RestErrorBodySchema
432
+ }
433
+ }
434
+ },
435
+ 400: {
436
+ description: 'Bad request',
437
+ content: {
438
+ 'application/json': {
439
+ schema: RestErrorBodySchema
440
+ }
441
+ }
442
+ }
443
+ }
444
+ });
445
+ registry.registerPath({
446
+ method: 'get',
447
+ path: '/v1/authors/by-slug/{slug}/posts-count',
448
+ tags: ['Taxonomy'],
449
+ description: 'Public post count for an author (matches GraphQL `GetAuthorPostsCount`).',
450
+ request: {
451
+ params: V1FeedSlugPathParamsSchema
452
+ },
453
+ responses: {
454
+ 200: {
455
+ description: 'Success',
456
+ content: {
457
+ 'application/json': {
458
+ schema: V1AuthorPostsCountResponseSchema
459
+ }
460
+ }
461
+ },
462
+ 404: {
463
+ description: 'Author not found',
464
+ content: {
465
+ 'application/json': {
466
+ schema: RestErrorBodySchema
467
+ }
468
+ }
469
+ }
470
+ }
471
+ });
472
+
473
+ /* --- Authenticated: member (Bearer JWT) */
474
+
475
+ registry.registerPath({
476
+ method: 'get',
477
+ path: '/v1/members/me/posts-with-answers',
478
+ tags: ['Members'],
479
+ description: 'Cursor-paginated; role must be `member` or `admin`.',
480
+ request: {
481
+ query: V1MemberPostsWithAnswersQuerySchema
482
+ },
483
+ responses: {
484
+ 200: {
485
+ description: 'Success',
486
+ content: {
487
+ 'application/json': {
488
+ schema: V1MemberPostsWithAnswersResponseSchema
489
+ }
490
+ }
491
+ },
492
+ 401: {
493
+ description: 'Missing or invalid JWT',
494
+ content: {
495
+ 'application/json': {
496
+ schema: RestErrorBodySchema
497
+ }
498
+ }
499
+ },
500
+ 403: {
501
+ description: 'Role not allowed',
502
+ content: {
503
+ 'application/json': {
504
+ schema: RestErrorBodySchema
505
+ }
506
+ }
507
+ },
508
+ 404: {
509
+ description: 'Member not found',
510
+ content: {
511
+ 'application/json': {
512
+ schema: RestErrorBodySchema
513
+ }
514
+ }
515
+ }
516
+ }
517
+ });
518
+ registry.registerPath({
519
+ method: 'get',
520
+ path: '/v1/members/me/essay-answers/has-liked',
521
+ tags: ['Members'],
522
+ description: 'Batch check liked state per ID.',
523
+ request: {
524
+ query: V1EssayAnswersHasLikedQuerySchema
525
+ },
526
+ responses: {
527
+ 200: {
528
+ description: 'Success: root array (empty if no ids or none match).',
529
+ content: {
530
+ 'application/json': {
531
+ schema: V1EssayAnswersHasLikedResponseSchema
532
+ }
533
+ }
534
+ },
535
+ 400: {
536
+ description: 'Invalid query (e.g. validation failure)',
537
+ content: {
538
+ 'application/json': {
539
+ schema: RestErrorBodySchema
540
+ }
541
+ }
542
+ },
543
+ 401: {
544
+ description: 'Missing or invalid JWT',
545
+ content: {
546
+ 'application/json': {
547
+ schema: RestErrorBodySchema
548
+ }
549
+ }
550
+ },
551
+ 403: {
552
+ description: 'Role not allowed',
553
+ content: {
554
+ 'application/json': {
555
+ schema: RestErrorBodySchema
556
+ }
557
+ }
558
+ },
559
+ 404: {
560
+ description: 'Member not found',
561
+ content: {
562
+ 'application/json': {
563
+ schema: RestErrorBodySchema
564
+ }
565
+ }
566
+ }
567
+ }
568
+ });
569
+ registry.registerPath({
570
+ method: 'post',
571
+ path: '/v1/members/me/avatar',
572
+ tags: ['Members'],
573
+ description: 'Upload: `multipart/form-data` field `file` (jpeg/png/gif/webp, max 5MB). `IMAGES_STORAGE_PATH` must be configured. Role `member` or `admin`.',
574
+ request: {
575
+ body: {
576
+ content: {
577
+ 'multipart/form-data': {
578
+ schema: z.object({
579
+ file: z.string().openapi({
580
+ type: 'string',
581
+ format: 'binary'
582
+ }),
583
+ name: z.string().optional().openapi({
584
+ description: 'Display name, default `memberAvatar`.'
585
+ })
586
+ })
587
+ }
588
+ }
589
+ }
590
+ },
591
+ responses: {
592
+ 200: {
593
+ description: 'Created',
594
+ content: {
595
+ 'application/json': {
596
+ schema: V1MemberAvatarUploadResponseSchema
597
+ }
598
+ }
599
+ },
600
+ 400: {
601
+ description: 'Missing file, invalid type, etc.',
602
+ content: {
603
+ 'application/json': {
604
+ schema: RestErrorBodySchema
605
+ }
606
+ }
607
+ },
608
+ 401: {
609
+ description: 'Missing or invalid JWT',
610
+ content: {
611
+ 'application/json': {
612
+ schema: RestErrorBodySchema
613
+ }
614
+ }
615
+ },
616
+ 403: {
617
+ description: 'Role not allowed',
618
+ content: {
619
+ 'application/json': {
620
+ schema: RestErrorBodySchema
621
+ }
622
+ }
623
+ },
624
+ 404: {
625
+ description: 'Member not found',
626
+ content: {
627
+ 'application/json': {
628
+ schema: RestErrorBodySchema
629
+ }
630
+ }
631
+ },
632
+ 500: {
633
+ description: 'Storage not configured',
634
+ content: {
635
+ 'application/json': {
636
+ schema: RestErrorBodySchema
637
+ }
638
+ }
639
+ }
640
+ }
641
+ });
642
+ registry.registerPath({
643
+ method: 'delete',
644
+ path: '/v1/members/me/avatar',
645
+ tags: ['Members'],
646
+ description: 'Removes current avatar and deletes the stored file.',
647
+ responses: {
648
+ 200: {
649
+ description: 'Success',
650
+ content: {
651
+ 'application/json': {
652
+ schema: V1MemberAvatarDeleteResponseSchema
653
+ }
654
+ }
655
+ },
656
+ 401: {
657
+ description: 'Missing or invalid JWT',
658
+ content: {
659
+ 'application/json': {
660
+ schema: RestErrorBodySchema
661
+ }
662
+ }
663
+ },
664
+ 404: {
665
+ description: 'No avatar to delete',
666
+ content: {
667
+ 'application/json': {
668
+ schema: RestErrorBodySchema
669
+ }
670
+ }
671
+ }
672
+ }
673
+ });
674
+ //# sourceMappingURL=extra-openapi-paths.js.map