@buildinternet/releases-api-types 0.58.3 → 0.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/README.md +14 -6
  2. package/dist/api-types.d.ts +1168 -0
  3. package/dist/api-types.d.ts.map +1 -0
  4. package/dist/api-types.js +134 -0
  5. package/dist/schemas/account-profile.d.ts +29 -0
  6. package/dist/schemas/account-profile.d.ts.map +1 -0
  7. package/dist/schemas/account-profile.js +24 -0
  8. package/dist/schemas/ai-models.d.ts +67 -0
  9. package/dist/schemas/ai-models.d.ts.map +1 -0
  10. package/{src/schemas/ai-models.ts → dist/schemas/ai-models.js} +25 -31
  11. package/dist/schemas/api-keys.d.ts +71 -0
  12. package/dist/schemas/api-keys.d.ts.map +1 -0
  13. package/dist/schemas/api-keys.js +32 -0
  14. package/dist/schemas/classifications.d.ts +134 -0
  15. package/dist/schemas/classifications.d.ts.map +1 -0
  16. package/dist/schemas/classifications.js +96 -0
  17. package/dist/schemas/collections.d.ts +605 -0
  18. package/dist/schemas/collections.d.ts.map +1 -0
  19. package/dist/schemas/collections.js +320 -0
  20. package/dist/schemas/datasets.d.ts +34 -0
  21. package/dist/schemas/datasets.d.ts.map +1 -0
  22. package/dist/schemas/datasets.js +11 -0
  23. package/dist/schemas/errors.d.ts +43 -0
  24. package/dist/schemas/errors.d.ts.map +1 -0
  25. package/dist/schemas/errors.js +61 -0
  26. package/dist/schemas/feedback.d.ts +134 -0
  27. package/dist/schemas/feedback.d.ts.map +1 -0
  28. package/dist/schemas/feedback.js +76 -0
  29. package/dist/schemas/follows.d.ts +154 -0
  30. package/dist/schemas/follows.d.ts.map +1 -0
  31. package/dist/schemas/follows.js +42 -0
  32. package/dist/schemas/listing.d.ts +374 -0
  33. package/dist/schemas/listing.d.ts.map +1 -0
  34. package/dist/schemas/listing.js +175 -0
  35. package/dist/schemas/lookups.d.ts +285 -0
  36. package/dist/schemas/lookups.d.ts.map +1 -0
  37. package/{src/schemas/lookups.ts → dist/schemas/lookups.js} +67 -80
  38. package/dist/schemas/marketing-classifier.d.ts +32 -0
  39. package/dist/schemas/marketing-classifier.d.ts.map +1 -0
  40. package/{src/schemas/marketing-classifier.ts → dist/schemas/marketing-classifier.js} +14 -18
  41. package/dist/schemas/orgs.d.ts +950 -0
  42. package/dist/schemas/orgs.d.ts.map +1 -0
  43. package/dist/schemas/orgs.js +473 -0
  44. package/dist/schemas/overviews.d.ts +205 -0
  45. package/dist/schemas/overviews.d.ts.map +1 -0
  46. package/{src/schemas/overviews.ts → dist/schemas/overviews.js} +41 -54
  47. package/dist/schemas/products.d.ts +595 -0
  48. package/dist/schemas/products.d.ts.map +1 -0
  49. package/{src/schemas/products.ts → dist/schemas/products.js} +113 -136
  50. package/dist/schemas/publish-tokens.d.ts +55 -0
  51. package/dist/schemas/publish-tokens.d.ts.map +1 -0
  52. package/{src/schemas/publish-tokens.ts → dist/schemas/publish-tokens.js} +18 -29
  53. package/dist/schemas/recommendations.d.ts +52 -0
  54. package/dist/schemas/recommendations.d.ts.map +1 -0
  55. package/dist/schemas/recommendations.js +58 -0
  56. package/dist/schemas/related.d.ts +331 -0
  57. package/dist/schemas/related.d.ts.map +1 -0
  58. package/dist/schemas/related.js +115 -0
  59. package/dist/schemas/releases.d.ts +623 -0
  60. package/dist/schemas/releases.d.ts.map +1 -0
  61. package/dist/schemas/releases.js +414 -0
  62. package/dist/schemas/resolve.d.ts +259 -0
  63. package/dist/schemas/resolve.d.ts.map +1 -0
  64. package/{src/schemas/resolve.ts → dist/schemas/resolve.js} +2 -5
  65. package/dist/schemas/search.d.ts +475 -0
  66. package/dist/schemas/search.d.ts.map +1 -0
  67. package/dist/schemas/search.js +258 -0
  68. package/dist/schemas/semantic-alert-summary.d.ts +54 -0
  69. package/dist/schemas/semantic-alert-summary.d.ts.map +1 -0
  70. package/dist/schemas/semantic-alert-summary.js +57 -0
  71. package/dist/schemas/semantic-alerts.d.ts +41 -0
  72. package/dist/schemas/semantic-alerts.d.ts.map +1 -0
  73. package/dist/schemas/semantic-alerts.js +42 -0
  74. package/dist/schemas/shared.d.ts +247 -0
  75. package/dist/schemas/shared.d.ts.map +1 -0
  76. package/dist/schemas/shared.js +248 -0
  77. package/dist/schemas/site-notice.d.ts +35 -0
  78. package/dist/schemas/site-notice.d.ts.map +1 -0
  79. package/{src/schemas/site-notice.ts → dist/schemas/site-notice.js} +14 -18
  80. package/dist/schemas/sitemap.d.ts +97 -0
  81. package/dist/schemas/sitemap.d.ts.map +1 -0
  82. package/dist/schemas/sitemap.js +107 -0
  83. package/dist/schemas/sources.d.ts +1145 -0
  84. package/dist/schemas/sources.d.ts.map +1 -0
  85. package/dist/schemas/sources.js +645 -0
  86. package/dist/schemas/stats.d.ts +82 -0
  87. package/dist/schemas/stats.d.ts.map +1 -0
  88. package/dist/schemas/stats.js +59 -0
  89. package/dist/schemas/taxonomy.d.ts +344 -0
  90. package/dist/schemas/taxonomy.d.ts.map +1 -0
  91. package/{src/schemas/taxonomy.ts → dist/schemas/taxonomy.js} +47 -58
  92. package/dist/schemas/webhooks.d.ts +618 -0
  93. package/dist/schemas/webhooks.d.ts.map +1 -0
  94. package/dist/schemas/webhooks.js +191 -0
  95. package/dist/schemas/well-known.d.ts +302 -0
  96. package/dist/schemas/well-known.d.ts.map +1 -0
  97. package/dist/schemas/well-known.js +192 -0
  98. package/dist/schemas/workspace-integrations.d.ts +27 -0
  99. package/dist/schemas/workspace-integrations.d.ts.map +1 -0
  100. package/dist/schemas/workspace-integrations.js +22 -0
  101. package/package.json +16 -4
  102. package/src/api-types.ts +0 -2223
  103. package/src/schemas/account-profile.ts +0 -28
  104. package/src/schemas/ai-models.test.ts +0 -29
  105. package/src/schemas/classifications.ts +0 -109
  106. package/src/schemas/collections.ts +0 -346
  107. package/src/schemas/errors.test.ts +0 -55
  108. package/src/schemas/errors.ts +0 -81
  109. package/src/schemas/feedback.ts +0 -56
  110. package/src/schemas/listing.ts +0 -226
  111. package/src/schemas/orgs.ts +0 -533
  112. package/src/schemas/recommendations.ts +0 -39
  113. package/src/schemas/related.ts +0 -126
  114. package/src/schemas/releases.ts +0 -436
  115. package/src/schemas/search.ts +0 -274
  116. package/src/schemas/semantic-alert-summary.ts +0 -65
  117. package/src/schemas/shared.notice.test.ts +0 -36
  118. package/src/schemas/shared.ts +0 -267
  119. package/src/schemas/site-notice.test.ts +0 -67
  120. package/src/schemas/sitemap.ts +0 -114
  121. package/src/schemas/sources.ts +0 -674
  122. package/src/schemas/stats.ts +0 -62
  123. package/src/schemas/well-known.test.ts +0 -264
  124. package/src/schemas/well-known.ts +0 -252
  125. package/src/schemas/workspace-integrations.ts +0 -26
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sources.d.ts","sourceRoot":"","sources":["../../src/schemas/sources.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,645 @@
1
+ import { z } from "zod";
2
+ import { SOURCE_TYPES, SOURCE_DISCOVERY, SOURCE_FETCH_PRIORITIES, } from "@buildinternet/releases-core/source-enums";
3
+ import { KIND_VALUES } from "@buildinternet/releases-core/kinds";
4
+ import { ListResponseSchema, NoticeSchema, ReleaseItemSchema, ReleaseSummaryItemSchema, ReleaseTypeSchema, } from "./shared.js";
5
+ export const SourceTypeSchema = z.enum(SOURCE_TYPES);
6
+ export const SourceDiscoverySchema = z.enum(SOURCE_DISCOVERY);
7
+ export const SourceFetchPrioritySchema = z.enum(SOURCE_FETCH_PRIORITIES);
8
+ /**
9
+ * Per-source row used in list/embedded contexts (e.g. nested inside OrgDetail).
10
+ * Most fields are optional — embedded shapes (org detail) only carry the
11
+ * subset needed for that view.
12
+ */
13
+ export const SourceListItemSchema = z.object({
14
+ id: z.string().optional(),
15
+ slug: z.string(),
16
+ name: z.string(),
17
+ type: SourceTypeSchema,
18
+ url: z.string().optional(),
19
+ // `orgSlug` is populated on the standalone `/v1/sources` list path; the
20
+ // `OrgDetail.sources` embedded path omits it (the parent already names the org).
21
+ orgSlug: z.string().nullable().optional(),
22
+ releaseCount: z.number().int().min(0),
23
+ latestVersion: z.string().nullable(),
24
+ latestDate: z.string().nullable(),
25
+ latestAddedAt: z.string().nullable().optional(),
26
+ isPrimary: z.boolean().optional(),
27
+ isHidden: z.boolean().optional(),
28
+ /**
29
+ * How the row was created. Optional on the wire so older API responses
30
+ * (mid-deploy or pinned old workers) degrade gracefully — consumers that
31
+ * see `undefined` should treat it as `"curated"`.
32
+ */
33
+ discovery: SourceDiscoverySchema.optional(),
34
+ fetchPriority: SourceFetchPrioritySchema.nullable().optional(),
35
+ lastFetchedAt: z.string().nullable().optional(),
36
+ lastPolledAt: z.string().nullable().optional(),
37
+ changeDetectedAt: z.string().nullable().optional(),
38
+ consecutiveNoChange: z.number().int().nullable().optional(),
39
+ consecutiveErrors: z.number().int().nullable().optional(),
40
+ nextFetchAfter: z.string().nullable().optional(),
41
+ medianGapDays: z.number().nullable().optional(),
42
+ lastRetieredAt: z.string().nullable().optional(),
43
+ metadata: z.string().nullable().optional(),
44
+ productName: z.string().nullable().optional(),
45
+ productSlug: z.string().nullable().optional(),
46
+ kind: z.enum(KIND_VALUES).nullable().optional(),
47
+ // GitHub stargazer count. `nullable().optional()` (the house idiom for these
48
+ // shapes): non-github sources and older responses omit it; a fetched row may
49
+ // carry `null`. Consumers treat null/undefined as "unknown".
50
+ stars: z.number().int().min(0).nullable().optional(),
51
+ starsFetchedAt: z.string().nullable().optional(),
52
+ });
53
+ /**
54
+ * Canonical row returned by `GET /v1/sources` (list). Superset of
55
+ * `SourceListItem` with required `id` / `orgName` / `orgSlug` fields used by
56
+ * the CLI `list` command.
57
+ */
58
+ export const SourceWithOrgSchema = z.object({
59
+ id: z.string(),
60
+ name: z.string(),
61
+ slug: z.string(),
62
+ type: SourceTypeSchema,
63
+ url: z.string(),
64
+ orgName: z.string().nullable(),
65
+ orgSlug: z.string().nullable(),
66
+ productName: z.string().nullable(),
67
+ productSlug: z.string().nullable(),
68
+ isPrimary: z.boolean(),
69
+ isHidden: z.boolean().nullable(),
70
+ discovery: SourceDiscoverySchema.optional(),
71
+ metadata: z.string().nullable(),
72
+ releaseCount: z.number().int().min(0),
73
+ latestVersion: z.string().nullable(),
74
+ latestDate: z.string().nullable(),
75
+ lastFetchedAt: z.string().nullable(),
76
+ lastPolledAt: z.string().nullable(),
77
+ fetchPriority: SourceFetchPrioritySchema.nullable(),
78
+ changeDetectedAt: z.string().nullable(),
79
+ consecutiveNoChange: z.number().int().nullable(),
80
+ consecutiveErrors: z.number().int().nullable(),
81
+ nextFetchAfter: z.string().nullable(),
82
+ medianGapDays: z.number().nullable(),
83
+ lastRetieredAt: z.string().nullable(),
84
+ kind: z.enum(KIND_VALUES).nullable().optional(),
85
+ // GitHub stargazer count. `nullable().optional()` (the house idiom for these
86
+ // shapes): non-github sources and older responses omit it; a fetched row may
87
+ // carry `null`. Consumers treat null/undefined as "unknown".
88
+ stars: z.number().int().min(0).nullable().optional(),
89
+ starsFetchedAt: z.string().nullable().optional(),
90
+ });
91
+ export const SourceListResponseSchema = ListResponseSchema(SourceWithOrgSchema);
92
+ /**
93
+ * `GET /v1/sources` returns a bare array by default and an envelope when
94
+ * `?envelope=true` is passed. The OpenAPI spec advertises the union so
95
+ * either client expectation is documented.
96
+ */
97
+ export const SourceListResultSchema = z.union([
98
+ z.array(SourceWithOrgSchema),
99
+ SourceListResponseSchema,
100
+ ]);
101
+ const SourceDetailSummariesSchema = z.object({
102
+ rolling: ReleaseSummaryItemSchema.nullable(),
103
+ monthly: z.array(ReleaseSummaryItemSchema),
104
+ });
105
+ /**
106
+ * Cursor pagination shape for the source detail's embedded release feed.
107
+ * Mirrors `OrgFeedPaginationSchema` and `CollectionFeedPaginationSchema` —
108
+ * release lists are feed-shaped (append-only, mutates between calls), so they
109
+ * use cursor pagination per the AGENTS.md convention.
110
+ */
111
+ export const SourceFeedPaginationSchema = z.object({
112
+ nextCursor: z.string().nullable(),
113
+ limit: z.number().int().min(1),
114
+ });
115
+ /**
116
+ * Response shape for `GET /v1/sources/:slug/releases` (and the org-scoped
117
+ * alias). The source identity is in the URL, so items omit the `source` block
118
+ * the org feed carries.
119
+ */
120
+ export const SourceReleasesResponseSchema = z.object({
121
+ releases: z.array(ReleaseItemSchema),
122
+ pagination: SourceFeedPaginationSchema,
123
+ });
124
+ /**
125
+ * Resolved attribution block returned alongside a source row. `id` is
126
+ * additive vs the legacy `{ slug, name }` shape — older clients that ignored
127
+ * it still parse cleanly. `productId` / `productSlug` are sibling top-level
128
+ * fields on the source response (kept flat to mirror `orgId`), not nested
129
+ * inside this block.
130
+ */
131
+ export const SourceOrgRefSchema = z.object({
132
+ id: z.string(),
133
+ slug: z.string(),
134
+ name: z.string(),
135
+ });
136
+ export const SourceDetailSchema = z.object({
137
+ id: z.string(),
138
+ slug: z.string(),
139
+ name: z.string(),
140
+ type: SourceTypeSchema,
141
+ url: z.string(),
142
+ orgId: z.string().nullable(),
143
+ productId: z.string().nullable(),
144
+ productSlug: z.string().nullable(),
145
+ // Hidden sources are reachable by direct URL but excluded from listings,
146
+ // sitemap, and AI features. On-demand lookups and admin-suppressed rows
147
+ // set this true; canonical curated/agent sources leave it false.
148
+ isHidden: z.boolean(),
149
+ // Pairs with `isHidden` to distinguish admin-suppressed rows (`curated` /
150
+ // `agent` + hidden) from rows materialized by `/v1/lookups` (`on_demand`).
151
+ // Optional for graceful degradation against older API responses.
152
+ discovery: SourceDiscoverySchema.optional(),
153
+ /** Poll tier; `paused` stops fetching. Optional for older servers. */
154
+ fetchPriority: z.enum(["normal", "low", "paused"]).nullable().optional(),
155
+ isPrimary: z.boolean(),
156
+ metadata: z.string(),
157
+ changelogUrl: z.string().nullable().optional(),
158
+ hasChangelogFile: z.boolean().optional(),
159
+ org: SourceOrgRefSchema.nullable(),
160
+ releaseCount: z.number().int().min(0),
161
+ releasesLast30Days: z.number().int().min(0),
162
+ avgReleasesPerWeek: z.number(),
163
+ latestVersion: z.string().nullable(),
164
+ latestDate: z.string().nullable(),
165
+ lastFetchedAt: z.string().nullable(),
166
+ lastPolledAt: z.string().nullable(),
167
+ // #1969: additive so pinned old workers/clients degrade gracefully.
168
+ changeDetectedAt: z.string().nullable().optional(),
169
+ trackingSince: z.string(),
170
+ releases: z.array(ReleaseItemSchema),
171
+ pagination: SourceFeedPaginationSchema,
172
+ summaries: SourceDetailSummariesSchema,
173
+ kind: z.enum(KIND_VALUES).nullable().optional(),
174
+ // GitHub stargazer count. `nullable().optional()` (the house idiom for these
175
+ // shapes): non-github sources and older responses omit it; a fetched row may
176
+ // carry `null`. Consumers treat null/undefined as "unknown".
177
+ stars: z.number().int().min(0).nullable().optional(),
178
+ starsFetchedAt: z.string().nullable().optional(),
179
+ notice: NoticeSchema.nullable().optional(),
180
+ });
181
+ /**
182
+ * Response returned by `POST /v1/sources` and `PATCH /v1/sources/:slug`.
183
+ *
184
+ * Pre-#794 these endpoints returned the bare drizzle row, leaving callers to
185
+ * round-trip a separate `GET` to confirm the resolved org/product attribution.
186
+ * Now they return the same source row plus a resolved `org { id, slug, name }`
187
+ * block and `productSlug`, so an agent can answer "did the write take?" from
188
+ * the response alone.
189
+ *
190
+ * Wire fields are loosely-typed (`z.unknown()`) for the timestamps/counters
191
+ * that aren't load-bearing for callers — the row is large and we don't want
192
+ * to hand-mirror every column. Stable, agent-relevant fields are typed.
193
+ */
194
+ export const SourceMutationResponseSchema = z.looseObject({
195
+ id: z.string(),
196
+ slug: z.string(),
197
+ name: z.string(),
198
+ type: SourceTypeSchema,
199
+ url: z.string(),
200
+ orgId: z.string().nullable(),
201
+ productId: z.string().nullable(),
202
+ productSlug: z.string().nullable(),
203
+ org: SourceOrgRefSchema.nullable(),
204
+ metadata: z.string().nullable(),
205
+ isPrimary: z.boolean().nullable().optional(),
206
+ isHidden: z.boolean().nullable().optional(),
207
+ discovery: SourceDiscoverySchema.optional(),
208
+ createdAt: z.string().optional(),
209
+ kind: z.enum(KIND_VALUES).nullable().optional(),
210
+ });
211
+ /**
212
+ * Response from `POST /v1/sources/appstore` — materializes an App Store listing
213
+ * into a curated Org → Product → Source → first Release (modeled on the GitHub
214
+ * coordinate `LookupResponse`).
215
+ *
216
+ * Unlike `SourceMutationResponse`, the `source` here is the **bare inserted (or
217
+ * idempotently looked-up) drizzle row** the materialize handler returns verbatim:
218
+ * no resolved `org { id, slug, name }` block and no `productSlug`. `loose` so the
219
+ * untyped timestamp/counter columns on the row pass through.
220
+ *
221
+ * `status: "indexed"` is a brand-new source (HTTP 201); `"existing"` is the
222
+ * idempotent hit on a prior materialize of the same trackId (HTTP 200).
223
+ */
224
+ export const AppStoreMaterializeResponseSchema = z.object({
225
+ status: z.enum(["indexed", "existing"]),
226
+ source: z.looseObject({
227
+ id: z.string(),
228
+ slug: z.string(),
229
+ name: z.string(),
230
+ type: SourceTypeSchema,
231
+ url: z.string(),
232
+ orgId: z.string().nullable(),
233
+ productId: z.string().nullable(),
234
+ metadata: z.string().nullable(),
235
+ kind: z.enum(KIND_VALUES).nullable().optional(),
236
+ discovery: SourceDiscoverySchema.optional(),
237
+ isHidden: z.boolean().nullable().optional(),
238
+ createdAt: z.string().optional(),
239
+ }),
240
+ releaseCount: z.number(),
241
+ });
242
+ /** Body accepted by `PATCH /v1/sources/:slug`. */
243
+ export const SourcePatchInputSchema = z.object({
244
+ name: z.string().optional(),
245
+ url: z.string().optional(),
246
+ type: SourceTypeSchema.optional(),
247
+ slug: z.string().optional(),
248
+ metadata: z.string().optional(),
249
+ orgId: z.string().nullable().optional(),
250
+ productId: z.string().nullable().optional(),
251
+ lastFetchedAt: z.string().nullable().optional(),
252
+ lastContentHash: z.string().nullable().optional(),
253
+ fetchPriority: SourceFetchPrioritySchema.optional(),
254
+ consecutiveNoChange: z.number().int().optional(),
255
+ consecutiveErrors: z.number().int().optional(),
256
+ nextFetchAfter: z.string().nullable().optional(),
257
+ isPrimary: z.boolean().optional(),
258
+ isHidden: z.boolean().optional(),
259
+ changeDetectedAt: z.string().nullable().optional(),
260
+ lastPolledAt: z.string().nullable().optional(),
261
+ kind: z.enum(KIND_VALUES).nullable().optional(),
262
+ /** Admin-only: promote or demote the source's discovery status (curated/agent/on_demand). */
263
+ discovery: SourceDiscoverySchema.optional(),
264
+ notice: NoticeSchema.nullable().optional(),
265
+ });
266
+ /** Body accepted by `POST /v1/sources`. */
267
+ export const CreateSourceBodySchema = z.object({
268
+ name: z.string().min(1),
269
+ url: z.string().min(1),
270
+ type: SourceTypeSchema.optional(),
271
+ slug: z.string().optional(),
272
+ orgId: z.string().optional(),
273
+ orgSlug: z.string().optional(),
274
+ /**
275
+ * Either accepted; `productId` wins when both are supplied. `productSlug`
276
+ * resolves within the requested org (slug uniqueness is per-org). The
277
+ * legacy `productId` ingestion path is preserved for callers that already
278
+ * resolve products client-side; new agent flows should pass `productSlug`.
279
+ */
280
+ productId: z.string().optional(),
281
+ productSlug: z.string().optional(),
282
+ metadata: z.string().optional(),
283
+ isPrimary: z.boolean().optional(),
284
+ kind: z.enum(KIND_VALUES).nullable().optional(),
285
+ });
286
+ export const ChangelogFileSummarySchema = z.object({
287
+ path: z.string(),
288
+ filename: z.string(),
289
+ url: z.string(),
290
+ bytes: z.number().int().min(0),
291
+ fetchedAt: z.string(),
292
+ });
293
+ export const SourceChangelogResponseSchema = z.object({
294
+ path: z.string(),
295
+ filename: z.string(),
296
+ url: z.string(),
297
+ rawUrl: z.string(),
298
+ content: z.string(),
299
+ bytes: z.number().int().min(0),
300
+ fetchedAt: z.string(),
301
+ /** Character offset of the first character in `content` within the full file. */
302
+ offset: z.number().int().min(0),
303
+ /** The limit (in chars) that was applied to produce this slice. */
304
+ limit: z.number().int().min(0),
305
+ /** Next offset to request for the next slice, or null if `content` is the tail. */
306
+ nextOffset: z.number().int().nullable(),
307
+ /** Total length of the full file in characters. */
308
+ totalChars: z.number().int().min(0),
309
+ /** Requested token budget when in token mode (cl100k_base). */
310
+ tokens: z.number().int().optional(),
311
+ /** Encoded token count of the returned `content`. Set in token mode. */
312
+ sliceTokens: z.number().int().optional(),
313
+ /** Full-file token count (cl100k_base). Always populated. */
314
+ totalTokens: z.number().int().min(0),
315
+ /** True when the upstream file exceeded the 1MB cap and content was sliced. */
316
+ truncated: z.boolean(),
317
+ /** Byte offset where the file was truncated, or null when not truncated. */
318
+ truncatedAt: z.number().int().nullable(),
319
+ /**
320
+ * Index of every changelog file tracked for this source (root plus any
321
+ * discovered per-package files). Always present even for single-file
322
+ * sources so clients can lazily render a file picker.
323
+ */
324
+ files: z.array(ChangelogFileSummarySchema),
325
+ });
326
+ // ── Source activity (/sources/:slug/activity, /orgs/:orgSlug/sources/:sourceSlug/activity) ──
327
+ const SourceActivityWeeklyBucketSchema = z.object({
328
+ weekStart: z.string(),
329
+ count: z.number().int().min(0),
330
+ earliestVersion: z.string().nullable(),
331
+ latestVersion: z.string().nullable(),
332
+ });
333
+ /**
334
+ * Response shape for `GET /v1/sources/:slug/activity` and the org-scoped twin.
335
+ * Returns week-bucketed release counts for the full tracking lifetime of the source.
336
+ * Optional `?from=YYYY-MM-DD` and `?to=YYYY-MM-DD` narrow the range.
337
+ */
338
+ export const SourceActivityResponseSchema = z.object({
339
+ source: z.object({
340
+ slug: z.string(),
341
+ name: z.string(),
342
+ orgSlug: z.string().nullable(),
343
+ orgName: z.string().nullable(),
344
+ }),
345
+ range: z.object({ from: z.string(), to: z.string() }),
346
+ weeklyBuckets: z.array(SourceActivityWeeklyBucketSchema),
347
+ });
348
+ // ── Source heatmap (/sources/:slug/heatmap, /orgs/:orgSlug/sources/:sourceSlug/heatmap) ──
349
+ /**
350
+ * Response shape for `GET /v1/sources/:slug/heatmap` and the org-scoped twin.
351
+ * Returns daily release counts for the trailing 365 days — contribution-graph
352
+ * visualization for the source detail page.
353
+ */
354
+ export const SourceHeatmapResponseSchema = z.object({
355
+ source: z.object({ slug: z.string(), name: z.string() }),
356
+ range: z.object({ from: z.string(), to: z.string() }),
357
+ dailyCounts: z.array(z.object({ date: z.string(), count: z.number().int().min(0) })),
358
+ total: z.number().int().min(0),
359
+ });
360
+ // ── Source known releases (/sources/:slug/known-releases, /orgs/:orgSlug/…) ──
361
+ /**
362
+ * Single row returned inside `GET /v1/sources/:slug/known-releases`. Minimal
363
+ * identifier set used by the incremental parsing agent to skip already-known
364
+ * versions without fetching full content.
365
+ */
366
+ export const SourceKnownReleaseItemSchema = z.object({
367
+ version: z.string().nullable(),
368
+ title: z.string(),
369
+ publishedAt: z.string().nullable(),
370
+ });
371
+ /**
372
+ * Response shape for `GET /v1/sources/:slug/known-releases`. Returns the
373
+ * N most-recent non-suppressed releases (default N=10, max 500), ordered
374
+ * by `publishedAt` descending. Accepts `?limit=N`.
375
+ */
376
+ export const SourceKnownReleasesResponseSchema = z.array(SourceKnownReleaseItemSchema);
377
+ // ── Source recent releases (/sources/:slug/recent-releases, /orgs/:orgSlug/…) ──
378
+ /**
379
+ * Response shape for `GET /v1/sources/:slug/recent-releases`. Returns the
380
+ * full release rows (non-suppressed, published at or after `?cutoff=`) ordered
381
+ * by `publishedAt` descending. Used by the summarization agent to retrieve the
382
+ * release window it needs to summarize. `?cutoff=` is required (ISO-8601 date
383
+ * string); missing `?cutoff=` returns 400.
384
+ */
385
+ export const SourceRecentReleasesResponseSchema = z.array(z.looseObject({
386
+ id: z.string(),
387
+ sourceId: z.string(),
388
+ version: z.string().nullable(),
389
+ title: z.string(),
390
+ content: z.string(),
391
+ url: z.string().nullable(),
392
+ publishedAt: z.string().nullable(),
393
+ fetchedAt: z.string().optional(),
394
+ }));
395
+ // ── Source sessions (/sources/:slug/sessions) ──
396
+ /**
397
+ * Response shape for `GET /v1/sources/:slug/sessions`. Returns the active
398
+ * discovery session (if any) touching this source. The session object is the
399
+ * live DO state blob from the `StatusHub` Durable Object. Returns `{ sessions:
400
+ * [] }` when no active session references this source.
401
+ */
402
+ export const SourceSessionsResponseSchema = z.object({
403
+ sessions: z.array(z.record(z.string(), z.unknown())),
404
+ });
405
+ // ── Source summaries (/sources/:slug/summaries) ──
406
+ /**
407
+ * Full summary row as stored in `release_summaries`, returned by
408
+ * `GET /v1/sources/:slug/summaries`. Filter by `?type=rolling|monthly`,
409
+ * `?year=`, `?month=`.
410
+ */
411
+ export const SourceSummaryRowSchema = z.object({
412
+ id: z.string().optional(),
413
+ sourceId: z.string(),
414
+ orgId: z.string().nullable(),
415
+ type: z.enum(["rolling", "monthly"]),
416
+ year: z.number().int().nullable(),
417
+ month: z.number().int().nullable(),
418
+ windowDays: z.number().int().nullable(),
419
+ summary: z.string(),
420
+ releaseCount: z.number().int().min(0),
421
+ generatedAt: z.string(),
422
+ });
423
+ export const SourceSummariesResponseSchema = z.array(SourceSummaryRowSchema);
424
+ /**
425
+ * Body accepted by `POST /v1/sources/:slug/summaries`.
426
+ *
427
+ * `month` is calendar-bounded (1..12) and `windowDays` must be a positive
428
+ * integer when present — silently dropping wrong-typed values masks client
429
+ * bugs, so the validator rejects them explicitly. `year` is not bound-checked
430
+ * (any cutoff would be arbitrary).
431
+ */
432
+ export const CreateSourceSummaryBodySchema = z.object({
433
+ type: z.enum(["rolling", "monthly"]),
434
+ year: z.number().int().nullable().optional(),
435
+ month: z.number().int().min(1).max(12).nullable().optional(),
436
+ windowDays: z.number().int().min(1).nullable().optional(),
437
+ summary: z.string().min(1),
438
+ releaseCount: z.number().int().min(0),
439
+ });
440
+ /** Response for `POST /v1/sources/:slug/summaries` (upsert). */
441
+ export const CreateSourceSummaryResponseSchema = z.object({
442
+ ok: z.literal(true),
443
+ });
444
+ // ── Source fetch trigger (POST /sources/:slug/fetch) ──
445
+ /**
446
+ * Response shape for `POST /v1/sources/:slug/fetch`. Three branches:
447
+ * - Feed/GitHub/scraped-with-feed sources: `{ fetched: true, releasesInserted, … }`
448
+ * - Scrape/agent sources (flagged for CLI pickup): `{ queued: true, type: "flagged" }`
449
+ * - Render dry-run probe (`?dryRun=true` on a client-rendered scrape source):
450
+ * `{ renderCheck: true, rendered, candidateCount, sampleUrls, … }`
451
+ */
452
+ export const SourceFetchResponseSchema = z.union([
453
+ z.looseObject({ fetched: z.literal(true) }),
454
+ z.object({ queued: z.literal(true), type: z.string() }),
455
+ // Render dry-run probe (#1528): renders a client-rendered scrape source's
456
+ // index once and reports candidate links found, without the managed-agent
457
+ // extraction loop.
458
+ z.looseObject({
459
+ renderCheck: z.literal(true),
460
+ status: z.enum(["dry_run", "error"]),
461
+ rendered: z.boolean(),
462
+ candidateCount: z.number().int(),
463
+ sampleUrls: z.array(z.string()),
464
+ durationMs: z.number().int(),
465
+ error: z.string().optional(),
466
+ }),
467
+ ]);
468
+ // ── Source content-hash check (POST /sources/:slug/content-hash) ──
469
+ /** Response for `POST /v1/sources/:slug/content-hash`. */
470
+ export const SourceContentHashResponseSchema = z.union([
471
+ z.object({ unchanged: z.literal(true) }),
472
+ z.object({ unchanged: z.literal(false) }),
473
+ ]);
474
+ /** Body for `POST /v1/sources/:slug/content-hash`. */
475
+ export const SourceContentHashBodySchema = z.object({
476
+ contentHash: z.string().min(1),
477
+ });
478
+ // ── Changelog token backfill (PATCH /sources/:slug/changelog/tokens) ──
479
+ /** Response for `PATCH /v1/sources/:slug/changelog/tokens`. */
480
+ export const ChangelogTokensResponseSchema = z.object({
481
+ path: z.string(),
482
+ oldTokens: z.number().int().nullable(),
483
+ tokens: z.number().int().min(0),
484
+ });
485
+ // ── Source metadata merge (PATCH /sources/:slug/metadata) ──
486
+ /** Response for `PATCH /v1/sources/:slug/metadata`. Echoes the merged metadata object. */
487
+ export const SourceMetadataResponseSchema = z.object({
488
+ metadata: z.record(z.string(), z.unknown()),
489
+ });
490
+ // ── Changelog probe (POST /sources/:slug/changelog/probe) ──
491
+ /**
492
+ * A discovered CHANGELOG path returned by the probe. Mirrors
493
+ * `DiscoveredChangelogPath` from `@releases/adapters/github-discovery`:
494
+ * `origin` tags where the path came from (repo root, a workspace package, or
495
+ * an explicit `metadata.changelogPaths` override) and `exists` is true when a
496
+ * directory listing confirmed the file on HEAD.
497
+ */
498
+ export const DiscoveredChangelogPathSchema = z.object({
499
+ path: z.string(),
500
+ origin: z.enum(["root", "workspace", "override"]),
501
+ exists: z.boolean(),
502
+ });
503
+ /** Response for `POST /v1/sources/:slug/changelog/probe`. */
504
+ export const ChangelogProbeResponseSchema = z.object({
505
+ sourceId: z.string(),
506
+ sourceSlug: z.string(),
507
+ url: z.string(),
508
+ paths: z.array(DiscoveredChangelogPathSchema),
509
+ });
510
+ // ── Delete source (DELETE /sources/:slug) ──
511
+ /**
512
+ * Response for `DELETE /v1/sources/:slug`. Two shapes:
513
+ * - Soft delete: `{ deleted: true, deletedAt: "<iso>" }`
514
+ * - Hard delete (`?hard=true`): `{ deleted: true, hard: true }`
515
+ */
516
+ export const DeleteSourceResponseSchema = z.union([
517
+ z.object({ deleted: z.literal(true), deletedAt: z.string() }),
518
+ z.object({ deleted: z.literal(true), hard: z.literal(true) }),
519
+ ]);
520
+ // ── Delete source releases (DELETE /sources/:slug/releases) ──
521
+ /**
522
+ * Response for `DELETE /v1/sources/:slug/releases`. Two shapes:
523
+ * - Soft (`?hard` absent): `{ suppressed: N }` — releases hidden, not deleted
524
+ * - Hard (`?hard=true`): `{ deleted: N, hard: true }`
525
+ */
526
+ export const DeleteSourceReleasesResponseSchema = z.union([
527
+ z.object({ suppressed: z.number().int().min(0) }),
528
+ z.object({ deleted: z.number().int().min(0), hard: z.literal(true) }),
529
+ ]);
530
+ // ── Single release insert (POST /sources/:slug/releases) ──
531
+ /**
532
+ * Response for `POST /v1/sources/:slug/releases`. Returns the inserted release
533
+ * row (201) or `{ skipped: true }` (200) on URL conflict.
534
+ */
535
+ export const InsertReleaseResponseSchema = z.union([
536
+ z.looseObject({
537
+ id: z.string(),
538
+ sourceId: z.string(),
539
+ title: z.string(),
540
+ }),
541
+ z.object({ skipped: z.literal(true) }),
542
+ ]);
543
+ // ── Batch release insert (POST /sources/:slug/releases/batch) ──
544
+ /** One release in a `POST /v1/sources/:slug/releases/batch` body. */
545
+ export const BatchReleaseInputSchema = z.object({
546
+ title: z.string(),
547
+ content: z.string().describe("Release notes, as markdown."),
548
+ version: z
549
+ .string()
550
+ .nullable()
551
+ .optional()
552
+ .describe("Placeholder versions such as `<UNKNOWN>` or `n/a` are stripped."),
553
+ url: z
554
+ .string()
555
+ .nullable()
556
+ .optional()
557
+ .describe("Canonical permalink. Re-posting the same URL updates that release."),
558
+ contentHash: z.string().optional(),
559
+ publishedAt: z.string().nullable().optional().describe("ISO 8601 timestamp."),
560
+ media: z
561
+ .string()
562
+ .nullable()
563
+ .optional()
564
+ .describe("JSON-encoded array of `{ type, url, alt? }` media items."),
565
+ type: ReleaseTypeSchema.optional(),
566
+ prerelease: z.boolean().optional(),
567
+ });
568
+ /**
569
+ * Body for `POST /v1/sources/:slug/releases/batch`. Without `mode`, a URL that
570
+ * already exists only has its empty fields filled. `mode: "upsert-content"`
571
+ * overwrites content and media on a URL match, which is what publishers
572
+ * (`actions/publish-changelog`, `releases publish`) send.
573
+ */
574
+ export const BatchReleasesBodySchema = z.object({
575
+ mode: z.literal("upsert-content").optional(),
576
+ releases: z.array(BatchReleaseInputSchema),
577
+ });
578
+ /** Response for `POST /v1/sources/:slug/releases/batch`. */
579
+ export const BatchReleasesResponseSchema = z.object({
580
+ inserted: z.number().int().min(0),
581
+ total: z.number().int().min(0),
582
+ /** IDs of the releases this batch created (not those it updated). */
583
+ insertedIds: z.array(z.string()),
584
+ });
585
+ // ── Raw page snapshot (POST /sources/:slug/raw-snapshot) ──
586
+ /**
587
+ * Response for `POST /v1/sources/:slug/raw-snapshot` (#1283). On a store or a
588
+ * content-hash dedup hit, all fields are present; `stored` is `false` for the
589
+ * dedup case. When the `RAW_SNAPSHOTS` bucket is unbound the capture soft-fails
590
+ * with `{ stored: false, reason: "no_binding" }` and the snapshot fields are
591
+ * omitted — capture is best-effort and never errors ingest.
592
+ */
593
+ export const RawSnapshotResponseSchema = z.object({
594
+ stored: z.boolean(),
595
+ r2Key: z.string().optional(),
596
+ contentHash: z.string().optional(),
597
+ bytes: z.number().int().min(0).optional(),
598
+ // Only set on the soft-fail path; bounded so consumers can branch on it.
599
+ reason: z.enum(["no_binding"]).optional(),
600
+ });
601
+ // ── Oversized changelog files (GET /sources/changelog-files/oversized) ──
602
+ /**
603
+ * Row returned by `GET /v1/sources/changelog-files/oversized`. Each row is a
604
+ * changelog file whose content length exceeds `?minBytes=` (default 256 KB).
605
+ * Used by `scripts/backfill-changelog-tokens.ts` to find rows with estimated
606
+ * (not exact) token counts.
607
+ */
608
+ export const OversizedChangelogFileRowSchema = z.object({
609
+ sourceId: z.string(),
610
+ sourceSlug: z.string(),
611
+ sourceName: z.string(),
612
+ orgSlug: z.string(),
613
+ path: z.string(),
614
+ filename: z.string(),
615
+ bytes: z.number().int().min(0),
616
+ tokens: z.number().int().nullable(),
617
+ fetchedAt: z.string(),
618
+ });
619
+ export const OversizedChangelogFilesResponseSchema = z.array(OversizedChangelogFileRowSchema);
620
+ // ── Fetchable sources (GET /sources/fetchable) ──
621
+ /**
622
+ * Response for `GET /v1/sources/fetchable`. Returns raw source rows matching
623
+ * the requested fetch mode (`?mode=unfetched|stale|retry_errors|all`).
624
+ * Accepts `?staleHours=N` when `mode=stale`.
625
+ */
626
+ export const FetchableSourcesResponseSchema = z.array(z.looseObject({
627
+ id: z.string(),
628
+ slug: z.string(),
629
+ name: z.string(),
630
+ type: SourceTypeSchema,
631
+ url: z.string(),
632
+ metadata: z.string().nullable(),
633
+ }));
634
+ // ── Feed sources (GET /sources/feeds) ──
635
+ /**
636
+ * Response for `GET /v1/sources/feeds`. Returns visible source rows where
637
+ * `metadata.feedUrl` is set and `fetchPriority != 'paused'`.
638
+ */
639
+ export const FeedSourcesResponseSchema = FetchableSourcesResponseSchema;
640
+ // ── Changed sources (GET /sources/changes) ──
641
+ /**
642
+ * Response for `GET /v1/sources/changes`. Returns visible source rows that
643
+ * have a non-null `changeDetectedAt` — the set flagged for CLI pickup.
644
+ */
645
+ export const ChangedSourcesResponseSchema = FetchableSourcesResponseSchema;