@buildinternet/releases-api-types 0.20.0 → 0.22.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 +14 -4
- package/src/schemas/lookups.ts +2 -0
- package/src/schemas/orgs.ts +33 -6
- package/src/schemas/products.ts +5 -0
- package/src/schemas/search.ts +7 -5
- package/src/schemas/sources.ts +20 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buildinternet/releases-api-types",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.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.22.0",
|
|
21
21
|
"zod": "^4.4.3"
|
|
22
22
|
},
|
|
23
23
|
"publishConfig": {
|
package/src/api-types.ts
CHANGED
|
@@ -60,6 +60,7 @@ import type {
|
|
|
60
60
|
SourceListResponseSchema,
|
|
61
61
|
SourceListResultSchema,
|
|
62
62
|
SourceDetailSchema,
|
|
63
|
+
SourceFeedPaginationSchema,
|
|
63
64
|
SourceMutationResponseSchema,
|
|
64
65
|
SourceOrgRefSchema,
|
|
65
66
|
SourcePatchInputSchema,
|
|
@@ -268,6 +269,7 @@ export {
|
|
|
268
269
|
SourceListResponseSchema,
|
|
269
270
|
SourceListResultSchema,
|
|
270
271
|
SourceDetailSchema,
|
|
272
|
+
SourceFeedPaginationSchema,
|
|
271
273
|
SourceMutationResponseSchema,
|
|
272
274
|
SourceOrgRefSchema,
|
|
273
275
|
SourcePatchInputSchema,
|
|
@@ -508,6 +510,7 @@ export type CreateSourceBody = z.infer<typeof CreateSourceBodySchema>;
|
|
|
508
510
|
export type ChangelogFileSummary = z.infer<typeof ChangelogFileSummarySchema>;
|
|
509
511
|
export type SourceChangelogResponse = z.infer<typeof SourceChangelogResponseSchema>;
|
|
510
512
|
export type SourceDetail = z.infer<typeof SourceDetailSchema>;
|
|
513
|
+
export type SourceFeedPagination = z.infer<typeof SourceFeedPaginationSchema>;
|
|
511
514
|
export type SourceMutationResponse = z.infer<typeof SourceMutationResponseSchema>;
|
|
512
515
|
export type SourceOrgRef = z.infer<typeof SourceOrgRefSchema>;
|
|
513
516
|
export type SourceActivityResponse = z.infer<typeof SourceActivityResponseSchema>;
|
|
@@ -641,17 +644,22 @@ export type SearchSourceHit = z.infer<typeof SearchSourceHitSchema>;
|
|
|
641
644
|
export interface RawSourceHit extends SearchSourceHit {
|
|
642
645
|
productName?: string;
|
|
643
646
|
productCategory?: string;
|
|
647
|
+
/** Entity taxonomy kind from the `sources.kind` column. */
|
|
648
|
+
entityKind?: string | null;
|
|
644
649
|
}
|
|
645
650
|
|
|
646
651
|
/**
|
|
647
652
|
* Fold source hits into the catalog list. Sources under a matched product
|
|
648
|
-
* are dropped (product wins); orphan sources become `
|
|
653
|
+
* are dropped (product wins); orphan sources become `entryType: "source"`.
|
|
649
654
|
*/
|
|
650
655
|
export function foldSourcesIntoCatalog(
|
|
651
656
|
existingProducts: SearchCatalogHit[],
|
|
652
657
|
rawSources: RawSourceHit[],
|
|
653
658
|
): SearchCatalogHit[] {
|
|
654
|
-
const result: SearchCatalogHit[] = existingProducts.map((p) => ({
|
|
659
|
+
const result: SearchCatalogHit[] = existingProducts.map((p) => ({
|
|
660
|
+
...p,
|
|
661
|
+
entryType: "product" as const,
|
|
662
|
+
}));
|
|
655
663
|
const seen = new Set(result.map((p) => p.slug));
|
|
656
664
|
for (const s of rawSources) {
|
|
657
665
|
if (s.productSlug) {
|
|
@@ -662,7 +670,8 @@ export function foldSourcesIntoCatalog(
|
|
|
662
670
|
orgSlug: s.orgSlug,
|
|
663
671
|
orgName: s.orgName,
|
|
664
672
|
category: s.productCategory ?? null,
|
|
665
|
-
|
|
673
|
+
entryType: "product",
|
|
674
|
+
kind: undefined,
|
|
666
675
|
});
|
|
667
676
|
seen.add(s.productSlug);
|
|
668
677
|
} else {
|
|
@@ -672,7 +681,8 @@ export function foldSourcesIntoCatalog(
|
|
|
672
681
|
orgSlug: s.orgSlug,
|
|
673
682
|
orgName: s.orgName,
|
|
674
683
|
category: null,
|
|
675
|
-
|
|
684
|
+
entryType: "source",
|
|
685
|
+
kind: (s.entityKind as SearchCatalogHit["kind"]) ?? undefined,
|
|
676
686
|
sourceSlug: s.slug,
|
|
677
687
|
sourceType: s.type,
|
|
678
688
|
});
|
package/src/schemas/lookups.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { KIND_VALUES } from "@buildinternet/releases-core/kinds";
|
|
2
3
|
import { CategorySchema } from "./shared.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -114,6 +115,7 @@ export const DomainLookupProductSchema = z.object({
|
|
|
114
115
|
// path resolves through `resolveCategoryInput` before persisting — unlike
|
|
115
116
|
// the search catalog category, which passes the column through raw.
|
|
116
117
|
category: CategorySchema.nullable(),
|
|
118
|
+
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
117
119
|
});
|
|
118
120
|
|
|
119
121
|
export const DomainLookupResponseSchema = z.object({
|
package/src/schemas/orgs.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { KIND_VALUES } from "@buildinternet/releases-core/kinds";
|
|
2
3
|
import {
|
|
3
4
|
CategorySchema,
|
|
4
5
|
ListResponseSchema,
|
|
5
6
|
OverviewPageItemSchema,
|
|
7
|
+
PaginationSchema,
|
|
6
8
|
ReleaseItemSchema,
|
|
7
9
|
} from "./shared.js";
|
|
8
10
|
import { SourceListItemSchema } from "./sources.js";
|
|
@@ -25,7 +27,28 @@ export const OrgListItemSchema = z.object({
|
|
|
25
27
|
sparkline: z.array(z.number().int().min(0)).length(30),
|
|
26
28
|
});
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Per-request side-channel for the orgs list (#746). `emptyOrgCount` is the
|
|
32
|
+
* number of orgs that match the same `q` filter but have zero indexed
|
|
33
|
+
* releases — used to label a "Show empty orgs" toggle without a second
|
|
34
|
+
* round-trip. The value is scoped by the search term but independent of
|
|
35
|
+
* `?includeEmpty=` so the toggle CTA can render a count whether the current
|
|
36
|
+
* response included empty orgs or not.
|
|
37
|
+
*/
|
|
38
|
+
export const OrgListMetaSchema = z.object({
|
|
39
|
+
emptyOrgCount: z.number().int().min(0),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* `meta` is optional on the wire so older workers / pinned clients mid-deploy
|
|
44
|
+
* don't trip the schema check. New clients can treat missing as
|
|
45
|
+
* `emptyOrgCount: 0` (the toggle just won't render a count).
|
|
46
|
+
*/
|
|
47
|
+
export const OrgListResponseSchema = z.object({
|
|
48
|
+
items: z.array(OrgListItemSchema),
|
|
49
|
+
pagination: PaginationSchema,
|
|
50
|
+
meta: OrgListMetaSchema.optional(),
|
|
51
|
+
});
|
|
29
52
|
|
|
30
53
|
export const OrgAccountItemSchema = z.object({
|
|
31
54
|
platform: z.string(),
|
|
@@ -103,7 +126,8 @@ const OrgDetailPlaybookSchema = z.object({
|
|
|
103
126
|
// ── Org catalog (/orgs/:slug/catalog) ──
|
|
104
127
|
|
|
105
128
|
const OrgCatalogProductItemSchema = z.object({
|
|
106
|
-
|
|
129
|
+
entryType: z.literal("product"),
|
|
130
|
+
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
107
131
|
id: z.string(),
|
|
108
132
|
slug: z.string(),
|
|
109
133
|
name: z.string(),
|
|
@@ -113,7 +137,8 @@ const OrgCatalogProductItemSchema = z.object({
|
|
|
113
137
|
});
|
|
114
138
|
|
|
115
139
|
const OrgCatalogSourceItemSchema = z.object({
|
|
116
|
-
|
|
140
|
+
entryType: z.literal("source"),
|
|
141
|
+
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
117
142
|
id: z.string(),
|
|
118
143
|
slug: z.string(),
|
|
119
144
|
name: z.string(),
|
|
@@ -124,10 +149,12 @@ const OrgCatalogSourceItemSchema = z.object({
|
|
|
124
149
|
|
|
125
150
|
/**
|
|
126
151
|
* Combined product + source catalog for `GET /v1/orgs/:slug/catalog`.
|
|
127
|
-
* `
|
|
128
|
-
* `
|
|
152
|
+
* `entryType` discriminates between the two row shapes (`"product"` vs
|
|
153
|
+
* `"source"`). `kind` carries the entity taxonomy (platform/sdk/mobile/…).
|
|
154
|
+
* Products carry `category` and `description`; sources carry `type` and
|
|
155
|
+
* `productId`.
|
|
129
156
|
*/
|
|
130
|
-
export const OrgCatalogItemSchema = z.discriminatedUnion("
|
|
157
|
+
export const OrgCatalogItemSchema = z.discriminatedUnion("entryType", [
|
|
131
158
|
OrgCatalogProductItemSchema,
|
|
132
159
|
OrgCatalogSourceItemSchema,
|
|
133
160
|
]);
|
package/src/schemas/products.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { KIND_VALUES } from "@buildinternet/releases-core/kinds";
|
|
2
3
|
import { CategorySchema, ListResponseSchema } from "./shared.js";
|
|
3
4
|
import { SourceTypeSchema } from "./sources.js";
|
|
4
5
|
|
|
@@ -19,6 +20,7 @@ export const ProductRowSchema = z.object({
|
|
|
19
20
|
url: z.string().nullable(),
|
|
20
21
|
description: z.string().nullable(),
|
|
21
22
|
category: CategorySchema.nullable(),
|
|
23
|
+
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
22
24
|
createdAt: z.string(),
|
|
23
25
|
embeddedAt: z.string().nullable(),
|
|
24
26
|
deletedAt: z.string().nullable(),
|
|
@@ -37,6 +39,7 @@ export const ProductListItemSchema = z.object({
|
|
|
37
39
|
url: z.string().nullable(),
|
|
38
40
|
description: z.string().nullable(),
|
|
39
41
|
category: CategorySchema.nullable(),
|
|
42
|
+
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
40
43
|
createdAt: z.string(),
|
|
41
44
|
sourceCount: z.number().int().min(0),
|
|
42
45
|
});
|
|
@@ -83,6 +86,7 @@ export const CreateProductBodySchema = z.object({
|
|
|
83
86
|
description: z.string().optional(),
|
|
84
87
|
category: z.string().min(1).optional(),
|
|
85
88
|
tags: z.array(z.string()).optional(),
|
|
89
|
+
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
86
90
|
});
|
|
87
91
|
|
|
88
92
|
/**
|
|
@@ -97,6 +101,7 @@ export const UpdateProductBodySchema = z.object({
|
|
|
97
101
|
category: z.string().min(1).nullable().optional(),
|
|
98
102
|
tags: z.array(z.string()).optional(),
|
|
99
103
|
aliases: z.array(z.string()).optional(),
|
|
104
|
+
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
100
105
|
});
|
|
101
106
|
|
|
102
107
|
/**
|
package/src/schemas/search.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { KIND_VALUES } from "@buildinternet/releases-core/kinds";
|
|
2
3
|
import { MediaItemSchema, ReleaseTypeSchema } from "./shared.js";
|
|
3
4
|
import { SourceTypeSchema } from "./sources.js";
|
|
4
5
|
import { LookupStatusSchema } from "./lookups.js";
|
|
@@ -24,10 +25,10 @@ export const SearchOrgHitSchema = z.object({
|
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* Unified catalog entry — either a product row or a standalone source
|
|
27
|
-
* presented as product-shaped. `
|
|
28
|
-
*
|
|
29
|
-
* `type`) because
|
|
30
|
-
* `type: github|scrape|feed|agent` on the wire.
|
|
28
|
+
* presented as product-shaped. `entryType` discriminates between the two
|
|
29
|
+
* row shapes (`"product"` vs `"source"`). `kind` carries the entity
|
|
30
|
+
* taxonomy (platform/sdk/mobile/…). `entryType` (not `type`) because
|
|
31
|
+
* source rows already carry `type: github|scrape|feed|agent` on the wire.
|
|
31
32
|
*/
|
|
32
33
|
export const SearchCatalogHitSchema = z.object({
|
|
33
34
|
slug: z.string(),
|
|
@@ -41,7 +42,8 @@ export const SearchCatalogHitSchema = z.object({
|
|
|
41
42
|
// that haven't been migrated yet. Tighten once #689 (category overlay
|
|
42
43
|
// backfill) lands across the prod data set.
|
|
43
44
|
category: z.string().nullable(),
|
|
44
|
-
|
|
45
|
+
entryType: z.enum(["product", "source"]),
|
|
46
|
+
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
45
47
|
sourceSlug: z.string().optional(),
|
|
46
48
|
sourceType: SourceTypeSchema.optional(),
|
|
47
49
|
});
|
package/src/schemas/sources.ts
CHANGED
|
@@ -4,12 +4,8 @@ import {
|
|
|
4
4
|
SOURCE_DISCOVERY,
|
|
5
5
|
SOURCE_FETCH_PRIORITIES,
|
|
6
6
|
} from "@buildinternet/releases-core/source-enums";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
PaginationSchema,
|
|
10
|
-
ReleaseItemSchema,
|
|
11
|
-
ReleaseSummaryItemSchema,
|
|
12
|
-
} from "./shared.js";
|
|
7
|
+
import { KIND_VALUES } from "@buildinternet/releases-core/kinds";
|
|
8
|
+
import { ListResponseSchema, ReleaseItemSchema, ReleaseSummaryItemSchema } from "./shared.js";
|
|
13
9
|
|
|
14
10
|
export const SourceTypeSchema = z.enum(SOURCE_TYPES);
|
|
15
11
|
export const SourceDiscoverySchema = z.enum(SOURCE_DISCOVERY);
|
|
@@ -53,6 +49,7 @@ export const SourceListItemSchema = z.object({
|
|
|
53
49
|
metadata: z.string().nullable().optional(),
|
|
54
50
|
productName: z.string().nullable().optional(),
|
|
55
51
|
productSlug: z.string().nullable().optional(),
|
|
52
|
+
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
56
53
|
});
|
|
57
54
|
|
|
58
55
|
/**
|
|
@@ -86,6 +83,7 @@ export const SourceWithOrgSchema = z.object({
|
|
|
86
83
|
nextFetchAfter: z.string().nullable(),
|
|
87
84
|
medianGapDays: z.number().nullable(),
|
|
88
85
|
lastRetieredAt: z.string().nullable(),
|
|
86
|
+
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
89
87
|
});
|
|
90
88
|
|
|
91
89
|
export const SourceListResponseSchema = ListResponseSchema(SourceWithOrgSchema);
|
|
@@ -105,6 +103,17 @@ const SourceDetailSummariesSchema = z.object({
|
|
|
105
103
|
monthly: z.array(ReleaseSummaryItemSchema),
|
|
106
104
|
});
|
|
107
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Cursor pagination shape for the source detail's embedded release feed.
|
|
108
|
+
* Mirrors `OrgFeedPaginationSchema` and `CollectionFeedPaginationSchema` —
|
|
109
|
+
* release lists are feed-shaped (append-only, mutates between calls), so they
|
|
110
|
+
* use cursor pagination per the AGENTS.md convention.
|
|
111
|
+
*/
|
|
112
|
+
export const SourceFeedPaginationSchema = z.object({
|
|
113
|
+
nextCursor: z.string().nullable(),
|
|
114
|
+
limit: z.number().int().min(1),
|
|
115
|
+
});
|
|
116
|
+
|
|
108
117
|
/**
|
|
109
118
|
* Resolved attribution block returned alongside a source row. `id` is
|
|
110
119
|
* additive vs the legacy `{ slug, name }` shape — older clients that ignored
|
|
@@ -149,8 +158,9 @@ export const SourceDetailSchema = z.object({
|
|
|
149
158
|
lastPolledAt: z.string().nullable(),
|
|
150
159
|
trackingSince: z.string(),
|
|
151
160
|
releases: z.array(ReleaseItemSchema),
|
|
152
|
-
pagination:
|
|
161
|
+
pagination: SourceFeedPaginationSchema,
|
|
153
162
|
summaries: SourceDetailSummariesSchema,
|
|
163
|
+
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
154
164
|
});
|
|
155
165
|
|
|
156
166
|
/**
|
|
@@ -181,6 +191,7 @@ export const SourceMutationResponseSchema = z.looseObject({
|
|
|
181
191
|
isHidden: z.boolean().nullable().optional(),
|
|
182
192
|
discovery: SourceDiscoverySchema.optional(),
|
|
183
193
|
createdAt: z.string().optional(),
|
|
194
|
+
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
184
195
|
});
|
|
185
196
|
|
|
186
197
|
/** Body accepted by `PATCH /v1/sources/:slug`. */
|
|
@@ -202,6 +213,7 @@ export const SourcePatchInputSchema = z.object({
|
|
|
202
213
|
isHidden: z.boolean().optional(),
|
|
203
214
|
changeDetectedAt: z.string().nullable().optional(),
|
|
204
215
|
lastPolledAt: z.string().nullable().optional(),
|
|
216
|
+
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
205
217
|
});
|
|
206
218
|
|
|
207
219
|
/** Body accepted by `POST /v1/sources`. */
|
|
@@ -222,6 +234,7 @@ export const CreateSourceBodySchema = z.object({
|
|
|
222
234
|
productSlug: z.string().optional(),
|
|
223
235
|
metadata: z.string().optional(),
|
|
224
236
|
isPrimary: z.boolean().optional(),
|
|
237
|
+
kind: z.enum(KIND_VALUES).nullable().optional(),
|
|
225
238
|
});
|
|
226
239
|
|
|
227
240
|
export const ChangelogFileSummarySchema = z.object({
|