feat(lib/plugins): switch to objects for storage

This commit is contained in:
PalmDevs 2025-07-18 23:37:22 +07:00
parent 00638ed845
commit d3b754d2cc
No known key found for this signature in database
GPG key ID: 6FAE8F8677ACA8BA
3 changed files with 36 additions and 28 deletions

View file

@ -36,12 +36,12 @@ export const pEmitter = new TypedEventEmitter<{
export const pList = new Map<PluginManifest['id'], InternalPlugin>() export const pList = new Map<PluginManifest['id'], InternalPlugin>()
export const pMetadata = new Map< export const pMetadata = new Map<
PluginManifest['id'], PluginManifest['id'],
[ {
api: PreInitPluginApi | InitPluginApi | PluginApi | undefined, api: PreInitPluginApi | InitPluginApi | PluginApi | undefined
promises: Promise<void>[], promises: Promise<void>[]
iflags: number, iflags: number
apiLevel: number, apiLevel: number
] }
>() >()
export function registerPlugin<O extends PluginApiExtensionsOptions>( export function registerPlugin<O extends PluginApiExtensionsOptions>(
@ -71,7 +71,12 @@ export function registerPlugin<O extends PluginApiExtensionsOptions>(
stop: () => stopPlugin(plugin), stop: () => stopPlugin(plugin),
} }
pMetadata.set(manifest.id, [undefined, [], iflags, PluginApiLevel.None]) pMetadata.set(manifest.id, {
api: undefined,
promises: [],
iflags,
apiLevel: PluginApiLevel.None,
})
pList.set(manifest.id, plugin) pList.set(manifest.id, plugin)
pEmitter.emit('register', plugin, options) pEmitter.emit('register', plugin, options)
@ -80,7 +85,7 @@ export function registerPlugin<O extends PluginApiExtensionsOptions>(
function handlePluginError(e: unknown, plugin: InternalPlugin) { function handlePluginError(e: unknown, plugin: InternalPlugin) {
plugin.errors.push(e) plugin.errors.push(e)
plugin.flags |= Flag.Errored plugin.flags |= Flag.Errored
const [api, , iflags] = pMetadata.get(plugin.manifest.id)! const { api, iflags } = pMetadata.get(plugin.manifest.id)!
;(api as InitPluginApi).logger.error('Plugin encountered an error', e) ;(api as InitPluginApi).logger.error('Plugin encountered an error', e)
nativeLoggingHook( nativeLoggingHook(
@ -100,7 +105,7 @@ function preparePluginPreInit(plugin: InternalPlugin) {
plugin.errors = [] plugin.errors = []
plugin.status &= ~Flag.Errored plugin.status &= ~Flag.Errored
const api = (meta[0] = { const api = (meta.api = {
cleanup: (...items) => { cleanup: (...items) => {
plugin._c.push(...items) plugin._c.push(...items)
}, },
@ -109,28 +114,28 @@ function preparePluginPreInit(plugin: InternalPlugin) {
}) })
pEmitter.emit('preInit', plugin, api) pEmitter.emit('preInit', plugin, api)
meta[3] = PluginApiLevel.PreInit meta.apiLevel = PluginApiLevel.PreInit
} }
function preparePluginInit(plugin: InternalPlugin) { function preparePluginInit(plugin: InternalPlugin) {
const meta = pMetadata.get(plugin.manifest.id)! const meta = pMetadata.get(plugin.manifest.id)!
pEmitter.emit('init', plugin, meta[0] as InitPluginApi) pEmitter.emit('init', plugin, meta.api as InitPluginApi)
meta[3] = PluginApiLevel.Init meta.apiLevel = PluginApiLevel.Init
} }
function preparePluginStart(plugin: InternalPlugin) { function preparePluginStart(plugin: InternalPlugin) {
const meta = pMetadata.get(plugin.manifest.id)! const meta = pMetadata.get(plugin.manifest.id)!
pEmitter.emit('start', plugin, meta[0] as PluginApi) pEmitter.emit('start', plugin, meta.api as PluginApi)
meta[3] = PluginApiLevel.Start meta.apiLevel = PluginApiLevel.Start
} }
async function disablePlugin(plugin: InternalPlugin) { async function disablePlugin(plugin: InternalPlugin) {
if (!(plugin.flags & Flag.Enabled)) if (!(plugin.flags & Flag.Enabled))
throw new Error(`Plugin "${plugin.manifest.id}" is not enabled`) throw new Error(`Plugin "${plugin.manifest.id}" is not enabled`)
const iflags = pMetadata.get(plugin.manifest.id)![2] ?? 0 const iflags = pMetadata.get(plugin.manifest.id)!.iflags ?? 0
if (iflags & InternalPluginFlags.Essential) if (iflags & InternalPluginFlags.Essential)
throw new Error( throw new Error(
`Plugin "${plugin.manifest.id}" is essential and cannot be disabled`, `Plugin "${plugin.manifest.id}" is essential and cannot be disabled`,
@ -166,7 +171,7 @@ export async function preInitPlugin(plugin: InternalPlugin) {
if (!lifecycles.preInit) return if (!lifecycles.preInit) return
const meta = pMetadata.get(id)! const meta = pMetadata.get(id)!
const [, promises] = meta const { promises } = meta
if (!(plugin.flags & Flag.Enabled)) if (!(plugin.flags & Flag.Enabled))
throw new Error(`Plugin "${id}" is not enabled`) throw new Error(`Plugin "${id}" is not enabled`)
@ -180,7 +185,7 @@ export async function preInitPlugin(plugin: InternalPlugin) {
plugin.status |= Status.PreIniting plugin.status |= Status.PreIniting
try { try {
const prom = lifecycles.preInit(meta[0] as PreInitPluginApi) const prom = lifecycles.preInit(meta.api as PreInitPluginApi)
promises.push(prom) promises.push(prom)
await prom await prom
@ -205,7 +210,7 @@ export async function initPlugin(plugin: InternalPlugin) {
if (!lifecycles.init) return if (!lifecycles.init) return
const meta = pMetadata.get(id)! const meta = pMetadata.get(id)!
const [, promises, , apiLevel] = meta const { promises, apiLevel } = meta
if (!(plugin.flags & Flag.Enabled)) if (!(plugin.flags & Flag.Enabled))
throw new Error(`Plugin "${id}" is not enabled`) throw new Error(`Plugin "${id}" is not enabled`)
@ -220,7 +225,7 @@ export async function initPlugin(plugin: InternalPlugin) {
plugin.status |= Status.Initing plugin.status |= Status.Initing
try { try {
const prom = lifecycles.init(meta[0] as InitPluginApi) const prom = lifecycles.init(meta.api as InitPluginApi)
promises.push(prom) promises.push(prom)
await prom await prom
@ -245,7 +250,7 @@ export async function startPlugin(plugin: InternalPlugin) {
if (!lifecycles.start) return if (!lifecycles.start) return
const meta = pMetadata.get(id)! const meta = pMetadata.get(id)!
const [, promises, , apiLevel] = meta const { promises, apiLevel } = meta
if (!(plugin.flags & Flag.Enabled)) if (!(plugin.flags & Flag.Enabled))
throw new Error(`Plugin "${id}" is not enabled`) throw new Error(`Plugin "${id}" is not enabled`)
@ -261,7 +266,7 @@ export async function startPlugin(plugin: InternalPlugin) {
plugin.status |= Status.Starting plugin.status |= Status.Starting
try { try {
const prom = lifecycles.start(meta[0] as PluginApi) const prom = lifecycles.start(meta.api as PluginApi)
promises.push(prom) promises.push(prom)
await prom await prom
@ -284,7 +289,7 @@ export async function stopPlugin(plugin: InternalPlugin) {
lifecycles, lifecycles,
} = plugin } = plugin
const meta = pMetadata.get(id)! const meta = pMetadata.get(id)!
const [, promises, iflags, apiLevel] = meta const { promises, iflags, apiLevel } = meta
if (iflags & InternalPluginFlags.Essential) if (iflags & InternalPluginFlags.Essential)
throw new Error(`Plugin "${id}" is essential and cannot be stopped`) throw new Error(`Plugin "${id}" is essential and cannot be stopped`)
@ -326,7 +331,7 @@ export async function stopPlugin(plugin: InternalPlugin) {
if (!lifecycles.stop) return if (!lifecycles.stop) return
await Promise.race([ await Promise.race([
lifecycles.stop(meta[0] as PluginApi), lifecycles.stop(meta.api as PluginApi),
sleepReject( sleepReject(
MaxWaitTime, MaxWaitTime,
'Plugin stop lifecycle timed out, force stopping', 'Plugin stop lifecycle timed out, force stopping',
@ -360,11 +365,11 @@ export async function stopPlugin(plugin: InternalPlugin) {
} }
// Reset APIs // Reset APIs
meta[0] = undefined meta.api = undefined
meta[3] = PluginApiLevel.None meta.apiLevel = PluginApiLevel.None
// Clear temp data // Clear temp data
meta[1] = [] meta.promises = []
plugin._c = [] plugin._c = []
// Reset status // Reset status

View file

@ -109,7 +109,10 @@ function Screen() {
() => () =>
[...pList.values()].map( [...pList.values()].map(
plugin => plugin =>
[plugin, pMetadata.get(plugin.manifest.id)![2]] as const, [
plugin,
pMetadata.get(plugin.manifest.id)!.iflags,
] as const,
), ),
[], [],
) )

View file

@ -16,7 +16,7 @@ const { NavigatorHeader } = Design
// TODO(plugins/settings): Register a custom route instead, so plugin "settings" can actually be pinned and navigated to without hassle. // TODO(plugins/settings): Register a custom route instead, so plugin "settings" can actually be pinned and navigated to without hassle.
// This would require us to implement the event-based plugin management system first, so we can listen to plugin enable/disable events and update the settings accordingly. // This would require us to implement the event-based plugin management system first, so we can listen to plugin enable/disable events and update the settings accordingly.
export function navigatePluginSettings(plugin: Plugin) { export function navigatePluginSettings(plugin: Plugin) {
const [api] = pMetadata.get(plugin.manifest.id)! const { api } = pMetadata.get(plugin.manifest.id)!
const navigation = const navigation =
RootNavigationRef.getRootNavigationRef<ReactNavigationParamList>() RootNavigationRef.getRootNavigationRef<ReactNavigationParamList>()
if (!navigation.isReady()) return if (!navigation.isReady()) return