Skip to content

Commit 9e086d6

Browse files
committed
refactor: split plugin index record reader
1 parent 57c4279 commit 9e086d6

5 files changed

Lines changed: 104 additions & 70 deletions
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import type { PluginInstallRecord } from "../config/types.plugins.js";
2+
import { readJsonFile, readJsonFileSync } from "../infra/json-files.js";
3+
import {
4+
resolveInstalledPluginIndexStorePath,
5+
type InstalledPluginIndexStoreOptions,
6+
} from "./installed-plugin-index-store-path.js";
7+
8+
function isRecord(value: unknown): value is Record<string, unknown> {
9+
return typeof value === "object" && value !== null && !Array.isArray(value);
10+
}
11+
12+
function cloneInstallRecords(
13+
records: Record<string, PluginInstallRecord> | undefined,
14+
): Record<string, PluginInstallRecord> {
15+
return structuredClone(records ?? {});
16+
}
17+
18+
export function extractPluginInstallRecordsFromPersistedInstalledPluginIndex(
19+
index: unknown,
20+
): Record<string, PluginInstallRecord> | null {
21+
if (!isRecord(index) || !Array.isArray(index.plugins)) {
22+
return null;
23+
}
24+
const records: Record<string, PluginInstallRecord> = {};
25+
for (const entry of index.plugins) {
26+
if (!isRecord(entry) || typeof entry.pluginId !== "string" || !isRecord(entry.installRecord)) {
27+
continue;
28+
}
29+
records[entry.pluginId] = structuredClone(entry.installRecord) as PluginInstallRecord;
30+
}
31+
return records;
32+
}
33+
34+
export async function readPersistedInstalledPluginIndexInstallRecords(
35+
options: InstalledPluginIndexStoreOptions = {},
36+
): Promise<Record<string, PluginInstallRecord> | null> {
37+
const parsed = await readJsonFile<unknown>(resolveInstalledPluginIndexStorePath(options));
38+
return extractPluginInstallRecordsFromPersistedInstalledPluginIndex(parsed);
39+
}
40+
41+
export function readPersistedInstalledPluginIndexInstallRecordsSync(
42+
options: InstalledPluginIndexStoreOptions = {},
43+
): Record<string, PluginInstallRecord> | null {
44+
const parsed = readJsonFileSync(resolveInstalledPluginIndexStorePath(options));
45+
return extractPluginInstallRecordsFromPersistedInstalledPluginIndex(parsed);
46+
}
47+
48+
export async function loadInstalledPluginIndexInstallRecords(
49+
params: InstalledPluginIndexStoreOptions = {},
50+
): Promise<Record<string, PluginInstallRecord>> {
51+
return cloneInstallRecords((await readPersistedInstalledPluginIndexInstallRecords(params)) ?? {});
52+
}
53+
54+
export function loadInstalledPluginIndexInstallRecordsSync(
55+
params: InstalledPluginIndexStoreOptions = {},
56+
): Record<string, PluginInstallRecord> {
57+
return cloneInstallRecords(readPersistedInstalledPluginIndexInstallRecordsSync(params) ?? {});
58+
}

src/plugins/installed-plugin-index-records.ts

Lines changed: 15 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
import type { OpenClawConfig } from "../config/types.openclaw.js";
22
import type { PluginInstallRecord } from "../config/types.plugins.js";
33
import {
4-
readPersistedInstalledPluginIndex,
5-
readPersistedInstalledPluginIndexSync,
6-
refreshPersistedInstalledPluginIndex,
7-
resolveInstalledPluginIndexStorePath,
8-
} from "./installed-plugin-index-store.js";
9-
import {
10-
extractPluginInstallRecordsFromInstalledPluginIndex,
11-
type RefreshInstalledPluginIndexParams,
12-
} from "./installed-plugin-index.js";
4+
loadInstalledPluginIndexInstallRecords,
5+
loadInstalledPluginIndexInstallRecordsSync,
6+
readPersistedInstalledPluginIndexInstallRecords,
7+
readPersistedInstalledPluginIndexInstallRecordsSync,
8+
} from "./installed-plugin-index-record-reader.js";
9+
import { resolveInstalledPluginIndexStorePath } from "./installed-plugin-index-store-path.js";
10+
import { refreshPersistedInstalledPluginIndex } from "./installed-plugin-index-store.js";
11+
import { type RefreshInstalledPluginIndexParams } from "./installed-plugin-index.js";
1312
import { recordPluginInstall, type PluginInstallUpdate } from "./installs.js";
1413

