@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,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The configuration files: where they are, how they are read, and how the
|
|
3
|
+
* custom file merges over the shipped one (#1214).
|
|
4
|
+
*
|
|
5
|
+
* One implementation, used by `ConfigurationManager` after the engine exists
|
|
6
|
+
* and by `app.ts` before it does (the listen port, TLS at bind time, and the
|
|
7
|
+
* base-URL explicitness check all run before `engine.initialize()`). Until
|
|
8
|
+
* #1214 `app.ts` had its own copy: a shallow spread that replaced a whole map
|
|
9
|
+
* on override, kept `_comment` keys, and computed the paths a second and third
|
|
10
|
+
* time. It happened to agree with the manager only because the keys it read
|
|
11
|
+
* were scalars.
|
|
12
|
+
*
|
|
13
|
+
* What stays in the manager: the legacy-key migrations (#642, #1117). They
|
|
14
|
+
* log, and they run between reading and merging. A pre-engine read therefore
|
|
15
|
+
* does not see a base URL set under a legacy key; that is a known limit named
|
|
16
|
+
* in the issue, not a second rule.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_CONFIG_FILE = "app-default-config.json";
|
|
19
|
+
export declare const DEFAULT_CUSTOM_CONFIG_FILE = "app-custom-config.json";
|
|
20
|
+
type Env = Record<string, string | undefined>;
|
|
21
|
+
/**
|
|
22
|
+
* The marker the setup wizard (or a headless install) writes when a site is
|
|
23
|
+
* installed. Its presence alone says the site is installed (#1410).
|
|
24
|
+
*/
|
|
25
|
+
export declare function installCompletePath(dataFolder: string): string;
|
|
26
|
+
/** Fast-storage data folder: FAST_STORAGE, then the legacy INSTANCE_DATA_FOLDER, then ./data. */
|
|
27
|
+
export declare function instanceDataFolder(env?: Env): string;
|
|
28
|
+
export interface ConfigFilePaths {
|
|
29
|
+
instanceDataFolder: string;
|
|
30
|
+
/** The shipped defaults, in the code checkout: ./config/app-default-config.json */
|
|
31
|
+
defaultConfigPath: string;
|
|
32
|
+
/** The instance's overrides: <instanceDataFolder>/config/<INSTANCE_CONFIG_FILE or app-custom-config.json> */
|
|
33
|
+
customConfigPath: string;
|
|
34
|
+
}
|
|
35
|
+
/** The one derivation of both paths. */
|
|
36
|
+
export declare function configFilePaths(env?: Env, cwd?: string): ConfigFilePaths;
|
|
37
|
+
export interface ConfigFiles {
|
|
38
|
+
/** Null when the shipped file is missing — the caller decides whether that is fatal. */
|
|
39
|
+
defaultConfig: Record<string, unknown> | null;
|
|
40
|
+
/** The custom file with `_`-prefixed comment keys dropped; empty when the file is absent. */
|
|
41
|
+
customConfig: Record<string, unknown>;
|
|
42
|
+
/** The keys the operator set themselves, for "explicit or inherited?" questions (#1163). */
|
|
43
|
+
customKeys: Set<string>;
|
|
44
|
+
customConfigFound: boolean;
|
|
45
|
+
}
|
|
46
|
+
/** Read both files. Synchronous: both callers run at boot, before anything is served. */
|
|
47
|
+
export declare function readConfigFilesSync(paths: ConfigFilePaths): ConfigFiles;
|
|
48
|
+
export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
49
|
+
/**
|
|
50
|
+
* Merge two arrays.
|
|
51
|
+
*
|
|
52
|
+
* If both hold objects that share an identity field (`id`, `authproviderid`),
|
|
53
|
+
* merge by it: a later entry overrides the same one and adds new ones.
|
|
54
|
+
* Otherwise the custom array replaces the default entirely — which is why a set
|
|
55
|
+
* an operator or an add-on may extend one entry at a time is a map, never an
|
|
56
|
+
* array (#1612).
|
|
57
|
+
*/
|
|
58
|
+
export declare function mergeArrays(defaultArray: unknown[], customArray: unknown[]): unknown[];
|
|
59
|
+
/** The entries of a set: a map's `true` keys, in order; a legacy array's strings. Anything else is empty. */
|
|
60
|
+
export declare function enabledEntries(value: unknown): string[];
|
|
61
|
+
/**
|
|
62
|
+
* Deep merge, custom over default.
|
|
63
|
+
*
|
|
64
|
+
* - plain objects: recursively, key by key
|
|
65
|
+
* - arrays: {@link mergeArrays}
|
|
66
|
+
* - an array of names onto a set map: the names are added (#1612)
|
|
67
|
+
* - `null`: an explicit removal — the custom file cannot express a deletion any other way
|
|
68
|
+
* - `undefined`: skipped
|
|
69
|
+
* - anything else: custom wins
|
|
70
|
+
*/
|
|
71
|
+
export declare function deepMergeObjects(defaultObj: Record<string, unknown>, customObj: Record<string, unknown>): Record<string, unknown>;
|
|
72
|
+
/** The top-level merge is the same rule; the name records what the two inputs are. */
|
|
73
|
+
export declare function deepMergeConfigs<T extends Record<string, unknown>>(defaultConfig: T, customConfig: Partial<T>): T;
|
|
74
|
+
export {};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The configuration files: where they are, how they are read, and how the
|
|
3
|
+
* custom file merges over the shipped one (#1214).
|
|
4
|
+
*
|
|
5
|
+
* One implementation, used by `ConfigurationManager` after the engine exists
|
|
6
|
+
* and by `app.ts` before it does (the listen port, TLS at bind time, and the
|
|
7
|
+
* base-URL explicitness check all run before `engine.initialize()`). Until
|
|
8
|
+
* #1214 `app.ts` had its own copy: a shallow spread that replaced a whole map
|
|
9
|
+
* on override, kept `_comment` keys, and computed the paths a second and third
|
|
10
|
+
* time. It happened to agree with the manager only because the keys it read
|
|
11
|
+
* were scalars.
|
|
12
|
+
*
|
|
13
|
+
* What stays in the manager: the legacy-key migrations (#642, #1117). They
|
|
14
|
+
* log, and they run between reading and merging. A pre-engine read therefore
|
|
15
|
+
* does not see a base URL set under a legacy key; that is a known limit named
|
|
16
|
+
* in the issue, not a second rule.
|
|
17
|
+
*/
|
|
18
|
+
import fs from 'fs';
|
|
19
|
+
import path from 'path';
|
|
20
|
+
export const DEFAULT_CONFIG_FILE = 'app-default-config.json';
|
|
21
|
+
export const DEFAULT_CUSTOM_CONFIG_FILE = 'app-custom-config.json';
|
|
22
|
+
/**
|
|
23
|
+
* The marker the setup wizard (or a headless install) writes when a site is
|
|
24
|
+
* installed. Its presence alone says the site is installed (#1410).
|
|
25
|
+
*/
|
|
26
|
+
export function installCompletePath(dataFolder) {
|
|
27
|
+
return path.join(dataFolder, '.install-complete');
|
|
28
|
+
}
|
|
29
|
+
/** Fast-storage data folder: FAST_STORAGE, then the legacy INSTANCE_DATA_FOLDER, then ./data. */
|
|
30
|
+
export function instanceDataFolder(env = process.env) {
|
|
31
|
+
return env.FAST_STORAGE || env.INSTANCE_DATA_FOLDER || './data';
|
|
32
|
+
}
|
|
33
|
+
/** The one derivation of both paths. */
|
|
34
|
+
export function configFilePaths(env = process.env, cwd = process.cwd()) {
|
|
35
|
+
const dataFolder = instanceDataFolder(env);
|
|
36
|
+
return {
|
|
37
|
+
instanceDataFolder: dataFolder,
|
|
38
|
+
defaultConfigPath: path.join(cwd, 'config', DEFAULT_CONFIG_FILE),
|
|
39
|
+
customConfigPath: path.join(dataFolder, 'config', env.INSTANCE_CONFIG_FILE || DEFAULT_CUSTOM_CONFIG_FILE)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** Read both files. Synchronous: both callers run at boot, before anything is served. */
|
|
43
|
+
export function readConfigFilesSync(paths) {
|
|
44
|
+
const defaultConfig = fs.existsSync(paths.defaultConfigPath)
|
|
45
|
+
? JSON.parse(fs.readFileSync(paths.defaultConfigPath, 'utf8'))
|
|
46
|
+
: null;
|
|
47
|
+
const customConfig = {};
|
|
48
|
+
const customConfigFound = fs.existsSync(paths.customConfigPath);
|
|
49
|
+
if (customConfigFound) {
|
|
50
|
+
const raw = JSON.parse(fs.readFileSync(paths.customConfigPath, 'utf8'));
|
|
51
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
52
|
+
if (!key.startsWith('_'))
|
|
53
|
+
customConfig[key] = value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { defaultConfig, customConfig, customKeys: new Set(Object.keys(customConfig)), customConfigFound };
|
|
57
|
+
}
|
|
58
|
+
export function isPlainObject(value) {
|
|
59
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The fields that identify an entry in a list of objects: `id`, or
|
|
63
|
+
* `authproviderid` for `ngdpbase.auth.factors` (#1523, #1612).
|
|
64
|
+
*/
|
|
65
|
+
const IDENTITY_FIELDS = ['id', 'authproviderid'];
|
|
66
|
+
function identityField(arr) {
|
|
67
|
+
if (arr.length === 0)
|
|
68
|
+
return null;
|
|
69
|
+
for (const field of IDENTITY_FIELDS) {
|
|
70
|
+
if (arr.every((e) => isPlainObject(e) && typeof e[field] === 'string'))
|
|
71
|
+
return field;
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Merge two arrays.
|
|
77
|
+
*
|
|
78
|
+
* If both hold objects that share an identity field (`id`, `authproviderid`),
|
|
79
|
+
* merge by it: a later entry overrides the same one and adds new ones.
|
|
80
|
+
* Otherwise the custom array replaces the default entirely — which is why a set
|
|
81
|
+
* an operator or an add-on may extend one entry at a time is a map, never an
|
|
82
|
+
* array (#1612).
|
|
83
|
+
*/
|
|
84
|
+
export function mergeArrays(defaultArray, customArray) {
|
|
85
|
+
const field = identityField(defaultArray);
|
|
86
|
+
if (field && identityField(customArray) === field) {
|
|
87
|
+
const merged = new Map();
|
|
88
|
+
for (const item of defaultArray)
|
|
89
|
+
merged.set(item[field], item);
|
|
90
|
+
for (const item of customArray)
|
|
91
|
+
merged.set(item[field], item);
|
|
92
|
+
return Array.from(merged.values());
|
|
93
|
+
}
|
|
94
|
+
return customArray;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* A set kept as a map of entry → `true` (#1612): any layer — an add-on, the
|
|
98
|
+
* operator — adds an entry without restating the rest, and `false` removes one
|
|
99
|
+
* on the record. An array merged onto such a map adds its entries rather than
|
|
100
|
+
* replacing the map, so a layer written as a list (an older custom config, an
|
|
101
|
+
* add-on) still extends the set.
|
|
102
|
+
*/
|
|
103
|
+
function isEntryMap(value) {
|
|
104
|
+
return isPlainObject(value)
|
|
105
|
+
&& Object.keys(value).length > 0
|
|
106
|
+
&& Object.values(value).every((v) => typeof v === 'boolean' || v === null);
|
|
107
|
+
}
|
|
108
|
+
/** The entries of a set: a map's `true` keys, in order; a legacy array's strings. Anything else is empty. */
|
|
109
|
+
export function enabledEntries(value) {
|
|
110
|
+
if (Array.isArray(value))
|
|
111
|
+
return value.filter((v) => typeof v === 'string');
|
|
112
|
+
if (isPlainObject(value))
|
|
113
|
+
return Object.entries(value).filter(([, v]) => v === true).map(([k]) => k);
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Deep merge, custom over default.
|
|
118
|
+
*
|
|
119
|
+
* - plain objects: recursively, key by key
|
|
120
|
+
* - arrays: {@link mergeArrays}
|
|
121
|
+
* - an array of names onto a set map: the names are added (#1612)
|
|
122
|
+
* - `null`: an explicit removal — the custom file cannot express a deletion any other way
|
|
123
|
+
* - `undefined`: skipped
|
|
124
|
+
* - anything else: custom wins
|
|
125
|
+
*/
|
|
126
|
+
export function deepMergeObjects(defaultObj, customObj) {
|
|
127
|
+
const result = { ...defaultObj };
|
|
128
|
+
for (const key of Object.keys(customObj)) {
|
|
129
|
+
const customValue = customObj[key];
|
|
130
|
+
const defaultValue = result[key];
|
|
131
|
+
if (customValue === undefined)
|
|
132
|
+
continue;
|
|
133
|
+
else if (customValue === null)
|
|
134
|
+
result[key] = customValue;
|
|
135
|
+
else if (Array.isArray(customValue) && Array.isArray(defaultValue))
|
|
136
|
+
result[key] = mergeArrays(defaultValue, customValue);
|
|
137
|
+
else if (Array.isArray(customValue) && isEntryMap(defaultValue) && customValue.every((e) => typeof e === 'string')) {
|
|
138
|
+
result[key] = { ...defaultValue, ...Object.fromEntries(customValue.map((e) => [e, true])) };
|
|
139
|
+
}
|
|
140
|
+
else if (isPlainObject(customValue) && isPlainObject(defaultValue))
|
|
141
|
+
result[key] = deepMergeObjects(defaultValue, customValue);
|
|
142
|
+
else
|
|
143
|
+
result[key] = customValue;
|
|
144
|
+
}
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
/** The top-level merge is the same rule; the name records what the two inputs are. */
|
|
148
|
+
export function deepMergeConfigs(defaultConfig, customConfig) {
|
|
149
|
+
return deepMergeObjects(defaultConfig, customConfig);
|
|
150
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a delegated credential may never carry (#946, #1198, #1576).
|
|
3
|
+
*
|
|
4
|
+
* A person delegates a subset of their own permissions to something acting
|
|
5
|
+
* for them: an agent token they mint, or an app they allow through the OpenID
|
|
6
|
+
* Connect provider. Two kinds of permission are never delegable, however
|
|
7
|
+
* privileged the person:
|
|
8
|
+
*
|
|
9
|
+
* - `admin-*` — administration stays with a person at the keyboard
|
|
10
|
+
* (#946 decision 3);
|
|
11
|
+
* - `token-mint` — a delegation never creates a standing credential, or a
|
|
12
|
+
* leaked one could breed (#1198);
|
|
13
|
+
* - `token-extend` — a delegation never lengthens a standing credential, or a
|
|
14
|
+
* leaked token could keep itself alive (#1734). Step-up already refuses it
|
|
15
|
+
* to a delegation, but a site may turn step-up off; this does not move;
|
|
16
|
+
* - `proxy-grant` — a token or an app never lets someone act for the person
|
|
17
|
+
* (#1762). A proxy grant itself may carry it (that is how a proxy passes
|
|
18
|
+
* access on), so this rule is for tokens and apps, not proxy grants;
|
|
19
|
+
* - `vault-proxy` — it means something only inside a proxy grant, where it
|
|
20
|
+
* lets the proxy into the person's vaults (#1762 slice 2); on a token or an
|
|
21
|
+
* app it would read as access it can never give.
|
|
22
|
+
*
|
|
23
|
+
* One rule for both delegations, so they cannot drift.
|
|
24
|
+
*/
|
|
25
|
+
/** Actions a delegation may never carry, by prefix. */
|
|
26
|
+
export declare const FORBIDDEN_DELEGATED_PREFIX = "admin-";
|
|
27
|
+
/** The permission to mint a token — never carried by a delegation. */
|
|
28
|
+
export declare const MINT_PERMISSION = "token-mint";
|
|
29
|
+
/** The permission to extend a token — never carried by a delegation (#1734). */
|
|
30
|
+
export declare const EXTEND_PERMISSION = "token-extend";
|
|
31
|
+
/** The permission to make a proxy grant — never carried by a token or an app (#1762). */
|
|
32
|
+
export declare const PROXY_GRANT_PERMISSION = "proxy-grant";
|
|
33
|
+
/** The proxy grant's vault marker — never carried by a token or an app (#1762). */
|
|
34
|
+
export declare const VAULT_PROXY_PERMISSION = "vault-proxy";
|
|
35
|
+
/** Why a scope cannot be delegated, or null when it can. */
|
|
36
|
+
export declare function refusedDelegatedScope(scope: string): string | null;
|
|
37
|
+
/**
|
|
38
|
+
* A delegation's permission list as given (#946, #1762): at least one, each a
|
|
39
|
+
* non-empty string, de-duplicated in order. Throws a caller-safe message. One
|
|
40
|
+
* rule for agent tokens and proxy grants.
|
|
41
|
+
*/
|
|
42
|
+
export declare function requireScopeList(scopes: unknown): string[];
|
|
43
|
+
/**
|
|
44
|
+
* "Delegate, not grant" (#1178, #1762): the scopes the delegator does not hold,
|
|
45
|
+
* in order. Empty means every scope is theirs to hand on. `holds` is asked of
|
|
46
|
+
* the policy decision point; with no answer there is no delegation.
|
|
47
|
+
*/
|
|
48
|
+
export declare function lackingScopes(scopes: readonly string[], holds: (scope: string) => Promise<boolean>): Promise<string[]>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive a username from an email address (#1026).
|
|
3
|
+
*
|
|
4
|
+
* Extracted from GoogleOIDCProvider so every auto-provisioning path — OIDC and
|
|
5
|
+
* magic link — produces the same username for the same address. Two rules that
|
|
6
|
+
* disagree would let one provider create `jim-smith` and the other `jimsmith`
|
|
7
|
+
* for the same person.
|
|
8
|
+
*/
|
|
9
|
+
import type UserManager from '../managers/UserManager.js';
|
|
10
|
+
/**
|
|
11
|
+
* Turn an email address into an unused username.
|
|
12
|
+
*
|
|
13
|
+
* Local part, lowercased, with anything outside `[a-z0-9]` replaced by a
|
|
14
|
+
* hyphen; a numeric suffix is appended until the name is free.
|
|
15
|
+
*
|
|
16
|
+
* The result is only free as of this call — a caller that derives a name and
|
|
17
|
+
* creates the account later must be prepared for `createUser` to reject a
|
|
18
|
+
* duplicate that appeared in between.
|
|
19
|
+
*/
|
|
20
|
+
export declare function deriveUsername(email: string, userManager: UserManager): Promise<string>;
|
|
21
|
+
export default deriveUsername;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/** Why a path is, or is not, in the index. */
|
|
2
|
+
export type MediaPathVerdict =
|
|
3
|
+
/** Present in the index under this exact path. */
|
|
4
|
+
'indexed'
|
|
5
|
+
/** Folded into another item by the #515 alternate-format dedup. */
|
|
6
|
+
| 'alternate'
|
|
7
|
+
/** Outside every configured scan folder. */
|
|
8
|
+
| 'not-in-scanned-folder'
|
|
9
|
+
/** Name begins with a dot. */
|
|
10
|
+
| 'dotfile'
|
|
11
|
+
/** Extension is not in the configured set. */
|
|
12
|
+
| 'extension'
|
|
13
|
+
/** A path segment matches `ignoreDirs`. */
|
|
14
|
+
| 'ignore-dir'
|
|
15
|
+
/** Deeper than `maxDepth` below its scan root. */
|
|
16
|
+
| 'max-depth'
|
|
17
|
+
/** Matched a `.ngdpbaseignore` pattern — the file's own or an ancestor's. */
|
|
18
|
+
| 'ignore-pattern'
|
|
19
|
+
/** Carries the `ngdpbaseignore` EXIF/XMP keyword. */
|
|
20
|
+
| 'ignore-keyword'
|
|
21
|
+
/** Eligible, but not in the index — most likely never scanned. */
|
|
22
|
+
| 'eligible-not-indexed';
|
|
23
|
+
/**
|
|
24
|
+
* The subset of {@link MediaPathVerdict} that means "the scanner skipped this"
|
|
25
|
+
* (#1056). `indexed`, `alternate` and `eligible-not-indexed` are states rather
|
|
26
|
+
* than skips, so they are excluded.
|
|
27
|
+
*
|
|
28
|
+
* Shared with the scan-time skip report so the probe and the report cannot
|
|
29
|
+
* disagree about why a file is missing. Two vocabularies for one question is
|
|
30
|
+
* the drift this taxonomy exists to prevent.
|
|
31
|
+
*/
|
|
32
|
+
export type MediaSkipReason = Extract<MediaPathVerdict, 'not-in-scanned-folder' | 'dotfile' | 'extension' | 'ignore-dir' | 'max-depth' | 'ignore-pattern' | 'ignore-keyword'>;
|
|
33
|
+
/** One file or directory the scanner passed over, and why. */
|
|
34
|
+
export interface SkippedEntry {
|
|
35
|
+
/** Absolute path. For a directory-level skip, the directory itself. */
|
|
36
|
+
path: string;
|
|
37
|
+
reason: MediaSkipReason;
|
|
38
|
+
/** True when `path` is a directory whose contents were never walked. */
|
|
39
|
+
isDirectory?: boolean;
|
|
40
|
+
/** The pattern or directory name that matched, when there was one. */
|
|
41
|
+
matched?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The persisted skip report for the most recent scan (#1056).
|
|
45
|
+
*
|
|
46
|
+
* `totalSkipped` is always exact; `skipped` may be capped, in which case
|
|
47
|
+
* `truncated` is true. Reporting a shortened list as if it were complete is the
|
|
48
|
+
* failure this shape exists to prevent — "0 excluded" while a directory-level
|
|
49
|
+
* ignore quietly dropped a whole tree is what made the original counter
|
|
50
|
+
* useless.
|
|
51
|
+
*/
|
|
52
|
+
export interface MediaSkipReport {
|
|
53
|
+
version: number;
|
|
54
|
+
/** ISO timestamp of the scan that produced it. */
|
|
55
|
+
scannedAt: string;
|
|
56
|
+
totalSkipped: number;
|
|
57
|
+
truncated: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Exact count per reason, never capped.
|
|
60
|
+
*
|
|
61
|
+
* Measured on the reference library, the capped list alone would have been
|
|
62
|
+
* close to useless: 14,746 skips, 14,714 of them `extension`, against a cap
|
|
63
|
+
* of 1,000. The list gives examples; these counts give the shape, and the
|
|
64
|
+
* shape is what tells an operator whether their file is one of 14,000
|
|
65
|
+
* sidecars or the single odd case.
|
|
66
|
+
*/
|
|
67
|
+
byReason: Partial<Record<MediaSkipReason, number>>;
|
|
68
|
+
/**
|
|
69
|
+
* Exact count per matched value within a reason — extensions, ignored
|
|
70
|
+
* directory names, ignore patterns. Answers "is my .orf being dropped?"
|
|
71
|
+
* without needing the file to appear in the capped list at all.
|
|
72
|
+
*
|
|
73
|
+
* Entries with no `matched` (dotfiles) are absent by construction.
|
|
74
|
+
*/
|
|
75
|
+
byMatched: Record<string, number>;
|
|
76
|
+
skipped: SkippedEntry[];
|
|
77
|
+
}
|
|
78
|
+
export interface MediaPathExplanation {
|
|
79
|
+
verdict: MediaPathVerdict;
|
|
80
|
+
/** One sentence an operator can act on. */
|
|
81
|
+
detail: string;
|
|
82
|
+
/** Index id, when the path resolves to an item. */
|
|
83
|
+
itemId?: string;
|
|
84
|
+
/** For `alternate`, the primary item this file was folded into. */
|
|
85
|
+
primaryPath?: string;
|
|
86
|
+
/** For `ignore-dir` / `ignore-pattern`, what actually matched. */
|
|
87
|
+
matched?: string;
|
|
88
|
+
}
|
|
89
|
+
/** What the classifier needs to know. Mirrors FileSystemMediaProviderConfig. */
|
|
90
|
+
export interface MediaPathRules {
|
|
91
|
+
/** Absolute scan roots. */
|
|
92
|
+
folders: string[];
|
|
93
|
+
/** Directory names skipped anywhere in the tree. */
|
|
94
|
+
ignoreDirs: string[];
|
|
95
|
+
/** 0 = unlimited. Depth is counted below the scan root. */
|
|
96
|
+
maxDepth: number;
|
|
97
|
+
/** Lowercase, no leading dot. */
|
|
98
|
+
extensions: Set<string>;
|
|
99
|
+
}
|
|
100
|
+
/** Index facts, resolved by the caller so this stays free of I/O. */
|
|
101
|
+
export interface MediaPathIndexFacts {
|
|
102
|
+
/** Item id when this exact path is indexed. */
|
|
103
|
+
indexedId?: string;
|
|
104
|
+
/** Set when the path is an alternate folded into another item. */
|
|
105
|
+
alternateOf?: {
|
|
106
|
+
id: string;
|
|
107
|
+
filePath: string;
|
|
108
|
+
};
|
|
109
|
+
/** `.ngdpbaseignore` pattern that matched, if the caller found one. */
|
|
110
|
+
ignorePatternMatch?: string;
|
|
111
|
+
/** True when the file carries the `ngdpbaseignore` EXIF/XMP keyword. */
|
|
112
|
+
hasIgnoreKeyword?: boolean;
|
|
113
|
+
}
|
|
114
|
+
/** Longest configured folder containing `target`, or null. */
|
|
115
|
+
export declare function scanRootFor(target: string, folders: string[]): string | null;
|
|
116
|
+
/**
|
|
117
|
+
* Classify one absolute path.
|
|
118
|
+
*
|
|
119
|
+
* Evaluated in scanner order. The caller supplies index facts and any
|
|
120
|
+
* `.ngdpbaseignore` match, because those need I/O and this deliberately does
|
|
121
|
+
* none — which is what makes every branch testable without a filesystem.
|
|
122
|
+
*/
|
|
123
|
+
export declare function explainMediaPath(target: string, rules: MediaPathRules, facts?: MediaPathIndexFacts): MediaPathExplanation;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Former-title tracking for renamed pages (#1105).
|
|
3
|
+
*
|
|
4
|
+
* A rename leaves every inbound link pointing at a name that no longer exists.
|
|
5
|
+
* `rewriteInboundLinksAfterRename` repairs the links *inside* pages, but it is
|
|
6
|
+
* structurally unable to help a bookmark, a search result, or a link on someone
|
|
7
|
+
* else's site — those are not ours to rewrite.
|
|
8
|
+
*
|
|
9
|
+
* The record therefore lives in the page's own frontmatter:
|
|
10
|
+
*
|
|
11
|
+
* ```yaml
|
|
12
|
+
* title: New Title
|
|
13
|
+
* formerTitles:
|
|
14
|
+
* - Old Title
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* The page is its own durable record. No sidecar to drift from the pages it
|
|
18
|
+
* describes, no retention window to expire, and it survives backup/restore,
|
|
19
|
+
* export/import and a full index rebuild — because it *is* the data rather than
|
|
20
|
+
* a cache of it. This replaced an earlier design that rehydrated from
|
|
21
|
+
* `page-rename` audit events, which carry a 90-day retention: a page renamed and
|
|
22
|
+
* then left alone would have had its old URL start failing again months later,
|
|
23
|
+
* silently.
|
|
24
|
+
*
|
|
25
|
+
* Two rules carried over from the rename map this supersedes (#1082):
|
|
26
|
+
*
|
|
27
|
+
* **Consulted only after live resolution fails.** The caller tries the real page
|
|
28
|
+
* set first, so a former title can never shadow an existing page.
|
|
29
|
+
*
|
|
30
|
+
* **Ambiguity is refused, never guessed.** If one former title is claimed by more
|
|
31
|
+
* than one page, no answer is given. A confidently wrong redirect to a page that
|
|
32
|
+
* merely once shared a name is worse than the 404 it replaces, because a 404 is
|
|
33
|
+
* visibly broken and a wrong page is not.
|
|
34
|
+
*/
|
|
35
|
+
/** Sentinel for a former title claimed by more than one page. */
|
|
36
|
+
export declare const AMBIGUOUS: unique symbol;
|
|
37
|
+
/** Minimal page shape needed to derive the index. */
|
|
38
|
+
export interface FormerTitleSource {
|
|
39
|
+
title: string;
|
|
40
|
+
formerTitles?: unknown;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Work out what `formerTitles` should be after a save.
|
|
44
|
+
*
|
|
45
|
+
* Returns `undefined` when there is nothing to record and nothing already held,
|
|
46
|
+
* so an ordinary save on a never-renamed page does not add an empty key to its
|
|
47
|
+
* frontmatter.
|
|
48
|
+
*
|
|
49
|
+
* @param existing - Current `formerTitles` value, straight off disk and untrusted
|
|
50
|
+
* @param previousTitle - The title the page had before this save
|
|
51
|
+
* @param newTitle - The title it is being given
|
|
52
|
+
* @returns The new list, or undefined when the field should stay absent
|
|
53
|
+
*/
|
|
54
|
+
export declare function computeFormerTitles(existing: unknown, previousTitle: string | undefined | null, newTitle: string | undefined | null): string[] | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Derive a former-title → current-title lookup from the page set.
|
|
57
|
+
*
|
|
58
|
+
* Keys are lowercased, so resolution is case-insensitive in the same way the
|
|
59
|
+
* provider's title index is.
|
|
60
|
+
*
|
|
61
|
+
* A title held by a **live** page is marked ambiguous rather than resolving to
|
|
62
|
+
* whoever once held it: live resolution runs first and will have answered
|
|
63
|
+
* already, so reaching this map for a live title means something is wrong, and
|
|
64
|
+
* refusing is the safe reading.
|
|
65
|
+
*
|
|
66
|
+
* @param pages - Every page, with its title and raw `formerTitles` frontmatter
|
|
67
|
+
* @returns Map of lowercased former title → current title, or AMBIGUOUS
|
|
68
|
+
*/
|
|
69
|
+
export declare function buildFormerTitleIndex(pages: readonly FormerTitleSource[]): Map<string, string | typeof AMBIGUOUS>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* frontmatterAccess — the single implementation of ACL Tier 1 (#1054).
|
|
3
|
+
*
|
|
4
|
+
* A page's frontmatter can restrict who may act on it, via `access[<action>]`
|
|
5
|
+
* or, for viewing, `audience`. Two subsystems needed that answer and each had
|
|
6
|
+
* its own version of it, which is how they came to disagree:
|
|
7
|
+
*
|
|
8
|
+
* - `PolicyInformationPoint.checkFrontmatterAccess` decided a request, correctly.
|
|
9
|
+
* - `VersioningFileProvider.getRecentChanges` decided a listing, and only
|
|
10
|
+
* consulted `audience` for pages already marked private — so every
|
|
11
|
+
* non-private page carrying an audience was listed to everyone, while
|
|
12
|
+
* `/view/` on the same page returned 403.
|
|
13
|
+
*
|
|
14
|
+
* Both now call this. The invariant worth keeping is that anything which LISTS
|
|
15
|
+
* pages agrees with whatever DECIDES access, so a page can never appear in a
|
|
16
|
+
* listing its viewer cannot open.
|
|
17
|
+
*
|
|
18
|
+
* Pure and dependency-free so the provider layer can use it without reaching
|
|
19
|
+
* up into a manager.
|
|
20
|
+
*/
|
|
21
|
+
/** The frontmatter fields Tier 1 reads. */
|
|
22
|
+
export interface AccessMetadataLike {
|
|
23
|
+
/** `{ view: ['role'], edit: [...] }` — per-action principal lists. */
|
|
24
|
+
access?: unknown;
|
|
25
|
+
/** Shorthand for `access.view`. */
|
|
26
|
+
audience?: unknown;
|
|
27
|
+
}
|
|
28
|
+
export interface FrontmatterDecision {
|
|
29
|
+
/** False when the page carries no rule for this action — caller falls through. */
|
|
30
|
+
decided: boolean;
|
|
31
|
+
/** Meaningful only when `decided`. */
|
|
32
|
+
allowed: boolean;
|
|
33
|
+
/** The principal that matched, for logging. */
|
|
34
|
+
matched?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Principals permitted to perform `action`, or null when the page states no
|
|
38
|
+
* rule and the caller should fall through to global policy.
|
|
39
|
+
*
|
|
40
|
+
* `access[action]` wins; `audience` is the shorthand and applies to `view`
|
|
41
|
+
* only. An empty list counts as no rule — a page saying `audience: []`
|
|
42
|
+
* restricts nobody, which matters because a bare `access:` key parses that way.
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveFrontmatterPrincipals(metadata: AccessMetadataLike | null | undefined, action: string): string[] | null;
|
|
45
|
+
/**
|
|
46
|
+
* Decide Tier 1 for a viewer described as a flat principal list — their roles
|
|
47
|
+
* plus their username. Callers holding a `UserContext` should pass
|
|
48
|
+
* `[...roles, username]`; the provider layer already works in these terms.
|
|
49
|
+
*
|
|
50
|
+
* Denies when the page states a rule and nothing matches. That default is the
|
|
51
|
+
* point: a listing that treats "no match" as "show it" is the bug this exists
|
|
52
|
+
* to prevent.
|
|
53
|
+
*/
|
|
54
|
+
export declare function decideFrontmatterAccess(metadata: AccessMetadataLike | null | undefined, viewerPrincipals: readonly string[], action: string): FrontmatterDecision;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless-install guard for the bootstrap admin password (#1087).
|
|
3
|
+
*
|
|
4
|
+
* `InstallService` documented a safety property the code did not have:
|
|
5
|
+
*
|
|
6
|
+
* > "There is no default — a headless install with that variable unset refuses
|
|
7
|
+
* > to start."
|
|
8
|
+
*
|
|
9
|
+
* It did not refuse. `ngdpbase.user.security.defaultpassword` ships as the
|
|
10
|
+
* literal `admin123`, and `UserManager.getBootstrapPassword()` falls back to a
|
|
11
|
+
* matching hardcoded constant when the key is missing or blank. So a headless
|
|
12
|
+
* container or k8s deploy with `NGDPBASE_ADMIN_PASSWORD` unset came up with
|
|
13
|
+
* `admin` / `admin123` — failing *open*, onto a credential published in this
|
|
14
|
+
* repository, where the documentation said it failed *closed*.
|
|
15
|
+
*
|
|
16
|
+
* This makes the documented behaviour real, for headless installs only.
|
|
17
|
+
*
|
|
18
|
+
* ## Why only headless
|
|
19
|
+
*
|
|
20
|
+
* A fresh local install coming up unattended so the setup wizard is reachable
|
|
21
|
+
* is a deliberate, documented property (`config/app-default-config.json`, the
|
|
22
|
+
* `_comment_user_security_defaultpassword` entry). There is a human present who
|
|
23
|
+
* will see the startup banner that `isAdminUsingDefaultPassword()` drives, and
|
|
24
|
+
* refusing to boot would strand them with no way in.
|
|
25
|
+
*
|
|
26
|
+
* An unattended deploy has neither the human nor the banner. That asymmetry is
|
|
27
|
+
* the whole reason this guard is scoped the way it is.
|
|
28
|
+
*
|
|
29
|
+
* ## Why it compares the value rather than reading the variable
|
|
30
|
+
*
|
|
31
|
+
* The check cannot simply ask whether `NGDPBASE_ADMIN_PASSWORD` is set, because
|
|
32
|
+
* the env-ref is opt-in: an operator supplies the password either by exporting
|
|
33
|
+
* that variable *and* pointing the config key at it, or by setting the key
|
|
34
|
+
* directly in `app-custom-config.json`. Both are legitimate. What actually
|
|
35
|
+
* matters is whether the resolved password is still the well-known shipped one,
|
|
36
|
+
* which catches both "variable unset" and "operator never changed the config".
|
|
37
|
+
*
|
|
38
|
+
* The cost is that an operator who deliberately chooses `admin123` is treated as
|
|
39
|
+
* having chosen nothing. That is the right trade.
|
|
40
|
+
*/
|
|
41
|
+
/** The bootstrap password shipped in `config/app-default-config.json`. */
|
|
42
|
+
export declare const SHIPPED_BOOTSTRAP_PASSWORD = "admin123";
|
|
43
|
+
/**
|
|
44
|
+
* Refuse a headless install that would create the admin account on the shipped
|
|
45
|
+
* password.
|
|
46
|
+
*
|
|
47
|
+
* Called only from the path that actually creates the account — an instance
|
|
48
|
+
* that already has an admin never reaches it, so an existing deployment is
|
|
49
|
+
* unaffected by a restart.
|
|
50
|
+
*
|
|
51
|
+
* @param resolvedPassword - The password `createDefaultAdmin` is about to use.
|
|
52
|
+
* @param isHeadless - Whether `HEADLESS_INSTALL=true`.
|
|
53
|
+
* @throws When headless and the password is absent or still the shipped one.
|
|
54
|
+
*/
|
|
55
|
+
export declare function assertHeadlessBootstrapPassword(resolvedPassword: string, isHeadless: boolean): void;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* imageTransform — shared image transform pipeline for Asset providers.
|
|
3
|
+
*
|
|
4
|
+
* Wraps sharp with a consistent options interface so all providers
|
|
5
|
+
* (BasicAttachmentProvider, FileSystemMediaProvider, future S3Provider, etc.)
|
|
6
|
+
* generate thumbnails and apply format conversions through a single code path.
|
|
7
|
+
*
|
|
8
|
+
* Part of Epic #405 Phase 6 — Transform pipeline.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Options for a single image transform operation.
|
|
12
|
+
*/
|
|
13
|
+
export interface ImageTransformOptions {
|
|
14
|
+
/** Target width in pixels (omit to skip resizing) */
|
|
15
|
+
width?: number;
|
|
16
|
+
/** Target height in pixels (omit to skip resizing) */
|
|
17
|
+
height?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Resize fit mode (default: 'inside').
|
|
20
|
+
* inside — preserve full image, letterbox to fit bounds
|
|
21
|
+
* cover — crop to fill bounds exactly
|
|
22
|
+
* contain — letterbox with background fill
|
|
23
|
+
* fill — stretch to exact bounds (may distort)
|
|
24
|
+
*/
|
|
25
|
+
fit?: 'inside' | 'cover' | 'contain' | 'fill';
|
|
26
|
+
/** Output format (default: 'jpeg') */
|
|
27
|
+
format?: 'jpeg' | 'webp' | 'png';
|
|
28
|
+
/** Compression quality 1–100 (default: 85; ignored for png) */
|
|
29
|
+
quality?: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Transform an image using sharp.
|
|
33
|
+
*
|
|
34
|
+
* Accepts either a Buffer (e.g. a freshly uploaded file) or an absolute
|
|
35
|
+
* file path (e.g. a media-library file). Applies EXIF-based auto-rotation
|
|
36
|
+
* before resizing so orientation is always correct regardless of camera model.
|
|
37
|
+
*
|
|
38
|
+
* @param input - Image buffer or absolute path to the source file
|
|
39
|
+
* @param options - Resize / format options
|
|
40
|
+
* @returns - Transformed image as a Buffer
|
|
41
|
+
* @throws - Re-throws sharp errors (caller should handle)
|
|
42
|
+
*/
|
|
43
|
+
export declare function transformImage(input: Buffer | string, options: ImageTransformOptions): Promise<Buffer>;
|
|
44
|
+
/**
|
|
45
|
+
* Parse a size string of the form "WxH" (e.g. "300x300", "150x150").
|
|
46
|
+
*
|
|
47
|
+
* @returns `{ width, height }` on success, or `null` if the string is malformed.
|
|
48
|
+
*/
|
|
49
|
+
export declare function parseSize(size: string): {
|
|
50
|
+
width: number;
|
|
51
|
+
height: number;
|
|
52
|
+
} | null;
|