@jwilleke/ngdpbase-types 5.42.0 → 5.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/src/context/ApiContext.d.ts +10 -3
- package/lib/src/context/ApiContext.js +37 -4
- package/lib/src/context/BaseContext.d.ts +20 -20
- package/lib/src/context/BaseContext.js +41 -24
- package/lib/src/context/JobContext.d.ts +31 -0
- package/lib/src/context/JobContext.js +34 -1
- package/lib/src/ice/runExport.d.ts +8 -3
- package/lib/src/ice/searchExportSource.d.ts +44 -0
- package/lib/src/ice/types.d.ts +6 -0
- package/lib/src/managers/AddonsManager.d.ts +0 -28
- package/lib/src/managers/AgentTokenManager.d.ts +8 -0
- package/lib/src/managers/AttachmentManager.d.ts +11 -0
- package/lib/src/managers/AuditManager.d.ts +0 -1
- package/lib/src/managers/AuthManager.d.ts +38 -0
- package/lib/src/managers/AuthManager.js +84 -12
- package/lib/src/managers/BackupManager.d.ts +9 -2
- package/lib/src/managers/BackupManager.js +42 -19
- package/lib/src/managers/BaseManager.d.ts +17 -3
- package/lib/src/managers/BaseManager.js +12 -8
- package/lib/src/managers/ConfigurationManager.d.ts +28 -0
- package/lib/src/managers/ConsentManager.d.ts +1 -1
- package/lib/src/managers/DatabaseManager.js +17 -8
- package/lib/src/managers/EmailManager.d.ts +1 -1
- package/lib/src/managers/ExportManager.d.ts +1 -44
- package/lib/src/managers/ExportManager.js +3 -115
- package/lib/src/managers/ImportManager.d.ts +7 -0
- package/lib/src/managers/PageManager.d.ts +194 -23
- package/lib/src/managers/PolicyEvaluator.d.ts +182 -24
- package/lib/src/managers/ShareManager.d.ts +3 -2
- package/lib/src/managers/ThemeManager.d.ts +20 -1
- package/lib/src/managers/UserManager.d.ts +19 -5
- package/lib/src/managers/ValidationManager.d.ts +3 -1
- package/lib/src/providers/AgentTokenAuthProvider.js +1 -1
- package/lib/src/providers/BaseAuthProvider.d.ts +2 -0
- package/lib/src/providers/FileLoggingProvider.d.ts +12 -1
- package/lib/src/providers/FileLoggingProvider.js +37 -4
- package/lib/src/security/PolicyDecisionPoint.d.ts +107 -4
- package/lib/src/security/PolicyInformationPoint.d.ts +113 -7
- package/lib/src/security/ownedRecords.d.ts +69 -0
- package/lib/src/security/permissions.generated.d.ts +8 -0
- package/lib/src/security/signedOutWrites.d.ts +69 -0
- package/lib/src/security/signedOutWrites.js +146 -0
- package/lib/src/types/Page.d.ts +0 -7
- package/lib/src/types/Policy.d.ts +65 -8
- package/lib/src/types/Policy.js +6 -0
- package/lib/src/types/Provider.d.ts +12 -0
- package/lib/src/types/User.d.ts +6 -0
- package/lib/src/utils/addonConfigLayer.d.ts +6 -3
- package/lib/src/utils/addonConfigLayer.js +61 -3
- package/lib/src/utils/addonHost.d.ts +25 -0
- package/lib/src/utils/addonHost.js +29 -1
- package/lib/src/utils/addonPageSync.d.ts +0 -12
- package/lib/src/utils/auditEventNames.generated.d.ts +2 -0
- package/lib/src/utils/auditEventNames.generated.js +2 -0
- package/lib/src/utils/auditEvents.d.ts +2 -0
- package/lib/src/utils/auditEvents.js +3 -1
- package/lib/src/utils/availability.d.ts +15 -0
- package/lib/src/utils/availability.js +88 -0
- package/lib/src/utils/frontmatterAccess.d.ts +15 -24
- package/lib/src/utils/legacyStoreKeys.d.ts +16 -0
- package/lib/src/utils/pageTitleConflict.d.ts +32 -0
- package/lib/src/utils/pageWritePolicies.d.ts +74 -0
- package/lib/src/utils/permissionCatalog.d.ts +2 -0
- package/lib/src/utils/permissionCatalog.js +19 -0
- package/lib/src/utils/plainAttention.d.ts +19 -0
- package/lib/src/utils/plainAttention.js +18 -0
- package/lib/src/utils/policyAttributes.d.ts +232 -0
- package/lib/src/utils/policyAttributes.js +404 -0
- package/lib/src/utils/privateStoreAccess.d.ts +24 -4
- package/lib/src/utils/privateStoreAccess.js +19 -4
- package/lib/src/utils/roleGrants.d.ts +1 -1
- package/lib/src/utils/username.d.ts +13 -2
- package/lib/src/utils/username.js +18 -3
- package/package.json +7 -7
|
@@ -10,17 +10,7 @@ export type { PdfOptions, PdfMargin, PdfPageSize } from '../export/pdfEngine.js'
|
|
|
10
10
|
* when something asked for a PDF and could not have one; cleared by the next
|
|
11
11
|
* render that works.
|
|
12
12
|
*/
|
|
13
|
-
export declare const PDF_UNAVAILABLE_REASON = "
|
|
14
|
-
/**
|
|
15
|
-
* Export file metadata interface
|
|
16
|
-
*/
|
|
17
|
-
export interface ExportFileInfo {
|
|
18
|
-
filename: string;
|
|
19
|
-
path: string;
|
|
20
|
-
size: number;
|
|
21
|
-
created: Date;
|
|
22
|
-
modified: Date;
|
|
23
|
-
}
|
|
13
|
+
export declare const PDF_UNAVAILABLE_REASON = "PDFs can't be made right now. Choose how PDFs are made in the settings, or fix the one already chosen.";
|
|
24
14
|
/**
|
|
25
15
|
* Export configuration interface
|
|
26
16
|
*/
|
|
@@ -146,13 +136,6 @@ declare class ExportManager extends BaseManager {
|
|
|
146
136
|
* @returns {Promise<void>}
|
|
147
137
|
*/
|
|
148
138
|
initialize(config?: ExportConfig): Promise<void>;
|
|
149
|
-
/**
|
|
150
|
-
* Export a single page to HTML
|
|
151
|
-
* @param {string} pageName - Page name to export
|
|
152
|
-
* @param {ExportUser|null} user - User object for locale-aware formatting
|
|
153
|
-
* @returns {Promise<string>} HTML content
|
|
154
|
-
*/
|
|
155
|
-
exportPageToHtml(pageName: string, ctx: ActorContext, user?: ExportUser | null): Promise<string>;
|
|
156
139
|
/**
|
|
157
140
|
* One page as a complete HTML document: its content rendered, with the
|
|
158
141
|
* export's styling and metadata footer. It reads nothing: the caller has
|
|
@@ -171,32 +154,6 @@ declare class ExportManager extends BaseManager {
|
|
|
171
154
|
* @returns {Promise<string>} Combined HTML content
|
|
172
155
|
*/
|
|
173
156
|
exportPagesToHtml(pageNames: string[], ctx: ActorContext, user?: ExportUser | null): Promise<string>;
|
|
174
|
-
/**
|
|
175
|
-
* Export page(s) to markdown
|
|
176
|
-
* @param {string|string[]} pageNames - Single page name or array of page names
|
|
177
|
-
* @param {ExportUser|null} user - User object for locale-aware formatting
|
|
178
|
-
* @returns {Promise<string>} Markdown content
|
|
179
|
-
*/
|
|
180
|
-
exportToMarkdown(pageNames: string | string[], ctx: ActorContext, user?: ExportUser | null): Promise<string>;
|
|
181
|
-
/**
|
|
182
|
-
* Save export to file
|
|
183
|
-
* @param {string} content - Content to save
|
|
184
|
-
* @param {string} filename - Filename
|
|
185
|
-
* @param {string} format - File format
|
|
186
|
-
* @returns {Promise<string>} File path
|
|
187
|
-
*/
|
|
188
|
-
saveExport(content: string, filename: string, format: string): Promise<string>;
|
|
189
|
-
/**
|
|
190
|
-
* Get list of available exports
|
|
191
|
-
* @returns {Promise<ExportFileInfo[]>} List of export files
|
|
192
|
-
*/
|
|
193
|
-
getExports(): Promise<ExportFileInfo[]>;
|
|
194
|
-
/**
|
|
195
|
-
* Delete an export file
|
|
196
|
-
* @param {string} filename - Export filename
|
|
197
|
-
* @returns {Promise<void>}
|
|
198
|
-
*/
|
|
199
|
-
deleteExport(filename: string): Promise<void>;
|
|
200
157
|
/**
|
|
201
158
|
* Get formatted timestamp using user's locale
|
|
202
159
|
* @param {ExportUser|null} user - User object (optional)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import BaseManager from './BaseManager.js';
|
|
2
2
|
import fs from 'fs/promises';
|
|
3
|
-
import path from 'path';
|
|
4
3
|
import LocaleUtils from '../utils/LocaleUtils.js';
|
|
5
4
|
import logger from '../utils/logger.js';
|
|
6
5
|
import { actorOf } from '../context/ActorContext.js';
|
|
@@ -15,7 +14,7 @@ export { PdfUnavailableError };
|
|
|
15
14
|
* when something asked for a PDF and could not have one; cleared by the next
|
|
16
15
|
* render that works.
|
|
17
16
|
*/
|
|
18
|
-
export const PDF_UNAVAILABLE_REASON = '
|
|
17
|
+
export const PDF_UNAVAILABLE_REASON = "PDFs can't be made right now. Choose how PDFs are made in the settings, or fix the one already chosen.";
|
|
19
18
|
/** Shipped defaults of the `ngdpbase.export.pdf.*` keys. */
|
|
20
19
|
const PDF_DEFAULTS = { provider: 'none', pageSize: 'Letter', timeoutMs: 30_000, maxConcurrent: 2, serviceMaxBytes: 52_428_800 };
|
|
21
20
|
/**
|
|
@@ -34,7 +33,7 @@ const PDF_DEFAULTS = { provider: 'none', pageSize: 'Letter', timeoutMs: 30_000,
|
|
|
34
33
|
*
|
|
35
34
|
* @example
|
|
36
35
|
* const exportManager = engine.getManager('ExportManager');
|
|
37
|
-
* const
|
|
36
|
+
* const csv = exportManager.toCsv(['name'], [['Main']]);
|
|
38
37
|
*/
|
|
39
38
|
/** A text cell starting with one of these runs as a spreadsheet formula. */
|
|
40
39
|
const CSV_FORMULA_START = /^[=+\-@\t\r]/;
|
|
@@ -229,7 +228,7 @@ class ExportManager extends BaseManager {
|
|
|
229
228
|
this.exportDirectory = config.exportDirectory
|
|
230
229
|
|| this.engine.getManager('ConfigurationManager')?.getResolvedDataPath('ngdpbase.directories.exports', '${FAST_STORAGE}/exports')
|
|
231
230
|
|| './exports';
|
|
232
|
-
const preflight = this.preflightConfiguredPath('
|
|
231
|
+
const preflight = this.preflightConfiguredPath('ngdpbase.directories.exports', this.exportDirectory);
|
|
233
232
|
if (!preflight.ok) {
|
|
234
233
|
this.exportDirectory = '';
|
|
235
234
|
logger.info('ExportManager initialized (degraded — exports disabled)');
|
|
@@ -240,24 +239,6 @@ class ExportManager extends BaseManager {
|
|
|
240
239
|
const pdfProviderName = this.engine.getManager('ConfigurationManager')?.getProperty('ngdpbase.export.pdf.provider', PDF_DEFAULTS.provider);
|
|
241
240
|
logger.info(`ExportManager initialized (PDF provider: ${pdfProviderName ?? PDF_DEFAULTS.provider})`);
|
|
242
241
|
}
|
|
243
|
-
/**
|
|
244
|
-
* Export a single page to HTML
|
|
245
|
-
* @param {string} pageName - Page name to export
|
|
246
|
-
* @param {ExportUser|null} user - User object for locale-aware formatting
|
|
247
|
-
* @returns {Promise<string>} HTML content
|
|
248
|
-
*/
|
|
249
|
-
async exportPageToHtml(pageName, ctx, user = null) {
|
|
250
|
-
const pageManager = this.engine.getManager('PageManager');
|
|
251
|
-
const renderingManager = this.engine.getManager('RenderingManager');
|
|
252
|
-
if (!pageManager || !renderingManager) {
|
|
253
|
-
throw new Error('Required managers not available');
|
|
254
|
-
}
|
|
255
|
-
const page = (await pageManager.getPage(pageName, ctx));
|
|
256
|
-
if (!page) {
|
|
257
|
-
throw new Error(`Page '${pageName}' not found`);
|
|
258
|
-
}
|
|
259
|
-
return this.renderPageDocument(pageName, page, user);
|
|
260
|
-
}
|
|
261
242
|
/**
|
|
262
243
|
* One page as a complete HTML document: its content rendered, with the
|
|
263
244
|
* export's styling and metadata footer. It reads nothing: the caller has
|
|
@@ -480,99 +461,6 @@ class ExportManager extends BaseManager {
|
|
|
480
461
|
</html>`;
|
|
481
462
|
return html;
|
|
482
463
|
}
|
|
483
|
-
/**
|
|
484
|
-
* Export page(s) to markdown
|
|
485
|
-
* @param {string|string[]} pageNames - Single page name or array of page names
|
|
486
|
-
* @param {ExportUser|null} user - User object for locale-aware formatting
|
|
487
|
-
* @returns {Promise<string>} Markdown content
|
|
488
|
-
*/
|
|
489
|
-
async exportToMarkdown(pageNames, ctx, user = null) {
|
|
490
|
-
const pageManager = this.engine.getManager('PageManager');
|
|
491
|
-
if (!pageManager) {
|
|
492
|
-
throw new Error('PageManager not available');
|
|
493
|
-
}
|
|
494
|
-
const names = Array.isArray(pageNames) ? pageNames : [pageNames];
|
|
495
|
-
let markdown = '';
|
|
496
|
-
if (names.length > 1) {
|
|
497
|
-
markdown += '# ngdpbase Export\n\n';
|
|
498
|
-
markdown += `*Exported on ${this.getFormattedTimestamp(user)}*\n\n`;
|
|
499
|
-
markdown += '## Table of Contents\n\n';
|
|
500
|
-
for (const pageName of names) {
|
|
501
|
-
markdown += `- [${pageName}](#${pageName.toLowerCase().replace(/[^a-z0-9]/g, '-')})\n`;
|
|
502
|
-
}
|
|
503
|
-
markdown += '\n---\n\n';
|
|
504
|
-
}
|
|
505
|
-
for (const pageName of names) {
|
|
506
|
-
const page = (await pageManager.getPage(pageName, ctx));
|
|
507
|
-
if (page) {
|
|
508
|
-
if (names.length > 1) {
|
|
509
|
-
markdown += `# ${pageName}\n\n`;
|
|
510
|
-
}
|
|
511
|
-
markdown += page.content;
|
|
512
|
-
if (names.length > 1) {
|
|
513
|
-
markdown += '\n\n---\n\n';
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
return markdown;
|
|
518
|
-
}
|
|
519
|
-
/**
|
|
520
|
-
* Save export to file
|
|
521
|
-
* @param {string} content - Content to save
|
|
522
|
-
* @param {string} filename - Filename
|
|
523
|
-
* @param {string} format - File format
|
|
524
|
-
* @returns {Promise<string>} File path
|
|
525
|
-
*/
|
|
526
|
-
async saveExport(content, filename, format) {
|
|
527
|
-
const sanitizedFilename = filename.replace(/[^a-zA-Z0-9\-_]/g, '-');
|
|
528
|
-
const timestamp = new Date().toISOString().slice(0, 10);
|
|
529
|
-
const fullFilename = `${sanitizedFilename}_${timestamp}.${format}`;
|
|
530
|
-
const filePath = path.join(this.exportDirectory, fullFilename);
|
|
531
|
-
await fs.writeFile(filePath, content, 'utf8');
|
|
532
|
-
logger.info(`Exported to: ${fullFilename}`);
|
|
533
|
-
return filePath;
|
|
534
|
-
}
|
|
535
|
-
/**
|
|
536
|
-
* Get list of available exports
|
|
537
|
-
* @returns {Promise<ExportFileInfo[]>} List of export files
|
|
538
|
-
*/
|
|
539
|
-
async getExports() {
|
|
540
|
-
try {
|
|
541
|
-
const files = await fs.readdir(this.exportDirectory);
|
|
542
|
-
const exports = [];
|
|
543
|
-
for (const file of files) {
|
|
544
|
-
const filePath = path.join(this.exportDirectory, file);
|
|
545
|
-
const stats = await fs.stat(filePath);
|
|
546
|
-
exports.push({
|
|
547
|
-
filename: file,
|
|
548
|
-
path: filePath,
|
|
549
|
-
size: stats.size,
|
|
550
|
-
created: stats.birthtime,
|
|
551
|
-
modified: stats.mtime
|
|
552
|
-
});
|
|
553
|
-
}
|
|
554
|
-
return exports.sort((a, b) => b.created.getTime() - a.created.getTime());
|
|
555
|
-
}
|
|
556
|
-
catch (err) {
|
|
557
|
-
logger.error('Error getting exports:', err);
|
|
558
|
-
return [];
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
/**
|
|
562
|
-
* Delete an export file
|
|
563
|
-
* @param {string} filename - Export filename
|
|
564
|
-
* @returns {Promise<void>}
|
|
565
|
-
*/
|
|
566
|
-
async deleteExport(filename) {
|
|
567
|
-
// Only a plain file name inside the export folder: never a path that
|
|
568
|
-
// reaches another folder.
|
|
569
|
-
if (!filename || filename !== path.basename(filename) || filename === '.' || filename === '..') {
|
|
570
|
-
throw new Error(`Not an export file name: ${filename}`);
|
|
571
|
-
}
|
|
572
|
-
const filePath = path.join(this.exportDirectory, filename);
|
|
573
|
-
await fs.unlink(filePath);
|
|
574
|
-
logger.info(`Deleted export: ${filename}`);
|
|
575
|
-
}
|
|
576
464
|
/**
|
|
577
465
|
* Get formatted timestamp using user's locale
|
|
578
466
|
* @param {ExportUser|null} user - User object (optional)
|
|
@@ -633,6 +633,13 @@ declare class ImportManager extends BaseManager {
|
|
|
633
633
|
* is permitted, and a refusal to someone who holds it is step-up's.
|
|
634
634
|
*/
|
|
635
635
|
private mayExportPage;
|
|
636
|
+
/**
|
|
637
|
+
* `core.search` as an export source: a search's pages and files as one zip
|
|
638
|
+
* ({@link searchExportSource}). Like a page, a search is offered to a user
|
|
639
|
+
* who holds `page-export`, and read as the user who asked, each page and
|
|
640
|
+
* file by its own read check. Recorded as one `page-export` of the search.
|
|
641
|
+
*/
|
|
642
|
+
private searchExportSource;
|
|
636
643
|
/**
|
|
637
644
|
* `core.pages` as an export source (#2120, D5: pages export checks
|
|
638
645
|
* `page-export`). A page is offered for export when its reader may see it
|
|
@@ -97,6 +97,26 @@ export interface PageSaveOptions {
|
|
|
97
97
|
* doc says so.
|
|
98
98
|
*/
|
|
99
99
|
preserveEditor?: boolean;
|
|
100
|
+
/**
|
|
101
|
+
* The user a NEW page is made for, by a system job (#1815): they become its
|
|
102
|
+
* author and editor, where a create otherwise takes both from the acting
|
|
103
|
+
* context. For the pages the system makes on a user's behalf — the profile
|
|
104
|
+
* page at sign-up, the new profile page of a rename — so the job's
|
|
105
|
+
* `page-create`, granted in policy, decides the write, and the page is
|
|
106
|
+
* still the user's (author-lock leaves it to them). Refused from any context
|
|
107
|
+
* but the system principal's job, and refused for a page that already
|
|
108
|
+
* exists (`PageWriteRefusedError`, `page-create`): the job never edits
|
|
109
|
+
* that page instead.
|
|
110
|
+
*/
|
|
111
|
+
createdFor?: string;
|
|
112
|
+
/**
|
|
113
|
+
* The author of a page an add-on pushes (#2255): `addon:<slug>`. A new copy
|
|
114
|
+
* is authored by the add-on, and a copy seeded before #2255, authored by
|
|
115
|
+
* the system principal, is re-stamped with it. Taken only from the system
|
|
116
|
+
* principal's job and only in the `addon:<slug>` form; ignored otherwise.
|
|
117
|
+
* Any other author is kept, as on every edit.
|
|
118
|
+
*/
|
|
119
|
+
shippedAuthor?: string;
|
|
100
120
|
/**
|
|
101
121
|
* Request-level detail the manager cannot see, for the audit record (#1121).
|
|
102
122
|
*
|
|
@@ -199,11 +219,25 @@ export declare class PageAudienceRefusedError extends Error {
|
|
|
199
219
|
readonly pageName: string;
|
|
200
220
|
constructor(pageName: string);
|
|
201
221
|
}
|
|
222
|
+
/** The permission a page write is decided on (#1815). */
|
|
223
|
+
export type PageWriteAction = 'page-create' | 'page-edit' | 'page-delete' | 'page-rename';
|
|
224
|
+
/**
|
|
225
|
+
* A write refused by the write door (#1815): the context may not create,
|
|
226
|
+
* edit or delete this page, decided by policy, the page's own rules and the
|
|
227
|
+
* vault container rule. Nothing was written. Routes answer it with 403. The
|
|
228
|
+
* message names no page, so a private title never reaches a log (#2178).
|
|
229
|
+
*/
|
|
230
|
+
export declare class PageWriteRefusedError extends Error {
|
|
231
|
+
readonly pageName: string;
|
|
232
|
+
readonly action: PageWriteAction;
|
|
233
|
+
constructor(pageName: string, action: PageWriteAction);
|
|
234
|
+
}
|
|
202
235
|
export declare class PageContentValidationError extends Error {
|
|
203
236
|
readonly validationErrors: FilterValidationError[];
|
|
204
237
|
constructor(pageName: string, validationErrors: FilterValidationError[]);
|
|
205
238
|
}
|
|
206
239
|
import { type PageAction } from '../utils/pageUrl.js';
|
|
240
|
+
import { type VaultSealState } from '../utils/privateStoreMeta.js';
|
|
207
241
|
/** One vault page with data outside its vault (#1842), as the repair actions offer it. */
|
|
208
242
|
export interface VaultRepairItem {
|
|
209
243
|
owner: string;
|
|
@@ -261,6 +295,9 @@ export type VaultRepairResult = ({
|
|
|
261
295
|
/** The move of existing captures into the capture vault (#1505), recorded per vault it moved them out of. */
|
|
262
296
|
/** The refusal when someone without `admin-system` names a system-category (#1503, operator 2026-10-29). */
|
|
263
297
|
export declare const SYSTEM_CATEGORY_ADMIN_ONLY = "Only administrators can set a page's system category";
|
|
298
|
+
/** The system-category of a page in a shared folder (#2255): given when it is created there, by nothing else. */
|
|
299
|
+
export declare const SHARED_CATEGORY = "shared";
|
|
300
|
+
export declare const SHARED_CATEGORY_REFUSAL = "A page is in the shared system category only when it is created in a shared folder";
|
|
264
301
|
/** What a write may give a page as its system-category ({@link PageManager.systemCategoryForWrite}). */
|
|
265
302
|
export type SystemCategoryForWrite = {
|
|
266
303
|
ok: true;
|
|
@@ -327,6 +364,11 @@ export interface ShippedPageSource {
|
|
|
327
364
|
exclude?: (data: Record<string, unknown>) => string | undefined;
|
|
328
365
|
/** Metadata the source adds to every page it writes (e.g. `addon`, a default category) */
|
|
329
366
|
extraMetadata?: (data: Record<string, unknown>) => Record<string, unknown>;
|
|
367
|
+
/**
|
|
368
|
+
* The author of the pages it writes, for an add-on's (#2255): `addon:<slug>`
|
|
369
|
+
* (`SavePageOptions.shippedAuthor`). Absent, they are the system principal's.
|
|
370
|
+
*/
|
|
371
|
+
author?: string;
|
|
330
372
|
/**
|
|
331
373
|
* Called by the boot seed for a source page this site already holds, with the
|
|
332
374
|
* live page and the parsed source — the addon seed's metadata tidy-ups and
|
|
@@ -622,8 +664,10 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
622
664
|
private readShippedSystemCategories;
|
|
623
665
|
/**
|
|
624
666
|
* Which system-category a write by `ctx` gives a page (#1503, operator
|
|
625
|
-
* 2026-10-29): only
|
|
626
|
-
* or changes one.
|
|
667
|
+
* 2026-10-29): only a holder of `page-categorize` (#2255; shipped to admin)
|
|
668
|
+
* sets or changes one. `shared` is set by nobody: a page created in a shared
|
|
669
|
+
* folder gets it at the write door (`savePage`), and a request naming it for
|
|
670
|
+
* any other page is refused, `page-categorize` or not. Anyone else's new page takes `fallback` (else the default
|
|
627
671
|
* system-category) and an existing page keeps its own (`current`); a request
|
|
628
672
|
* naming any other is refused, never rewritten. An administrator's request
|
|
629
673
|
* comes back as given (`undefined` when it names none), for the caller's own
|
|
@@ -644,16 +688,10 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
644
688
|
* taken for a shipped one. Shipped pages are admin-only and never private.
|
|
645
689
|
*/
|
|
646
690
|
isShippedPageCopy(uuid: string): Promise<boolean>;
|
|
647
|
-
/**
|
|
648
|
-
* One-time access backfill for a live required page (#1411): a page with no
|
|
649
|
-
* `access` gets the default for its category, as a metadata-only save that
|
|
650
|
-
* keeps `lastModified`. A page that has any `access` — an operator's choice —
|
|
651
|
-
* is left alone.
|
|
652
|
-
*/
|
|
653
|
-
private backfillShippedPageAccess;
|
|
654
691
|
/**
|
|
655
692
|
* The source for the pages an addon ships (#1406). Pages it writes carry
|
|
656
|
-
* `addon
|
|
693
|
+
* `addon`, the add-on as author (`addon:<slug>`, #2255) and, when the source
|
|
694
|
+
* names none, the `addon` category (#931).
|
|
657
695
|
*
|
|
658
696
|
* @param addonName - The addon's name
|
|
659
697
|
* @param pagesDir - The addon's `pages/` folder
|
|
@@ -926,6 +964,82 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
926
964
|
* @throws when `ctx` may not file into the page's folder
|
|
927
965
|
*/
|
|
928
966
|
private assertMayFileInSharedFolder;
|
|
967
|
+
/**
|
|
968
|
+
* May `ctx` edit or delete this existing page (#1815)? The page decision
|
|
969
|
+
* every route asks (`canUserAccessPage`), with the page's metadata, so the
|
|
970
|
+
* delegation ceilings, the vault container rule, author-lock, the page's own
|
|
971
|
+
* rules and policy all count, in that one evaluator. `record` false asks it
|
|
972
|
+
* as an affordance, which records nothing (#1668). Without a PIP nothing is
|
|
973
|
+
* writable.
|
|
974
|
+
*/
|
|
975
|
+
private mayChangeExisting;
|
|
976
|
+
/**
|
|
977
|
+
* May `ctx` create a page at `destination` (#1815)? In a vault, the
|
|
978
|
+
* container rule and then `page-create` with the vault (`permitsInVault`;
|
|
979
|
+
* a shared folder by its own permission). A public page, `page-create`
|
|
980
|
+
* asked of the PDP about the page (#2255: the PDP reads its attributes from
|
|
981
|
+
* the PIP). The same rule as `hasPermissionOn`.
|
|
982
|
+
*
|
|
983
|
+
* @returns `permit`, and the `policyIds` of the `page-create` decision when
|
|
984
|
+
* one was made (#2272); a vault page is decided by the container rule and
|
|
985
|
+
* `permitsInVault`, which name none.
|
|
986
|
+
*/
|
|
987
|
+
private decideCreateAt;
|
|
988
|
+
/**
|
|
989
|
+
* Where a save puts the page (#1815, decision 7): the private name the
|
|
990
|
+
* provider will store it under, or its public title. Read from the page's
|
|
991
|
+
* own `private` / `store` frontmatter, the rule the provider's placement
|
|
992
|
+
* follows: a private name stays private unless the save says
|
|
993
|
+
* `private: false`; a public title goes into a vault when it says
|
|
994
|
+
* `private: true`, the owner's and the store it names, else the vault the
|
|
995
|
+
* page is in now, else the default vault.
|
|
996
|
+
*
|
|
997
|
+
* @param owner - The username whose vault a public title would go into: the
|
|
998
|
+
* page's author (for a new page, whoever it is made by or for)
|
|
999
|
+
*/
|
|
1000
|
+
private writeDestination;
|
|
1001
|
+
/** The container a page name is in: its vault, or the public space. */
|
|
1002
|
+
private static containerOf;
|
|
1003
|
+
/**
|
|
1004
|
+
* The write door's decision for a save (#1815, operator 2026-11-04). An
|
|
1005
|
+
* existing page is edited only by a context the page decision lets edit it
|
|
1006
|
+
* ({@link mayChangeExisting}); a page arriving in a container it is not in
|
|
1007
|
+
* now — a new page, or a move into or out of a vault — is also created
|
|
1008
|
+
* there ({@link decideCreateAt}). Whatever route the write came by: the route's
|
|
1009
|
+
* own check is an early refusal, this is the one that counts.
|
|
1010
|
+
*
|
|
1011
|
+
* @throws PageWriteRefusedError when either is refused; nothing is written
|
|
1012
|
+
*/
|
|
1013
|
+
private assertMaySave;
|
|
1014
|
+
/**
|
|
1015
|
+
* Record a refused `page-create` as `authorization-deny` (#1815, security
|
|
1016
|
+
* review L1). The edit and delete refusals are recorded by the page
|
|
1017
|
+
* decision (`canUserAccessPage`); a create is decided by the PDP, which
|
|
1018
|
+
* records nothing, so the door records it here: core records every refusal.
|
|
1019
|
+
* Standard tier, `continue` on failure: the refusal stands either way.
|
|
1020
|
+
*
|
|
1021
|
+
* @param policyIds - The policies of the `page-create` decision that refused
|
|
1022
|
+
* (#2272). Only a refusal that decision made passes any; one no policy
|
|
1023
|
+
* decided (`createdFor` misuse) leaves the field off the record.
|
|
1024
|
+
* @param conditionNotMet - Allows of that decision that matched but whose condition was not met (#2288).
|
|
1025
|
+
*/
|
|
1026
|
+
private recordCreateRefusal;
|
|
1027
|
+
/** `shippedAuthor` when the system principal's job gives it in the `addon:<slug>` form (#2255); otherwise nothing. */
|
|
1028
|
+
private shippedAuthorFor;
|
|
1029
|
+
/** Is `ctx` a job of the system principal (#631) — the only context that may make a page for someone else (#1815)? */
|
|
1030
|
+
private isSystemJob;
|
|
1031
|
+
/** The username a page's stored author names (#2097), or null. */
|
|
1032
|
+
private authorUsername;
|
|
1033
|
+
/**
|
|
1034
|
+
* Would `ctx` be allowed to edit this page — asked only to decide what to
|
|
1035
|
+
* offer or report, never before writing it (#1815). The same decision as
|
|
1036
|
+
* the write door, as an affordance: it records nothing (#1668). Saving still
|
|
1037
|
+
* asks the door.
|
|
1038
|
+
*
|
|
1039
|
+
* @param identifier - Page UUID, title, slug or vault name
|
|
1040
|
+
* @param ctx - Whose question this is — never defaulted
|
|
1041
|
+
*/
|
|
1042
|
+
mayEditPage(identifier: string, ctx: ActorContext): Promise<boolean>;
|
|
929
1043
|
/**
|
|
930
1044
|
* Admin-override save (#689): the admin "Edit raw" textarea, split into
|
|
931
1045
|
* frontmatter and body and written through the door (#1462).
|
|
@@ -975,7 +1089,7 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
975
1089
|
* What a page stores for the user `name` (#2097): `IdentityManager.storedReferenceOf`.
|
|
976
1090
|
*/
|
|
977
1091
|
private referenceTo;
|
|
978
|
-
/** `audience`
|
|
1092
|
+
/** The `audience` of a save, user entries as {@link referenceTo} stores them (#2097). */
|
|
979
1093
|
private convertAccessListsForSave;
|
|
980
1094
|
/**
|
|
981
1095
|
* The frontmatter a save writes, from the frontmatter it was given (#1462).
|
|
@@ -994,6 +1108,7 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
994
1108
|
* @param actingUser - Who is writing this revision, from the context (#1164)
|
|
995
1109
|
* @param viaToken - The agent token this write came through (#946), if any
|
|
996
1110
|
* @param keepEditor - Leave the stored `editor` alone (`preserveEditor`, #1457)
|
|
1111
|
+
* @param shippedAuthor - An add-on page's author (`shippedAuthor`, #2255), already checked
|
|
997
1112
|
* @returns The frontmatter to hand the provider
|
|
998
1113
|
*/
|
|
999
1114
|
private normalizeSaveMetadata;
|
|
@@ -1009,7 +1124,9 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
1009
1124
|
*
|
|
1010
1125
|
* `ctx` is mandatory and positional: the save acts as the caller's subject
|
|
1011
1126
|
* (#1179), a private-store write reaches that session's keys through it
|
|
1012
|
-
* (#1382), and the audit record names it.
|
|
1127
|
+
* (#1382), and the audit record names it. The write is decided here, for
|
|
1128
|
+
* that context, before anything is written (#1815): `page-edit` on the page
|
|
1129
|
+
* as it is, `page-create` where it is going when that is somewhere new. A caller forwards the context it
|
|
1013
1130
|
* was given — a request's subject, or the job's for boot and scheduled
|
|
1014
1131
|
* work — and never invents one.
|
|
1015
1132
|
*
|
|
@@ -1020,6 +1137,7 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
1020
1137
|
* @param options - Validation and conflict-check opt-outs, `rawFrontmatter` (#689),
|
|
1021
1138
|
* `preserveLastModified`, `preserveEditor` (#1457), audit enrichment
|
|
1022
1139
|
* @returns Where the page landed, and what it was called before
|
|
1140
|
+
* @throws PageWriteRefusedError when `ctx` may not make this write
|
|
1023
1141
|
*
|
|
1024
1142
|
* @example
|
|
1025
1143
|
* await pageManager.savePage('New Page', '# Hello World', { 'user-keywords': ['tutorial'] }, req.userContext);
|
|
@@ -1108,8 +1226,37 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
1108
1226
|
title: string;
|
|
1109
1227
|
uuid: string;
|
|
1110
1228
|
}>>;
|
|
1229
|
+
/**
|
|
1230
|
+
* Every public page stamped with one system-category (#2299), by the name
|
|
1231
|
+
* `mayShowPage` takes. Vault pages are not listed: they are their owners', not
|
|
1232
|
+
* an administrator's to be told about. Says nothing about who may see a page;
|
|
1233
|
+
* the caller asks `mayShowPage` as the reader.
|
|
1234
|
+
*
|
|
1235
|
+
* @param category - The category's key or label, any case
|
|
1236
|
+
*/
|
|
1237
|
+
listCategoryPages(category: string): Promise<string[]>;
|
|
1111
1238
|
/** The store ids the requester has of their own (#1387). */
|
|
1112
1239
|
listOwnStoreIds(ctx: ActorContext): Promise<string[]>;
|
|
1240
|
+
/**
|
|
1241
|
+
* The requester's own vaults, each with its pages and whether it is
|
|
1242
|
+
* encrypted (#2245), for `/my/vaults`.
|
|
1243
|
+
*
|
|
1244
|
+
* `state` is the vault's {@link VaultSealState} (#1834): `sealed` is
|
|
1245
|
+
* encrypted, `plain` is not, and `unopened` or `mismatch` is a vault whose
|
|
1246
|
+
* kind says it must be encrypted while its copy is not. `readable` is false
|
|
1247
|
+
* for a vault this context cannot open, such as a sealed vault still
|
|
1248
|
+
* locked: its pages are then unknown, not none.
|
|
1249
|
+
*/
|
|
1250
|
+
listOwnVaults(ctx: ActorContext): Promise<Array<{
|
|
1251
|
+
id: string;
|
|
1252
|
+
state: VaultSealState;
|
|
1253
|
+
readable: boolean;
|
|
1254
|
+
pages: Array<{
|
|
1255
|
+
name: string;
|
|
1256
|
+
title: string;
|
|
1257
|
+
uuid: string;
|
|
1258
|
+
}>;
|
|
1259
|
+
}>>;
|
|
1113
1260
|
listOwnDeletedPrivatePages(ctx: ActorContext): Promise<StoreDeletedEntry[]>;
|
|
1114
1261
|
/**
|
|
1115
1262
|
* Bring one of the requester's private pages back from its store's trash
|
|
@@ -1233,14 +1380,25 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
1233
1380
|
* and the context names who put it there. Afterwards the page is taken out
|
|
1234
1381
|
* of every shared index, here and nowhere else (#1462).
|
|
1235
1382
|
*
|
|
1383
|
+
* The write door decides `page-delete` on the page first (#1815), and the
|
|
1384
|
+
* delete is recorded as `page-delete` before it happens.
|
|
1385
|
+
*
|
|
1236
1386
|
* @param identifier - Page UUID, title, or slug
|
|
1237
1387
|
* @param ctx - Who is acting (#1179)
|
|
1388
|
+
* @param options - `audit.skip` when the caller already recorded this
|
|
1389
|
+
* delete; `audit.ipAddress`, the request's address, for the record
|
|
1238
1390
|
* @returns True if deleted, false if not found
|
|
1391
|
+
* @throws PageWriteRefusedError when `ctx` may not delete the page
|
|
1239
1392
|
*
|
|
1240
1393
|
* @example
|
|
1241
1394
|
* const deleted = await pageManager.deletePage('Old Page', req.userContext);
|
|
1242
1395
|
*/
|
|
1243
|
-
deletePage(identifier: string, ctx: ActorContext
|
|
1396
|
+
deletePage(identifier: string, ctx: ActorContext, options?: {
|
|
1397
|
+
audit?: {
|
|
1398
|
+
skip?: boolean;
|
|
1399
|
+
ipAddress?: string;
|
|
1400
|
+
};
|
|
1401
|
+
}): Promise<boolean>;
|
|
1244
1402
|
/**
|
|
1245
1403
|
* Delete the pages a test made (#2008): every shared page carrying the
|
|
1246
1404
|
* `test-artifact` system keyword (#1355), and never a page without it.
|
|
@@ -1253,7 +1411,8 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
1253
1411
|
* follow. One page's failure never stops the rest; it is reported.
|
|
1254
1412
|
*
|
|
1255
1413
|
* The caller decides who may ask: the sweep route asks `admin-system`, and
|
|
1256
|
-
* an account delete has already been decided as `user-delete
|
|
1414
|
+
* an account delete has already been decided as `user-delete` and sends a
|
|
1415
|
+
* system job (#1815). Each delete is still decided by `deletePage`. Private
|
|
1257
1416
|
* vault pages are not in the shared index, so they are not reached here.
|
|
1258
1417
|
*
|
|
1259
1418
|
* @param ctx - Who is acting; named in every record
|
|
@@ -1502,7 +1661,7 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
1502
1661
|
private warnUnmatchableAddonAudience;
|
|
1503
1662
|
/**
|
|
1504
1663
|
* At startup, once add-ons have loaded (#1964): warn about every page whose
|
|
1505
|
-
* `audience`
|
|
1664
|
+
* `audience` names an add-on that can match nobody — unknown,
|
|
1506
1665
|
* not per-person, or not loaded.
|
|
1507
1666
|
*
|
|
1508
1667
|
* @returns Each page and slug warned about
|
|
@@ -1617,8 +1776,10 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
1617
1776
|
*
|
|
1618
1777
|
* `pages/vaults/{user}/` and every store below it is owned by that user.
|
|
1619
1778
|
* Nobody else acts in it unless the owner delegated (a share the owner
|
|
1620
|
-
* issued, once the store's Share switch exists — #1388). No role reaches in
|
|
1621
|
-
*
|
|
1779
|
+
* issued, once the store's Share switch exists — #1388). No role reaches in
|
|
1780
|
+
* (security-posture P2: no `hasRole` as an allow); at a door, a holder of
|
|
1781
|
+
* `admin-system` may enter an unencrypted vault when
|
|
1782
|
+
* `ngdpbase.vaults.admin-access` is on (#2255), recorded each time.
|
|
1622
1783
|
*
|
|
1623
1784
|
* The owner is the one in the page's name (#1456), not frontmatter
|
|
1624
1785
|
* `author`, so reassigning `author` cannot move ownership (#711).
|
|
@@ -1629,7 +1790,7 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
1629
1790
|
* - `false` — a private name, and the caller is neither — whether or not
|
|
1630
1791
|
* the page exists
|
|
1631
1792
|
*/
|
|
1632
|
-
checkPrivatePageAccess(wikiContext: WikiContext, pageNameOrUuid: string): Promise<boolean | null>;
|
|
1793
|
+
checkPrivatePageAccess(wikiContext: WikiContext, pageNameOrUuid: string, adminEntry?: 'record' | 'quiet' | 'none'): Promise<boolean | null>;
|
|
1633
1794
|
/**
|
|
1634
1795
|
* Owner and store of a private page, or `null` when the page is not private
|
|
1635
1796
|
* or does not exist (#1398). The owner owns the page and every attachment
|
|
@@ -1686,6 +1847,15 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
1686
1847
|
* Best-effort: a failure is logged and never blocks start-up. An encrypted
|
|
1687
1848
|
* store is not readable here and is not touched — it migrates at its
|
|
1688
1849
|
* owner's unlock, through {@link migratePrivateLinks}.
|
|
1850
|
+
*
|
|
1851
|
+
* #1815 (operator, 2026-11-04, decision 2): the public pages are the system
|
|
1852
|
+
* principal's work; each user's vaults are a job on behalf of that user
|
|
1853
|
+
* ({@link jobContextForOwner}), through the write door like any write. The
|
|
1854
|
+
* vault container rule admits a job carrying its owner, and the owner's
|
|
1855
|
+
* rights in their own vaults come from policy, resolved when each write is
|
|
1856
|
+
* decided. The system principal is let into no vault. A shared folder is
|
|
1857
|
+
* not a user's and opens to no boot job (its key never reaches one), so it
|
|
1858
|
+
* is not visited.
|
|
1689
1859
|
*/
|
|
1690
1860
|
migratePrivateLinksAtBoot(): Promise<void>;
|
|
1691
1861
|
/**
|
|
@@ -1799,8 +1969,9 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
1799
1969
|
* keeps its `lastModified` and `editor`, since nobody edited it. The audit
|
|
1800
1970
|
* record is a rename, from the old path to the new.
|
|
1801
1971
|
*
|
|
1802
|
-
* Access is
|
|
1803
|
-
*
|
|
1972
|
+
* Access is decided by the save it makes, as for every write (#1815): an
|
|
1973
|
+
* edit of the page where it is and a create in the vault it goes to. A
|
|
1974
|
+
* page is never moved out of an encrypted vault
|
|
1804
1975
|
* into one that is not, since that would write it in the clear.
|
|
1805
1976
|
*
|
|
1806
1977
|
* @param pageName - The page's private name, `vaults/{owner}/{vault}/{title}`
|
|
@@ -1823,7 +1994,7 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
1823
1994
|
*
|
|
1824
1995
|
* @returns How many pages moved
|
|
1825
1996
|
*/
|
|
1826
|
-
moveCapturesToCaptureVault(ctx: ActorContext): Promise<number>;
|
|
1997
|
+
moveCapturesToCaptureVault(ctx: ActorContext, owner?: string): Promise<number>;
|
|
1827
1998
|
/**
|
|
1828
1999
|
* Move private pages made before #1505 into their system-category's own
|
|
1829
2000
|
* vault (#1507). Until then every private page went to its owner's default
|
|
@@ -1838,7 +2009,7 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
1838
2009
|
*
|
|
1839
2010
|
* @returns How many pages moved
|
|
1840
2011
|
*/
|
|
1841
|
-
movePagesToCategoryVaults(ctx: ActorContext): Promise<number>;
|
|
2012
|
+
movePagesToCategoryVaults(ctx: ActorContext, owner?: string): Promise<number>;
|
|
1842
2013
|
/**
|
|
1843
2014
|
* Every private slug in the `vaults--…` form (#1507), once per vault. A
|
|
1844
2015
|
* private page's slug is made fresh on every save, so each page whose slug
|
|
@@ -1848,7 +2019,7 @@ declare class PageManager extends BaseManager implements CatalogSource {
|
|
|
1848
2019
|
*
|
|
1849
2020
|
* @returns How many pages were re-saved
|
|
1850
2021
|
*/
|
|
1851
|
-
migratePrivateSlugs(ctx: ActorContext): Promise<number>;
|
|
2022
|
+
migratePrivateSlugs(ctx: ActorContext, owner?: string): Promise<number>;
|
|
1852
2023
|
/**
|
|
1853
2024
|
* One pass of a vault move: every page in the vaults `ctx` can open is
|
|
1854
2025
|
* offered to `pick`, which names the vault it goes to (and any frontmatter
|