14+
export {
15+
loadInstalledPluginIndexInstallRecords,
16+
loadInstalledPluginIndexInstallRecordsSync,
17+
readPersistedInstalledPluginIndexInstallRecords,
18+
readPersistedInstalledPluginIndexInstallRecordsSync,
19+
};
20+
1521
export const PLUGIN_INSTALLS_CONFIG_PATH = ["plugins", "installs"] as const;
1622

1723
export type InstalledPluginIndexRecordStoreOptions = {
@@ -25,39 +31,12 @@ type InstalledPluginIndexRecordRefreshOptions = InstalledPluginIndexRecordStoreO
2531
now?: () => Date;
2632
};
2733

28-
function toInstallRecords(
29-
index: Awaited<ReturnType<typeof readPersistedInstalledPluginIndex>>,
30-
): Record<string, PluginInstallRecord> | null {
31-
if (!index) {
32-
return null;
33-
}
34-
return extractPluginInstallRecordsFromInstalledPluginIndex(index);
35-
}
36-
37-
function cloneInstallRecords(
38-
records: Record<string, PluginInstallRecord> | undefined,
39-
): Record<string, PluginInstallRecord> {
40-
return structuredClone(records ?? {});
41-
}
42-
4334
export function resolveInstalledPluginIndexRecordsStorePath(
4435
options: InstalledPluginIndexRecordStoreOptions = {},
4536
): string {
4637
return resolveInstalledPluginIndexStorePath(options);
4738
}
4839

