@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.
Files changed (302) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +16 -2
  3. package/lib/packages/ngdpbase-types/index.d.ts +32 -0
  4. package/lib/packages/ngdpbase-types/testing.d.ts +43 -0
  5. package/lib/packages/ngdpbase-types/testing.js +53 -0
  6. package/lib/src/cache/CacheDependencies.d.ts +50 -0
  7. package/lib/src/cache/CacheDependencies.js +50 -0
  8. package/lib/src/cache/ICacheAdapter.d.ts +86 -0
  9. package/lib/src/cache/RegionCache.d.ts +171 -0
  10. package/lib/src/context/ActorContext.d.ts +48 -0
  11. package/lib/src/context/ActorContext.js +33 -0
  12. package/lib/src/context/ApiContext.d.ts +194 -0
  13. package/lib/src/context/ApiContext.js +273 -0
  14. package/lib/src/context/BaseContext.d.ts +110 -0
  15. package/lib/src/context/BaseContext.js +143 -0
  16. package/lib/src/context/JobContext.d.ts +153 -0
  17. package/lib/src/context/JobContext.js +144 -0
  18. package/lib/src/context/WikiContext.d.ts +404 -0
  19. package/lib/src/context/bootActions.d.ts +64 -0
  20. package/lib/src/context/bootActions.js +111 -0
  21. package/lib/src/converters/HtmlConverter.d.ts +70 -0
  22. package/lib/src/converters/IContentConverter.d.ts +94 -0
  23. package/lib/src/converters/JSPWikiConverter.d.ts +117 -0
  24. package/lib/src/converters/conversionWarning.d.ts +15 -0
  25. package/lib/src/converters/ncm/bridge.d.ts +22 -0
  26. package/lib/src/converters/ncm/existing.d.ts +23 -0
  27. package/lib/src/converters/ncm/fix/blocks.d.ts +89 -0
  28. package/lib/src/converters/ncm/fix/bulletMarkers.d.ts +21 -0
  29. package/lib/src/converters/ncm/fix/index.d.ts +36 -0
  30. package/lib/src/converters/ncm/fix/jspwikiBullets.d.ts +20 -0
  31. package/lib/src/converters/ncm/fix/jspwikiCodeMarkers.d.ts +38 -0
  32. package/lib/src/converters/ncm/fix/jspwikiHeadings.d.ts +25 -0
  33. package/lib/src/converters/ncm/fix/jspwikiIndent.d.ts +24 -0
  34. package/lib/src/converters/ncm/fix/jspwikiInsert.d.ts +21 -0
  35. package/lib/src/converters/ncm/fix/jspwikiItalic.d.ts +29 -0
  36. package/lib/src/converters/ncm/fix/jspwikiLinkLeftovers.d.ts +27 -0
  37. package/lib/src/converters/ncm/fix/moreInformationFooter.d.ts +25 -0
  38. package/lib/src/converters/ncm/fix/styleClosers.d.ts +25 -0
  39. package/lib/src/converters/ncm/fix/tightenLists.d.ts +30 -0
  40. package/lib/src/converters/ncm/fix/types.d.ts +33 -0
  41. package/lib/src/converters/ncm/footnotes.d.ts +39 -0
  42. package/lib/src/converters/ncm/images.d.ts +49 -0
  43. package/lib/src/converters/ncm/index.d.ts +20 -0
  44. package/lib/src/converters/ncm/links.d.ts +21 -0
  45. package/lib/src/converters/ncm/normalize.d.ts +39 -0
  46. package/lib/src/converters/ncm/placeholder.d.ts +25 -0
  47. package/lib/src/converters/ncm/tables.d.ts +36 -0
  48. package/lib/src/converters/ncm/types.d.ts +42 -0
  49. package/lib/src/http/egressPolicy.d.ts +52 -0
  50. package/lib/src/http/guardedFetch.d.ts +89 -0
  51. package/lib/src/http/guardedFetch.js +113 -0
  52. package/lib/src/http/guardedLookup.d.ts +48 -0
  53. package/lib/src/http/guardedLookup.js +73 -0
  54. package/lib/src/http/ssrf.d.ts +83 -0
  55. package/lib/src/http/ssrf.js +311 -0
  56. package/lib/src/mail/MailProvider.d.ts +36 -0
  57. package/lib/src/mail/NodemailerMailProvider.d.ts +26 -0
  58. package/lib/src/managers/AddonsManager.d.ts +691 -0
  59. package/lib/src/managers/AgentTokenManager.d.ts +269 -0
  60. package/lib/src/managers/AssetManager.d.ts +116 -0
  61. package/lib/src/managers/AttachmentManager.d.ts +1032 -0
  62. package/lib/src/managers/AuthManager.d.ts +1257 -0
  63. package/lib/src/managers/BackgroundJobManager.d.ts +434 -0
  64. package/lib/src/managers/BaseManager.d.ts +391 -0
  65. package/lib/src/managers/BaseManager.js +415 -0
  66. package/lib/src/managers/CacheManager.d.ts +159 -0
  67. package/lib/src/managers/CatalogManager.d.ts +235 -0
  68. package/lib/src/managers/CommentManager.d.ts +75 -0
  69. package/lib/src/managers/ConfigurationManager.d.ts +747 -0
  70. package/lib/src/managers/DatabaseManager.d.ts +89 -0
  71. package/lib/src/managers/DatabaseManager.js +236 -0
  72. package/lib/src/managers/EmailManager.d.ts +47 -0
  73. package/lib/src/managers/ExportManager.d.ts +129 -0
  74. package/lib/src/managers/ExportManager.js +420 -0
  75. package/lib/src/managers/FootnoteManager.d.ts +118 -0
  76. package/lib/src/managers/MediaManager.d.ts +279 -0
  77. package/lib/src/managers/MetricsManager.d.ts +89 -0
  78. package/lib/src/managers/NotificationManager.d.ts +165 -0
  79. package/lib/src/managers/OidcManager.d.ts +210 -0
  80. package/lib/src/managers/OrganizationManager.d.ts +107 -0
  81. package/lib/src/managers/PageManager.d.ts +1645 -0
  82. package/lib/src/managers/PersonManager.d.ts +48 -0
  83. package/lib/src/managers/PluginManager.d.ts +179 -0
  84. package/lib/src/managers/PluginManager.js +393 -0
  85. package/lib/src/managers/PolicyEvaluator.d.ts +213 -0
  86. package/lib/src/managers/RenderingManager.d.ts +358 -0
  87. package/lib/src/managers/RoleManager.d.ts +134 -0
  88. package/lib/src/managers/SearchManager.d.ts +402 -0
  89. package/lib/src/managers/ShareManager.d.ts +251 -0
  90. package/lib/src/managers/TemplateManager.d.ts +176 -0
  91. package/lib/src/managers/ThemeManager.d.ts +56 -0
  92. package/lib/src/managers/UserManager.d.ts +480 -0
  93. package/lib/src/managers/ValidationManager.d.ts +426 -0
  94. package/lib/src/managers/VariableManager.d.ts +145 -0
  95. package/lib/src/middleware/csrf.d.ts +3 -0
  96. package/lib/src/parsers/LinkParser.d.ts +457 -0
  97. package/lib/src/parsers/MarkupParser.d.ts +769 -0
  98. package/lib/src/parsers/context/ParseContext.d.ts +314 -0
  99. package/lib/src/parsers/data/emoji-map.d.ts +18 -0
  100. package/lib/src/parsers/dom/DOMBuilder.d.ts +210 -0
  101. package/lib/src/parsers/dom/DOMParser.d.ts +252 -0
  102. package/lib/src/parsers/dom/Tokenizer.d.ts +358 -0
  103. package/lib/src/parsers/dom/WikiDocument.d.ts +272 -0
  104. package/lib/src/parsers/dom/handlers/DOMLinkHandler.d.ts +260 -0
  105. package/lib/src/parsers/dom/handlers/DOMPluginHandler.d.ts +212 -0
  106. package/lib/src/parsers/dom/handlers/DOMVariableHandler.d.ts +171 -0
  107. package/lib/src/parsers/filters/BaseFilter.d.ts +269 -0
  108. package/lib/src/parsers/filters/FilterChain.d.ts +320 -0
  109. package/lib/src/parsers/handlers/BaseSyntaxHandler.d.ts +331 -0
  110. package/lib/src/parsers/handlers/HandlerRegistry.d.ts +225 -0
  111. package/lib/src/parsers/handlers/JSPWikiPreprocessor.d.ts +89 -0
  112. package/lib/src/parsers/handlers/LinkParserHandler.d.ts +80 -0
  113. package/lib/src/parsers/handlers/PluginSyntaxHandler.d.ts +78 -0
  114. package/lib/src/parsers/handlers/WikiFormHandler.d.ts +149 -0
  115. package/lib/src/parsers/handlers/WikiTagHandler.d.ts +125 -0
  116. package/lib/src/parsers/jspwikiTableRow.d.ts +39 -0
  117. package/lib/src/parsers/styleBlockSyntax.d.ts +14 -0
  118. package/lib/src/plugins/types.d.ts +79 -0
  119. package/lib/src/providers/AgentTokenAuthProvider.d.ts +40 -0
  120. package/lib/src/providers/AuthentikBearerAuthProvider.d.ts +75 -0
  121. package/lib/src/providers/BaseAttachmentProvider.d.ts +197 -0
  122. package/lib/src/providers/BaseAuthProvider.d.ts +220 -0
  123. package/lib/src/providers/BaseCacheProvider.d.ts +140 -0
  124. package/lib/src/providers/BaseCredentialsProvider.d.ts +84 -0
  125. package/lib/src/providers/BaseDatabaseProvider.d.ts +22 -0
  126. package/lib/src/providers/BaseDatabaseProvider.js +12 -0
  127. package/lib/src/providers/BaseLoggingProvider.d.ts +68 -0
  128. package/lib/src/providers/BaseLoggingProvider.js +37 -0
  129. package/lib/src/providers/BaseMediaProvider.d.ts +308 -0
  130. package/lib/src/providers/BasePageProvider.d.ts +337 -0
  131. package/lib/src/providers/BaseProvider.d.ts +98 -0
  132. package/lib/src/providers/BaseProvider.js +74 -0
  133. package/lib/src/providers/BaseSearchProvider.d.ts +362 -0
  134. package/lib/src/providers/BasicAttachmentProvider.d.ts +599 -0
  135. package/lib/src/providers/CloudflareAccessAuthProvider.d.ts +72 -0
  136. package/lib/src/providers/FileCredentialsProvider.d.ts +33 -0
  137. package/lib/src/providers/FileJobStateProvider.d.ts +58 -0
  138. package/lib/src/providers/FileLoggingProvider.d.ts +43 -0
  139. package/lib/src/providers/FileLoggingProvider.js +120 -0
  140. package/lib/src/providers/FileOidcAdapter.d.ts +25 -0
  141. package/lib/src/providers/FileSystemMediaProvider.d.ts +403 -0
  142. package/lib/src/providers/LunrSearchProvider.d.ts +280 -0
  143. package/lib/src/providers/MagicLinkAuthProvider.d.ts +125 -0
  144. package/lib/src/providers/NullCacheProvider.d.ts +72 -0
  145. package/lib/src/providers/OidcAuthProvider.d.ts +180 -0
  146. package/lib/src/providers/OidcBearerAuthProvider.d.ts +30 -0
  147. package/lib/src/providers/PasskeyAuthProvider.d.ts +58 -0
  148. package/lib/src/providers/PasswordAuthProvider.d.ts +20 -0
  149. package/lib/src/providers/SqliteDatabaseProvider.d.ts +55 -0
  150. package/lib/src/providers/SqliteDatabaseProvider.js +90 -0
  151. package/lib/src/providers/SqliteJobStateProvider.d.ts +41 -0
  152. package/lib/src/providers/databaseMigrations.d.ts +8 -0
  153. package/lib/src/providers/databaseMigrations.js +47 -0
  154. package/lib/src/providers/jobState.d.ts +99 -0
  155. package/lib/src/providers/lunrQueryToMiniSearch.d.ts +32 -0
  156. package/lib/src/providers/mediaFilenameDate.d.ts +22 -0
  157. package/lib/src/providers/sqliteLocation.d.ts +17 -0
  158. package/lib/src/providers/sqliteLocation.js +55 -0
  159. package/lib/src/providers/sqliteMigrations.d.ts +48 -0
  160. package/lib/src/providers/sqliteMigrations.js +72 -0
  161. package/lib/src/rendering/githubAlerts.d.ts +22 -0
  162. package/lib/src/rendering/htmlPolicy.d.ts +76 -0
  163. package/lib/src/rendering/markdownConverter.d.ts +73 -0
  164. package/lib/src/security/PolicyDecisionPoint.d.ts +245 -0
  165. package/lib/src/security/PolicyInformationPoint.d.ts +448 -0
  166. package/lib/src/security/pageActions.d.ts +40 -0
  167. package/lib/src/security/permissions.generated.d.ts +78 -0
  168. package/lib/src/types/Asset.d.ts +414 -0
  169. package/lib/src/types/Catalog.d.ts +61 -0
  170. package/lib/src/types/Comment.d.ts +11 -0
  171. package/lib/src/types/Config.d.ts +388 -0
  172. package/lib/src/types/Organization.d.ts +78 -0
  173. package/lib/src/types/OrganizationProvider.d.ts +38 -0
  174. package/lib/src/types/Page.d.ts +220 -0
  175. package/lib/src/types/Person.d.ts +56 -0
  176. package/lib/src/types/PersonProvider.d.ts +25 -0
  177. package/lib/src/types/Policy.d.ts +139 -0
  178. package/lib/src/types/Policy.js +17 -0
  179. package/lib/src/types/Provider.d.ts +810 -0
  180. package/lib/src/types/Role.d.ts +84 -0
  181. package/lib/src/types/RoleProvider.d.ts +35 -0
  182. package/lib/src/types/Schema.d.ts +425 -0
  183. package/lib/src/types/Share.d.ts +172 -0
  184. package/lib/src/types/Share.js +81 -0
  185. package/lib/src/types/User.d.ts +304 -0
  186. package/lib/src/types/Version.d.ts +189 -0
  187. package/lib/src/types/WikiEngine.d.ts +128 -0
  188. package/lib/src/types/express.d.ts +120 -0
  189. package/lib/src/types/express.js +1 -0
  190. package/lib/src/types/guards.d.ts +78 -0
  191. package/lib/src/types/heic-decode.d.ts +16 -0
  192. package/lib/src/types/index.d.ts +19 -0
  193. package/lib/src/types/linkedom.d.ts +77 -0
  194. package/lib/src/types/markdown-it-plugins.d.ts +21 -0
  195. package/lib/src/utils/DeltaStorage.d.ts +118 -0
  196. package/lib/src/utils/LocaleUtils.d.ts +105 -0
  197. package/lib/src/utils/LocaleUtils.js +294 -0
  198. package/lib/src/utils/PageNameMatcher.d.ts +98 -0
  199. package/lib/src/utils/PathPreflight.d.ts +38 -0
  200. package/lib/src/utils/PathPreflight.js +50 -0
  201. package/lib/src/utils/SectionUtils.d.ts +40 -0
  202. package/lib/src/utils/addonConfigLayer.d.ts +144 -0
  203. package/lib/src/utils/addonConfigLayer.js +382 -0
  204. package/lib/src/utils/addonDatabaseRules.d.ts +25 -0
  205. package/lib/src/utils/addonDatabaseRules.js +57 -0
  206. package/lib/src/utils/addonHost.d.ts +51 -0
  207. package/lib/src/utils/addonHost.js +72 -0
  208. package/lib/src/utils/addonInstaller.d.ts +109 -0
  209. package/lib/src/utils/addonLock.d.ts +37 -0
  210. package/lib/src/utils/addonLock.js +48 -0
  211. package/lib/src/utils/addonPageSync.d.ts +45 -0
  212. package/lib/src/utils/addonProvenance.d.ts +48 -0
  213. package/lib/src/utils/addonTarball.d.ts +41 -0
  214. package/lib/src/utils/addonsPathResolver.d.ts +76 -0
  215. package/lib/src/utils/addonsPathResolver.js +129 -0
  216. package/lib/src/utils/assetMetadataTags.d.ts +66 -0
  217. package/lib/src/utils/atomicWrite.d.ts +28 -0
  218. package/lib/src/utils/auditConfigChange.d.ts +57 -0
  219. package/lib/src/utils/auditEventNames.d.ts +50 -0
  220. package/lib/src/utils/auditEventNames.generated.d.ts +163 -0
  221. package/lib/src/utils/auditEventNames.generated.js +168 -0
  222. package/lib/src/utils/auditEventNames.js +49 -0
  223. package/lib/src/utils/auditEvents.d.ts +265 -0
  224. package/lib/src/utils/auditEvents.js +309 -0
  225. package/lib/src/utils/auditRegistry.d.ts +102 -0
  226. package/lib/src/utils/auditRegistry.js +168 -0
  227. package/lib/src/utils/bip39English.d.ts +2 -0
  228. package/lib/src/utils/configEnvKeys.d.ts +68 -0
  229. package/lib/src/utils/configFiles.d.ts +74 -0
  230. package/lib/src/utils/configFiles.js +150 -0
  231. package/lib/src/utils/delegation.d.ts +48 -0
  232. package/lib/src/utils/deriveUsername.d.ts +21 -0
  233. package/lib/src/utils/explainMediaPath.d.ts +123 -0
  234. package/lib/src/utils/formerTitles.d.ts +69 -0
  235. package/lib/src/utils/frontmatterAccess.d.ts +54 -0
  236. package/lib/src/utils/headlessAdminPassword.d.ts +55 -0
  237. package/lib/src/utils/imageTransform.d.ts +52 -0
  238. package/lib/src/utils/instanceEnvSecret.d.ts +51 -0
  239. package/lib/src/utils/jobSlots.d.ts +43 -0
  240. package/lib/src/utils/keywordNormalizer.d.ts +127 -0
  241. package/lib/src/utils/logger.d.ts +70 -0
  242. package/lib/src/utils/logger.js +106 -0
  243. package/lib/src/utils/magicLinkDeviceState.d.ts +95 -0
  244. package/lib/src/utils/managerUtils.d.ts +40 -0
  245. package/lib/src/utils/migrateLegacyPrivateAttachments.d.ts +15 -0
  246. package/lib/src/utils/orgFilename.d.ts +22 -0
  247. package/lib/src/utils/pageFeature.d.ts +27 -0
  248. package/lib/src/utils/pageFrontmatter.d.ts +21 -0
  249. package/lib/src/utils/pageNotes.d.ts +150 -0
  250. package/lib/src/utils/pageTitleRule.d.ts +37 -0
  251. package/lib/src/utils/pageToArticle.d.ts +59 -0
  252. package/lib/src/utils/pageUrl.d.ts +18 -0
  253. package/lib/src/utils/passwordChange.d.ts +12 -0
  254. package/lib/src/utils/passwordHash.d.ts +60 -0
  255. package/lib/src/utils/permissionCatalog.d.ts +52 -0
  256. package/lib/src/utils/pluginFormatters.d.ts +452 -0
  257. package/lib/src/utils/pluginFormatters.js +714 -0
  258. package/lib/src/utils/privateLinkRewrite.d.ts +42 -0
  259. package/lib/src/utils/privateStoreAccess.d.ts +57 -0
  260. package/lib/src/utils/privateStoreAccess.js +65 -0
  261. package/lib/src/utils/privateStoreCatalogs.d.ts +40 -0
  262. package/lib/src/utils/privateStoreCrypto.d.ts +127 -0
  263. package/lib/src/utils/privateStoreDoor.d.ts +181 -0
  264. package/lib/src/utils/privateStoreExport.d.ts +72 -0
  265. package/lib/src/utils/privateStoreFiles.d.ts +120 -0
  266. package/lib/src/utils/privateStoreMeta.d.ts +64 -0
  267. package/lib/src/utils/privateStoreMigrations.d.ts +37 -0
  268. package/lib/src/utils/privateStorePath.d.ts +259 -0
  269. package/lib/src/utils/privateStorePath.js +436 -0
  270. package/lib/src/utils/privateStoreTakeout.d.ts +76 -0
  271. package/lib/src/utils/privateStoreUnlock.d.ts +175 -0
  272. package/lib/src/utils/proxyClaim.d.ts +59 -0
  273. package/lib/src/utils/proxyClaim.js +30 -0
  274. package/lib/src/utils/redactPrivateNames.d.ts +21 -0
  275. package/lib/src/utils/redactPrivateNames.js +89 -0
  276. package/lib/src/utils/redactSecrets.d.ts +58 -0
  277. package/lib/src/utils/redactSecrets.js +171 -0
  278. package/lib/src/utils/renameLinkRewrite.d.ts +112 -0
  279. package/lib/src/utils/roleGrants.d.ts +35 -0
  280. package/lib/src/utils/safeRedirect.d.ts +32 -0
  281. package/lib/src/utils/schedule.d.ts +54 -0
  282. package/lib/src/utils/secretFileMode.d.ts +17 -0
  283. package/lib/src/utils/seededShippedPages.d.ts +45 -0
  284. package/lib/src/utils/sensitiveValues.d.ts +59 -0
  285. package/lib/src/utils/sensitiveValues.js +106 -0
  286. package/lib/src/utils/sessionEnd.d.ts +12 -0
  287. package/lib/src/utils/sessionGeneration.d.ts +24 -0
  288. package/lib/src/utils/sessionIdle.d.ts +41 -0
  289. package/lib/src/utils/sessionLimits.d.ts +68 -0
  290. package/lib/src/utils/shareLockbox.d.ts +64 -0
  291. package/lib/src/utils/sigstoreTrustedRoot.d.ts +33 -0
  292. package/lib/src/utils/sniffMime.d.ts +21 -0
  293. package/lib/src/utils/storeSearchIndex.d.ts +75 -0
  294. package/lib/src/utils/subjectMayDo.d.ts +21 -0
  295. package/lib/src/utils/systemCategoryRules.d.ts +30 -0
  296. package/lib/src/utils/systemCategoryRules.js +41 -0
  297. package/lib/src/utils/userCreateError.d.ts +62 -0
  298. package/lib/src/utils/username.d.ts +53 -0
  299. package/lib/src/utils/vaultUrlRewrite.d.ts +27 -0
  300. package/lib/src/utils/version.d.ts +84 -0
  301. package/lib/src/utils/videoFrame.d.ts +21 -0
  302. 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;