@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
@@ -14,12 +14,14 @@
14
14
  * noise filter, fingerprints, skipping unchanged files, per-file status,
15
15
  * keeping the original, and running the work as a background job.
16
16
  *
17
- * Only importing is built in the first slice. `out` / `both` shims and the
18
- * `write` half are declared so export (#1974) and feeds (D10) fit later
19
- * without changing these shapes.
17
+ * - An __export source__ is the owner's door for reading the thing being
18
+ * exported (#2120): a page, later a search result or an add-on's list. It
19
+ * checks the owner's existing read permission (D5) and yields items that
20
+ * `out` shims chain into one file to download.
20
21
  */
21
22
  import type { ActorContext } from '../context/ActorContext.js';
22
23
  import type { GuardedFetchOptions, GuardedResponse } from '../http/guardedFetch.js';
24
+ import type { IceNameForm } from './givenName.js';
23
25
  /** In a shim's `receivers`: any receiver that takes the kind the shim produces (#2109). */
24
26
  export declare const ANY_RECEIVER = "*";
25
27
  /** D3: which way a shim moves data. Most are `in`; PDF (#1974) would be `both`. */
@@ -60,6 +62,28 @@ export interface IceItem {
60
62
  * (a feed entry): an item whose key the engine has seen before is skipped.
61
63
  */
62
64
  itemKey?: string;
65
+ /**
66
+ * The archive the file was unpacked from (#2118), when it was: what lies
67
+ * beside it, for a shim whose format spans the files of an archive (a
68
+ * takeout's pages link to its files by the ids its file index names).
69
+ * The engine still hands each file to the shims on its own.
70
+ */
71
+ archive?: IceArchiveView;
72
+ }
73
+ /**
74
+ * The archive a file was unpacked from (#2118): this file's own path in it,
75
+ * and every member, read only. Paths are the archive's own, `/`-separated.
76
+ * Never part of an item's key: what an item is, is its content.
77
+ */
78
+ export interface IceArchiveView {
79
+ /** The archive's name, as the file's `name` begins: `takeout.zip`. */
80
+ name: string;
81
+ /** This file's path within the archive. */
82
+ path: string;
83
+ /** Every member's path, in the archive's order. */
84
+ paths: readonly string[];
85
+ /** A member's bytes, or undefined when there is no such member. */
86
+ read(memberPath: string): Buffer | undefined;
63
87
  }
64
88
  /**
65
89
  * The `data` of a `record` item (#2109): one row of a table or one entry of a
@@ -98,6 +122,14 @@ export interface ShimTools {
98
122
  */
99
123
  options: Record<string, unknown>;
100
124
  }
125
+ /** What an `out` shim is handed (#2120): a shim's tools, and who the export is for. */
126
+ export interface ExportShimTools extends ShimTools {
127
+ /**
128
+ * The job context of the user who asked for the export, for a core shim
129
+ * that calls a core door with it (the PDF render, P1).
130
+ */
131
+ ctx: ActorContext;
132
+ }
101
133
  /** What a shim's read returns. */
102
134
  export interface ShimReadResult {
103
135
  items: IceItem[];
@@ -106,6 +138,13 @@ export interface ShimReadResult {
106
138
  * records plus a partial note): the file's status is then `partial`, with this.
107
139
  */
108
140
  partial?: string;
141
+ /**
142
+ * Set, with no items, when the file is part of its format's layout and
143
+ * holds nothing to import on its own (#2118): a takeout's file index, read
144
+ * through the archive by the files it names. The file's status is then
145
+ * `noise`, with this, and the ledger records nothing for it.
146
+ */
147
+ skip?: string;
109
148
  }
110
149
  /**
111
150
  * A shim as its author writes it (D7). `name` is unique within its owner; the
@@ -131,8 +170,47 @@ export interface IceShim {
131
170
  duplicates?: 'file' | 'item';
132
171
  /** Inbound: read one input. Required for `in` and `both`. */
133
172
  read?(input: IceItem, tools: ShimTools): Promise<ShimReadResult>;
134
- /** Outbound (export, #1974): not run in the first slice. Required for `out` and `both`. */
135
- write?(items: IceItem[], tools: ShimTools): Promise<IceItem[]>;
173
+ /**
174
+ * Outbound (export, #2120): turn the items into this shim's `produces`.
175
+ * Required for `out` and `both`. An outbound shim matches its input by
176
+ * `accepts.kinds` alone; a shim that produces `file` makes the download,
177
+ * and is what the Export action offers.
178
+ */
179
+ write?(items: IceItem[], tools: ExportShimTools): Promise<IceItem[]>;
180
+ /**
181
+ * An outbound shim whose engine is missing says so (#2120): PDF with no
182
+ * provider chosen. The Export action does not offer it. Default: available.
183
+ */
184
+ isAvailable?(): boolean;
185
+ /**
186
+ * Vetting (#2149): clean this format's own kind of risky content out of a
187
+ * file that is about to be kept as a file (scripts and handlers in an SVG,
188
+ * HTML and formulas in CSV cells). Returns the cleaned bytes, and one note
189
+ * per kind of thing removed, saying what and how many, never the content.
190
+ * An inbound shim has `read()`, `clean()` or both; one with only `clean()`
191
+ * is never picked for a run.
192
+ */
193
+ clean?(input: IceItem): Promise<IceCleanResult>;
194
+ }
195
+ /** What a shim's `clean` returns (#2149). `removed` is empty when the file needed nothing. */
196
+ export interface IceCleanResult {
197
+ bytes: Buffer;
198
+ removed: IceWarning[];
199
+ }
200
+ /**
201
+ * A file after the engine's vetting (#2149): its type read from its bytes,
202
+ * and, when its format's shim found something to clean, the cleaned bytes
203
+ * and what was removed. The caller keeps `bytes`, never the bytes it sent.
204
+ */
205
+ export interface IceVetResult {
206
+ name: string;
207
+ bytes: Buffer;
208
+ /** From the bytes when they show it (#1624), else the declared or extension type. */
209
+ mediaType: string;
210
+ /** What cleaning removed, one note per kind; empty when nothing was. */
211
+ removed: IceWarning[];
212
+ /** The shim that cleaned it, when one did. */
213
+ cleanedBy?: string;
136
214
  }