49-
export async function readPersistedInstalledPluginIndexInstallRecords(
50-
options: InstalledPluginIndexRecordStoreOptions = {},
51-
): Promise<Record<string, PluginInstallRecord> | null> {
52-
return toInstallRecords(await readPersistedInstalledPluginIndex(options));
53-
}
54-
55-
export function readPersistedInstalledPluginIndexInstallRecordsSync(
56-
options: InstalledPluginIndexRecordStoreOptions = {},
57-
): Record<string, PluginInstallRecord> | null {
58-
return toInstallRecords(readPersistedInstalledPluginIndexSync(options));
59-
}
60-
6140
export async function writePersistedInstalledPluginIndexInstallRecords(
6241
records: Record<string, PluginInstallRecord>,
6342
options: InstalledPluginIndexRecordRefreshOptions = {},
@@ -70,18 +49,6 @@ export async function writePersistedInstalledPluginIndexInstallRecords(
7049
return resolveInstalledPluginIndexRecordsStorePath(options);
7150
}
7251

73-
export async function loadInstalledPluginIndexInstallRecords(
74-
params: InstalledPluginIndexRecordStoreOptions = {},
75-
): Promise<Record<string, PluginInstallRecord>> {
76-
return cloneInstallRecords((await readPersistedInstalledPluginIndexInstallRecords(params)) ?? {});
77-
}
78-
79-
export function loadInstalledPluginIndexInstallRecordsSync(
80-
params: InstalledPluginIndexRecordStoreOptions = {},
81-
): Record<string, PluginInstallRecord> {
82-
return cloneInstallRecords(readPersistedInstalledPluginIndexInstallRecordsSync(params) ?? {});
83-
}
84-
8552
export function withPluginInstallRecords(
8653
config: OpenClawConfig,
8754
records: Record<string, PluginInstallRecord>,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import path from "node:path";
2+
import { resolveStateDir } from "../config/paths.js";
3+
4+
export const INSTALLED_PLUGIN_INDEX_STORE_PATH = path.join("plugins", "installs.json");
5+
6+
export type InstalledPluginIndexStoreOptions = {
7+
env?: NodeJS.ProcessEnv;
8+
stateDir?: string;
9+
filePath?: string;
10+
};
11+
12+
export function resolveInstalledPluginIndexStorePath(
13+
options: InstalledPluginIndexStoreOptions = {},
14+
): string {
15+
if (options.filePath) {
16+
return options.filePath;
17+
}
18+
const env = options.env ?? process.env;
19+
const stateDir = options.stateDir ?? resolveStateDir(env);
20+
return path.join(stateDir, INSTALLED_PLUGIN_INDEX_STORE_PATH);
21+
}

src/plugins/installed-plugin-index-store.ts

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import path from "node:path";
21
import { z } from "zod";
3-
import { resolveStateDir } from "../config/paths.js";
42
import { readJsonFile, readJsonFileSync, writeJsonAtomic } from "../infra/json-files.js";
53
import { safeParseWithSchema } from "../utils/zod-parse.js";
4+
import {
5+
resolveInstalledPluginIndexStorePath,
6+
type InstalledPluginIndexStoreOptions,
7+
} from "./installed-plugin-index-store-path.js";
68
import {
79
diffInstalledPluginIndexInvalidationReasons,
810
extractPluginInstallRecordsFromInstalledPluginIndex,
@@ -16,14 +18,11 @@ import {
1618
type LoadInstalledPluginIndexParams,
1719
type RefreshInstalledPluginIndexParams,
1820
} from "./installed-plugin-index.js";
19-
20-
export const INSTALLED_PLUGIN_INDEX_STORE_PATH = path.join("plugins", "installs.json");
21-
22-
export type InstalledPluginIndexStoreOptions = {
23-
env?: NodeJS.ProcessEnv;
24-
stateDir?: string;
25-
filePath?: string;
26-
};
21+
export {
22+
INSTALLED_PLUGIN_INDEX_STORE_PATH,
23+
resolveInstalledPluginIndexStorePath,
24+
type InstalledPluginIndexStoreOptions,
25+
} from "./installed-plugin-index-store-path.js";
2726

2827
export type InstalledPluginIndexStoreState = "missing" | "fresh" | "stale";
2928

@@ -112,17 +111,6 @@ function parseInstalledPluginIndex(value: unknown): InstalledPluginIndex | null
112111
return safeParseWithSchema(InstalledPluginIndexSchema, value) as InstalledPluginIndex | null;
113112
}
114113

115-
export function resolveInstalledPluginIndexStorePath(
116-
options: InstalledPluginIndexStoreOptions = {},
117-
): string {
118-
if (options.filePath) {
119-
return options.filePath;
120-
}
121-
const env = options.env ?? process.env;
122-
const stateDir = options.stateDir ?? resolveStateDir(env);
123-
return path.join(stateDir, INSTALLED_PLUGIN_INDEX_STORE_PATH);
124-
}
125-
126114
export async function readPersistedInstalledPluginIndex(
127115
options: InstalledPluginIndexStoreOptions = {},
128116
): Promise<InstalledPluginIndex | null> {

src/plugins/manifest-registry.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
type NormalizedPluginsConfig,
1717
} from "./config-policy.js";
1818
import { discoverOpenClawPlugins, type PluginCandidate } from "./discovery.js";
19-
import { loadInstalledPluginIndexInstallRecordsSync } from "./installed-plugin-index-records.js";
19+
import { loadInstalledPluginIndexInstallRecordsSync } from "./installed-plugin-index-record-reader.js";
2020
import type { PluginManifestCommandAlias } from "./manifest-command-aliases.js";
2121
import {
2222
clearPluginManifestRegistryCache,

0 commit comments

Comments
 (0)