@jwilleke/ngdpbase-types 5.37.0 → 5.39.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 (32) hide show
  1. package/lib/packages/ngdpbase-types/index.d.ts +2 -1
  2. package/lib/src/ice/ledger.d.ts +2 -0
  3. package/lib/src/ice/runImport.d.ts +6 -0
  4. package/lib/src/ice/shims/csv.d.ts +59 -0
  5. package/lib/src/ice/shims/dotpath.d.ts +7 -0
  6. package/lib/src/ice/shims/geojson.d.ts +19 -0
  7. package/lib/src/ice/shims/index.d.ts +16 -0
  8. package/lib/src/ice/shims/record.d.ts +68 -0
  9. package/lib/src/ice/shims/restjson.d.ts +14 -0
  10. package/lib/src/ice/shims/rss-atom.d.ts +22 -0
  11. package/lib/src/ice/shims/xml-index.d.ts +32 -0
  12. package/lib/src/ice/shims/xml.d.ts +30 -0
  13. package/lib/src/ice/types.d.ts +109 -7
  14. package/lib/src/managers/AddonsManager.d.ts +50 -6
  15. package/lib/src/managers/BackupManager.d.ts +20 -2
  16. package/lib/src/managers/BackupManager.js +39 -6
  17. package/lib/src/managers/ConsentManager.d.ts +19 -3
  18. package/lib/src/managers/ImportManager.d.ts +36 -1
  19. package/lib/src/managers/PageManager.d.ts +53 -3
  20. package/lib/src/managers/ShareManager.d.ts +27 -0
  21. package/lib/src/managers/ValidationManager.d.ts +27 -16
  22. package/lib/src/security/permissions.generated.d.ts +2 -0
  23. package/lib/src/types/Consent.d.ts +43 -1
  24. package/lib/src/types/Consent.js +34 -0
  25. package/lib/src/utils/auditEventNames.generated.d.ts +8 -0
  26. package/lib/src/utils/auditEventNames.generated.js +8 -0
  27. package/lib/src/utils/privateStoreCrypto.d.ts +10 -2
  28. package/lib/src/utils/privateStoreUnlock.d.ts +2 -0
  29. package/lib/src/utils/shareLockbox.d.ts +1 -1
  30. package/lib/src/utils/sharedFoldersKey.d.ts +17 -0
  31. package/lib/src/utils/sharedFoldersKey.js +33 -0
  32. package/package.json +2 -1
@@ -15,6 +15,7 @@
15
15
  * only, so the published entry carries no JavaScript.
16
16
  */
17
17
  export type { AddonModule, AddonManifest, AddonListing } from '../../src/managers/AddonsManager.js';
18
+ export type { AddonDashboardCard, AddonCardSurface, AddonCardSummary, AddonCardFigure } from '../../src/managers/AddonsManager.js';
18
19
  export type { AddonHost, AddonIceDeclaration, PersonSwitchSetter } from '../../src/utils/addonHost.js';
19
20
  export type { WikiEngine } from '../../src/types/WikiEngine.js';
20
21
  export type { ApiContext } from '../../src/context/ApiContext.js';
@@ -39,7 +40,7 @@ export type { PageAction } from '../../src/utils/pageUrl.js';
39
40
  export type { default as ConsentManager, ConsentScopeStatus } from '../../src/managers/ConsentManager.js';
40
41
  export type { AddonConsentRegistration, ConsentDocument, ConsentRecord, ConsentRequiredError, ConsentScope, ConsentSeen, ConsentStatus } from '../../src/types/Consent.js';
41
42
  export type { default as ImportManager } from '../../src/managers/ImportManager.js';
42
- export type { IceShim, IceReceiver, IceItem, IceKind, IceWarning, ShimDirection, ShimAccepts, ShimTools, ShimReadResult, ReceiveInfo, ReceiveOutcome, IceCitation, KeptOriginal, OriginalChoice, IceOriginalChoices, OriginalKeep, IceUpload, IceSource, IceRunRequest, IceRunReport, IceFileReport, IceFileStatus } from '../../src/ice/types.js';
43
+ export type { IceShim, IceReceiver, IceItem, IceKind, IceWarning, ShimDirection, ShimAccepts, ShimTools, ShimReadResult, ReceiveInfo, ReceiveOutcome, IceCitation, KeptOriginal, OriginalChoice, IceOriginalChoices, OriginalKeep, IceUpload, IceSource, IceUrlSource, IceRunRequest, IceRecordData, FileDoneInfo, IceRunReport, IceFileReport, IceFileStatus, RecentImport } from '../../src/ice/types.js';
43
44
  export type { default as UserManager, UserUpdateInput, UserUpdateFn } from '../../src/managers/UserManager.js';
44
45
  export type { User, UserPreferences } from '../../src/types/User.js';
