@jwilleke/ngdpbase-types 5.34.0 → 5.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/src/ice/ledger.d.ts +8 -0
- package/lib/src/ice/runImport.d.ts +18 -2
- package/lib/src/ice/types.d.ts +14 -5
- package/lib/src/managers/AddonsManager.d.ts +57 -0
- package/lib/src/managers/AttachmentManager.d.ts +27 -0
- package/lib/src/managers/IdentityManager.d.ts +77 -0
- package/lib/src/managers/ImportManager.d.ts +12 -0
- package/lib/src/managers/PageManager.d.ts +126 -2
- package/lib/src/managers/SearchManager.d.ts +10 -0
- package/lib/src/parsers/MarkupParser.d.ts +9 -0
- package/lib/src/parsers/context/ParseContext.d.ts +6 -0
- package/lib/src/providers/BaseSearchProvider.d.ts +8 -4
- package/lib/src/providers/BasicAttachmentProvider.d.ts +14 -0
- package/lib/src/security/PolicyInformationPoint.d.ts +15 -0
- package/lib/src/types/Provider.d.ts +14 -2
- package/lib/src/utils/addonInstaller.d.ts +90 -0
- package/lib/src/utils/addonLock.d.ts +21 -3
- package/lib/src/utils/addonLock.js +2 -0
- package/lib/src/utils/auditEventNames.generated.d.ts +1 -1
- package/lib/src/utils/auditEventNames.generated.js +1 -1
- package/lib/src/utils/identityStoreUnavailable.d.ts +19 -0
- package/lib/src/utils/username.d.ts +17 -1
- package/package.json +1 -1
package/lib/src/ice/ledger.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import type { IceLedger, LedgerFileEntry } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The ledger file form (#2013). A scope is bound to its receiver and user
|
|
4
|
+
* (`boundScope`); a file without this form was written before that binding,
|
|
5
|
+
* under a scope any caller could name, so it belongs to nobody: it is read
|
|
6
|
+
* as empty, never as anyone's history, and its files are imported once more.
|
|
7
|
+
*/
|
|
8
|
+
export declare const LEDGER_FORM = 2;
|
|
2
9
|
interface ScopeFile {
|
|
10
|
+
form?: number;
|
|
3
11
|
scope: string;
|
|
4
12
|
files: LedgerFileEntry[];
|
|
5
13
|
items: string[];
|
|
@@ -21,8 +21,24 @@ export interface IceRunDeps {
|
|
|
21
21
|
}
|
|
22
22
|
/** The id of the receiver that keeps an original when the chosen receiver does not keep its own (D11). */
|
|
23
23
|
export declare const FILES_RECEIVER = "core.files";
|
|
24
|
-
/**
|
|
25
|
-
|
|
24
|
+
/**
|
|
25
|
+
* The engine's key for an item of a file it stored, so a retry of that file
|
|
26
|
+
* skips it (#2010): the file's fingerprint, the chain, and a hash of the
|
|
27
|
+
* item's content (kind, text, data, bytes). Never its position: a shim that
|
|
28
|
+
* changes or orders its output differently cannot make a retry skip the
|
|
29
|
+
* wrong item. `seen` is how many identical items came before it in this file.
|
|
30
|
+
*/
|
|
31
|
+
export declare function itemKeyOf(sha256: string, chain: string[], item: IceItem, seen: number): string;
|
|
32
|
+
/** The form of a bound scope (#2013). Scopes written before it never match one. */
|
|
33
|
+
export declare const SCOPE_FORM = "ice2";
|
|
34
|
+
/**
|
|
35
|
+
* The scope a run's fingerprints, per-file status and item keys live in
|
|
36
|
+
* (#2013): bound to the receiver and to the user the run is for, by their
|
|
37
|
+
* identity `@id` (else their username, #2041). The engine works it out from
|
|
38
|
+
* the caller's context; a request cannot name another user's. A caller's own
|
|
39
|
+
* `sub` (phr's source id) narrows it within that user's scope, never outside.
|
|
40
|
+
*/
|
|
41
|
+
export declare function boundScope(receiverId: string, owner: string, sub?: string): string;
|
|
26
42
|
export interface ChainLeaf {
|
|
27
43
|
item: IceItem;
|
|
28
44
|
chain: string[];
|
package/lib/src/ice/types.d.ts
CHANGED
|
@@ -201,7 +201,13 @@ export interface IceReceiver {
|
|
|
201
201
|
label?: string;
|
|
202
202
|
/** The kinds it takes, e.g. `['ncm']`, `['file']`, `['phr.health-record']`. */
|
|
203
203
|
accepts: IceKind[];
|
|
204
|
-
/**
|
|
204
|
+
/**
|
|
205
|
+
* May this context use this receiver? Its existing permission (D5). The
|
|
206
|
+
* import screen shows only these, and the engine asks it before any work
|
|
207
|
+
* of a run (#2012): a no refuses every file before anything is unpacked,
|
|
208
|
+
* read by a shim, kept or fetched. Answer no whenever `receive` would
|
|
209
|
+
* refuse this context outright; `receive` still checks each item.
|
|
210
|
+
*/
|
|
205
211
|
mayUse(ctx: ActorContext): Promise<boolean>;
|
|
206
212
|
/** Hand one item to the receiver's own door. */
|
|
207
213
|
receive(item: IceItem, ctx: ActorContext, info: ReceiveInfo): Promise<ReceiveOutcome>;
|
|
@@ -252,10 +258,13 @@ export interface IceRunRequest {
|
|
|
252
258
|
/** D11. Default `ngdpbase.import.engine.default-original`. */
|
|
253
259
|
original?: OriginalChoice;
|
|
254
260
|
/**
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
261
|
+
* A narrower ledger scope within the caller's own (#2013). The engine binds
|
|
262
|
+
* every run's scope to the receiver and to the user it runs as (their
|
|
263
|
+
* identity `@id`, else their username): the same file imported again by
|
|
264
|
+
* the same user into the same receiver is skipped. A caller with its own
|
|
265
|
+
* notion of a source (phr's source id) passes it to keep those apart; it
|
|
266
|
+
* can never reach another user's or another receiver's scope. Never pass a
|
|
267
|
+
* value a client sent.
|
|
259
268
|
*/
|
|
260
269
|
scope?: string;
|
|
261
270
|
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { type ActorContext } from '../context/ActorContext.js';
|
|
14
14
|
import type { BackupFolder } from './BaseManager.js';
|
|
15
|
+
import { type UpdateCheck, type UpdateOutcome } from '../utils/addonInstaller.js';
|
|
15
16
|
import BaseManager from './BaseManager.js';
|
|
16
17
|
import type { BackupData } from './BaseManager.js';
|
|
17
18
|
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
@@ -302,6 +303,9 @@ export interface AddonDashboardCard {
|
|
|
302
303
|
/** URL of the add-on's admin page */
|
|
303
304
|
adminUrl: string;
|
|
304
305
|
}
|
|
306
|
+
/** #2100: the PDP refused an administrator's add-on Update or Check. */
|
|
307
|
+
export declare class AddonUpdateForbiddenError extends Error {
|
|
308
|
+
}
|
|
305
309
|
/**
|
|
306
310
|
* Status information returned by getStatus()
|
|
307
311
|
*/
|
|
@@ -344,6 +348,10 @@ declare class AddonsManager extends BaseManager {
|
|
|
344
348
|
private installedDir;
|
|
345
349
|
/** #1830: the installed add-ons verified this boot — the only ones loaded from `installedDir`. */
|
|
346
350
|
private verifiedInstalled;
|
|
351
|
+
/** #2100: installed add-ons this start switched to an Update's release; settled after the load pass. */
|
|
352
|
+
private updatedThisStart;
|
|
353
|
+
/** #2100: Updates run one at a time, so two never write the lock file at once. */
|
|
354
|
+
private updateQueue;
|
|
347
355
|
/** Stylesheets registered by add-ons via registerStylesheet() */
|
|
348
356
|
private registeredStylesheets;
|
|
349
357
|
/** Dashboard cards registered by add-ons via registerDashboardCard() */
|
|
@@ -374,6 +382,14 @@ declare class AddonsManager extends BaseManager {
|
|
|
374
382
|
* this boot, not the next.
|
|
375
383
|
*/
|
|
376
384
|
private installConfiguredAddons;
|
|
385
|
+
/**
|
|
386
|
+
* The installer's options for this site (#1830, #2100): the registry and the
|
|
387
|
+
* limits from configuration, and every request — registry, attestations, the
|
|
388
|
+
* Sigstore TUF repository — through the guarded client and the egress policy
|
|
389
|
+
* (#1139). `timeoutKey` is the `…install.<key>` that bounds each request: the
|
|
390
|
+
* boot install's, or the shorter one an admin page waits on.
|
|
391
|
+
*/
|
|
392
|
+
private installerOptions;
|
|
377
393
|
/**
|
|
378
394
|
* `addon-install` (#1830): written after every check passed and before
|
|
379
395
|
* anything is written to disk. on-failure: refuse — code does not join the
|
|
@@ -381,6 +397,47 @@ declare class AddonsManager extends BaseManager {
|
|
|
381
397
|
* (recordSystemAction throws, and the installer writes nothing).
|
|
382
398
|
*/
|
|
383
399
|
private recordAddonInstall;
|
|
400
|
+
/**
|
|
401
|
+
* #2100: after the load pass, settle each add-on this start switched to an
|
|
402
|
+
* Update's release. Loaded (or switched off, so there was nothing to try):
|
|
403
|
+
* the previous release's folder and tarball go. Not loaded: the previous
|
|
404
|
+
* release is restored on disk and in the lock, so the next start runs it.
|
|
405
|
+
* Core cannot unload an add-on that failed half-way (its modules, routes and
|
|
406
|
+
* jobs stay), so the restored release is not loaded in this process.
|
|
407
|
+
*/
|
|
408
|
+
private settleUpdatedAddons;
|
|
409
|
+
/**
|
|
410
|
+
* `addon-update` (#2100) for a fall-back: core moved an installed add-on back
|
|
411
|
+
* from `from` to `to` because `from` did not load. on-failure: continue for
|
|
412
|
+
* this record — the fall-back has already happened on disk, and a lost
|
|
413
|
+
* record must not undo it.
|
|
414
|
+
*/
|
|
415
|
+
private recordFallback;
|
|
416
|
+
/** #2100: the door for the admin's add-on questions. Throws when the PDP refuses. */
|
|
417
|
+
private requireAddonPermission;
|
|
418
|
+
/**
|
|
419
|
+
* #2100: for each add-on in `ngdpbase.addons.install`, the pinned release,
|
|
420
|
+
* the configured range, and the newest release in it that is newer — asked
|
|
421
|
+
* of the registry now. Called when an administrator opens the add-ons page
|
|
422
|
+
* or presses Check; never at start. Registry unreachable is an answer
|
|
423
|
+
* (`registryUnreachable`), not an error.
|
|
424
|
+
*/
|
|
425
|
+
checkInstalledUpdates(ctx: ActorContext): Promise<UpdateCheck[]>;
|
|
426
|
+
/**
|
|
427
|
+
* #2100: an administrator's Update. Downloads `version` of the installed
|
|
428
|
+
* add-on `slug`, checks it exactly as a first install is checked (sha512,
|
|
429
|
+
* provenance, contents, `ngdpbase.minVersion`) and stages it: it runs from
|
|
430
|
+
* the next start. Asks the PDP for `config-manage`. Recorded as
|
|
431
|
+
* `addon-update` (who, the add-on, from and to) before anything is written;
|
|
432
|
+
* a record that cannot be written stops the Update.
|
|
433
|
+
*/
|
|
434
|
+
updateInstalledAddon(ctx: ActorContext, slug: string, version: string): Promise<UpdateOutcome>;
|
|
435
|
+
/**
|
|
436
|
+
* `addon-update` (#2100): an administrator's Update, written after every
|
|
437
|
+
* check passed and before anything is written to disk. on-failure: refuse —
|
|
438
|
+
* a record that cannot be written stops the Update.
|
|
439
|
+
*/
|
|
440
|
+
private recordAddonUpdate;
|
|
384
441
|
/**
|
|
385
442
|
* `addon-verify-failed` (#1830): an integrity, provenance, tarball, manifest,
|
|
386
443
|
* lock or folder check refused (or, for a repaired folder, found it changed).
|
|
@@ -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;
|
|
@@ -382,6 +382,18 @@ declare class ImportManager extends BaseManager {
|
|
|
382
382
|
importRunsFor(ctx: ActorContext): IceRunReport[];
|
|
383
383
|
/** Use this ledger instead of the file one: a test's, or a database table's. */
|
|
384
384
|
setImportLedger(ledger: IceLedger): void;
|
|
385
|
+
/**
|
|
386
|
+
* The ledger scope a run uses (#2013): bound to the receiver and to the user
|
|
387
|
+
* `ctx` names, by their identity `@id`; their username only when the
|
|
388
|
+
* identity store answers that there is no record for them (#2041). Never a
|
|
389
|
+
* scope the caller names: a request's `scope` only narrows within this
|
|
390
|
+
* user's own.
|
|
391
|
+
*
|
|
392
|
+
* @throws IdentityStoreUnavailableError when the identity store cannot
|
|
393
|
+
* answer: the run does not start, and nothing is recorded, rather than
|
|
394
|
+
* land in a scope the user's other runs do not use
|
|
395
|
+
*/
|
|
396
|
+
private importScope;
|
|
385
397
|
private newRunReport;
|
|
386
398
|
private executeRun;
|
|
387
399
|
/** What a run, or keeping at upload, works with. */
|
|
@@ -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
|
|
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
|
-
/**
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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`
|
|
366
|
-
*
|
|
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
|
*
|
|
@@ -85,6 +85,12 @@ export type InstallStatus =
|
|
|
85
85
|
| 'verified'
|
|
86
86
|
/** Already installed; the folder differed from its verified tarball and was rewritten. */
|
|
87
87
|
| 'repaired'
|
|
88
|
+
/**
|
|
89
|
+
* #2100: switched to the release an administrator's Update staged; the
|
|
90
|
+
* previous release is kept until AddonsManager confirms this one loaded
|
|
91
|
+
* ({@link confirmUpdate}) or falls back to it ({@link rollBackUpdate}).
|
|
92
|
+
*/
|
|
93
|
+
| 'updated'
|
|
88
94
|
/** Not installed (and not loaded); `reason` says why. */
|
|
89
95
|
| 'skipped';
|
|
90
96
|
/** Which verification refused, for `addon-verify-failed`. */
|
|
@@ -99,6 +105,20 @@ export interface InstallOutcome {
|
|
|
99
105
|
registryUnreachable?: boolean;
|
|
100
106
|
/** Set when a verification refused, or (for `repaired`) found the folder changed. */
|
|
101
107
|
failedCheck?: FailedCheck;
|
|
108
|
+
/** For `updated`: the release it replaced. */
|
|
109
|
+
previousVersion?: string;
|
|
110
|
+
/**
|
|
111
|
+
* #2100: a switch an earlier start made and never confirmed (the new release
|
|
112
|
+
* did not load, or the process stopped before it could) was undone at this
|
|
113
|
+
* start, before anything loaded: `from` is the release given up, `to` the
|
|
114
|
+
* one restored.
|
|
115
|
+
*/
|
|
116
|
+
rolledBack?: {
|
|
117
|
+
from: string;
|
|
118
|
+
to: string;
|
|
119
|
+
};
|
|
120
|
+
/** #2100: something admins should hear about a staged Update, such as one discarded. */
|
|
121
|
+
notice?: string;
|
|
102
122
|
}
|
|
103
123
|
/** `sha512-<base64>` of `data`. */
|
|
104
124
|
export declare function sha512Integrity(data: Buffer): string;
|
|
@@ -107,3 +127,73 @@ export declare function sha512Integrity(data: Buffer): string;
|
|
|
107
127
|
* Never throws for one add-on's problem: each outcome says what happened.
|
|
108
128
|
*/
|
|
109
129
|
export declare function installConfiguredAddons(specs: Record<string, unknown>, opts: InstallerOptions): Promise<InstallOutcome[]>;
|
|
130
|
+
/**
|
|
131
|
+
* The switched-to release loaded (or the add-on is off, so there is nothing to
|
|
132
|
+
* try): the previous release's folder and tarball go. No-op when nothing waits.
|
|
133
|
+
*/
|
|
134
|
+
export declare function confirmUpdate(addonsDir: string, slug: string): Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* The switched-to release did not load: restore the previous release on disk
|
|
137
|
+
* and in the lock, so the next start runs it. The new folder is set aside
|
|
138
|
+
* (`.rejected-…`) for inspection. Undefined when there is nothing to undo.
|
|
139
|
+
*/
|
|
140
|
+
export declare function rollBackUpdate(addonsDir: string, slug: string, now?: Date): Promise<{
|
|
141
|
+
from: string;
|
|
142
|
+
to: string;
|
|
143
|
+
} | undefined>;
|
|
144
|
+
/** Why an Update is refused while the last one is not settled (`previous` still in the lock). */
|
|
145
|
+
export declare const FINISH_LAST_UPDATE = "finish the last update first: restart the site";
|
|
146
|
+
/** What the admin page shows for one add-on in `ngdpbase.addons.install`. */
|
|
147
|
+
export interface UpdateCheck {
|
|
148
|
+
slug: string;
|
|
149
|
+
package?: string;
|
|
150
|
+
/** The configured range (`version` in the entry). */
|
|
151
|
+
range?: string;
|
|
152
|
+
/** The release the lock pins: what runs. Absent when not installed. */
|
|
153
|
+
pinned?: string;
|
|
154
|
+
/** A release an Update downloaded, used from the next start. */
|
|
155
|
+
pending?: string;
|
|
156
|
+
/** The newest release in the range, whether or not this core can run it. */
|
|
157
|
+
newest?: string;
|
|
158
|
+
/** The newest release in the range, newer than what is pinned or staged, that this core can run: what Update installs. */
|
|
159
|
+
available?: string;
|
|
160
|
+
/** The newest release in the range, newer than what is pinned or staged, when it needs a newer ngdpbase. */
|
|
161
|
+
needsCore?: {
|
|
162
|
+
version: string;
|
|
163
|
+
minVersion: string;
|
|
164
|
+
};
|
|
165
|
+
registryUnreachable?: boolean;
|
|
166
|
+
/** Why there is nothing to compare: not installed, or a configuration problem. */
|
|
167
|
+
problem?: string;
|
|
168
|
+
/** The last update is not settled (the lock still names the release it replaced): no Update until a restart. */
|
|
169
|
+
blocked?: string;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Ask the registry, for every installed add-on, which releases in its range
|
|
173
|
+
* are newer than the one pinned. Reads the packument only; writes nothing.
|
|
174
|
+
* Called only when an administrator opens the add-ons page or presses Check,
|
|
175
|
+
* never at start.
|
|
176
|
+
*/
|
|
177
|
+
export declare function checkForUpdates(specs: Record<string, unknown>, opts: InstallerOptions): Promise<UpdateCheck[]>;
|
|
178
|
+
/** What an Update did. */
|
|
179
|
+
export interface UpdateOutcome {
|
|
180
|
+
slug: string;
|
|
181
|
+
/** `staged`: verified and downloaded; it runs from the next start. `refused`: nothing changed; `reason` says why. */
|
|
182
|
+
status: 'staged' | 'refused';
|
|
183
|
+
package?: string;
|
|
184
|
+
version?: string;
|
|
185
|
+
/** The release that runs until the next start. */
|
|
186
|
+
previousVersion?: string;
|
|
187
|
+
reason?: string;
|
|
188
|
+
registryUnreachable?: boolean;
|
|
189
|
+
failedCheck?: FailedCheck;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Download `version` of an installed add-on and stage it for the next start
|
|
193
|
+
* (#2100). It must be a release in the configured range, newer than the
|
|
194
|
+
* pinned one, that this core can run, and it passes the same checks as a
|
|
195
|
+
* first install ({@link fetchVerified}): sha512, provenance, contents,
|
|
196
|
+
* package.json. `beforeInstall` (the audit record) runs after the checks and
|
|
197
|
+
* before anything is written. The running add-on's folder is not touched.
|
|
198
|
+
*/
|
|
199
|
+
export declare function stageUpdate(slug: string, rawSpec: unknown, version: string, opts: InstallerOptions): Promise<UpdateOutcome>;
|
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
* folders in `<data>/addons` are installed add-ons at all: a folder the lock
|
|
8
8
|
* does not name is never an add-on, whatever it contains.
|
|
9
9
|
*/
|
|
10
|
-
/**
|
|
11
|
-
export interface
|
|
12
|
-
package: string;
|
|
10
|
+
/** One verified release of an add-on: what it is and what it was accepted on. */
|
|
11
|
+
export interface PinnedVersion {
|
|
13
12
|
version: string;
|
|
14
13
|
integrity: string;
|
|
15
14
|
installedAt: string;
|
|
@@ -19,6 +18,23 @@ export interface LockEntry {
|
|
|
19
18
|
verifiedAt: string;
|
|
20
19
|
};
|
|
21
20
|
}
|
|
21
|
+
/** What the lock records for one installed add-on. */
|
|
22
|
+
export interface LockEntry extends PinnedVersion {
|
|
23
|
+
package: string;
|
|
24
|
+
/**
|
|
25
|
+
* #2100: a newer release an administrator's Update downloaded and verified.
|
|
26
|
+
* Its tarball waits in `.packages/`; the next start switches to it. Nothing
|
|
27
|
+
* else reads it, so until then the site runs `version`.
|
|
28
|
+
*/
|
|
29
|
+
pending?: PinnedVersion;
|
|
30
|
+
/**
|
|
31
|
+
* #2100: the release this one replaced at the start that switched to it,
|
|
32
|
+
* kept (its folder as `.previous-<slug>`, its tarball in `.packages/`) until
|
|
33
|
+
* this one has loaded. Present at a later start only when that start never
|
|
34
|
+
* confirmed the switch, so the installer falls back to it.
|
|
35
|
+
*/
|
|
36
|
+
previous?: PinnedVersion;
|
|
37
|
+
}
|
|
22
38
|
export interface LockFile {
|
|
23
39
|
lockfileVersion: 1;
|
|
24
40
|
addons: Record<string, LockEntry>;
|
|
@@ -26,6 +42,8 @@ export interface LockFile {
|
|
|
26
42
|
export declare const LOCK_FILE_NAME = "addons-lock.json";
|
|
27
43
|
/** Verified tarballs, kept so a restart needs no registry. Dot-named: discovery skips it. */
|
|
28
44
|
export declare const PACKAGES_DIR_NAME = ".packages";
|
|
45
|
+
/** #2100: the folder a switched-from release waits in until the new one has loaded. Dot-named: discovery skips it. */
|
|
46
|
+
export declare const PREVIOUS_DIR_PREFIX = ".previous-";
|
|
29
47
|
export declare function lockFilePath(addonsDir: string): string;
|
|
30
48
|
/** The lock file, or an empty one when there is none. Throws when it exists and is not JSON. */
|
|
31
49
|
export declare function readLockSync(addonsDir: string): LockFile;
|
|
@@ -12,6 +12,8 @@ import path from 'path';
|
|
|
12
12
|
export const LOCK_FILE_NAME = 'addons-lock.json';
|
|
13
13
|
/** Verified tarballs, kept so a restart needs no registry. Dot-named: discovery skips it. */
|
|
14
14
|
export const PACKAGES_DIR_NAME = '.packages';
|
|
15
|
+
/** #2100: the folder a switched-from release waits in until the new one has loaded. Dot-named: discovery skips it. */
|
|
16
|
+
export const PREVIOUS_DIR_PREFIX = '.previous-';
|
|
15
17
|
export function lockFilePath(addonsDir) {
|
|
16
18
|
return path.join(addonsDir, LOCK_FILE_NAME);
|
|
17
19
|
}
|
|
@@ -4,7 +4,7 @@ export declare const AUDIT_EVENT_NAME_PATTERN: RegExp;
|
|
|
4
4
|
export declare const AUDIT_EVENT: {
|
|
5
5
|
/** An add-on named in ngdpbase.addons.install installed into the data folder: package, version, sha512 and the repository its provenance names; recorded before it is written, which cannot proceed without it (#1830) */
|
|
6
6
|
readonly ADDON_INSTALL: "addon-install";
|
|
7
|
-
/**
|
|
7
|
+
/** The release an installed add-on runs changed: an administrator's Update (who, the add-on, from and to version), recorded before the release is written, which cannot proceed without it; or core falling back to the previous release because the new one did not load (#2100) */
|
|
8
8
|
readonly ADDON_UPDATE: "addon-update";
|
|
9
9
|
/** An add-on refused or set aside by a check: its sha512, provenance, tarball contents, package.json, the lock file, or a changed installed folder (#1830) */
|
|
10
10
|
readonly ADDON_VERIFY_FAILED: "addon-verify-failed";
|
|
@@ -11,7 +11,7 @@ export const AUDIT_EVENT_NAME_PATTERN = /^[a-z]+(-[a-z]+)+$/;
|
|
|
11
11
|
export const AUDIT_EVENT = {
|
|
12
12
|
/** An add-on named in ngdpbase.addons.install installed into the data folder: package, version, sha512 and the repository its provenance names; recorded before it is written, which cannot proceed without it (#1830) */
|
|
13
13
|
ADDON_INSTALL: 'addon-install',
|
|
14
|
-
/**
|
|
14
|
+
/** The release an installed add-on runs changed: an administrator's Update (who, the add-on, from and to version), recorded before the release is written, which cannot proceed without it; or core falling back to the previous release because the new one did not load (#2100) */
|
|
15
15
|
ADDON_UPDATE: 'addon-update',
|
|
16
16
|
/** An add-on refused or set aside by a check: its sha512, provenance, tarball contents, package.json, the lock file, or a changed installed folder (#1830) */
|
|
17
17
|
ADDON_VERIFY_FAILED: 'addon-verify-failed',
|
|
@@ -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
|
-
/**
|
|
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.
|
|
3
|
+
"version": "5.36.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",
|