@jwilleke/ngdpbase-types 5.34.0 → 5.35.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.
@@ -320,6 +320,33 @@ declare class AttachmentManager extends BaseManager implements CatalogSource {
320
320
  * with no person behind it are refused. A refusal reads 'Permission denied'
321
321
  * so the route answers it as one (401 for an anonymous caller).
322
322
  */
323
+ /**
324
+ * Once, at start-up (#2097): the shared pool's uploaders and editors become
325
+ * identity @ids (`IdentityManager.convertRecordedUser`: an orphan for a
326
+ * name with no account, `orphan:unknown` for the pool's `Unknown`), written
327
+ * once through the provider, under the system context. A second start
328
+ * writes nothing. Counts only in the log; orphans on the dashboard. When the
329
+ * identity store cannot answer, nothing is written and the next start tries
330
+ * again. A vault's files convert when the vault is opened
331
+ * ({@link convertVaultUploaders}).
332
+ */
333
+ convertUploadersAtBoot(): Promise<void>;
334
+ /**
335
+ * The uploaders in the vaults of the context's own user that it can open
336
+ * (#2097), when they are opened: at sign-in and unlock. Each vault's files
337
+ * index, through its own I/O. Nothing is written when the identity store
338
+ * cannot answer; the next opening tries again.
339
+ */
340
+ convertVaultUploaders(ctx: ActorContext): Promise<number>;
341
+ /** Counts only, never a name (#2097). */
342
+ private reportUploaderConversion;
343
+ /** The identity store could not answer: nothing is converted this start, and the next start tries again (#2097). */
344
+ private postponeUploaderConversion;
345
+ /**
346
+ * What a file record stores for the user `username` (#2097): their identity
347
+ * @id, or the username when there is none or the identity store cannot answer.
348
+ */
349
+ private userReference;
323
350
  private static uploaderOf;
324
351
  /** A file is added to a private page only by someone who may edit it: the page's own decision (#1398). */
325
352
  private assertMayEditPage;
@@ -309,6 +309,83 @@ declare class IdentityManager extends BaseManager {
309
309
  * postpones the whole pass when it is false.
310
310
  */
311
311
  canAnswer(): boolean;
312
+ /**
313
+ * {@link convertReference} for records that may hold a value meaning "no
314
+ * user was recorded" (#2097): `migrated`, `unknown`, `import`
315
+ * ({@link NO_RECORDED_USER}) become `orphan:<value>` without a lookup,
316
+ * outcome `no-user`, so an account that later takes one of those names
317
+ * gains nothing, and the caller does not count them as needing attention.
318
+ * Everything else is `convertReference`'s, and throws as it does.
319
+ */
320
+ convertRecordedUser(ctx: ActorContext, stored: string): Promise<{
321
+ value: string;
322
+ outcome: 'kept' | 'orphan' | 'converted' | 'orphaned' | 'no-user';
323
+ }>;
324
+ /**
325
+ * What a new record stores for the user `username` (#2097): their identity
326
+ * `@id`; a "no user recorded" value (`migrated`, `unknown`, `import`) as
327
+ * its orphan form, so no account ever gains it; a principal (`anonymous`,
328
+ * the system principal), an `@id` or an orphan as it is; a name with no
329
+ * account, or any name when the store cannot answer ({@link identityIdOf}),
330
+ * as given. The one rule for page authors and editors, tombstones and file
331
+ * uploaders.
332
+ */
333
+ storedReferenceOf(ctx: ActorContext, username: string): Promise<string>;
334
+ /**
335
+ * One start-up conversion pass over stored references (#2097): `convert`
336
+ * is {@link convertRecordedUser}'s value, and `counts` tallies its outcomes
337
+ * (values converted to an @id, orphans found or made, "no user recorded"
338
+ * values) for the caller's counts-only report. When the store cannot
339
+ * answer it throws `IdentityStoreUnavailableError`, so the caller postpones
340
+ * its pass rather than skip a record.
341
+ */
342
+ conversionPass(ctx: ActorContext): {
343
+ convert: (stored: string) => Promise<string>;
344
+ counts: {
345
+ converted: number;
346
+ orphans: number;
347
+ noUser: number;
348
+ };
349
+ };
350
+ /**
351
+ * Whether a stored reference names the user `username` (#2097): it is their
352
+ * identity `@id`, or, on a record not yet converted, their username. An
353
+ * orphan (`orphan:<name>`) and a "no user recorded" value never match
354
+ * anyone, so a later account with the same name inherits nothing. The `@id`
355
+ * is looked up with {@link identityIdOf}: false when the store cannot answer.
356
+ */
357
+ refersTo(ctx: ActorContext, stored: unknown, username: string | null | undefined): Promise<boolean>;
358
+ /**
359
+ * The stored values that name the user `username` (#2097): their identity
360
+ * `@id` and, for records not yet converted, their username — the batch form
361
+ * of {@link refersTo}, for a list filter that compares many records (the
362
+ * page index, the search index). A "no user recorded" name is left out, and
363
+ * no orphan is ever in it. Only the username when the store cannot answer.
364
+ */
365
+ referencesOf(ctx: ActorContext, username: string | null | undefined): Promise<string[]>;
366
+ /**
367
+ * The username a stored reference names (#2097), for code that needs the
368
+ * sign-in name (a vault path, a preference): an `@id` gives its account's
369
+ * username, an orphan gives null, anything else (a username not yet
370
+ * converted, `anonymous`, the system principal) is returned as it is. Null
371
+ * for an `@id` with no account (deleted, or a contact's) and when the store
372
+ * cannot answer, logged without the value; a refusal still throws.
373
+ */
374
+ usernameOf(ctx: ActorContext, stored: unknown): Promise<string | null>;
375
+ /**
376
+ * How a stored reference to a user is shown to people (#2097, operator
377
+ * 2026-10-26): the username followed by the id, `jim (urn:uuid:3f6c…)`. An
378
+ * orphan shows as `jim (no account)`, an `@id` whose account is gone as
379
+ * `urn:uuid:… (no account)`; anything else (a username not yet converted,
380
+ * `anonymous`, the system principal) as it is. Resolved when shown, never
381
+ * stored. Never throws for a store that is down.
382
+ *
383
+ * `ctx` is whoever the name is shown to. Showing who wrote a page needs no
384
+ * `identity-read`: only the username and the id are shown, as a page always
385
+ * showed its author's username, so the record is read as core's own lookup
386
+ * (the system context, naming the caller in its reason).
387
+ */
388
+ describeReference(ctx: ActorContext, stored: unknown): Promise<string>;
312
389
  private requireProvider;
313
390
  private getMetrics;
314
391
  private normalizeProviderName;
@@ -258,6 +258,31 @@ export declare const CAPTURE_VAULT_MIGRATION = "capture-vault";
258
258
  export declare const CATEGORY_VAULT_MIGRATION = "category-vaults";
259
259
  /** Private slugs from `private--…` to `vaults--…` (#1507). */
260
260
  export declare const VAULT_SLUG_MIGRATION = "vault-slugs";
261
+ /** Page authorship stored as identity @ids (#2097): every page and its history visited once; recorded per site and per vault. */
262
+ export declare const AUTHORSHIP_ID_MIGRATION = "authorship-identity-ids";
263
+ /** What one authorship conversion pass did (#2097), counts only. */
264
+ export interface AuthorshipConversion {
265
+ /** `done`, or `postponed` when the identity store could not answer. */
266
+ status: 'done' | 'postponed';
267
+ /** Every page visited (`full`), or only those whose index entry still names a username. */
268
+ full: boolean;
269
+ pagesVisited: number;
270
+ pagesChanged: number;
271
+ /** Pages whose frontmatter could not be rewritten line by line, left as they are. */
272
+ pagesRefused: number;
273
+ versionsChanged: number;
274
+ tombstonesChanged: number;
275
+ converted: number;
276
+ orphans: number;
277
+ /** Orphans on shipped (documentation, system) and add-on pages: their author is upstream's, not a site user; not needing attention. */
278
+ shippedOrphans: number;
279
+ noUser: number;
280
+ /** Pages and tombstones skipped for their own reason (a file that cannot be read or written, a full disk, a vault that cannot be opened). */
281
+ pagesSkipped: number;
282
+ /** The error class of each skip (`EACCES`, `ENOSPC`, ...), never a message, name or path. */
283
+ skipErrors: string[];
284
+ ms: number;
285
+ }
261
286
  /**
262
287
  * Minimal WikiContext interface for type safety
263
288
  * TODO: Convert WikiContext.js to TypeScript and import proper type
@@ -377,6 +402,15 @@ declare class PageManager extends BaseManager implements CatalogSource {
377
402
  * #754 backfill (v3.33.0).
378
403
  */
379
404
  readonly types: readonly SchemaType[];
405
+ /** One write at a time per page, by uuid (#2097), when the provider keeps no such lock itself. */
406
+ private readonly pageWrites;
407
+ /**
408
+ * Run `task` as the only write on page `key` (#2097): saves, deletes, the
409
+ * authorship conversion, and the provider's own restores and purges. The
410
+ * lock is the provider's (`withPageWrite`), so its trash work takes the same
411
+ * one; a provider without it gets this manager's.
412
+ */
413
+ private pageWrite;
380
414
  /**
381
415
  * On-disk schema version for the page-frontmatter shape (Decision 6).
382
416
  * Bump when the persisted Article-relevant fields on a page change. v1 is
@@ -867,6 +901,12 @@ declare class PageManager extends BaseManager implements CatalogSource {
867
901
  * @throws PageContentValidationError when a filter reports severity 'error'
868
902
  */
869
903
  private assertContentPasses;
904
+ /**
905
+ * What a page stores for the user `name` (#2097): `IdentityManager.storedReferenceOf`.
906
+ */
907
+ private referenceTo;
908
+ /** `audience` and each `access[action]` of a save, user entries as {@link referenceTo} stores them (#2097). */
909
+ private convertAccessListsForSave;
870
910
  /**
871
911
  * The frontmatter a save writes, from the frontmatter it was given (#1462).
872
912
  *
@@ -1415,6 +1455,12 @@ declare class PageManager extends BaseManager implements CatalogSource {
1415
1455
  * feed. New code should prefer this over enumerating getAllPages().
1416
1456
  */
1417
1457
  getRecentChanges(ctx: ActorContext, options?: RecentChangesOptions): Promise<RecentChangeEntry[]>;
1458
+ /**
1459
+ * A principal list (roles and the username) with the user's identity @id
1460
+ * added (#2097), so a listing matches an access-list entry stored as the
1461
+ * @id as the page view does (`PolicyInformationPoint` principals).
1462
+ */
1463
+ private principalsWithIdentityId;
1418
1464
  /**
1419
1465
  * Every indexed page carrying an `addon` provenance stamp.
1420
1466
  * Used by addon orphan detection; reads the in-memory index, no disk I/O.
@@ -1440,15 +1486,25 @@ declare class PageManager extends BaseManager implements CatalogSource {
1440
1486
  * `creator` only.
1441
1487
  */
1442
1488
  getPagesByCreator(username: string, ctx: ActorContext, options?: GetPagesByCreatorOptions): Promise<RecentChangeEntry[]>;
1489
+ /**
1490
+ * The stored values that name `username` (#2097): their identity @id and, for
1491
+ * pages not yet converted, the username (`IdentityManager.referencesOf`), so
1492
+ * "my pages" lists converted and unconverted pages alike and an orphan never.
1493
+ */
1494
+ private referencesOf;
1443
1495
  /**
1444
1496
  * Pages most recently edited by a user (#640 Phase 2).
1445
1497
  */
1446
1498
  getPagesByEditor(username: string, ctx: ActorContext, options?: PagesScanOptions): Promise<RecentChangeEntry[]>;
1447
1499
  /**
1448
1500
  * Pages whose frontmatter audience matches any of the caller's principals,
1449
- * excluding pages the caller already owns (#640 Phase 2).
1501
+ * excluding pages the caller already owns (#640 Phase 2). `options.username`
1502
+ * names the caller, so a page whose author is their identity @id counts as
1503
+ * theirs (#2097).
1450
1504
  */
1451
- getPagesSharedWith(principals: string[], options?: PagesScanOptions): Promise<RecentChangeEntry[]>;
1505
+ getPagesSharedWith(principals: string[], options?: PagesScanOptions & {
1506
+ username?: string;
1507
+ }): Promise<RecentChangeEntry[]>;
1452
1508
  /**
1453
1509
  * The private-container decision for a page (docs/private-stores.md,
1454
1510
  * Access). PolicyInformationPoint's Tier 0 asks this, so every `canAccess` on a page —
@@ -1538,6 +1594,74 @@ declare class PageManager extends BaseManager implements CatalogSource {
1538
1594
  migrateVaultUrlsInPublicPages(ctx: ActorContext): Promise<number>;
1539
1595
  /** Every vault on disk, as `owner/store` — encrypted ones included, since only their folder names are read. */
1540
1596
  private listVaults;
1597
+ /**
1598
+ * The page-authorship conversion at start-up (#2097), on the public pages:
1599
+ * best-effort, never throws, and runs while the site serves (WikiEngine does
1600
+ * not wait for it). See {@link convertAuthorship}.
1601
+ */
1602
+ convertAuthorshipAtBoot(): Promise<AuthorshipConversion | null>;
1603
+ /**
1604
+ * Convert the public pages' authorship to identity @ids (#2097, operator
1605
+ * 2026-10-26), through this door's own write path, one page at a time
1606
+ * with the saves ({@link pageWrite}):
1607
+ *
1608
+ * - each page's frontmatter `author` and `editor`, written without
1609
+ * touching its body, its other fields or its `lastModified`, and without
1610
+ * a new version; its page index entry follows;
1611
+ * - its version history's editors (`manifest.json`), in place;
1612
+ * - the tombstones' `author`, `editor` and `deletedBy`;
1613
+ * - then the search index, rebuilt from the pages once if anything changed.
1614
+ *
1615
+ * Each value gets `IdentityManager.convertRecordedUser`'s answer: a
1616
+ * username with an account becomes its @id; one with none `orphan:<name>`
1617
+ * (counted, and on the dashboard: only an administrator acts on those
1618
+ * pages); `migrated`, `unknown` and `import` `orphan:<value>` without a
1619
+ * lookup (counted apart, not needing attention); @ids, orphans and
1620
+ * principals are kept, so a second run writes nothing.
1621
+ *
1622
+ * The first complete pass visits every page and its history, and is then
1623
+ * recorded in the site's migrations record; later passes visit only pages
1624
+ * whose index entry still holds a value to convert. Pages are taken in
1625
+ * batches of `ngdpbase.page.authorship-conversion.batch-size`, yielding
1626
+ * between batches. When the identity store cannot answer (`canAnswer()`
1627
+ * false, or a lookup fails mid-pass) the pass stops, says so on the
1628
+ * dashboard, and the next start tries again. Logs counts only. Vault pages
1629
+ * convert when their vault is opened ({@link convertVaultAuthorship}).
1630
+ */
1631
+ convertAuthorship(ctx: ActorContext): Promise<AuthorshipConversion | null>;
1632
+ /**
1633
+ * The authorship conversion for the vaults of `owner` this context can open
1634
+ * (#2097), when they are opened: at sign-in and unlock, where the owner's
1635
+ * keys are live. Each vault's pages (frontmatter, its own page index entry,
1636
+ * history, access lists) and the owner's vault trash; each vault is
1637
+ * recorded as done in its own migrations record and skipped from then on.
1638
+ * Postponed, and tried at the next opening, when the identity store cannot
1639
+ * answer; a page or tombstone that fails for its own reason is skipped and
1640
+ * counted, and keeps its vault from being recorded as done.
1641
+ */
1642
+ convertVaultAuthorship(ctx: ActorContext, owner: string): Promise<AuthorshipConversion | null>;
1643
+ /**
1644
+ * How a frontmatter `audience` or `access[action]` entry converts (#2097):
1645
+ * an entry naming a user becomes their identity @id with the pass's own rule
1646
+ * (`orphan:<name>` for a name with no account, counted); every other kind is
1647
+ * kept as written: a role (declared in `ngdpbase.roles.definitions` or held
1648
+ * as a role record), the pseudo-roles `All`, `Authenticated`, `Anonymous`
1649
+ * and `Asserted`, an `addon:`/`job:` or any other `kind:` entry, an @id or
1650
+ * an orphan. A name that is both a role and a username stays the role.
1651
+ */
1652
+ private accessListConverter;
1653
+ /** Every role name a principal list may hold, lower-cased (#2097): declared roles, role records, and the pseudo-roles. */
1654
+ private roleNames;
1655
+ /** Whether an index entry still holds an author or editor the conversion would change (#2097). No lookup is written. */
1656
+ private authorshipNeedsConversion;
1657
+ /** The system-categories whose pages are shipped with the code (source `shipped`), lower-cased (#2097). */
1658
+ private shippedCategories;
1659
+ /** `ngdpbase.page.authorship-conversion.batch-size`: pages converted between yields (#2097). */
1660
+ private authorshipBatchSize;
1661
+ /** Counts only, never a name (#2097): the log, and the dashboard for orphans and refused pages. */
1662
+ private reportAuthorshipConversion;
1663
+ /** The identity store could not answer: nothing more is converted this start, and the next start tries again (#2097). */
1664
+ private postponeAuthorshipConversion;
1541
1665
  /**
1542
1666
  * Rewrite `[Title]` to `[store/Title]` in the private pages that mean it.
1543
1667
  *
@@ -45,6 +45,10 @@ interface AdvancedSearchOptions {
45
45
  };
46
46
  /** #774: which date field `dateRange` filters on. Default `'modified'`. */
47
47
  dateField?: 'modified' | 'created';
48
+ /** A username, or several: pages whose author is that user (#2097: their identity @id or, not yet converted, the username). */
49
+ author?: string | string[];
50
+ /** As `author`, for the last editor. */
51
+ editor?: string | string[];
48
52
  [key: string]: unknown;
49
53
  }
50
54
  /**
@@ -240,6 +244,12 @@ declare class SearchManager extends BaseManager {
240
244
  * });
241
245
  */
242
246
  advancedSearchWithContext(wikiContext: WikiContext, options?: AdvancedSearchOptions): Promise<SearchResult[]>;
247
+ /**
248
+ * The stored values a search for `who` matches (#2097): for each username,
249
+ * that user's identity @id and the username (`IdentityManager.referencesOf`).
250
+ * Without IdentityManager, the values as given.
251
+ */
252
+ private referencesOf;
243
253
  /**
244
254
  * Search for pages matching the query
245
255
  *
@@ -414,6 +414,15 @@ declare class MarkupParser extends BaseManager {
414
414
  * claiming a page is missing.
415
415
  */
416
416
  private resolvePrivateLinkTitles;
417
+ /**
418
+ * The username a PUBLIC page's author names, for a `[store/Title]` link on
419
+ * it (#1457, #2097): the author is stored as an identity @id, and a vault is
420
+ * named by its owner's username, so the @id is resolved once per parse
421
+ * through IdentityManager. Null when a caller already said who the owner
422
+ * is, on a private page (its name carries the owner), on a page with no
423
+ * such link, and for an author that names no account.
424
+ */
425
+ private resolveAuthorOwner;
417
426
  generateCacheKey(content: string, context: ParseContextData): string;
418
427
  /**
419
428
  * Extract style blocks using state-stack parsing for proper nesting support
@@ -186,6 +186,12 @@ declare class ParseContext {
186
186
  * Resolved once per parse by MarkupParser, never per link.
187
187
  */
188
188
  privateLinkTitles: Map<string, Set<string>> | null;
189
+ /**
190
+ * The username of a public page's author, when the author is stored as an
191
+ * identity @id (#2097): what a `[store/Title]` link on it resolves in.
192
+ * Resolved once per parse by MarkupParser; null when not needed.
193
+ */
194
+ pageOwner: string | null;
189
195
  /**
190
196
  * Hand finished HTML from a syntax handler to MarkupParser; write the
191
197
  * returned placeholder into the content instead (#1623). The HTML is merged
@@ -101,10 +101,14 @@ export interface SearchCriteria {
101
101
  statuses?: string[];
102
102
  /** Fields to search in (title, content, category, keywords, or all) */
103
103
  searchIn?: string[];
104
- /** Author to filter by (page creator — matches metadata.author) */
105
- author?: string;
106
- /** Editor to filter by (last modifier — matches metadata.editor) */
107
- editor?: string;
104
+ /**
105
+ * Author to filter by (page creator — matches metadata.author). A list
106
+ * matches any of its values: SearchManager turns a username into the
107
+ * values that name that user, their identity @id and the username (#2097).
108
+ */
109
+ author?: string | string[];
110
+ /** Editor to filter by (last modifier — matches metadata.editor); a list as for `author`. */
111
+ editor?: string | string[];
108
112
  /** Date range */
109
113
  dateRange?: {
110
114
  from?: string;
@@ -203,6 +203,20 @@ declare class BasicAttachmentProvider extends BaseAttachmentProvider implements
203
203
  * @private
204
204
  */
205
205
  private saveMetadata;
206
+ /**
207
+ * The shared pool's uploaders and editors (#2097): each record's
208
+ * `author.name` and `editor.name` become what `convert` answers (the
209
+ * identity @id), and the metadata file is written once when anything
210
+ * changed. `convert` may throw (the identity store cannot answer); then
211
+ * nothing is written. Returns how many values changed.
212
+ */
213
+ convertPoolReferences(convert: (stored: string) => Promise<string>): Promise<number>;
214
+ /**
215
+ * One vault's uploaders (#2097): each entry's `author` in the vault's own
216
+ * files index, through the vault's I/O. Written once when anything
217
+ * changed; nothing when `convert` throws. Returns how many values changed.
218
+ */
219
+ convertStoreReferences(location: StoreFileLocation, convert: (stored: string) => Promise<string>): Promise<number>;
206
220
  /**
207
221
  * Generate unique attachment ID using SHA-256 hash of content
208
222
  * @param buffer - File data
@@ -188,6 +188,13 @@ declare class PolicyInformationPoint extends BaseManager {
188
188
  * a role its user holds left the job out.
189
189
  */
190
190
  private matchableNow;
191
+ /**
192
+ * The subject as matched, carrying its identity @id (#2097): page access
193
+ * lists name users by @id. Matching only: the subject is never changed for
194
+ * any other use. No lookup for nobody or a reserved name; null when the
195
+ * identity store cannot answer, and then only entries not yet converted match.
196
+ */
197
+ private withIdentityId;
191
198
  /**
192
199
  * Who is making this request: the subject the session middleware resolved,
193
200
  * else the session's account resolved now, else the anonymous visitor.
@@ -357,6 +364,14 @@ declare class PolicyInformationPoint extends BaseManager {
357
364
  * audience terms by the NCM funnel (#1446).
358
365
  */
359
366
  private walkPageTiers;
367
+ /**
368
+ * Whether `username` is the page's author (#2097): `IdentityManager.refersTo`,
369
+ * so an author stored as an identity @id matches only its own account, one
370
+ * not yet converted matches its username, and an orphan matches nobody.
371
+ */
372
+ private isPageAuthor;
373
+ /** The username a page's stored author names (#2097), for the private-container fallback. */
374
+ private authorUsername;
360
375
  /**
361
376
  * The private-container decision for something that is not a page — a file
362
377
  * in a store (docs/private-stores.md, Access). The same rule a
@@ -107,6 +107,12 @@ export interface GetPagesByCreatorOptions {
107
107
  * failure mode). Frontmatter is the source of truth and needs no back-fill.
108
108
  */
109
109
  systemKeywords?: string[];
110
+ /**
111
+ * The stored values that name the user (#2097): their identity @id and,
112
+ * for pages not yet converted, their username (`IdentityManager.referencesOf`).
113
+ * Set by PageManager; a provider compares `author` against these. Default `[username]`.
114
+ */
115
+ references?: string[];
110
116
  }
111
117
  /**
112
118
  * Options for {@link PageProvider.getPagesByEditor} / {@link PageProvider.getPagesSharedWith} (#640 Phase 2).
@@ -116,6 +122,12 @@ export interface PagesScanOptions {
116
122
  limit?: number;
117
123
  /** Sort order. Default `lastModified-desc`. */
118
124
  sortBy?: 'lastModified-desc' | 'title-asc';
125
+ /**
126
+ * The stored values that name the user (#2097), as {@link GetPagesByCreatorOptions.references}:
127
+ * `getPagesByEditor` compares `editor` against them, `getPagesSharedWith` leaves out pages
128
+ * whose `author` is one of them. Set by PageManager.
129
+ */
130
+ references?: string[];
119
131
  }
