@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.
- package/README.md +14 -6
- package/dist/api-types.d.ts +1168 -0
- package/dist/api-types.d.ts.map +1 -0
- package/dist/api-types.js +134 -0
- package/dist/schemas/account-profile.d.ts +29 -0
- package/dist/schemas/account-profile.d.ts.map +1 -0
- package/dist/schemas/account-profile.js +24 -0
- package/dist/schemas/ai-models.d.ts +67 -0
- package/dist/schemas/ai-models.d.ts.map +1 -0
- package/{src/schemas/ai-models.ts → dist/schemas/ai-models.js} +25 -31
- package/dist/schemas/api-keys.d.ts +71 -0
- package/dist/schemas/api-keys.d.ts.map +1 -0
- package/dist/schemas/api-keys.js +32 -0
- package/dist/schemas/classifications.d.ts +134 -0
- package/dist/schemas/classifications.d.ts.map +1 -0
- package/dist/schemas/classifications.js +96 -0
- package/dist/schemas/collections.d.ts +605 -0
- package/dist/schemas/collections.d.ts.map +1 -0
- package/dist/schemas/collections.js +320 -0
- package/dist/schemas/datasets.d.ts +34 -0
- package/dist/schemas/datasets.d.ts.map +1 -0
- package/dist/schemas/datasets.js +11 -0
- package/dist/schemas/errors.d.ts +43 -0
- package/dist/schemas/errors.d.ts.map +1 -0
- package/dist/schemas/errors.js +61 -0
- package/dist/schemas/feedback.d.ts +134 -0
- package/dist/schemas/feedback.d.ts.map +1 -0
- package/dist/schemas/feedback.js +76 -0
- package/dist/schemas/follows.d.ts +154 -0
- package/dist/schemas/follows.d.ts.map +1 -0
- package/dist/schemas/follows.js +42 -0
- package/dist/schemas/listing.d.ts +374 -0
- package/dist/schemas/listing.d.ts.map +1 -0
- package/dist/schemas/listing.js +175 -0
- package/dist/schemas/lookups.d.ts +285 -0
- package/dist/schemas/lookups.d.ts.map +1 -0
- package/{src/schemas/lookups.ts → dist/schemas/lookups.js} +67 -80
- package/dist/schemas/marketing-classifier.d.ts +32 -0
- package/dist/schemas/marketing-classifier.d.ts.map +1 -0
- package/{src/schemas/marketing-classifier.ts → dist/schemas/marketing-classifier.js} +14 -18
- package/dist/schemas/orgs.d.ts +950 -0
- package/dist/schemas/orgs.d.ts.map +1 -0
- package/dist/schemas/orgs.js +473 -0
- package/dist/schemas/overviews.d.ts +205 -0
- package/dist/schemas/overviews.d.ts.map +1 -0
- package/{src/schemas/overviews.ts → dist/schemas/overviews.js} +41 -54
- package/dist/schemas/products.d.ts +595 -0
- package/dist/schemas/products.d.ts.map +1 -0
- package/{src/schemas/products.ts → dist/schemas/products.js} +113 -136
- package/dist/schemas/publish-tokens.d.ts +55 -0
- package/dist/schemas/publish-tokens.d.ts.map +1 -0
- package/{src/schemas/publish-tokens.ts → dist/schemas/publish-tokens.js} +18 -29
- package/dist/schemas/recommendations.d.ts +52 -0
- package/dist/schemas/recommendations.d.ts.map +1 -0
- package/dist/schemas/recommendations.js +58 -0
- package/dist/schemas/related.d.ts +331 -0
- package/dist/schemas/related.d.ts.map +1 -0
- package/dist/schemas/related.js +115 -0
- package/dist/schemas/releases.d.ts +623 -0
- package/dist/schemas/releases.d.ts.map +1 -0
- package/dist/schemas/releases.js +414 -0
- package/dist/schemas/resolve.d.ts +259 -0
- package/dist/schemas/resolve.d.ts.map +1 -0
- package/{src/schemas/resolve.ts → dist/schemas/resolve.js} +2 -5
- package/dist/schemas/search.d.ts +475 -0
- package/dist/schemas/search.d.ts.map +1 -0
- package/dist/schemas/search.js +258 -0
- package/dist/schemas/semantic-alert-summary.d.ts +54 -0
- package/dist/schemas/semantic-alert-summary.d.ts.map +1 -0
- package/dist/schemas/semantic-alert-summary.js +57 -0
- package/dist/schemas/semantic-alerts.d.ts +41 -0
- package/dist/schemas/semantic-alerts.d.ts.map +1 -0
- package/dist/schemas/semantic-alerts.js +42 -0
- package/dist/schemas/shared.d.ts +247 -0
- package/dist/schemas/shared.d.ts.map +1 -0
- package/dist/schemas/shared.js +248 -0
- package/dist/schemas/site-notice.d.ts +35 -0
- package/dist/schemas/site-notice.d.ts.map +1 -0
- package/{src/schemas/site-notice.ts → dist/schemas/site-notice.js} +14 -18
- package/dist/schemas/sitemap.d.ts +97 -0
- package/dist/schemas/sitemap.d.ts.map +1 -0
- package/dist/schemas/sitemap.js +107 -0
- package/dist/schemas/sources.d.ts +1145 -0
- package/dist/schemas/sources.d.ts.map +1 -0
- package/dist/schemas/sources.js +645 -0
- package/dist/schemas/stats.d.ts +82 -0
- package/dist/schemas/stats.d.ts.map +1 -0
- package/dist/schemas/stats.js +59 -0
- package/dist/schemas/taxonomy.d.ts +344 -0
- package/dist/schemas/taxonomy.d.ts.map +1 -0
- package/{src/schemas/taxonomy.ts → dist/schemas/taxonomy.js} +47 -58
- package/dist/schemas/webhooks.d.ts +618 -0
- package/dist/schemas/webhooks.d.ts.map +1 -0
- package/dist/schemas/webhooks.js +191 -0
- package/dist/schemas/well-known.d.ts +302 -0
- package/dist/schemas/well-known.d.ts.map +1 -0
- package/dist/schemas/well-known.js +192 -0
- package/dist/schemas/workspace-integrations.d.ts +27 -0
- package/dist/schemas/workspace-integrations.d.ts.map +1 -0
- package/dist/schemas/workspace-integrations.js +22 -0
- package/package.json +16 -4
- package/src/api-types.ts +0 -2223
- package/src/schemas/account-profile.ts +0 -28
- package/src/schemas/ai-models.test.ts +0 -29
- package/src/schemas/classifications.ts +0 -109
- package/src/schemas/collections.ts +0 -346
- package/src/schemas/errors.test.ts +0 -55
- package/src/schemas/errors.ts +0 -81
- package/src/schemas/feedback.ts +0 -56
- package/src/schemas/listing.ts +0 -226
- package/src/schemas/orgs.ts +0 -533
- package/src/schemas/recommendations.ts +0 -39
- package/src/schemas/related.ts +0 -126
- package/src/schemas/releases.ts +0 -436
- package/src/schemas/search.ts +0 -274
- package/src/schemas/semantic-alert-summary.ts +0 -65
- package/src/schemas/shared.notice.test.ts +0 -36
- package/src/schemas/shared.ts +0 -267
- package/src/schemas/site-notice.test.ts +0 -67
- package/src/schemas/sitemap.ts +0 -114
- package/src/schemas/sources.ts +0 -674
- package/src/schemas/stats.ts +0 -62
- package/src/schemas/well-known.test.ts +0 -264
- package/src/schemas/well-known.ts +0 -252
- package/src/schemas/workspace-integrations.ts +0 -26
package/src/schemas/search.ts
DELETED
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { KIND_VALUES } from "@buildinternet/releases-core/kinds";
|
|
3
|
-
import {
|
|
4
|
-
AppStoreSourceInfoSchema,
|
|
5
|
-
ImportanceScoreSchema,
|
|
6
|
-
MediaItemSchema,
|
|
7
|
-
OrgStatusSchema,
|
|
8
|
-
ReleaseThumbnailSchema,
|
|
9
|
-
ReleaseTypeSchema,
|
|
10
|
-
VideoSourceInfoSchema,
|
|
11
|
-
} from "./shared.js";
|
|
12
|
-
import { SourceTypeSchema } from "./sources.js";
|
|
13
|
-
import { LookupStatusSchema } from "./lookups.js";
|
|
14
|
-
import { CollectionMemberSchema } from "./collections.js";
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Org hit on the unified `/v1/search` response. The list is built either
|
|
18
|
-
* from `searchOrgs` LIKE matches or, when `?domain=` resolves, from a
|
|
19
|
-
* single-row "scoped org" projection — both produce the same wire shape.
|
|
20
|
-
*
|
|
21
|
-
* `category` is loose (`z.string().nullable()`) rather than the canonical
|
|
22
|
-
* `CategorySchema` enum because the scoped-org projection passes the raw
|
|
23
|
-
* DB column through without going through `resolveCategoryInput`; legacy
|
|
24
|
-
* rows may carry deprecated slugs that haven't been migrated yet. Tighten
|
|
25
|
-
* once #689 (category overlay backfill) lands across the prod data set.
|
|
26
|
-
*/
|
|
27
|
-
export const SearchOrgHitSchema = z.object({
|
|
28
|
-
slug: z.string(),
|
|
29
|
-
name: z.string(),
|
|
30
|
-
domain: z.string().nullable(),
|
|
31
|
-
avatarUrl: z.string().nullable(),
|
|
32
|
-
category: z.string().nullable(),
|
|
33
|
-
/**
|
|
34
|
-
* Org tier (#1947/#2034). Optional for mid-deploy tolerance; absent ⇒
|
|
35
|
-
* `"tracked"`. Lets search render the icon-only stub marker (#2031 parity).
|
|
36
|
-
*/
|
|
37
|
-
status: OrgStatusSchema.optional(),
|
|
38
|
-
/**
|
|
39
|
-
* Org-level alternate domains (product-scoped aliases excluded, sorted),
|
|
40
|
-
* for the `+N` hover on the origin domain — the anti-impersonation anchor
|
|
41
|
-
* mirrored from the catalog row (#2031/#2034). Optional; absent ⇒ none.
|
|
42
|
-
*/
|
|
43
|
-
aliasDomains: z.array(z.string()).optional(),
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Unified catalog entry — either a product row or a standalone source
|
|
48
|
-
* presented as product-shaped. `entryType` discriminates between the two
|
|
49
|
-
* row shapes (`"product"` vs `"source"`). `kind` carries the entity
|
|
50
|
-
* taxonomy (platform/sdk/mobile/…). `entryType` (not `type`) because
|
|
51
|
-
* source rows already carry `type: github|scrape|feed|agent` on the wire.
|
|
52
|
-
*/
|
|
53
|
-
export const SearchCatalogHitSchema = z.object({
|
|
54
|
-
slug: z.string(),
|
|
55
|
-
name: z.string(),
|
|
56
|
-
orgSlug: z.string().nullable(),
|
|
57
|
-
orgName: z.string().nullable(),
|
|
58
|
-
/** Owning org's avatar URL, for a small inline byline avatar. Products have
|
|
59
|
-
* no avatar of their own, so the web row anchors on the org's instead. */
|
|
60
|
-
orgAvatarUrl: z.string().nullable().optional(),
|
|
61
|
-
// Loose `z.string().nullable()` rather than the canonical `CategorySchema`
|
|
62
|
-
// enum because `foldSourcesIntoCatalog` and the catalog-search handlers
|
|
63
|
-
// pass `s.productCategory` / `o.category` through without validating
|
|
64
|
-
// against `resolveCategoryInput`. Legacy rows may carry deprecated slugs
|
|
65
|
-
// that haven't been migrated yet. Tighten once #689 (category overlay
|
|
66
|
-
// backfill) lands across the prod data set.
|
|
67
|
-
category: z.string().nullable(),
|
|
68
|
-
entryType: z.enum(["product", "source"]),
|
|
69
|
-
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
70
|
-
sourceSlug: z.string().optional(),
|
|
71
|
-
sourceType: SourceTypeSchema.optional(),
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
export const SearchSourceHitSchema = z.object({
|
|
75
|
-
slug: z.string(),
|
|
76
|
-
name: z.string(),
|
|
77
|
-
type: SourceTypeSchema,
|
|
78
|
-
orgSlug: z.string().nullable(),
|
|
79
|
-
orgName: z.string().nullable(),
|
|
80
|
-
productSlug: z.string().nullable(),
|
|
81
|
-
stars: z.number().int().min(0).nullable().optional(),
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
export const SearchReleaseHitSchema = z.object({
|
|
85
|
-
id: z.string(),
|
|
86
|
-
sourceSlug: z.string(),
|
|
87
|
-
sourceName: z.string(),
|
|
88
|
-
// Loose `z.string()` rather than `SourceTypeSchema` because the
|
|
89
|
-
// release-hit hydration path reads `sources.type` straight from the DB
|
|
90
|
-
// without re-validating; legacy rows occasionally drift from the
|
|
91
|
-
// canonical four-value set. Used by the web byline icon.
|
|
92
|
-
sourceType: z.string().optional(),
|
|
93
|
-
/**
|
|
94
|
-
* App Store platform + icon for `type: "appstore"` sources, null/absent
|
|
95
|
-
* otherwise. Powers the compact app-update treatment on the search card. #1206
|
|
96
|
-
*/
|
|
97
|
-
appStore: AppStoreSourceInfoSchema.nullable().optional(),
|
|
98
|
-
/**
|
|
99
|
-
* Video provider tag for `type: "video"` sources, absent otherwise. Powers
|
|
100
|
-
* the thumbnail-forward video row on search cards. #video
|
|
101
|
-
*/
|
|
102
|
-
video: VideoSourceInfoSchema.nullable().optional(),
|
|
103
|
-
orgSlug: z.string().nullable(),
|
|
104
|
-
orgName: z.string().nullable().optional(),
|
|
105
|
-
/** Owning product slug — present when the source belongs to a product. Lets
|
|
106
|
-
* the web byline link to the product page instead of the source. */
|
|
107
|
-
productSlug: z.string().nullable().optional(),
|
|
108
|
-
version: z.string().nullable(),
|
|
109
|
-
title: z.string(),
|
|
110
|
-
summary: z.string(),
|
|
111
|
-
titleGenerated: z.string().nullable().optional(),
|
|
112
|
-
titleShort: z.string().nullable().optional(),
|
|
113
|
-
/**
|
|
114
|
-
* The release's upstream source URL, when it has one. Lets the web link
|
|
115
|
-
* search/lookup hits straight to the original page instead of the
|
|
116
|
-
* noindexed `/release/<id>` stub — see `releaseLinkProps()` in
|
|
117
|
-
* `apps/web/src/lib/release-link.ts`. Optional for older servers / mid-deploy
|
|
118
|
-
* pin tolerance.
|
|
119
|
-
*/
|
|
120
|
-
url: z.string().nullable().optional(),
|
|
121
|
-
/**
|
|
122
|
-
* AI-scored importance 1–5 (5=landmark, 1=housekeeping). Null when
|
|
123
|
-
* unscored; optional for older servers / mid-deploy pin tolerance.
|
|
124
|
-
*/
|
|
125
|
-
importance: ImportanceScoreSchema,
|
|
126
|
-
/**
|
|
127
|
-
* Full markdown body. Omitted on list hits by default (summary + media
|
|
128
|
-
* cover the card/snippet surfaces). Opt in with `?include_content=true`
|
|
129
|
-
* on `/v1/search`. Fetch full body via `GET /v1/releases/:id` otherwise.
|
|
130
|
-
*/
|
|
131
|
-
content: z.string().optional(),
|
|
132
|
-
media: z.array(MediaItemSchema).optional(),
|
|
133
|
-
publishedAt: z.string().nullable(),
|
|
134
|
-
// Hybrid fusion score; absent on pure-lexical results.
|
|
135
|
-
score: z.number().optional(),
|
|
136
|
-
type: ReleaseTypeSchema.optional(),
|
|
137
|
-
coverageCount: z.number().int().min(0).optional(),
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Collection hit on the unified `/v1/search` response. Two origins are folded
|
|
142
|
-
* into the same wire shape via the `via` discriminator:
|
|
143
|
-
*
|
|
144
|
-
* - `direct` — the collection itself matched (name/description via LIKE in
|
|
145
|
-
* every mode, plus vector match in hybrid/semantic mode).
|
|
146
|
-
* - `member` — the collection was surfaced because one of the orgs that
|
|
147
|
-
* *did* match the query is a member of it; `matchedOrgSlugs`
|
|
148
|
-
* carries the subset of result-set org slugs that triggered
|
|
149
|
-
* the rollup so the UI can render an "includes X, Y" hint.
|
|
150
|
-
*
|
|
151
|
-
* `direct` rows always sort ahead of `member` rows; ties break on
|
|
152
|
-
* `score` then `memberCount`. Slugs are unique across the array — a
|
|
153
|
-
* direct hit that's also a member rollup keeps only the `direct` row
|
|
154
|
-
* with `matchedOrgSlugs` carried over.
|
|
155
|
-
*/
|
|
156
|
-
export const SearchCollectionHitSchema = z.object({
|
|
157
|
-
slug: z.string(),
|
|
158
|
-
name: z.string(),
|
|
159
|
-
description: z.string().nullable(),
|
|
160
|
-
memberCount: z.number().int().min(0),
|
|
161
|
-
via: z.enum(["direct", "member"]),
|
|
162
|
-
/** Hybrid/semantic fusion score; absent on pure-lexical hits. */
|
|
163
|
-
score: z.number().optional(),
|
|
164
|
-
/** Result-set org slugs that triggered this rollup. Always present on `via=member`. */
|
|
165
|
-
matchedOrgSlugs: z.array(z.string()).optional(),
|
|
166
|
-
/**
|
|
167
|
-
* A small org preview (capped at 3) for an inline avatar facepile on the
|
|
168
|
-
* search card, mirroring the collections list page. Org-kind only — search's
|
|
169
|
-
* `memberCount` counts orgs, so the facepile and its "+N more" stay
|
|
170
|
-
* consistent. Attached after hit merge; optional on the wire so older workers
|
|
171
|
-
* mid-rollout don't trip the schema.
|
|
172
|
-
*/
|
|
173
|
-
previewMembers: z.array(CollectionMemberSchema).optional(),
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Heading-aware CHANGELOG.md slice returned by hybrid / semantic search.
|
|
178
|
-
* Clients can deep-link to
|
|
179
|
-
* `/<orgSlug>/<sourceSlug>/changelog?offset=<offset>#chunk` to read the
|
|
180
|
-
* surrounding file content (the web `chunkDeepLink` builder; falls back to
|
|
181
|
-
* the `/source/<sourceSlug>` redirect shim when `orgSlug` is null).
|
|
182
|
-
*/
|
|
183
|
-
export const SearchChunkHitSchema = z.object({
|
|
184
|
-
sourceSlug: z.string(),
|
|
185
|
-
sourceName: z.string(),
|
|
186
|
-
orgSlug: z.string().nullable(),
|
|
187
|
-
orgName: z.string().nullable().optional(),
|
|
188
|
-
filePath: z.string(),
|
|
189
|
-
offset: z.number().int().min(0),
|
|
190
|
-
length: z.number().int().min(0),
|
|
191
|
-
heading: z.string().nullable(),
|
|
192
|
-
snippet: z.string(),
|
|
193
|
-
score: z.number(),
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Slim wire payload embedded in a search response when the query is a
|
|
198
|
-
* GitHub `org/repo` coordinate and no existing entity matched. Reuses
|
|
199
|
-
* `LookupStatusSchema` from `./lookups.js` rather than redefining — the
|
|
200
|
-
* thick `POST /v1/lookups` response and the slim search-embedded payload
|
|
201
|
-
* share the same status set.
|
|
202
|
-
*/
|
|
203
|
-
export const LookupResultPayloadSchema = z.object({
|
|
204
|
-
status: LookupStatusSchema,
|
|
205
|
-
source: z
|
|
206
|
-
.object({
|
|
207
|
-
id: z.string(),
|
|
208
|
-
slug: z.string(),
|
|
209
|
-
name: z.string(),
|
|
210
|
-
url: z.string(),
|
|
211
|
-
discovery: z.enum(["curated", "agent", "on_demand"]),
|
|
212
|
-
stars: z.number().int().min(0).nullable().optional(),
|
|
213
|
-
})
|
|
214
|
-
.optional(),
|
|
215
|
-
releases: z
|
|
216
|
-
.array(
|
|
217
|
-
z.object({
|
|
218
|
-
id: z.string(),
|
|
219
|
-
version: z.string().nullable(),
|
|
220
|
-
title: z.string(),
|
|
221
|
-
publishedAt: z.string().nullable(),
|
|
222
|
-
/** First image/gif thumbnail; null when the release has none. */
|
|
223
|
-
thumbnail: ReleaseThumbnailSchema.nullable().optional(),
|
|
224
|
-
/** The release's upstream source URL, when it has one. See
|
|
225
|
-
* `SearchReleaseHitSchema.url`. */
|
|
226
|
-
url: z.string().nullable().optional(),
|
|
227
|
-
}),
|
|
228
|
-
)
|
|
229
|
-
.optional(),
|
|
230
|
-
// Unambiguous "did you mean" rail; null when the org segment matches
|
|
231
|
-
// multiple curated orgs or none.
|
|
232
|
-
relatedOrg: z
|
|
233
|
-
.object({
|
|
234
|
-
org: z.object({ id: z.string(), slug: z.string(), name: z.string() }),
|
|
235
|
-
sources: z.array(
|
|
236
|
-
z.object({ id: z.string(), slug: z.string(), name: z.string(), url: z.string() }),
|
|
237
|
-
),
|
|
238
|
-
})
|
|
239
|
-
.nullable(),
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
export const UnifiedSearchResponseSchema = z.object({
|
|
243
|
-
query: z.string(),
|
|
244
|
-
// Normalized `?domain=` echo + resolution outcome. Both absent when no
|
|
245
|
-
// domain filter was applied.
|
|
246
|
-
domain: z.string().optional(),
|
|
247
|
-
domainStatus: z.enum(["matched", "not_found"]).optional(),
|
|
248
|
-
// Normalized `?category=` echo. Present only when a category filter was
|
|
249
|
-
// applied; the resolved canonical slug (aliases are folded to canonical).
|
|
250
|
-
// `categoryStatus` is always `matched` because an unknown category is a 400,
|
|
251
|
-
// not an empty envelope — categories are a fixed enum, unlike collections.
|
|
252
|
-
category: z.string().optional(),
|
|
253
|
-
categoryStatus: z.enum(["matched"]).optional(),
|
|
254
|
-
// Normalized `?collection=` echo + resolution outcome. `not_found` when the
|
|
255
|
-
// slug matched no collection (empty envelope, mirroring `domainStatus`).
|
|
256
|
-
collection: z.string().optional(),
|
|
257
|
-
collectionStatus: z.enum(["matched", "not_found"]).optional(),
|
|
258
|
-
orgs: z.array(SearchOrgHitSchema),
|
|
259
|
-
catalog: z.array(SearchCatalogHitSchema),
|
|
260
|
-
sources: z.array(SearchSourceHitSchema),
|
|
261
|
-
releases: z.array(SearchReleaseHitSchema),
|
|
262
|
-
/**
|
|
263
|
-
* Curated collections matching the query, either directly (name/description
|
|
264
|
-
* match or vector hit) or via member rollup (one of the result orgs is in
|
|
265
|
-
* the collection). Optional on the wire so older workers mid-rollout don't
|
|
266
|
-
* trip the schema check; clients should treat missing and `[]` identically.
|
|
267
|
-
*/
|
|
268
|
-
collections: z.array(SearchCollectionHitSchema).optional(),
|
|
269
|
-
chunks: z.array(SearchChunkHitSchema).optional(),
|
|
270
|
-
mode: z.enum(["lexical", "semantic", "hybrid"]).optional(),
|
|
271
|
-
degraded: z.boolean().optional(),
|
|
272
|
-
degradedReason: z.string().optional(),
|
|
273
|
-
lookup: LookupResultPayloadSchema.nullable().optional(),
|
|
274
|
-
});
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Admin interest-alert quality (`GET /v1/admin/semantic-alerts/summary`).
|
|
5
|
-
* Decisions live in the classifications Analytics Engine dataset with
|
|
6
|
-
* `blob4 = semantic-alert`. This is the hydrated read model. Alert query
|
|
7
|
-
* text is not a field — it is not stored on the points.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const ProbabilityBinSchema = z.object({
|
|
11
|
-
/** Inclusive. Width is 0.1. `0.8` is the start of the default-threshold bin. */
|
|
12
|
-
start: z.number(),
|
|
13
|
-
/** Exclusive, except the last bin (`0.9`–`1`) which includes `1`. */
|
|
14
|
-
end: z.number(),
|
|
15
|
-
count: z.number(),
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
const SemanticAlertDispositionCountsSchema = z.object({
|
|
19
|
-
matched: z.number(),
|
|
20
|
-
belowThreshold: z.number(),
|
|
21
|
-
failed: z.number(),
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* `GET /v1/admin/semantic-alerts/summary`.
|
|
26
|
-
*
|
|
27
|
-
* `scored` is matched + below threshold (a probability came back).
|
|
28
|
-
* `matchRate` is matched / scored, or null when nothing was scored.
|
|
29
|
-
* `failed` is fail-closed and stays out of the rate.
|
|
30
|
-
* Cost is not on these points; `meta.costLane` names the `ai_usage` lane.
|
|
31
|
-
*/
|
|
32
|
-
export const SemanticAlertSummarySchema = z.object({
|
|
33
|
-
after: z.string(),
|
|
34
|
-
before: z.string(),
|
|
35
|
-
bucket: z.enum(["hour", "day"]),
|
|
36
|
-
totals: SemanticAlertDispositionCountsSchema.extend({
|
|
37
|
-
scored: z.number(),
|
|
38
|
-
matchRate: z.number().nullable(),
|
|
39
|
-
}),
|
|
40
|
-
series: z.array(
|
|
41
|
-
SemanticAlertDispositionCountsSchema.extend({
|
|
42
|
-
t: z.string(),
|
|
43
|
-
}),
|
|
44
|
-
),
|
|
45
|
-
failures: z.array(
|
|
46
|
-
z.object({
|
|
47
|
-
category: z.enum(["provider_error", "invalid_probability", "unknown"]),
|
|
48
|
-
count: z.number(),
|
|
49
|
-
}),
|
|
50
|
-
),
|
|
51
|
-
probability: z.object({
|
|
52
|
-
/** Default alert threshold. Each point stores its own threshold separately. */
|
|
53
|
-
defaultThreshold: z.literal(0.8),
|
|
54
|
-
bins: z.array(ProbabilityBinSchema),
|
|
55
|
-
/** Sampled rows whose probability double is `< 0` (the absent sentinel). */
|
|
56
|
-
missing: z.number(),
|
|
57
|
-
}),
|
|
58
|
-
meta: z.object({
|
|
59
|
-
dataset: z.string(),
|
|
60
|
-
retentionDays: z.literal(90),
|
|
61
|
-
sampled: z.literal(true),
|
|
62
|
-
/** `ai_usage` lane that carries JEV cost. Not summed from these points. */
|
|
63
|
-
costLane: z.literal("semantic-alert-match"),
|
|
64
|
-
}),
|
|
65
|
-
});
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "bun:test";
|
|
2
|
-
import { NoticeSchema } from "./shared";
|
|
3
|
-
|
|
4
|
-
describe("NoticeSchema", () => {
|
|
5
|
-
it("accepts a message-only notice", () => {
|
|
6
|
-
expect(NoticeSchema.safeParse({ message: "Heads up" }).success).toBe(true);
|
|
7
|
-
});
|
|
8
|
-
it("accepts a message + internal coordinate", () => {
|
|
9
|
-
expect(
|
|
10
|
-
NoticeSchema.safeParse({
|
|
11
|
-
message: "Moved",
|
|
12
|
-
coordinate: "cognition/devin",
|
|
13
|
-
linkText: "View Devin",
|
|
14
|
-
}).success,
|
|
15
|
-
).toBe(true);
|
|
16
|
-
});
|
|
17
|
-
it("accepts a message + external href", () => {
|
|
18
|
-
expect(NoticeSchema.safeParse({ message: "Moved", href: "https://devin.ai" }).success).toBe(
|
|
19
|
-
true,
|
|
20
|
-
);
|
|
21
|
-
});
|
|
22
|
-
it("rejects both coordinate and href", () => {
|
|
23
|
-
expect(
|
|
24
|
-
NoticeSchema.safeParse({ message: "x", coordinate: "a/b", href: "https://x.com" }).success,
|
|
25
|
-
).toBe(false);
|
|
26
|
-
});
|
|
27
|
-
it("rejects an empty message and an over-long message", () => {
|
|
28
|
-
expect(NoticeSchema.safeParse({ message: "" }).success).toBe(false);
|
|
29
|
-
expect(NoticeSchema.safeParse({ message: "x".repeat(281) }).success).toBe(false);
|
|
30
|
-
});
|
|
31
|
-
it("rejects a malformed coordinate", () => {
|
|
32
|
-
expect(NoticeSchema.safeParse({ message: "x", coordinate: "/leading" }).success).toBe(false);
|
|
33
|
-
expect(NoticeSchema.safeParse({ message: "x", coordinate: "a/b/c" }).success).toBe(false);
|
|
34
|
-
expect(NoticeSchema.safeParse({ message: "x", coordinate: "" }).success).toBe(false);
|
|
35
|
-
});
|
|
36
|
-
});
|
package/src/schemas/shared.ts
DELETED
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { RELEASE_TYPES } from "@buildinternet/releases-core/schema";
|
|
3
|
-
import { BREAKING_LEVELS } from "@buildinternet/releases-core/breaking";
|
|
4
|
-
import { CATEGORIES } from "@buildinternet/releases-core/categories";
|
|
5
|
-
import { isValidNoticeCoordinate } from "@buildinternet/releases-core/notice";
|
|
6
|
-
import { IMPORTANCE_MAX, IMPORTANCE_MIN } from "@buildinternet/releases-core/importance";
|
|
7
|
-
|
|
8
|
-
export const CategorySchema = z.enum(CATEGORIES);
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* AI-scored release importance, 1 (housekeeping) to 5 (landmark). Scored at
|
|
12
|
-
* ingest; `.nullable().optional()` — null when unscored, absent on older
|
|
13
|
-
* servers/pinned workers.
|
|
14
|
-
*/
|
|
15
|
-
export const ImportanceScoreSchema = z
|
|
16
|
-
.number()
|
|
17
|
-
.int()
|
|
18
|
-
.min(IMPORTANCE_MIN)
|
|
19
|
-
.max(IMPORTANCE_MAX)
|
|
20
|
-
.nullable()
|
|
21
|
-
.optional();
|
|
22
|
-
|
|
23
|
-
export const MediaItemSchema = z.object({
|
|
24
|
-
type: z.enum(["image", "video", "gif"]),
|
|
25
|
-
url: z.string(),
|
|
26
|
-
alt: z.string().optional(),
|
|
27
|
-
// R2 object key (`releases/<hash>.<ext>`) for media mirrored into the
|
|
28
|
-
// `released-media` bucket; read paths resolve it to a same-origin `r2Url`
|
|
29
|
-
// (`resolveR2Url`). Stamped by `processMediaForR2` at ingest / manual edit and
|
|
30
|
-
// part of the stored `media[]` JSON — surfaced on the wire so a curator round-
|
|
31
|
-
// tripping `media` through `PATCH /v1/releases/:id` can preserve an already-
|
|
32
|
-
// mirrored item without forcing a re-fetch.
|
|
33
|
-
r2Key: z.string().optional(),
|
|
34
|
-
r2Url: z.string().optional(),
|
|
35
|
-
// For `type: "video"` items promoted from a hosted-video link found inline in
|
|
36
|
-
// a release body (Wistia/Loom/Vimeo/YouTube), `url` holds the poster/thumbnail
|
|
37
|
-
// (mirrored to R2 like any image) and `linkUrl` holds the human watch URL the
|
|
38
|
-
// play-card links out to. Absent for ordinary image/gif media. See
|
|
39
|
-
// `detectInlineVideos` in `@releases/rendering/video-embed` and web's
|
|
40
|
-
// release-content video card.
|
|
41
|
-
linkUrl: z.string().optional(),
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* App Store platform + icon for a `type: "appstore"` source. Threaded onto
|
|
46
|
-
* read surfaces so the UI can render the compact app-update row (app icon +
|
|
47
|
-
* "Available for iOS/macOS") instead of the standard version/notes/thumbnail
|
|
48
|
-
* layout. Sourced from `source.metadata.appStore` server-side. #mobile-appstore-feed-row
|
|
49
|
-
*/
|
|
50
|
-
export const AppStoreSourceInfoSchema = z.object({
|
|
51
|
-
platform: z.enum(["ios", "macos"]),
|
|
52
|
-
iconUrl: z.string().nullable(),
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
/** Video provider tag, present only when `sourceType === "video"`. Thumbnail
|
|
56
|
-
* and watch URL reuse the release's existing `media[]` / `url`. */
|
|
57
|
-
export const VideoSourceInfoSchema = z.object({
|
|
58
|
-
provider: z.enum(["youtube", "vimeo", "wistia"]),
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Org tier surfaced on read paths (#1947). `stub` = known only by declared
|
|
63
|
-
* release locations (no processed sources yet); `tracked` = a normal org whose
|
|
64
|
-
* sources fetch. The column is `organizations.tier`; the wire name is `status`.
|
|
65
|
-
* Optional on the wire so a pinned client mid-deploy tolerates its absence —
|
|
66
|
-
* treat a missing value as `"tracked"`.
|
|
67
|
-
*/
|
|
68
|
-
export const OrgStatusSchema = z.enum(["stub", "tracked"]);
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* A declared release location (#1947) — one `release_locations` row on the
|
|
72
|
-
* wire, the same shape a releases.json `releases[]` entry carries plus its
|
|
73
|
-
* provenance `basis` and, once promoted, the `sourceId` it materialized into.
|
|
74
|
-
* Surfaced on stub orgs so an agent gets "release info is published at these
|
|
75
|
-
* locations" instead of an empty sources list.
|
|
76
|
-
*/
|
|
77
|
-
export const ReleaseLocationItemSchema = z.object({
|
|
78
|
-
url: z.string().optional(),
|
|
79
|
-
feed: z.string().optional(),
|
|
80
|
-
github: z.string().optional(),
|
|
81
|
-
appstore: z.string().optional(),
|
|
82
|
-
file: z.string().optional(),
|
|
83
|
-
title: z.string().nullable().optional(),
|
|
84
|
-
canonical: z.boolean(),
|
|
85
|
-
basis: z.enum(["curator", "declared", "detected", "generated"]),
|
|
86
|
-
/** Owning product id when the locator is product-scoped, else null. */
|
|
87
|
-
productId: z.string().nullable(),
|
|
88
|
-
/** Set once the locator has been promoted into a source, else null. */
|
|
89
|
-
sourceId: z.string().nullable(),
|
|
90
|
-
/** `"push"` when the owner publishes this changelog instead of us polling it. */
|
|
91
|
-
publish: z.literal("push").optional(),
|
|
92
|
-
/** Changelog file or glob. Present with `publish: "push"`. */
|
|
93
|
-
path: z.string().optional(),
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
export const PaginationSchema = z.object({
|
|
97
|
-
page: z.number().int().min(1),
|
|
98
|
-
pageSize: z.number().int().min(1),
|
|
99
|
-
returned: z.number().int().min(0),
|
|
100
|
-
totalItems: z.number().int().min(0).optional(),
|
|
101
|
-
totalPages: z.number().int().min(0).optional(),
|
|
102
|
-
hasMore: z.boolean(),
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
export const ListResponseSchema = <T extends z.ZodTypeAny>(item: T) =>
|
|
106
|
-
z.object({
|
|
107
|
-
items: z.array(item),
|
|
108
|
-
pagination: PaginationSchema,
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
export const StatsSchema = z.object({
|
|
112
|
-
orgs: z.number().int().min(0),
|
|
113
|
-
sources: z.number().int().min(0),
|
|
114
|
-
releases: z.number().int().min(0),
|
|
115
|
-
products: z.number().int().min(0),
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
export const ReleaseTypeSchema = z.enum(RELEASE_TYPES);
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Per-category item counts produced by the AI release-content pass and
|
|
122
|
-
* persisted on `releases.metadata.composition`. Surfaces as a chip on the
|
|
123
|
-
* release detail UI ("12 fixes · 3 features"); `null` when the row hasn't
|
|
124
|
-
* been through the AI pass yet, the body was empty/boilerplate, or all
|
|
125
|
-
* three counts came back zero (we filter that case out on the parse side).
|
|
126
|
-
*/
|
|
127
|
-
export const ReleaseCompositionSchema = z.object({
|
|
128
|
-
bugs: z.number().int().min(0),
|
|
129
|
-
features: z.number().int().min(0),
|
|
130
|
-
enhancements: z.number().int().min(0),
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
export const ReleaseItemSchema = z.object({
|
|
134
|
-
id: z.string().optional(),
|
|
135
|
-
version: z.string().nullable(),
|
|
136
|
-
title: z.string(),
|
|
137
|
-
summary: z.string(),
|
|
138
|
-
content: z.string().optional(),
|
|
139
|
-
publishedAt: z.string().nullable(),
|
|
140
|
-
// ISO timestamp of when the row was fetched/inserted — used by the feed
|
|
141
|
-
// cursor's tie-break (publishedAt|fetchedAt|id) so that same-`publishedAt`
|
|
142
|
-
// rows stay stably ordered across paginated requests. `.optional()` because
|
|
143
|
-
// not every endpoint that returns a ReleaseItem populates it (notably
|
|
144
|
-
// single-release reads).
|
|
145
|
-
fetchedAt: z.string().optional(),
|
|
146
|
-
url: z.string().nullable(),
|
|
147
|
-
media: z.array(MediaItemSchema).optional(),
|
|
148
|
-
// `.optional()` — older API responses (mid-deploy or pinned old workers)
|
|
149
|
-
// may omit `type`; consumers should treat `undefined` as `"feature"`.
|
|
150
|
-
type: ReleaseTypeSchema.optional(),
|
|
151
|
-
// Pre-release flag (alpha/beta/rc/preview/nightly). Computed at ingest —
|
|
152
|
-
// GitHub uses the API's authoritative `prerelease` field; other adapters
|
|
153
|
-
// fall back to a SemVer-prerelease regex. `.optional()` for the same
|
|
154
|
-
// older-response degrade-gracefully reason as `type`.
|
|
155
|
-
prerelease: z.boolean().optional(),
|
|
156
|
-
// AI-generated headline forms paired with `summary` (#852, renamed in #860).
|
|
157
|
-
// Both nullable — most rows in the DB have neither populated, and we don't
|
|
158
|
-
// plan to backfill, so consumers must fall back to `title` for display.
|
|
159
|
-
// `.optional()` for the same mid-deploy / pinned-worker reason as `type`.
|
|
160
|
-
titleGenerated: z.string().nullable().optional(),
|
|
161
|
-
titleShort: z.string().nullable().optional(),
|
|
162
|
-
// Machine-readable breaking-change level (#1696, surfaced on read paths in
|
|
163
|
-
// #1710). `.optional()` for mid-deploy / pinned-worker tolerance and because
|
|
164
|
-
// rows predating the column omit it; absent or `"unknown"` means not
|
|
165
|
-
// classified (the fail-open default). List paths carry `breaking` only —
|
|
166
|
-
// `migrationNotes` stays detail-route-only to keep list payloads slim.
|
|
167
|
-
breaking: z.enum(BREAKING_LEVELS).optional(),
|
|
168
|
-
importance: ImportanceScoreSchema,
|
|
169
|
-
// Count of demoted siblings rolling up into this row via `release_coverage`
|
|
170
|
-
// (0 when standalone). Lets list views show a cluster indicator without a
|
|
171
|
-
// detail-page round trip. `.optional()` — older API responses and pinned
|
|
172
|
-
// workers omit the field; treat undefined as 0.
|
|
173
|
-
coverageCount: z.number().int().min(0).optional(),
|
|
174
|
-
// Cached `LENGTH(content)` and `countTokensSafe(content)` for the release
|
|
175
|
-
// body — lets feed surfaces ("releases get <org>", `/v1/releases/latest`,
|
|
176
|
-
// MCP `get_latest_releases`) advertise "this release is ~1.5K tokens" so
|
|
177
|
-
// agents can decide whether to pull the full body. `.optional()` and
|
|
178
|
-
// nullable because pre-existing rows landed without the columns; the
|
|
179
|
-
// backfill script populates them and renderers degrade to "size unknown"
|
|
180
|
-
// when null. See #958.
|
|
181
|
-
contentChars: z.number().int().min(0).nullable().optional(),
|
|
182
|
-
contentTokens: z.number().int().min(0).nullable().optional(),
|
|
183
|
-
// Per-category item counts produced by the AI release-content pass and
|
|
184
|
-
// stored on `releases.metadata.composition`. `null`/`undefined` when the
|
|
185
|
-
// row hasn't been through the AI pass yet or the body was boilerplate.
|
|
186
|
-
composition: ReleaseCompositionSchema.nullable().optional(),
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
export const ReleaseSummaryItemSchema = z.object({
|
|
190
|
-
year: z.number().int().nullable().optional(),
|
|
191
|
-
month: z.number().int().nullable().optional(),
|
|
192
|
-
windowDays: z.number().int().nullable().optional(),
|
|
193
|
-
summary: z.string(),
|
|
194
|
-
releaseCount: z.number().int().min(0),
|
|
195
|
-
generatedAt: z.string(),
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* A source attached to an overview page (#846, reshaped #1934). Each row names
|
|
200
|
-
* a source the overview drew on — `sourceUrl` plus, when it resolved to an
|
|
201
|
-
* on-registry release, `releaseId` + a canonical `releaseWebUrl`. This is a
|
|
202
|
-
* source *list*, not span-anchored provenance: the old body char offsets +
|
|
203
|
-
* verbatim quote are removed. Optional everywhere it appears — consumers should
|
|
204
|
-
* degrade gracefully.
|
|
205
|
-
*/
|
|
206
|
-
export const OverviewCitationSchema = z.object({
|
|
207
|
-
sourceUrl: z.string(),
|
|
208
|
-
title: z.string().nullable().optional(),
|
|
209
|
-
/** Best-effort link to the on-registry release this source maps to (resolved server-side). */
|
|
210
|
-
releaseId: z.string().nullable().optional(),
|
|
211
|
-
/**
|
|
212
|
-
* Canonical web URL of the on-registry release page for this source, when
|
|
213
|
-
* `releaseId` resolved (#1934). Kept for machine consumers; the web Sources
|
|
214
|
-
* footer links `sourceUrl` (the #2218 link policy — `/release/*` pages are
|
|
215
|
-
* noindexed).
|
|
216
|
-
*/
|
|
217
|
-
releaseWebUrl: z.string().nullable().optional(),
|
|
218
|
-
/**
|
|
219
|
-
* @deprecated Body char offsets / verbatim quote — removed with span-anchored
|
|
220
|
-
* citations (#1934). Kept optional for wire back-compat; no producer emits
|
|
221
|
-
* them and no consumer should read them.
|
|
222
|
-
*/
|
|
223
|
-
startIndex: z.number().int().min(0).optional(),
|
|
224
|
-
endIndex: z.number().int().min(0).optional(),
|
|
225
|
-
citedText: z.string().optional(),
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Entity notice — a small curator-set note on an org / product / source. Stored
|
|
230
|
-
* under the `notice` key of the entity's metadata JSON; mirrors the `Notice`
|
|
231
|
-
* type in @buildinternet/releases-core/notice. `coordinate` is an internal
|
|
232
|
-
* registry path ("org" / "org/slug"); `href` is an external URL; at most one.
|
|
233
|
-
*/
|
|
234
|
-
export const NoticeSchema = z
|
|
235
|
-
.object({
|
|
236
|
-
message: z.string().min(1).max(280),
|
|
237
|
-
linkText: z.string().min(1).max(60).optional(),
|
|
238
|
-
coordinate: z.string().min(1).max(200).optional(),
|
|
239
|
-
href: z.url().max(500).optional(),
|
|
240
|
-
})
|
|
241
|
-
.refine((n) => !(n.coordinate && n.href), {
|
|
242
|
-
message: "notice may set coordinate or href, not both",
|
|
243
|
-
})
|
|
244
|
-
.refine((n) => !n.coordinate || isValidNoticeCoordinate(n.coordinate), {
|
|
245
|
-
message: "notice.coordinate must be 'org' or 'org/slug'",
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Display thumbnail derived server-side from a release's first image/gif media
|
|
250
|
-
* entry. Shared by the coverage, lookup, and related-release payloads.
|
|
251
|
-
*/
|
|
252
|
-
export const ReleaseThumbnailSchema = z.object({
|
|
253
|
-
url: z.string(),
|
|
254
|
-
alt: z.string().optional(),
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
export const OverviewPageItemSchema = z.object({
|
|
258
|
-
scope: z.enum(["org", "product"]),
|
|
259
|
-
orgSlug: z.string().nullable().optional(),
|
|
260
|
-
productSlug: z.string().nullable().optional(),
|
|
261
|
-
content: z.string(),
|
|
262
|
-
releaseCount: z.number().int().min(0),
|
|
263
|
-
lastContributingReleaseAt: z.string().nullable(),
|
|
264
|
-
generatedAt: z.string(),
|
|
265
|
-
updatedAt: z.string(),
|
|
266
|
-
citations: z.array(OverviewCitationSchema).optional(),
|
|
267
|
-
});
|