@jwilleke/ngdpbase-types 5.39.0 → 5.41.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 (99) hide show
  1. package/lib/packages/ngdpbase-types/index.d.ts +1 -1
  2. package/lib/src/context/ApiContext.d.ts +3 -2
  3. package/lib/src/context/ApiContext.js +4 -2
  4. package/lib/src/ice/fileHandling.d.ts +6 -1
  5. package/lib/src/ice/givenName.d.ts +19 -0
  6. package/lib/src/ice/runExport.d.ts +41 -0
  7. package/lib/src/ice/runImport.d.ts +37 -8
  8. package/lib/src/ice/shims/csv.d.ts +24 -0
  9. package/lib/src/ice/shims/export.d.ts +23 -0
  10. package/lib/src/ice/shims/index.d.ts +3 -1
  11. package/lib/src/ice/shims/svg.d.ts +27 -0
  12. package/lib/src/ice/shims/takeout.d.ts +83 -0
  13. package/lib/src/ice/types.d.ts +301 -5
  14. package/lib/src/ice/vet.d.ts +19 -0
  15. package/lib/src/managers/AttachmentManager.d.ts +100 -36
  16. package/lib/src/managers/AuthManager.d.ts +152 -12
  17. package/lib/src/managers/AuthManager.js +3830 -0
  18. package/lib/src/managers/BackgroundJobManager.d.ts +25 -0
  19. package/lib/src/managers/BackupManager.d.ts +24 -0
  20. package/lib/src/managers/BackupManager.js +48 -1
  21. package/lib/src/managers/ExportManager.d.ts +16 -0
  22. package/lib/src/managers/ExportManager.js +25 -0
  23. package/lib/src/managers/FileManager.d.ts +182 -0
  24. package/lib/src/managers/ImportManager.d.ts +279 -16
  25. package/lib/src/managers/MediaManager.d.ts +2 -0
  26. package/lib/src/managers/PageManager.d.ts +53 -0
  27. package/lib/src/managers/SearchManager.d.ts +4 -0
  28. package/lib/src/managers/SharedFolderManager.d.ts +320 -0
  29. package/lib/src/managers/ValidationManager.d.ts +5 -2
  30. package/lib/src/middleware/csrf.js +99 -0
  31. package/lib/src/providers/AgentTokenAuthProvider.js +46 -0
  32. package/lib/src/providers/AuthentikBearerAuthProvider.js +176 -0
  33. package/lib/src/providers/BaseCredentialsProvider.js +21 -0
  34. package/lib/src/providers/BaseMediaProvider.d.ts +4 -0
  35. package/lib/src/providers/BasePageProvider.d.ts +8 -0
  36. package/lib/src/providers/CloudflareAccessAuthProvider.js +160 -0
  37. package/lib/src/providers/FileCredentialsProvider.js +188 -0
  38. package/lib/src/providers/FileSystemMediaProvider.d.ts +6 -0
  39. package/lib/src/providers/MagicLinkAuthProvider.js +333 -0
  40. package/lib/src/providers/OidcAuthProvider.js +350 -0
  41. package/lib/src/providers/OidcBearerAuthProvider.js +27 -0
  42. package/lib/src/providers/PasskeyAuthProvider.js +147 -0
  43. package/lib/src/providers/PasswordAuthProvider.js +44 -0
  44. package/lib/src/security/PolicyDecisionPoint.d.ts +3 -2
  45. package/lib/src/security/pageActions.js +43 -0
  46. package/lib/src/security/permissions.generated.d.ts +7 -3
  47. package/lib/src/types/File.d.ts +90 -0
  48. package/lib/src/types/Provider.d.ts +15 -0
  49. package/lib/src/types/express.d.ts +7 -1
  50. package/lib/src/types/guards.js +366 -0
  51. package/lib/src/types/linkedom.d.ts +2 -0
  52. package/lib/src/utils/atomicWrite.js +129 -0
  53. package/lib/src/utils/auditEventNames.generated.d.ts +12 -0
  54. package/lib/src/utils/auditEventNames.generated.js +12 -0
  55. package/lib/src/utils/auditEvents.d.ts +15 -0
  56. package/lib/src/utils/auditEvents.js +17 -1
  57. package/lib/src/utils/bip39English.js +2 -0
  58. package/lib/src/utils/browserDescription.js +28 -0
  59. package/lib/src/utils/delegation.js +79 -0
  60. package/lib/src/utils/deriveUsername.js +38 -0
  61. package/lib/src/utils/emailForLog.js +28 -0
  62. package/lib/src/utils/fileMigrationPlan.d.ts +81 -0
  63. package/lib/src/utils/instanceEnvBackup.d.ts +6 -0
  64. package/lib/src/utils/instanceEnvBackup.js +13 -0
  65. package/lib/src/utils/ipLocation.js +106 -0
  66. package/lib/src/utils/magicLinkDeviceState.js +97 -0
  67. package/lib/src/utils/pageUrl.d.ts +1 -0
  68. package/lib/src/utils/passwordHash.js +150 -0
  69. package/lib/src/utils/permissionCatalog.js +102 -0
  70. package/lib/src/utils/privateStoreAccess.d.ts +25 -0
  71. package/lib/src/utils/privateStoreAccess.js +22 -1
  72. package/lib/src/utils/privateStoreCatalogs.js +57 -0
  73. package/lib/src/utils/privateStoreCrypto.js +266 -0
  74. package/lib/src/utils/privateStoreDoor.d.ts +10 -0
  75. package/lib/src/utils/privateStoreDoor.js +298 -0
  76. package/lib/src/utils/privateStoreImport.d.ts +0 -10
  77. package/lib/src/utils/privateStoreMeta.js +123 -0
  78. package/lib/src/utils/privateStorePath.d.ts +37 -1
  79. package/lib/src/utils/privateStorePath.js +60 -3
  80. package/lib/src/utils/privateStoreTakeout.js +149 -0
  81. package/lib/src/utils/privateStoreUnlock.d.ts +3 -0
  82. package/lib/src/utils/privateStoreUnlock.js +499 -0
  83. package/lib/src/utils/recoveryWordsFile.d.ts +54 -0
  84. package/lib/src/utils/safeRedirect.js +55 -0
  85. package/lib/src/utils/sessionAccount.js +55 -0
  86. package/lib/src/utils/sessionEnd.d.ts +1 -0
  87. package/lib/src/utils/sessionEnd.js +42 -0
  88. package/lib/src/utils/sessionGeneration.js +29 -0
  89. package/lib/src/utils/sessionIdle.d.ts +4 -0
  90. package/lib/src/utils/sessionIdle.js +72 -0
  91. package/lib/src/utils/sessionLimits.js +72 -0
  92. package/lib/src/utils/sessionSecurity.js +122 -0
  93. package/lib/src/utils/shareLockbox.js +101 -0
  94. package/lib/src/utils/sharedFolderAccess.d.ts +57 -0
  95. package/lib/src/utils/sharedFolderAccess.js +80 -0
  96. package/lib/src/utils/userCreateError.js +82 -0
  97. package/lib/src/utils/username.d.ts +2 -0
  98. package/lib/src/utils/username.js +105 -0
  99. package/package.json +2 -1