120
132
  /**
121
133
  * Single entry returned by {@link PageProvider.getRecentChanges}.
@@ -362,8 +374,8 @@ export interface PageProvider extends BaseProvider {
362
374
  /**
363
375
  * Return pages owned by the given user (#640).
364
376
  *
365
- * "Owned" matches by `author` or `creator` field — pageIndex keeps these in
366
- * lockstep on save. The visibility filter is intentionally NOT applied here
377
+ * "Owned" matches by `author` (any of `options.references`, #2097) or, on a
378
+ * private page, by `creator`, its container owner's username. The visibility filter is intentionally NOT applied here
367
379
  * because by definition a user can see their own pages: the route handler
368
380
  * verifies that `username === currentUser.username` before calling this.
369
381
  *
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The identity store could not answer a conversion's lookup (#2097).
3
+ *
4
+ * A start-up conversion tells this apart from every other failure: this one
5
+ * postpones the whole pass (no username is orphaned because it could not be
6
+ * looked up), while a page that fails for its own reason (a file it cannot
7
+ * read or write, a full disk, a vault it cannot open) is skipped, counted and
8
+ * the pass goes on. `IdentityManager.conversionPass` throws it; the providers
9
+ * and managers running a pass rethrow it and catch anything else per record.
10
+ */
11
+ export declare class IdentityStoreUnavailableError extends Error {
12
+ constructor(cause: unknown);
13
+ }
14
+ /**
15
+ * How a failure is named in a counts-only report: its error code (`EACCES`,
16
+ * `ENOSPC`) or class name, never its message (which may hold a path or a
17
+ * name).
18
+ */
19
+ export declare function errorClassOf(error: unknown): string;
@@ -70,5 +70,21 @@ export declare const IDENTITY_ID_PREFIX = "urn:";
70
70
  * start with it.
