Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
286
extensions/memory-wiki/src/source-sync-state.ts
Normal file
286
extensions/memory-wiki/src/source-sync-state.ts
Normal file
@@ -0,0 +1,286 @@
|
||||
// Memory Wiki plugin module implements source sync state behavior.
|
||||
import { createHash } from "node:crypto";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { readJsonFileWithFallback } from "openclaw/plugin-sdk/json-store";
|
||||
import type {
|
||||
OpenKeyedStoreOptions,
|
||||
PluginStateKeyedStore,
|
||||
} from "openclaw/plugin-sdk/plugin-state-runtime";
|
||||
|
||||
export type MemoryWikiImportedSourceGroup = "bridge" | "unsafe-local";
|
||||
|
||||
export type MemoryWikiImportedSourceStateEntry = {
|
||||
group: MemoryWikiImportedSourceGroup;
|
||||
pagePath: string;
|
||||
sourcePath: string;
|
||||
sourceUpdatedAtMs: number;
|
||||
sourceSize: number;
|
||||
renderFingerprint: string;
|
||||
};
|
||||
|
||||
export type MemoryWikiImportedSourceState = {
|
||||
version: 1;
|
||||
entries: Record<string, MemoryWikiImportedSourceStateEntry>;
|
||||
};
|
||||
|
||||
type MemoryWikiSourceSyncStateStore = {
|
||||
read: (vaultRoot: string) => Promise<MemoryWikiImportedSourceState>;
|
||||
write: (vaultRoot: string, state: MemoryWikiImportedSourceState) => Promise<void>;
|
||||
};
|
||||
|
||||
type MemoryWikiSourceSyncStateRecord = MemoryWikiImportedSourceStateEntry & {
|
||||
vaultRootKey: string;
|
||||
syncKey: string;
|
||||
};
|
||||
|
||||
export const MEMORY_WIKI_SOURCE_SYNC_STATE_NAMESPACE = "source-sync";
|
||||
export const MEMORY_WIKI_SOURCE_SYNC_STATE_MAX_ENTRIES = 20_000;
|
||||
|
||||
const EMPTY_STATE: MemoryWikiImportedSourceState = {
|
||||
version: 1,
|
||||
entries: {},
|
||||
};
|
||||
|
||||
let configuredSourceSyncStore: MemoryWikiSourceSyncStateStore | undefined;
|
||||
const memorySourceSyncStateByVault = new Map<string, MemoryWikiImportedSourceState>();
|
||||
|
||||
export function resolveMemoryWikiSourceSyncStatePath(vaultRoot: string): string {
|
||||
return path.join(vaultRoot, ".openclaw-wiki", "source-sync.json");
|
||||
}
|
||||
|
||||
function cloneSourceSyncState(state: MemoryWikiImportedSourceState): MemoryWikiImportedSourceState {
|
||||
return {
|
||||
version: 1,
|
||||
entries: Object.fromEntries(
|
||||
Object.entries(state.entries).map(([key, value]) => [key, { ...value }]),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeSourceSyncState(value: unknown): MemoryWikiImportedSourceState {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
return EMPTY_STATE;
|
||||
}
|
||||
const parsed = value as Partial<MemoryWikiImportedSourceState>;
|
||||
if (parsed.version !== 1 || !parsed.entries || typeof parsed.entries !== "object") {
|
||||
return EMPTY_STATE;
|
||||
}
|
||||
const entries: Record<string, MemoryWikiImportedSourceStateEntry> = {};
|
||||
for (const [syncKey, entry] of Object.entries(parsed.entries)) {
|
||||
if (
|
||||
!entry ||
|
||||
typeof entry !== "object" ||
|
||||
Array.isArray(entry) ||
|
||||
(entry.group !== "bridge" && entry.group !== "unsafe-local") ||
|
||||
typeof entry.pagePath !== "string" ||
|
||||
typeof entry.sourcePath !== "string" ||
|
||||
typeof entry.sourceUpdatedAtMs !== "number" ||
|
||||
typeof entry.sourceSize !== "number" ||
|
||||
typeof entry.renderFingerprint !== "string"
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
entries[syncKey] = {
|
||||
group: entry.group,
|
||||
pagePath: entry.pagePath,
|
||||
sourcePath: entry.sourcePath,
|
||||
sourceUpdatedAtMs: entry.sourceUpdatedAtMs,
|
||||
sourceSize: entry.sourceSize,
|
||||
renderFingerprint: entry.renderFingerprint,
|
||||
};
|
||||
}
|
||||
return { version: 1, entries };
|
||||
}
|
||||
|
||||
function resolveVaultRootKey(vaultRoot: string): string {
|
||||
return createHash("sha256").update(path.resolve(vaultRoot), "utf8").digest("hex").slice(0, 32);
|
||||
}
|
||||
|
||||
function resolveStateEntryKey(vaultRootKey: string, syncKey: string): string {
|
||||
return createHash("sha256").update(`${vaultRootKey}\0${syncKey}`, "utf8").digest("hex");
|
||||
}
|
||||
|
||||
function createMemoryFallbackStateStore(): MemoryWikiSourceSyncStateStore {
|
||||
return {
|
||||
async read(vaultRoot) {
|
||||
const vaultRootKey = resolveVaultRootKey(vaultRoot);
|
||||
return cloneSourceSyncState(memorySourceSyncStateByVault.get(vaultRootKey) ?? EMPTY_STATE);
|
||||
},
|
||||
async write(vaultRoot, state) {
|
||||
assertSourceSyncStateWithinLimit(state);
|
||||
const vaultRootKey = resolveVaultRootKey(vaultRoot);
|
||||
memorySourceSyncStateByVault.set(vaultRootKey, cloneSourceSyncState(state));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function assertSourceSyncStateWithinLimit(state: MemoryWikiImportedSourceState): void {
|
||||
const count = Object.keys(state.entries).length;
|
||||
if (count > MEMORY_WIKI_SOURCE_SYNC_STATE_MAX_ENTRIES) {
|
||||
throw new Error(
|
||||
`Memory Wiki source sync state exceeds SQLite entry limit (${count}/${MEMORY_WIKI_SOURCE_SYNC_STATE_MAX_ENTRIES})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function assertMemoryWikiSourceSyncStateCapacity(params: {
|
||||
state: MemoryWikiImportedSourceState;
|
||||
group: MemoryWikiImportedSourceGroup;
|
||||
incomingCount: number;
|
||||
}): void {
|
||||
const retainedOtherGroupCount = Object.values(params.state.entries).filter(
|
||||
(entry) => entry.group !== params.group,
|
||||
).length;
|
||||
const projectedCount = retainedOtherGroupCount + params.incomingCount;
|
||||
if (projectedCount > MEMORY_WIKI_SOURCE_SYNC_STATE_MAX_ENTRIES) {
|
||||
throw new Error(
|
||||
`Memory Wiki source sync state exceeds SQLite entry limit (${projectedCount}/${MEMORY_WIKI_SOURCE_SYNC_STATE_MAX_ENTRIES})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function createMemoryWikiSourceSyncStateStore(
|
||||
openKeyedStore: <T>(options: OpenKeyedStoreOptions) => PluginStateKeyedStore<T>,
|
||||
): MemoryWikiSourceSyncStateStore {
|
||||
const openStore = () =>
|
||||
openKeyedStore<MemoryWikiSourceSyncStateRecord>({
|
||||
namespace: MEMORY_WIKI_SOURCE_SYNC_STATE_NAMESPACE,
|
||||
maxEntries: MEMORY_WIKI_SOURCE_SYNC_STATE_MAX_ENTRIES,
|
||||
});
|
||||
|
||||
return {
|
||||
async read(vaultRoot) {
|
||||
const vaultRootKey = resolveVaultRootKey(vaultRoot);
|
||||
const entries: MemoryWikiImportedSourceState["entries"] = {};
|
||||
for (const row of await openStore().entries()) {
|
||||
const value = row.value;
|
||||
if (value.vaultRootKey !== vaultRootKey || typeof value.syncKey !== "string") {
|
||||
continue;
|
||||
}
|
||||
const normalized = normalizeSourceSyncState({
|
||||
version: 1,
|
||||
entries: { [value.syncKey]: value },
|
||||
});
|
||||
const entry = normalized.entries[value.syncKey];
|
||||
if (entry) {
|
||||
entries[value.syncKey] = entry;
|
||||
}
|
||||
}
|
||||
return { version: 1, entries };
|
||||
},
|
||||
async write(vaultRoot, state) {
|
||||
assertSourceSyncStateWithinLimit(state);
|
||||
const vaultRootKey = resolveVaultRootKey(vaultRoot);
|
||||
const store = openStore();
|
||||
const normalized = normalizeSourceSyncState(state);
|
||||
const nextKeys = new Set<string>();
|
||||
for (const [syncKey, entry] of Object.entries(normalized.entries)) {
|
||||
const key = resolveStateEntryKey(vaultRootKey, syncKey);
|
||||
nextKeys.add(key);
|
||||
await store.register(key, {
|
||||
...entry,
|
||||
vaultRootKey,
|
||||
syncKey,
|
||||
});
|
||||
}
|
||||
for (const row of await store.entries()) {
|
||||
if (row.value.vaultRootKey === vaultRootKey && !nextKeys.has(row.key)) {
|
||||
await store.delete(row.key);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function configureMemoryWikiSourceSyncStateStore(
|
||||
store: MemoryWikiSourceSyncStateStore | undefined,
|
||||
): void {
|
||||
configuredSourceSyncStore = store;
|
||||
}
|
||||
|
||||
function resolveSourceSyncStore(
|
||||
store?: MemoryWikiSourceSyncStateStore,
|
||||
): MemoryWikiSourceSyncStateStore {
|
||||
return store ?? configuredSourceSyncStore ?? createMemoryFallbackStateStore();
|
||||
}
|
||||
|
||||
export async function readMemoryWikiSourceSyncState(
|
||||
vaultRoot: string,
|
||||
store?: MemoryWikiSourceSyncStateStore,
|
||||
): Promise<MemoryWikiImportedSourceState> {
|
||||
return await resolveSourceSyncStore(store).read(vaultRoot);
|
||||
}
|
||||
|
||||
export async function readLegacyMemoryWikiSourceSyncState(
|
||||
vaultRoot: string,
|
||||
): Promise<MemoryWikiImportedSourceState> {
|
||||
const statePath = resolveMemoryWikiSourceSyncStatePath(vaultRoot);
|
||||
const { value: parsed } = await readJsonFileWithFallback<unknown>(statePath, EMPTY_STATE);
|
||||
return normalizeSourceSyncState(parsed);
|
||||
}
|
||||
|
||||
export async function writeMemoryWikiSourceSyncState(
|
||||
vaultRoot: string,
|
||||
state: MemoryWikiImportedSourceState,
|
||||
store?: MemoryWikiSourceSyncStateStore,
|
||||
): Promise<void> {
|
||||
await resolveSourceSyncStore(store).write(vaultRoot, state);
|
||||
}
|
||||
|
||||
export async function shouldSkipImportedSourceWrite(params: {
|
||||
vaultRoot: string;
|
||||
syncKey: string;
|
||||
expectedPagePath: string;
|
||||
expectedSourcePath: string;
|
||||
sourceUpdatedAtMs: number;
|
||||
sourceSize: number;
|
||||
renderFingerprint: string;
|
||||
state: MemoryWikiImportedSourceState;
|
||||
}): Promise<boolean> {
|
||||
const entry = params.state.entries[params.syncKey];
|
||||
if (!entry) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
entry.pagePath !== params.expectedPagePath ||
|
||||
entry.sourcePath !== params.expectedSourcePath ||
|
||||
entry.sourceUpdatedAtMs !== params.sourceUpdatedAtMs ||
|
||||
entry.sourceSize !== params.sourceSize ||
|
||||
entry.renderFingerprint !== params.renderFingerprint
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const pagePath = path.join(params.vaultRoot, params.expectedPagePath);
|
||||
return await fs
|
||||
.access(pagePath)
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
}
|
||||
|
||||
export async function pruneImportedSourceEntries(params: {
|
||||
vaultRoot: string;
|
||||
group: MemoryWikiImportedSourceGroup;
|
||||
activeKeys: Set<string>;
|
||||
state: MemoryWikiImportedSourceState;
|
||||
}): Promise<number> {
|
||||
let removedCount = 0;
|
||||
for (const [syncKey, entry] of Object.entries(params.state.entries)) {
|
||||
if (entry.group !== params.group || params.activeKeys.has(syncKey)) {
|
||||
continue;
|
||||
}
|
||||
const pageAbsPath = path.join(params.vaultRoot, entry.pagePath);
|
||||
await fs.rm(pageAbsPath, { force: true }).catch(() => undefined);
|
||||
delete params.state.entries[syncKey];
|
||||
removedCount += 1;
|
||||
}
|
||||
return removedCount;
|
||||
}
|
||||
|
||||
export function setImportedSourceEntry(params: {
|
||||
syncKey: string;
|
||||
entry: MemoryWikiImportedSourceStateEntry;
|
||||
state: MemoryWikiImportedSourceState;
|
||||
}): void {
|
||||
params.state.entries[params.syncKey] = params.entry;
|
||||
}
|
||||
Reference in New Issue
Block a user