45
46
  export type { default as AuthManager, InviteOutcome, InvitationView } from '../../src/managers/AuthManager.js';
@@ -25,6 +25,8 @@ declare abstract class ScopedLedger implements IceLedger {
25
25
  recordFile(scope: string, entry: LedgerFileEntry): Promise<void>;
26
26
  hasItem(scope: string, itemKey: string): Promise<boolean>;
27
27
  recordItem(scope: string, itemKey: string): Promise<void>;
28
+ seenItems(scope: string, itemKeys: string[]): Promise<Set<string>>;
29
+ recordItems(scope: string, itemKeys: string[]): Promise<void>;
28
30
  files(scope: string): Promise<LedgerFileEntry[]>;
29
31
  }
30
32
  /** The ledger in memory: for tests, and for a site that keeps no import history. */
@@ -9,6 +9,8 @@ export interface IceEngineSettings {
9
9
  maxFileBytes: number;
10
10
  maxChainDepth: number;
11
11
  defaultOriginal: OriginalChoice;
12
+ /** The largest response a URL source may return (#2109). */
13
+ fetchMaxBytes: number;
12
14
  }
13
15
  export interface IceRunDeps {
14
16
  /** Enabled shims, in registration order. */
@@ -17,6 +19,8 @@ export interface IceRunDeps {
17
19
  ledger: IceLedger;
18
20
  settings: IceEngineSettings;
19
21
  tools(shim: RegisteredShim): ShimTools;
22
+ /** The engine's guarded fetch, for a URL source (#2109): the same one `tools` hands shims. */
23
+ fetch?: ShimTools['fetch'];
20
24
  now?: () => Date;
21
25
  }
22
26
  /** The id of the receiver that keeps an original when the chosen receiver does not keep its own (D11). */
@@ -39,6 +43,8 @@ export declare const SCOPE_FORM = "ice2";
39
43
  * `sub` (phr's source id) narrows it within that user's scope, never outside.
40
44
  */
41
45
  export declare function boundScope(receiverId: string, owner: string, sub?: string): string;
46
+ /** Does this shim list the receiver, by id or as any receiver (`'*'`, #2109)? */
47
+ export declare function handsTo(shim: Pick<RegisteredShim, 'receivers'>, receiverId: string): boolean;
42
48
  export interface ChainLeaf {
43
49
  item: IceItem;
44
50
  chain: string[];
@@ -0,0 +1,59 @@
1
+ /**
2
+ * csv shim (#685 slice 4+, #911; a core shim since #2109) — zero dependency (a
3
+ * small RFC 4180 parser). Needed for sources that only publish CSV, e.g. NASA
4
+ * FIRMS thermal hotspots (geohazardwatch#4), whose `/api/area/csv/...`
5
+ * endpoint has no JSON/GeoJSON/XML variant.
6
+ *
7
+ * Nothing in it is about feeds, so any receiver of records can use it (a
8
+ * bank statement as well as a feed). The first row is the header; each
9
+ * subsequent row becomes one `record` item keyed by the header column names
10
+ * (all values are strings — `map`/dot-path and the downstream schema
11
+ * mappers cast as needed, exactly as for the other shims).
12
+ *
13
+ * Rows rarely carry an `id` column, so when neither the `recordIdField`
14
+ * option nor an `id` field resolves, a stable content hash of the row is
15
+ * synthesized as the id — order-independent (keys sorted) so a column
16
+ * reorder upstream doesn't churn ids, and content-addressed so
17
+ * change-detection still works.
18
+ *
19
+ * Delimiter defaults to `,`; override with the `delimiter` option (e.g. `\t`).
20
+ *
21
+ * The `skipLines` option skips N leading lines before the header (#1102).
22
+ * Some sources — government data exports especially — emit a human-readable
23
+ * caption above the real header. Without skipping it, the caption becomes
24
+ * the sole column name, the real header is consumed as the first record,
25
+ * and every column but the first is discarded. That failure is entirely
26
+ * silent: HTTP 200, a successful parse, records stored, and a table of empty
27
+ * values. Hence {@link preambleSuspicion}, which makes it visible (a warning
28
+ * on the read) without blocking it.
29
+ */
30
+ import { type IceRecordData, type IceShim } from '../types.js';
31
+ import { type RecordShimOptions, type RawRecord } from './record.js';
32
+ /**
33
+ * Parse CSV text into header-keyed row objects. RFC 4180 subset: quoted fields,
34
+ * escaped quotes (`""`), embedded commas/newlines inside quotes, CRLF/CR/LF line
35
+ * endings, and a leading UTF-8 BOM. Values are returned verbatim (strings);
36
+ * header cells are trimmed. Fully-empty lines are skipped.
37
+ *
38
+ * `skipLines` drops that many leading lines before the header is taken, for
39
+ * feeds with a caption preamble (#1102). Default 0 — byte-identical to the
40
+ * previous behaviour.
41
+ */
42
+ export declare function parseCsv(text: string, delimiter?: string, skipLines?: number): Record<string, string>[];
43
+ /**
44
+ * Return the suspect header when a parse looks like it consumed an unskipped
45
+ * preamble, or `null` when it looks fine (#1102).
46
+ *
47
+ * The signature of the failure is a single column whose name reads like a
48
+ * sentence: a caption line has no delimiter in it, so the whole line becomes one
49
+ * field. A legitimate single-column CSV has a short, word-like header, so the
50
+ * two are distinguishable in practice.
51
+ *
52
+ * Deliberately advisory. A one-column CSV is legal, just unusual, and a caller
53
+ * that guessed and re-parsed would only trade a visible wrong answer for an
54
+ * invisible one.
55
+ */
56
+ export declare function preambleSuspicion(columns: readonly string[]): string | null;
57
+ /** One row as a record: its own id, or one synthesized from its content. */
58
+ export declare function csvRecord(raw: RawRecord, opts: RecordShimOptions): IceRecordData | null;
59
+ export declare const csvShim: IceShim;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Trivial dot-path lookup (#685) — the only "mapping engine" the framework has.
3
+ * NO JSONPath dependency (design §7): `getByPath(obj, 'properties.mag')`,
4
+ * `getByPath(obj, 'geometry.coordinates.2')` (numeric segment = array index).
5
+ * Returns undefined for any missing segment.
6
+ */
7
+ export declare function getByPath(obj: unknown, path: string): unknown;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * GeoJSON shim (#685 slice 4; a core shim since #2109) — zero dependency (JSON.parse).
3
+ *
4
+ * Handles a GeoJSON FeatureCollection (`{ features: [...] }`), a bare array of
5
+ * features, or a single Feature. Per feature, the id comes from the
6
+ * `recordIdField` option (dot-path) or the feature's own `id`; properties come
7
+ * from the `map` option (dot-path per field) or, with no map, the feature's
8
+ * `properties` object.
9
+ *
10
+ * Reference feed: the USGS earthquakes GeoJSON
11
+ * (https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/4.5_week.geojson).
12
+ */
13
+ import { type IceRecordData, type IceShim } from '../types.js';
14
+ import { type RecordShimOptions, type RawRecord } from './record.js';
15
+ /** The features of a GeoJSON document. */
16
+ export declare function geojsonFeatures(data: unknown): RawRecord[];
17
+ /** One feature as a record. With no map, the feature's own `properties` object is lifted. */
18
+ export declare function geojsonRecord(raw: RawRecord, opts: RecordShimOptions): IceRecordData | null;
19
+ export declare const geojsonShim: IceShim;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Core's record shims (#2109; moved into core by the operator's decision of
3
+ * 2026-10-29, "generic work goes to core"): one per structured format,
4
+ * registered by ImportManager as `core.<name>`, so any receiver that takes
5
+ * records can use them, with no add-on enabled. The feeds add-on was their
6
+ * first user and names one per source as its `adapter`; accounting's bank
7
+ * statements (#2121) are the next.
8
+ *
9
+ * Shipped: geojson + rest-json (zero-dep), xml (fast-xml-parser), csv
10
+ * (zero-dep, #911), rss-atom (#913, xml-based) and xml-index (#912, two-phase).
11
+ * Each produces `record` items with per-item duplicates and hands to any
12
+ * receiver that takes records (`receivers: ['*']`).
13
+ */
14
+ import type { IceShim } from '../types.js';
15
+ /** Every record shim, in registration order. */
16
+ export declare const RECORD_SHIMS: readonly IceShim[];
@@ -0,0 +1,68 @@
1
+ /**
2
+ * What core's record shims share (#685 slice 8; core shims since #2109): the
3
+ * options a run hands its shim, turning one raw item into a `record` item,
4
+ * and the item's key.
5
+ *
6
+ * Nothing here is about any one receiver. A record shim reads one format
7
+ * into `record` items (`data` is `{ id, properties }`, `IceRecordData`); which
8
+ * receiver takes them, and what it does with them, is the receiver's
9
+ * business. So the same CSV shim serves a feed (the feeds add-on's
10
+ * `feeds.items`) and a bank-statement import alike, with neither add-on's
11
+ * code (operator, 2026-10-29: generic work goes to core).
12
+ *
13
+ * id ← the `recordIdField` option (dot-path) or the item's own `id`.
14
+ * properties ← the `map` option (dot-path per field), or — with no map — the
15
+ * supplied `defaultProps` object (geojson lifts `feature.properties`;
16
+ * rest-json lifts the item itself). No id resolves → no record (skipped).
17
+ */
18
+ import type { IceItem, IceRecordData, IceWarning } from '../types.js';
19
+ /** One raw item straight off the wire (a GeoJSON feature, an RSS item, a CSV row, …). */
20
+ export type RawRecord = Record<string, unknown>;
21
+ /** The kind every record shim produces: core's generic record (#2109). */
22
+ export declare const RECORD_KIND = "record";
23
+ /**
24
+ * A record shim's options (#2109): the site's `ngdpbase.import.shims.<id>.options`,
25
+ * with the run's own over them (a feed source's settings, an import's).
26
+ * Each is optional and generic: where the items are, which field is the id,
27
+ * a field map, and the few format settings (CSV's delimiter and preamble, an
28
+ * index's links).
29
+ */
30
+ export interface RecordShimOptions {
31
+ /** Dot-path to each item's id. */
32
+ recordIdField?: string;
33
+ /** Dot-path map: property → source path. */
34
+ map?: Record<string, string>;
35
+ /** Dot-path to the items array (rest-json, xml, rss-atom, xml-index). */
36
+ itemsPath?: string;
37
+ /** csv: the field delimiter. Default ','. */
38
+ delimiter?: string;
39
+ /** csv: lines skipped before the header (#1102). Default 0. */
40
+ skipLines?: number;
41
+ /** xml-index: a regex matched against the index's hrefs. */
42
+ linkPattern?: string;
43
+ /** xml-index: the most item documents fetched in one run. */
44
+ maxItems?: number;
45
+ }
46
+ /**
47
+ * The options as a shim reads them: each of the right type, or absent. The
48
+ * caller (a feed's configuration) validated its own; this only refuses a
49
+ * value of the wrong type from anywhere.
50
+ */
51
+ export declare function recordOptions(raw: Record<string, unknown>): RecordShimOptions;
52
+ export declare function buildRecord(raw: RawRecord, opts: RecordShimOptions, defaultProps: Record<string, unknown> | undefined): IceRecordData | null;
53
+ /**
54
+ * The key the engine knows a record by (per-item duplicates, #2109): its own
55
+ * id with a hash of its content. The id alone would skip a record that
56
+ * changed upstream under the same id (a revised advisory), so the hash is
57
+ * the version. A receiver's own duplicate check is still its own (the feeds
58
+ * add-on compares the hash its record store keeps).
59
+ */
60
+ export declare function recordItemKey(rec: IceRecordData): string;
61
+ /** The `record` items of one read, from its records in order; null records (no id) are dropped. */
62
+ export declare function recordItems(input: IceItem, records: Array<IceRecordData | null>, warnings?: IceWarning[]): IceItem[];
63
+ /**
64
+ * Locate the items array in a JSON response when no explicit `itemsPath` is
65
+ * configured: the body itself if it's an array, else the first array found
66
+ * under common envelope keys, else any array-valued property, else [].
67
+ */
68
+ export declare function pickItemsArray(data: unknown): unknown[];
@@ -0,0 +1,14 @@
1
+ /**
2
+ * rest-json shim (#685 slice 8; a core shim since #2109) — zero dependency (JSON.parse).
3
+ *
4
+ * Generic REST GET → JSON. Locates the items array via the `itemsPath` option
5
+ * (dot-path, e.g. 'results' or 'data.events'); with none, falls back to the
6
+ * body itself if it's an array, or the first array under a common envelope
7
+ * key (results/data/items/records). Each item IS the record (its own fields
8
+ * are lifted as properties when no `map` is set).
9
+ */
10
+ import { type IceShim } from '../types.js';
11
+ import { type RawRecord } from './record.js';
12
+ /** The items of a JSON document: at `itemsPath`, or the first likely array. */
13
+ export declare function jsonItems(data: unknown, itemsPath: string | undefined): RawRecord[];
14
+ export declare const restJsonShim: IceShim;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * rss-atom shim (#913; a core shim since #2109) — dependency: fast-xml-parser
3
+ * (shared with the xml shim). RSS 2.0 and Atom 1.0 are both XML, so this is a
4
+ * thin, sensible-default wrapper over the shared XML machinery rather than a
5
+ * separate parser.
6
+ *
7
+ * Item location (when the `itemsPath` option is not set):
8
+ * - RSS 2.0 → `rss.channel.item`
9
+ * - Atom 1.0 → `feed.entry`
10
+ * - else the generic envelope detection (pickItemsArray)
11
+ *
12
+ * Id resolution (when `recordIdField` is not set): RSS `<guid>` → `<link>`;
13
+ * Atom `<id>` → `<link href>`. guid/link may parse as objects (guid with
14
+ * `@isPermaLink`, Atom link as an attribute-only element) — both are unwrapped.
15
+ */
16
+ import { type IceRecordData, type IceShim } from '../types.js';
17
+ import { type RecordShimOptions, type RawRecord } from './record.js';
18
+ /** The items of an RSS or Atom document. */
19
+ export declare function rssAtomItems(text: string, itemsPath: string | undefined): RawRecord[];
20
+ /** One RSS item or Atom entry as a record. */
21
+ export declare function rssAtomRecord(raw: RawRecord, opts: RecordShimOptions): IceRecordData | null;
22
+ export declare const rssAtomShim: IceShim;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * xml-index shim (#912; a core shim since #2109) — dependency: fast-xml-parser
3
+ * (shared with the xml shim). For "index page + N item documents, no
4
+ * combined feed" sources common to bulletin-style publishers (e.g. Washington
5
+ * VAAC ash advisories, geohazardwatch#5): an HTML/text index links to many
6
+ * separately-fetchable per-record XML files, with no single URL returning
7
+ * them all.
8
+ *
9
+ * Two phases:
10
+ * 1. The index is the file the engine hands it (a URL source, fetched by the
11
+ * engine). Item URLs are the hrefs matching the `linkPattern` option (a
12
+ * regex string), resolved against the index's URL (`metadata.sourceUrl`,
13
+ * else the file's name). Deduped, capped at the `maxItems` option
14
+ * (default 100; a site sets its own with
15
+ * `ngdpbase.import.shims.core.xml-index.options.maxItems`).
16
+ * 2. Each item document is fetched by asking the engine (`tools.fetch`), so
17
+ * every request takes the one guarded path with the source's headers
18
+ * and timeout (design rule), then XML-parsed. Each yields one record (its
19
+ * unwrapped root), or — with `itemsPath` — the items located in it. The
20
+ * item's URL is kept as `__itemUrl` so it serves as the id when the
21
+ * document has none.
22
+ *
23
+ * A single failed item fetch or parse is skipped, never failing the whole
24
+ * read: the read is then `partial`, saying which.
25
+ */
26
+ import { type IceShim } from '../types.js';
27
+ import { buildRecord, type RecordShimOptions, type RawRecord } from './record.js';
28
+ /** Extract, resolve, dedupe and cap item URLs from index HTML/text. */
29
+ export declare function extractItemUrls(html: string, baseUrl: string, linkPattern: string, maxItems: number): string[];
30
+ /** One item document's record: `__itemUrl` is plumbing, kept out of the properties but usable as the id. */
31
+ export declare function xmlIndexRecord(raw: RawRecord, opts: RecordShimOptions): ReturnType<typeof buildRecord>;
32
+ export declare const xmlIndexShim: IceShim;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * xml shim (#685 slice 8; a core shim since #2109) — dependency: fast-xml-parser
3
+ * (itself zero-dep).
4
+ *
5
+ * Generic XML → records, for sources with no JSON surface (e.g. VAAC ash
6
+ * advisories, geohazardwatch#5). The document is parsed to plain objects so
7
+ * the existing dot-path machinery applies unchanged: locate the items array
8
+ * via the `itemsPath` option (e.g. 'rss.channel.item' or
9
+ * 'advisories.advisory'), map fields via `map` / `recordIdField`.
10
+ *
11
+ * XML-specific shape rules:
12
+ * - Attributes are kept, prefixed '@' — dot-path them as e.g. 'link.@href'.
13
+ * - Element text alongside attributes lands under '#text'.
14
+ * - A single repeated element parses as an object, not a 1-element array —
15
+ * the shim coerces the located items to an array, so `itemsPath` behaves
16
+ * identically for 1 and N items.
17
+ */
18
+ import { XMLParser } from 'fast-xml-parser';
19
+ import { type IceShim } from '../types.js';
20
+ import { type RawRecord } from './record.js';
21
+ /**
22
+ * Shared fast-xml-parser instance — reused by the rss-atom (#913) and xml-index
23
+ * (#912) shims so XML shape rules stay identical across all XML-based sources.
24
+ */
25
+ export declare const xmlParser: XMLParser;
26
+ /** XML parses a lone repeated element as an object — normalize to an array. */
27
+ export declare function coerceItems(located: unknown): RawRecord[];
28
+ /** The items of an XML document: at `itemsPath`, else under its (single) root by envelope detection. */
29
+ export declare function xmlItems(text: string, itemsPath: string | undefined): RawRecord[];
30
+ export declare const xmlShim: IceShim;
@@ -20,12 +20,16 @@
20
20
  */
21
21
  import type { ActorContext } from '../context/ActorContext.js';
22
22
  import type { GuardedFetchOptions, GuardedResponse } from '../http/guardedFetch.js';
23
+ /** In a shim's `receivers`: any receiver that takes the kind the shim produces (#2109). */
24
+ export declare const ANY_RECEIVER = "*";
23
25
  /** D3: which way a shim moves data. Most are `in`; PDF (#1974) would be `both`. */
24
26
  export type ShimDirection = 'in' | 'out' | 'both';
25
27
  /**
26
28
  * The kind of a piece of content moving through the engine. `file` is raw
27
- * input bytes; `html` and `ncm` are core's; an add-on's own kinds are named
28
- * `<slug>.<kind>`, e.g. `phr.health-record`.
29
+ * input bytes; `html` and `ncm` are core's; `record` is one structured entry
30
+ * of a table or feed (`data` is `{ id, properties }`, {@link IceRecordData}),
31
+ * so a format shim such as CSV serves any receiver that takes records (#2109);
32
+ * an add-on's own kinds are named `<slug>.<kind>`, e.g. `phr.health-record`.
29
33
  */
30
34
  export type IceKind = string;
31
35
  /** A structured note from a shim, the same shape as a converter's warning. */
@@ -57,6 +61,15 @@ export interface IceItem {
57
61
  */
58
62
  itemKey?: string;
59
63
  }
64
+ /**
65
+ * The `data` of a `record` item (#2109): one row of a table or one entry of a
66
+ * feed. `id` is the entry's own id (a configured id field, the format's own
67
+ * id, or a hash of the row when it has none); `properties` its fields.
68
+ */
69
+ export interface IceRecordData {
70
+ id: string;
71
+ properties: Record<string, unknown>;
72
+ }
60
73
  /** What a shim takes. A file matches by extension or media type; a chained input by kind. */
61
74
  export interface ShimAccepts {
62
75
  /** Lower-case, with the dot: `['.xml']`. */
@@ -78,7 +91,11 @@ export interface ShimTools {
78
91
  * path, with the site's egress policy, User-Agent and timeout.
79
92
  */
80
93
  fetch(url: string, options?: Omit<GuardedFetchOptions, 'policy' | 'transport' | 'resolver'>): Promise<GuardedResponse>;
81
- /** This shim's options from site configuration (D6): `ngdpbase.import.shims.<id>.options`. */
94
+ /**
95
+ * This shim's options from site configuration (D6):
96
+ * `ngdpbase.import.shims.<id>.options`, with the run's own `options` over
97
+ * them (a feed source's parse settings, #2109).
98
+ */
82
99
  options: Record<string, unknown>;
83
100
  }
84
101
  /** What a shim's read returns. */
@@ -104,7 +121,10 @@ export interface IceShim {
104
121
  produces: IceKind;
105
122
  /**
106
123
  * Receivers it may hand to directly (ids, `core.pages` or `<slug>.<name>`).
107
- * Empty for a shim whose output only feeds another shim.
124
+ * Empty for a shim whose output only feeds another shim. {@link ANY_RECEIVER}
125
+ * (`'*'`): any receiver that takes the kind it produces — a generic format
126
+ * shim such as core's CSV, whose `record` items any add-on's receiver may
127
+ * take (#2109). Each receiver still decides with its own permission (D5).
108
128
  */
109
129
  receivers: string[];
110
130
  /** Two duplicate kinds (design rule): a whole file by fingerprint, or each item by `itemKey`. Default `file`. */
@@ -185,6 +205,13 @@ export interface ReceiveInfo {
185
205
  citation?: IceCitation;
186
206
  original?: KeptOriginal;
187
207
  }
208
+ /** What a receiver's `fileDone` is told (#2109). */
209
+ export interface FileDoneInfo extends ReceiveInfo {
210
+ /** What the source said about the file: a URL source's `metadata`. */
211
+ metadata: Record<string, unknown>;
212
+ /** Every item the file held, in order: those handed to `receive`, and those skipped as already seen. */
213
+ items: IceItem[];
214
+ }
188
215
  /** A receiver's answer for one item. `refused` is its existing permission saying no (D5). */
189
216
  export interface ReceiveOutcome {
190
217
  status: 'stored' | 'duplicate' | 'refused';
@@ -211,6 +238,29 @@ export interface IceReceiver {
211
238
  mayUse(ctx: ActorContext): Promise<boolean>;
212
239
  /** Hand one item to the receiver's own door. */
213
240
  receive(item: IceItem, ctx: ActorContext, info: ReceiveInfo): Promise<ReceiveOutcome>;
241
+ /**
242
+ * Called once per file whose shims read it through (#2109), after each of
243
+ * its items was handed to `receive` or skipped as already seen, with every
244
+ * item the file held, in order. A receiver that holds a snapshot of a
245
+ * source (a feed's records) stores it here and drops what the source no
246
+ * longer holds: an item skipped as already seen is still in the source.
247
+ * Not called for a file that failed before its items were known. Returns a
248
+ * note for the file's status; a throw fails the file.
249
+ */
250
+ fileDone?(ctx: ActorContext, info: FileDoneInfo): Promise<string | void>;
251
+ /**
252
+ * `false`: this receiver is fed only by its owner's own sources (a feed's
253
+ * schedule, #2109). The import screen never offers it and an upload into
254
+ * it is refused. Default: it takes uploads.
255
+ */
256
+ uploads?: boolean;
257
+ /**
258
+ * Where this context can open what one of its references names (#2110),
259
+ * for "your recent imports" on the import screen: a URL, or null when
260
+ * the thing is gone or this context may not open it. The receiver decides
261
+ * with its own existing read rule (D5). Without it, nothing is linked.
262
+ */
263
+ linkFor?(ref: string, ctx: ActorContext): Promise<string | null>;
214
264
  /**
215
265
  * Keep the original file itself (D11: phr keeps originals in its own
216
266
  * store). Without this, `keep` hands the original to the files receiver.
@@ -241,13 +291,36 @@ export interface IceUpload {
241
291
  mediaType?: string;
242
292
  }
243
293
  /**
244
- * A source (D1). The first slice reads uploads; a URL fetched on a schedule
245
- * (D10, feeds) and a server folder join this union later.
294
+ * A URL the engine fetches as the run's one file (D1, D10, #2109): through
295
+ * the one guarded path (`guardedFetch`, #1139) with the site's egress policy
296
+ * and default User-Agent, and the source's own headers and timeout (#1959),
297
+ * which also apply to every follow-up fetch a shim asks of the engine during
298
+ * the run. A feed's schedule is its owner's job; the engine fetches when run.
299
+ */
300
+ export interface IceUrlSource {
301
+ type: 'url';
302
+ url: string;
303
+ /** The name the fetched file gets in reports and the ledger. Default: the URL. */
304
+ name?: string;
305
+ /** Request headers for every fetch of the run (a `User-Agent` replaces the site default). */
306
+ headers?: Record<string, string>;
307
+ /** Each fetch's time limit, ms. Default `ngdpbase.fetch-timeout-ms`. */
308
+ timeoutMs?: number;
309
+ /**
310
+ * Facts the source states about the file, carried on it and on every item
311
+ * read from it (a feed source's id). The engine adds `sourceUrl`, the URL
312
+ * fetched, so a shim can resolve the file's relative links.
313
+ */
314
+ metadata?: Record<string, unknown>;
315
+ }
316
+ /**
317
+ * A source (D1): uploaded files, or a URL fetched when the run starts
318
+ * (#2109). A server folder joins this union later.
246
319
  */
247
320
  export type IceSource = {
248
321
  type: 'upload';
249
322
  files: IceUpload[];
250
- };
323
+ } | IceUrlSource;
251
324
  /** One import, as a caller asks for it. */
252
325
  export interface IceRunRequest {
253
326
  source: IceSource;
@@ -267,6 +340,12 @@ export interface IceRunRequest {
267
340
  * value a client sent.
268
341
  */
269
342
  scope?: string;
343
+ /**
344
+ * Options for this run's shims, over the site's (`ShimTools.options`): a
345
+ * feed source's parse settings (#2109). Server-built; never a value a
346
+ * client sent.
347
+ */
348
+ options?: Record<string, unknown>;
270
349
  }
271
350
  /** Per-file status (first slice). */
272
351
  export type IceFileStatus = 'imported' | 'partial' | 'unchanged' | 'duplicate' | 'noise' | 'unreadable' | 'refused' | 'failed';
@@ -318,6 +397,25 @@ export interface LedgerFileEntry {
318
397
  /** The original kept for this fingerprint, whatever became of the conversion (D11). */
319
398
  original?: LedgerOriginal;
320
399
  }
400
+ /**
401
+ * One file in a user's recent imports (#2110), read from the ledger: what
402
+ * the import screen lists, so the list survives a restart. Only the user's
403
+ * own entries, from the receivers they may use.
404
+ */
405
+ export interface RecentImport {
406
+ name: string;
407
+ status: IceFileStatus;
408
+ message?: string;
409
+ /** When the ledger last recorded this file, ISO. */
410
+ at: string;
411
+ /** The receiver it went into. */
412
+ receiver: string;
413
+ receiverLabel?: string;
414
+ /** How many references the receiver gave (pages, records). */
415
+ refs: number;
416
+ /** Where the user can open the first of them (`linkFor`), when they may. */
417
+ link?: string;
418
+ }
321
419
  /**
322
420
  * Where the engine keeps fingerprints, per-file status and item keys, per
323
421
  * scope. One interface so a database table can replace the file store when
@@ -328,5 +426,9 @@ export interface IceLedger {
328
426
  recordFile(scope: string, entry: LedgerFileEntry): Promise<void>;
329
427
  hasItem(scope: string, itemKey: string): Promise<boolean>;
330
428
  recordItem(scope: string, itemKey: string): Promise<void>;
429
+ /** Which of these item keys the scope has seen: one read for a whole file's items (#2109). */
430
+ seenItems(scope: string, itemKeys: string[]): Promise<Set<string>>;
431
+ /** Record several item keys in one write (#2109). */
432
+ recordItems(scope: string, itemKeys: string[]): Promise<void>;
331
433
  files(scope: string): Promise<LedgerFileEntry[]>;
332
434
  }
@@ -289,9 +289,27 @@ export interface AddonStatusDetails {
289
289
  message?: string;
290
290
  [key: string]: unknown;
291
291
  }
292
+ /** The pages an add-on's card can be shown on: `/admin` (the default) and the Executive Hub, `/executive` (#2093). */
293
+ export type AddonCardSurface = 'admin' | 'executive';
294
+ /** One labelled figure on an Executive Hub card (`Funds`, `$12,400.00`). The add-on formats the value. */
295
+ export interface AddonCardFigure {
296
+ label: string;
297
+ value: string | number;
298
+ }
299
+ /** What an Executive Hub card shows a viewer: its figures, and an optional line under them. */
300
+ export interface AddonCardSummary {
301
+ figures?: AddonCardFigure[];
302
+ message?: string;
303
+ }
292
304
  /**
293
- * Card registered by an add-on for display on the admin dashboard.
294
- * The card's live stats are sourced from the add-on's existing status() method.
305
+ * Card registered by an add-on for display on the admin dashboard, or, with
306
+ * `surface: 'executive'`, on the Executive Hub (#2093). An add-on registers
307
+ * at most one card per surface.
308
+ *
309
+ * An admin card's live stats come from the add-on's existing status() method.
310
+ * An executive card names the `permission` a viewer must hold to see it, and
311
+ * its figures come from `summary(ctx)`, called with the viewer's context, so
312
+ * the add-on's own doors decide what it reads.
295
313
  */
296
314
  export interface AddonDashboardCard {
297
315
  /** Must match the add-on's registered name */
@@ -300,8 +318,25 @@ export interface AddonDashboardCard {
300
318
  title: string;
301
319
  /** Font Awesome class, e.g. 'fas fa-wpforms' */
302
320
  icon: string;
303
- /** URL of the add-on's admin page */
321
+ /** The page the card links to: the add-on's admin page, or for an executive card the page with the detail */
304
322
  adminUrl: string;
323
+ /** Where the card is shown; omitted means `/admin` */
324
+ surface?: AddonCardSurface;
325
+ /** Executive cards: the permission a viewer must hold to see the card (the add-on's own read permission). Required there */
326
+ permission?: string;
327
+ /** Executive cards: the card's figures for this viewer, read with their context. Throw to say they could not be read */
328
+ summary?: (ctx: ActorContext) => Promise<AddonCardSummary>;
329
+ }
330
+ /** An executive card as the hub shows it to one viewer: the figures read for them, or that they could not be. */
331
+ export interface ExecutiveCardView {
332
+ addonName: string;
333
+ title: string;
334
+ icon: string;
335
+ adminUrl: string;
336
+ figures: AddonCardFigure[];
337
+ message: string;
338
+ /** True when the add-on's summary threw; the card says its figures could not be read. */
339
+ failed: boolean;
305
340
  }
306
341
  /** #2100: the PDP refused an administrator's add-on Update or Check. */
307
342
  export declare class AddonUpdateForbiddenError extends Error {
@@ -823,10 +858,19 @@ declare class AddonsManager extends BaseManager {
823
858
  */
824
859
  registerDashboardCard(card: AddonDashboardCard): void;
825
860
  /**
826
- * Return all registered dashboard cards in registration order.
827
- * Called by the admin dashboard route to populate the addon cards row.
861
+ * Return the registered cards for one surface, in registration order.
862
+ * The admin dashboard route calls it with no argument for the `/admin` cards.
863
+ */
864
+ getDashboardCards(surface?: AddonCardSurface): AddonDashboardCard[];
865
+ /**
866
+ * The Executive Hub's cards for one viewer (#2093): each card whose
867
+ * permission the PDP permits `ctx`, asked with that context, with the
868
+ * figures its add-on's `summary(ctx)` reads for them. A card the viewer may
869
+ * not see is left out; a summary that throws shows the card with its
870
+ * figures marked as not read, and is logged by add-on name only. No
871
+ * context, or no PDP, shows no cards.
828
872
  */
829
- getDashboardCards(): AddonDashboardCard[];
873
+ executiveCardsFor(ctx: ActorContext): Promise<ExecutiveCardView[]>;
830
874
  /**
831
875
  * Get list of all discovered add-on names
832
876
  */