Replace fragile string matching with Symbol-based plugin detection (#140)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Innei <41265413+Innei@users.noreply.github.com>
This commit is contained in:
Copilot
2025-10-31 21:48:58 +08:00
committed by GitHub
parent 751140a1b7
commit b4c196630c
4 changed files with 29 additions and 10 deletions

View File

@@ -7,7 +7,12 @@ import type {
StorageConfig,
StorageObject,
} from '@afilmory/builder'
import { AfilmoryBuilder, processPhotoWithPipeline, thumbnailStoragePlugin } from '@afilmory/builder'
import {
AfilmoryBuilder,
processPhotoWithPipeline,
THUMBNAIL_PLUGIN_SYMBOL,
thumbnailStoragePlugin,
} from '@afilmory/builder'
import type { Logger as BuilderLogger } from '@afilmory/builder/logger/index.js'
import type { PhotoProcessingLoggers } from '@afilmory/builder/photo/index.js'
import { createPhotoProcessingLoggers, setGlobalLoggers } from '@afilmory/builder/photo/index.js'
@@ -144,13 +149,11 @@ export class PhotoBuilderService {
private ensureThumbnailPlugin(config: BuilderConfig): BuilderConfig {
const existingPlugins = config.plugins ?? []
const hasPlugin = existingPlugins.some((entry) => {
if (typeof entry === 'string') {
return entry.includes('thumbnail-storage')
}
if (typeof entry === 'function') {
const fnName = entry.name ?? ''
return fnName.includes('thumbnailStorage') || entry.toString().includes('thumbnail-storage')
// Check for the unique Symbol identifier for reliable detection
if (typeof entry === 'object' && entry !== null && THUMBNAIL_PLUGIN_SYMBOL in entry) {
return true
}
// Fallback: check by name property for backward compatibility
return entry?.name === 'afilmory:thumbnail-storage'
})

View File

@@ -23,7 +23,7 @@ export { default as localStoragePlugin } from './plugins/storage/local.js'
export type { S3StoragePluginOptions } from './plugins/storage/s3.js'
export { default as s3StoragePlugin } from './plugins/storage/s3.js'
export type { ThumbnailStoragePluginOptions } from './plugins/thumbnail-storage/index.js'
export { default as thumbnailStoragePlugin } from './plugins/thumbnail-storage/index.js'
export { THUMBNAIL_PLUGIN_SYMBOL, default as thumbnailStoragePlugin } from './plugins/thumbnail-storage/index.js'
export type {
BuilderPlugin,
BuilderPluginConfigEntry,

View File

@@ -2,7 +2,12 @@ import { StorageManager } from '../../storage/index.js'
import type { StorageConfig } from '../../storage/interfaces.js'
import type { BuilderPlugin } from '../types.js'
import type { ThumbnailPluginData } from './shared.js'
import { DEFAULT_CONTENT_TYPE, DEFAULT_DIRECTORY, THUMBNAIL_PLUGIN_DATA_KEY } from './shared.js'
import {
DEFAULT_CONTENT_TYPE,
DEFAULT_DIRECTORY,
THUMBNAIL_PLUGIN_DATA_KEY,
THUMBNAIL_PLUGIN_SYMBOL,
} from './shared.js'
const PLUGIN_NAME = 'afilmory:thumbnail-storage'
const RUN_STATE_KEY = 'state'
@@ -82,8 +87,9 @@ export default function thumbnailStoragePlugin(options: ThumbnailStoragePluginOp
let resolved: ResolvedPluginConfig | null = null
let externalStorageManager: StorageManager | null = null
return {
const plugin: BuilderPlugin & { [THUMBNAIL_PLUGIN_SYMBOL]: true } = {
name: PLUGIN_NAME,
[THUMBNAIL_PLUGIN_SYMBOL]: true,
hooks: {
onInit: ({ builder, config, logger }) => {
const storageConfig = (options.storageConfig ?? config.storage) as StorageConfig
@@ -175,6 +181,10 @@ export default function thumbnailStoragePlugin(options: ThumbnailStoragePluginOp
},
},
}
return plugin
}
export type { ThumbnailStoragePluginOptions }
export { THUMBNAIL_PLUGIN_SYMBOL } from './shared.js'

View File

@@ -2,6 +2,12 @@ import type { Buffer } from 'node:buffer'
export const THUMBNAIL_PLUGIN_DATA_KEY = 'afilmory:thumbnail-storage:data'
/**
* Unique symbol identifier for the thumbnail storage plugin.
* Used for reliable plugin detection without fragile string matching.
*/
export const THUMBNAIL_PLUGIN_SYMBOL = Symbol.for('afilmory:thumbnail-storage')
export interface ThumbnailPluginData {
photoId: string
fileName: string