@@ -77,6 +77,23 @@ export interface ScheduledJobView {
77
77
  /** History, newest first. A run with no slot was started by hand. */
78
78
  runs: JobRunRecord[];
79
79
  }
80
+ /** A run that ended, as the /admin dashboard shows it (#2138). */
81
+ export interface LastJobRun {
82
+ startedAt: string;
83
+ completedAt: string;
84
+ status: 'completed' | 'failed';
85
+ /** The username who asked; null for a run its schedule started. */
86
+ ranBy: string | null;
87
+ }
88
+ /** A job's newest ended run (#2138), from {@link BackgroundJobManager.lastFinishedRun}. */
89
+ export interface JobLastRun {
90
+ jobId: string;
91
+ displayName: string;
92
+ /** Null when it has never run — or, with `sinceStart`, not since this server started. */
93
+ lastRun: LastJobRun | null;
94
+ /** The job's history is kept in memory only (a maintenance tick, or no job state open): it starts empty at each start. */
95
+ sinceStart: boolean;
96
+ }
80
97
  /**
81
98
  * A job type that can be registered with the BackgroundJobManager.
82
99
  */
@@ -343,6 +360,14 @@ declare class BackgroundJobManager extends BaseManager {
343
360
  * job's history.
344
361
  */
345
362
  scheduledJobView(jobId: string): Promise<ScheduledJobView | null>;
363
+ /**
364
+ * The newest run of a job that ended, completed or failed, and who asked
365
+ * for it (#2138) — what the /admin dashboard shows under Reindex and
366
+ * Rebuild. Read from the history already kept: the job state, and this
367
+ * process's own runs for what the state does not hold (a maintenance tick,
368
+ * or no state open). Null when no job of that id is registered.
369
+ */
370
+ lastFinishedRun(jobId: string): Promise<JobLastRun | null>;
346
371
  private viewOf;
347
372
  private scheduledOrThrow;
348
373
  /** Refused while the job runs, waits for a retry, or the server is stopping: one run of a job at a time. */
@@ -322,6 +322,18 @@ export declare function longestGapMs(schedule: Schedule, from: Date): number;
322
322
  * said nothing.
323
323
  */
324
324
  export declare function autoBackupSchedule(time: string, days: string, timeZone: string): ScheduleInput;
325
+ /**
326
+ * The backup key file (#2134): what it is, for which site, the key's
327
+ * fingerprint, when it was saved (the server's local time), how to restore
328
+ * with it, and the one `NGDPBASE_BACKUP_KEY=` line, written so the boot's
329
+ * `.env` reader reads the key back exactly.
330
+ */
331
+ export declare function backupKeyFileText(input: {
332
+ site: string;
333
+ fingerprint: string;
334
+ key: string;
335
+ saved: string;
336
+ }): string;
325
337
  /**
326
338
  * The text of the critical "not restored" notification (#1876): what the
327
339
  * restore put back, what it did not, and per job the runs since the backup —
@@ -483,6 +495,18 @@ declare class BackupManager extends BaseManager {
483
495
  * sign-in; nothing else shows, logs or returns it.
484
496
  */
485
497
  revealBackupKey(): string | null;
498
+ /**
499
+ * The backup key as a small text file, for an admin to keep off the server
500
+ * without the rest of `.env` (#2134). The route asks `config-manage`, with
501
+ * its fresh sign-in. Recorded as `backup-key-download` (who and the
502
+ * fingerprint, never the key) before the file is made: on-failure refuse,
503
+ * so a record that cannot be written throws and nothing is sent. Null when
504
+ * the site has no backup key. Nothing is kept on the server.
505
+ */
506
+ revealBackupKeyFile(ctx: ActorContext): Promise<{
507
+ fingerprint: string;
508
+ text: string;
509
+ } | null>;
486
510
  /**
487
511
  * The instance `.env` exactly as it is on disk, backup key line included,
488
512
  * for an admin to keep a copy off the server (#1938). The route asks
@@ -15,7 +15,7 @@ import { BACKUP_KEY_ENV, backupKeyFingerprint, currentBackupKey, isLeftOutForNoK
15
15
  import { currentSharedFoldersKey, SHARED_FOLDERS_KEY_ENV, sharedFoldersKeyFingerprint } from '../utils/sharedFoldersKey.js';
16
16
  import { applyDeclaredPartsAtStart, archiveReader, archiveTarget, archiveWriter, backupDeclaredParts, placePartRecords, stageDeclaredParts, stageFilePart } from '../utils/directoryBackup.js';
17
17
  import { writeTreeArchive } from '../utils/treeArchive.js';
18
- import { envForBackup, envKeyNames, envKeyNamesFingerprint, INSTANCE_ENV_PART, instanceEnvPath } from '../utils/instanceEnvBackup.js';
18
+ import { envFileLine, envForBackup, envKeyNames, envKeyNamesFingerprint, INSTANCE_ENV_PART, instanceEnvPath } from '../utils/instanceEnvBackup.js';
19
19
  import { readEnvLine } from '../utils/instanceEnvSecret.js';
20
20
  import { systemContext } from '../context/bootActions.js';
21
21
  import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
@@ -315,6 +315,24 @@ function localTime(iso) {
315
315
  const t = iso ? Date.parse(iso) : NaN;
316
316
  return Number.isFinite(t) ? new Date(t).toLocaleString() : 'an unknown time';
317
317
  }
318
+ /**
319
+ * The backup key file (#2134): what it is, for which site, the key's
320
+ * fingerprint, when it was saved (the server's local time), how to restore
321
+ * with it, and the one `NGDPBASE_BACKUP_KEY=` line, written so the boot's
322
+ * `.env` reader reads the key back exactly.
323
+ */
324
+ export function backupKeyFileText(input) {
325
+ return [
326
+ '# ngdpbase backup key',
327
+ `# Site: ${input.site}`,
328
+ `# Fingerprint: ${input.fingerprint} (Admin → Backup on the restoring site must show the same)`,
329
+ `# Saved: ${input.saved}`,
330
+ '# Restore: put the line below in the new server\'s .env before it first starts (or replace its key, then restart).',
331
+ '# Keep this file off the server, somewhere as safe as a password manager. Anyone with this file and a backup can read that backup.',
332
+ envFileLine(BACKUP_KEY_ENV, input.key),
333
+ ''
334
+ ].join('\n');
335
+ }
318
336
  /**
319
337
  * The text of the critical "not restored" notification (#1876): what the
320
338
  * restore put back, what it did not, and per job the runs since the backup —
@@ -925,6 +943,35 @@ class BackupManager extends BaseManager {
925
943
  revealBackupKey() {
926
944
  return currentBackupKey() || null;
927
945
  }
946
+ /**
947
+ * The backup key as a small text file, for an admin to keep off the server
948
+ * without the rest of `.env` (#2134). The route asks `config-manage`, with
949
+ * its fresh sign-in. Recorded as `backup-key-download` (who and the
950
+ * fingerprint, never the key) before the file is made: on-failure refuse,
951
+ * so a record that cannot be written throws and nothing is sent. Null when
952
+ * the site has no backup key. Nothing is kept on the server.
953
+ */
954
+ async revealBackupKeyFile(ctx) {
955
+ const key = currentBackupKey();
956
+ if (!key)
957
+ return null;
958
+ const fingerprint = backupKeyFingerprint(key);
959
+ const who = actorOf(ctx);
960
+ await recordAuditEvent(this.engine.getManager('AuditManager'), {
961
+ eventType: AUDIT_EVENT.BACKUP_KEY_DOWNLOAD,
962
+ ...who,
963
+ action: 'backup-key-download',
964
+ result: 'success',
965
+ severity: 'high',
966
+ resource: BACKUP_KEY_ENV,
967
+ resourceType: 'backup-key',
968
+ metadata: { ...who.metadata, fingerprint }
969
+ });
970
+ const name = this.setting('ngdpbase.application-name', '');
971
+ const baseUrl = this.setting('ngdpbase.application.base-url', '');
972
+ const site = (name && baseUrl ? `${name} (${baseUrl})` : name || baseUrl) || 'this site';
973
+ return { fingerprint, text: backupKeyFileText({ site, fingerprint, key, saved: localTime(new Date().toISOString()) }) };
974
+ }
928
975
  /**
929
976
  * The instance `.env` exactly as it is on disk, backup key line included,
930
977
  * for an admin to keep a copy off the server (#1938). The route asks
@@ -128,6 +128,11 @@ declare class ExportManager extends BaseManager {
128
128
  * with the reason, as `none` is.
129
129
  */
130
130
  private pdfProvider;
131
+ /**
132
+ * Is a PDF provider chosen (#2120)? The Export action offers PDF only then.
133
+ * Whether the chosen one works is known only by rendering, as before.
134
+ */
135
+ isPdfConfigured(): boolean;
131
136
  /** Log why, flag the manager (unless something worse is already shown), and refuse. */
132
137
  private pdfUnavailable;
133
138
  private acquirePdfSlot;
@@ -148,6 +153,17 @@ declare class ExportManager extends BaseManager {
148
153
  * @returns {Promise<string>} HTML content
149
154
  */
150
155
  exportPageToHtml(pageName: string, ctx: ActorContext, user?: ExportUser | null): Promise<string>;
156
+ /**
157
+ * One page as a complete HTML document: its content rendered, with the
158
+ * export's styling and metadata footer. It reads nothing: the caller has
159
+ * read the page through its door (the page export source, #2120).
160
+ *
161
+ * @param pageName - The page's name, its heading
162
+ * @param page - Its content and metadata
163
+ * @param user - User object for locale-aware formatting
164
+ * @returns The HTML document
165
+ */
166
+ renderPageDocument(pageName: string, page: PageForExport, user?: ExportUser | null): Promise<string>;
151
167
  /**
152
168
  * Export multiple pages to a single HTML file
153
169
  * @param {string[]} pageNames - Array of page names
@@ -182,6 +182,14 @@ class ExportManager extends BaseManager {
182
182
  return new NonePdfProvider(`ngdpbase.export.pdf.provider '${name}' is not a PDF provider: use ${PDF_PROVIDERS.join(', ')}`);
183
183
  }
184
184
  }
185
+ /**
186
+ * Is a PDF provider chosen (#2120)? The Export action offers PDF only then.
187
+ * Whether the chosen one works is known only by rendering, as before.
188
+ */
189
+ isPdfConfigured() {
190
+ const name = this.engine.getManager('ConfigurationManager')?.getProperty('ngdpbase.export.pdf.provider', PDF_DEFAULTS.provider);
191
+ return (name ?? PDF_DEFAULTS.provider) !== 'none';
192
+ }
185
193
  /** Log why, flag the manager (unless something worse is already shown), and refuse. */
186
194
  pdfUnavailable(reason) {
187
195
  logger.warn(`[ExportManager] PDF export unavailable: ${reason}`);
@@ -248,6 +256,23 @@ class ExportManager extends BaseManager {
248
256
  if (!page) {
249
257
  throw new Error(`Page '${pageName}' not found`);
250
258
  }
259
+ return this.renderPageDocument(pageName, page, user);
260
+ }
261
+ /**
262
+ * One page as a complete HTML document: its content rendered, with the
263
+ * export's styling and metadata footer. It reads nothing: the caller has
264
+ * read the page through its door (the page export source, #2120).
265
+ *
266
+ * @param pageName - The page's name, its heading
267
+ * @param page - Its content and metadata
268
+ * @param user - User object for locale-aware formatting
269
+ * @returns The HTML document
270
+ */
271
+ async renderPageDocument(pageName, page, user = null) {
272
+ const renderingManager = this.engine.getManager('RenderingManager');
273
+ if (!renderingManager) {
274
+ throw new Error('Required managers not available');
275
+ }
251
276
  // Render the page content (without user context for exports)
252
277
  const renderedContent = await renderingManager.renderMarkdown(page.content, pageName, null);
253
278
  // Create full HTML document
@@ -0,0 +1,182 @@
1
+ /**
2
+ * FileManager — the one door for files (#1852, epic #1851).
3
+ *
4
+ * It reads today's attachments as file records (src/types/File.ts): the
5
+ * shared pool as the public place, and every vault the requester may read.
6
+ * It writes through one door, `put` (#2148): a file goes into the place the
7
+ * caller chose, after the permission for that place, with AttachmentManager
8
+ * as the storage underneath; `placesWritableBy` lists the places a user may
9
+ * write to. Rename, move, delete and per-place indexes are later steps
10
+ * (docs/guides/storage-developer-guide.md).
11
+ *
12
+ * Read access is the existing rules, asked at this door:
13
+ * - the public place needs `asset-read`, from the PDP;
14
+ * - a vault is read through AttachmentManager's own vault reads, so the
15
+ * container rule (owner or delegate, never a role, admin included) decides
16
+ * exactly as it does for a vault page's files.
17
+ *
18
+ * The operator inventory (the migration dry run, #1859) is not on the
19
+ * engine's door: only an instance opened by {@link FileManager.openForOperator},
20
+ * from the command line, answers it.
21
+ */
22
+ import BaseManager from './BaseManager.js';
23
+ import { type AttachmentMetadata, type PutFile, type PutOptions, type UploadOptions } from './AttachmentManager.js';
24
+ import { type ActorContext } from '../context/ActorContext.js';
25
+ import { type AttachmentDetail } from '../utils/auditEvents.js';
26
+ import { type AssetRecord } from '../types/Asset.js';
27
+ import type { FileInventory, FilePlace, FileRecord } from '../types/File.js';
28
+ import type { WikiEngine } from '../types/WikiEngine.js';
29
+ import { type FileMigrationReport } from '../utils/fileMigrationPlan.js';
30
+ /**
31
+ * What `put` hands back (#2149): the stored file's metadata, and, when
32
+ * vetting cleaned it, what was removed (one note per kind), so the uploader
33
+ * can be told.
34
+ */
35
+ export type PutResult = AttachmentMetadata & {
36
+ cleaned?: string[];
37
+ };
38
+ /** A place the Upload file dialog offers (#2149): its key in the form, the words the user reads, and the place. */
39
+ export interface UploadPlace {
40
+ key: string;
41
+ label: string;
42
+ place: FilePlace;
43
+ }
44
+ /** Where the Upload file dialog was opened (#2149): the page's path, and the page it shows, when there is one. */
45
+ export interface UploadWhere {
46
+ path?: string;
47
+ pageName?: string;
48
+ }
49
+ declare class FileManager extends BaseManager {
50
+ /** Set only by {@link openForOperator}: this instance answers the inventory. */
51
+ private operatorInstance;
52
+ constructor(engine: WikiEngine);
53
+ /**
54
+ * Open the door read-only for an operator command (#1859): the site's
55
+ * configuration and attachment index, with nothing else started and
56
+ * nothing written. The site may be running; this process changes no file.
57
+ */
58
+ static openForOperator(): Promise<FileManager>;
59
+ /** Every file `ctx` may read, newest first; `place` narrows it to one place. */
60
+ list(ctx: ActorContext, filter?: {
61
+ place?: FilePlace;
62
+ }): Promise<FileRecord[]>;
63
+ /** The file with this id, when `ctx` may read it. */
64
+ get(id: string, ctx: ActorContext): Promise<FileRecord | null>;
65
+ /**
66
+ * The files named `filename` that `ctx` may read: one per place at most,
67
+ * since a name is unique within a place (today's pool can hold more than
68
+ * one, which is what the migration's suffixes are for). `place` narrows it.
69
+ */
70
+ findByName(filename: string, ctx: ActorContext, place?: FilePlace): Promise<FileRecord[]>;
71
+ /**
72
+ * The username a public record's uploader names (#2097): the uploader is
73
+ * stored as an identity @id, resolved through IdentityManager; an orphan,
74
+ * an @id with no account, `Unknown` or an email names no owner.
75
+ */
76
+ private ownerOf;
77
+ private readable;
78
+ /** `asset-read` for the public place, from the PDP. No PDP, no answer: closed. */
79
+ private mayReadPublic;
80
+ /**
81
+ * The places `ctx` may put a file in (#2148, operator 2026-10-30): the
82
+ * public place when the PDP grants `asset-upload`; each of the user's own
83
+ * vaults the vault rule lets them upload to (the default vault, and every
84
+ * vault they already have); each shared folder where they hold
85
+ * `shared-folder-file`. A place they may not write to is never listed.
86
+ *
87
+ * Each place is asked exactly as {@link put} asks it, so what is offered is
88
+ * what the door allows. An affordance: nothing is recorded.
89
+ */
90
+ placesWritableBy(ctx: ActorContext): Promise<FilePlace[]>;
91
+ /**
92
+ * Put a file in `place` (#2148): the one write door. The permission for
93
+ * that place is asked first, and a refusal stores nothing. Then ICE vets
94
+ * the file (#2149): its type from its bytes, and its format's shim cleans
95
+ * it; the cleaned copy is the one kept. AttachmentManager stores it as the
96
+ * place requires (sealed in a sealed vault or shared folder), and this door
97
+ * records the upload with what vetting removed (a sealed place's file by
98
+ * id only, #2048). No page decides the place: `options.pageName` is only
99
+ * the page that shows it.
100
+ *
101
+ * @throws Error 'Permission denied: …' when `ctx` may not write there, or
102
+ * is nobody (#1926); a route answers it 401 or 403.
103
+ * @throws IceVetRefused when the file could not be checked; nothing is stored
104
+ */
105
+ put(ctx: ActorContext, place: FilePlace, file: PutFile, options?: PutOptions): Promise<PutResult>;
106
+ /**
107
+ * Record a file stored or deleted (#2149: moved here from AttachmentManager,
108
+ * which stores and records nothing itself). An upload is `continue`: a
109
+ * failed record is logged, never fatal. A delete is critical: it rethrows,
110
+ * tagged `AUDIT_WRITE_FAILED`, so the delete does not happen unrecorded and
111
+ * a caller can tell an unwritable record from a failed delete.
112
+ *
113
+ * A sealed store's file is recorded by id and store only (#2048), its
114
+ * owner by identity @id with the username beside it (#2041).
115
+ * AttachmentManager's delete door calls this until deleting is FileManager's.
116
+ */
117
+ recordFileEvent(op: 'upload' | 'delete', context: ActorContext, detail: AttachmentDetail, wikiContext?: {
118
+ request?: {
119
+ ip?: string;
120
+ } | null;
121
+ }): Promise<void>;
122
+ /**
123
+ * What the Upload file dialog offers `ctx` (#2149): every place
124
+ * {@link placesWritableBy} lists, and nothing else, each with the words the
125
+ * user reads; and the one selected at first: where they are working
126
+ * ({@link workingPlaceKey}), else the public place, else the first. Never
127
+ * inferred from whether a page exists. An affordance: nothing is recorded.
128
+ */
129
+ uploadPlaces(ctx: ActorContext, where?: UploadWhere): Promise<{
130
+ places: UploadPlace[];
131
+ defaultKey: string | null;
132
+ }>;
133
+ /** The place a dialog key names, when it is one `ctx` may write to (#2149); null otherwise. Never a place it was not offered. */
134
+ placeForKey(ctx: ActorContext, key: string): Promise<FilePlace | null>;
135
+ /** A place's key in the dialog: `public`, `vault:<id>` (always the user's own) or `folder:<name>`. */
136
+ static placeKey(place: FilePlace): string;
137
+ /** The words the user reads for a place. */
138
+ private placeLabel;
139
+ /**
140
+ * Where the user is working, as a dialog key (#2149): a shared folder's
141
+ * list (`/shared/<folder>`) or one of its pages is that folder; a page in
142
+ * one of their own vaults is that vault. Read from the location, never
143
+ * from whether a page by that name exists; anywhere else is no place.
144
+ */
145
+ private workingPlaceKey;
146
+ /**
147
+ * The place today's page-tied upload goes to (#1396, #1398, #2125), until
148
+ * #2149/#2150 move its callers to a place the user chose: an upload onto a
149
+ * shared folder's page goes into that folder, saved or not; onto a vault
150
+ * page into that page's vault, by someone who may edit the page; with the
151
+ * Private tick into the uploader's own vault (`store`, or their default);
152
+ * otherwise the public place.
153
+ */
154
+ placeForPageUpload(ctx: ActorContext, options: Pick<UploadOptions, 'pageName' | 'private' | 'store'>): Promise<FilePlace>;
155
+ /** May `ctx` put a file in `place`? The existing rule for each kind, asked of its owner. */
156
+ private mayPut;
157
+ /** The user's own vaults a file may go in: their default vault, and every vault they already have. */
158
+ private ownVaultIds;
159
+ private storeLayout;
160
+ /** A well-formed place: a vault is a user's (never the shared folders' container), with valid ids. */
161
+ private static isPlace;
162
+ /** A place in a log line. Ids only, never a file's name. */
163
+ private static describePlace;
164
+ /**
165
+ * Everything kept as files, for the migration dry run: the public files in
166
+ * full and each vault as counts (files, bytes, clash renames), never its
167
+ * names. Only on an instance opened by {@link openForOperator}, for an
168
+ * operator's context.
169
+ */
170
+ inventory(ctx: ActorContext): Promise<FileInventory>;
171
+ /** The dry run: the inventory, planned with the configured values. Writes nothing. */
172
+ migrationDryRun(ctx: ActorContext): Promise<FileMigrationReport>;
173
+ /** An asset record as a file record: the same fields, plus place, owner and system-category. */
174
+ static toFileRecord(asset: AssetRecord, place: FilePlace, owner: string | null, systemCategory: string | null): FileRecord;
175
+ private configManager;
176
+ /** The public place's category today: the default entry of `ngdpbase.system-category`. */
177
+ private defaultCategory;
178
+ /** The category that declares this vault, or null when none does. */
179
+ private vaultCategory;
180
+ private suffixDigits;
181
+ }
182
+ export default FileManager;