@jwilleke/ngdpbase-types 0.0.0 → 5.18.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 +391 -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 +89 -0
- package/lib/src/managers/DatabaseManager.js +236 -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/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 +163 -0
- package/lib/src/utils/auditEventNames.generated.js +168 -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,1645 @@
|
|
|
1
|
+
import { type DeclinedShippedPage } from '../utils/seededShippedPages.js';
|
|
2
|
+
import BaseManager, { BackupData, type ManagerStats } from './BaseManager.js';
|
|
3
|
+
import { WikiEngine } from '../types/WikiEngine.js';
|
|
4
|
+
import { PageProvider, RecentChangesOptions, RecentChangeEntry, GetPagesByCreatorOptions, PagesScanOptions, StoreDeletedEntry, StoreRestoreResult } from '../types/Provider.js';
|
|
5
|
+
import { WikiPage, PageFrontmatter } from '../types/Page.js';
|
|
6
|
+
import type { VersionContent, VersionDiff, VersionHistoryEntry } from '../types/Version.js';
|
|
7
|
+
import type { PermissionSubject } from './UserManager.js';
|
|
8
|
+
import type { CatalogSource, CatalogQuery, CatalogPage, CreativeWork, RebuildOpts, SchemaType } from '../types/Schema.js';
|
|
9
|
+
import { type PageMutationOp } from '../utils/auditEvents.js';
|
|
10
|
+
import { type FixChange, type FixResult, type RunFixesOptions } from '../converters/ncm/fix/index.js';
|
|
11
|
+
import { type NcmResult } from '../converters/ncm/index.js';
|
|
12
|
+
import type { FilterValidationError } from '../parsers/filters/FilterChain.js';
|
|
13
|
+
import { type ActorContext } from '../context/ActorContext.js';
|
|
14
|
+
import { type StoreSearchMatch, type StoreSearchQuery } from '../utils/storeSearchIndex.js';
|
|
15
|
+
import { type Takeout } from '../utils/privateStoreExport.js';
|
|
16
|
+
/**
|
|
17
|
+
* A save refused because the content broke a filter rule (#1037).
|
|
18
|
+
*
|
|
19
|
+
* Carries the structured violations so a route can answer with the same
|
|
20
|
+
* `400 { validationErrors }` shape the editor already understands, rather
|
|
21
|
+
* than degrading to a generic 500.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Options for the page write methods (#1037).
|
|
25
|
+
*/
|
|
26
|
+
export interface PageSaveOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Skip content validation. For content the instance ships ITSELF — addon
|
|
29
|
+
* page seeding, required-pages sync, generated profile pages. Never for
|
|
30
|
+
* anything a user supplied.
|
|
31
|
+
*/
|
|
32
|
+
skipValidation?: boolean;
|
|
33
|
+
/** Attributed to the validation log line; purely diagnostic. */
|
|
34
|
+
userName?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Skip the uniqueness check (#510) for a write whose whole purpose is to
|
|
37
|
+
* repair what that check refuses: the admin raw editor (#689) exists to fix
|
|
38
|
+
* duplicate slugs and duplicate uuids, and the gate would block the very
|
|
39
|
+
* edit being attempted. Never for an ordinary save.
|
|
40
|
+
*/
|
|
41
|
+
skipConflictCheck?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Write the frontmatter EXACTLY as given — no sanitisation (#296), no
|
|
44
|
+
* author/editor stamping (#1354), no provenance (#946), no former titles
|
|
45
|
+
* (#1105), no keyword, status or privacy normalisation (#893, #915, #639).
|
|
46
|
+
*
|
|
47
|
+
* For the admin raw editor (#689) and nothing else: there the textarea IS
|
|
48
|
+
* the page's bytes, and the admin is repairing frontmatter the normal path
|
|
49
|
+
* would rewrite — corrupt YAML, a duplicate slug, a stale uuid. Stamping
|
|
50
|
+
* the admin as `editor` would also be wrong: they are fixing a file, not
|
|
51
|
+
* authoring a revision, and the audit log is where that act is recorded.
|
|
52
|
+
*
|
|
53
|
+
* It also lifts the inline ACL-markup refusal, because a page still
|
|
54
|
+
* carrying legacy `[{ALLOW}]` markup is one of the things the raw editor
|
|
55
|
+
* exists to repair.
|
|
56
|
+
*/
|
|
57
|
+
rawFrontmatter?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* The private name the page is at now, when this save moves it into
|
|
60
|
+
* another of its owner's vaults (#1505). Set by {@link PageManager.movePageToVault};
|
|
61
|
+
* the page, its history and its index entries go with it.
|
|
62
|
+
*/
|
|
63
|
+
moveFrom?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Rewrite a title that breaks the title rule (#1455) instead of refusing
|
|
66
|
+
* the page: the forbidden characters become `-`, the page records what its
|
|
67
|
+
* source called it in `alternateName`, and the result says so in
|
|
68
|
+
* `normalisedTitleFrom` for the caller's report.
|
|
69
|
+
*
|
|
70
|
+
* For the NCM funnel — import, MCP, paste, ingest — which converts data
|
|
71
|
+
* somebody else wrote, where refusing would drop a page from a bulk import
|
|
72
|
+
* over one character (operator, 2026-09-23). The editor never passes it: a
|
|
73
|
+
* person typing a title can simply be told.
|
|
74
|
+
*/
|
|
75
|
+
normaliseTitle?: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Keep the page's `lastModified` from the metadata passed in. For a write
|
|
78
|
+
* that changes no content a reader cares about — e.g. stamping a shipped
|
|
79
|
+
* page's source hash (#1408) — so the page does not jump to the top of
|
|
80
|
+
* Recent Changes.
|
|
81
|
+
*/
|
|
82
|
+
preserveLastModified?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Keep the page's stored `editor` — who is recorded as having written this
|
|
85
|
+
* version — instead of taking it from the acting context (#1354).
|
|
86
|
+
*
|
|
87
|
+
* For the one-time private-link migration (#1457), and said to be that:
|
|
88
|
+
* rewriting `[Title]` to `[store/Title]` changes the syntax of a link, not
|
|
89
|
+
* what anybody wrote, so the last person to edit the page is still the last
|
|
90
|
+
* person to edit it. Stamping the system principal (at boot) or the owner
|
|
91
|
+
* (at unlock) over their name would be a claim about authorship that the
|
|
92
|
+
* migration has no business making. The act itself is in the audit trail,
|
|
93
|
+
* as `link-rewrite`, where it belongs.
|
|
94
|
+
*
|
|
95
|
+
* NOT `rawFrontmatter` (#689): that is the admin raw editor's, it lifts
|
|
96
|
+
* every rule in `normalizeSaveMetadata` rather than this one, and its own
|
|
97
|
+
* doc says so.
|
|
98
|
+
*/
|
|
99
|
+
preserveEditor?: boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Request-level detail the manager cannot see, for the audit record (#1121).
|
|
102
|
+
*
|
|
103
|
+
* The event is emitted by the manager either way — a caller that omits this
|
|
104
|
+
* still gets audited, which is the whole point of auditing at the door. This
|
|
105
|
+
* only ENRICHES the record with what lives on the HTTP request (the client
|
|
106
|
+
* IP) or with a semantic the manager cannot infer (a link-rewrite looks
|
|
107
|
+
* exactly like an ordinary edit from inside PageManager).
|
|
108
|
+
*/
|
|
109
|
+
audit?: {
|
|
110
|
+
/**
|
|
111
|
+
* Override the derived op. Only `link-rewrite` needs this: create, edit
|
|
112
|
+
* and rename are all derivable here from the existing page and the
|
|
113
|
+
* incoming title, so a caller that passes nothing still gets them right.
|
|
114
|
+
*/
|
|
115
|
+
op?: PageMutationOp;
|
|
116
|
+
/** Client IP, when the write arrived over HTTP. */
|
|
117
|
+
ipAddress?: string;
|
|
118
|
+
/** The rename that caused a `link-rewrite`. Ignored for other ops. */
|
|
119
|
+
rewriteOf?: {
|
|
120
|
+
from: string;
|
|
121
|
+
to: string;
|
|
122
|
+
} | null;
|
|
123
|
+
/**
|
|
124
|
+
* Suppress the event. For writes that are a side effect of an already
|
|
125
|
+
* audited operation, where a second record would describe the same act
|
|
126
|
+
* twice.
|
|
127
|
+
*/
|
|
128
|
+
skip?: boolean;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/** What {@link PageManager.savePage} wrote. */
|
|
132
|
+
export interface PageSaveResult {
|
|
133
|
+
/** The body as saved — always the caller's text: a save never rewrites it (#1332). */
|
|
134
|
+
content: string;
|
|
135
|
+
/** Where the page landed (#1462): its title, or its private path (#1456). */
|
|
136
|
+
name: string;
|
|
137
|
+
uuid: string;
|
|
138
|
+
/** The name it had before this save; null for a new page. */
|
|
139
|
+
previousName: string | null;
|
|
140
|
+
/** Pages that linked to the old name, read before a rename took it out of the link graph (#1094). */
|
|
141
|
+
previousReferrers: string[];
|
|
142
|
+
/**
|
|
143
|
+
* What the source called this page, when `normaliseTitle` rewrote a title
|
|
144
|
+
* that broke the rule (#1455). Null when nothing was rewritten. A funnel
|
|
145
|
+
* names it in its report — a page whose title changed under the reader's
|
|
146
|
+
* feet is a fact they are owed.
|
|
147
|
+
*/
|
|
148
|
+
normalisedTitleFrom: string | null;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Why a deciding page read handed back nothing (#1622).
|
|
152
|
+
*
|
|
153
|
+
* - `not-found` — no such page for this caller (a sealed page this session
|
|
154
|
+
* cannot unlock reads the same way).
|
|
155
|
+
* - `no-metadata` — the page exists but its metadata cannot be read, so
|
|
156
|
+
* nothing can be decided about it: damage, refused (#1431 step 7d).
|
|
157
|
+
* - `denied` — the caller may not read it.
|
|
158
|
+
*
|
|
159
|
+
* A caller that answers a person who may not see the page should answer all
|
|
160
|
+
* three alike, so a refusal does not tell them the page exists.
|
|
161
|
+
*/
|
|
162
|
+
/**
|
|
163
|
+
* A site-chrome slot: a page that frames every page (#952, #950, #1622).
|
|
164
|
+
* `configKey` names the page; `legacy` is the deprecated slug-convention
|
|
165
|
+
* chain tried when the key is empty; `defaultPage` is used when the key is
|
|
166
|
+
* unset; `quiet` slots are optional, so a missing page is not reported.
|
|
167
|
+
*/
|
|
168
|
+
export type ChromeSlot = 'left-menu' | 'footer' | 'page-tabs';
|
|
169
|
+
export type PageReadRefusal = 'not-found' | 'no-metadata' | 'denied';
|
|
170
|
+
/**
|
|
171
|
+
* The answer of a deciding page read (#1622): the value, with the name and
|
|
172
|
+
* metadata the `page-read` decision was made on, or why there is none.
|
|
173
|
+
*/
|
|
174
|
+
export type PageRead<T> = {
|
|
175
|
+
ok: true;
|
|
176
|
+
name: string;
|
|
177
|
+
metadata: PageFrontmatter;
|
|
178
|
+
value: T;
|
|
179
|
+
} | {
|
|
180
|
+
ok: false;
|
|
181
|
+
refusal: PageReadRefusal;
|
|
182
|
+
};
|
|
183
|
+
/** {@link PageManager.convertPageToNcm}: the NCM result plus the fix steps that changed the body. */
|
|
184
|
+
export interface PageConvertResult extends NcmResult {
|
|
185
|
+
fixes: FixChange[];
|
|
186
|
+
}
|
|
187
|
+
export declare class PageContentValidationError extends Error {
|
|
188
|
+
readonly validationErrors: FilterValidationError[];
|
|
189
|
+
constructor(pageName: string, validationErrors: FilterValidationError[]);
|
|
190
|
+
}
|
|
191
|
+
/** One vault page with data outside its vault (#1842), as the repair actions offer it. */
|
|
192
|
+
export interface VaultRepairItem {
|
|
193
|
+
owner: string;
|
|
194
|
+
vault: string;
|
|
195
|
+
/** The page's uuid; null for the pages of this vault that shared-pool records name. */
|
|
196
|
+
page: string | null;
|
|
197
|
+
/** Shared-pool files it uses. */
|
|
198
|
+
files: number;
|
|
199
|
+
/** Footnotes in the public folder. */
|
|
200
|
+
footnotes: boolean;
|
|
201
|
+
/** Comments in the public folder. */
|
|
202
|
+
comments: boolean;
|
|
203
|
+
/** Whether the asking context may repair it here. */
|
|
204
|
+
may: boolean;
|
|
205
|
+
/** Why not, in plain words. */
|
|
206
|
+
refusal?: string;
|
|
207
|
+
/**
|
|
208
|
+
* The vault page(s) as their reader sees them — title, link and the outside
|
|
209
|
+
* files by name — only when asked for (`listVaultRepairs(ctx, { details })`)
|
|
210
|
+
* and only those `ctx` may read; absent otherwise. Shown, never logged or
|
|
211
|
+
* recorded.
|
|
212
|
+
*/
|
|
213
|
+
pages?: VaultRepairPage[];
|
|
214
|
+
}
|
|
215
|
+
/** One vault page of a repair item, for the reader of that page only (#1842). */
|
|
216
|
+
export interface VaultRepairPage {
|
|
217
|
+
title: string;
|
|
218
|
+
/** The page's view URL. */
|
|
219
|
+
href: string;
|
|
220
|
+
/** Its shared-pool files, by name, with the URL that serves each today. */
|
|
221
|
+
files: Array<{
|
|
222
|
+
name: string;
|
|
223
|
+
href: string;
|
|
224
|
+
}>;
|
|
225
|
+
}
|
|
226
|
+
/** What one repair moved (#1842). */
|
|
227
|
+
export interface VaultRepairCounts {
|
|
228
|
+
/** Shared-pool files moved in, no public copy left. */
|
|
229
|
+
filesMoved: number;
|
|
230
|
+
/** Shared-pool files copied in, the public copy left for the other pages that use it. */
|
|
231
|
+
filesCopied: number;
|
|
232
|
+
footnotes: number;
|
|
233
|
+
/** Comment files. */
|
|
234
|
+
comments: number;
|
|
235
|
+
}
|
|
236
|
+
export type VaultRepairRefusal = 'not-found' | 'vault-not-open' | 'denied' | 'locked' | 'owner-only';
|
|
237
|
+
export type VaultRepairResult = ({
|
|
238
|
+
ok: true;
|
|
239
|
+
as: 'owner' | 'admin';
|
|
240
|
+
} & VaultRepairCounts) | {
|
|
241
|
+
ok: false;
|
|
242
|
+
refusal: VaultRepairRefusal;
|
|
243
|
+
message: string;
|
|
244
|
+
};
|
|
245
|
+
/** The move of existing captures into the capture vault (#1505), recorded per vault it moved them out of. */
|
|
246
|
+
export declare const CAPTURE_VAULT_MIGRATION = "capture-vault";
|
|
247
|
+
/** The move of private pages from the default vault into their system-category's own vault (#1507). */
|
|
248
|
+
export declare const CATEGORY_VAULT_MIGRATION = "category-vaults";
|
|
249
|
+
/** Private slugs from `private--…` to `vaults--…` (#1507). */
|
|
250
|
+
export declare const VAULT_SLUG_MIGRATION = "vault-slugs";
|
|
251
|
+
/**
|
|
252
|
+
* Minimal WikiContext interface for type safety
|
|
253
|
+
* TODO: Convert WikiContext.js to TypeScript and import proper type
|
|
254
|
+
*/
|
|
255
|
+
interface WikiContext {
|
|
256
|
+
pageName: string;
|
|
257
|
+
content: string;
|
|
258
|
+
userContext?: {
|
|
259
|
+
username?: string;
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* A folder of pages ngdpbase ships, for `PageManager.seedShippedPages` (#1405).
|
|
264
|
+
*/
|
|
265
|
+
export interface ShippedPageSource {
|
|
266
|
+
/** Key in the site's seeded-pages record: `required-pages`, or `addon:<name>` */
|
|
267
|
+
id: string;
|
|
268
|
+
/** For log lines */
|
|
269
|
+
label: string;
|
|
270
|
+
/** Folder of `.md` source pages */
|
|
271
|
+
dir: string;
|
|
272
|
+
/** Frontmatter key for the stamped body hash (`required-source-hash`, `addon-source-hash`) */
|
|
273
|
+
stampKey: string;
|
|
274
|
+
/** Reason to never seed a page, or undefined to seed it */
|
|
275
|
+
exclude?: (data: Record<string, unknown>) => string | undefined;
|
|
276
|
+
/** Metadata the source adds to every page it writes (e.g. `addon`, a default category) */
|
|
277
|
+
extraMetadata?: (data: Record<string, unknown>) => Record<string, unknown>;
|
|
278
|
+
/**
|
|
279
|
+
* Called by the boot seed for a source page this site already holds, with the
|
|
280
|
+
* live page and the parsed source — the addon seed's metadata tidy-ups and
|
|
281
|
+
* opt-in reseed (#971, #1003, #920). Not called for removed or trashed pages.
|
|
282
|
+
*/
|
|
283
|
+
onPresent?: (live: WikiPage, source: {
|
|
284
|
+
data: Record<string, unknown>;
|
|
285
|
+
content: string;
|
|
286
|
+
}, ctx: ActorContext) => Promise<void>;
|
|
287
|
+
}
|
|
288
|
+
/** Why a shipped page was not seeded. */
|
|
289
|
+
export type ShippedPageFailureCode = 'missing-or-invalid-uuid' | 'missing-title' | 'missing-slug' | 'duplicate-uuid' | 'save-failed';
|
|
290
|
+
/** What `seedShippedPages` did with each source page. */
|
|
291
|
+
export interface ShippedPageSeedReport {
|
|
292
|
+
/** Titles saved as new pages */
|
|
293
|
+
seeded: string[];
|
|
294
|
+
/** Source pages already live */
|
|
295
|
+
present: number;
|
|
296
|
+
/** Titles seeded here before (or trashed) and no longer live — left removed */
|
|
297
|
+
removed: string[];
|
|
298
|
+
/** Pages the source excludes */
|
|
299
|
+
excluded: Array<{
|
|
300
|
+
file: string;
|
|
301
|
+
title: string;
|
|
302
|
+
reason: string;
|
|
303
|
+
}>;
|
|
304
|
+
/** Pages that could not be seeded, with why */
|
|
305
|
+
failed: Array<{
|
|
306
|
+
file: string;
|
|
307
|
+
title: string;
|
|
308
|
+
reason: string;
|
|
309
|
+
code: ShippedPageFailureCode;
|
|
310
|
+
}>;
|
|
311
|
+
/** True when this run started the site's record for the source */
|
|
312
|
+
recordStarted: boolean;
|
|
313
|
+
/** Titles of live pages that had no stamp and matched the source, now stamped (#1408) */
|
|
314
|
+
stamped: string[];
|
|
315
|
+
/** Titles of live pages with no stamp whose body differs from the source — left unstamped (#1408) */
|
|
316
|
+
unstamped: string[];
|
|
317
|
+
/** Titles this site declined from the source — never seeded, never reported as a failure (#1412) */
|
|
318
|
+
declined: string[];
|
|
319
|
+
}
|
|
320
|
+
/** What `syncShippedPages` did with each requested uuid. */
|
|
321
|
+
export interface ShippedPageSyncReport {
|
|
322
|
+
/** Uuids saved from the source */
|
|
323
|
+
synced: string[];
|
|
324
|
+
/** Uuids left alone because the live page was edited on this site */
|
|
325
|
+
protected: string[];
|
|
326
|
+
/** Uuids the source does not ship */
|
|
327
|
+
missing: string[];
|
|
328
|
+
/** Uuids that could not be saved, with why */
|
|
329
|
+
failed: Array<{
|
|
330
|
+
uuid: string;
|
|
331
|
+
reason: string;
|
|
332
|
+
}>;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* PageManager - Manages wiki page operations through a pluggable provider system
|
|
336
|
+
*
|
|
337
|
+
* Follows JSPWiki's provider pattern where the actual storage implementation
|
|
338
|
+
* is abstracted behind a provider interface. This allows for different storage
|
|
339
|
+
* backends (filesystem, database, cloud, etc.) to be swapped via configuration.
|
|
340
|
+
*
|
|
341
|
+
* The PageManager acts as a thin coordinator that:
|
|
342
|
+
* - Loads the configured provider (via "ngdpbase.page.provider")
|
|
343
|
+
* - Proxies all page operations to the provider
|
|
344
|
+
* - Maintains the public API for backward compatibility
|
|
345
|
+
*
|
|
346
|
+
* @class PageManager
|
|
347
|
+
* @extends BaseManager
|
|
348
|
+
*
|
|
349
|
+
* @property {PageProvider|null} provider - The active page storage provider
|
|
350
|
+
* @property {string} providerClass - The class name of the loaded provider
|
|
351
|
+
*
|
|
352
|
+
* @see {@link BaseManager} for base functionality
|
|
353
|
+
* @see {@link FileSystemProvider} for default provider implementation
|
|
354
|
+
*
|
|
355
|
+
* @example
|
|
356
|
+
* const pageManager = engine.getManager('PageManager');
|
|
357
|
+
* const page = await pageManager.getPage('Main');
|
|
358
|
+
* console.log(page.content);
|
|
359
|
+
*/
|
|
360
|
+
declare class PageManager extends BaseManager implements CatalogSource {
|
|
361
|
+
/** CatalogSource identifier (Slice 4 of #755 / #772). */
|
|
362
|
+
readonly sourceId = "pages";
|
|
363
|
+
/**
|
|
364
|
+
* Subtypes this source produces. Pages always map to Article (Decision 1).
|
|
365
|
+
* Very-sparse pre-#754 pages still produce Article — `dateCreated` is the
|
|
366
|
+
* only Article-distinctive field and it's now mandatory after the
|
|
367
|
+
* #754 backfill (v3.33.0).
|
|
368
|
+
*/
|
|
369
|
+
readonly types: readonly SchemaType[];
|
|
370
|
+
/**
|
|
371
|
+
* On-disk schema version for the page-frontmatter shape (Decision 6).
|
|
372
|
+
* Bump when the persisted Article-relevant fields on a page change. v1 is
|
|
373
|
+
* the post-#754 (v3.33.0) shape, which adds the required `created`
|
|
374
|
+
* timestamp to every page.
|
|
375
|
+
*/
|
|
376
|
+
static readonly CURRENT_SCHEMA_VERSION = 1;
|
|
377
|
+
readonly currentSchemaVersion = 1;
|
|
378
|
+
private provider;
|
|
379
|
+
/**
|
|
380
|
+
* Lowercased former title → current title, or AMBIGUOUS (#1105).
|
|
381
|
+
*
|
|
382
|
+
* Null until first use. Built lazily rather than at boot: it is only ever
|
|
383
|
+
* consulted on the 404 path, so an instance that never serves a stale URL
|
|
384
|
+
* never pays for it. Invalidated on any save that changes `formerTitles`.
|
|
385
|
+
*/
|
|
386
|
+
private formerTitleIndex;
|
|
387
|
+
private providerClass?;
|
|
388
|
+
/**
|
|
389
|
+
* Creates a new PageManager instance
|
|
390
|
+
*
|
|
391
|
+
* @constructor
|
|
392
|
+
* @param {WikiEngine} engine - The wiki engine instance
|
|
393
|
+
*/
|
|
394
|
+
constructor(engine: WikiEngine);
|
|
395
|
+
/**
|
|
396
|
+
* Initialize the PageManager by loading and initializing the configured provider
|
|
397
|
+
*
|
|
398
|
+
* Reads the page provider configuration and dynamically loads the provider class.
|
|
399
|
+
* The provider name is normalized from lowercase (config) to PascalCase (class name).
|
|
400
|
+
*
|
|
401
|
+
* @async
|
|
402
|
+
* @param {Object} [config={}] - Configuration object (unused, reads from ConfigurationManager)
|
|
403
|
+
* @returns {Promise<void>}
|
|
404
|
+
* @throws {Error} If ConfigurationManager is not available or provider fails to load
|
|
405
|
+
*
|
|
406
|
+
* @example
|
|
407
|
+
* await pageManager.initialize();
|
|
408
|
+
* // Loads FileSystemProvider by default
|
|
409
|
+
*/
|
|
410
|
+
initialize(config?: Record<string, unknown>): Promise<void>;
|
|
411
|
+
/**
|
|
412
|
+
* Startup check (#1834): every vault of a sealed kind whose files are in the
|
|
413
|
+
* clear — written before its door was opened, or under a `store.json` that
|
|
414
|
+
* is not sealed. Such a vault is closed (its store I/O refuses it); this
|
|
415
|
+
* makes it visible: an error in the log, the degraded-subsystem banner and a
|
|
416
|
+
* system notification, each naming owner and vault. Nothing is moved, sealed
|
|
417
|
+
* or deleted; an administrator decides what to do with the files.
|
|
418
|
+
*/
|
|
419
|
+
private reportUnsealedVaults;
|
|
420
|
+
/**
|
|
421
|
+
* Convert a single page (frontmatter + name) to its schema.org `Article`
|
|
422
|
+
* record. Public so view-page / API consumers can build the same shape
|
|
423
|
+
* without rerouting through `CatalogManager`.
|
|
424
|
+
*
|
|
425
|
+
* The render mapper for the `<script type="application/ld+json">` block on
|
|
426
|
+
* `view.ejs` is a separate utility (`src/utils/buildPageJsonLd.ts`) — it
|
|
427
|
+
* adds `@context` and a couple of JSON-LD-render conventions on top of this
|
|
428
|
+
* internal record. Per `docs/schemas.md` Decision 11.
|
|
429
|
+
*/
|
|
430
|
+
toCreativeWork(pageName: string, metadata: PageFrontmatter | null | undefined, options?: {
|
|
431
|
+
baseUrl?: string;
|
|
432
|
+
autoTaggedKeywords?: string[];
|
|
433
|
+
}): CreativeWork;
|
|
434
|
+
/**
|
|
435
|
+
* CatalogSource.get — fetch a single page by UUID and return its `Article`.
|
|
436
|
+
*
|
|
437
|
+
* Returns null for unknown UUIDs. ACL filtering is **not** applied here —
|
|
438
|
+
* callers needing the full WikiContext path should go through `getPage()`.
|
|
439
|
+
* (Same convention as `MediaManager.get` / `AttachmentManager.get`.)
|
|
440
|
+
*/
|
|
441
|
+
get(identifier: string): Promise<CreativeWork | null>;
|
|
442
|
+
/**
|
|
443
|
+
* CatalogSource.list — paginated list of CreativeWorks across all pages.
|
|
444
|
+
*
|
|
445
|
+
* Initial implementation: pulls `getAllPageInfo()`, applies text / keyword
|
|
446
|
+
* / type / dateRange filters in-process, returns up to `query.limit`
|
|
447
|
+
* items. Cursor pagination is not implemented yet — the existing
|
|
448
|
+
* search-provider path (Lunr / ES) is the production query surface;
|
|
449
|
+
* this method exists so cross-source CatalogManager fan-out works.
|
|
450
|
+
*/
|
|
451
|
+
list(query: CatalogQuery): Promise<CatalogPage>;
|
|
452
|
+
/**
|
|
453
|
+
* CatalogSource.rebuild — re-scan storage and rebuild the page-index. Wraps
|
|
454
|
+
* `provider.refreshPageList()`. The `force` option is accepted for
|
|
455
|
+
* `RebuildOpts` compatibility but doesn't change behavior — the underlying
|
|
456
|
+
* provider always does a full rescan.
|
|
457
|
+
*/
|
|
458
|
+
rebuild(_opts?: RebuildOpts): Promise<void>;
|
|
459
|
+
/**
|
|
460
|
+
* Seed the pages a source ships into this site (#1405, epic #1404).
|
|
461
|
+
*
|
|
462
|
+
* The one place shipped pages are seeded. For each source page, in order:
|
|
463
|
+
*
|
|
464
|
+
* - no valid uuid, no title or no slug: skipped and reported
|
|
465
|
+
* - a uuid already used by another file of the same source: skipped and reported
|
|
466
|
+
* - excluded by the source (e.g. github-only categories): skipped
|
|
467
|
+
* - **in this site's seeded record**: never seeded again. If it is no longer
|
|
468
|
+
* live it was removed here, and it stays removed.
|
|
469
|
+
* - live already: added to the record
|
|
470
|
+
* - in the trash: added to the record and skipped (#1403)
|
|
471
|
+
* - otherwise: saved through `savePage` with the source's stamp, indexed for
|
|
472
|
+
* search, and added to the record
|
|
473
|
+
*
|
|
474
|
+
* A save the ValidationManager refuses (a title or slug held by another uuid)
|
|
475
|
+
* is reported, not retried; Required Pages Sync shows it as a UUID mismatch.
|
|
476
|
+
*
|
|
477
|
+
* A source this site has no record for yet starts it from the uuids that are
|
|
478
|
+
* live or in the trash, so an established site does not treat its shipped
|
|
479
|
+
* pages as new.
|
|
480
|
+
*
|
|
481
|
+
* @param source - Where the pages come from and how they are stamped
|
|
482
|
+
* @param ctx - Who seeds; a boot job's system context
|
|
483
|
+
* @returns What happened to each source page
|
|
484
|
+
*/
|
|
485
|
+
seedShippedPages(source: ShippedPageSource, ctx: ActorContext): Promise<ShippedPageSeedReport>;
|
|
486
|
+
/**
|
|
487
|
+
* Save the source's copy of the given uuids as this site's pages (#1406) —
|
|
488
|
+
* the explicit sync behind Admin → Required Pages Sync.
|
|
489
|
+
*
|
|
490
|
+
* Unlike the boot seed this overwrites a live page, and it ignores the
|
|
491
|
+
* seeded-pages record: an admin's Sync is how a page removed on the site is
|
|
492
|
+
* brought back. Without `force`, a live page edited on this site (the
|
|
493
|
+
* `user-modified` flag, or a body that no longer matches its stamp) is left
|
|
494
|
+
* alone and reported as protected.
|
|
495
|
+
*
|
|
496
|
+
* A live page under another title is saved under that title, with the
|
|
497
|
+
* source title in the metadata — a rename, not a second page (#1376).
|
|
498
|
+
*
|
|
499
|
+
* @param source - Where the pages come from and how they are stamped
|
|
500
|
+
* @param uuids - Pages to sync
|
|
501
|
+
* @param options - `force` overwrites pages edited on this site
|
|
502
|
+
* @param ctx - Who asked
|
|
503
|
+
* @returns What happened to each uuid
|
|
504
|
+
*/
|
|
505
|
+
syncShippedPages(source: ShippedPageSource, uuids: string[], options: {
|
|
506
|
+
force?: boolean;
|
|
507
|
+
}, ctx: ActorContext): Promise<ShippedPageSyncReport>;
|
|
508
|
+
/**
|
|
509
|
+
* Record that this site will not take a shipped page from a source (#1412).
|
|
510
|
+
*
|
|
511
|
+
* For a page whose title or slug belongs to a page the site would rather
|
|
512
|
+
* keep: the seeder skips it from then on, so the conflict stops being
|
|
513
|
+
* reported at every start-up. Nothing is written to any page, and the entry
|
|
514
|
+
* is per source — an addon shipping the same uuid is still offered.
|
|
515
|
+
*
|
|
516
|
+
* @param sourceId - `required-pages`, or `addon:<name>`
|
|
517
|
+
* @param uuid - The shipped page's uuid
|
|
518
|
+
* @param reason - The conflict that prompted it
|
|
519
|
+
* @param ctx - Who decided
|
|
520
|
+
* @returns True when it was not already declined
|
|
521
|
+
*/
|
|
522
|
+
declineShippedPage(sourceId: string, uuid: string, reason: string, ctx: ActorContext): Promise<boolean>;
|
|
523
|
+
/**
|
|
524
|
+
* Undo a decline (#1412), so the page is offered again at the next start-up
|
|
525
|
+
* or Sync.
|
|
526
|
+
*
|
|
527
|
+
* @param sourceId - `required-pages`, or `addon:<name>`
|
|
528
|
+
* @param uuid - The shipped page's uuid
|
|
529
|
+
* @param ctx - Who decided
|
|
530
|
+
* @returns True when there was a decline to undo
|
|
531
|
+
*/
|
|
532
|
+
allowShippedPage(sourceId: string, uuid: string, ctx: ActorContext): Promise<boolean>;
|
|
533
|
+
/**
|
|
534
|
+
* Every shipped page this site has declined, as source id → uuid → entry
|
|
535
|
+
* (#1412). Read by Required Pages Sync to list them.
|
|
536
|
+
*/
|
|
537
|
+
declinedShippedPages(): Promise<Record<string, Record<string, DeclinedShippedPage>>>;
|
|
538
|
+
/** The site's seeded-pages record. */
|
|
539
|
+
private loadSeededRecord;
|
|
540
|
+
/**
|
|
541
|
+
* The source for the required pages this release ships (#1405, #1406).
|
|
542
|
+
* Pages in a category whose source is `repo` are excluded.
|
|
543
|
+
*/
|
|
544
|
+
requiredPagesSource(): ShippedPageSource;
|
|
545
|
+
/**
|
|
546
|
+
* One-time access backfill for a live required page (#1411): a page with no
|
|
547
|
+
* `access` gets the default for its category, as a metadata-only save that
|
|
548
|
+
* keeps `lastModified`. A page that has any `access` — an operator's choice —
|
|
549
|
+
* is left alone.
|
|
550
|
+
*/
|
|
551
|
+
private backfillShippedPageAccess;
|
|
552
|
+
/**
|
|
553
|
+
* The source for the pages an addon ships (#1406). Pages it writes carry
|
|
554
|
+
* `addon` and, when the source names none, the `addon` category (#931).
|
|
555
|
+
*
|
|
556
|
+
* @param addonName - The addon's name
|
|
557
|
+
* @param pagesDir - The addon's `pages/` folder
|
|
558
|
+
*/
|
|
559
|
+
addonPagesSource(addonName: string, pagesDir: string): ShippedPageSource;
|
|
560
|
+
/**
|
|
561
|
+
* Stamp a live shipped page that has no stamp, when its body matches the
|
|
562
|
+
* source (#1408). Its hash then equals the source's, so the stamp records
|
|
563
|
+
* exactly what the site holds; without one, a later source change cannot be
|
|
564
|
+
* told from a local edit. A page whose body differs cannot be proven
|
|
565
|
+
* unedited and is left unstamped, reported for an admin.
|
|
566
|
+
*
|
|
567
|
+
* A metadata-only save: no version is written for the unchanged body
|
|
568
|
+
* (#1407), and `lastModified` is kept so Recent Changes stays quiet.
|
|
569
|
+
*
|
|
570
|
+
* @returns True when the page was stamped
|
|
571
|
+
*/
|
|
572
|
+
private backfillShippedPageStamp;
|
|
573
|
+
/**
|
|
574
|
+
* This site's own copy of a shipped page, or null. A page served from the
|
|
575
|
+
* source folder is the source, not the site's page.
|
|
576
|
+
*/
|
|
577
|
+
private storeCopyByUUID;
|
|
578
|
+
/**
|
|
579
|
+
* Write one shipped page through `savePage`, stamped, as the system
|
|
580
|
+
* principal, then index it for search. Throws when the save is refused.
|
|
581
|
+
*/
|
|
582
|
+
private saveShippedPage;
|
|
583
|
+
/**
|
|
584
|
+
* Seed the required pages this release ships (#1405). Runs at the end of
|
|
585
|
+
* engine start-up, once ValidationManager and SearchManager exist, so a
|
|
586
|
+
* seeded page gets the conflict check and search indexing a save gets.
|
|
587
|
+
*
|
|
588
|
+
* A required page is seeded once per site: pages new in a release appear at
|
|
589
|
+
* restart, and a page removed on the site stays removed (#954). Pages in a
|
|
590
|
+
* category whose source is `repo` are never seeded.
|
|
591
|
+
*
|
|
592
|
+
* Best-effort: a failure is logged and never blocks start-up.
|
|
593
|
+
*/
|
|
594
|
+
seedRequiredPages(): Promise<void>;
|
|
595
|
+
/**
|
|
596
|
+
* Get provider information
|
|
597
|
+
* @private
|
|
598
|
+
*/
|
|
599
|
+
private getProviderInfo;
|
|
600
|
+
/**
|
|
601
|
+
* Normalize provider name from configuration (lowercase) to class name (PascalCase)
|
|
602
|
+
* @param {string} providerName - Provider name from configuration (e.g., 'filesystemprovider')
|
|
603
|
+
* @returns {string} Normalized class name (e.g., 'FileSystemProvider')
|
|
604
|
+
* @private
|
|
605
|
+
*/
|
|
606
|
+
private normalizeProviderName;
|
|
607
|
+
/**
|
|
608
|
+
* Get the current page provider instance
|
|
609
|
+
*
|
|
610
|
+
* @returns {PageProvider} The active provider instance
|
|
611
|
+
*
|
|
612
|
+
* @example
|
|
613
|
+
* const provider = pageManager.getCurrentPageProvider();
|
|
614
|
+
* const info = provider.getProviderInfo();
|
|
615
|
+
* console.log('Using:', info.name);
|
|
616
|
+
*/
|
|
617
|
+
getCurrentPageProvider(): PageProvider | null;
|
|
618
|
+
getPageUUID(identifier: string, ctx: ActorContext): string | null;
|
|
619
|
+
/**
|
|
620
|
+
* Evict a page's cached content, and bump the page-data version so every
|
|
621
|
+
* cached render re-renders (#1751).
|
|
622
|
+
*/
|
|
623
|
+
invalidatePageCache(identifier: string): string | null;
|
|
624
|
+
/**
|
|
625
|
+
* Get complete page with content and metadata
|
|
626
|
+
*
|
|
627
|
+
* Retrieves a page by UUID, title, or slug. Returns the full page object
|
|
628
|
+
* including content, metadata, and file path information.
|
|
629
|
+
*
|
|
630
|
+
* @async
|
|
631
|
+
* @param {string} identifier - Page UUID, title, or slug
|
|
632
|
+
* @returns {Promise<WikiPage|null>} Page object or null if not found
|
|
633
|
+
*
|
|
634
|
+
* @example
|
|
635
|
+
* const page = await pageManager.getPage('Main');
|
|
636
|
+
* console.log(page.title, page.metadata.author);
|
|
637
|
+
*/
|
|
638
|
+
getPage(identifier: string, ctx: ActorContext): Promise<WikiPage | null>;
|
|
639
|
+
/**
|
|
640
|
+
* The page in a site-chrome slot — the left menu, the footer, the page-tabs
|
|
641
|
+
* template — read the same way for every caller (#952, #950, #1622).
|
|
642
|
+
*
|
|
643
|
+
* Resolution order (#952):
|
|
644
|
+
* 1. The configured page, when the slot's key is set. Authoritative: if
|
|
645
|
+
* that page is missing, the legacy chain is NOT tried, because silently
|
|
646
|
+
* substituting a page the operator did not choose is the problem the
|
|
647
|
+
* setting exists to remove.
|
|
648
|
+
* 2. Otherwise the legacy slug chain (`left-menu-content` before
|
|
649
|
+
* `LeftMenu`), logged at info when a page shadows the core one.
|
|
650
|
+
*
|
|
651
|
+
* Chrome is read as the anonymous reader, for every viewer: it frames every
|
|
652
|
+
* page for everyone, and gating a fragment only ever removed navigation
|
|
653
|
+
* site-wide without protecting the pages it links to (#950). A restriction
|
|
654
|
+
* set on a chrome page is therefore reported, not enforced. This is not a
|
|
655
|
+
* person's page view, so it does not pass the page-read door; it is the one
|
|
656
|
+
* place chrome is read, which is what the door rule needs (#1622).
|
|
657
|
+
*
|
|
658
|
+
* @param slot - Which chrome page
|
|
659
|
+
* @returns The page, or null when none resolves
|
|
660
|
+
*/
|
|
661
|
+
readChromePage(slot: ChromeSlot): Promise<WikiPage | null>;
|
|
662
|
+
/**
|
|
663
|
+
* Warn when a chrome page carries access control that is no longer honoured (#950).
|
|
664
|
+
*
|
|
665
|
+
* `LeftMenu` and `Footer` used to run the full ACL evaluator, and a denial
|
|
666
|
+
* replaced the fragment with an empty string — so an affected user lost the
|
|
667
|
+
* sidebar or footer on EVERY page of the site, with nothing logged above
|
|
668
|
+
* `info` and nothing pointing at permissions as the cause.
|
|
669
|
+
*
|
|
670
|
+
* That gating is removed. A fragment is never a destination: denying it
|
|
671
|
+
* protected nothing, because the pages it links to still enforce their own
|
|
672
|
+
* ACLs, and a link the user cannot follow returns a comprehensible 403.
|
|
673
|
+
*
|
|
674
|
+
* The failure mode of removing it is the mirror image — frontmatter that
|
|
675
|
+
* silently stops working — so a restriction on a chrome page is reported
|
|
676
|
+
* rather than dropped in silence. `private: true` is called out by name
|
|
677
|
+
* because it is a hard constraint everywhere else in the evaluator, and an
|
|
678
|
+
* operator who set it has the strongest expectation of enforcement.
|
|
679
|
+
*
|
|
680
|
+
* @param label - Chrome slot being rendered
|
|
681
|
+
* @param metadata - The chrome page's frontmatter, if a page was resolved
|
|
682
|
+
*/
|
|
683
|
+
private warnOnChromeRestriction;
|
|
684
|
+
/**
|
|
685
|
+
* Get only page content (without metadata)
|
|
686
|
+
*
|
|
687
|
+
* More efficient than getPage() when only content is needed.
|
|
688
|
+
*
|
|
689
|
+
* @async
|
|
690
|
+
* @param {string} identifier - Page UUID, title, or slug
|
|
691
|
+
* @returns {Promise<string>} Markdown content
|
|
692
|
+
*
|
|
693
|
+
* @example
|
|
694
|
+
* const content = await pageManager.getPageContent('Main');
|
|
695
|
+
* console.log(content);
|
|
696
|
+
*/
|
|
697
|
+
getPageContent(identifier: string, ctx: ActorContext): Promise<string>;
|
|
698
|
+
/**
|
|
699
|
+
* Get only page metadata (without content)
|
|
700
|
+
*
|
|
701
|
+
* More efficient than getPage() when only metadata is needed.
|
|
702
|
+
*
|
|
703
|
+
* @async
|
|
704
|
+
* @param {string} identifier - Page UUID, title, or slug
|
|
705
|
+
* @returns {Promise<PageFrontmatter|null>} Metadata object or null if not found
|
|
706
|
+
*
|
|
707
|
+
* @example
|
|
708
|
+
* const meta = await pageManager.getPageMetadata('Main');
|
|
709
|
+
* console.log('Author:', meta.author);
|
|
710
|
+
*/
|
|
711
|
+
/**
|
|
712
|
+
* Resolve a former page title to the page that holds it now, or null (#1105).
|
|
713
|
+
*
|
|
714
|
+
* **Callers must have already failed live resolution.** This is a fallback, not
|
|
715
|
+
* a lookup: answering for a title that still exists would let a stale name
|
|
716
|
+
* shadow a real page. `viewPage` calls it only on the not-found path.
|
|
717
|
+
*
|
|
718
|
+
* Returns null on ambiguity rather than guessing. A confidently wrong redirect
|
|
719
|
+
* to a page that merely once shared a name is worse than the 404 it replaces —
|
|
720
|
+
* a 404 is visibly broken, and the wrong page is not.
|
|
721
|
+
*
|
|
722
|
+
* @param formerTitle - The title that failed to resolve
|
|
723
|
+
* @returns The current title of the page that was renamed from it, or null
|
|
724
|
+
*/
|
|
725
|
+
resolveFormerTitle(formerTitle: string): Promise<string | null>;
|
|
726
|
+
/**
|
|
727
|
+
* Walk the page set and derive the former-title index.
|
|
728
|
+
*
|
|
729
|
+
* Metadata comes from the provider's already-loaded cache, so this is memory
|
|
730
|
+
* work rather than I/O. A page whose metadata cannot be read is skipped — a
|
|
731
|
+
* single unreadable page must not deny resolution for every other.
|
|
732
|
+
*
|
|
733
|
+
* @private
|
|
734
|
+
*/
|
|
735
|
+
private buildFormerTitleIndexFromPages;
|
|
736
|
+
getPageMetadata(identifier: string, ctx: ActorContext): Promise<PageFrontmatter | null>;
|
|
737
|
+
/**
|
|
738
|
+
* May `ctx` read this page, and which page is it? The one `page-read`
|
|
739
|
+
* decision (#1622), made for every deciding read below.
|
|
740
|
+
*
|
|
741
|
+
* The identifier — uuid, slug, title or vault name — is resolved once, and
|
|
742
|
+
* the decision is made on the page it resolved to, by its canonical name.
|
|
743
|
+
* `key` is how the reads then reach that same page: a vault page by its
|
|
744
|
+
* vault name, any other by its uuid, so a read cannot land on a different
|
|
745
|
+
* page than the one decided about.
|
|
746
|
+
*
|
|
747
|
+
* The page decision itself is the PIP's (`canUserAccessPage`, `view`):
|
|
748
|
+
* delegation ceilings, private, audience, policy. Without a PIP nothing is
|
|
749
|
+
* readable — never the page by accident.
|
|
750
|
+
*/
|
|
751
|
+
private decideRead;
|
|
752
|
+
/** The provider's version reads, or a throw when it keeps no history. */
|
|
753
|
+
private versionReads;
|
|
754
|
+
/**
|
|
755
|
+
* Would `ctx` be allowed to read this page — asked only to decide whether to
|
|
756
|
+
* SHOW something (`<wiki:If exists:Page>`), never before reading it (#1668).
|
|
757
|
+
* The same `page-read` decision as {@link readPage}, so the two agree; it
|
|
758
|
+
* reads no content and writes no audit record. A page `ctx` may not read
|
|
759
|
+
* answers false, as one that does not exist does.
|
|
760
|
+
*
|
|
761
|
+
* @param identifier - Page UUID, title, slug or vault name
|
|
762
|
+
* @param ctx - Whose question this is (#1179) — never defaulted
|
|
763
|
+
*/
|
|
764
|
+
mayShowPage(identifier: string, ctx: ActorContext): Promise<boolean>;
|
|
765
|
+
/**
|
|
766
|
+
* Read a page — content and metadata — as `ctx`, if `ctx` may read it (#1622).
|
|
767
|
+
*
|
|
768
|
+
* @param identifier - Page UUID, title, slug or vault name
|
|
769
|
+
* @param ctx - Whose read this is (#1179) — never defaulted
|
|
770
|
+
* @returns The page, with the name and metadata the decision was made on,
|
|
771
|
+
* or why there is none
|
|
772
|
+
*/
|
|
773
|
+
readPage(identifier: string, ctx: ActorContext): Promise<PageRead<WikiPage>>;
|
|
774
|
+
/**
|
|
775
|
+
* A page's version history, newest first, if `ctx` may read the page (#1622).
|
|
776
|
+
*
|
|
777
|
+
* @throws When the provider keeps no history, or the history cannot be read
|
|
778
|
+
*/
|
|
779
|
+
readVersionHistory(identifier: string, ctx: ActorContext, limit?: number): Promise<PageRead<VersionHistoryEntry[]>>;
|
|
780
|
+
/**
|
|
781
|
+
* One past version of a page, if `ctx` may read the page (#1622).
|
|
782
|
+
*
|
|
783
|
+
* @throws When the provider keeps no history, or the version does not exist
|
|
784
|
+
*/
|
|
785
|
+
readVersion(identifier: string, version: number, ctx: ActorContext): Promise<PageRead<VersionContent>>;
|
|
786
|
+
/**
|
|
787
|
+
* The difference between two versions of a page, if `ctx` may read the page (#1622).
|
|
788
|
+
*
|
|
789
|
+
* @throws When the provider keeps no history, or either version does not exist
|
|
790
|
+
*/
|
|
791
|
+
readVersionDiff(identifier: string, fromVersion: number, toVersion: number, ctx: ActorContext): Promise<PageRead<VersionDiff>>;
|
|
792
|
+
/**
|
|
793
|
+
* Read the literal raw file content for a page — frontmatter YAML + body
|
|
794
|
+
* markdown together, exactly as it appears on disk. Backs the admin
|
|
795
|
+
* "Edit raw" UI (#689); intended for recovery of pages whose frontmatter
|
|
796
|
+
* the normal getPage() path sanitises or normalises away. Returns null
|
|
797
|
+
* when the page is unknown or the provider doesn't support raw reads.
|
|
798
|
+
*/
|
|
799
|
+
getRawPageContent(identifier: string): Promise<{
|
|
800
|
+
filePath: string;
|
|
801
|
+
content: string;
|
|
802
|
+
} | null>;
|
|
803
|
+
/**
|
|
804
|
+
* Admin-override save (#689): the admin "Edit raw" textarea, split into
|
|
805
|
+
* frontmatter and body and written through the door (#1462).
|
|
806
|
+
*
|
|
807
|
+
* It goes through `savePage` like every other write — so it versions, keeps
|
|
808
|
+
* the shared indexes in step and is audited — with the three opt-outs that
|
|
809
|
+
* make raw editing what it is, stated at this one call site:
|
|
810
|
+
*
|
|
811
|
+
* rawFrontmatter the file's bytes are the point: no sanitisation, no
|
|
812
|
+
* stamping. The admin is repairing a file, not authoring
|
|
813
|
+
* a revision, so they do NOT become its `editor`.
|
|
814
|
+
* skipConflictCheck a duplicate slug or uuid is what it was opened to fix.
|
|
815
|
+
* skipValidation so is content a filter rule refuses.
|
|
816
|
+
*
|
|
817
|
+
* The audit op is pinned to `edit`: the raw editor writes an existing file
|
|
818
|
+
* in place, and the identifier in its URL may be a uuid, so a difference
|
|
819
|
+
* between it and the frontmatter title is not a rename. The route adds its
|
|
820
|
+
* own `page.raw-edit` record, which carries what this one cannot — the
|
|
821
|
+
* admin override, the file path and the byte count.
|
|
822
|
+
*
|
|
823
|
+
* Throws if the textarea content isn't parseable YAML.
|
|
824
|
+
*/
|
|
825
|
+
saveRawPageWithAdminOverride(pageName: string, rawFileContent: string, ctx: ActorContext): Promise<PageSaveResult>;
|
|
826
|
+
/**
|
|
827
|
+
* Refuse a save whose content breaks a filter rule (#1037).
|
|
828
|
+
*
|
|
829
|
+
* Lives here, not in the route handlers, because that is where it kept
|
|
830
|
+
* getting forgotten: `/save/:page` had the check, `POST /create` — the
|
|
831
|
+
* "Create New Page" item in the header — did not, and neither did
|
|
832
|
+
* `/api/page/ingest`. Every write path reaches PageManager, so putting the
|
|
833
|
+
* gate here means a new caller inherits it instead of having to remember.
|
|
834
|
+
*
|
|
835
|
+
* `skipValidation` is for TRUSTED content the instance ships itself —
|
|
836
|
+
* addon page seeding, required-pages sync, generated profile pages. Those
|
|
837
|
+
* run at startup, and a rule aimed at user input must never be able to stop
|
|
838
|
+
* the instance booting. It is an explicit opt-out precisely so that reading
|
|
839
|
+
* a call site tells you which kind of content it writes.
|
|
840
|
+
*
|
|
841
|
+
* A failure inside ValidationManager is NOT a failure to save: it already
|
|
842
|
+
* degrades to "no errors" internally, and this method must not turn an
|
|
843
|
+
* infrastructure problem into a refused edit.
|
|
844
|
+
*
|
|
845
|
+
* @throws PageContentValidationError when a filter reports severity 'error'
|
|
846
|
+
*/
|
|
847
|
+
private assertContentPasses;
|
|
848
|
+
/**
|
|
849
|
+
* The frontmatter a save writes, from the frontmatter it was given (#1462).
|
|
850
|
+
*
|
|
851
|
+
* Everything a write must not be able to forget about metadata, in one
|
|
852
|
+
* place: author preservation and the editor of record (#1354), agent
|
|
853
|
+
* provenance (#946), former titles (#1105), the privacy and vocabulary
|
|
854
|
+
* rules (#639, #893, #915) and sanitisation (#296).
|
|
855
|
+
*
|
|
856
|
+
* Only the admin raw editor skips it (`rawFrontmatter`, #689): there the
|
|
857
|
+
* file's bytes are the point, and every rule here would rewrite them.
|
|
858
|
+
*
|
|
859
|
+
* @param pageName - The page being written: its title, or a private path
|
|
860
|
+
* @param metadata - The frontmatter as the caller gave it
|
|
861
|
+
* @param existingPage - The page as stored, or null when this save creates it
|
|
862
|
+
* @param actingUser - Who is writing this revision, from the context (#1164)
|
|
863
|
+
* @param viaToken - The agent token this write came through (#946), if any
|
|
864
|
+
* @param keepEditor - Leave the stored `editor` alone (`preserveEditor`, #1457)
|
|
865
|
+
* @returns The frontmatter to hand the provider
|
|
866
|
+
*/
|
|
867
|
+
private normalizeSaveMetadata;
|
|
868
|
+
/**
|
|
869
|
+
* Save a page — the one door every write goes through (#1462 slice 2).
|
|
870
|
+
*
|
|
871
|
+
* Creates a page or updates one, and owns everything a write must not be
|
|
872
|
+
* able to forget: content validation (#1037), the ACL-markup refusal,
|
|
873
|
+
* author preservation and the editor of record (#1354), agent provenance
|
|
874
|
+
* (#946), former titles (#1105), the privacy and vocabulary rules (#639,
|
|
875
|
+
* #893, #915), metadata sanitisation (#296), the uniqueness check (#510),
|
|
876
|
+
* the shared indexes (#1462) and the audit event (#1121).
|
|
877
|
+
*
|
|
878
|
+
* `ctx` is mandatory and positional: the save acts as the caller's subject
|
|
879
|
+
* (#1179), a private-store write reaches that session's keys through it
|
|
880
|
+
* (#1382), and the audit record names it. A caller forwards the context it
|
|
881
|
+
* was given — a request's subject, or the job's for boot and scheduled
|
|
882
|
+
* work — and never invents one.
|
|
883
|
+
*
|
|
884
|
+
* @param pageName - The page to write: its title, or a private path (#1456)
|
|
885
|
+
* @param content - The body, written exactly as given — a save never rewrites it (#1332)
|
|
886
|
+
* @param metadata - Frontmatter; server-owned fields in it are discarded, not merged
|
|
887
|
+
* @param ctx - Who is acting (#1179)
|
|
888
|
+
* @param options - Validation and conflict-check opt-outs, `rawFrontmatter` (#689),
|
|
889
|
+
* `preserveLastModified`, `preserveEditor` (#1457), audit enrichment
|
|
890
|
+
* @returns Where the page landed, and what it was called before
|
|
891
|
+
*
|
|
892
|
+
* @example
|
|
893
|
+
* await pageManager.savePage('New Page', '# Hello World', { 'user-keywords': ['tutorial'] }, req.userContext);
|
|
894
|
+
*/
|
|
895
|
+
savePage(pageName: string, content: string, metadata: Partial<PageFrontmatter> | undefined, ctx: ActorContext, options?: PageSaveOptions): Promise<PageSaveResult>;
|
|
896
|
+
/**
|
|
897
|
+
* Put an old version's body back, as a new version (#1462 slice 3).
|
|
898
|
+
*
|
|
899
|
+
* A restore is a save: it writes the page. It used to call the provider's
|
|
900
|
+
* own `restoreVersion`, which saved underneath the door — no content
|
|
901
|
+
* validation, no uniqueness check, no audit record and no shared-index
|
|
902
|
+
* work, so a restored page kept the RESTORED body on disk while search, the
|
|
903
|
+
* link graph and the rendered cache still described the one it replaced.
|
|
904
|
+
* Here the version is read and the body goes back through `savePage`.
|
|
905
|
+
*
|
|
906
|
+
* Nothing is destroyed: the newer versions stay in the history, and the
|
|
907
|
+
* restore is simply the newest one.
|
|
908
|
+
*
|
|
909
|
+
* @param identifier - Page UUID, title or slug
|
|
910
|
+
* @param version - The version whose body to restore
|
|
911
|
+
* @param ctx - Who is acting (#1179); the restore is attributed to them
|
|
912
|
+
* @returns Where the page landed, and the version number this created
|
|
913
|
+
* @throws When the page, the version or the provider's history is missing
|
|
914
|
+
*/
|
|
915
|
+
restoreVersion(identifier: string, version: number, ctx: ActorContext): Promise<PageSaveResult & {
|
|
916
|
+
version: number | null;
|
|
917
|
+
}>;
|
|
918
|
+
/**
|
|
919
|
+
* Bring a page back from the trash (#947), and back into the shared
|
|
920
|
+
* indexes (#1462 slice 3).
|
|
921
|
+
*
|
|
922
|
+
* The provider moves the file and its history back; a restore is then a
|
|
923
|
+
* save-shaped change — the page is there again, under its restored title —
|
|
924
|
+
* so the link graph, search, mentions, assets and the rendered cache are
|
|
925
|
+
* brought in step here, where every other write has them done. The route
|
|
926
|
+
* used to do a partial job of this itself (search and the link graph only).
|
|
927
|
+
*
|
|
928
|
+
* @param uuid - The trashed page's UUID
|
|
929
|
+
* @param ctx - Who is acting (#1179); the restored page is read as them
|
|
930
|
+
* @returns The provider's result, unchanged — `ok`, or why not
|
|
931
|
+
*/
|
|
932
|
+
restoreDeletedPage(uuid: string, ctx: ActorContext): Promise<{
|
|
933
|
+
ok: true;
|
|
934
|
+
title: string;
|
|
935
|
+
} | {
|
|
936
|
+
ok: false;
|
|
937
|
+
reason: string;
|
|
938
|
+
detail?: string;
|
|
939
|
+
}>;
|
|
940
|
+
/**
|
|
941
|
+
* The requester's own deleted private pages, newest first (#1459).
|
|
942
|
+
*
|
|
943
|
+
* Only their own container, and only the stores this context can open: a
|
|
944
|
+
* sealed store that is still locked contributes nothing.
|
|
945
|
+
*
|
|
946
|
+
* @param ctx - Who is asking (#1179) — never rebuilt, never defaulted
|
|
947
|
+
*/
|
|
948
|
+
/**
|
|
949
|
+
* A takeout of one of the requester's OWN private stores (#1387).
|
|
950
|
+
*
|
|
951
|
+
* The door is here rather than in a route because a route should not be
|
|
952
|
+
* working out where a store lives on disk. What comes back is decrypted and
|
|
953
|
+
* in memory, ready to be packed and streamed; nothing is written anywhere.
|
|
954
|
+
*
|
|
955
|
+
* Only the requester's own store: `mayActInPrivateContainer` decides, as
|
|
956
|
+
* everywhere else, on ownership or delegation and never on a role. A store
|
|
957
|
+
* that is encrypted and locked in this session THROWS rather than returning
|
|
958
|
+
* an empty takeout — an archive that downloads cleanly and cannot be read is
|
|
959
|
+
* the worst outcome available, since it is discovered long afterwards.
|
|
960
|
+
*/
|
|
961
|
+
buildOwnStoreTakeout(ctx: ActorContext, options: {
|
|
962
|
+
store: string;
|
|
963
|
+
pagesOnly?: boolean;
|
|
964
|
+
}): Promise<Takeout>;
|
|
965
|
+
/**
|
|
966
|
+
* The current pages of one vault, as `ctx` may enter it (#1388): its owner,
|
|
967
|
+
* or a share link the owner issued that names this vault. Anyone else gets
|
|
968
|
+
* an empty list, the same as a vault with nothing in it. Whether each page
|
|
969
|
+
* may then be read is still the page door's answer.
|
|
970
|
+
*/
|
|
971
|
+
listVaultPages(ctx: ActorContext, owner: string, vault: string): Promise<Array<{
|
|
972
|
+
name: string;
|
|
973
|
+
title: string;
|
|
974
|
+
uuid: string;
|
|
975
|
+
}>>;
|
|
976
|
+
/** The store ids the requester has of their own (#1387). */
|
|
977
|
+
listOwnStoreIds(ctx: ActorContext): Promise<string[]>;
|
|
978
|
+
listOwnDeletedPrivatePages(ctx: ActorContext): Promise<StoreDeletedEntry[]>;
|
|
979
|
+
/**
|
|
980
|
+
* Bring one of the requester's private pages back from its store's trash
|
|
981
|
+
* (#1459).
|
|
982
|
+
*
|
|
983
|
+
* The provider moves the file back and restores the store's page-index
|
|
984
|
+
* entry; the store's search index is rebuilt for that page here, through the
|
|
985
|
+
* same reconciliation every save and restore runs, so no index is left
|
|
986
|
+
* behind. The page's version history never moved, so it is simply there again.
|
|
987
|
+
*
|
|
988
|
+
* @param ctx - Who is restoring (#1179)
|
|
989
|
+
* @param store - Which of the requester's stores
|
|
990
|
+
* @param uuid - The trashed page's uuid
|
|
991
|
+
*/
|
|
992
|
+
restoreOwnPrivatePage(ctx: ActorContext, store: string, uuid: string): Promise<StoreRestoreResult>;
|
|
993
|
+
/**
|
|
994
|
+
* Destroy one of the requester's trashed private pages for good (#1459):
|
|
995
|
+
* the file, its version history in the store, and the tombstone.
|
|
996
|
+
*
|
|
997
|
+
* @param ctx - Who is purging (#1179)
|
|
998
|
+
* @param store - Which of the requester's stores
|
|
999
|
+
* @param uuid - The trashed page's uuid
|
|
1000
|
+
*/
|
|
1001
|
+
purgeOwnPrivatePage(ctx: ActorContext, store: string, uuid: string): Promise<boolean>;
|
|
1002
|
+
/**
|
|
1003
|
+
* Expire the requester's own private tombstones past the retention window
|
|
1004
|
+
* (#1459), in their session.
|
|
1005
|
+
*
|
|
1006
|
+
* THE SPLIT, stated where it happens: the boot and hourly retention passes
|
|
1007
|
+
* run under a job context, which holds no store key, so they reach the
|
|
1008
|
+
* unencrypted stores only. A SEALED store can be opened by nobody but its
|
|
1009
|
+
* owner, so its trash is expired here, right after an unlock — the same
|
|
1010
|
+
* division as #1457's link migration and #1458's search-index build, and for
|
|
1011
|
+
* the same reason.
|
|
1012
|
+
*
|
|
1013
|
+
* Best-effort: a failure is logged and never blocks the unlock.
|
|
1014
|
+
*
|
|
1015
|
+
* @param ctx - The owner's context, holding the unlocked store keys
|
|
1016
|
+
*/
|
|
1017
|
+
purgeExpiredOwnPrivateTrash(ctx: ActorContext): Promise<number>;
|
|
1018
|
+
/**
|
|
1019
|
+
* Convert a stored page (frontmatter + body) to NCM, with every fix step
|
|
1020
|
+
* (#1332) — the one implementation behind Convert to NCM, agent ingest and
|
|
1021
|
+
* the MCP create/update tools.
|
|
1022
|
+
*
|
|
1023
|
+
* The fix steps run on the body first, then `normalizeExistingPageToNcm`
|
|
1024
|
+
* (links, table up-convert, `ncmVersion`), so NCM's determinism stays owned
|
|
1025
|
+
* in one place. Each step that changed something is also reported as a
|
|
1026
|
+
* `converter-note` warning, so the existing preview and notification
|
|
1027
|
+
* surfaces show it without knowing about steps.
|
|
1028
|
+
*
|
|
1029
|
+
* Pure: nothing is saved.
|
|
1030
|
+
*
|
|
1031
|
+
* @param raw - Full page text, YAML frontmatter and body
|
|
1032
|
+
* @returns The NCM result and the fix steps that changed the body
|
|
1033
|
+
*/
|
|
1034
|
+
convertPageToNcm(raw: string): PageConvertResult;
|
|
1035
|
+
/**
|
|
1036
|
+
* The NCM door's second half (#1486): the conversion steps that write.
|
|
1037
|
+
*
|
|
1038
|
+
* `convertPageToNcm` is pure — fix steps, links, tables, `ncmVersion` — so a
|
|
1039
|
+
* caller can validate and check for a stale version before anything is
|
|
1040
|
+
* written. This runs after those checks, and is the one place the funnel's
|
|
1041
|
+
* side effects happen:
|
|
1042
|
+
*
|
|
1043
|
+
* - remote images become attachments of the page (AttachmentManager), when
|
|
1044
|
+
* `localizeImages` is set — interactive Convert to NCM only, today; the
|
|
1045
|
+
* agent and import paths leave remote images as links;
|
|
1046
|
+
* - `[^id]: text` definitions move to the page's footnote list
|
|
1047
|
+
* (FootnoteManager), when the page has a uuid and footnotes are enabled.
|
|
1048
|
+
*
|
|
1049
|
+
* Import, ingest, MCP and Convert to NCM all come through here, so they
|
|
1050
|
+
* cannot drift apart again. With `dryRun` nothing is stored: the preview
|
|
1051
|
+
* shows what apply will do.
|
|
1052
|
+
*
|
|
1053
|
+
* @param content - The converted page text (with or without frontmatter)
|
|
1054
|
+
* @param target - The page it belongs to: its name, and its uuid when known
|
|
1055
|
+
* @param ctx - Who is acting; attachments and footnotes are theirs
|
|
1056
|
+
* @param options - `dryRun`, `localizeImages`
|
|
1057
|
+
* @returns The text to save, and a warning per thing done or refused
|
|
1058
|
+
*/
|
|
1059
|
+
completeNcmConversion(content: string, target: {
|
|
1060
|
+
pageName: string;
|
|
1061
|
+
uuid?: string;
|
|
1062
|
+
}, ctx: ActorContext, options?: {
|
|
1063
|
+
dryRun?: boolean;
|
|
1064
|
+
localizeImages?: boolean;
|
|
1065
|
+
}): Promise<{
|
|
1066
|
+
content: string;
|
|
1067
|
+
warnings: string[];
|
|
1068
|
+
}>;
|
|
1069
|
+
/**
|
|
1070
|
+
* Run the Markdown fix steps over a page body (#1332).
|
|
1071
|
+
*
|
|
1072
|
+
* The one entry point for rewriting page text, so Convert to NCM, ingest,
|
|
1073
|
+
* import and migrations cannot disagree about what a page should become.
|
|
1074
|
+
* Every step runs, in registry order; `steps` picks steps by id instead.
|
|
1075
|
+
* An ordinary save never calls this.
|
|
1076
|
+
*
|
|
1077
|
+
* Pure: nothing is saved. The result lists what each step changed, so the
|
|
1078
|
+
* caller can report it.
|
|
1079
|
+
*
|
|
1080
|
+
* @param content - Page body, without frontmatter
|
|
1081
|
+
* @param options - `steps` by id; default every step
|
|
1082
|
+
* @returns The fixed body and the changes, empty when nothing changed
|
|
1083
|
+
*
|
|
1084
|
+
* @example
|
|
1085
|
+
* const { content, changes } = pageManager.normalizePageContent(body);
|
|
1086
|
+
*/
|
|
1087
|
+
normalizePageContent(content: string, options?: RunFixesOptions): FixResult;
|
|
1088
|
+
/**
|
|
1089
|
+
* Delete a page — the one door every delete goes through (#1462 slice 3).
|
|
1090
|
+
*
|
|
1091
|
+
* There were two: this one and `deletePageWithContext`, which took a whole
|
|
1092
|
+
* WikiContext to read two fields off it and, when the context carried no
|
|
1093
|
+
* user, deleted as the anonymous subject — a caller that HAD a subject could
|
|
1094
|
+
* lose it on the way in. The context is mandatory and positional here, as it
|
|
1095
|
+
* is for a save.
|
|
1096
|
+
*
|
|
1097
|
+
* The delete is soft (#947): the page goes to the trash with its history,
|
|
1098
|
+
* and the context names who put it there. Afterwards the page is taken out
|
|
1099
|
+
* of every shared index, here and nowhere else (#1462).
|
|
1100
|
+
*
|
|
1101
|
+
* @param identifier - Page UUID, title, or slug
|
|
1102
|
+
* @param ctx - Who is acting (#1179)
|
|
1103
|
+
* @returns True if deleted, false if not found
|
|
1104
|
+
*
|
|
1105
|
+
* @example
|
|
1106
|
+
* const deleted = await pageManager.deletePage('Old Page', req.userContext);
|
|
1107
|
+
*/
|
|
1108
|
+
deletePage(identifier: string, ctx: ActorContext): Promise<boolean>;
|
|
1109
|
+
/**
|
|
1110
|
+
* The name a page was reached by, as the shared indexes key it: a private
|
|
1111
|
+
* page's path (#1456), else its title — the caller may have used a uuid or
|
|
1112
|
+
* slug.
|
|
1113
|
+
*/
|
|
1114
|
+
private nameOf;
|
|
1115
|
+
/**
|
|
1116
|
+
* The shared indexes after a page changed (#1462) — the one place they are
|
|
1117
|
+
* kept in step: the link graph, search, attachment mentions, page assets,
|
|
1118
|
+
* and the rendered-page cache of the page and of the pages linking to it.
|
|
1119
|
+
* A private page (#1456) is in none of them; a page that left the public
|
|
1120
|
+
* space, or was deleted or renamed, is taken out under its old name.
|
|
1121
|
+
*
|
|
1122
|
+
* A failure here is logged, not thrown: the page is already saved, and the
|
|
1123
|
+
* indexes are rebuilt by the reindex job. It is never silent.
|
|
1124
|
+
*
|
|
1125
|
+
* @returns the pages that linked to the old name, read before it left the
|
|
1126
|
+
* link graph — what a rename rewrites (#1094)
|
|
1127
|
+
*/
|
|
1128
|
+
private reconcileSharedIndexes;
|
|
1129
|
+
/** A private page's store's search index, after the page changed (#1458). */
|
|
1130
|
+
private reconcileStoreSearch;
|
|
1131
|
+
/**
|
|
1132
|
+
* The requester's own private pages that match, from their stores' own
|
|
1133
|
+
* search indexes (#1458).
|
|
1134
|
+
*
|
|
1135
|
+
* Only the requester's own container is read: `mayActInPrivateContainer` is
|
|
1136
|
+
* the same rule the page gate applies, so no role reaches in and an admin
|
|
1137
|
+
* searching finds nothing of anyone else's. A store whose key this context
|
|
1138
|
+
* does not hold contributes nothing — a locked store simply yields no
|
|
1139
|
+
* matches, never a plaintext read.
|
|
1140
|
+
*
|
|
1141
|
+
* Each match carries the page's private NAME (`private/{owner}/{store}/{title}`),
|
|
1142
|
+
* so `pageUrl` builds a link that works.
|
|
1143
|
+
*
|
|
1144
|
+
* @param ctx - Who is searching (#1179) — never rebuilt, never defaulted
|
|
1145
|
+
* @param ask - The query, as {@link matchStoreSearch} reads it
|
|
1146
|
+
*/
|
|
1147
|
+
searchOwnPrivatePages(ctx: ActorContext, ask?: StoreSearchQuery): Promise<Array<StoreSearchMatch & {
|
|
1148
|
+
name: string;
|
|
1149
|
+
owner: string;
|
|
1150
|
+
store: string;
|
|
1151
|
+
}>>;
|
|
1152
|
+
/**
|
|
1153
|
+
* Build a store's search index from its pages, replacing whatever is there.
|
|
1154
|
+
*
|
|
1155
|
+
* The repair for a lost or stale index, and what a store with none gets at
|
|
1156
|
+
* its first boot or unlock. The pages are read through this door with `ctx`,
|
|
1157
|
+
* so a sealed store is read with the owner's key and nothing reads a store
|
|
1158
|
+
* file from outside the provider.
|
|
1159
|
+
*
|
|
1160
|
+
* @param ctx - Whose reading and writing this is (#1179)
|
|
1161
|
+
* @param owner - Whose store
|
|
1162
|
+
* @param store - Which store
|
|
1163
|
+
* @returns How many documents the index now holds
|
|
1164
|
+
*/
|
|
1165
|
+
rebuildStoreSearchIndex(ctx: ActorContext, owner: string, store: string): Promise<number>;
|
|
1166
|
+
/**
|
|
1167
|
+
* Give every readable store that has no search index one, built from its
|
|
1168
|
+
* pages (#1458).
|
|
1169
|
+
*
|
|
1170
|
+
* A store with an index is left alone, so this is cheap at every boot and
|
|
1171
|
+
* every unlock; a store whose index was lost gets it back. That the file is
|
|
1172
|
+
* missing IS the marker — unlike the link migration (#1457), which had to
|
|
1173
|
+
* record that it ran because its result is indistinguishable from never
|
|
1174
|
+
* having run, an absent index says plainly that one is needed.
|
|
1175
|
+
*
|
|
1176
|
+
* Only the stores `ctx` can open are visited: the system principal reaches
|
|
1177
|
+
* the unencrypted ones at boot, the owner's own session reaches their sealed
|
|
1178
|
+
* ones at unlock. One store's failure never stops the others.
|
|
1179
|
+
*
|
|
1180
|
+
* @param ctx - Whose reading and writing this is (#1179)
|
|
1181
|
+
* @param owner - One user's stores, or every readable store when omitted
|
|
1182
|
+
* @returns How many stores were given an index
|
|
1183
|
+
*/
|
|
1184
|
+
buildMissingStoreSearchIndexes(ctx: ActorContext, owner?: string): Promise<number>;
|
|
1185
|
+
/**
|
|
1186
|
+
* Build the missing search indexes of the unencrypted stores, at boot.
|
|
1187
|
+
*
|
|
1188
|
+
* Best-effort: a failure is logged and never blocks start-up. An encrypted
|
|
1189
|
+
* store is not readable here — it gets its index at its owner's unlock, the
|
|
1190
|
+
* same split the link migration uses (#1457).
|
|
1191
|
+
*/
|
|
1192
|
+
buildStoreSearchIndexesAtBoot(): Promise<void>;
|
|
1193
|
+
/**
|
|
1194
|
+
* Whether a page may go into an index that every reader of this process or
|
|
1195
|
+
* its disk shares — the search index, the page-assets index, the link graph
|
|
1196
|
+
* and the known-page-name list (#1419).
|
|
1197
|
+
*
|
|
1198
|
+
* Such an index is built as the anonymous subject (docs/planning/
|
|
1199
|
+
* private-stores.md, Context): the question is asked with that subject
|
|
1200
|
+
* stated, not with a default. A private page never qualifies (#1456): it
|
|
1201
|
+
* is listed only in its own store's indexes, encrypted or not.
|
|
1202
|
+
*/
|
|
1203
|
+
isSharedIndexable(identifier: string): boolean;
|
|
1204
|
+
/**
|
|
1205
|
+
* Check if page exists
|
|
1206
|
+
*
|
|
1207
|
+
* Fast existence check without loading page content.
|
|
1208
|
+
*
|
|
1209
|
+
* @param {string} identifier - Page UUID, title, or slug
|
|
1210
|
+
* @returns {boolean} True if page exists
|
|
1211
|
+
*
|
|
1212
|
+
* @example
|
|
1213
|
+
* if (pageManager.pageExists('Main')) {
|
|
1214
|
+
* console.log('Main page exists');
|
|
1215
|
+
* }
|
|
1216
|
+
*/
|
|
1217
|
+
pageExists(identifier: string, ctx: ActorContext): boolean;
|
|
1218
|
+
/**
|
|
1219
|
+
* The private titles `ctx` can see in `owner`'s stores, by store id (#1457).
|
|
1220
|
+
*
|
|
1221
|
+
* For the one question a `[store/Title]` link asks while a PRIVATE page is
|
|
1222
|
+
* rendered: is that page there? A private page's HTML is never cached for
|
|
1223
|
+
* sharing (`isSharedIndexable` is false for every private name), so its
|
|
1224
|
+
* links may be answered for the reader in front of them.
|
|
1225
|
+
*
|
|
1226
|
+
* A store `ctx` cannot open is absent from the map, not empty: the caller
|
|
1227
|
+
* must render "cannot say" as it renders for everyone, never as a missing
|
|
1228
|
+
* page. Titles are folded to lower case, which is how a store index matches
|
|
1229
|
+
* them, so a caller compares folded.
|
|
1230
|
+
*
|
|
1231
|
+
* @param owner - Whose stores
|
|
1232
|
+
* @param ctx - Whose reading this is (#1179) — never rebuilt, never defaulted
|
|
1233
|
+
*/
|
|
1234
|
+
readablePrivateTitles(owner: string, ctx: ActorContext): Promise<Map<string, Set<string>>>;
|
|
1235
|
+
/**
|
|
1236
|
+
* Whether a page uuid is in the trash (#1403).
|
|
1237
|
+
*
|
|
1238
|
+
* A seed of shipped pages must treat a trashed uuid as deliberately removed,
|
|
1239
|
+
* not missing. `pageExists` sees live pages only. A provider without soft
|
|
1240
|
+
* delete has no trash, so this is false there.
|
|
1241
|
+
*
|
|
1242
|
+
* @param uuid - Page UUID
|
|
1243
|
+
* @param _ctx - Who is asking
|
|
1244
|
+
* @returns True if the uuid has a trash entry
|
|
1245
|
+
*/
|
|
1246
|
+
isPageDeleted(uuid: string, _ctx: ActorContext): boolean;
|
|
1247
|
+
/**
|
|
1248
|
+
* Get all page titles
|
|
1249
|
+
*
|
|
1250
|
+
* Returns a sorted list of all page titles in the wiki.
|
|
1251
|
+
*
|
|
1252
|
+
* @async
|
|
1253
|
+
* @returns {Promise<string[]>} Sorted array of page titles
|
|
1254
|
+
*
|
|
1255
|
+
* @example
|
|
1256
|
+
* const pages = await pageManager.getAllPages();
|
|
1257
|
+
* console.log('Total pages:', pages.length);
|
|
1258
|
+
*/
|
|
1259
|
+
getAllPages(): Promise<string[]>;
|
|
1260
|
+
/**
|
|
1261
|
+
* The pages `subject` may perform `action` on — the door for anything that
|
|
1262
|
+
* lists pages to a reader (#1219, guiding-framework rule 10).
|
|
1263
|
+
*
|
|
1264
|
+
* A title is content: a private page named for what it holds discloses by
|
|
1265
|
+
* appearing in a list. `getAllPages()` above is the unfiltered index and
|
|
1266
|
+
* stays for callers with no reader — indexing, link graphs, jobs, and admin
|
|
1267
|
+
* surfaces already gated by `admin-system`. Anything rendered to a request
|
|
1268
|
+
* goes through here, so the listing and `canAccess` are the same evaluator
|
|
1269
|
+
* (`PolicyInformationPoint.filterAccessiblePages`). Without an PolicyInformationPoint nothing is
|
|
1270
|
+
* listed — never the unfiltered index by accident.
|
|
1271
|
+
*
|
|
1272
|
+
* @param subject - The reader's own context, forwarded (viaToken / viaShare ride on it)
|
|
1273
|
+
* @param action - Legacy verb (`view`, `edit`) or policy action; default `view`
|
|
1274
|
+
*/
|
|
1275
|
+
listPagesFor(subject: PermissionSubject | null | undefined, action?: string): Promise<string[]>;
|
|
1276
|
+
/**
|
|
1277
|
+
* Get all page titles (explicit alias for getAllPages)
|
|
1278
|
+
* Prefer this for new code that only needs page names.
|
|
1279
|
+
* Use getAllPageInfo() when you need uuid/slug/author etc.
|
|
1280
|
+
*/
|
|
1281
|
+
/**
|
|
1282
|
+
* #1006: how many pages this instance holds.
|
|
1283
|
+
*
|
|
1284
|
+
* Names, not pages: `getAllPageNames()` reads the index, where
|
|
1285
|
+
* `getAllPages()` would load every page's content to count them.
|
|
1286
|
+
*/
|
|
1287
|
+
getManagerStats(): Promise<ManagerStats>;
|
|
1288
|
+
getAllPageNames(): Promise<string[]>;
|
|
1289
|
+
/**
|
|
1290
|
+
* Get a page by its UUID
|
|
1291
|
+
* @param {string} uuid - Page UUID
|
|
1292
|
+
* @returns {Promise<WikiPage | null>} Page or null if not found
|
|
1293
|
+
*/
|
|
1294
|
+
getPageByUUID(uuid: string, ctx: ActorContext): Promise<WikiPage | null>;
|
|
1295
|
+
/**
|
|
1296
|
+
* Get a page by its slug
|
|
1297
|
+
* @param {string} slug - URL-friendly slug
|
|
1298
|
+
* @returns {Promise<WikiPage | null>} Page or null if not found
|
|
1299
|
+
*/
|
|
1300
|
+
getPageBySlug(slug: string, ctx: ActorContext): Promise<WikiPage | null>;
|
|
1301
|
+
/**
|
|
1302
|
+
* Refresh internal cache/index
|
|
1303
|
+
*
|
|
1304
|
+
* Forces the provider to rebuild its internal caches and indices.
|
|
1305
|
+
* Useful after external file system changes.
|
|
1306
|
+
*
|
|
1307
|
+
* @async
|
|
1308
|
+
* @returns {Promise<void>}
|
|
1309
|
+
*
|
|
1310
|
+
* @example
|
|
1311
|
+
* await pageManager.refreshPageList();
|
|
1312
|
+
* console.log('Page list refreshed');
|
|
1313
|
+
*/
|
|
1314
|
+
/**
|
|
1315
|
+
* Return pages most recently modified, sorted descending by lastModified (#635).
|
|
1316
|
+
*
|
|
1317
|
+
* Delegates to the provider's in-memory state (pageIndex / pageCache) — no
|
|
1318
|
+
* direct disk I/O. Honors private-page visibility based on `options.principals`;
|
|
1319
|
+
* the provider derives the admin bypass from an `admin` principal (#1116).
|
|
1320
|
+
*
|
|
1321
|
+
* Used by RecentChangesPlugin and any other consumer that needs a "recent edits"
|
|
1322
|
+
* feed. New code should prefer this over enumerating getAllPages().
|
|
1323
|
+
*/
|
|
1324
|
+
getRecentChanges(ctx: ActorContext, options?: RecentChangesOptions): Promise<RecentChangeEntry[]>;
|
|
1325
|
+
/**
|
|
1326
|
+
* Every indexed page carrying an `addon` provenance stamp.
|
|
1327
|
+
* Used by addon orphan detection; reads the in-memory index, no disk I/O.
|
|
1328
|
+
* Returns [] on providers that do not maintain a page index.
|
|
1329
|
+
*/
|
|
1330
|
+
getAddonSeededIndexEntries(): Array<{
|
|
1331
|
+
uuid: string;
|
|
1332
|
+
addon: string;
|
|
1333
|
+
slug?: string;
|
|
1334
|
+
title: string;
|
|
1335
|
+
}>;
|
|
1336
|
+
/**
|
|
1337
|
+
* Set the `addon` stamp on an existing index entry without rewriting the page.
|
|
1338
|
+
* Used by the boot-time back-fill. Returns false when unsupported or unchanged.
|
|
1339
|
+
*/
|
|
1340
|
+
setIndexAddon(uuid: string, addonName: string): Promise<boolean>;
|
|
1341
|
+
/**
|
|
1342
|
+
* Pages owned by a given user (#640).
|
|
1343
|
+
*
|
|
1344
|
+
* Authorization: callers MUST verify the requesting user is allowed to ask
|
|
1345
|
+
* about `username` (typically only their own username, or admin asking about
|
|
1346
|
+
* another). The provider does not enforce this — it filters by `author` /
|
|
1347
|
+
* `creator` only.
|
|
1348
|
+
*/
|
|
1349
|
+
getPagesByCreator(username: string, ctx: ActorContext, options?: GetPagesByCreatorOptions): Promise<RecentChangeEntry[]>;
|
|
1350
|
+
/**
|
|
1351
|
+
* Pages most recently edited by a user (#640 Phase 2).
|
|
1352
|
+
*/
|
|
1353
|
+
getPagesByEditor(username: string, ctx: ActorContext, options?: PagesScanOptions): Promise<RecentChangeEntry[]>;
|
|
1354
|
+
/**
|
|
1355
|
+
* Pages whose frontmatter audience matches any of the caller's principals,
|
|
1356
|
+
* excluding pages the caller already owns (#640 Phase 2).
|
|
1357
|
+
*/
|
|
1358
|
+
getPagesSharedWith(principals: string[], options?: PagesScanOptions): Promise<RecentChangeEntry[]>;
|
|
1359
|
+
/**
|
|
1360
|
+
* The private-container decision for a page (docs/private-stores.md,
|
|
1361
|
+
* Access). PolicyInformationPoint's Tier 0 asks this, so every `canAccess` on a page —
|
|
1362
|
+
* view, edit, lists, the attachment door — reaches the same rule.
|
|
1363
|
+
*
|
|
1364
|
+
* `pages/vaults/{user}/` and every store below it is owned by that user.
|
|
1365
|
+
* Nobody else acts in it unless the owner delegated (a share the owner
|
|
1366
|
+
* issued, once the store's Share switch exists — #1388). No role reaches in,
|
|
1367
|
+
* admin included (security-posture P2: no `hasRole` as an allow).
|
|
1368
|
+
*
|
|
1369
|
+
* The owner is the one in the page's name (#1456), not frontmatter
|
|
1370
|
+
* `author`, so reassigning `author` cannot move ownership (#711).
|
|
1371
|
+
*
|
|
1372
|
+
* Returns:
|
|
1373
|
+
* - `null` — a public name; the caller falls through to its next tier
|
|
1374
|
+
* - `true` — a private name, and the caller is the owner or the owner's delegate
|
|
1375
|
+
* - `false` — a private name, and the caller is neither — whether or not
|
|
1376
|
+
* the page exists
|
|
1377
|
+
*/
|
|
1378
|
+
checkPrivatePageAccess(wikiContext: WikiContext, pageNameOrUuid: string): Promise<boolean | null>;
|
|
1379
|
+
/**
|
|
1380
|
+
* Owner and store of a private page, or `null` when the page is not private
|
|
1381
|
+
* or does not exist (#1398). The owner owns the page and every attachment
|
|
1382
|
+
* uploaded onto it, so AttachmentManager uses this to route a new upload into
|
|
1383
|
+
* that owner's store.
|
|
1384
|
+
*
|
|
1385
|
+
* #1456: both come from the page's name, `private/{owner}/{store}/{title}`;
|
|
1386
|
+
* the page is looked up in that store through `ctx` only to confirm it exists.
|
|
1387
|
+
*/
|
|
1388
|
+
getPrivatePageOwner(pageNameOrUuid: string, ctx: ActorContext): Promise<{
|
|
1389
|
+
creator: string;
|
|
1390
|
+
store: string;
|
|
1391
|
+
} | null>;
|
|
1392
|
+
refreshPageList(): Promise<void>;
|
|
1393
|
+
/**
|
|
1394
|
+
* #1374: rewrite the provider's persistent page index from its last disk scan
|
|
1395
|
+
* (run {@link refreshPageList} first). Null when the provider keeps no such
|
|
1396
|
+
* index.
|
|
1397
|
+
*/
|
|
1398
|
+
rebuildPageIndex(): Promise<{
|
|
1399
|
+
pages: number;
|
|
1400
|
+
changed: number;
|
|
1401
|
+
removed: string[];
|
|
1402
|
+
keptUnscanned: number;
|
|
1403
|
+
historyInRequiredPages: number;
|
|
1404
|
+
} | null>;
|
|
1405
|
+
/**
|
|
1406
|
+
* At unlock (#1456): the owner's sealed pages move from their user-level
|
|
1407
|
+
* catalog into each encrypted store's own page index. Idempotent.
|
|
1408
|
+
*
|
|
1409
|
+
* @param ctx - The owner's context, holding the unlocked store keys
|
|
1410
|
+
*/
|
|
1411
|
+
adoptUserPageCatalog(ctx: ActorContext): Promise<number>;
|
|
1412
|
+
/**
|
|
1413
|
+
* Run the link migrations at boot: over the unencrypted stores (#1457,
|
|
1414
|
+
* #1506), then, once per site, `private/…` → `vaults/…` in the public pages
|
|
1415
|
+
* (#1506).
|
|
1416
|
+
*
|
|
1417
|
+
* Best-effort: a failure is logged and never blocks start-up. An encrypted
|
|
1418
|
+
* store is not readable here and is not touched — it migrates at its
|
|
1419
|
+
* owner's unlock, through {@link migratePrivateLinks}.
|
|
1420
|
+
*/
|
|
1421
|
+
migratePrivateLinksAtBoot(): Promise<void>;
|
|
1422
|
+
/**
|
|
1423
|
+
* `private/…` → `vaults/…` in every public page, once per site (#1506). The
|
|
1424
|
+
* site records it in `site-migrations.json` in the instance data folder,
|
|
1425
|
+
* beside `seeded-shipped-pages.json`; a second run would write nothing
|
|
1426
|
+
* anyway ({@link rewriteLegacyVaultUrls}).
|
|
1427
|
+
*
|
|
1428
|
+
* @returns How many pages were rewritten
|
|
1429
|
+
*/
|
|
1430
|
+
migrateVaultUrlsInPublicPages(ctx: ActorContext): Promise<number>;
|
|
1431
|
+
/** Every vault on disk, as `owner/store` — encrypted ones included, since only their folder names are read. */
|
|
1432
|
+
private listVaults;
|
|
1433
|
+
/**
|
|
1434
|
+
* Rewrite `[Title]` to `[store/Title]` in the private pages that mean it.
|
|
1435
|
+
*
|
|
1436
|
+
* Only a target naming a page in the SAME store is rewritten — never a
|
|
1437
|
+
* public title, never another store — and a target that already parses as a
|
|
1438
|
+
* private link is left alone, so a second run writes nothing
|
|
1439
|
+
* ({@link rewriteToPrivateLinks}).
|
|
1440
|
+
*
|
|
1441
|
+
* Each page goes through {@link savePage}, so it is versioned, reindexed and
|
|
1442
|
+
* audited as `link-rewrite`. It keeps its `lastModified` (operator,
|
|
1443
|
+
* 2026-09-23: a syntax rewrite must not move a page to the top of Recent
|
|
1444
|
+
* Changes) and its `editor`: nobody edited it.
|
|
1445
|
+
*
|
|
1446
|
+
* A store that has been migrated records it beside its own indexes and is
|
|
1447
|
+
* skipped from then on. One store's failure never stops the others.
|
|
1448
|
+
*
|
|
1449
|
+
* @param ctx - Whose reading and writing this is (#1179). Only the stores
|
|
1450
|
+
* this context can open are visited: the system principal reaches the
|
|
1451
|
+
* unencrypted ones, an owner's own unlocked session reaches theirs.
|
|
1452
|
+
* @param owner - One user's stores, or every readable store when omitted
|
|
1453
|
+
* @returns How many pages were rewritten
|
|
1454
|
+
*/
|
|
1455
|
+
migratePrivateLinks(ctx: ActorContext, owner?: string): Promise<number>;
|
|
1456
|
+
/**
|
|
1457
|
+
* One page's body rewritten by `rewrite`, saved through the door. Returns 1
|
|
1458
|
+
* when it changed, 0 when it did not or could not be read or saved.
|
|
1459
|
+
*/
|
|
1460
|
+
private rewritePage;
|
|
1461
|
+
/**
|
|
1462
|
+
* Move a private page into another of its owner's vaults (#1505), under the
|
|
1463
|
+
* same title. The page, its history and its index entries go with it; it
|
|
1464
|
+
* keeps its `lastModified` and `editor`, since nobody edited it. The audit
|
|
1465
|
+
* record is a rename, from the old path to the new.
|
|
1466
|
+
*
|
|
1467
|
+
* Access is not decided here, as for every save: the caller has already
|
|
1468
|
+
* been let into the page. A page is never moved out of an encrypted vault
|
|
1469
|
+
* into one that is not, since that would write it in the clear.
|
|
1470
|
+
*
|
|
1471
|
+
* @param pageName - The page's private name, `vaults/{owner}/{vault}/{title}`
|
|
1472
|
+
* @param toVault - The vault to move it to
|
|
1473
|
+
* @param ctx - Who is acting (#1179)
|
|
1474
|
+
* @param changes - Frontmatter to change in the same write, e.g. its `system-category`
|
|
1475
|
+
* @returns Where the page landed
|
|
1476
|
+
*/
|
|
1477
|
+
movePageToVault(pageName: string, toVault: string, ctx: ActorContext, changes?: Partial<PageFrontmatter>): Promise<PageSaveResult>;
|
|
1478
|
+
/**
|
|
1479
|
+
* Move the captures made before #1505 into the capture vault, with
|
|
1480
|
+
* `system-category: capture`. Until then a capture was a `general` page
|
|
1481
|
+
* carrying the system keyword `capture`, saved in its author's default
|
|
1482
|
+
* vault. A page whose system-category someone changed is left where it is.
|
|
1483
|
+
*
|
|
1484
|
+
* Runs over the vaults `ctx` can open — at boot, the unencrypted ones — and
|
|
1485
|
+
* each vault records that it is done. A page whose title is already taken
|
|
1486
|
+
* in the capture vault stays where it is, and is still found there: the
|
|
1487
|
+
* capture page looks in the capture vault, then the default one.
|
|
1488
|
+
*
|
|
1489
|
+
* @returns How many pages moved
|
|
1490
|
+
*/
|
|
1491
|
+
moveCapturesToCaptureVault(ctx: ActorContext): Promise<number>;
|
|
1492
|
+
/**
|
|
1493
|
+
* Move private pages made before #1505 into their system-category's own
|
|
1494
|
+
* vault (#1507). Until then every private page went to its owner's default
|
|
1495
|
+
* vault, whatever its system-category — journal entries included. A page in
|
|
1496
|
+
* the default vault whose system-category declares another vault
|
|
1497
|
+
* (`storageLocation.privatestore`) moves there; a page in any other vault
|
|
1498
|
+
* was put there on purpose and is left alone.
|
|
1499
|
+
*
|
|
1500
|
+
* Same rules as {@link moveCapturesToCaptureVault}: the vaults `ctx` can
|
|
1501
|
+
* open, once per vault, and a page whose title is taken where it would go
|
|
1502
|
+
* stays where it is.
|
|
1503
|
+
*
|
|
1504
|
+
* @returns How many pages moved
|
|
1505
|
+
*/
|
|
1506
|
+
movePagesToCategoryVaults(ctx: ActorContext): Promise<number>;
|
|
1507
|
+
/**
|
|
1508
|
+
* Every private slug in the `vaults--…` form (#1507), once per vault. A
|
|
1509
|
+
* private page's slug is made fresh on every save, so each page whose slug
|
|
1510
|
+
* is anything else is saved again, unchanged otherwise: same body,
|
|
1511
|
+
* `lastModified` and `editor`. That is `private--…` from before #1507, and a
|
|
1512
|
+
* plain slug from before #1456, which could clash with a public page's.
|
|
1513
|
+
*
|
|
1514
|
+
* @returns How many pages were re-saved
|
|
1515
|
+
*/
|
|
1516
|
+
migratePrivateSlugs(ctx: ActorContext): Promise<number>;
|
|
1517
|
+
/**
|
|
1518
|
+
* One pass of a vault move: every page in the vaults `ctx` can open is
|
|
1519
|
+
* offered to `pick`, which names the vault it goes to (and any frontmatter
|
|
1520
|
+
* to change with it), or null to leave it.
|
|
1521
|
+
*/
|
|
1522
|
+
private moveVaultPages;
|
|
1523
|
+
/**
|
|
1524
|
+
* Visit every page in the vaults `ctx` can open — at boot, the unencrypted
|
|
1525
|
+
* ones — once per vault: each vault records `migrationId` when visited, so
|
|
1526
|
+
* no later boot reads its pages again. `visit` returns the vault the page
|
|
1527
|
+
* was written to, or null when it left the page alone; one page's or one
|
|
1528
|
+
* vault's failure never stops the rest.
|
|
1529
|
+
*
|
|
1530
|
+
* @returns How many pages were written
|
|
1531
|
+
*/
|
|
1532
|
+
private visitVaultPages;
|
|
1533
|
+
/**
|
|
1534
|
+
* Every vault page the startup checks found with data outside its vault —
|
|
1535
|
+
* files in the shared pool (#1842), footnotes or comments in the public
|
|
1536
|
+
* folders (#1839) — as one list for the repair actions, with whether `ctx`
|
|
1537
|
+
* may repair each one here and, if not, why. Read from the checks' last
|
|
1538
|
+
* findings, so listing opens no vault. Only what `ctx` may see: every item
|
|
1539
|
+
* for someone holding `admin-system` (owner, vault and page uuid, as the
|
|
1540
|
+
* subsystem banner already shows), otherwise the items of their own vaults.
|
|
1541
|
+
*/
|
|
1542
|
+
listVaultRepairs(ctx: ActorContext, opts?: {
|
|
1543
|
+
details?: boolean;
|
|
1544
|
+
}): Promise<VaultRepairItem[]>;
|
|
1545
|
+
/**
|
|
1546
|
+
* The pages of one repair item that `ctx` may read, as the reader sees
|
|
1547
|
+
* them (#1842): each one read as `ctx` through {@link readPage}, the same
|
|
1548
|
+
* door as viewing it, after the unrecorded {@link mayShowPage}, so a refusal
|
|
1549
|
+
* never records a page name. Found through the vault stores `ctx` can open,
|
|
1550
|
+
* so a sealed vault contributes nothing without its owner's key. Its
|
|
1551
|
+
* outside files come from `AttachmentManager.poolFilesOfVaultPage`.
|
|
1552
|
+
* Best-effort: a failure shows no details, and is logged without a title.
|
|
1553
|
+
*/
|
|
1554
|
+
private vaultRepairPages;
|
|
1555
|
+
/**
|
|
1556
|
+
* Move one vault page's leftovers into its vault (#1842): the shared-pool
|
|
1557
|
+
* files it uses (`AttachmentManager.movePoolFilesIntoVault`) and its
|
|
1558
|
+
* footnotes and comments from the public folders
|
|
1559
|
+
* (`FootnoteManager` / `CommentManager.moveOutside…IntoVault`), each by its
|
|
1560
|
+
* own manager through the vault's store I/O — sealed when the vault is. A
|
|
1561
|
+
* pool file another public page uses is copied, never taken from it.
|
|
1562
|
+
*
|
|
1563
|
+
* `page` null is the startup check's "pages named by shared records": every
|
|
1564
|
+
* page of that vault a shared-pool record still names, files only.
|
|
1565
|
+
*
|
|
1566
|
+
* Who may: the vault's owner, in their own session (a sealed vault needs its
|
|
1567
|
+
* key, so it must be open); for a vault that isn't sealed, also someone
|
|
1568
|
+
* holding `admin-system`. A sealed vault asked for by anyone but its owner
|
|
1569
|
+
* is refused, and nothing changes.
|
|
1570
|
+
*
|
|
1571
|
+
* Recorded as `vault-repair` before anything moves (on-failure refuse), with
|
|
1572
|
+
* the counts it is about to move. Then the three checks run again, so the
|
|
1573
|
+
* subsystem banner clears once nothing is left.
|
|
1574
|
+
*/
|
|
1575
|
+
repairVaultPage(target: {
|
|
1576
|
+
owner: string;
|
|
1577
|
+
vault: string;
|
|
1578
|
+
page: string | null;
|
|
1579
|
+
}, ctx: ActorContext, opts?: {
|
|
1580
|
+
recheck?: boolean;
|
|
1581
|
+
}): Promise<VaultRepairResult>;
|
|
1582
|
+
/**
|
|
1583
|
+
* "Repair all I can" (#1842): every listed item `ctx` may repair, one by
|
|
1584
|
+
* one through {@link repairVaultPage}; one item's failure never stops the
|
|
1585
|
+
* rest. The checks run again once, at the end.
|
|
1586
|
+
*
|
|
1587
|
+
* @param opts.ownOnly - Only `ctx`'s own vaults (the profile's "Move all into my vaults")
|
|
1588
|
+
*/
|
|
1589
|
+
repairAllVaultPages(ctx: ActorContext, opts?: {
|
|
1590
|
+
ownOnly?: boolean;
|
|
1591
|
+
}): Promise<{
|
|
1592
|
+
repaired: number;
|
|
1593
|
+
refused: number;
|
|
1594
|
+
failed: number;
|
|
1595
|
+
} & VaultRepairCounts>;
|
|
1596
|
+
/** The three startup checks, run again (#1842). */
|
|
1597
|
+
private recheckVaultRepairs;
|
|
1598
|
+
private repairManagers;
|
|
1599
|
+
private vaultPaths;
|
|
1600
|
+
/** `admin-system`, asked of the PDP: `permits` at the door, `holds` for an affordance (#1635). */
|
|
1601
|
+
private adminSystem;
|
|
1602
|
+
/**
|
|
1603
|
+
* Who may move leftovers into `owner`'s vault `vault` (#1842). The owner (the
|
|
1604
|
+
* container rule, then `page-edit` in the vault, which `vault-owner` gives),
|
|
1605
|
+
* with the vault open when it is sealed; for a vault that isn't sealed, also
|
|
1606
|
+
* someone holding `admin-system` — the boot migration's reach (#1460),
|
|
1607
|
+
* which writes into unsealed vaults with no owner present. A sealed vault
|
|
1608
|
+
* needs its owner's key, so no one else can.
|
|
1609
|
+
*/
|
|
1610
|
+
private repairAuthority;
|
|
1611
|
+
/**
|
|
1612
|
+
* Flush any pending write queues in the provider (e.g. page-index writes).
|
|
1613
|
+
* Call before process exit to prevent data loss on unclean shutdown.
|
|
1614
|
+
*/
|
|
1615
|
+
flushWriteQueue(): Promise<void>;
|
|
1616
|
+
/**
|
|
1617
|
+
* Shutdown the PageManager and its provider
|
|
1618
|
+
*
|
|
1619
|
+
* Cleanly shuts down the provider, closing connections and flushing caches.
|
|
1620
|
+
*
|
|
1621
|
+
* @async
|
|
1622
|
+
* @returns {Promise<void>}
|
|
1623
|
+
*/
|
|
1624
|
+
shutdown(): Promise<void>;
|
|
1625
|
+
/**
|
|
1626
|
+
* Backup all pages through the provider
|
|
1627
|
+
*
|
|
1628
|
+
* Delegates to the provider's backup() method to serialize all page data.
|
|
1629
|
+
* The backup includes all page content, metadata, and directory structure.
|
|
1630
|
+
*
|
|
1631
|
+
* @returns {Promise<BackupData>} Backup data from provider
|
|
1632
|
+
*/
|
|
1633
|
+
backup(): Promise<BackupData>;
|
|
1634
|
+
/**
|
|
1635
|
+
* Restore pages from backup data
|
|
1636
|
+
*
|
|
1637
|
+
* Delegates to the provider's restore() method to recreate all pages
|
|
1638
|
+
* from the backup data.
|
|
1639
|
+
*
|
|
1640
|
+
* @param {BackupData} backupData - Backup data from backup() method
|
|
1641
|
+
* @returns {Promise<void>}
|
|
1642
|
+
*/
|
|
1643
|
+
restore(backupData: BackupData): Promise<void>;
|
|
1644
|
+
}
|
|
1645
|
+
export default PageManager;
|