137
215
  /** A shim the engine holds: its full id, and who registered it. */
138
216
  export interface RegisteredShim extends IceShim {
@@ -204,6 +282,27 @@ export interface ReceiveInfo {
204
282
  chain: string[];
205
283
  citation?: IceCitation;
206
284
  original?: KeptOriginal;
285
+ /**
286
+ * The name the user gave what this file becomes (#2130): a page's title, a
287
+ * file's name. Given only to a receiver that declares `names`, only when it
288
+ * passed the name rule (`givenNameProblem`) and differs from the file's own
289
+ * (`defaultGivenName`). The receiver applies it in its own door, where the
290
+ * thing goes as it would without one, and refuses the item when the name is
291
+ * taken there: never a silent replace.
292
+ */
293
+ givenName?: string;
294
+ /**
295
+ * The place within the receiver the run goes to (#2118), for a receiver
296
+ * with `targets`: one its `targets` gave the user the run is for, checked
297
+ * by the engine before the run. The receiver's door still checks it.
298
+ */
299
+ target?: string;
300
+ /**
301
+ * Set when the file was taken before and is read again only because the
302
+ * receiver `rechecks` this item (#2171): the receiver's own check decides
303
+ * whether it still holds it, and one it holds is `duplicate`.
304
+ */
305
+ rechecked?: boolean;
207
306
  }
208
307
  /** What a receiver's `fileDone` is told (#2109). */
209
308
  export interface FileDoneInfo extends ReceiveInfo {
@@ -218,6 +317,18 @@ export interface ReceiveOutcome {
218
317
  /** Where it went: a page name, an attachment id, a record id. */
219
318
  ref?: string;
220
319
  message?: string;
320
+ /**
321
+ * The name the thing goes by on the site, when it differs from the name
322
+ * the user gave (#2130): a `duplicate` the receiver already held under
323
+ * another name. The engine says so on the file's status, and records it
324
+ * so the same bytes uploaded again are told the same.
325
+ */
326
+ heldAs?: string;
327
+ }
328
+ /** A place within a receiver (#2118): a user's private store. */
329
+ export interface IceTarget {
330
+ id: string;
331
+ label?: string;
221
332
  }
222
333
  /**
223
334
  * A receiver as its owner writes it (D4). Its doors are the receiver's own
@@ -277,8 +388,60 @@ export interface IceReceiver {
277
388
  * before the run is queued; the run reuses what was kept and needs no key.
278
389
  */
279
390
  keepsEncrypted?: boolean;
391
+ /**
392
+ * `receive` itself may write into an encrypted store (a takeout into a
393
+ * sealed private store, #2118), which only a request holding the user's
394
+ * unlocked key can write. The engine then runs an upload into it during
395
+ * the upload request, with the request's own context, instead of as a
396
+ * background job: no key is ever carried into a job (#2034's decision).
397
+ * An import into it is confidential (#2110). Core's only: an add-on
398
+ * receiver that declares it is refused at registration, so an add-on is
399
+ * confidential only through `keepsEncrypted`.
400
+ */
401
+ writesEncrypted?: boolean;
402
+ /**
403
+ * A zip inside an uploaded zip is handed on as a file, not unpacked
404
+ * (#2118): for a receiver whose format holds zips as content (a takeout's
405
+ * files). Without it, the engine unpacks nested zips too.
406
+ */
407
+ innerZipsAsFiles?: boolean;
408
+ /**
409
+ * The items this receiver checks for itself whether it still holds
410
+ * (#2118, operator 2026-10-30): a takeout's page, by its uuid, and a
411
+ * takeout's file, by its bytes (#2171), so one deleted since an earlier
412
+ * import comes back when the same takeout is imported again. A file whose
413
+ * fingerprint the scope already took is then read again, and only these
414
+ * of its items go to `receive`, with `info.rechecked`; the rest are
415
+ * duplicates. When nothing new is stored it stays `unchanged`, unrecorded,
416
+ * as the fingerprint skip leaves it. Without this, the fingerprint decides.
417
+ */
418
+ rechecks?(item: IceItem): boolean;
419
+ /**
420
+ * The places within this receiver this context may import into (#2118):
421
+ * a user's own private stores. The receiver's own rule (D5). A receiver
422
+ * with targets takes a run only with one of them (`IceRunRequest.target`),
423
+ * which the engine checks against this before the run, binds into the
424
+ * ledger scope, and hands on in every item's `ReceiveInfo.target`.
425
+ */
426
+ targets?(ctx: ActorContext): Promise<IceTarget[]>;
427
+ /**
428
+ * Called once when a run into this receiver has read every file (#2118),
429
+ * with its report: a receiver that records the run as one act (a
430
+ * takeout's `store-import` audit event). Not called for a refused run. A
431
+ * throw is the run's failure.
432
+ */
433
+ runDone?(ctx: ActorContext, report: IceRunReport): Promise<void>;
280
434
  /** The original-file choices this receiver offers (D11). Without it: all three. */
281
435
  originals?: IceOriginalChoices;
436
+ /**
437
+ * This receiver applies a name the user gives (#2130): `title` (a page's
438
+ * title) or `file` (a file's name, keeping the upload's extension). The
439
+ * import screen then offers a name field for a single uploaded file, and
440
+ * the receiver finds the name in `ReceiveInfo.givenName`. Without it the
441
+ * receiver names what it creates itself: no field is offered, a posted
442
+ * name is refused, and the engine passes none.
443
+ */
444
+ names?: IceNameForm;
282
445
  }
283
446
  export interface RegisteredReceiver extends IceReceiver {
284
447
  id: string;
@@ -289,6 +452,14 @@ export interface IceUpload {
289
452
  name: string;
290
453
  bytes: Buffer;
291
454
  mediaType?: string;
455
+ /**
456
+ * The name the user gave what this file becomes (#2130). For a file
457
+ * uploaded on its own; a zip's files keep their own names (the engine
458
+ * drops it on a zip). Checked by the engine against the receiver's `names`.
459
+ */
460
+ givenName?: string;
461
+ /** The archive the engine unpacked this file from (#2118); set by the engine, never by a caller. */
462
+ archive?: IceArchiveView;
292
463
  }
293
464
  /**
294
465
  * A URL the engine fetches as the run's one file (D1, D10, #2109): through
@@ -346,11 +517,27 @@ export interface IceRunRequest {
346
517
  * client sent.
347
518
  */
348
519
  options?: Record<string, unknown>;
520
+ /**
521
+ * The place within the receiver (#2118), for a receiver with `targets`:
522
+ * one of those it gives the user the run is for. Required for such a
523
+ * receiver and refused for any other.
524
+ */
525
+ target?: string;
349
526
  }
350
527
  /** Per-file status (first slice). */
351
528
  export type IceFileStatus = 'imported' | 'partial' | 'unchanged' | 'duplicate' | 'noise' | 'unreadable' | 'refused' | 'failed';
352
529
  export interface IceFileReport {
353
530
  name: string;
531
+ /** The name the user gave what it becomes (#2130), when it was applied: something was stored under it. */
532
+ givenName?: string;
533
+ /**
534
+ * Where the user can open what the file already is on the site, when a
535
+ * name they gave was not applied because it was already here (#2130):
536
+ * the receiver's `linkFor`, a path on this site only.
537
+ */
538
+ link?: string;
539
+ /** The name what the file already is goes by on the site, when a name given was not applied (#2130). */
540
+ heldAs?: string;
354
541
  sha256?: string;
355
542
  size: number;
356
543
  status: IceFileStatus;
@@ -368,6 +555,8 @@ export interface IceFileReport {
368
555
  export interface IceRunReport {
369
556
  runId: string;
370
557
  receiver: string;
558
+ /** The place within the receiver (#2118), when it has places. */
559
+ target?: string;
371
560
  scope: string;
372
561
  /** Who it ran as. */
373
562
  username: string;
@@ -387,7 +576,12 @@ export interface LedgerOriginal extends KeptOriginal {
387
576
  }
388
577
  /** What the engine remembers about one file in a scope. */
389
578
  export interface LedgerFileEntry {
579
+ /** The file's own name, as uploaded. */
390
580
  name: string;
581
+ /** The name the user gave what it became (#2130), when it was applied. */
582
+ givenName?: string;
583
+ /** The name what it is already goes by on the site, when a given name was not applied (#2130). */
584
+ heldAs?: string;
391
585
  sha256: string;
392
586
  size: number;
393
587
  status: IceFileStatus;
@@ -403,7 +597,10 @@ export interface LedgerFileEntry {
403
597
  * own entries, from the receivers they may use.
404
598
  */
405
599
  export interface RecentImport {
600
+ /** The file's own name, as uploaded. */
406
601
  name: string;
602
+ /** The name the user gave what it became (#2130); the screen shows it with `name` beside it. */
603
+ givenName?: string;
407
604
  status: IceFileStatus;
408
605
  message?: string;
409
606
  /** When the ledger last recorded this file, ISO. */
@@ -411,6 +608,9 @@ export interface RecentImport {
411
608
  /** The receiver it went into. */
412
609
  receiver: string;
413
610
  receiverLabel?: string;
611
+ /** The place within it (#2118), when the receiver has places. */
612
+ target?: string;
613
+ targetLabel?: string;
414
614
  /** How many references the receiver gave (pages, records). */
415
615
  refs: number;
416
616
  /** Where the user can open the first of them (`linkFor`), when they may. */
@@ -432,3 +632,99 @@ export interface IceLedger {
432
632
  recordItems(scope: string, itemKeys: string[]): Promise<void>;
433
633
  files(scope: string): Promise<LedgerFileEntry[]>;
434
634
  }
635
+ /**
636
+ * The kind of the one item an export ends with: a file to download, with
637
+ * `bytes`, `mediaType` and the `name` it is saved under.
638
+ */
639
+ export declare const EXPORT_FILE_KIND = "file";
640
+ /** What an export source is told about the read it is asked for. */
641
+ export interface ExportReadInfo {
642
+ /** The shim that will make the file (`core.pdf`). */
643
+ format: string;
644
+ }
645
+ /**
646
+ * The owner's door for reading the thing being exported (#2120, D5): a page
647
+ * through PageManager, later a search result or an add-on's list. Export
648
+ * starts from the thing (operator, 2026-10-28): the source says what kind of
649
+ * items it yields, and only the `out` shims that can make a file from that
650
+ * kind are offered.
651
+ */
652
+ export interface IceExportSource {
653
+ name: string;
654
+ label?: string;
655
+ /** The kind of the items it yields, e.g. `ncm` for a page. */
656
+ yields: IceKind;
657
+ /**
658
+ * Would this context be allowed to export `ref`? Asked only to decide
659
+ * whether to offer the Export action: an affordance, which records nothing.
660
+ * The owner's existing read rule (D5).
661
+ */
662
+ mayExport(ref: string, ctx: ActorContext): Promise<boolean>;
663
+ /**
664
+ * Read `ref` for export as `ctx`: the door. It checks the owner's existing
665
+ * permission, records what its owner records, and answers null when the
666
+ * thing is not there or `ctx` may not export it, without telling the two apart.
667
+ */
668
+ read(ref: string, ctx: ActorContext, info: ExportReadInfo): Promise<IceItem[] | null>;
669
+ /**
670
+ * Record how an export of `ref` ended, as its owner records exports: once
671
+ * the run has ended, with the file made or not. Also called, as a failure,
672
+ * when what was read is not exported (too large, the site too busy).
673
+ * Optional; a record that fails is logged, never thrown.
674
+ */
675
+ recordExport?(ref: string, ctx: ActorContext, outcome: ExportOutcome): Promise<void>;
676
+ }
677
+ /** How an export ended, for its source's record (#2120). */
678
+ export interface ExportOutcome {
679
+ /** The shim that made, or was to make, the file. */
680
+ format: string;
681
+ runId: string;
682
+ /** The names of the items the source read. */
683
+ items: string[];
684
+ result: 'success' | 'failure';
685
+ /** Why it failed, as the user is told. */
686
+ reason?: string;
687
+ }
688
+ export interface RegisteredExportSource extends IceExportSource {
689
+ id: string;
690
+ owner: string;
691
+ }
692
+ /** A format the Export action offers: the shim that makes the file, and the shims before it. */
693
+ export interface IceExportFormat {
694
+ /** The shim that makes the file, `core.pdf`. */
695
+ id: string;
696
+ label: string;
697
+ /** Every shim the export runs through, in order, ending with `id`. */
698
+ chain: string[];
699
+ }
700
+ /** One export, as a caller asks for it. */
701
+ export interface IceExportRequest {
702
+ /** The export source's id, `core.pages`. */
703
+ source: string;
704
+ /** What to export, in the source's own terms: a page's name. */
705
+ ref: string;
706
+ /** The format: an id `exportFormatsFor` offered. */
707
+ format: string;
708
+ }
709
+ /** The file an export made, as its status shows it. */
710
+ export interface IceExportFile {
711
+ name: string;
712
+ mediaType: string;
713
+ size: number;
714
+ }
715
+ export interface IceExportReport {
716
+ runId: string;
717
+ source: string;
718
+ ref: string;
719
+ format: string;
720
+ chain: string[];
721
+ /** Who it ran as: the only user who sees it or downloads its file. */
722
+ username: string;
723
+ state: 'queued' | 'running' | 'done' | 'failed';
724
+ startedAt?: string;
725
+ finishedAt?: string;
726
+ error?: string;
727
+ file?: IceExportFile;
728
+ /** When the file is let go (`ngdpbase.export.engine.keep-minutes`). */
729
+ expiresAt?: string;
730
+ }
@@ -0,0 +1,19 @@
1
+ import type { IceUpload, IceVetResult, RegisteredShim } from './types.js';
2
+ /** A file whose format's shim failed while cleaning it: it is not kept, since it could not be checked. */
3
+ export declare class IceVetRefused extends Error {
4
+ readonly file: string;
5
+ readonly reason: string;
6
+ constructor(file: string, reason: string);
7
+ }
8
+ /**
9
+ * The shim that cleans this file: one whose media types include the type the
10
+ * bytes show; or, when the bytes show no type of their own (CSV, plain text),
11
+ * one whose extensions include the file's. Its "can I read this?" must agree.
12
+ */
13
+ export declare function cleanerFor(shims: readonly RegisteredShim[], name: string, mediaType: string, bytesShowType: boolean): RegisteredShim | null;
14
+ /**
15
+ * Vet one file (#2149). `shims` are the enabled ones. Throws
16
+ * {@link IceVetRefused} when the cleaning shim fails: a file that could not
17
+ * be checked is not kept.
18
+ */
19
+ export declare function vetFile(shims: readonly RegisteredShim[], upload: IceUpload): Promise<IceVetResult>;
@@ -6,12 +6,14 @@ import { type ActorContext } from '../context/ActorContext.js';
6
6
  * failed" (#1183). A caller refusing a destructive action needs to say which.
7
7
  */
8
8
  export declare const AUDIT_WRITE_FAILED = "EAUDITWRITE";
9
+ import type { AttachmentDetail } from '../utils/auditEvents.js';
9
10
  import type { WikiEngine } from '../types/WikiEngine.js';
10
11
  import type { CatalogSource, CatalogQuery, CatalogPage, CreativeWork, SchemaType, RebuildOpts } from '../types/Schema.js';
11
12
  import type { User } from '../providers/BaseAttachmentProvider.js';
12
13
  import { type AssetQuery, type AssetRecord } from '../types/Asset.js';
13
14
  import type { StoreFileEntry, StoreFileInput, StoreFileLocation } from '../types/Provider.js';
14
15
  import type { OriginalKeep } from '../ice/types.js';
16
+ import type { FilePlace } from '../types/File.js';
15
17
  /**
16
18
  * Base attachment provider interface
17
19
  */
@@ -82,23 +84,24 @@ export interface FileInfo {
82
84
  mimeType: string;
83
85
  size: number;
84
86
  }
87
+ /** A file handed to the write door (#2148): its bytes, and what the uploader said about them. */
88
+ export interface PutFile {
89
+ bytes: Buffer;
90
+ info: FileInfo;
91
+ }
85
92
  /**
86
- * Upload options interface
93
+ * What else a write carries (#2148). None of it decides where the file goes:
94
+ * that is the place the caller passed to `FileManager.put`.
87
95
  */
88
- export interface UploadOptions {
96
+ export interface PutOptions {
97
+ /**
98
+ * The page that shows the file, recorded as a mention (and on the audit
99
+ * record of an unsealed place). Never the place: a page only links to a file.
100
+ */
89
101
  pageName?: string;
90
102
  description?: string;
91
103
  /** WikiContext for the current request — audit IP fallback */
92
104
  wikiContext?: import('../context/WikiContext.js').default;
93
- /**
94
- * Private-store destination (#1396): the upload dialog's checkbox. Decides
95
- * only when `pageName` is not a private page — an upload onto a private page
96
- * is always private, in that page's author's store (#1398). Absent or false
97
- * otherwise keeps the public attachments pool.
98
- */
99
- private?: boolean;
100
- /** Store id when private with no private page; default from ConfigurationManager defaultstoreid */
101
- store?: string;
102
105
  /**
103
106
  * An import's keep at upload (#2061): the `OriginalKeep` the engine handed
104
107
  * the receiver's `keepOriginal`, passed on unchanged. Honoured only while
@@ -108,6 +111,37 @@ export interface UploadOptions {
108
111
  * served as a download. Anywhere else it changes nothing.
109
112
  */
110
113
  keep?: OriginalKeep;
114
+ /**
115
+ * An import's own file name, when the user gave the file another (#2130):
116
+ * recorded beside `filename` on the upload's audit record. A sealed store's
117
+ * record names the file by id only, so it is not recorded there.
118
+ */
119
+ importedFrom?: string;
120
+ }
121
+ /**
122
+ * The page-tied upload's options (#1396, #1398): today's callers name the page
123
+ * they upload onto and the dialog's Private tick, and FileManager turns that
124
+ * into a place (`placeForPageUpload`) until #2149/#2150 move them to a place
125
+ * the user chose.
126
+ */
127
+ /**
128
+ * What storage hands back to FileManager (#2149): the stored file's metadata,
129
+ * and what the upload's audit record says about it, which FileManager records.
130
+ */
131
+ export interface StoredInPlace {
132
+ metadata: AttachmentMetadata;
133
+ record: AttachmentDetail;
134
+ }
135
+ export interface UploadOptions extends PutOptions {
136
+ /**
137
+ * Private-store destination (#1396): the upload dialog's checkbox. Decides
138
+ * only when `pageName` is not a private page — an upload onto a private page
139
+ * is always private, in that page's author's store (#1398). Absent or false
140
+ * otherwise keeps the public attachments pool.
141
+ */
142
+ private?: boolean;
143
+ /** Store id when private with no private page; default from ConfigurationManager defaultstoreid */
144
+ store?: string;
111
145
  }
112
146
  /**
113
147
  * Mention object (WebPage reference)
@@ -298,19 +332,39 @@ declare class AttachmentManager extends BaseManager implements CatalogSource {
298
332
  */
299
333
  private writeAttachments;
300
334
  /**
301
- * Upload an attachment
335
+ * Upload a file onto a page: the page-tied path its current callers use
336
+ * (the editor's upload route, imports, NCM image localization, thumbnails).
337
+ *
338
+ * #2148: this decides nothing. Where the file goes is FileManager's
339
+ * (`placeForPageUpload`, kept until #2149/#2150 move these callers to a
340
+ * place the user chose), and the write is FileManager's one door, `put`,
341
+ * which asks the permission for that place and then has this manager store
342
+ * it ({@link storeInPlace}). One write path, not two.
302
343
  *
303
- * @param {Buffer} fileBuffer - File data
304
- * @param {FileInfo} fileInfo - { originalName, mimeType, size }
305
- * @param {UploadOptions} options - Upload options
306
- * @param {string} options.pageName - Page uploaded onto (optional). A private page forces the upload private, into its author's store (#1398)
307
- * @param {string} options.description - File description
308
- * @param {boolean} options.private - Private-store destination for an upload with no private page (#1396)
309
- * @param {string} options.store - Store id when private with no private page; else ConfigurationManager defaultstoreid
344
+ * @param fileBuffer - File data
345
+ * @param fileInfo - { originalName, mimeType, size }
310
346
  * @param ctx - Who is uploading (#1179): the request's subject, or a JobContext for an in-engine caller. Mandatory and positional.
311
- * @returns {Promise<AttachmentMetadata>} Attachment metadata
347
+ * @param options - The page it is uploaded onto, the private tick and store, description, keep, importedFrom
348
+ * @returns Attachment metadata
312
349
  */
313
350
  uploadAttachment(fileBuffer: Buffer, fileInfo: FileInfo, ctx: ActorContext, options?: UploadOptions): Promise<AttachmentMetadata>;
351
+ /** The owner of files (#2148, #2149): every upload goes through its door, and it writes every upload and delete record. */
352
+ private fileManager;
353
+ /**
354
+ * Store a file in `place` (#2148): the storage underneath FileManager's
355
+ * door. Only `FileManager.put` calls this, after it has asked the
356
+ * permission for the place; nothing here decides where a file goes or who
357
+ * may put it there.
358
+ *
359
+ * What it does, as the place requires: the type read from the bytes
360
+ * (#1624); the uploader recorded by identity @id, and nobody uploads
361
+ * anonymously (#1926, #2097); a vault or shared folder keeps the file in its
362
+ * own store, sealed when the store is (#1460, #1394); and an import's kept
363
+ * original honoured only in its user's own vault (#2061). It records
364
+ * nothing (#2149): it returns what the upload's audit record says (a sealed
365
+ * store's file by id only, #2048), and FileManager, the owner, records it.
366
+ */
367
+ storeInPlace(ctx: ActorContext, place: FilePlace, file: PutFile, options?: PutOptions): Promise<StoredInPlace>;
314
368
  /**
315
369
  * The username an upload is recorded under (#1926): the person the context
316
370
  * names. Asked after policy has allowed the upload and before anything is
@@ -348,8 +402,12 @@ declare class AttachmentManager extends BaseManager implements CatalogSource {
348
402
  */
349
403
  private userReference;
350
404
  private static uploaderOf;
351
- /** A file is added to a private page only by someone who may edit it: the page's own decision (#1398). */
352
- private assertMayEditPage;
405
+ /**
406
+ * A file is added to a private page only by someone who may edit it: the
407
+ * page's own decision (#1398). Asked here by `attachToPage`, and by
408
+ * FileManager's page-tied upload (#2148).
409
+ */
410
+ assertMayEditPage(ctx: ActorContext, pageName: string): Promise<void>;
353
411
  /**
354
412
  * One vault as a provider location, opened through `ctx` (#1842). Throws as
355
413
  * `storeFileIO` does: a locked vault, or a sealed kind's vault not yet opened.
@@ -395,6 +453,14 @@ declare class AttachmentManager extends BaseManager implements CatalogSource {
395
453
  * in the clear.
396
454
  */
397
455
  private ownPrivateStores;
456
+ /**
457
+ * The shared folders `ctx` may open (#2125), as provider locations: the
458
+ * folders SharedFolderManager's PDP question permits (`shared-folder-read`),
459
+ * each opened through `ctx`. A folder it may not read is not here at all.
460
+ */
461
+ private sharedFolderStores;
462
+ /** The requester's own stores, then the shared folders it may open (#2125): where a file it may read can be. */
463
+ private readableStores;
398
464
  private mayReach;
399
465
  /**
400
466
  * How a store file is shown to callers that expect attachment metadata.
@@ -414,9 +480,10 @@ declare class AttachmentManager extends BaseManager implements CatalogSource {
414
480
  /**
415
481
  * May `ctx` do `permission` to `what` in `owner`'s vault `store` (#1539)?
416
482
  * The container rule, then the capability asked with the vault, so
417
- * `vault-owner` decides — the same rule as a vault page.
483
+ * `vault-owner` decides — the same rule as a vault page. FileManager's
484
+ * write door asks it for a vault (#2148), so the rule has one home.
418
485
  */
419
- private mayInVault;
486
+ mayInVault(ctx: ActorContext, permission: string, owner: string, store: string, what: string): Promise<boolean>;
420
487
  /** The requester's own store file and its entry, before the capability is asked — see {@link findOwnStoreFile}. */
421
488
  private locateOwnStoreFile;
422
489
  /**
@@ -473,6 +540,15 @@ declare class AttachmentManager extends BaseManager implements CatalogSource {
473
540
  * differently without reading it twice (#1460).
474
541
  */
475
542
  isOwnPrivateStoreFile(attachmentId: string, ctx: ActorContext): Promise<boolean>;
543
+ /**
544
+ * The files the requester's own store `store` holds, as fingerprint → id,
545
+ * or null when this context cannot open it (#2171). The store's own
546
+ * duplicate check (its index's fingerprints), read without storing or
547
+ * recording anything: a takeout import reads it once per run and asks it
548
+ * before putting a file again, so a file deleted since comes back and one
549
+ * still held writes no second upload record. Only that store is opened.
550
+ */
551
+ ownStoreFingerprints(ctx: ActorContext, store: string): Promise<Map<string, string> | null>;
476
552
  /** The requester's own private-store files uploaded onto `pageName`. */
477
553
  getPrivateStoreAttachmentsForPage(pageName: string, ctx: ActorContext): Promise<AttachmentMetadata[]>;
478
554
  /**
@@ -825,18 +901,6 @@ declare class AttachmentManager extends BaseManager implements CatalogSource {
825
901
  ip?: string;
826
902
  } | null;
827
903
  }): Promise<boolean>;
828
- /**
829
- * Emit an attachment audit event from the manager that owns the resource.
830
- *
831
- * `docs/audit-posture.md` states the rule this implements: an action is
832
- * "emitted through `recordAuditEvent` (or the manager door that calls it)".
833
- * The door is here. Placing it at a route means every future caller has to
834
- * remember, which is the thing #1120 exists to end — being audited must not
835
- * depend on a producer recalling a method.
836
- *
837
- * `critical` decides whether a failure to record stops the action.
838
- */
839
- private recordAttachmentEvent;
840
904
  /** #1204: an attachment's metadata changed; field NAMES only, at the door both edit paths pass through. */
841
905
  private recordAssetEdit;
842
906
  /**