Skip to content

Commit af9090f

Browse files
authored
fix: reduce executable registry size (#237)
## Summary - stop generating full executable action ID arrays in the Node and Cloudflare provider registries - use the loaded executor module service keys to identify locally executable providers - resolve those services to the exact action IDs present in the loaded catalog before creating `CatalogStore` - preserve explicit `executableActionIds`, runtime execution metadata, and public response shapes This is the first of the two changes discussed in #235. Catalog asset chunking will follow in a separate PR. ## Why The generated Cloudflare registry repeated all 12,743 executable action IDs even though each included provider already has a lazy executor module entry. The existing generator marks every action from an included provider executable, so the executor module service keys carry the same information more compactly. ## Wrangler size comparison Measured from `v1.3.3` and this branch with Wrangler 4.115.0, the same Cloudflare bindings/configuration, `--dry-run`, and `--minify`: | Build | Minified upload | Gzip | | --- | ---: | ---: | | `v1.3.3` baseline | 11,935.15 KiB | 2,854.70 KiB | | This PR | 11,566.36 KiB | 2,777.99 KiB | | Reduction | **368.79 KiB** | **76.71 KiB** | The emitted `cloudflare.js` decreased from 12,221,596 to 11,843,955 bytes, a reduction of 377,641 bytes. ## Validation - generated both provider registries for 1,194 total / 1,192 Cloudflare providers - `oxlint . --fix` - `oxfmt .` - `node scripts/typecheck.ts src scripts-all examples` - `vitest run` — 61 test files and 591 tests passed - Wrangler dry-run builds for both baseline and optimized revisions Refs #235
1 parent e86860c commit af9090f

6 files changed

Lines changed: 73 additions & 25 deletions

File tree

scripts/generate-provider-registry.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,6 @@ function propertyName(service: string): string {
2929

3030
async function writeRegistry(filename: string, sources: ProviderSource[]): Promise<void> {
3131
const services = sources.map((source) => source.service);
32-
const executableActionIds = new Map<string, string[]>(
33-
sources.map((source) => [
34-
source.service,
35-
source.definition.actions.map((action) => action.id).sort((a, b) => a.localeCompare(b)),
36-
]),
37-
);
3832
const lines = [
3933
'import type { ExecutorModule } from "./provider-loader.ts";',
4034
"",
@@ -44,15 +38,6 @@ async function writeRegistry(filename: string, sources: ProviderSource[]): Promi
4438
(service) => ` ${propertyName(service)}: (): Promise<ExecutorModule> => import("./${service}/executors.ts"),`,
4539
),
4640
"};",
47-
"",
48-
"/** Generated local executable action ids by provider. Do not hand-edit. */",
49-
"export const executableActionIds: Record<string, string[]> = {",
50-
...services.flatMap((service) => [
51-
` ${propertyName(service)}: [`,
52-
...(executableActionIds.get(service) ?? []).map((actionId) => ` ${JSON.stringify(actionId)},`),
53-
" ],",
54-
]),
55-
"};",
5641
];
5742

5843
const path = join(providersDir, filename);

src/catalog-store.test.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { ProviderDefinition } from "./core/types.ts";
22

33
import { describe, expect, it } from "vitest";
4-
import { createCatalogStore } from "./catalog-store.ts";
4+
import { createCatalogStore, resolveExecutableActionIds } from "./catalog-store.ts";
55

66
describe("catalog store", () => {
77
it("preserves optional provider descriptions without defaulting missing ones", () => {
@@ -72,4 +72,38 @@ describe("catalog store", () => {
7272
properties: { message: { type: "string" } },
7373
});
7474
});
75+
76+
it("resolves every action from executable services alongside explicit action ids", () => {
77+
const providers = [providerFixture("example", ["ping", "pong"]), providerFixture("remote", ["ping"])];
78+
79+
const catalog = createCatalogStore(providers, {
80+
executableActionIds: resolveExecutableActionIds(providers, {
81+
executableServices: ["example"],
82+
executableActionIds: ["remote.ping"],
83+
}),
84+
});
85+
86+
expect(catalog.executableActionIds).toEqual(new Set(["example.ping", "example.pong", "remote.ping"]));
87+
expect(catalog.actionsById.get("example.pong")?.execution.locallyExecutable).toBe(true);
88+
});
7589
});
90+
91+
function providerFixture(service: string, actionNames: string[]): ProviderDefinition {
92+
return {
93+
service,
94+
displayName: service,
95+
categories: ["Developer Tools"],
96+
authTypes: ["no_auth"],
97+
auth: [{ type: "no_auth" }],
98+
actions: actionNames.map((name) => ({
99+
id: `${service}.${name}`,
100+
service,
101+
name,
102+
description: `${name} action.`,
103+
requiredScopes: [],
104+
providerPermissions: [],
105+
inputSchema: {},
106+
outputSchema: {},
107+
})),
108+
};
109+
}

src/catalog-store.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,19 @@ export type CatalogStore = {
6969
executableActionIds: Set<string>;
7070
};
7171

72-
export interface LoadCatalogOptions {
72+
export interface CreateCatalogStoreOptions {
7373
executableActionIds?: Iterable<string>;
7474
}
7575

76-
export function createCatalogStore(providers: ProviderDefinition[], options: LoadCatalogOptions = {}): CatalogStore {
76+
export interface LoadCatalogOptions extends CreateCatalogStoreOptions {
77+
/** Mark every catalog action owned by these locally loaded provider services as executable. */
78+
executableServices?: Iterable<string>;
79+
}
80+
81+
export function createCatalogStore(
82+
providers: ProviderDefinition[],
83+
options: CreateCatalogStoreOptions = {},
84+
): CatalogStore {
7785
const sortedProviders = sortProviders(providers);
7886
const executableActions = new Set(options.executableActionIds ?? []);
7987
const runtimeProviders = sortedProviders.map((provider): RuntimeProviderDefinition => {
@@ -148,7 +156,26 @@ export async function loadCatalog(
148156
return JSON.parse(content) as ProviderDefinition;
149157
}),
150158
);
151-
return createCatalogStore(providers, options);
159+
return createCatalogStore(providers, {
160+
executableActionIds: resolveExecutableActionIds(providers, options),
161+
});
162+
}
163+
164+
/** Resolve provider-level executable services into the exact action ids present in a loaded catalog. */
165+
export function resolveExecutableActionIds(
166+
providers: ProviderDefinition[],
167+
options: LoadCatalogOptions = {},
168+
): Set<string> {
169+
const actionIds = new Set(options.executableActionIds ?? []);
170+
const services = new Set(options.executableServices ?? []);
171+
for (const provider of providers) {
172+
if (services.has(provider.service)) {
173+
for (const action of provider.actions) {
174+
actionIds.add(action.id);
175+
}
176+
}
177+
}
178+
return actionIds;
152179
}
153180

154181
function createActionExecutionStatus(

src/server/cloudflare.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type { ISecretCodec } from "./secrets/secret-codec-core.ts";
88
import { ActionPolicyService, parseActionPolicyList } from "../core/action-policy.ts";
99
import { parsePrivateNetworkAccessFlag, setPrivateNetworkAccessAllowed } from "../core/request.ts";
1010
import { ProviderLoader } from "../providers/provider-loader.ts";
11-
import { executableActionIds, executorModules } from "../providers/registry.cloudflare.generated.ts";
11+
import { executorModules } from "../providers/registry.cloudflare.generated.ts";
1212
import { isConsoleShellPath } from "./api/console-paths.ts";
1313
import { loadCatalogFromAssets } from "./cloudflare/catalog-assets.ts";
1414
import { readPositiveInteger, resolvePublicOrigin } from "./cloudflare/cloudflare-env.ts";
@@ -120,7 +120,7 @@ function writeWorkerLog(level: "error" | "info" | "warn"): (fields: unknown, mes
120120

121121
function loadCatalogOnce(assets: AssetsBinding): Promise<CatalogStore> {
122122
catalogPromise ??= loadCatalogFromAssets(assets, {
123-
executableActionIds: Object.values(executableActionIds).flat(),
123+
executableServices: Object.keys(executorModules),
124124
});
125125
return catalogPromise;
126126
}

src/server/cloudflare/catalog-assets.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { CatalogStore, LoadCatalogOptions } from "../../catalog-store.ts";
22
import type { ProviderDefinition } from "../../core/types.ts";
33
import type { AssetsBinding } from "./cloudflare-bindings.ts";
44

5-
import { createCatalogStore } from "../../catalog-store.ts";
5+
import { createCatalogStore, resolveExecutableActionIds } from "../../catalog-store.ts";
66

77
const catalogAssetPath = "/catalog/apps.json";
88

@@ -11,7 +11,9 @@ export async function loadCatalogFromAssets(
1111
options: LoadCatalogOptions = {},
1212
): Promise<CatalogStore> {
1313
const providers = (await readJsonAsset(assets, catalogAssetPath)) as ProviderDefinition[];
14-
return createCatalogStore(providers, options);
14+
return createCatalogStore(providers, {
15+
executableActionIds: resolveExecutableActionIds(providers, options),
16+
});
1517
}
1618

1719
async function readJsonAsset(assets: AssetsBinding, path: string): Promise<unknown> {

src/server/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { loadCatalog } from "../catalog-store.ts";
55
import { ActionPolicyService, parseActionPolicyList } from "../core/action-policy.ts";
66
import { parsePrivateNetworkAccessFlag, setPrivateNetworkAccessAllowed } from "../core/request.ts";
77
import { ProviderLoader } from "../providers/provider-loader.ts";
8-
import { executableActionIds, executorModules } from "../providers/registry.generated.ts";
8+
import { executorModules } from "../providers/registry.generated.ts";
99
import { createRuntimeJwtVerifier } from "./api/runtime-jwt.ts";
1010
import { registerStaticRoutes } from "./api/static-routes.ts";
1111
import { createConnectApp } from "./connect-app.ts";
@@ -41,7 +41,7 @@ const builtRoot = join(process.cwd(), "dist/web");
4141
const staticRoot = await resolveStaticRoot(builtRoot);
4242
await mkdir(dataDir, { recursive: true });
4343
const catalog = await loadCatalog(undefined, {
44-
executableActionIds: Object.values(executableActionIds).flat(),
44+
executableServices: Object.keys(executorModules),
4545
});
4646
const providerLoader = new ProviderLoader(executorModules);
4747
const runtimeDatabase = new SqliteRuntimeDatabase(join(dataDir, "connect.sqlite"), {

0 commit comments

Comments
 (0)