@buildinternet/releases-api-types 0.57.1 → 0.58.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.57.1",
3
+ "version": "0.58.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.34.0",
20
+ "@buildinternet/releases-core": "^0.35.0",
21
21
  "zod": "^4.4.3"
22
22
  },
23
23
  "publishConfig": {
package/src/api-types.ts CHANGED
@@ -396,6 +396,7 @@ export {
396
396
  ListingValidateBodySchema,
397
397
  ListingActivateBodySchema,
398
398
  ListingIssueSchema,
399
+ ListingSetupStepSchema,
399
400
  ListingLocationSchema,
400
401
  ListingOrgPointerSchema,
401
402
  ListingValidationResultSchema,
@@ -419,6 +420,7 @@ export {
419
420
  type ListingValidateBody,
420
421
  type ListingActivateBody,
421
422
  type ListingIssue,
423
+ type ListingSetupStep,
422
424
  type ListingLocation,
423
425
  type ListingValidationResult,
424
426
  type ListingActivateResult,
@@ -24,15 +24,27 @@ export const ListingIssueSchema = z.strictObject({
24
24
  });
25
25
  export type ListingIssue = z.infer<typeof ListingIssueSchema>;
26
26
 
27
+ /** One follow-up the owner takes after declaring `publish: "push"`. */
28
+ export const ListingSetupStepSchema = z.strictObject({
29
+ id: z.enum(["verify", "token", "action"]),
30
+ title: z.string(),
31
+ detail: z.string(),
32
+ });
33
+ export type ListingSetupStep = z.infer<typeof ListingSetupStepSchema>;
34
+
27
35
  export const ListingLocationSchema = z.strictObject({
28
36
  /** The declared locator value (feed URL, github ref, url, appstore id, file). */
29
37
  locator: z.string(),
30
- kind: z.enum(["feed", "github", "appstore", "url", "file"]),
31
- classification: z.enum(["tier1-live", "tier2-paused-review"]),
38
+ kind: z.enum(["feed", "github", "appstore", "url", "file", "push"]),
39
+ classification: z.enum(["tier1-live", "tier2-paused-review", "push-setup"]),
32
40
  /** Plain-English "what this becomes" for the preview UI. */
33
41
  becomes: z.string(),
34
42
  /** Present when the locator is nested under a manifest product. */
35
43
  productName: z.string().optional(),
44
+ /** Changelog file or glob. Present on `kind: "push"`. */
45
+ path: z.string().optional(),
46
+ /** Present on `kind: "push"`: verify ownership, mint a token, add the Action. */
47
+ setupSteps: z.array(ListingSetupStepSchema).optional(),
36
48
  });
37
49
  export type ListingLocation = z.infer<typeof ListingLocationSchema>;
38
50
 
@@ -87,6 +87,10 @@ export const ReleaseLocationItemSchema = z.object({
87
87
  productId: z.string().nullable(),
88
88
  /** Set once the locator has been promoted into a source, else null. */
89
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(),
90
94
  });
91
95
 
92
96
  export const PaginationSchema = z.object({
@@ -122,6 +122,67 @@ describe("releases.json v2 schemas", () => {
122
122
  ).toBe(false);
123
123
  });
124
124
 
125
+ it("accepts a push-publish locator on github + path", () => {
126
+ expect(
127
+ ReleasesJsonDomainSchema.parse({
128
+ version: 2,
129
+ releases: [
130
+ { github: "acme/docs", path: "changelog/**/*.mdx", publish: "push", title: "Docs" },
131
+ ],
132
+ }),
133
+ ).toEqual({
134
+ version: 2,
135
+ releases: [
136
+ { github: "acme/docs", path: "changelog/**/*.mdx", publish: "push", title: "Docs" },
137
+ ],
138
+ });
139
+ expect(
140
+ ReleasesJsonRepoSchema.safeParse({
141
+ version: 2,
142
+ releases: [{ github: "self", path: "CHANGELOG.md", publish: "push" }],
143
+ }).success,
144
+ ).toBe(true);
145
+ });
146
+
147
+ it("rejects an incomplete or unsafe push-publish locator", () => {
148
+ expect(
149
+ ReleasesJsonDomainSchema.safeParse({
150
+ version: 2,
151
+ releases: [{ github: "acme/docs", publish: "push" }],
152
+ }).success,
153
+ ).toBe(false);
154
+ expect(
155
+ ReleasesJsonDomainSchema.safeParse({
156
+ version: 2,
157
+ releases: [{ path: "CHANGELOG.md", publish: "push" }],
158
+ }).success,
159
+ ).toBe(false);
160
+ expect(
161
+ ReleasesJsonDomainSchema.safeParse({
162
+ version: 2,
163
+ releases: [{ github: "acme/docs", path: "CHANGELOG.md" }],
164
+ }).success,
165
+ ).toBe(false);
166
+ expect(
167
+ ReleasesJsonDomainSchema.safeParse({
168
+ version: 2,
169
+ releases: [{ github: "acme/docs", path: "CHANGELOG.md", publish: "poll" }],
170
+ }).success,
171
+ ).toBe(false);
172
+ expect(
173
+ ReleasesJsonDomainSchema.safeParse({
174
+ version: 2,
175
+ releases: [{ github: "acme/docs", path: "../secrets", publish: "push" }],
176
+ }).success,
177
+ ).toBe(false);
178
+ expect(
179
+ ReleasesJsonDomainSchema.safeParse({
180
+ version: 2,
181
+ releases: [{ github: "acme/docs", path: "/etc/passwd", publish: "push" }],
182
+ }).success,
183
+ ).toBe(false);
184
+ });
185
+
125
186
  it("allows github self only in repo files", () => {
126
187
  expect(
127
188
  ReleasesJsonDomainSchema.safeParse({ version: 2, releases: [{ github: "self" }] }).success,
@@ -17,6 +17,19 @@ const HttpsUrlSchema = z.url().startsWith("https://", "locator must be an https
17
17
  /** Shared tag-array bounds — kept identical for org-level and product-level tags. */
18
18
  const TagsSchema = z.array(z.string().min(1).max(60)).max(50);
19
19
 
20
+ /**
21
+ * Changelog file or glob inside the declared repo (Action `changelog-path` /
22
+ * `changelog-glob`). Relative only — no scheme, no leading slash, no `..`.
23
+ */
24
+ const PublishPathSchema = z
25
+ .string()
26
+ .min(1)
27
+ .max(200)
28
+ .regex(
29
+ /^(?!\/)(?!.*(?:^|\/)\.\.(?:\/|$))[A-Za-z0-9._~@%+\-/*?[\]{}]+$/,
30
+ "path must be a relative changelog file or glob",
31
+ );
32
+
20
33
  const ReleaseLocationFields = {
21
34
  url: HttpsUrlSchema.optional(),
22
35
  feed: HttpsUrlSchema.optional(),
@@ -24,6 +37,13 @@ const ReleaseLocationFields = {
24
37
  file: HttpsUrlSchema.optional(),
25
38
  title: z.string().min(1).max(200).optional(),
26
39
  canonical: z.boolean().optional(),
40
+ /**
41
+ * `"push"` means the owner publishes this changelog (GitHub Action / publish
42
+ * token) instead of us polling it. Requires `github` + `path`.
43
+ */
44
+ publish: z.literal("push").optional(),
45
+ /** Required with `publish: "push"`. The file or glob the Action watches. */
46
+ path: PublishPathSchema.optional(),
27
47
  };
28
48
 
29
49
  function hasLocator(value: {
@@ -36,6 +56,12 @@ function hasLocator(value: {
36
56
  return Boolean(value.url || value.feed || value.github || value.appstore || value.file);
37
57
  }
38
58
 
59
+ /** `publish: "push"` is a mode on a github locator, not a locator by itself. */
60
+ function pushPublishComplete(value: { github?: string; publish?: "push"; path?: string }): boolean {
61
+ if (value.publish === "push") return Boolean(value.github && value.path);
62
+ return value.path === undefined;
63
+ }
64
+
39
65
  /** A domain manifest can name a concrete GitHub repository, but never `self`. */
40
66
  export const ReleasesJsonDomainReleaseSchema = z
41
67
  .strictObject({
@@ -45,7 +71,11 @@ export const ReleasesJsonDomainReleaseSchema = z
45
71
  .regex(/^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/, "github must be owner/repo")
46
72
  .optional(),
47
73
  })
48
- .refine(hasLocator, "at least one release locator is required");
74
+ .refine(hasLocator, "at least one release locator is required")
75
+ .refine(
76
+ pushPublishComplete,
77
+ 'publish: "push" requires github (owner/repo) and path (changelog file or glob)',
78
+ );
49
79
 
50
80
  /** A repo manifest additionally accepts `github: "self"`. */
51
81
  export const ReleasesJsonRepoReleaseSchema = z
@@ -58,7 +88,11 @@ export const ReleasesJsonRepoReleaseSchema = z
58
88
  ])
59
89
  .optional(),
60
90
  })
61
- .refine(hasLocator, "at least one release locator is required");
91
+ .refine(hasLocator, "at least one release locator is required")
92
+ .refine(
93
+ pushPublishComplete,
94
+ 'publish: "push" requires github (owner/repo or "self") and path (changelog file or glob)',
95
+ );
62
96
 
63
97
  function releasesArray<T extends z.ZodType>(item: T, max: number) {
64
98
  return z