@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,691 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AddonsManager - Core Add-on Management System
|
|
3
|
+
*
|
|
4
|
+
* Handles discovery, registration, lifecycle management, and dependencies
|
|
5
|
+
* for optional ngdpbase add-ons. This enables optional business modules
|
|
6
|
+
* (person-contacts, financial-ledger, etc.) without modifying ngdpbase core.
|
|
7
|
+
*
|
|
8
|
+
* @class AddonsManager
|
|
9
|
+
* @extends BaseManager
|
|
10
|
+
*
|
|
11
|
+
* @see {@link https://github.com/jwilleke/ngdpbase/issues/158}
|
|
12
|
+
*/
|
|
13
|
+
import type { ActorContext } from '../context/ActorContext.js';
|
|
14
|
+
import BaseManager from './BaseManager.js';
|
|
15
|
+
import type { BackupData } from './BaseManager.js';
|
|
16
|
+
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
17
|
+
import type { User } from '../types/User.js';
|
|
18
|
+
import { type StoreOwnerState } from '../utils/privateStoreDoor.js';
|
|
19
|
+
import { type AddonHost } from '../utils/addonHost.js';
|
|
20
|
+
/**
|
|
21
|
+
* Type passed to `AddonModule.profileSection()` (#534). Matches what
|
|
22
|
+
* `UserManager.getUser()` returns at runtime — `User` minus the password
|
|
23
|
+
* hash — so addons can read preferences/roles/email without coupling
|
|
24
|
+
* to whether the host happened to fetch the full User record.
|
|
25
|
+
*/
|
|
26
|
+
export type AddonProfileUser = Omit<User, 'password'>;
|
|
27
|
+
/**
|
|
28
|
+
* Flatten nested plain-object values into dotted-key form (#534 defensive).
|
|
29
|
+
*
|
|
30
|
+
* `express.urlencoded({ extended: true })` keeps dotted form-field names FLAT
|
|
31
|
+
* by default — `<input name="journal.x">` → `body['journal.x']` directly,
|
|
32
|
+
* NOT `body.journal.x`. (Verified: qs's `allowDots` defaults to false, and
|
|
33
|
+
* body-parser doesn't override that.) But the WikiRoutes core code defends
|
|
34
|
+
* against a future config change with its own `getBodyValue` walker, and
|
|
35
|
+
* `AddonsManager.saveProfileSections` normalizes addon-bound body shape so
|
|
36
|
+
* addon authors can rely on flat lookups regardless of host config.
|
|
37
|
+
*
|
|
38
|
+
* Rules:
|
|
39
|
+
* - Non-object values (string, number, boolean, null, undefined) pass
|
|
40
|
+
* through as-is at the current dotted key.
|
|
41
|
+
* - Arrays pass through as-is (not flattened element-wise — qs already
|
|
42
|
+
* produces sensible array shapes for `name="x[]"` inputs).
|
|
43
|
+
* - Plain objects are recursively flattened.
|
|
44
|
+
* - Flat dotted keys present on the input pass through unchanged.
|
|
45
|
+
* - If both flat (`'a.b': X`) and nested (`a: { b: Y }`) shapes are
|
|
46
|
+
* present, the LAST seen during iteration wins — typically the nested
|
|
47
|
+
* value, since flat dotted keys are inserted before nested objects in
|
|
48
|
+
* qs output. This is intentional: nested is the more-specific shape.
|
|
49
|
+
*/
|
|
50
|
+
export declare function flattenDottedKeys(obj: Record<string, unknown>, prefix?: string): Record<string, unknown>;
|
|
51
|
+
/**
|
|
52
|
+
* Standard interface that all add-ons must implement
|
|
53
|
+
*/
|
|
54
|
+
export interface AddonModule {
|
|
55
|
+
/** Unique identifier for the add-on */
|
|
56
|
+
name: string;
|
|
57
|
+
/** Semantic version string */
|
|
58
|
+
version: string;
|
|
59
|
+
/** Human-readable description */
|
|
60
|
+
description?: string;
|
|
61
|
+
/** Author name or organization */
|
|
62
|
+
author?: string;
|
|
63
|
+
/** List of add-on names this add-on depends on */
|
|
64
|
+
dependencies?: string[];
|
|
65
|
+
/**
|
|
66
|
+
* #1808: add-ons to load first when they are installed and enabled. Unlike
|
|
67
|
+
* `dependencies`, a missing or disabled one is skipped, not an error.
|
|
68
|
+
*/
|
|
69
|
+
optionalDependencies?: string[];
|
|
70
|
+
/**
|
|
71
|
+
* Called during app startup if add-on is enabled.
|
|
72
|
+
* Use this to register routes, initialize databases, etc.
|
|
73
|
+
*
|
|
74
|
+
* #1813: `host` carries the core values an add-on uses (ApiContext,
|
|
75
|
+
* recordAuditEvent, logger, guardedFetch, …), built for this add-on and
|
|
76
|
+
* frozen. An add-on that takes two arguments keeps working until it moves.
|
|
77
|
+
*/
|
|
78
|
+
register(engine: WikiEngine, config: Record<string, unknown>, host?: AddonHost): Promise<void> | void;
|
|
79
|
+
/**
|
|
80
|
+
* Optional health check returning add-on status details.
|
|
81
|
+
*/
|
|
82
|
+
status?(): Promise<AddonStatusDetails> | AddonStatusDetails;
|
|
83
|
+
/**
|
|
84
|
+
* Optional `/profile` page extension hook (#534).
|
|
85
|
+
*
|
|
86
|
+
* When implemented, the addon contributes a section to the core profile
|
|
87
|
+
* page rather than maintaining a standalone settings route. The returned
|
|
88
|
+
* `html` is inserted **inside** the core preferences form, so the form
|
|
89
|
+
* fields it carries are submitted with the existing `POST /preferences`
|
|
90
|
+
* and surfaced to `saveProfileSection()` for persistence.
|
|
91
|
+
*
|
|
92
|
+
* Return `null` to opt out per-user (e.g. when the addon's feature is
|
|
93
|
+
* disabled for this user's roles).
|
|
94
|
+
*
|
|
95
|
+
* ⚠️ SECURITY — the returned `html` is rendered with EJS `<%- %>` (RAW,
|
|
96
|
+
* NOT escaped) inside the user's `/profile` form. **The addon is fully
|
|
97
|
+
* responsible for escaping any user-controlled data it interpolates.**
|
|
98
|
+
* Use `<%= %>` in your own EJS templates, or an explicit escape helper.
|
|
99
|
+
* A buggy `profileSection()` that string-concatenates user input becomes
|
|
100
|
+
* stored-XSS against every user who views their profile.
|
|
101
|
+
*
|
|
102
|
+
* Two specific things to avoid:
|
|
103
|
+
* - Do NOT emit `<input name="_csrf" …>` — that would override the
|
|
104
|
+
* host form's CSRF token and corrupt the user's save.
|
|
105
|
+
* - Do NOT emit a wrapping `<form>` — your fields submit with the host
|
|
106
|
+
* preferences form. A nested `<form>` is invalid HTML and browsers
|
|
107
|
+
* will hoist your inputs unpredictably.
|
|
108
|
+
*
|
|
109
|
+
* RECOMMENDED — include a hidden marker so your paired `saveProfileSection`
|
|
110
|
+
* can distinguish "section was rendered but checkboxes unchecked" from
|
|
111
|
+
* "section was not rendered at all" (addon disabled mid-session, cached
|
|
112
|
+
* form). Without it, absent checkbox fields look identical to "user
|
|
113
|
+
* submitted no such field" and you may silently clobber stored values.
|
|
114
|
+
*/
|
|
115
|
+
profileSection?(user: AddonProfileUser): Promise<AddonProfileSection | null> | AddonProfileSection | null;
|
|
116
|
+
/**
|
|
117
|
+
* Optional save handler paired with `profileSection()` (#534).
|
|
118
|
+
*
|
|
119
|
+
* Receives a shallow-cloned `req.body` from `POST /preferences`; the addon
|
|
120
|
+
* extracts whichever fields its `profileSection()` form rendered and
|
|
121
|
+
* persists them. Errors are caught by AddonsManager and logged — they
|
|
122
|
+
* do NOT block the core-preferences save or other addons' saves.
|
|
123
|
+
*
|
|
124
|
+
* Two pitfalls the implementation must guard against (the host cannot
|
|
125
|
+
* do this for you):
|
|
126
|
+
*
|
|
127
|
+
* 1. ABSENT-FIELD CLOBBER. HTML checkboxes only submit when checked, so
|
|
128
|
+
* an absent key is ambiguous between "user unchecked it" and "the
|
|
129
|
+
* field was never rendered". Without a presence signal (see the
|
|
130
|
+
* recommended hidden marker on `profileSection()`), writing
|
|
131
|
+
* `body['x'] === 'on'` unconditionally turns every save where the
|
|
132
|
+
* section was hidden into a stealth reset to `false`.
|
|
133
|
+
*
|
|
134
|
+
* 2. CONFIG-GATED FIELDS. If your partial gates a field behind an admin
|
|
135
|
+
* config flag (`<% if (enabled) %>`), your save handler MUST re-check
|
|
136
|
+
* the same flag server-side before writing — otherwise a crafted
|
|
137
|
+
* POST can bypass the admin's gate.
|
|
138
|
+
*
|
|
139
|
+
* __Since #1234 the first argument is the caller's context, not a name.__
|
|
140
|
+
* `ctx` is the request's subject (`PermissionSubject`: `username`, `roles`,
|
|
141
|
+
* `isAuthenticated`, `ipAddress`, `viaToken` / `viaShare` when delegated)
|
|
142
|
+
* — forward it to whatever you write (`userManager.updateUser(ctx.username,
|
|
143
|
+
* updates, ctx)`), never rebuild one from `ctx.username`. An addon still
|
|
144
|
+
* written against the old `(username, body)` shape gets a warning at load
|
|
145
|
+
* naming it; its saves then fail per call and are logged.
|
|
146
|
+
*/
|
|
147
|
+
saveProfileSection?(ctx: ActorContext, body: Record<string, unknown>): Promise<void> | void;
|
|
148
|
+
/**
|
|
149
|
+
* Optional cleanup on app shutdown.
|
|
150
|
+
*/
|
|
151
|
+
shutdown?(): Promise<void> | void;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Returned from `AddonModule.profileSection()` — a single section rendered as
|
|
155
|
+
* a card inside the `/profile` preferences form. `html` is inserted with `<%-`
|
|
156
|
+
* (raw); the addon is responsible for any escaping of its own data.
|
|
157
|
+
*/
|
|
158
|
+
export interface AddonProfileSection {
|
|
159
|
+
/** Heading shown in the card header (e.g. "Journal"). */
|
|
160
|
+
title: string;
|
|
161
|
+
/** Raw HTML rendered inside the card body; should contain form input fields. */
|
|
162
|
+
html: string;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* One entry per addon that contributed a profile section, returned by
|
|
166
|
+
* `AddonsManager.getProfileSections()`. `addonName` is the registry key —
|
|
167
|
+
* useful for debug logging and CSS scoping.
|
|
168
|
+
*/
|
|
169
|
+
export interface AddonProfileSectionEntry extends AddonProfileSection {
|
|
170
|
+
addonName: string;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Shape of the `ngdpbase` key in an add-on's package.json.
|
|
174
|
+
* All fields are optional — omitting the key entirely is valid.
|
|
175
|
+
*/
|
|
176
|
+
export interface AddonManifest {
|
|
177
|
+
/**
|
|
178
|
+
* Canonical addon identity (#927). Authoritative id used as the registry
|
|
179
|
+
* key, the `ngdpbase.addons.<slug>.enabled` config key, dependency
|
|
180
|
+
* references, and the boot-time validator's match — read statically from
|
|
181
|
+
* `package.json` with no module import. When absent, identity falls back
|
|
182
|
+
* to the package/folder name (minus a conventional trailing `-addon`).
|
|
183
|
+
* The module's exported `name` is a display label and must equal this.
|
|
184
|
+
*/
|
|
185
|
+
slug?: string;
|
|
186
|
+
/** 'domain' = this addon IS the site identity; 'additive' = augments an existing wiki */
|
|
187
|
+
type?: 'domain' | 'additive';
|
|
188
|
+
/**
|
|
189
|
+
* The oldest ngdpbase release this addon runs on (`x.y.z`). Checked before
|
|
190
|
+
* the addon is imported: on an older core it is not loaded, and its status
|
|
191
|
+
* says which release it needs, instead of failing at import with a missing
|
|
192
|
+
* module.
|
|
193
|
+
*/
|
|
194
|
+
minVersion?: string;
|
|
195
|
+
/**
|
|
196
|
+
* Config keys this addon wants applied by default at load time.
|
|
197
|
+
* Each key is only set if the operator has not already explicitly
|
|
198
|
+
* set it in custom config. Values are ephemeral (merged config only,
|
|
199
|
+
* not persisted to app-custom-config.json).
|
|
200
|
+
*/
|
|
201
|
+
domainDefaults?: Record<string, unknown>;
|
|
202
|
+
/** Capability flags this addon advertises */
|
|
203
|
+
capabilities?: string[];
|
|
204
|
+
/**
|
|
205
|
+
* Private store kinds this addon owns (#1414): `[{ id, encrypt }]`. Saved to
|
|
206
|
+
* configuration at the addon's first load, owned by its slug; from then on
|
|
207
|
+
* configuration wins and this is never consulted again. See
|
|
208
|
+
* docs/guides/addons-developer-guide.md "Declare a Private Store".
|
|
209
|
+
*/
|
|
210
|
+
stores?: unknown;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Details returned by add-on's status() method
|
|
214
|
+
*/
|
|
215
|
+
export interface AddonStatusDetails {
|
|
216
|
+
healthy: boolean;
|
|
217
|
+
database?: string;
|
|
218
|
+
records?: number;
|
|
219
|
+
message?: string;
|
|
220
|
+
[key: string]: unknown;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Card registered by an add-on for display on the admin dashboard.
|
|
224
|
+
* The card's live stats are sourced from the add-on's existing status() method.
|
|
225
|
+
*/
|
|
226
|
+
export interface AddonDashboardCard {
|
|
227
|
+
/** Must match the add-on's registered name */
|
|
228
|
+
addonName: string;
|
|
229
|
+
/** Card heading */
|
|
230
|
+
title: string;
|
|
231
|
+
/** Font Awesome class, e.g. 'fas fa-wpforms' */
|
|
232
|
+
icon: string;
|
|
233
|
+
/** URL of the add-on's admin page */
|
|
234
|
+
adminUrl: string;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Status information returned by getStatus()
|
|
238
|
+
*/
|
|
239
|
+
export interface AddonStatus {
|
|
240
|
+
name: string;
|
|
241
|
+
version: string;
|
|
242
|
+
description: string;
|
|
243
|
+
author: string;
|
|
244
|
+
enabled: boolean;
|
|
245
|
+
loaded: boolean;
|
|
246
|
+
dependencies: string[];
|
|
247
|
+
/** #1808: loaded first when installed and enabled; never required */
|
|
248
|
+
optionalDependencies: string[];
|
|
249
|
+
error: string | null;
|
|
250
|
+
/** 'domain' | 'additive' | undefined (unset addons behave as additive) */
|
|
251
|
+
type?: 'domain' | 'additive';
|
|
252
|
+
details?: AddonStatusDetails;
|
|
253
|
+
statusError?: string;
|
|
254
|
+
/** #443: add-on ships a deployable theme/ (theme.json sentinel present) */
|
|
255
|
+
hasTheme?: boolean;
|
|
256
|
+
/** #443: themes/<name>/ already exists in the instance */
|
|
257
|
+
themeDeployed?: boolean;
|
|
258
|
+
/** #1414: what turning this addon off would shut — shown in the Disable confirmation */
|
|
259
|
+
disableWarnings?: string[];
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* AddonsManager - Manages optional add-on modules
|
|
263
|
+
*/
|
|
264
|
+
declare class AddonsManager extends BaseManager {
|
|
265
|
+
/** Map of discovered add-ons by name */
|
|
266
|
+
private addons;
|
|
267
|
+
/** Configured paths to addons directories (one or more) */
|
|
268
|
+
private addonsPaths;
|
|
269
|
+
/** Resolved absolute paths to addons directories */
|
|
270
|
+
private resolvedAddonsPaths;
|
|
271
|
+
/** #673: npm package globs (from `node_modules:` addons-path entries) to discover from node_modules. */
|
|
272
|
+
private npmAddonPatterns;
|
|
273
|
+
/** #1830: `<data>/addons`, where installed add-ons live; null when there is no data folder. */
|
|
274
|
+
private installedDir;
|
|
275
|
+
/** #1830: the installed add-ons verified this boot — the only ones loaded from `installedDir`. */
|
|
276
|
+
private verifiedInstalled;
|
|
277
|
+
/** Stylesheets registered by add-ons via registerStylesheet() */
|
|
278
|
+
private registeredStylesheets;
|
|
279
|
+
/** Dashboard cards registered by add-ons via registerDashboardCard() */
|
|
280
|
+
private dashboardCards;
|
|
281
|
+
/** Name of the first domain addon loaded — only one is permitted */
|
|
282
|
+
private domainAddonName;
|
|
283
|
+
constructor(engine: WikiEngine);
|
|
284
|
+
/**
|
|
285
|
+
* Initialize the AddonsManager
|
|
286
|
+
*
|
|
287
|
+
* Reads configuration, discovers add-ons, and loads enabled ones
|
|
288
|
+
* in dependency order.
|
|
289
|
+
*/
|
|
290
|
+
initialize(config?: Record<string, unknown>): Promise<void>;
|
|
291
|
+
/**
|
|
292
|
+
* #1830: install the add-ons `ngdpbase.addons.install` names into
|
|
293
|
+
* `<data>/addons` (sha512 and Sigstore provenance checked), or verify the
|
|
294
|
+
* ones already there. An empty map (the shipped default) does nothing at all.
|
|
295
|
+
* Every request — registry, attestations, the Sigstore TUF repository — goes
|
|
296
|
+
* through the guarded client and the egress policy (#1139). A problem with one add-on
|
|
297
|
+
* never stops the boot: it is logged and posted to admins, and that add-on is
|
|
298
|
+
* simply not there to load. When anything on disk changed, the configuration
|
|
299
|
+
* is re-merged so the add-on's config/default-config.json layer applies on
|
|
300
|
+
* this boot, not the next.
|
|
301
|
+
*/
|
|
302
|
+
private installConfiguredAddons;
|
|
303
|
+
/**
|
|
304
|
+
* `addon-install` (#1830): written after every check passed and before
|
|
305
|
+
* anything is written to disk. on-failure: refuse — code does not join the
|
|
306
|
+
* site unrecorded, so a record that cannot be written stops the install
|
|
307
|
+
* (recordSystemAction throws, and the installer writes nothing).
|
|
308
|
+
*/
|
|
309
|
+
private recordAddonInstall;
|
|
310
|
+
/**
|
|
311
|
+
* `addon-verify-failed` (#1830): an integrity, provenance, tarball, manifest,
|
|
312
|
+
* lock or folder check refused (or, for a repaired folder, found it changed).
|
|
313
|
+
* on-failure: continue — the refusal has already happened; a lost record
|
|
314
|
+
* must not change it.
|
|
315
|
+
*/
|
|
316
|
+
private recordVerifyFailed;
|
|
317
|
+
/** Tell admins about an install problem (#1830). Best-effort, like recordSeedFailure. */
|
|
318
|
+
private notifyInstallProblem;
|
|
319
|
+
/**
|
|
320
|
+
* Discover available add-ons by scanning all configured addons directories.
|
|
321
|
+
*/
|
|
322
|
+
discoverAddons(): Promise<void>;
|
|
323
|
+
/**
|
|
324
|
+
* #1830: register the add-ons in `<data>/addons` that the installer verified
|
|
325
|
+
* this boot. Anything else there — a folder copied in by hand, or one whose
|
|
326
|
+
* entry left the install map — is not loaded, and admins are told.
|
|
327
|
+
*/
|
|
328
|
+
private scanInstalledAddons;
|
|
329
|
+
/**
|
|
330
|
+
* Scan a single addons directory and register any add-ons found.
|
|
331
|
+
*/
|
|
332
|
+
private scanAddonsDirectory;
|
|
333
|
+
/**
|
|
334
|
+
* #673 (packaged model): discover addons published as npm packages, matching a
|
|
335
|
+
* `node_modules:<glob>` addons-path entry (e.g. `node_modules:@jwilleke/*-addon`).
|
|
336
|
+
* Each matching package is loaded through the same slug/module/register()
|
|
337
|
+
* contract as bundled and drop-in addons — only discovery differs.
|
|
338
|
+
*/
|
|
339
|
+
private scanNpmAddons;
|
|
340
|
+
/** Locate the node_modules directory (app root / cwd). */
|
|
341
|
+
private findNodeModules;
|
|
342
|
+
/** Expand a `@scope/glob` (or bare `glob`) pattern to package directories. */
|
|
343
|
+
private resolveNpmAddonDirs;
|
|
344
|
+
/**
|
|
345
|
+
* Load a single addon from its directory (index.js/ts + optional package.json
|
|
346
|
+
* `ngdpbase` manifest) and register it. Shared by directory scans and npm
|
|
347
|
+
* (#673) discovery; `sourceLabel` is for logging only. `source` selects the
|
|
348
|
+
* no-slug identity fallback (#927): a directory addon's folder name is its
|
|
349
|
+
* identity verbatim, while an npm package strips the conventional `-addon`
|
|
350
|
+
* suffix. Duplicate ids are skipped — since npm discovery runs last, a
|
|
351
|
+
* bundled/drop-in addon wins.
|
|
352
|
+
*/
|
|
353
|
+
private registerAddonFromDir;
|
|
354
|
+
/**
|
|
355
|
+
* Why an addon cannot run on this core (#1757): its manifest's `minVersion`
|
|
356
|
+
* is newer than this release. Null when it can, or when it states none.
|
|
357
|
+
*/
|
|
358
|
+
private incompatibility;
|
|
359
|
+
/**
|
|
360
|
+
* Check if an add-on is enabled in configuration
|
|
361
|
+
*/
|
|
362
|
+
isEnabled(addonName: string): boolean;
|
|
363
|
+
/**
|
|
364
|
+
* Validate that an add-on can be safely disabled (#617).
|
|
365
|
+
*
|
|
366
|
+
* Mirrors the enable-time topological-sort dep check at `resolveLoadOrder()`:
|
|
367
|
+
* if any *enabled* add-on declares `dependencies: ['<addonName>']`, the
|
|
368
|
+
* disable is blocked and the dependent's name is returned. Callers should
|
|
369
|
+
* surface the blocker to the operator and refuse the disable.
|
|
370
|
+
*
|
|
371
|
+
* @param addonName The add-on the operator wants to disable.
|
|
372
|
+
* @returns `{ ok: true }` if safe to disable. Otherwise
|
|
373
|
+
* `{ ok: false, blockedBy: ['dep1', 'dep2', ...] }` with
|
|
374
|
+
* the names of every enabled addon that depends on it.
|
|
375
|
+
*/
|
|
376
|
+
canDisable(addonName: string): {
|
|
377
|
+
ok: true;
|
|
378
|
+
} | {
|
|
379
|
+
ok: false;
|
|
380
|
+
blockedBy: string[];
|
|
381
|
+
};
|
|
382
|
+
/**
|
|
383
|
+
* Get add-on specific configuration
|
|
384
|
+
*/
|
|
385
|
+
getAddonConfig(addonName: string): Record<string, unknown>;
|
|
386
|
+
/**
|
|
387
|
+
* Seed wiki pages shipped with an add-on.
|
|
388
|
+
*
|
|
389
|
+
* If the add-on directory contains a `pages/` subdirectory, any `.md` files
|
|
390
|
+
* found there are copied to the instance pages directory — but only if a file
|
|
391
|
+
* with the same name does not already exist (user edits are never overwritten).
|
|
392
|
+
*
|
|
393
|
+
* This mirrors how `required-pages/` seeds core pages at install time.
|
|
394
|
+
*
|
|
395
|
+
* @param addonName Name of the add-on (for logging)
|
|
396
|
+
* @param addonPath Filesystem path to the add-on directory
|
|
397
|
+
*/
|
|
398
|
+
/**
|
|
399
|
+
* Default edit-protection for a seeded addon page (#971, addons.md §3).
|
|
400
|
+
*
|
|
401
|
+
* §3 settled the mechanism: the seeder stamps frontmatter `access`, which
|
|
402
|
+
* Tier 1 of the evaluator honours directly, needs no `PolicyEvaluator`
|
|
403
|
+
* change, and is hash-neutral (`pageSourceHash` covers the body only, so
|
|
404
|
+
* adding metadata cannot disturb the #920 reseed comparison).
|
|
405
|
+
*
|
|
406
|
+
* §9 settled *who owns what*, and the two must be read together. A blanket
|
|
407
|
+
* admin lock would contradict the operator's decision that domain content
|
|
408
|
+
* pages "should be normal pages, purely seeded by the addon" — the addon
|
|
409
|
+
* provides a starting corpus and then lets go. So the default follows §9's
|
|
410
|
+
* ownership column rather than locking everything:
|
|
411
|
+
*
|
|
412
|
+
* | Category | Purpose | Default |
|
|
413
|
+
* |---|---|---|
|
|
414
|
+
* | `system` | Feature UI, site chrome — infrastructure | admin-only edit |
|
|
415
|
+
* | `documentation` | Help / docs — addon-owned | admin-only edit |
|
|
416
|
+
* | `general` | Domain content, demo — **instance-owned** | no stamp |
|
|
417
|
+
* | anything else | unclassified, treated as addon-owned | admin-only edit |
|
|
418
|
+
*
|
|
419
|
+
* An addon can always override by declaring its own `access`, which is the
|
|
420
|
+
* documented escape hatch in §3 and the reason the default is a default.
|
|
421
|
+
*
|
|
422
|
+
* @param category - The page's resolved `system-category`
|
|
423
|
+
* @returns The access object to stamp, or undefined to leave the page unprotected
|
|
424
|
+
*/
|
|
425
|
+
private defaultAddonPageAccess;
|
|
426
|
+
/**
|
|
427
|
+
* Surface a page-level seed failure beyond the boot log (#951).
|
|
428
|
+
*
|
|
429
|
+
* A boot-time log line is invisible ten minutes later, which is how a page
|
|
430
|
+
* that never seeded stays missing for weeks. Raising an operator
|
|
431
|
+
* notification makes the failure outlive the boot it happened on.
|
|
432
|
+
*
|
|
433
|
+
* Deliberately NOT fail-fast. Refusing to start because a third-party addon
|
|
434
|
+
* shipped one malformed page turns an authoring typo into an outage, and the
|
|
435
|
+
* #672 precedent does not transfer — that was the operator's own config
|
|
436
|
+
* naming a nonexistent addon, which the operator can fix. A vendor's
|
|
437
|
+
* malformed file is not in the operator's control. Skip the page, report
|
|
438
|
+
* loudly, surface it after boot.
|
|
439
|
+
*
|
|
440
|
+
* Best-effort: notification failure must never break seeding.
|
|
441
|
+
*/
|
|
442
|
+
private recordSeedFailure;
|
|
443
|
+
/**
|
|
444
|
+
* #1559: tell admins what the configuration merge refused in the add-ons'
|
|
445
|
+
* system-categories, or overrode in the operator's file — logged there at
|
|
446
|
+
* boot, surfaced here once notifications exist. Not fail-fast, for the
|
|
447
|
+
* reason recordSeedFailure gives: an add-on's file is not in the operator's
|
|
448
|
+
* control. A refused entry fails closed (its category and vault do not
|
|
449
|
+
* exist, so nothing is written unsealed or public by mistake). Best-effort.
|
|
450
|
+
*/
|
|
451
|
+
private notifySystemCategoryProblems;
|
|
452
|
+
private seedAddonPages;
|
|
453
|
+
/** Pages reseeded by the current `seedAddonPages` pass (#920). */
|
|
454
|
+
private lastReseedCount;
|
|
455
|
+
/**
|
|
456
|
+
* An addon page this site already holds: metadata tidy-ups (#971, #1003) and
|
|
457
|
+
* the opt-in content reseed (#920), then a search-index refresh. Called by the
|
|
458
|
+
* shared seeder for each source page that is live (#1406).
|
|
459
|
+
*/
|
|
460
|
+
private reconcileSeededAddonPage;
|
|
461
|
+
private getInstanceThemesDir;
|
|
462
|
+
/**
|
|
463
|
+
* True if the add-on ships a deployable theme. `theme/theme.json` is the
|
|
464
|
+
* presence sentinel (mirrors `pages/` needing at least one `.md`).
|
|
465
|
+
*/
|
|
466
|
+
addonShipsTheme(addonPath: string): boolean;
|
|
467
|
+
/**
|
|
468
|
+
* #443: Deploy an add-on's `theme/` into the instance `themes/<name>/`.
|
|
469
|
+
*
|
|
470
|
+
* When an add-on ships a `theme/` subdirectory it should land in the
|
|
471
|
+
* instance themes directory the same way `seedAddonPages()` handles
|
|
472
|
+
* `pages/`. ThemeManager then reads it via `ngdpbase.theme.active` (set by
|
|
473
|
+
* the addon's domainDefaults) — no ThemeManager change needed.
|
|
474
|
+
*
|
|
475
|
+
* @param addonName Add-on name (also the destination theme folder)
|
|
476
|
+
* @param addonPath Filesystem path to the add-on directory
|
|
477
|
+
* @param overwrite false (default) = first-boot copy, never overwrites an
|
|
478
|
+
* existing `themes/<name>/` (preserves instance edits);
|
|
479
|
+
* true = dashboard-triggered redeploy, replaces it.
|
|
480
|
+
* @returns Result describing what happened (for the admin UI).
|
|
481
|
+
*/
|
|
482
|
+
seedAddonTheme(addonName: string, addonPath: string, overwrite?: boolean): Promise<{
|
|
483
|
+
ok: boolean;
|
|
484
|
+
deployed: boolean;
|
|
485
|
+
message: string;
|
|
486
|
+
}>;
|
|
487
|
+
/**
|
|
488
|
+
* #443: Dashboard-triggered theme (re)deploy — always overwrites so theme
|
|
489
|
+
* updates shipped by the add-on can be pulled in without a server restart
|
|
490
|
+
* (theme CSS is served as static files; a page reload suffices).
|
|
491
|
+
*
|
|
492
|
+
* @param addonName Add-on name
|
|
493
|
+
* @returns Result for the admin UI
|
|
494
|
+
*/
|
|
495
|
+
deployAddonTheme(addonName: string): Promise<{
|
|
496
|
+
ok: boolean;
|
|
497
|
+
deployed: boolean;
|
|
498
|
+
message: string;
|
|
499
|
+
}>;
|
|
500
|
+
/**
|
|
501
|
+
* Load all enabled add-ons in dependency order
|
|
502
|
+
*/
|
|
503
|
+
private loadAddons;
|
|
504
|
+
/**
|
|
505
|
+
* Resolve dependency order using topological sort
|
|
506
|
+
*
|
|
507
|
+
* @returns Array of add-on names in load order
|
|
508
|
+
* @throws Error if circular dependency detected
|
|
509
|
+
*/
|
|
510
|
+
resolveLoadOrder(): string[];
|
|
511
|
+
/**
|
|
512
|
+
* Load a single add-on
|
|
513
|
+
*/
|
|
514
|
+
loadAddon(addonName: string): Promise<void>;
|
|
515
|
+
/**
|
|
516
|
+
* What turning this addon off would shut (#1414): one line per store kind it
|
|
517
|
+
* owns that users hold data in. Turning it off is warned, never refused —
|
|
518
|
+
* nothing is destroyed, the kind stays in configuration, every copy stays on
|
|
519
|
+
* disk (sealed ones keep their wrapped key), and turning it back on reopens
|
|
520
|
+
* them. Separate from `canDisable`, which stays synchronous: counting copies
|
|
521
|
+
* reads the disk.
|
|
522
|
+
*/
|
|
523
|
+
storeDisableWarnings(addonName: string): Promise<string[]>;
|
|
524
|
+
/**
|
|
525
|
+
* Where the addon that owns a store kind stands, for the store door (#1414):
|
|
526
|
+
* loaded, enabled but failed to load, turned off, or not on this site.
|
|
527
|
+
*/
|
|
528
|
+
storeOwnerState(addonName: string): StoreOwnerState;
|
|
529
|
+
/**
|
|
530
|
+
* Inject domainDefaults from the add-on's package.json ngdpbase key
|
|
531
|
+
* into the merged config, but only for keys not already explicitly
|
|
532
|
+
* set by the operator in custom config.
|
|
533
|
+
*/
|
|
534
|
+
private applyDomainDefaults;
|
|
535
|
+
/**
|
|
536
|
+
* Get status of all discovered add-ons
|
|
537
|
+
*/
|
|
538
|
+
getStatus(): Promise<AddonStatus[]>;
|
|
539
|
+
/**
|
|
540
|
+
* Collect profile-page sections contributed by loaded addons (#534).
|
|
541
|
+
*
|
|
542
|
+
* Iterates every loaded addon that implements `profileSection()`, fans
|
|
543
|
+
* out IN PARALLEL via `Promise.allSettled`, then packages results for
|
|
544
|
+
* `profile.ejs`. Errors and `null` returns are skipped (malformed shapes
|
|
545
|
+
* are logged so addon authors get a diagnostic instead of an invisibly-
|
|
546
|
+
* missing card). One failing addon must not break the profile page.
|
|
547
|
+
*
|
|
548
|
+
* Mirrors the `getStatus()` pattern but runs in parallel so page-render
|
|
549
|
+
* latency is bounded by the slowest addon, not the sum.
|
|
550
|
+
*
|
|
551
|
+
* Result order follows registration order of the addons map so the
|
|
552
|
+
* displayed section order is stable across renders.
|
|
553
|
+
*/
|
|
554
|
+
getProfileSections(user: AddonProfileUser): Promise<AddonProfileSectionEntry[]>;
|
|
555
|
+
/**
|
|
556
|
+
* #1234: say at load when an addon still implements the pre-#1234 hook.
|
|
557
|
+
*
|
|
558
|
+
* The two shapes have the same arity, so the only tell is the name the
|
|
559
|
+
* addon gave its first parameter — a heuristic, and stated as one. A false
|
|
560
|
+
* negative costs nothing new: the save still fails per call and is logged
|
|
561
|
+
* by `saveProfileSections`. A hit turns that silent stop into a boot line
|
|
562
|
+
* naming the addon and the migration.
|
|
563
|
+
*/
|
|
564
|
+
private warnOldProfileHook;
|
|
565
|
+
/**
|
|
566
|
+
* Fan out `POST /preferences` body to every loaded addon that registered a
|
|
567
|
+
* `saveProfileSection()` handler (#534).
|
|
568
|
+
*
|
|
569
|
+
* Runs SEQUENTIALLY — each addon's read-modify-write of user preferences
|
|
570
|
+
* (typical implementation: `getUser → merge → updateUser`) must see the
|
|
571
|
+
* previous addon's writes. Parallel execution would create a same-snapshot
|
|
572
|
+
* race where two addons read identical preferences, each merge their own
|
|
573
|
+
* keys, and the second writer clobbers the first writer's changes.
|
|
574
|
+
* Self-inflicted by the original #534 PR; reverted to sequential here.
|
|
575
|
+
*
|
|
576
|
+
* (`getProfileSections()` above stays parallel — it's a read path with no
|
|
577
|
+
* cross-addon write contention.)
|
|
578
|
+
*
|
|
579
|
+
* Body shape: the host normalizes whatever shape qs produced into FLAT
|
|
580
|
+
* dotted keys before fan-out, so addons always see `body['journal.X']`
|
|
581
|
+
* regardless of whether body-parser was configured with `allowDots: true`
|
|
582
|
+
* (which would have produced `body.journal.X` instead). This is a
|
|
583
|
+
* defensive contract — today's `express.urlencoded({ extended: true })`
|
|
584
|
+
* leaves dotted form-field names flat by default, but a future config
|
|
585
|
+
* change shouldn't silently break addons. See `flattenDottedKeys`.
|
|
586
|
+
*
|
|
587
|
+
* Errors are caught per-addon and logged so one bad addon cannot block
|
|
588
|
+
* other addons' saves or the core-preferences save.
|
|
589
|
+
*/
|
|
590
|
+
saveProfileSections(ctx: ActorContext, body: Record<string, unknown>): Promise<void>;
|
|
591
|
+
/**
|
|
592
|
+
* Get count of loaded add-ons
|
|
593
|
+
*/
|
|
594
|
+
private getLoadedCount;
|
|
595
|
+
/**
|
|
596
|
+
* Register a stylesheet URL to be injected into every page's <head>.
|
|
597
|
+
*
|
|
598
|
+
* Call this from your add-on's register() function:
|
|
599
|
+
* ```js
|
|
600
|
+
* const addonsManager = engine.getManager('AddonsManager');
|
|
601
|
+
* addonsManager.registerStylesheet('/addons/my-addon/css/style.css');
|
|
602
|
+
* ```
|
|
603
|
+
*
|
|
604
|
+
* The URL must be publicly accessible (served via Express static or a CDN).
|
|
605
|
+
* Add-on CSS files under `addons/<name>/public/` are automatically served at
|
|
606
|
+
* `/addons/<name>/public/` when the server starts.
|
|
607
|
+
*
|
|
608
|
+
* @param url Public URL of the stylesheet (e.g. '/addons/my-addon/css/style.css')
|
|
609
|
+
* @param addonName Optional: name of the registering add-on (for logging)
|
|
610
|
+
*/
|
|
611
|
+
registerStylesheet(url: string, addonName?: string): void;
|
|
612
|
+
/**
|
|
613
|
+
* Return the ordered list of stylesheet URLs registered by all add-ons.
|
|
614
|
+
* Called by the template layer to inject <link> tags into <head>.
|
|
615
|
+
*/
|
|
616
|
+
getRegisteredStylesheets(): string[];
|
|
617
|
+
/**
|
|
618
|
+
* Register a dashboard card for this add-on. Called from the add-on's register() function.
|
|
619
|
+
* Live stats shown on the card are sourced from the add-on's existing status() method.
|
|
620
|
+
*/
|
|
621
|
+
registerDashboardCard(card: AddonDashboardCard): void;
|
|
622
|
+
/**
|
|
623
|
+
* Return all registered dashboard cards in registration order.
|
|
624
|
+
* Called by the admin dashboard route to populate the addon cards row.
|
|
625
|
+
*/
|
|
626
|
+
getDashboardCards(): AddonDashboardCard[];
|
|
627
|
+
/**
|
|
628
|
+
* Get list of all discovered add-on names
|
|
629
|
+
*/
|
|
630
|
+
getAddonNames(): string[];
|
|
631
|
+
/**
|
|
632
|
+
* Return the `pages/` directory path for each enabled addon that has one.
|
|
633
|
+
* Used by Required Pages Sync to surface addon pages alongside required pages.
|
|
634
|
+
*/
|
|
635
|
+
getEnabledAddonPagesDirectories(): Array<{
|
|
636
|
+
name: string;
|
|
637
|
+
pagesDir: string;
|
|
638
|
+
}>;
|
|
639
|
+
/**
|
|
640
|
+
* #930: addon-seeded instance pages whose source file the addon **no longer
|
|
641
|
+
* ships** ("source removed"). Leave-and-flag — this only *detects*; nothing is
|
|
642
|
+
* deleted here. The admin surface surfaces these and offers an opt-in,
|
|
643
|
+
* versioned removal.
|
|
644
|
+
*
|
|
645
|
+
* Enumeration uses the indexed `system-category: addon` (via the search index)
|
|
646
|
+
* to narrow to addon pages — a small candidate set — instead of scanning the
|
|
647
|
+
* whole page store. Each candidate is attributed to its addon via the `addon`
|
|
648
|
+
* frontmatter stamp and diffed against that addon's CURRENT source UUID set.
|
|
649
|
+
* If search is unavailable, returns `[]` (detection is best-effort, never a
|
|
650
|
+
* source of deletions).
|
|
651
|
+
*/
|
|
652
|
+
findOrphanedAddonPages(): Promise<Array<{
|
|
653
|
+
addonName: string;
|
|
654
|
+
uuid: string;
|
|
655
|
+
slug: string;
|
|
656
|
+
title: string;
|
|
657
|
+
userModified: boolean;
|
|
658
|
+
}>>;
|
|
659
|
+
/**
|
|
660
|
+
* Back-fill the page index's `addon` stamp for pages seeded before the field
|
|
661
|
+
* was indexed.
|
|
662
|
+
*
|
|
663
|
+
* Without this, orphan detection is blind on any existing instance: the field
|
|
664
|
+
* is written on save, and seeded pages are deliberately not re-saved once
|
|
665
|
+
* they exist. Cheap — it walks only each addon's own source pages (tens of
|
|
666
|
+
* files), never the whole page tree.
|
|
667
|
+
*/
|
|
668
|
+
private backfillIndexAddonStamps;
|
|
669
|
+
/**
|
|
670
|
+
* Check if an add-on exists (discovered)
|
|
671
|
+
*/
|
|
672
|
+
hasAddon(addonName: string): boolean;
|
|
673
|
+
/**
|
|
674
|
+
* Check if an add-on is loaded
|
|
675
|
+
*/
|
|
676
|
+
isLoaded(addonName: string): boolean;
|
|
677
|
+
/**
|
|
678
|
+
* Graceful shutdown of all loaded add-ons
|
|
679
|
+
*/
|
|
680
|
+
shutdown(): Promise<void>;
|
|
681
|
+
/**
|
|
682
|
+
* Backup add-on manager state
|
|
683
|
+
*/
|
|
684
|
+
backup(): Promise<BackupData>;
|
|
685
|
+
/**
|
|
686
|
+
* Restore is not supported for AddonsManager
|
|
687
|
+
* (add-ons are discovered at startup)
|
|
688
|
+
*/
|
|
689
|
+
restore(backupData: BackupData): Promise<void>;
|
|
690
|
+
}
|
|
691
|
+
export default AddonsManager;
|