71
71
  */
72
72
  export declare const ORPHAN_PREFIX = "orphan:";
73
- /** True when the name belongs to a principal rather than a user, or has the contact, `@id` or orphan prefix. */
73
+ /**
74
+ * Values older records hold where no user was recorded (#2096, #2097): the
75
+ * footnote migration's `migrated`, the editor fallback `unknown` (and the
76
+ * attachment pool's `Unknown`), the JSPWiki import's `import`. Never a
77
+ * username: a start-up conversion never looks them up (an account that later
78
+ * takes one of these names must not gain the records), stores them as
79
+ * `orphan:<value>` and does not count them as needing attention.
80
+ */
81
+ export declare const NO_RECORDED_USER: readonly string[];
82
+ /** True when `value` is one of {@link NO_RECORDED_USER}, bare or as its orphan form. */
83
+ export declare function isNoRecordedUser(value: unknown): boolean;
84
+ /**
85
+ * True when the name belongs to a principal rather than a user, is a "no user
86
+ * recorded" value ({@link NO_RECORDED_USER}, #2097: an account by that name
87
+ * would take over the records that hold it), or has the contact, `@id` or
88
+ * orphan prefix.
89
+ */
74
90
  export declare function isReservedUsername(username: unknown): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jwilleke/ngdpbase-types",
3
- "version": "5.34.0",
3
+ "version": "5.35.0",
4
4
  "description": "TypeScript types for ngdpbase add-ons, and a test kit of core's own values. A dev dependency, versioned with ngdpbase.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Jim Willeke",