@jwilleke/ngdpbase-types 5.25.0 → 5.27.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/README.md +1 -1
- package/lib/packages/ngdpbase-types/index.d.ts +9 -2
- package/lib/packages/ngdpbase-types/testing.d.ts +7 -3
- package/lib/packages/ngdpbase-types/testing.js +7 -3
- package/lib/src/converters/DocxConverter.d.ts +30 -0
- package/lib/src/converters/MarkdownConverter.d.ts +31 -0
- package/lib/src/export/ChromeServicePdfProvider.d.ts +52 -0
- package/lib/src/export/ChromeServicePdfProvider.js +153 -0
- package/lib/src/export/LocalChromePdfProvider.d.ts +25 -0
- package/lib/src/export/LocalChromePdfProvider.js +33 -0
- package/lib/src/export/PdfProvider.d.ts +51 -0
- package/lib/src/export/PdfProvider.js +37 -0
- package/lib/src/export/pdfEngine.d.ts +113 -0
- package/lib/src/export/pdfEngine.js +195 -0
- package/lib/src/http/egressPolicy.js +61 -0
- package/lib/src/http/guardedElasticsearch.d.ts +27 -0
- package/lib/src/http/guardedElasticsearch.js +99 -0
- package/lib/src/ice/converterShim.d.ts +5 -0
- package/lib/src/ice/fileHandling.d.ts +30 -0
- package/lib/src/ice/ledger.d.ts +37 -0
- package/lib/src/ice/runImport.d.ts +51 -0
- package/lib/src/ice/types.d.ts +262 -0
- package/lib/src/mail/MailProvider.d.ts +15 -0
- package/lib/src/mail/NodemailerMailProvider.d.ts +1 -1
- package/lib/src/managers/AddonsManager.d.ts +55 -3
- package/lib/src/managers/AttachmentManager.d.ts +4 -5
- package/lib/src/managers/AuthManager.d.ts +150 -32
- package/lib/src/managers/BackgroundJobManager.d.ts +22 -12
- package/lib/src/managers/BackupManager.d.ts +779 -0
- package/lib/src/managers/BackupManager.js +2042 -0
- package/lib/src/managers/BaseManager.d.ts +156 -6
- package/lib/src/managers/BaseManager.js +64 -2
- package/lib/src/managers/CommentManager.d.ts +6 -6
- package/lib/src/managers/ConsentManager.d.ts +130 -0
- package/lib/src/managers/DatabaseManager.d.ts +6 -0
- package/lib/src/managers/DatabaseManager.js +25 -15
- package/lib/src/managers/EmailManager.d.ts +32 -5
- package/lib/src/managers/ExportManager.d.ts +62 -0
- package/lib/src/managers/ExportManager.js +147 -2
- package/lib/src/managers/FootnoteManager.d.ts +6 -6
- package/lib/src/managers/ImportManager.d.ts +590 -0
- package/lib/src/managers/OrganizationManager.d.ts +8 -6
- package/lib/src/managers/PageManager.d.ts +61 -1
- package/lib/src/managers/PersonManager.d.ts +8 -6
- package/lib/src/managers/RoleManager.d.ts +6 -8
- package/lib/src/providers/BaseAttachmentProvider.d.ts +4 -4
- package/lib/src/providers/BaseBackupProvider.d.ts +77 -0
- package/lib/src/providers/BaseBackupProvider.js +52 -0
- package/lib/src/providers/BasicAttachmentProvider.d.ts +4 -4
- package/lib/src/providers/FileBackupProvider.d.ts +34 -0
- package/lib/src/providers/FileBackupProvider.js +116 -0
- package/lib/src/providers/FileConsentProvider.d.ts +18 -0
- package/lib/src/providers/SqliteDatabaseProvider.d.ts +2 -2
- package/lib/src/providers/SqliteDatabaseProvider.js +12 -11
- package/lib/src/providers/attachmentFilesBackup.d.ts +5 -4
- package/lib/src/providers/databaseMigrations.js +45 -0
- package/lib/src/security/PolicyDecisionPoint.d.ts +14 -0
- package/lib/src/security/PolicyInformationPoint.d.ts +25 -2
- package/lib/src/types/Config.d.ts +2 -0
- package/lib/src/types/Consent.d.ts +90 -0
- package/lib/src/types/express.d.ts +6 -0
- package/lib/src/utils/addonHost.d.ts +45 -2
- package/lib/src/utils/addonHost.js +39 -3
- package/lib/src/utils/auditEventNames.generated.d.ts +20 -0
- package/lib/src/utils/auditEventNames.generated.js +20 -0
- package/lib/src/utils/backupCrypto.js +275 -0
- package/lib/src/utils/backupDocument.d.ts +36 -0
- package/lib/src/utils/backupDocument.js +99 -0
- package/lib/src/utils/browserDescription.d.ts +11 -0
- package/lib/src/utils/directoryBackup.d.ts +48 -48
- package/lib/src/utils/directoryBackup.js +291 -0
- package/lib/src/utils/frontmatterAccess.d.ts +54 -11
- package/lib/src/utils/importRunSummary.d.ts +64 -0
- package/lib/src/utils/instanceEnvBackup.d.ts +46 -0
- package/lib/src/utils/instanceEnvBackup.js +120 -0
- package/lib/src/utils/instanceEnvSecret.js +79 -0
- package/lib/src/utils/ipLocation.d.ts +11 -0
- package/lib/src/utils/ncmNotify.d.ts +30 -0
- package/lib/src/utils/privateStoreImport.d.ts +58 -0
- package/lib/src/utils/schedule.js +171 -0
- package/lib/src/utils/secretFileMode.js +78 -0
- package/lib/src/utils/siteRestore.d.ts +13 -16
- package/lib/src/utils/siteRestore.js +33 -6
- package/lib/src/utils/treeArchive.d.ts +2 -0
- package/lib/src/utils/treeArchive.js +242 -0
- package/lib/src/utils/version.js +408 -0
- package/lib/src/utils/zipArchive.d.ts +71 -0
- package/package.json +6 -1
- package/lib/src/utils/administrators.d.ts +0 -21
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ import type { AddonModule, AddonHost, WikiEngine } from '@jwilleke/ngdpbase-type
|
|
|
11
11
|
import { buildAddonHost, openAddonTestDatabase, toCsv, formatters } from '@jwilleke/ngdpbase-types/testing';
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
- The main entry is types only. Importing it at run time fails, on purpose.
|
|
14
|
+
- The main entry is types only. Importing it at run time fails, on purpose. An add-on takes core's values from `host` (`AddonHost`: the request and audit helpers, the logger, `guardedFetch`, the egress tools, `isAtLeast`) and a manager's job from `engine.getManager` (such as `PageManager.pageUrl` or `PolicyDecisionPoint.usersHolding`); it never imports core's values ([#1813](https://github.com/jwilleke/ngdpbase/issues/1813)).
|
|
15
15
|
- `/testing` is for tests. `buildAddonHost(slug, defaults, setEnabledForPerson?, engine?)` is the real host; `host.setEnabledForPerson` calls the setter you pass (it refuses without one), and `engine` names the system principal `host.systemContext(reason)` uses (core's fallback without one). `openAddonTestDatabase(owner, { migrations, ledgerTable })` is the handle `openAddonDatabase` returns, in memory. `toCsv` is `ExportManager.toCsv`. `formatters` is a plugin's `context.formatters`.
|
|
16
16
|
|
|
17
17
|
The add-on guide is [docs/guides/addons-developer-guide.md](https://github.com/jwilleke/ngdpbase/blob/master/docs/guides/addons-developer-guide.md).
|
|
@@ -15,7 +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 { AddonHost, PersonSwitchSetter } from '../../src/utils/addonHost.js';
|
|
18
|
+
export type { AddonHost, AddonIceDeclaration, PersonSwitchSetter } from '../../src/utils/addonHost.js';
|
|
19
19
|
export type { WikiEngine } from '../../src/types/WikiEngine.js';
|
|
20
20
|
export type { ApiContext } from '../../src/context/ApiContext.js';
|
|
21
21
|
export type { ActorContext } from '../../src/context/ActorContext.js';
|
|
@@ -28,8 +28,15 @@ export type { JobRunContext } from '../../src/managers/BackgroundJobManager.js';
|
|
|
28
28
|
export type { default as DatabaseManager, AddonDatabaseOptions } from '../../src/managers/DatabaseManager.js';
|
|
29
29
|
export type { Migration } from '../../src/providers/sqliteMigrations.js';
|
|
30
30
|
export type { AddonDatabaseDeclaration, DatabaseEncrypt } from '../../src/utils/addonDatabaseRules.js';
|
|
31
|
-
export type { default as ExportManager } from '../../src/managers/ExportManager.js';
|
|
31
|
+
export type { default as ExportManager, PdfOptions, PdfMargin, PdfPageSize, PdfUnavailableError } from '../../src/managers/ExportManager.js';
|
|
32
|
+
export type { default as EmailManager, SendOptions, MailAttachment, AttachmentRefusedError } from '../../src/managers/EmailManager.js';
|
|
32
33
|
export type { default as PolicyDecisionPoint } from '../../src/security/PolicyDecisionPoint.js';
|
|
34
|
+
export type { default as PageManager } from '../../src/managers/PageManager.js';
|
|
35
|
+
export type { PageAction } from '../../src/utils/pageUrl.js';
|
|
36
|
+
export type { default as ConsentManager, ConsentScopeStatus } from '../../src/managers/ConsentManager.js';
|
|
37
|
+
export type { AddonConsentRegistration, ConsentDocument, ConsentRecord, ConsentRequiredError, ConsentScope, ConsentSeen, ConsentStatus } from '../../src/types/Consent.js';
|
|
38
|
+
export type { default as ImportManager } from '../../src/managers/ImportManager.js';
|
|
39
|
+
export type { IceShim, IceReceiver, IceItem, IceKind, IceWarning, ShimDirection, ShimAccepts, ShimTools, ShimReadResult, ReceiveInfo, ReceiveOutcome, IceCitation, KeptOriginal, OriginalChoice, IceUpload, IceSource, IceRunRequest, IceRunReport, IceFileReport, IceFileStatus } from '../../src/ice/types.js';
|
|
33
40
|
export type { default as PersonManager, LinkOfferOutcome } from '../../src/managers/PersonManager.js';
|
|
34
41
|
export type { default as OrganizationManager } from '../../src/managers/OrganizationManager.js';
|
|
35
42
|
export type { Person, PersonAffiliation, PersonInput, PersonUpdate } from '../../src/types/Person.js';
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import ExportManager from '../../src/managers/ExportManager.js';
|
|
5
5
|
import { type AddonDatabaseOptions } from '../../src/managers/DatabaseManager.js';
|
|
6
6
|
import { type SqliteHandle } from '../../src/providers/SqliteDatabaseProvider.js';
|
|
7
|
-
import { buildAddonHost as buildHost, type AddonHost, type PersonSwitchSetter } from '../../src/utils/addonHost.js';
|
|
7
|
+
import { buildAddonHost as buildHost, type AddonHost, type AddonIceDeclaration, type PersonSwitchSetter } from '../../src/utils/addonHost.js';
|
|
8
8
|
export { formatters } from '../../src/managers/PluginManager.js';
|
|
9
9
|
/**
|
|
10
10
|
* Core's `buildAddonHost`, with the audit registry bound to this add-on's
|
|
@@ -19,9 +19,13 @@ export { formatters } from '../../src/managers/PluginManager.js';
|
|
|
19
19
|
* test (#1963); a site binds core's setter. Without one it refuses.
|
|
20
20
|
* @param engine - Optional: an engine (or stand-in) whose PolicyInformationPoint
|
|
21
21
|
* names the system principal for `host.systemContext`; without one it is
|
|
22
|
-
* core's fallback principal
|
|
22
|
+
* core's fallback principal. Its `ImportManager` (a real one, or a
|
|
23
|
+
* stand-in) is where `host.registerShim` and `host.registerReceiver` register
|
|
24
|
+
* @param manifest - Optional: the add-on's manifest (the `ngdpbase` block of
|
|
25
|
+
* its package.json); its `shims` and `receivers` are the names the host lets
|
|
26
|
+
* it register (#1979)
|
|
23
27
|
*/
|
|
24
|
-
export declare function buildAddonHost(slug: string, addonDefaults: Record<string, unknown>, setEnabledForPerson?: PersonSwitchSetter, engine?: Parameters<typeof buildHost>[3]): AddonHost;
|
|
28
|
+
export declare function buildAddonHost(slug: string, addonDefaults: Record<string, unknown>, setEnabledForPerson?: PersonSwitchSetter, engine?: Parameters<typeof buildHost>[3], manifest?: AddonIceDeclaration | null): AddonHost;
|
|
25
29
|
/** The add-on whose declaration a test database is checked against: its slug and its own `config/default-config.json`. */
|
|
26
30
|
export interface AddonTestDatabaseDeclarer {
|
|
27
31
|
slug: string;
|
|
@@ -18,11 +18,15 @@ export { formatters } from '../../src/managers/PluginManager.js';
|
|
|
18
18
|
* test (#1963); a site binds core's setter. Without one it refuses.
|
|
19
19
|
* @param engine - Optional: an engine (or stand-in) whose PolicyInformationPoint
|
|
20
20
|
* names the system principal for `host.systemContext`; without one it is
|
|
21
|
-
* core's fallback principal
|
|
21
|
+
* core's fallback principal. Its `ImportManager` (a real one, or a
|
|
22
|
+
* stand-in) is where `host.registerShim` and `host.registerReceiver` register
|
|
23
|
+
* @param manifest - Optional: the add-on's manifest (the `ngdpbase` block of
|
|
24
|
+
* its package.json); its `shims` and `receivers` are the names the host lets
|
|
25
|
+
* it register (#1979)
|
|
22
26
|
*/
|
|
23
|
-
export function buildAddonHost(slug, addonDefaults, setEnabledForPerson, engine) {
|
|
27
|
+
export function buildAddonHost(slug, addonDefaults, setEnabledForPerson, engine, manifest) {
|
|
24
28
|
bindAuditEvents((key, fallback) => addonDefaults[key] ?? fallback);
|
|
25
|
-
return buildHost(slug, addonDefaults, setEnabledForPerson, engine);
|
|
29
|
+
return buildHost(slug, addonDefaults, setEnabledForPerson, engine, manifest);
|
|
26
30
|
}
|
|
27
31
|
/**
|
|
28
32
|
* An add-on's database as `DatabaseManager.openAddonDatabase` returns it, in
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Word (.docx) converter (#1131) — the first BINARY source format.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately thin: mammoth (pure JS, no native deps) turns the document
|
|
5
|
+
* into clean HTML, and ImportManager routes that HTML through the EXISTING
|
|
6
|
+
* html→NCM path — turndown, §2.4 link normalization, §2.1 table up-convert,
|
|
7
|
+
* §2.2 image localization, and the #1125 footnote transfer all come from
|
|
8
|
+
* the funnel, not from here. The converter's own contribution is the
|
|
9
|
+
* `convertBuffer` contract that text converters don't need.
|
|
10
|
+
*
|
|
11
|
+
* Known limitation, recorded on the issue: docx footnotes arrive as
|
|
12
|
+
* mammoth's anchor/list HTML rather than `[^id]` markdown, so they survive
|
|
13
|
+
* as ordinary links instead of sidecar records. Mapping them is a follow-up.
|
|
14
|
+
*/
|
|
15
|
+
import type { IContentConverter, ConversionResult } from './IContentConverter.js';
|
|
16
|
+
export default class DocxConverter implements IContentConverter {
|
|
17
|
+
readonly formatId = "docx";
|
|
18
|
+
readonly formatName = "Word (.docx)";
|
|
19
|
+
readonly fileExtensions: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Never claims by content (#879 extension-first): a docx read as UTF-8 is
|
|
22
|
+
* zip garbage, and probing it risks both false negatives and another
|
|
23
|
+
* converter mis-claiming. detectFormat's extension pass finds this
|
|
24
|
+
* converter; the content pass must stay silent.
|
|
25
|
+
*/
|
|
26
|
+
canHandle(_content: string, _filename: string): boolean;
|
|
27
|
+
/** A docx has no meaningful text form — the binary contract is the only door. */
|
|
28
|
+
convert(_content: string): ConversionResult;
|
|
29
|
+
convertBuffer(buffer: Buffer): Promise<ConversionResult>;
|
|
30
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown Pass-Through Converter
|
|
3
|
+
*
|
|
4
|
+
* Imports `.md` and `.markdown` files into ngdpbase without format conversion.
|
|
5
|
+
* Parses existing YAML frontmatter and exposes it as metadata so ImportManager
|
|
6
|
+
* can preserve recognised fields (title, slug, uuid, audience, system-category,
|
|
7
|
+
* user-keywords, author, source-url, etc.) and generate fresh values for any
|
|
8
|
+
* that are missing (uuid, lastModified).
|
|
9
|
+
*
|
|
10
|
+
* @module MarkdownConverter
|
|
11
|
+
*/
|
|
12
|
+
import { IContentConverter, ConversionResult } from './IContentConverter.js';
|
|
13
|
+
declare class MarkdownConverter implements IContentConverter {
|
|
14
|
+
readonly formatId = "markdown";
|
|
15
|
+
readonly formatName = "Markdown";
|
|
16
|
+
readonly fileExtensions: string[];
|
|
17
|
+
/**
|
|
18
|
+
* Parse frontmatter and return body content unchanged.
|
|
19
|
+
*
|
|
20
|
+
* Existing frontmatter fields are extracted as metadata so ImportManager's
|
|
21
|
+
* `buildFrontmatter` can honour them (e.g. preserve a uuid that already
|
|
22
|
+
* exists, carry over audience restrictions, etc.).
|
|
23
|
+
*/
|
|
24
|
+
convert(content: string): ConversionResult;
|
|
25
|
+
/**
|
|
26
|
+
* Accepts .md / .markdown by extension; also matches content that starts
|
|
27
|
+
* with a YAML frontmatter block or a Markdown heading.
|
|
28
|
+
*/
|
|
29
|
+
canHandle(content: string, filename: string): boolean;
|
|
30
|
+
}
|
|
31
|
+
export default MarkdownConverter;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type Transport } from '../http/guardedFetch.js';
|
|
2
|
+
import type { Resolver } from '../http/guardedLookup.js';
|
|
3
|
+
import type { EgressPolicy } from '../http/ssrf.js';
|
|
4
|
+
import { type PdfPageSize, type ResolvedPdfOptions } from './pdfEngine.js';
|
|
5
|
+
import type { PdfProvider, PdfRenderRequest, PdfRendered } from './PdfProvider.js';
|
|
6
|
+
/** Gotenberg's HTML → PDF route, relative to the configured service URL. */
|
|
7
|
+
export declare const GOTENBERG_HTML_ROUTE = "forms/chromium/convert/html";
|
|
8
|
+
/**
|
|
9
|
+
* The named page sizes in inches, as Chromium's (and Playwright's) print uses
|
|
10
|
+
* them, so a service render and a local one get the same paper.
|
|
11
|
+
*/
|
|
12
|
+
export declare const PAPER_INCHES: Record<PdfPageSize, readonly [number, number]>;
|
|
13
|
+
export interface ChromeServiceSettings {
|
|
14
|
+
/** `ngdpbase.export.pdf.provider.chrome-service.url` — the service's base URL. */
|
|
15
|
+
url: string;
|
|
16
|
+
/** The site's egress policy (`resolveEgressPolicy`). */
|
|
17
|
+
policy: EgressPolicy;
|
|
18
|
+
/** The largest answer accepted, in bytes. */
|
|
19
|
+
maxBytes: number;
|
|
20
|
+
}
|
|
21
|
+
/** Hooks for tests; a site passes nothing. */
|
|
22
|
+
export interface ChromeServiceDeps {
|
|
23
|
+
transport?: Transport;
|
|
24
|
+
resolver?: Resolver;
|
|
25
|
+
normalize?: (pdf: Uint8Array, timeoutMs: number) => Promise<Uint8Array>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The form fields for one render. Every margin is sent, `0` when the caller
|
|
29
|
+
* set none, because Gotenberg's own default margin is not zero and a local
|
|
30
|
+
* render's is.
|
|
31
|
+
*
|
|
32
|
+
* @param options - The validated options
|
|
33
|
+
*/
|
|
34
|
+
export declare function gotenbergFields(options: ResolvedPdfOptions): Array<[string, string]>;
|
|
35
|
+
/**
|
|
36
|
+
* A `multipart/form-data` body: the fields, then the document as `files`
|
|
37
|
+
* named `index.html` (the name Gotenberg's HTML route requires).
|
|
38
|
+
*
|
|
39
|
+
* @param html - The document
|
|
40
|
+
* @param fields - Name and value pairs
|
|
41
|
+
* @param boundary - The part boundary
|
|
42
|
+
*/
|
|
43
|
+
export declare function multipartBody(html: string, fields: Array<[string, string]>, boundary: string): Buffer;
|
|
44
|
+
/** The service URL with the route appended (a path on the URL is kept). */
|
|
45
|
+
export declare function serviceEndpoint(base: string): string;
|
|
46
|
+
export declare class ChromeServicePdfProvider implements PdfProvider {
|
|
47
|
+
private readonly settings;
|
|
48
|
+
private readonly deps;
|
|
49
|
+
readonly name: "chrome-service";
|
|
50
|
+
constructor(settings: ChromeServiceSettings, deps?: ChromeServiceDeps);
|
|
51
|
+
render({ html, options, timeoutMs }: PdfRenderRequest): Promise<PdfRendered>;
|
|
52
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chrome-service` (#1974): a separate Chromium service reached by URL, for a
|
|
3
|
+
* site whose own image carries no browser (the Docker image). It speaks
|
|
4
|
+
* Gotenberg's HTML route: `POST <url>/forms/chromium/convert/html`, a
|
|
5
|
+
* multipart form with the document as `files` (`index.html`) and the page
|
|
6
|
+
* settings as fields; the answer is the PDF.
|
|
7
|
+
*
|
|
8
|
+
* - Outbound through `guardedFetch` under the site's egress policy. A service
|
|
9
|
+
* on the LAN or in the cluster is on a private address, which the policy
|
|
10
|
+
* refuses until the operator allows its range
|
|
11
|
+
* (`ngdpbase.security.egress.allowed-ranges`); loopback is never allowed.
|
|
12
|
+
* - The whole request is bounded by `ngdpbase.export.pdf.timeout-ms`, and the
|
|
13
|
+
* answer by `ngdpbase.export.pdf.provider.chrome-service.max-bytes`.
|
|
14
|
+
* - The PDF goes through the same `normalizePdf` pass a local render gets, in
|
|
15
|
+
* a child process, so the same HTML gives the same bytes for a given service
|
|
16
|
+
* version.
|
|
17
|
+
*
|
|
18
|
+
* What the service's Chromium may load (remote images, files) is the
|
|
19
|
+
* service's own setting, not this request's; the setup guide says how to
|
|
20
|
+
* lock it down.
|
|
21
|
+
*/
|
|
22
|
+
import { randomUUID } from 'node:crypto';
|
|
23
|
+
import { guardedFetch } from '../http/guardedFetch.js';
|
|
24
|
+
import { PdfUnavailableError, normalizePdfInChild } from './pdfEngine.js';
|
|
25
|
+
/** Gotenberg's HTML → PDF route, relative to the configured service URL. */
|
|
26
|
+
export const GOTENBERG_HTML_ROUTE = 'forms/chromium/convert/html';
|
|
27
|
+
/**
|
|
28
|
+
* The named page sizes in inches, as Chromium's (and Playwright's) print uses
|
|
29
|
+
* them, so a service render and a local one get the same paper.
|
|
30
|
+
*/
|
|
31
|
+
export const PAPER_INCHES = {
|
|
32
|
+
Letter: [8.5, 11],
|
|
33
|
+
Legal: [8.5, 14],
|
|
34
|
+
Tabloid: [11, 17],
|
|
35
|
+
Ledger: [17, 11],
|
|
36
|
+
A0: [33.1, 46.8],
|
|
37
|
+
A1: [23.4, 33.1],
|
|
38
|
+
A2: [16.54, 23.4],
|
|
39
|
+
A3: [11.7, 16.54],
|
|
40
|
+
A4: [8.27, 11.7],
|
|
41
|
+
A5: [5.83, 8.27],
|
|
42
|
+
A6: [4.13, 5.83]
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* The form fields for one render. Every margin is sent, `0` when the caller
|
|
46
|
+
* set none, because Gotenberg's own default margin is not zero and a local
|
|
47
|
+
* render's is.
|
|
48
|
+
*
|
|
49
|
+
* @param options - The validated options
|
|
50
|
+
*/
|
|
51
|
+
export function gotenbergFields(options) {
|
|
52
|
+
const [width, height] = PAPER_INCHES[options.format];
|
|
53
|
+
const margin = (side) => options.margin[side] ?? '0';
|
|
54
|
+
return [
|
|
55
|
+
['paperWidth', `${width}in`],
|
|
56
|
+
['paperHeight', `${height}in`],
|
|
57
|
+
['marginTop', margin('top')],
|
|
58
|
+
['marginRight', margin('right')],
|
|
59
|
+
['marginBottom', margin('bottom')],
|
|
60
|
+
['marginLeft', margin('left')],
|
|
61
|
+
['preferCssPageSize', 'true'],
|
|
62
|
+
['printBackground', String(options.printBackground)]
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A `multipart/form-data` body: the fields, then the document as `files`
|
|
67
|
+
* named `index.html` (the name Gotenberg's HTML route requires).
|
|
68
|
+
*
|
|
69
|
+
* @param html - The document
|
|
70
|
+
* @param fields - Name and value pairs
|
|
71
|
+
* @param boundary - The part boundary
|
|
72
|
+
*/
|
|
73
|
+
export function multipartBody(html, fields, boundary) {
|
|
74
|
+
const parts = [];
|
|
75
|
+
for (const [name, value] of fields) {
|
|
76
|
+
parts.push(Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="${name}"\r\n\r\n${value}\r\n`));
|
|
77
|
+
}
|
|
78
|
+
parts.push(Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="files"; filename="index.html"\r\nContent-Type: text/html; charset=utf-8\r\n\r\n`));
|
|
79
|
+
parts.push(Buffer.from(html, 'utf8'));
|
|
80
|
+
parts.push(Buffer.from(`\r\n--${boundary}--\r\n`));
|
|
81
|
+
return Buffer.concat(parts);
|
|
82
|
+
}
|
|
83
|
+
/** The service URL with the route appended (a path on the URL is kept). */
|
|
84
|
+
export function serviceEndpoint(base) {
|
|
85
|
+
const url = new URL(base);
|
|
86
|
+
if (!url.pathname.endsWith('/'))
|
|
87
|
+
url.pathname += '/';
|
|
88
|
+
return new URL(GOTENBERG_HTML_ROUTE, url).toString();
|
|
89
|
+
}
|
|
90
|
+
export class ChromeServicePdfProvider {
|
|
91
|
+
settings;
|
|
92
|
+
deps;
|
|
93
|
+
name = 'chrome-service';
|
|
94
|
+
constructor(settings, deps = {}) {
|
|
95
|
+
this.settings = settings;
|
|
96
|
+
this.deps = deps;
|
|
97
|
+
}
|
|
98
|
+
async render({ html, options, timeoutMs }) {
|
|
99
|
+
const { url, policy, maxBytes } = this.settings;
|
|
100
|
+
if (!url)
|
|
101
|
+
throw new PdfUnavailableError('ngdpbase.export.pdf.provider.chrome-service.url is not set');
|
|
102
|
+
let endpoint;
|
|
103
|
+
try {
|
|
104
|
+
endpoint = serviceEndpoint(url);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
throw new PdfUnavailableError('ngdpbase.export.pdf.provider.chrome-service.url is not a URL');
|
|
108
|
+
}
|
|
109
|
+
const where = new URL(endpoint).origin;
|
|
110
|
+
const boundary = `ngdpbase-pdf-${randomUUID()}`;
|
|
111
|
+
const body = multipartBody(html, gotenbergFields(options), boundary);
|
|
112
|
+
const started = Date.now();
|
|
113
|
+
let response;
|
|
114
|
+
try {
|
|
115
|
+
response = await withDeadline(guardedFetch(endpoint, {
|
|
116
|
+
policy,
|
|
117
|
+
method: 'POST',
|
|
118
|
+
body,
|
|
119
|
+
headers: { 'Content-Type': `multipart/form-data; boundary=${boundary}`, Accept: 'application/pdf' },
|
|
120
|
+
timeoutMs,
|
|
121
|
+
maxBytes,
|
|
122
|
+
...(this.deps.transport ? { transport: this.deps.transport } : {}),
|
|
123
|
+
...(this.deps.resolver ? { resolver: this.deps.resolver } : {})
|
|
124
|
+
}), timeoutMs);
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
const message = err.message;
|
|
128
|
+
// Too slow or too big: the service ran, and this render failed.
|
|
129
|
+
if (/timed out|too large/.test(message))
|
|
130
|
+
throw new Error(`PDF render failed: the PDF service at ${where} ${message}`, { cause: err });
|
|
131
|
+
// Refused by the egress policy, or nothing answering: no engine to use.
|
|
132
|
+
throw new PdfUnavailableError(`the PDF service at ${where} could not be reached: ${message}`, { cause: err });
|
|
133
|
+
}
|
|
134
|
+
if (response.status !== 200) {
|
|
135
|
+
const said = response.body.subarray(0, 200).toString('utf8').replace(/\s+/g, ' ').trim();
|
|
136
|
+
throw new Error(`PDF render failed: the PDF service at ${where} answered ${response.status}${said ? `: ${said}` : ''}`);
|
|
137
|
+
}
|
|
138
|
+
if (response.body.subarray(0, 5).toString('latin1') !== '%PDF-') {
|
|
139
|
+
throw new Error(`PDF render failed: the PDF service at ${where} answered, but not with a PDF`);
|
|
140
|
+
}
|
|
141
|
+
const remaining = Math.max(1, timeoutMs - (Date.now() - started));
|
|
142
|
+
const pdf = await (this.deps.normalize ?? normalizePdfInChild)(response.body, remaining);
|
|
143
|
+
return { pdf, engine: 'chrome-service' };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** `guardedFetch`'s timeout is per socket wait; this bounds the whole request. */
|
|
147
|
+
function withDeadline(work, timeoutMs) {
|
|
148
|
+
let timer;
|
|
149
|
+
const deadline = new Promise((_, reject) => {
|
|
150
|
+
timer = setTimeout(() => reject(new Error(`timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
151
|
+
});
|
|
152
|
+
return Promise.race([work, deadline]).finally(() => clearTimeout(timer));
|
|
153
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `local-chrome` (#1974): a Chromium already on the machine — the configured
|
|
3
|
+
* path (`ngdpbase.export.pdf.provider.local-chrome.chromium-path`), or one
|
|
4
|
+
* `resolveChromium` finds. Each render is its own child process
|
|
5
|
+
* (`pdfRenderWorker.ts`) with JavaScript off and every request refused except
|
|
6
|
+
* `data:` URIs, killed at the timeout.
|
|
7
|
+
*/
|
|
8
|
+
import { type ChromiumResolution, type PdfJob, type PdfJobResult } from './pdfEngine.js';
|
|
9
|
+
import type { PdfProvider, PdfRenderRequest, PdfRendered } from './PdfProvider.js';
|
|
10
|
+
/** Hooks for tests; a site passes nothing. */
|
|
11
|
+
export interface LocalChromeDeps {
|
|
12
|
+
render?: (job: PdfJob) => Promise<PdfJobResult>;
|
|
13
|
+
resolve?: (configuredPath: string) => Promise<ChromiumResolution>;
|
|
14
|
+
}
|
|
15
|
+
export declare class LocalChromePdfProvider implements PdfProvider {
|
|
16
|
+
private readonly chromiumPath;
|
|
17
|
+
private readonly deps;
|
|
18
|
+
readonly name: "local-chrome";
|
|
19
|
+
/**
|
|
20
|
+
* @param chromiumPath - The configured Chromium ('' = find one)
|
|
21
|
+
* @param deps - Test hooks
|
|
22
|
+
*/
|
|
23
|
+
constructor(chromiumPath: string, deps?: LocalChromeDeps);
|
|
24
|
+
render({ html, options, timeoutMs }: PdfRenderRequest): Promise<PdfRendered>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `local-chrome` (#1974): a Chromium already on the machine — the configured
|
|
3
|
+
* path (`ngdpbase.export.pdf.provider.local-chrome.chromium-path`), or one
|
|
4
|
+
* `resolveChromium` finds. Each render is its own child process
|
|
5
|
+
* (`pdfRenderWorker.ts`) with JavaScript off and every request refused except
|
|
6
|
+
* `data:` URIs, killed at the timeout.
|
|
7
|
+
*/
|
|
8
|
+
import { PdfUnavailableError, renderPdfInChild, resolveChromium } from './pdfEngine.js';
|
|
9
|
+
export class LocalChromePdfProvider {
|
|
10
|
+
chromiumPath;
|
|
11
|
+
deps;
|
|
12
|
+
name = 'local-chrome';
|
|
13
|
+
/**
|
|
14
|
+
* @param chromiumPath - The configured Chromium ('' = find one)
|
|
15
|
+
* @param deps - Test hooks
|
|
16
|
+
*/
|
|
17
|
+
constructor(chromiumPath, deps = {}) {
|
|
18
|
+
this.chromiumPath = chromiumPath;
|
|
19
|
+
this.deps = deps;
|
|
20
|
+
}
|
|
21
|
+
async render({ html, options, timeoutMs }) {
|
|
22
|
+
const engine = await (this.deps.resolve ?? ((p) => resolveChromium(p)))(this.chromiumPath);
|
|
23
|
+
if (!engine.ok)
|
|
24
|
+
throw new PdfUnavailableError(engine.reason);
|
|
25
|
+
const result = await (this.deps.render ?? renderPdfInChild)({ kind: 'render', html, executablePath: engine.executablePath, options, timeoutMs });
|
|
26
|
+
if (!result.ok) {
|
|
27
|
+
if (result.stage === 'launch')
|
|
28
|
+
throw new PdfUnavailableError(`Chromium at ${engine.executablePath} could not start: ${result.error}`);
|
|
29
|
+
throw new Error(`PDF render failed: ${result.error}`);
|
|
30
|
+
}
|
|
31
|
+
return { pdf: result.pdf, engine: `local-chrome:${engine.source}` };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PdfProvider — how ExportManager turns HTML into PDF (#1974).
|
|
3
|
+
*
|
|
4
|
+
* `ngdpbase.export.pdf.provider` chooses one, as `ngdpbase.mail.provider` and
|
|
5
|
+
* `ngdpbase.database.provider` do for their managers:
|
|
6
|
+
*
|
|
7
|
+
* - `none` (default) — `NonePdfProvider`: no PDF; `htmlToPdf` throws
|
|
8
|
+
* `PdfUnavailableError` and an add-on falls back to HTML.
|
|
9
|
+
* - `local-chrome` — `LocalChromePdfProvider`: a Chromium already on the
|
|
10
|
+
* machine, run in a child process with no network.
|
|
11
|
+
* - `chrome-service` — `ChromeServicePdfProvider`: a separate Chromium service
|
|
12
|
+
* reached by URL (Gotenberg's HTML route), through `guardedFetch`.
|
|
13
|
+
*
|
|
14
|
+
* A provider renders one document and returns the normalised bytes
|
|
15
|
+
* (`normalizePdf`), so the same HTML gives the same bytes for a given provider
|
|
16
|
+
* and engine version. It throws `PdfUnavailableError` when it has no engine to
|
|
17
|
+
* use (not set up, not reachable, cannot start) and a plain `Error` when the
|
|
18
|
+
* engine ran and the render failed. Another engine (e.g. WeasyPrint) is a new
|
|
19
|
+
* class here and one more name in `PDF_PROVIDERS`.
|
|
20
|
+
*/
|
|
21
|
+
import { type ResolvedPdfOptions } from './pdfEngine.js';
|
|
22
|
+
/** The values `ngdpbase.export.pdf.provider` takes. */
|
|
23
|
+
export declare const PDF_PROVIDERS: readonly ["none", "local-chrome", "chrome-service"];
|
|
24
|
+
export type PdfProviderName = (typeof PDF_PROVIDERS)[number];
|
|
25
|
+
/** One render, after the options are validated and the defaults filled. */
|
|
26
|
+
export interface PdfRenderRequest {
|
|
27
|
+
html: string;
|
|
28
|
+
options: ResolvedPdfOptions;
|
|
29
|
+
/** The whole render, including any wait on a PDF service. */
|
|
30
|
+
timeoutMs: number;
|
|
31
|
+
}
|
|
32
|
+
export interface PdfRendered {
|
|
33
|
+
/** The PDF, normalised. */
|
|
34
|
+
pdf: Uint8Array;
|
|
35
|
+
/** What made it, for the log line (`local-chrome:system`, `chrome-service`). */
|
|
36
|
+
engine: string;
|
|
37
|
+
}
|
|
38
|
+
export interface PdfProvider {
|
|
39
|
+
readonly name: PdfProviderName;
|
|
40
|
+
render(request: PdfRenderRequest): Promise<PdfRendered>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* `none`: PDF export is off. Also stands in for a provider name that is not
|
|
44
|
+
* one, so a typo is reported the same way — unavailable, with the reason.
|
|
45
|
+
*/
|
|
46
|
+
export declare class NonePdfProvider implements PdfProvider {
|
|
47
|
+
private readonly reason;
|
|
48
|
+
readonly name: "none";
|
|
49
|
+
constructor(reason?: string);
|
|
50
|
+
render(): Promise<PdfRendered>;
|
|
51
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PdfProvider — how ExportManager turns HTML into PDF (#1974).
|
|
3
|
+
*
|
|
4
|
+
* `ngdpbase.export.pdf.provider` chooses one, as `ngdpbase.mail.provider` and
|
|
5
|
+
* `ngdpbase.database.provider` do for their managers:
|
|
6
|
+
*
|
|
7
|
+
* - `none` (default) — `NonePdfProvider`: no PDF; `htmlToPdf` throws
|
|
8
|
+
* `PdfUnavailableError` and an add-on falls back to HTML.
|
|
9
|
+
* - `local-chrome` — `LocalChromePdfProvider`: a Chromium already on the
|
|
10
|
+
* machine, run in a child process with no network.
|
|
11
|
+
* - `chrome-service` — `ChromeServicePdfProvider`: a separate Chromium service
|
|
12
|
+
* reached by URL (Gotenberg's HTML route), through `guardedFetch`.
|
|
13
|
+
*
|
|
14
|
+
* A provider renders one document and returns the normalised bytes
|
|
15
|
+
* (`normalizePdf`), so the same HTML gives the same bytes for a given provider
|
|
16
|
+
* and engine version. It throws `PdfUnavailableError` when it has no engine to
|
|
17
|
+
* use (not set up, not reachable, cannot start) and a plain `Error` when the
|
|
18
|
+
* engine ran and the render failed. Another engine (e.g. WeasyPrint) is a new
|
|
19
|
+
* class here and one more name in `PDF_PROVIDERS`.
|
|
20
|
+
*/
|
|
21
|
+
import { PdfUnavailableError } from './pdfEngine.js';
|
|
22
|
+
/** The values `ngdpbase.export.pdf.provider` takes. */
|
|
23
|
+
export const PDF_PROVIDERS = ['none', 'local-chrome', 'chrome-service'];
|
|
24
|
+
/**
|
|
25
|
+
* `none`: PDF export is off. Also stands in for a provider name that is not
|
|
26
|
+
* one, so a typo is reported the same way — unavailable, with the reason.
|
|
27
|
+
*/
|
|
28
|
+
export class NonePdfProvider {
|
|
29
|
+
reason;
|
|
30
|
+
name = 'none';
|
|
31
|
+
constructor(reason = 'ngdpbase.export.pdf.provider is none: set it to local-chrome or chrome-service') {
|
|
32
|
+
this.reason = reason;
|
|
33
|
+
}
|
|
34
|
+
render() {
|
|
35
|
+
return Promise.reject(new PdfUnavailableError(this.reason));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/** Page sizes Chromium knows by name. */
|
|
2
|
+
export declare const PDF_PAGE_SIZES: readonly ["Letter", "Legal", "Tabloid", "Ledger", "A0", "A1", "A2", "A3", "A4", "A5", "A6"];
|
|
3
|
+
export type PdfPageSize = (typeof PDF_PAGE_SIZES)[number];
|
|
4
|
+
export interface PdfMargin {
|
|
5
|
+
top?: string;
|
|
6
|
+
right?: string;
|
|
7
|
+
bottom?: string;
|
|
8
|
+
left?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* What a caller may set. An `@page` rule in the HTML (size, margin) wins over
|
|
12
|
+
* these, as it does when a browser prints.
|
|
13
|
+
*/
|
|
14
|
+
export interface PdfOptions {
|
|
15
|
+
/** Page size; default `ngdpbase.export.pdf.page-size`. */
|
|
16
|
+
format?: PdfPageSize;
|
|
17
|
+
/** Page margins, each a CSS length (`'20mm'`, `'0.5in'`); default none. */
|
|
18
|
+
margin?: PdfMargin;
|
|
19
|
+
/** Print background colours and images; default `true`. */
|
|
20
|
+
printBackground?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** Options after validation and defaults. */
|
|
23
|
+
export interface ResolvedPdfOptions {
|
|
24
|
+
format: PdfPageSize;
|
|
25
|
+
margin: PdfMargin;
|
|
26
|
+
printBackground: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** One local Chromium render, as the child receives it. */
|
|
29
|
+
export interface PdfJob {
|
|
30
|
+
kind: 'render';
|
|
31
|
+
html: string;
|
|
32
|
+
executablePath: string;
|
|
33
|
+
options: ResolvedPdfOptions;
|
|
34
|
+
timeoutMs: number;
|
|
35
|
+
}
|
|
36
|
+
/** A PDF made elsewhere, to normalise in the child. */
|
|
37
|
+
export interface PdfNormalizeJob {
|
|
38
|
+
kind: 'normalize';
|
|
39
|
+
pdf: Uint8Array;
|
|
40
|
+
timeoutMs: number;
|
|
41
|
+
}
|
|
42
|
+
/** Anything the child is asked to do. */
|
|
43
|
+
export type PdfChildJob = PdfJob | PdfNormalizeJob;
|
|
44
|
+
/** What the child sends back. `launch` means Chromium could not start: the engine is unavailable. */
|
|
45
|
+
export type PdfJobResult = {
|
|
46
|
+
ok: true;
|
|
47
|
+
pdf: Uint8Array;
|
|
48
|
+
} | {
|
|
49
|
+
ok: false;
|
|
50
|
+
stage: 'launch' | 'render';
|
|
51
|
+
error: string;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* No PDF engine: `playwright-core` is not installed, no Chromium was found, or
|
|
55
|
+
* it could not start. An add-on catches it by `code` (it cannot import core
|
|
56
|
+
* values) and falls back to HTML.
|
|
57
|
+
*/
|
|
58
|
+
export declare class PdfUnavailableError extends Error {
|
|
59
|
+
readonly code = "PDF_UNAVAILABLE";
|
|
60
|
+
constructor(message: string, options?: ErrorOptions);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Validate a caller's options and fill the defaults. Throws on anything
|
|
64
|
+
* Chromium would misread, so a typo is an error rather than a different page.
|
|
65
|
+
*
|
|
66
|
+
* @param options - The caller's options
|
|
67
|
+
* @param defaultFormat - `ngdpbase.export.pdf.page-size`
|
|
68
|
+
*/
|
|
69
|
+
export declare function resolvePdfOptions(options: PdfOptions | undefined, defaultFormat: string): ResolvedPdfOptions;
|
|
70
|
+
/** Where Chromium or Chrome usually is, per platform. Checked after the configured path and Playwright's own. */
|
|
71
|
+
export declare const SYSTEM_CHROMIUM_PATHS: Record<string, string[]>;
|
|
72
|
+
export type ChromiumResolution = {
|
|
73
|
+
ok: true;
|
|
74
|
+
executablePath: string;
|
|
75
|
+
source: 'configured' | 'playwright' | 'system';
|
|
76
|
+
} | {
|
|
77
|
+
ok: false;
|
|
78
|
+
reason: string;
|
|
79
|
+
};
|
|
80
|
+
/** Hooks for tests; a site passes nothing. */
|
|
81
|
+
export interface ResolveDeps {
|
|
82
|
+
exists?: (p: string) => boolean;
|
|
83
|
+
platform?: string;
|
|
84
|
+
playwrightInstalled?: () => boolean;
|
|
85
|
+
/** Playwright's own Chromium, or null when it cannot say. */
|
|
86
|
+
playwrightChromium?: () => Promise<string | null>;
|
|
87
|
+
}
|
|
88
|
+
/** Whether `playwright-core` is installed (it is an optional dependency). */
|
|
89
|
+
export declare function playwrightInstalled(): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Find a Chromium to render with. Read on every call, so installing Chromium
|
|
92
|
+
* or setting the path takes effect without a restart.
|
|
93
|
+
*
|
|
94
|
+
* @param configuredPath - `ngdpbase.export.pdf.provider.local-chrome.chromium-path` ('' = find one)
|
|
95
|
+
*/
|
|
96
|
+
export declare function resolveChromium(configuredPath: string, deps?: ResolveDeps): Promise<ChromiumResolution>;
|
|
97
|
+
export declare function childEnv(env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
98
|
+
/**
|
|
99
|
+
* Render one job in a child process. Resolves with the child's answer; rejects
|
|
100
|
+
* only when the child timed out, crashed or never answered.
|
|
101
|
+
*
|
|
102
|
+
* @param job - The render
|
|
103
|
+
*/
|
|
104
|
+
export declare function renderPdfInChild(job: PdfJob): Promise<PdfJobResult>;
|
|
105
|
+
/**
|
|
106
|
+
* Normalise a PDF in a child process (`normalizePdf`, the same pass a local
|
|
107
|
+
* render gets). Rejects when the bytes are not a PDF pdf-lib can read, or the
|
|
108
|
+
* child times out.
|
|
109
|
+
*
|
|
110
|
+
* @param pdf - A PDF a PDF service returned
|
|
111
|
+
* @param timeoutMs - The child is killed after this
|
|
112
|
+
*/
|
|
113
|
+
export declare function normalizePdfInChild(pdf: Uint8Array, timeoutMs: number): Promise<Uint8Array>;
|