Skip to content

Commit d6c4dc5

Browse files
committed
fix(registry): move registry config types to their own module
The new RegistryConfig + ExperimentalConfig interfaces lived alongside definePlugin's overloads in astro/integration/runtime.ts. tsdown + rolldown's chunking decided to inline a bigger subset of plugin-related types into the entry chunk as a result, which broke definePlugin() overload resolution for trusted plugins building against core's dist on CI (plugins/forms failed with 'id does not exist in type StandardPluginDefinition'). Move both types to packages/core/src/registry/types.ts (still re-exported from runtime.ts for backwards compatibility) so the chunking matches main's layout and definePlugin's overloads resolve as before.
1 parent e590f86 commit d6c4dc5

4 files changed

Lines changed: 143 additions & 129 deletions

File tree

packages/core/src/api/handlers/registry.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
import type { Handle } from "@atcute/lexicons";
4242
import type { Kysely } from "kysely";
4343

44-
import type { RegistryConfig } from "../../astro/integration/runtime.js";
4544
import type { Database } from "../../database/types.js";
4645
import { extractBundle } from "../../plugins/marketplace.js";
4746
import type { PluginBundle } from "../../plugins/marketplace.js";
@@ -54,6 +53,7 @@ import {
5453
validateAggregatorUrl,
5554
} from "../../registry/config.js";
5655
import { makeRegistryPluginId } from "../../registry/plugin-id.js";
56+
import type { RegistryConfig } from "../../registry/types.js";
5757
import { EmDashStorageError } from "../../storage/types.js";
5858
import type { Storage } from "../../storage/types.js";
5959
import type { ApiResult } from "../types.js";

packages/core/src/astro/integration/runtime.ts

Lines changed: 3 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,11 @@ import type { AuthDescriptor, AuthProviderDescriptor } from "../../auth/types.js
1111
import type { DatabaseDescriptor } from "../../db/adapters.js";
1212
import type { MediaProviderDescriptor } from "../../media/types.js";
1313
import type { ResolvedPlugin } from "../../plugins/types.js";
14+
import type { ExperimentalConfig } from "../../registry/types.js";
1415
import type { StorageDescriptor } from "../storage/types.js";
1516

17+
export type { ExperimentalConfig, RegistryConfig } from "../../registry/types.js";
18+
1619
export type { ResolvedPlugin };
1720
export type { MediaProviderDescriptor };
1821

