@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buildinternet/releases-api-types",
3
- "version": "0.20.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.21.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 `kind: "source"`.
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) => ({ ...p, kind: "product" }));
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
- kind: "product",
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
- kind: "source",
684
+ entryType: "source",
685
+ kind: (s.entityKind as SearchCatalogHit["kind"]) ?? undefined,
676
686
  sourceSlug: s.slug,
677
687
  sourceType: s.type,
678
688
  });
@@ -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({
@@ -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
- export const OrgListResponseSchema = ListResponseSchema(OrgListItemSchema);
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
- kind: z.literal("product"),
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
- kind: z.literal("source"),
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
- * `kind` discriminates between the two row shapes. Products carry
128
- * `category` and `description`; sources carry `type` and `productId`.
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("kind", [
157
+ export const OrgCatalogItemSchema = z.discriminatedUnion("entryType", [
131
158
  OrgCatalogProductItemSchema,
132
159
  OrgCatalogSourceItemSchema,
133
160
  ]);
@@ -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
  /**
@@ -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. `kind` routes clicks to the right URL but
28
- * the two forms are otherwise interchangeable for display. `kind` (not
29
- * `type`) because source rows already carry
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
- kind: z.enum(["product", "source"]),
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
  });
@@ -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
- ListResponseSchema,
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: PaginationSchema,
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({