@buildinternet/releases-api-types 0.27.0 → 0.29.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/package.json +2 -2
- package/src/api-types.ts +23 -1
- package/src/schemas/lookups.ts +2 -0
- package/src/schemas/orgs.ts +3 -0
- package/src/schemas/products.ts +3 -1
- package/src/schemas/search.ts +2 -0
- package/src/schemas/shared.notice.test.ts +36 -0
- package/src/schemas/shared.ts +21 -0
- package/src/schemas/sources.ts +23 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buildinternet/releases-api-types",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "Wire protocol — request/response types for the Releases registry API. Shared by web, workers, and clients.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"README.md"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@buildinternet/releases-core": "^0.
|
|
20
|
+
"@buildinternet/releases-core": "^0.23.0",
|
|
21
21
|
"zod": "^4.4.3"
|
|
22
22
|
},
|
|
23
23
|
"publishConfig": {
|
package/src/api-types.ts
CHANGED
|
@@ -250,6 +250,7 @@ export {
|
|
|
250
250
|
OverviewPageItemSchema,
|
|
251
251
|
OverviewCitationSchema,
|
|
252
252
|
CategorySchema,
|
|
253
|
+
NoticeSchema,
|
|
253
254
|
} from "./schemas/shared.js";
|
|
254
255
|
export {
|
|
255
256
|
OrgListItemSchema,
|
|
@@ -636,7 +637,10 @@ export interface StuckSource {
|
|
|
636
637
|
isHidden: boolean;
|
|
637
638
|
/** Non-`dry_run` attempts examined in the window (all errors when stuck). */
|
|
638
639
|
recentAttempts: number;
|
|
639
|
-
/**
|
|
640
|
+
/**
|
|
641
|
+
* Failed or degraded attempts (`error`, `crawl_timeout`, `blocked`) among the
|
|
642
|
+
* examined attempts (equals `recentAttempts` when stuck).
|
|
643
|
+
*/
|
|
640
644
|
recentErrors: number;
|
|
641
645
|
/** Most-recent fetch attempt timestamp (ISO), or null. */
|
|
642
646
|
lastAttemptAt: string | null;
|
|
@@ -1169,6 +1173,24 @@ export interface FetchLogEntry {
|
|
|
1169
1173
|
createdAt: string;
|
|
1170
1174
|
}
|
|
1171
1175
|
|
|
1176
|
+
/**
|
|
1177
|
+
* The managed-agent session currently fetching a source, surfaced read-side on
|
|
1178
|
+
* the fetch-log view so an operator/agent can tell "a fetch is in flight" from
|
|
1179
|
+
* "stuck/dead" without waiting for a terminal `fetch_log` row (#1360). Only
|
|
1180
|
+
* running, non-stale sessions are reported (the StatusHub DO drops sessions idle
|
|
1181
|
+
* past its staleness cutoff), so the presence of this object means a fetch is
|
|
1182
|
+
* live; `startedAt` says how long it has been running.
|
|
1183
|
+
*/
|
|
1184
|
+
export interface ActiveFetchSession {
|
|
1185
|
+
sessionId: string;
|
|
1186
|
+
/** Always `running` today — only running sessions are surfaced. */
|
|
1187
|
+
status: string;
|
|
1188
|
+
/** Session start, epoch ms. */
|
|
1189
|
+
startedAt: number;
|
|
1190
|
+
/** Last session activity, epoch ms. */
|
|
1191
|
+
lastUpdatedAt: number;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1172
1194
|
// ── Usage ──
|
|
1173
1195
|
|
|
1174
1196
|
export interface UsageBreakdownRow {
|
package/src/schemas/lookups.ts
CHANGED
|
@@ -38,6 +38,8 @@ export const LookupSourceSchema = z.looseObject({
|
|
|
38
38
|
isHidden: z.boolean().nullable().optional(),
|
|
39
39
|
discovery: z.enum(["curated", "agent", "on_demand"]).optional(),
|
|
40
40
|
metadata: z.string().nullable(),
|
|
41
|
+
stars: z.number().int().min(0).nullable().optional(),
|
|
42
|
+
starsFetchedAt: z.string().nullable().optional(),
|
|
41
43
|
createdAt: z.string().optional(),
|
|
42
44
|
});
|
|
43
45
|
|
package/src/schemas/orgs.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
AppStoreSourceInfoSchema,
|
|
5
5
|
CategorySchema,
|
|
6
6
|
ListResponseSchema,
|
|
7
|
+
NoticeSchema,
|
|
7
8
|
OverviewPageItemSchema,
|
|
8
9
|
PaginationSchema,
|
|
9
10
|
ReleaseItemSchema,
|
|
@@ -114,6 +115,7 @@ export const UpdateOrgBodySchema = z.object({
|
|
|
114
115
|
featured: z.boolean().optional(),
|
|
115
116
|
/** Admin-only: promote or demote the org's discovery status (curated/agent/on_demand). */
|
|
116
117
|
discovery: z.enum(["curated", "agent", "on_demand"]).optional(),
|
|
118
|
+
notice: NoticeSchema.nullable().optional(),
|
|
117
119
|
});
|
|
118
120
|
|
|
119
121
|
// Org detail's products query selects a strict subset of `ProductListItem` —
|
|
@@ -455,4 +457,5 @@ export const OrgDetailSchema = z.object({
|
|
|
455
457
|
sources: z.array(SourceListItemSchema),
|
|
456
458
|
overview: OverviewPageItemSchema.nullable().optional(),
|
|
457
459
|
playbook: OrgDetailPlaybookSchema.nullable().optional(),
|
|
460
|
+
notice: NoticeSchema.nullable().optional(),
|
|
458
461
|
});
|
package/src/schemas/products.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { KIND_VALUES } from "@buildinternet/releases-core/kinds";
|
|
3
|
-
import { CategorySchema, ListResponseSchema, ReleaseItemSchema } from "./shared.js";
|
|
3
|
+
import { CategorySchema, ListResponseSchema, NoticeSchema, ReleaseItemSchema } from "./shared.js";
|
|
4
4
|
import { SourceTypeSchema, SourceFeedPaginationSchema } from "./sources.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -89,6 +89,7 @@ export const ProductDetailSchema = ProductRowSchema.extend({
|
|
|
89
89
|
releases: z.array(ReleaseItemSchema).optional(),
|
|
90
90
|
/** Cursor pagination for the embedded release feed — set by the web format adapter (PR #1207), absent on bare product detail. */
|
|
91
91
|
pagination: SourceFeedPaginationSchema.optional(),
|
|
92
|
+
notice: NoticeSchema.nullable().optional(),
|
|
92
93
|
});
|
|
93
94
|
|
|
94
95
|
/**
|
|
@@ -125,6 +126,7 @@ export const UpdateProductBodySchema = z.object({
|
|
|
125
126
|
aliases: z.array(z.string()).optional(),
|
|
126
127
|
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
127
128
|
avatarUrl: z.string().nullable().optional(),
|
|
129
|
+
notice: NoticeSchema.nullable().optional(),
|
|
128
130
|
});
|
|
129
131
|
|
|
130
132
|
/**
|
package/src/schemas/search.ts
CHANGED
|
@@ -60,6 +60,7 @@ export const SearchSourceHitSchema = z.object({
|
|
|
60
60
|
orgSlug: z.string().nullable(),
|
|
61
61
|
orgName: z.string().nullable(),
|
|
62
62
|
productSlug: z.string().nullable(),
|
|
63
|
+
stars: z.number().int().min(0).nullable().optional(),
|
|
63
64
|
});
|
|
64
65
|
|
|
65
66
|
export const SearchReleaseHitSchema = z.object({
|
|
@@ -164,6 +165,7 @@ export const LookupResultPayloadSchema = z.object({
|
|
|
164
165
|
name: z.string(),
|
|
165
166
|
url: z.string(),
|
|
166
167
|
discovery: z.enum(["curated", "agent", "on_demand"]),
|
|
168
|
+
stars: z.number().int().min(0).nullable().optional(),
|
|
167
169
|
})
|
|
168
170
|
.optional(),
|
|
169
171
|
releases: z
|
|
@@ -0,0 +1,36 @@
|
|
|
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
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { RELEASE_TYPES } from "@buildinternet/releases-core/schema";
|
|
3
3
|
import { CATEGORIES } from "@buildinternet/releases-core/categories";
|
|
4
|
+
import { isValidNoticeCoordinate } from "@buildinternet/releases-core/notice";
|
|
4
5
|
|
|
5
6
|
export const CategorySchema = z.enum(CATEGORIES);
|
|
6
7
|
|
|
@@ -150,6 +151,26 @@ export const OverviewCitationSchema = z.object({
|
|
|
150
151
|
releaseId: z.string().nullable().optional(),
|
|
151
152
|
});
|
|
152
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Entity notice — a small curator-set note on an org / product / source. Stored
|
|
156
|
+
* under the `notice` key of the entity's metadata JSON; mirrors the `Notice`
|
|
157
|
+
* type in @buildinternet/releases-core/notice. `coordinate` is an internal
|
|
158
|
+
* registry path ("org" / "org/slug"); `href` is an external URL; at most one.
|
|
159
|
+
*/
|
|
160
|
+
export const NoticeSchema = z
|
|
161
|
+
.object({
|
|
162
|
+
message: z.string().min(1).max(280),
|
|
163
|
+
linkText: z.string().min(1).max(60).optional(),
|
|
164
|
+
coordinate: z.string().min(1).max(200).optional(),
|
|
165
|
+
href: z.url().max(500).optional(),
|
|
166
|
+
})
|
|
167
|
+
.refine((n) => !(n.coordinate && n.href), {
|
|
168
|
+
message: "notice may set coordinate or href, not both",
|
|
169
|
+
})
|
|
170
|
+
.refine((n) => !n.coordinate || isValidNoticeCoordinate(n.coordinate), {
|
|
171
|
+
message: "notice.coordinate must be 'org' or 'org/slug'",
|
|
172
|
+
});
|
|
173
|
+
|
|
153
174
|
export const OverviewPageItemSchema = z.object({
|
|
154
175
|
scope: z.enum(["org", "product"]),
|
|
155
176
|
orgSlug: z.string().nullable().optional(),
|
package/src/schemas/sources.ts
CHANGED
|
@@ -5,7 +5,12 @@ import {
|
|
|
5
5
|
SOURCE_FETCH_PRIORITIES,
|
|
6
6
|
} from "@buildinternet/releases-core/source-enums";
|
|
7
7
|
import { KIND_VALUES } from "@buildinternet/releases-core/kinds";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
ListResponseSchema,
|
|
10
|
+
NoticeSchema,
|
|
11
|
+
ReleaseItemSchema,
|
|
12
|
+
ReleaseSummaryItemSchema,
|
|
13
|
+
} from "./shared.js";
|
|
9
14
|
|
|
10
15
|
export const SourceTypeSchema = z.enum(SOURCE_TYPES);
|
|
11
16
|
export const SourceDiscoverySchema = z.enum(SOURCE_DISCOVERY);
|
|
@@ -50,6 +55,11 @@ export const SourceListItemSchema = z.object({
|
|
|
50
55
|
productName: z.string().nullable().optional(),
|
|
51
56
|
productSlug: z.string().nullable().optional(),
|
|
52
57
|
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
58
|
+
// GitHub stargazer count. `nullable().optional()` (the house idiom for these
|
|
59
|
+
// shapes): non-github sources and older responses omit it; a fetched row may
|
|
60
|
+
// carry `null`. Consumers treat null/undefined as "unknown".
|
|
61
|
+
stars: z.number().int().min(0).nullable().optional(),
|
|
62
|
+
starsFetchedAt: z.string().nullable().optional(),
|
|
53
63
|
});
|
|
54
64
|
|
|
55
65
|
/**
|
|
@@ -84,6 +94,11 @@ export const SourceWithOrgSchema = z.object({
|
|
|
84
94
|
medianGapDays: z.number().nullable(),
|
|
85
95
|
lastRetieredAt: z.string().nullable(),
|
|
86
96
|
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
97
|
+
// GitHub stargazer count. `nullable().optional()` (the house idiom for these
|
|
98
|
+
// shapes): non-github sources and older responses omit it; a fetched row may
|
|
99
|
+
// carry `null`. Consumers treat null/undefined as "unknown".
|
|
100
|
+
stars: z.number().int().min(0).nullable().optional(),
|
|
101
|
+
starsFetchedAt: z.string().nullable().optional(),
|
|
87
102
|
});
|
|
88
103
|
|
|
89
104
|
export const SourceListResponseSchema = ListResponseSchema(SourceWithOrgSchema);
|
|
@@ -161,6 +176,12 @@ export const SourceDetailSchema = z.object({
|
|
|
161
176
|
pagination: SourceFeedPaginationSchema,
|
|
162
177
|
summaries: SourceDetailSummariesSchema,
|
|
163
178
|
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
179
|
+
// GitHub stargazer count. `nullable().optional()` (the house idiom for these
|
|
180
|
+
// shapes): non-github sources and older responses omit it; a fetched row may
|
|
181
|
+
// carry `null`. Consumers treat null/undefined as "unknown".
|
|
182
|
+
stars: z.number().int().min(0).nullable().optional(),
|
|
183
|
+
starsFetchedAt: z.string().nullable().optional(),
|
|
184
|
+
notice: NoticeSchema.nullable().optional(),
|
|
164
185
|
});
|
|
165
186
|
|
|
166
187
|
/**
|
|
@@ -248,6 +269,7 @@ export const SourcePatchInputSchema = z.object({
|
|
|
248
269
|
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
249
270
|
/** Admin-only: promote or demote the source's discovery status (curated/agent/on_demand). */
|
|
250
271
|
discovery: SourceDiscoverySchema.optional(),
|
|
272
|
+
notice: NoticeSchema.nullable().optional(),
|
|
251
273
|
});
|
|
252
274
|
|
|
253
275
|
/** Body accepted by `POST /v1/sources`. */
|