@jwilleke/ngdpbase-types 0.0.0 → 5.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +16 -2
- package/lib/packages/ngdpbase-types/index.d.ts +32 -0
- package/lib/packages/ngdpbase-types/testing.d.ts +43 -0
- package/lib/packages/ngdpbase-types/testing.js +53 -0
- package/lib/src/cache/CacheDependencies.d.ts +50 -0
- package/lib/src/cache/CacheDependencies.js +50 -0
- package/lib/src/cache/ICacheAdapter.d.ts +86 -0
- package/lib/src/cache/RegionCache.d.ts +171 -0
- package/lib/src/context/ActorContext.d.ts +48 -0
- package/lib/src/context/ActorContext.js +33 -0
- package/lib/src/context/ApiContext.d.ts +194 -0
- package/lib/src/context/ApiContext.js +273 -0
- package/lib/src/context/BaseContext.d.ts +110 -0
- package/lib/src/context/BaseContext.js +143 -0
- package/lib/src/context/JobContext.d.ts +153 -0
- package/lib/src/context/JobContext.js +144 -0
- package/lib/src/context/WikiContext.d.ts +404 -0
- package/lib/src/context/bootActions.d.ts +64 -0
- package/lib/src/context/bootActions.js +111 -0
- package/lib/src/converters/HtmlConverter.d.ts +70 -0
- package/lib/src/converters/IContentConverter.d.ts +94 -0
- package/lib/src/converters/JSPWikiConverter.d.ts +117 -0
- package/lib/src/converters/conversionWarning.d.ts +15 -0
- package/lib/src/converters/ncm/bridge.d.ts +22 -0
- package/lib/src/converters/ncm/existing.d.ts +23 -0
- package/lib/src/converters/ncm/fix/blocks.d.ts +89 -0
- package/lib/src/converters/ncm/fix/bulletMarkers.d.ts +21 -0
- package/lib/src/converters/ncm/fix/index.d.ts +36 -0
- package/lib/src/converters/ncm/fix/jspwikiBullets.d.ts +20 -0
- package/lib/src/converters/ncm/fix/jspwikiCodeMarkers.d.ts +38 -0
- package/lib/src/converters/ncm/fix/jspwikiHeadings.d.ts +25 -0
- package/lib/src/converters/ncm/fix/jspwikiIndent.d.ts +24 -0
- package/lib/src/converters/ncm/fix/jspwikiInsert.d.ts +21 -0
- package/lib/src/converters/ncm/fix/jspwikiItalic.d.ts +29 -0
- package/lib/src/converters/ncm/fix/jspwikiLinkLeftovers.d.ts +27 -0
- package/lib/src/converters/ncm/fix/moreInformationFooter.d.ts +25 -0
- package/lib/src/converters/ncm/fix/styleClosers.d.ts +25 -0
- package/lib/src/converters/ncm/fix/tightenLists.d.ts +30 -0
- package/lib/src/converters/ncm/fix/types.d.ts +33 -0
- package/lib/src/converters/ncm/footnotes.d.ts +39 -0
- package/lib/src/converters/ncm/images.d.ts +49 -0
- package/lib/src/converters/ncm/index.d.ts +20 -0
- package/lib/src/converters/ncm/links.d.ts +21 -0
- package/lib/src/converters/ncm/normalize.d.ts +39 -0
- package/lib/src/converters/ncm/placeholder.d.ts +25 -0
- package/lib/src/converters/ncm/tables.d.ts +36 -0
- package/lib/src/converters/ncm/types.d.ts +42 -0
- package/lib/src/http/egressPolicy.d.ts +52 -0
- package/lib/src/http/guardedFetch.d.ts +89 -0
- package/lib/src/http/guardedFetch.js +113 -0
- package/lib/src/http/guardedLookup.d.ts +48 -0
- package/lib/src/http/guardedLookup.js +73 -0
- package/lib/src/http/ssrf.d.ts +83 -0
- package/lib/src/http/ssrf.js +311 -0
- package/lib/src/mail/MailProvider.d.ts +36 -0
- package/lib/src/mail/NodemailerMailProvider.d.ts +26 -0
- package/lib/src/managers/AddonsManager.d.ts +691 -0
- package/lib/src/managers/AgentTokenManager.d.ts +269 -0
- package/lib/src/managers/AssetManager.d.ts +116 -0
- package/lib/src/managers/AttachmentManager.d.ts +1032 -0
- package/lib/src/managers/AuthManager.d.ts +1257 -0
- package/lib/src/managers/BackgroundJobManager.d.ts +434 -0
- package/lib/src/managers/BaseManager.d.ts +427 -0
- package/lib/src/managers/BaseManager.js +415 -0
- package/lib/src/managers/CacheManager.d.ts +159 -0
- package/lib/src/managers/CatalogManager.d.ts +235 -0
- package/lib/src/managers/CommentManager.d.ts +75 -0
- package/lib/src/managers/ConfigurationManager.d.ts +747 -0
- package/lib/src/managers/DatabaseManager.d.ts +188 -0
- package/lib/src/managers/DatabaseManager.js +590 -0
- package/lib/src/managers/EmailManager.d.ts +47 -0
- package/lib/src/managers/ExportManager.d.ts +129 -0
- package/lib/src/managers/ExportManager.js +420 -0
- package/lib/src/managers/FootnoteManager.d.ts +118 -0
- package/lib/src/managers/MediaManager.d.ts +279 -0
- package/lib/src/managers/MetricsManager.d.ts +89 -0
- package/lib/src/managers/NotificationManager.d.ts +165 -0
- package/lib/src/managers/OidcManager.d.ts +210 -0
- package/lib/src/managers/OrganizationManager.d.ts +107 -0
- package/lib/src/managers/PageManager.d.ts +1645 -0
- package/lib/src/managers/PersonManager.d.ts +48 -0
- package/lib/src/managers/PluginManager.d.ts +179 -0
- package/lib/src/managers/PluginManager.js +393 -0
- package/lib/src/managers/PolicyEvaluator.d.ts +213 -0
- package/lib/src/managers/RenderingManager.d.ts +358 -0
- package/lib/src/managers/RoleManager.d.ts +134 -0
- package/lib/src/managers/SearchManager.d.ts +402 -0
- package/lib/src/managers/ShareManager.d.ts +251 -0
- package/lib/src/managers/TemplateManager.d.ts +176 -0
- package/lib/src/managers/ThemeManager.d.ts +56 -0
- package/lib/src/managers/UserManager.d.ts +480 -0
- package/lib/src/managers/ValidationManager.d.ts +426 -0
- package/lib/src/managers/VariableManager.d.ts +145 -0
- package/lib/src/middleware/csrf.d.ts +3 -0
- package/lib/src/parsers/LinkParser.d.ts +457 -0
- package/lib/src/parsers/MarkupParser.d.ts +769 -0
- package/lib/src/parsers/context/ParseContext.d.ts +314 -0
- package/lib/src/parsers/data/emoji-map.d.ts +18 -0
- package/lib/src/parsers/dom/DOMBuilder.d.ts +210 -0
- package/lib/src/parsers/dom/DOMParser.d.ts +252 -0
- package/lib/src/parsers/dom/Tokenizer.d.ts +358 -0
- package/lib/src/parsers/dom/WikiDocument.d.ts +272 -0
- package/lib/src/parsers/dom/handlers/DOMLinkHandler.d.ts +260 -0
- package/lib/src/parsers/dom/handlers/DOMPluginHandler.d.ts +212 -0
- package/lib/src/parsers/dom/handlers/DOMVariableHandler.d.ts +171 -0
- package/lib/src/parsers/filters/BaseFilter.d.ts +269 -0
- package/lib/src/parsers/filters/FilterChain.d.ts +320 -0
- package/lib/src/parsers/handlers/BaseSyntaxHandler.d.ts +331 -0
- package/lib/src/parsers/handlers/HandlerRegistry.d.ts +225 -0
- package/lib/src/parsers/handlers/JSPWikiPreprocessor.d.ts +89 -0
- package/lib/src/parsers/handlers/LinkParserHandler.d.ts +80 -0
- package/lib/src/parsers/handlers/PluginSyntaxHandler.d.ts +78 -0
- package/lib/src/parsers/handlers/WikiFormHandler.d.ts +149 -0
- package/lib/src/parsers/handlers/WikiTagHandler.d.ts +125 -0
- package/lib/src/parsers/jspwikiTableRow.d.ts +39 -0
- package/lib/src/parsers/styleBlockSyntax.d.ts +14 -0
- package/lib/src/plugins/types.d.ts +79 -0
- package/lib/src/providers/AgentTokenAuthProvider.d.ts +40 -0
- package/lib/src/providers/AuthentikBearerAuthProvider.d.ts +75 -0
- package/lib/src/providers/BaseAttachmentProvider.d.ts +197 -0
- package/lib/src/providers/BaseAuthProvider.d.ts +220 -0
- package/lib/src/providers/BaseCacheProvider.d.ts +140 -0
- package/lib/src/providers/BaseCredentialsProvider.d.ts +84 -0
- package/lib/src/providers/BaseDatabaseProvider.d.ts +22 -0
- package/lib/src/providers/BaseDatabaseProvider.js +12 -0
- package/lib/src/providers/BaseLoggingProvider.d.ts +68 -0
- package/lib/src/providers/BaseLoggingProvider.js +37 -0
- package/lib/src/providers/BaseMediaProvider.d.ts +308 -0
- package/lib/src/providers/BasePageProvider.d.ts +337 -0
- package/lib/src/providers/BaseProvider.d.ts +98 -0
- package/lib/src/providers/BaseProvider.js +74 -0
- package/lib/src/providers/BaseSearchProvider.d.ts +362 -0
- package/lib/src/providers/BasicAttachmentProvider.d.ts +599 -0
- package/lib/src/providers/CloudflareAccessAuthProvider.d.ts +72 -0
- package/lib/src/providers/FileCredentialsProvider.d.ts +33 -0
- package/lib/src/providers/FileJobStateProvider.d.ts +58 -0
- package/lib/src/providers/FileLoggingProvider.d.ts +43 -0
- package/lib/src/providers/FileLoggingProvider.js +120 -0
- package/lib/src/providers/FileOidcAdapter.d.ts +25 -0
- package/lib/src/providers/FileSystemMediaProvider.d.ts +403 -0
- package/lib/src/providers/LunrSearchProvider.d.ts +280 -0
- package/lib/src/providers/MagicLinkAuthProvider.d.ts +125 -0
- package/lib/src/providers/NullCacheProvider.d.ts +72 -0
- package/lib/src/providers/OidcAuthProvider.d.ts +180 -0
- package/lib/src/providers/OidcBearerAuthProvider.d.ts +30 -0
- package/lib/src/providers/PasskeyAuthProvider.d.ts +58 -0
- package/lib/src/providers/PasswordAuthProvider.d.ts +20 -0
- package/lib/src/providers/SqliteDatabaseProvider.d.ts +55 -0
- package/lib/src/providers/SqliteDatabaseProvider.js +90 -0
- package/lib/src/providers/SqliteJobStateProvider.d.ts +41 -0
- package/lib/src/providers/databaseMigrations.d.ts +8 -0
- package/lib/src/providers/databaseMigrations.js +47 -0
- package/lib/src/providers/jobState.d.ts +99 -0
- package/lib/src/providers/lunrQueryToMiniSearch.d.ts +32 -0
- package/lib/src/providers/mediaFilenameDate.d.ts +22 -0
- package/lib/src/providers/sqliteBackup.d.ts +128 -0
- package/lib/src/providers/sqliteBackup.js +365 -0
- package/lib/src/providers/sqliteLocation.d.ts +17 -0
- package/lib/src/providers/sqliteLocation.js +55 -0
- package/lib/src/providers/sqliteMigrations.d.ts +48 -0
- package/lib/src/providers/sqliteMigrations.js +72 -0
- package/lib/src/rendering/githubAlerts.d.ts +22 -0
- package/lib/src/rendering/htmlPolicy.d.ts +76 -0
- package/lib/src/rendering/markdownConverter.d.ts +73 -0
- package/lib/src/security/PolicyDecisionPoint.d.ts +245 -0
- package/lib/src/security/PolicyInformationPoint.d.ts +448 -0
- package/lib/src/security/pageActions.d.ts +40 -0
- package/lib/src/security/permissions.generated.d.ts +78 -0
- package/lib/src/types/Asset.d.ts +414 -0
- package/lib/src/types/Catalog.d.ts +61 -0
- package/lib/src/types/Comment.d.ts +11 -0
- package/lib/src/types/Config.d.ts +388 -0
- package/lib/src/types/Organization.d.ts +78 -0
- package/lib/src/types/OrganizationProvider.d.ts +38 -0
- package/lib/src/types/Page.d.ts +220 -0
- package/lib/src/types/Person.d.ts +56 -0
- package/lib/src/types/PersonProvider.d.ts +25 -0
- package/lib/src/types/Policy.d.ts +139 -0
- package/lib/src/types/Policy.js +17 -0
- package/lib/src/types/Provider.d.ts +810 -0
- package/lib/src/types/Role.d.ts +84 -0
- package/lib/src/types/RoleProvider.d.ts +35 -0
- package/lib/src/types/Schema.d.ts +425 -0
- package/lib/src/types/Share.d.ts +172 -0
- package/lib/src/types/Share.js +81 -0
- package/lib/src/types/User.d.ts +304 -0
- package/lib/src/types/Version.d.ts +189 -0
- package/lib/src/types/WikiEngine.d.ts +128 -0
- package/lib/src/types/express.d.ts +120 -0
- package/lib/src/types/express.js +1 -0
- package/lib/src/types/guards.d.ts +78 -0
- package/lib/src/types/heic-decode.d.ts +16 -0
- package/lib/src/types/index.d.ts +19 -0
- package/lib/src/types/linkedom.d.ts +77 -0
- package/lib/src/types/markdown-it-plugins.d.ts +21 -0
- package/lib/src/utils/DeltaStorage.d.ts +118 -0
- package/lib/src/utils/LocaleUtils.d.ts +105 -0
- package/lib/src/utils/LocaleUtils.js +294 -0
- package/lib/src/utils/PageNameMatcher.d.ts +98 -0
- package/lib/src/utils/PathPreflight.d.ts +38 -0
- package/lib/src/utils/PathPreflight.js +50 -0
- package/lib/src/utils/SectionUtils.d.ts +40 -0
- package/lib/src/utils/addonConfigLayer.d.ts +144 -0
- package/lib/src/utils/addonConfigLayer.js +382 -0
- package/lib/src/utils/addonDatabaseRules.d.ts +25 -0
- package/lib/src/utils/addonDatabaseRules.js +57 -0
- package/lib/src/utils/addonHost.d.ts +51 -0
- package/lib/src/utils/addonHost.js +72 -0
- package/lib/src/utils/addonInstaller.d.ts +109 -0
- package/lib/src/utils/addonLock.d.ts +37 -0
- package/lib/src/utils/addonLock.js +48 -0
- package/lib/src/utils/addonPageSync.d.ts +45 -0
- package/lib/src/utils/addonProvenance.d.ts +48 -0
- package/lib/src/utils/addonTarball.d.ts +41 -0
- package/lib/src/utils/addonsPathResolver.d.ts +76 -0
- package/lib/src/utils/addonsPathResolver.js +129 -0
- package/lib/src/utils/assetMetadataTags.d.ts +66 -0
- package/lib/src/utils/atomicWrite.d.ts +28 -0
- package/lib/src/utils/auditConfigChange.d.ts +57 -0
- package/lib/src/utils/auditEventNames.d.ts +50 -0
- package/lib/src/utils/auditEventNames.generated.d.ts +169 -0
- package/lib/src/utils/auditEventNames.generated.js +174 -0
- package/lib/src/utils/auditEventNames.js +49 -0
- package/lib/src/utils/auditEvents.d.ts +265 -0
- package/lib/src/utils/auditEvents.js +309 -0
- package/lib/src/utils/auditRegistry.d.ts +102 -0
- package/lib/src/utils/auditRegistry.js +168 -0
- package/lib/src/utils/bip39English.d.ts +2 -0
- package/lib/src/utils/configEnvKeys.d.ts +68 -0
- package/lib/src/utils/configFiles.d.ts +74 -0
- package/lib/src/utils/configFiles.js +150 -0
- package/lib/src/utils/delegation.d.ts +48 -0
- package/lib/src/utils/deriveUsername.d.ts +21 -0
- package/lib/src/utils/explainMediaPath.d.ts +123 -0
- package/lib/src/utils/formerTitles.d.ts +69 -0
- package/lib/src/utils/frontmatterAccess.d.ts +54 -0
- package/lib/src/utils/headlessAdminPassword.d.ts +55 -0
- package/lib/src/utils/imageTransform.d.ts +52 -0
- package/lib/src/utils/instanceEnvSecret.d.ts +51 -0
- package/lib/src/utils/jobSlots.d.ts +43 -0
- package/lib/src/utils/keywordNormalizer.d.ts +127 -0
- package/lib/src/utils/logger.d.ts +70 -0
- package/lib/src/utils/logger.js +106 -0
- package/lib/src/utils/magicLinkDeviceState.d.ts +95 -0
- package/lib/src/utils/managerUtils.d.ts +40 -0
- package/lib/src/utils/migrateLegacyPrivateAttachments.d.ts +15 -0
- package/lib/src/utils/orgFilename.d.ts +22 -0
- package/lib/src/utils/pageFeature.d.ts +27 -0
- package/lib/src/utils/pageFrontmatter.d.ts +21 -0
- package/lib/src/utils/pageNotes.d.ts +150 -0
- package/lib/src/utils/pageTitleRule.d.ts +37 -0
- package/lib/src/utils/pageToArticle.d.ts +59 -0
- package/lib/src/utils/pageUrl.d.ts +18 -0
- package/lib/src/utils/passwordChange.d.ts +12 -0
- package/lib/src/utils/passwordHash.d.ts +60 -0
- package/lib/src/utils/permissionCatalog.d.ts +52 -0
- package/lib/src/utils/pluginFormatters.d.ts +452 -0
- package/lib/src/utils/pluginFormatters.js +714 -0
- package/lib/src/utils/privateLinkRewrite.d.ts +42 -0
- package/lib/src/utils/privateStoreAccess.d.ts +57 -0
- package/lib/src/utils/privateStoreAccess.js +65 -0
- package/lib/src/utils/privateStoreCatalogs.d.ts +40 -0
- package/lib/src/utils/privateStoreCrypto.d.ts +127 -0
- package/lib/src/utils/privateStoreDoor.d.ts +181 -0
- package/lib/src/utils/privateStoreExport.d.ts +72 -0
- package/lib/src/utils/privateStoreFiles.d.ts +120 -0
- package/lib/src/utils/privateStoreMeta.d.ts +64 -0
- package/lib/src/utils/privateStoreMigrations.d.ts +37 -0
- package/lib/src/utils/privateStorePath.d.ts +259 -0
- package/lib/src/utils/privateStorePath.js +436 -0
- package/lib/src/utils/privateStoreTakeout.d.ts +76 -0
- package/lib/src/utils/privateStoreUnlock.d.ts +175 -0
- package/lib/src/utils/proxyClaim.d.ts +59 -0
- package/lib/src/utils/proxyClaim.js +30 -0
- package/lib/src/utils/redactPrivateNames.d.ts +21 -0
- package/lib/src/utils/redactPrivateNames.js +89 -0
- package/lib/src/utils/redactSecrets.d.ts +58 -0
- package/lib/src/utils/redactSecrets.js +171 -0
- package/lib/src/utils/renameLinkRewrite.d.ts +112 -0
- package/lib/src/utils/roleGrants.d.ts +35 -0
- package/lib/src/utils/safeRedirect.d.ts +32 -0
- package/lib/src/utils/schedule.d.ts +54 -0
- package/lib/src/utils/secretFileMode.d.ts +17 -0
- package/lib/src/utils/seededShippedPages.d.ts +45 -0
- package/lib/src/utils/sensitiveValues.d.ts +59 -0
- package/lib/src/utils/sensitiveValues.js +106 -0
- package/lib/src/utils/sessionEnd.d.ts +12 -0
- package/lib/src/utils/sessionGeneration.d.ts +24 -0
- package/lib/src/utils/sessionIdle.d.ts +41 -0
- package/lib/src/utils/sessionLimits.d.ts +68 -0
- package/lib/src/utils/shareLockbox.d.ts +64 -0
- package/lib/src/utils/sigstoreTrustedRoot.d.ts +33 -0
- package/lib/src/utils/sniffMime.d.ts +21 -0
- package/lib/src/utils/storeSearchIndex.d.ts +75 -0
- package/lib/src/utils/subjectMayDo.d.ts +21 -0
- package/lib/src/utils/systemCategoryRules.d.ts +30 -0
- package/lib/src/utils/systemCategoryRules.js +41 -0
- package/lib/src/utils/userCreateError.d.ts +62 -0
- package/lib/src/utils/username.d.ts +53 -0
- package/lib/src/utils/vaultUrlRewrite.d.ts +27 -0
- package/lib/src/utils/version.d.ts +84 -0
- package/lib/src/utils/videoFrame.d.ts +21 -0
- package/package.json +54 -3
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import BaseManager, { type ManagerStats } from './BaseManager.js';
|
|
2
|
+
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
3
|
+
import type { Person, PersonUpdate } from '../types/Person.js';
|
|
4
|
+
import type { PersonProvider } from '../types/PersonProvider.js';
|
|
5
|
+
/**
|
|
6
|
+
* PersonManager — canonical core record for persons (#617).
|
|
7
|
+
*
|
|
8
|
+
* Each User has at most one paired Person record. UserManager calls
|
|
9
|
+
* `create`/`update`/`delete` here on the matching user lifecycle events.
|
|
10
|
+
* `getByUserIdentifier(username)` remains as a lazy-migration entry point
|
|
11
|
+
* for legacy users that pre-date the sync wiring.
|
|
12
|
+
*/
|
|
13
|
+
declare class PersonManager extends BaseManager {
|
|
14
|
+
readonly description = "Canonical Person records (#617)";
|
|
15
|
+
private provider;
|
|
16
|
+
private providerClass?;
|
|
17
|
+
private byIdentifierCache;
|
|
18
|
+
private byIdCache;
|
|
19
|
+
private metricsRef;
|
|
20
|
+
constructor(engine: WikiEngine);
|
|
21
|
+
initialize(config?: Record<string, unknown>): Promise<void>;
|
|
22
|
+
getProvider(): PersonProvider | null;
|
|
23
|
+
getById(id: string): Promise<Person | null>;
|
|
24
|
+
getByIdentifier(identifier: string): Promise<Person | null>;
|
|
25
|
+
/** #1006: how many people this instance holds. Count only, never records. */
|
|
26
|
+
getManagerStats(): Promise<ManagerStats>;
|
|
27
|
+
list(): Promise<Person[]>;
|
|
28
|
+
create(person: Person): Promise<Person>;
|
|
29
|
+
update(id: string, patch: PersonUpdate): Promise<Person | null>;
|
|
30
|
+
delete(id: string): Promise<boolean>;
|
|
31
|
+
/**
|
|
32
|
+
* Clear all caches. Called on every write — the simplest correct
|
|
33
|
+
* invalidation. Per-key invalidation can come later if needed.
|
|
34
|
+
*/
|
|
35
|
+
invalidateCache(): void;
|
|
36
|
+
/**
|
|
37
|
+
* Lazy-migration entry point.
|
|
38
|
+
*
|
|
39
|
+
* Returns the Person paired to `username`. If none exists and a matching
|
|
40
|
+
* User does, a Person is created from the User's email/displayName and
|
|
41
|
+
* persisted. Subsequent calls return the persisted record.
|
|
42
|
+
*/
|
|
43
|
+
getByUserIdentifier(username: string): Promise<Person | null>;
|
|
44
|
+
private requireProvider;
|
|
45
|
+
private getMetrics;
|
|
46
|
+
private normalizeProviderName;
|
|
47
|
+
}
|
|
48
|
+
export default PersonManager;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import BaseManager from './BaseManager.js';
|
|
2
|
+
import * as pluginFormatters from '../utils/pluginFormatters.js';
|
|
3
|
+
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
4
|
+
/**
|
|
5
|
+
* Plugin function type - old-style callable function plugin
|
|
6
|
+
*/
|
|
7
|
+
export type PluginFunction = (pageName: string, params: PluginParams, linkGraph: Record<string, unknown>) => Promise<string> | string;
|
|
8
|
+
/**
|
|
9
|
+
* Plugin object interface - new-style plugin with execute method
|
|
10
|
+
*/
|
|
11
|
+
export interface PluginObject {
|
|
12
|
+
name?: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
author?: string;
|
|
15
|
+
version?: string;
|
|
16
|
+
initialize?: (engine: WikiEngine) => Promise<void> | void;
|
|
17
|
+
fetch?: (engine: WikiEngine) => Promise<void> | void;
|
|
18
|
+
execute: (context: PluginContext, params: PluginParams) => Promise<string> | string;
|
|
19
|
+
/**
|
|
20
|
+
* Its output changes with nothing written (a clock): a page that runs it is
|
|
21
|
+
* not kept in the page cache (#1751). Data a plugin reads from a manager is
|
|
22
|
+
* declared with `context.dependsOn(topic)` instead, which keeps the page
|
|
23
|
+
* cached until that data changes.
|
|
24
|
+
*/
|
|
25
|
+
volatile?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Plugin type - can be either a function or an object with execute method
|
|
29
|
+
*/
|
|
30
|
+
export type Plugin = PluginFunction | PluginObject;
|
|
31
|
+
/**
|
|
32
|
+
* The plugin formatter helpers PluginManager owns (#1820): the functions and
|
|
33
|
+
* constants `src/utils/pluginFormatters.ts` exports, as the same objects.
|
|
34
|
+
* Their types stay importable from that module with `import type`.
|
|
35
|
+
*/
|
|
36
|
+
export type PluginFormatters = Readonly<typeof pluginFormatters>;
|
|
37
|
+
export declare const formatters: PluginFormatters;
|
|
38
|
+
/**
|
|
39
|
+
* Plugin context passed to plugins during execution
|
|
40
|
+
*/
|
|
41
|
+
export interface PluginContext {
|
|
42
|
+
engine: WikiEngine;
|
|
43
|
+
pageName: string;
|
|
44
|
+
linkGraph: Record<string, unknown>;
|
|
45
|
+
/** #1820: PluginManager's formatter helpers, e.g. `context.formatters.escapeHtml(s)`. */
|
|
46
|
+
formatters: PluginFormatters;
|
|
47
|
+
/**
|
|
48
|
+
* #1751: this render reads `topic`'s data (by convention the owning
|
|
49
|
+
* manager's name, e.g. 'LedgerManager'). Call before reading; the cached
|
|
50
|
+
* page is re-rendered once the manager bumps that topic.
|
|
51
|
+
*/
|
|
52
|
+
dependsOn?: (topic: string) => void;
|
|
53
|
+
/** #1751: this render's output changes on its own; do not cache the page. */
|
|
54
|
+
markVolatile?: () => void;
|
|
55
|
+
[key: string]: unknown;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Plugin parameters (parsed from plugin syntax)
|
|
59
|
+
*/
|
|
60
|
+
export interface PluginParams {
|
|
61
|
+
[key: string]: string | number | boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Plugin information returned by getPluginInfo
|
|
65
|
+
*/
|
|
66
|
+
export interface PluginInfo {
|
|
67
|
+
name: string;
|
|
68
|
+
description: string;
|
|
69
|
+
author: string;
|
|
70
|
+
version: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* PluginManager - Handles plugin discovery, registration, and execution
|
|
74
|
+
*
|
|
75
|
+
* Similar to JSPWiki's PluginManager, this manager provides a plugin system
|
|
76
|
+
* for extending wiki functionality. Plugins are discovered from configured
|
|
77
|
+
* search paths and executed during markup parsing.
|
|
78
|
+
*
|
|
79
|
+
* Key features:
|
|
80
|
+
* - Dynamic plugin discovery from search paths
|
|
81
|
+
* - Plugin registration and metadata management
|
|
82
|
+
* - Secure plugin execution with sandboxing
|
|
83
|
+
* - Configurable plugin search paths
|
|
84
|
+
*
|
|
85
|
+
* @class PluginManager
|
|
86
|
+
* @extends BaseManager
|
|
87
|
+
*
|
|
88
|
+
* @property {Map<string, Plugin>} plugins - Registered plugins
|
|
89
|
+
* @property {string[]} searchPaths - Directories to search for plugins
|
|
90
|
+
* @property {string[]} allowedRoots - Allowed root paths for security
|
|
91
|
+
*
|
|
92
|
+
* @see {@link BaseManager} for base functionality
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* const pluginManager = engine.getManager('PluginManager');
|
|
96
|
+
* const result = await pluginManager.execute('CurrentTimePlugin', params);
|
|
97
|
+
*/
|
|
98
|
+
declare class PluginManager extends BaseManager {
|
|
99
|
+
private plugins;
|
|
100
|
+
private allowedRoots;
|
|
101
|
+
/**
|
|
102
|
+
* The plugin formatter helpers this manager owns (#1820), frozen. Plugins
|
|
103
|
+
* receive them as `context.formatters`; other callers read them here.
|
|
104
|
+
*/
|
|
105
|
+
readonly formatters: PluginFormatters;
|
|
106
|
+
/**
|
|
107
|
+
* Creates a new PluginManager instance
|
|
108
|
+
*
|
|
109
|
+
* @constructor
|
|
110
|
+
* @param {any} engine - The wiki engine instance
|
|
111
|
+
*/
|
|
112
|
+
constructor(engine: WikiEngine);
|
|
113
|
+
/**
|
|
114
|
+
* Initialize the PluginManager and discover plugins
|
|
115
|
+
*
|
|
116
|
+
* @async
|
|
117
|
+
* @param {Record<string, unknown>} [config={}] - Configuration object (unused, reads from ConfigurationManager)
|
|
118
|
+
* @returns {Promise<void>}
|
|
119
|
+
*/
|
|
120
|
+
initialize(config?: Record<string, unknown>): Promise<void>;
|
|
121
|
+
/**
|
|
122
|
+
* Register all plugins from search paths obtained ONLY from
|
|
123
|
+
* ConfigurationManager at key: ngdpbase.managers.plugin-manager.search-paths
|
|
124
|
+
*/
|
|
125
|
+
registerPlugins(): Promise<void>;
|
|
126
|
+
/**
|
|
127
|
+
* Load a single plugin from a validated, allowed root
|
|
128
|
+
* @param {string} pluginPath - Path to the plugin file
|
|
129
|
+
*/
|
|
130
|
+
loadPlugin(pluginPath: string): Promise<void>;
|
|
131
|
+
/**
|
|
132
|
+
* Find plugin by name (case-insensitive)
|
|
133
|
+
* Supports JSPWiki-style plugin naming where you can use either:
|
|
134
|
+
* - "Search" or "SearchPlugin"
|
|
135
|
+
* - "Index" or "IndexPlugin"
|
|
136
|
+
*
|
|
137
|
+
* @param {string} pluginName - Name of the plugin to find
|
|
138
|
+
* @returns {Plugin|null} Plugin object or null if not found
|
|
139
|
+
*/
|
|
140
|
+
findPlugin(pluginName: string): Plugin | null;
|
|
141
|
+
/**
|
|
142
|
+
* Execute a plugin
|
|
143
|
+
* @param {string} pluginName - Name of the plugin
|
|
144
|
+
* @param {string} pageName - Current page name
|
|
145
|
+
* @param {PluginParams} params - Plugin parameters (parsed object)
|
|
146
|
+
* @param {Record<string, unknown>} context - Additional context
|
|
147
|
+
* @returns {Promise<string>} Plugin output
|
|
148
|
+
*/
|
|
149
|
+
execute(pluginName: string, pageName: string, params: PluginParams, context?: Record<string, unknown>): Promise<string>;
|
|
150
|
+
/**
|
|
151
|
+
* Get list of all registered plugins
|
|
152
|
+
* @returns {string[]} Array of plugin names
|
|
153
|
+
*/
|
|
154
|
+
getPluginNames(): string[];
|
|
155
|
+
/**
|
|
156
|
+
* Get plugin info
|
|
157
|
+
* @param {string} pluginName - Name of the plugin
|
|
158
|
+
* @returns {PluginInfo | null} Plugin information
|
|
159
|
+
*/
|
|
160
|
+
getPluginInfo(pluginName: string): PluginInfo | null;
|
|
161
|
+
/**
|
|
162
|
+
* Check if plugin exists
|
|
163
|
+
* @param {string} pluginName - Name of the plugin
|
|
164
|
+
* @returns {boolean} True if plugin exists
|
|
165
|
+
*/
|
|
166
|
+
hasPlugin(pluginName: string): boolean;
|
|
167
|
+
/**
|
|
168
|
+
* Programmatically register a plugin object (for use by add-ons).
|
|
169
|
+
*
|
|
170
|
+
* Skips the file-path security check used by loadPlugin() — the caller is
|
|
171
|
+
* responsible for providing a trusted Plugin instance. Calls
|
|
172
|
+
* plugin.initialize(engine) if present, consistent with loadPlugin().
|
|
173
|
+
*
|
|
174
|
+
* @param {string} name - Plugin name (used for [{name ...}] invocation)
|
|
175
|
+
* @param {Plugin} plugin - Plugin function or PluginObject
|
|
176
|
+
*/
|
|
177
|
+
registerPlugin(name: string, plugin: Plugin): Promise<void>;
|
|
178
|
+
}
|
|
179
|
+
export default PluginManager;
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import CacheDependencies from '../cache/CacheDependencies.js';
|
|
2
|
+
import BaseManager from './BaseManager.js';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import logger from '../utils/logger.js';
|
|
6
|
+
import * as pluginFormatters from '../utils/pluginFormatters.js';
|
|
7
|
+
import { enabledEntries } from '../utils/configFiles.js';
|
|
8
|
+
/**
|
|
9
|
+
* Type guard to check if plugin is an object with execute method
|
|
10
|
+
*/
|
|
11
|
+
function isPluginObject(obj) {
|
|
12
|
+
return typeof obj === 'object' && obj !== null && typeof obj.execute === 'function';
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Type guard to check if an unknown value is a valid Plugin
|
|
16
|
+
*/
|
|
17
|
+
function isPlugin(obj) {
|
|
18
|
+
return typeof obj === 'function' || isPluginObject(obj);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The render's dependency collector (#1751): on the parse context the plugin
|
|
22
|
+
* handlers pass as `wikiContext`, or on the context itself.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* The engine a plugin sees while its render is being cached (#1751): each
|
|
26
|
+
* `getManager(name)` also records `name` as data this render read, so the page
|
|
27
|
+
* re-renders when that manager bumps its topic. One path for every plugin,
|
|
28
|
+
* core or add-on — none declares what it reads.
|
|
29
|
+
*/
|
|
30
|
+
function trackedEngine(engine, deps) {
|
|
31
|
+
return new Proxy(engine, {
|
|
32
|
+
get(target, prop) {
|
|
33
|
+
if (prop === 'getManager') {
|
|
34
|
+
return (name) => {
|
|
35
|
+
deps.dependsOn(name);
|
|
36
|
+
return target.getManager(name);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const value = Reflect.get(target, prop, target);
|
|
40
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function cacheDependenciesOf(context) {
|
|
45
|
+
const own = context.cacheDependencies;
|
|
46
|
+
if (own instanceof CacheDependencies)
|
|
47
|
+
return own;
|
|
48
|
+
const parse = context.wikiContext?.cacheDependencies;
|
|
49
|
+
return parse instanceof CacheDependencies ? parse : undefined;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Type guard to check if a module export has a default export
|
|
53
|
+
*/
|
|
54
|
+
function hasDefaultExport(obj) {
|
|
55
|
+
return typeof obj === 'object' && obj !== null && 'default' in obj;
|
|
56
|
+
}
|
|
57
|
+
export const formatters = Object.freeze({ ...pluginFormatters });
|
|
58
|
+
/**
|
|
59
|
+
* PluginManager - Handles plugin discovery, registration, and execution
|
|
60
|
+
*
|
|
61
|
+
* Similar to JSPWiki's PluginManager, this manager provides a plugin system
|
|
62
|
+
* for extending wiki functionality. Plugins are discovered from configured
|
|
63
|
+
* search paths and executed during markup parsing.
|
|
64
|
+
*
|
|
65
|
+
* Key features:
|
|
66
|
+
* - Dynamic plugin discovery from search paths
|
|
67
|
+
* - Plugin registration and metadata management
|
|
68
|
+
* - Secure plugin execution with sandboxing
|
|
69
|
+
* - Configurable plugin search paths
|
|
70
|
+
*
|
|
71
|
+
* @class PluginManager
|
|
72
|
+
* @extends BaseManager
|
|
73
|
+
*
|
|
74
|
+
* @property {Map<string, Plugin>} plugins - Registered plugins
|
|
75
|
+
* @property {string[]} searchPaths - Directories to search for plugins
|
|
76
|
+
* @property {string[]} allowedRoots - Allowed root paths for security
|
|
77
|
+
*
|
|
78
|
+
* @see {@link BaseManager} for base functionality
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* const pluginManager = engine.getManager('PluginManager');
|
|
82
|
+
* const result = await pluginManager.execute('CurrentTimePlugin', params);
|
|
83
|
+
*/
|
|
84
|
+
class PluginManager extends BaseManager {
|
|
85
|
+
plugins;
|
|
86
|
+
allowedRoots;
|
|
87
|
+
/**
|
|
88
|
+
* The plugin formatter helpers this manager owns (#1820), frozen. Plugins
|
|
89
|
+
* receive them as `context.formatters`; other callers read them here.
|
|
90
|
+
*/
|
|
91
|
+
formatters = formatters;
|
|
92
|
+
/**
|
|
93
|
+
* Creates a new PluginManager instance
|
|
94
|
+
*
|
|
95
|
+
* @constructor
|
|
96
|
+
* @param {any} engine - The wiki engine instance
|
|
97
|
+
*/
|
|
98
|
+
constructor(engine) {
|
|
99
|
+
super(engine);
|
|
100
|
+
this.plugins = new Map();
|
|
101
|
+
this.allowedRoots = [];
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Initialize the PluginManager and discover plugins
|
|
105
|
+
*
|
|
106
|
+
* @async
|
|
107
|
+
* @param {Record<string, unknown>} [config={}] - Configuration object (unused, reads from ConfigurationManager)
|
|
108
|
+
* @returns {Promise<void>}
|
|
109
|
+
*/
|
|
110
|
+
async initialize(config = {}) {
|
|
111
|
+
await super.initialize(config);
|
|
112
|
+
// Do NOT read search paths from config here; only ConfigurationManager controls them
|
|
113
|
+
await this.registerPlugins();
|
|
114
|
+
this.engine.logger?.info?.('PluginManager initialized');
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Register all plugins from search paths obtained ONLY from
|
|
118
|
+
* ConfigurationManager at key: ngdpbase.managers.plugin-manager.search-paths
|
|
119
|
+
*/
|
|
120
|
+
async registerPlugins() {
|
|
121
|
+
const cfgMgr = this.engine.getManager('ConfigurationManager');
|
|
122
|
+
if (!cfgMgr || typeof cfgMgr.getProperty !== 'function') {
|
|
123
|
+
this.engine.logger?.warn?.('PluginManager: ConfigurationManager not available; no plugins will be loaded.');
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// MUST come only from config; no fallbacks
|
|
127
|
+
const raw = cfgMgr.getProperty('ngdpbase.managers.plugin-manager.search-paths');
|
|
128
|
+
this.engine.logger?.debug?.(`PluginManager: raw searchPaths type=${typeof raw} value=${JSON.stringify(raw)}`);
|
|
129
|
+
// Accept a set map, an array, or a comma-separated string
|
|
130
|
+
let configured = enabledEntries(raw);
|
|
131
|
+
if (typeof raw === 'string')
|
|
132
|
+
configured = raw
|
|
133
|
+
.split(',')
|
|
134
|
+
.map((s) => s.trim())
|
|
135
|
+
.filter(Boolean);
|
|
136
|
+
if (!Array.isArray(configured) || configured.length === 0) {
|
|
137
|
+
this.engine.logger?.info?.('PluginManager: No plugin search paths configured; skipping plugin load.');
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
// Resolve and validate configured roots
|
|
141
|
+
const roots = [];
|
|
142
|
+
for (const p of configured) {
|
|
143
|
+
try {
|
|
144
|
+
const abs = path.resolve(process.cwd(), String(p));
|
|
145
|
+
if (!(await fs.pathExists(abs))) {
|
|
146
|
+
this.engine.logger?.debug?.(`PluginManager: configured path does not exist: ${abs}`);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const st = await fs.lstat(abs);
|
|
150
|
+
if (!st.isDirectory()) {
|
|
151
|
+
this.engine.logger?.warn?.(`PluginManager: configured path is not a directory: ${abs}`);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
// Use realpath to collapse symlinks for later prefix checks
|
|
155
|
+
const real = await fs.realpath(abs);
|
|
156
|
+
roots.push(real);
|
|
157
|
+
}
|
|
158
|
+
catch (e) {
|
|
159
|
+
this.engine.logger?.warn?.(`PluginManager: failed to validate configured path "${p}": ${e.message}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
this.allowedRoots = roots;
|
|
163
|
+
if (this.allowedRoots.length === 0) {
|
|
164
|
+
this.engine.logger?.info?.('PluginManager: No valid plugin roots after validation; skipping plugin load.');
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
// Enumerate .js and .ts files in allowed roots only
|
|
168
|
+
for (const root of this.allowedRoots) {
|
|
169
|
+
try {
|
|
170
|
+
const entries = await fs.readdir(root, { withFileTypes: true });
|
|
171
|
+
for (const entry of entries) {
|
|
172
|
+
if (!entry.isFile())
|
|
173
|
+
continue;
|
|
174
|
+
// Skip test files and non-plugin files
|
|
175
|
+
const isJsPlugin = entry.name.endsWith('.js') && !entry.name.endsWith('.test.js');
|
|
176
|
+
const isTsPlugin = entry.name.endsWith('.ts') && !entry.name.endsWith('.test.ts') && !entry.name.endsWith('.d.ts');
|
|
177
|
+
if (!isJsPlugin && !isTsPlugin)
|
|
178
|
+
continue;
|
|
179
|
+
const candidate = path.join(root, entry.name);
|
|
180
|
+
await this.loadPlugin(candidate); // loadPlugin re-validates path is under an allowed root
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
this.engine.logger?.warn?.(`PluginManager: Failed to load plugins from ${root}: ${err.message}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Load a single plugin from a validated, allowed root
|
|
190
|
+
* @param {string} pluginPath - Path to the plugin file
|
|
191
|
+
*/
|
|
192
|
+
async loadPlugin(pluginPath) {
|
|
193
|
+
try {
|
|
194
|
+
// Resolve and validate canonical path
|
|
195
|
+
const realFile = await fs.realpath(path.resolve(pluginPath));
|
|
196
|
+
const isAllowed = this.allowedRoots.some((root) => realFile === root || realFile.startsWith(root + path.sep));
|
|
197
|
+
if (!isAllowed) {
|
|
198
|
+
this.engine.logger?.warn?.(`PluginManager: blocked plugin outside allowed roots: ${realFile}`);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const mod = await import(realFile);
|
|
202
|
+
// Extract the plugin from the module (handle both default and direct exports)
|
|
203
|
+
const rawPlugin = hasDefaultExport(mod) ? mod.default : mod;
|
|
204
|
+
// Validate the plugin is executable
|
|
205
|
+
if (!isPlugin(rawPlugin)) {
|
|
206
|
+
const fileName = path.parse(realFile).name;
|
|
207
|
+
this.engine.logger?.warn?.(`PluginManager: "${fileName}" is not an executable plugin; skipping.`);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
// Now we know rawPlugin is a valid Plugin
|
|
211
|
+
const plugin = rawPlugin;
|
|
212
|
+
// Get plugin name from metadata or filename
|
|
213
|
+
const pluginName = (isPluginObject(plugin) && plugin.name) || path.parse(realFile).name;
|
|
214
|
+
// Initialize the plugin if it has an initialize method (only object plugins have this)
|
|
215
|
+
if (isPluginObject(plugin) && typeof plugin.initialize === 'function') {
|
|
216
|
+
await plugin.initialize(this.engine);
|
|
217
|
+
}
|
|
218
|
+
this.plugins.set(pluginName, plugin);
|
|
219
|
+
this.engine.logger?.info?.(`PluginManager: Loaded plugin "${pluginName}" from ${realFile}`);
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
this.engine.logger?.error?.(`PluginManager: Failed to load plugin ${pluginPath}: ${err.message}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Find plugin by name (case-insensitive)
|
|
227
|
+
* Supports JSPWiki-style plugin naming where you can use either:
|
|
228
|
+
* - "Search" or "SearchPlugin"
|
|
229
|
+
* - "Index" or "IndexPlugin"
|
|
230
|
+
*
|
|
231
|
+
* @param {string} pluginName - Name of the plugin to find
|
|
232
|
+
* @returns {Plugin|null} Plugin object or null if not found
|
|
233
|
+
*/
|
|
234
|
+
findPlugin(pluginName) {
|
|
235
|
+
// Try exact match first (for performance)
|
|
236
|
+
if (this.plugins.has(pluginName)) {
|
|
237
|
+
return this.plugins.get(pluginName) ?? null;
|
|
238
|
+
}
|
|
239
|
+
// Try case-insensitive match
|
|
240
|
+
const lowerName = pluginName.toLowerCase();
|
|
241
|
+
for (const [key, plugin] of this.plugins) {
|
|
242
|
+
if (key.toLowerCase() === lowerName) {
|
|
243
|
+
return plugin;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// JSPWiki compatibility: Try with "Plugin" suffix if not already present
|
|
247
|
+
if (!pluginName.toLowerCase().endsWith('plugin')) {
|
|
248
|
+
const withSuffix = pluginName + 'Plugin';
|
|
249
|
+
if (this.plugins.has(withSuffix)) {
|
|
250
|
+
return this.plugins.get(withSuffix) ?? null;
|
|
251
|
+
}
|
|
252
|
+
// Try case-insensitive with suffix
|
|
253
|
+
const lowerWithSuffix = withSuffix.toLowerCase();
|
|
254
|
+
for (const [key, plugin] of this.plugins) {
|
|
255
|
+
if (key.toLowerCase() === lowerWithSuffix) {
|
|
256
|
+
return plugin;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// Try without "Plugin" suffix if it's present
|
|
261
|
+
if (pluginName.toLowerCase().endsWith('plugin')) {
|
|
262
|
+
const withoutSuffix = pluginName.slice(0, -6); // Remove "Plugin"
|
|
263
|
+
if (this.plugins.has(withoutSuffix)) {
|
|
264
|
+
return this.plugins.get(withoutSuffix) ?? null;
|
|
265
|
+
}
|
|
266
|
+
// Try case-insensitive without suffix
|
|
267
|
+
const lowerWithoutSuffix = withoutSuffix.toLowerCase();
|
|
268
|
+
for (const [key, plugin] of this.plugins) {
|
|
269
|
+
if (key.toLowerCase() === lowerWithoutSuffix) {
|
|
270
|
+
return plugin;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Execute a plugin
|
|
278
|
+
* @param {string} pluginName - Name of the plugin
|
|
279
|
+
* @param {string} pageName - Current page name
|
|
280
|
+
* @param {PluginParams} params - Plugin parameters (parsed object)
|
|
281
|
+
* @param {Record<string, unknown>} context - Additional context
|
|
282
|
+
* @returns {Promise<string>} Plugin output
|
|
283
|
+
*/
|
|
284
|
+
async execute(pluginName, pageName, params, context = {}) {
|
|
285
|
+
const plugin = this.findPlugin(pluginName);
|
|
286
|
+
if (!plugin) {
|
|
287
|
+
return `Plugin '${pluginName}' not found`;
|
|
288
|
+
}
|
|
289
|
+
try {
|
|
290
|
+
// Pass the full context through to plugins
|
|
291
|
+
// Plugins expect the full ParseContext/WikiContext with all properties
|
|
292
|
+
// #1751: what this render reads, for the page cache's read-through.
|
|
293
|
+
const deps = cacheDependenciesOf(context);
|
|
294
|
+
if (isPluginObject(plugin) && plugin.volatile)
|
|
295
|
+
deps?.markVolatile();
|
|
296
|
+
const pluginContext = {
|
|
297
|
+
...context, // Spread all context properties
|
|
298
|
+
// Always the manager's engine; while the page cache is collecting, every
|
|
299
|
+
// manager the plugin fetches is recorded as data this render read (#1751).
|
|
300
|
+
engine: deps ? trackedEngine(this.engine, deps) : this.engine,
|
|
301
|
+
pageName: pageName,
|
|
302
|
+
linkGraph: context.linkGraph || {},
|
|
303
|
+
formatters: this.formatters,
|
|
304
|
+
dependsOn: (topic) => deps?.dependsOn(topic),
|
|
305
|
+
markVolatile: () => deps?.markVolatile()
|
|
306
|
+
};
|
|
307
|
+
// Check if it's a new-style plugin with execute method
|
|
308
|
+
if (isPluginObject(plugin)) {
|
|
309
|
+
if (typeof plugin.fetch === 'function') {
|
|
310
|
+
// #1751: fetch reads data for this render, so it sees the same tracked engine.
|
|
311
|
+
await plugin.fetch(pluginContext.engine);
|
|
312
|
+
}
|
|
313
|
+
const result = await plugin.execute(pluginContext, params);
|
|
314
|
+
return result;
|
|
315
|
+
}
|
|
316
|
+
// Check if it's an old-style function plugin
|
|
317
|
+
if (typeof plugin === 'function') {
|
|
318
|
+
const result = await plugin(pageName, params, context.linkGraph || {});
|
|
319
|
+
return result;
|
|
320
|
+
}
|
|
321
|
+
// This should never happen due to type narrowing, but satisfies TypeScript
|
|
322
|
+
return `Plugin '${pluginName}' is not executable`;
|
|
323
|
+
}
|
|
324
|
+
catch (err) {
|
|
325
|
+
logger.error(`Plugin '${pluginName}' execution failed:`, err);
|
|
326
|
+
return `Plugin '${pluginName}' error: ${err.message}`;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Get list of all registered plugins
|
|
331
|
+
* @returns {string[]} Array of plugin names
|
|
332
|
+
*/
|
|
333
|
+
getPluginNames() {
|
|
334
|
+
return Array.from(this.plugins.keys());
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Get plugin info
|
|
338
|
+
* @param {string} pluginName - Name of the plugin
|
|
339
|
+
* @returns {PluginInfo | null} Plugin information
|
|
340
|
+
*/
|
|
341
|
+
getPluginInfo(pluginName) {
|
|
342
|
+
const plugin = this.findPlugin(pluginName);
|
|
343
|
+
if (!plugin) {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
// Only PluginObject has metadata properties
|
|
347
|
+
if (isPluginObject(plugin)) {
|
|
348
|
+
return {
|
|
349
|
+
name: pluginName,
|
|
350
|
+
description: plugin.description || 'No description available',
|
|
351
|
+
author: plugin.author || 'Unknown',
|
|
352
|
+
version: plugin.version || '1.0.0'
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
// Function plugins have no metadata
|
|
356
|
+
return {
|
|
357
|
+
name: pluginName,
|
|
358
|
+
description: 'No description available',
|
|
359
|
+
author: 'Unknown',
|
|
360
|
+
version: '1.0.0'
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Check if plugin exists
|
|
365
|
+
* @param {string} pluginName - Name of the plugin
|
|
366
|
+
* @returns {boolean} True if plugin exists
|
|
367
|
+
*/
|
|
368
|
+
hasPlugin(pluginName) {
|
|
369
|
+
return this.findPlugin(pluginName) !== null;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Programmatically register a plugin object (for use by add-ons).
|
|
373
|
+
*
|
|
374
|
+
* Skips the file-path security check used by loadPlugin() — the caller is
|
|
375
|
+
* responsible for providing a trusted Plugin instance. Calls
|
|
376
|
+
* plugin.initialize(engine) if present, consistent with loadPlugin().
|
|
377
|
+
*
|
|
378
|
+
* @param {string} name - Plugin name (used for [{name ...}] invocation)
|
|
379
|
+
* @param {Plugin} plugin - Plugin function or PluginObject
|
|
380
|
+
*/
|
|
381
|
+
async registerPlugin(name, plugin) {
|
|
382
|
+
if (!isPlugin(plugin)) {
|
|
383
|
+
logger.warn(`[PluginManager] registerPlugin('${name}'): argument is not a valid Plugin — skipped`);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
if (isPluginObject(plugin) && typeof plugin.initialize === 'function') {
|
|
387
|
+
await plugin.initialize(this.engine);
|
|
388
|
+
}
|
|
389
|
+
this.plugins.set(name, plugin);
|
|
390
|
+
logger.info(`[PluginManager] Registered plugin: ${name}`);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
export default PluginManager;
|