@@ -125,133 +128,6 @@ export interface PluginDescriptor<TOptions = Record<string, unknown>> {
125128
export type SandboxedPluginDescriptor<TOptions = Record<string, unknown>> =
126129
PluginDescriptor<TOptions>;
127130

128-
/**
129-
* Experimental plugin registry configuration.
130-
*
131-
* See {@link ExperimentalConfig.registry}.
132-
*/
133-
export interface RegistryConfig {
134-
/**
135-
* Base URL of the registry aggregator (an atproto AppView that indexes
136-
* the firehose for `pm.fair.package.*` and `com.emdashcms.*` records).
137-
*
138-
* Must be the origin where the aggregator's XRPC endpoints are mounted,
139-
* such that `${aggregatorUrl}/xrpc/<nsid>` resolves to a valid endpoint.
140-
*
141-
* Must be HTTPS in production; `http://localhost` or `http://127.0.0.1`
142-
* are accepted in dev.
143-
*/
144-
aggregatorUrl: string;
145-
146-
/**
147-
* Optional comma-separated list of labeller DIDs forwarded as the
148-
* `atproto-accept-labelers` header on every aggregator request.
149-
*
150-
* Format follows the atproto convention:
151-
* `did:plc:abc;redact, did:plc:def`
152-
*
153-
* When unset, the aggregator applies its operator-default labeller set
154-
* (typically the EmDash publisher-verification labeller and any
155-
* additional trusted labellers the aggregator operator configured).
156-
*/
157-
acceptLabelers?: string;
158-
159-
/**
160-
* Site-level policy applied to the latest-release selection filter.
161-
*
162-
* These filters operate over the signed records the aggregator returns;
163-
* they are not protocol-level constraints. See the RFC's
164-
* "Update Discovery and Takedowns" section for the integration point.
165-
*/
166-
policy?: {
167-
/**
168-
* Hold back releases newer than this when computing the recommended
169-
* install or update version. Mitigates "compromised publisher
170-
* account pushes a malicious release of an established plugin" by
171-
* giving the takedown labeller a detection window.
172-
*
173-
* Accepts a duration string (`"24h"`, `"48h"`, `"72h"`, `"7d"`) or a
174-
* number of seconds.
175-
*
176-
* Currently applies uniformly to all releases. A future addition
177-
* may exempt brand-new packages (those with no prior release
178-
* history) so the holdback doesn't block first-time publishing,
179-
* but that exemption is not implemented yet; use
180-
* {@link minimumReleaseAgeExclude} to allowlist trusted publishers
181-
* whose packages should install immediately.
182-
*
183-
* Defaults to `undefined` (no holdback). A future trust/moderation
184-
* RFC will specify the recommended default.
185-
*/
186-
minimumReleaseAge?: string | number;
187-
188-
/**
189-
* Packages exempt from the {@link minimumReleaseAge} holdback. Use
190-
* for publishers whose release tempo you've explicitly accepted --
191-
* your own first-party plugins, a trusted partner, etc.
192-
*
193-
* Each entry is either:
194-
* - A bare publisher DID (e.g. `"did:plc:abc123"`) -- every
195-
* package from that publisher is exempt.
196-
* - A `<did>/<slug>` pair (e.g.
197-
* `"did:plc:abc123/hotfix-plugin"`) -- only that specific
198-
* package is exempt.
199-
*
200-
* Whole-publisher exemptions are the common case: trust is
201-
* naturally a property of the publisher, not of each individual
202-
* package. Per-package exemptions exist for cases where a publisher
203-
* has one plugin you want fast-track installs for and others you'd
204-
* rather hold back.
205-
*
206-
* Only DIDs are accepted -- not handles. Handles are mutable
207-
* aggregator-supplied envelope data, and accepting them as a
208-
* trust input would let a compromised aggregator bypass the
209-
* holdback by claiming any handle for any package. DIDs are
210-
* tied to the AT URI of the package record itself, so even a
211-
* compromised aggregator cannot lie about which DID published
212-
* a release.
213-
*
214-
* Mirrors pnpm's `minimumReleaseAgeExclude`.
215-
*
216-
* @example
217-
* ```ts
218-
* minimumReleaseAgeExclude: [
219-
* "did:plc:emdashfirstparty", // every package from this publisher
220-
* "did:plc:abc123/hotfix-plugin", // just this one package
221-
* ]
222-
* ```
223-
*/
224-
minimumReleaseAgeExclude?: readonly string[];
225-
};
226-
}
227-
228-
/**
229-
* Experimental EmDash features. See {@link EmDashConfig.experimental}.
230-
*
231-
* Each field is independently opt-in. Fields may be promoted out of
232-
* `experimental` (becoming top-level `EmDashConfig` options) or removed
233-
* in minor releases; check the changelog when upgrading.
234-
*/
235-
export interface ExperimentalConfig {
236-
/**
237-
* Decentralized plugin registry.
238-
*
239-
* When set, replaces the centralized `marketplace` for the admin UI's
240-
* browse and install flows. The registry is an atproto-backed
241-
* federation: package metadata lives in each publisher's PDS, an
242-
* aggregator (the `aggregatorUrl`) indexes the firehose and exposes
243-
* read-only XRPC endpoints for discovery, and EmDash verifies each
244-
* release against the publisher's signed records before installing.
245-
*
246-
* See [RFC 0001](https://github.com/emdash-cms/emdash/pull/694) for
247-
* the protocol design and threat model.
248-
*
249-
* Requires `sandboxRunner` to be configured -- registry plugins always
250-
* run sandboxed.
251-
*/
252-
registry?: RegistryConfig;
253-
}
254-
255131
export interface EmDashConfig {
256132
/**
257133
* Database configuration

packages/core/src/registry/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* seconds so the browser doesn't need a duration parser.
99
*/
1010

11-
import type { RegistryConfig } from "../astro/integration/runtime.js";
11+
import type { RegistryConfig } from "./types.js";
1212

1313
/**
1414
* Shape returned in the admin manifest's `registry` field. The browser
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/**
2+
* Public types for the experimental plugin registry.
3+
*
4+
* Kept in their own module so they don't get re-bundled into the
5+
* `astro/integration/runtime.ts` chunk's dist output. tsdown / rolldown
6+
* are sensitive to which top-level types live alongside `definePlugin`'s
7+
* overloads, and pulling these types into the integration module
8+
* affected downstream `definePlugin()` overload resolution for trusted
9+
* plugins built against core's dist (see commit history for the
10+
* detailed write-up).
11+
*/
12+
13+
/**
14+
* Experimental plugin registry configuration.
15+
*
16+
* See {@link ExperimentalConfig.registry}.
17+
*/
18+
export interface RegistryConfig {
19+
/**
20+
* Base URL of the registry aggregator (an atproto AppView that indexes
21+
* the firehose for `pm.fair.package.*` and `com.emdashcms.*` records).
22+
*
23+
* Must be the origin where the aggregator's XRPC endpoints are mounted,
24+
* such that `${aggregatorUrl}/xrpc/<nsid>` resolves to a valid endpoint.
25+
*
26+
* Must be HTTPS in production; `http://localhost` or `http://127.0.0.1`
27+
* are accepted in dev.
28+
*/
29+
aggregatorUrl: string;
30+
31+
/**
32+
* Optional comma-separated list of labeller DIDs forwarded as the
33+
* `atproto-accept-labelers` header on every aggregator request.
34+
*
35+
* Format follows the atproto convention:
36+
* `did:plc:abc;redact, did:plc:def`
37+
*
38+
* When unset, the aggregator applies its operator-default labeller set
39+
* (typically the EmDash publisher-verification labeller and any
40+
* additional trusted labellers the aggregator operator configured).
41+
*/
42+
acceptLabelers?: string;
43+
44+
/**
45+
* Site-level policy applied to the latest-release selection filter.
46+
*
47+
* These filters operate over the signed records the aggregator returns;
48+
* they are not protocol-level constraints. See the RFC's
49+
* "Update Discovery and Takedowns" section for the integration point.
50+
*/
51+
policy?: {
52+
/**
53+
* Hold back releases newer than this when computing the recommended
54+
* install or update version. Mitigates "compromised publisher
55+
* account pushes a malicious release of an established plugin" by
56+
* giving the takedown labeller a detection window.
57+
*
58+
* Accepts a duration string (`"24h"`, `"48h"`, `"72h"`, `"7d"`) or a
59+
* number of seconds.
60+
*
61+
* Currently applies uniformly to all releases. A future addition
62+
* may exempt brand-new packages (those with no prior release
63+
* history) so the holdback doesn't block first-time publishing,
64+
* but that exemption is not implemented yet; use
65+
* {@link minimumReleaseAgeExclude} to allowlist trusted publishers
66+
* whose packages should install immediately.
67+
*
68+
* Defaults to `undefined` (no holdback). A future trust/moderation
69+
* RFC will specify the recommended default.
70+
*/
71+
minimumReleaseAge?: string | number;
72+
73+
/**
74+
* Packages exempt from the {@link minimumReleaseAge} holdback. Use
75+
* for publishers whose release tempo you've explicitly accepted --
76+
* your own first-party plugins, a trusted partner, etc.
77+
*
78+
* Each entry is either:
79+
* - A bare publisher DID (e.g. `"did:plc:abc123"`) -- every
80+
* package from that publisher is exempt.
81+
* - A `<did>/<slug>` pair (e.g.
82+
* `"did:plc:abc123/hotfix-plugin"`) -- only that specific
83+
* package is exempt.
84+
*
85+
* Whole-publisher exemptions are the common case: trust is
86+
* naturally a property of the publisher, not of each individual
87+
* package. Per-package exemptions exist for cases where a publisher
88+
* has one plugin you want fast-track installs for and others you'd
89+
* rather hold back.
90+
*
91+
* Only DIDs are accepted -- not handles. Handles are mutable
92+
* aggregator-supplied envelope data, and accepting them as a
93+
* trust input would let a compromised aggregator bypass the
94+
* holdback by claiming any handle for any package. DIDs are
95+
* tied to the AT URI of the package record itself, so even a
96+
* compromised aggregator cannot lie about which DID published
97+
* a release.
98+
*
99+
* Mirrors pnpm's `minimumReleaseAgeExclude`.
100+
*
101+
* @example
102+
* ```ts
103+
* minimumReleaseAgeExclude: [
104+
* "did:plc:emdashfirstparty", // every package from this publisher
105+
* "did:plc:abc123/hotfix-plugin", // just this one package
106+
* ]
107+
* ```
108+
*/
109+
minimumReleaseAgeExclude?: readonly string[];
110+
};
111+
}
112+
113+
/**
114+
* Experimental EmDash features. See `EmDashConfig.experimental`.
115+
*
116+
* Each field is independently opt-in. Fields may be promoted out of
117+
* `experimental` (becoming top-level `EmDashConfig` options) or removed
118+
* in minor releases; check the changelog when upgrading.
119+
*/
120+
export interface ExperimentalConfig {
121+
/**
122+
* Decentralized plugin registry.
123+
*
124+
* When set, replaces the centralized `marketplace` for the admin UI's
125+
* browse and install flows. The registry is an atproto-backed
126+
* federation: package metadata lives in each publisher's PDS, an
127+
* aggregator (the `aggregatorUrl`) indexes the firehose and exposes
128+
* read-only XRPC endpoints for discovery, and EmDash verifies each
129+
* release against the publisher's signed records before installing.
130+
*
131+
* See [RFC 0001](https://github.com/emdash-cms/emdash/pull/694) for
132+
* the protocol design and threat model.
133+
*
134+
* Requires `sandboxRunner` to be configured -- registry plugins always
135+
* run sandboxed.
136+
*/
137+
registry?: RegistryConfig;
138+
}

0 commit comments

Comments
 (0)