@murumets-ee/pdf 0.0.0-preview-20261014110621
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/LICENSE +94 -0
- package/README.md +104 -0
- package/dist/index.d.mts +143 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/dist/primitives.d.mts +10 -0
- package/dist/primitives.d.mts.map +1 -0
- package/dist/primitives.mjs +1 -0
- package/fonts/NotoSans-Bold.ttf +0 -0
- package/fonts/NotoSans-Regular.ttf +0 -0
- package/fonts/OFL.txt +93 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Elastic License 2.0 (ELv2)
|
|
2
|
+
|
|
3
|
+
URL: https://www.elastic.co/licensing/elastic-license
|
|
4
|
+
|
|
5
|
+
## Acceptance
|
|
6
|
+
|
|
7
|
+
By using the software, you agree to all of the terms and conditions below.
|
|
8
|
+
|
|
9
|
+
## Copyright License
|
|
10
|
+
|
|
11
|
+
The licensor grants you a non-exclusive, royalty-free, worldwide,
|
|
12
|
+
non-sublicensable, non-transferable license to use, copy, distribute, make
|
|
13
|
+
available, and prepare derivative works of the software, in each case subject
|
|
14
|
+
to the limitations and conditions below.
|
|
15
|
+
|
|
16
|
+
## Limitations
|
|
17
|
+
|
|
18
|
+
You may not provide the software to third parties as a hosted or managed
|
|
19
|
+
service, where the service provides users with access to any substantial set
|
|
20
|
+
of the features or functionality of the software.
|
|
21
|
+
|
|
22
|
+
You may not move, change, disable, or circumvent the license key functionality
|
|
23
|
+
in the software, and you may not remove or obscure any functionality in the
|
|
24
|
+
software that is protected by the license key.
|
|
25
|
+
|
|
26
|
+
You may not alter, remove, or obscure any licensing, copyright, or other
|
|
27
|
+
notices of the licensor in the software. Any use of the licensor's trademarks
|
|
28
|
+
is subject to applicable law.
|
|
29
|
+
|
|
30
|
+
## Patents
|
|
31
|
+
|
|
32
|
+
The licensor grants you a license, under any patent claims the licensor can
|
|
33
|
+
license, or becomes able to license, to make, have made, use, sell, offer for
|
|
34
|
+
sale, import and have imported the software, in each case subject to the
|
|
35
|
+
limitations and conditions in this license. This license does not cover any
|
|
36
|
+
patent claims that you cause to be infringed by modifications or additions to
|
|
37
|
+
the software. If you or your company make any written claim that the software
|
|
38
|
+
infringes or contributes to infringement of any patent, your patent license
|
|
39
|
+
for the software granted under these terms ends immediately. If your company
|
|
40
|
+
makes such a claim, your patent license ends immediately for work on behalf
|
|
41
|
+
of your company.
|
|
42
|
+
|
|
43
|
+
## Notices
|
|
44
|
+
|
|
45
|
+
You must ensure that anyone who gets a copy of any part of the software from
|
|
46
|
+
you also gets a copy of these terms.
|
|
47
|
+
|
|
48
|
+
If you modify the software, you must include in any modified copies of the
|
|
49
|
+
software prominent notices stating that you have modified the software.
|
|
50
|
+
|
|
51
|
+
## No Other Rights
|
|
52
|
+
|
|
53
|
+
These terms do not imply any licenses other than those expressly granted in
|
|
54
|
+
these terms.
|
|
55
|
+
|
|
56
|
+
## Termination
|
|
57
|
+
|
|
58
|
+
If you use the software in violation of these terms, such use is not licensed,
|
|
59
|
+
and your licenses will automatically terminate. If the licensor provides you
|
|
60
|
+
with a notice of your violation, and you cease all violation of this license
|
|
61
|
+
no later than 30 days after you receive that notice, your licenses will be
|
|
62
|
+
reinstated retroactively. However, if you violate these terms after such
|
|
63
|
+
reinstatement, any additional violation of these terms will cause your
|
|
64
|
+
licenses to terminate automatically and permanently.
|
|
65
|
+
|
|
66
|
+
## No Liability
|
|
67
|
+
|
|
68
|
+
As far as the law allows, the software comes as is, without any warranty or
|
|
69
|
+
condition, and the licensor will not be liable to you for any damages arising
|
|
70
|
+
out of these terms or the use or nature of the software, under any kind of
|
|
71
|
+
legal claim.
|
|
72
|
+
|
|
73
|
+
## Definitions
|
|
74
|
+
|
|
75
|
+
The **licensor** is the entity offering these terms, and the **software** is
|
|
76
|
+
the software the licensor makes available under these terms, including any
|
|
77
|
+
portion of it.
|
|
78
|
+
|
|
79
|
+
**you** refers to the individual or entity agreeing to these terms.
|
|
80
|
+
|
|
81
|
+
**your company** is any legal entity, sole proprietorship, or other kind of
|
|
82
|
+
organization that you work for, plus all organizations that have control over,
|
|
83
|
+
are under the control of, or are under common control with that organization.
|
|
84
|
+
**control** means ownership of substantially all the assets of an entity, or
|
|
85
|
+
the power to direct the management and policies of an entity (for example, by
|
|
86
|
+
voting right, contract, or otherwise). Control can be direct or indirect.
|
|
87
|
+
|
|
88
|
+
**your licenses** are all the licenses granted to you for the software under
|
|
89
|
+
these terms.
|
|
90
|
+
|
|
91
|
+
**use** means anything you do with the software requiring one of your
|
|
92
|
+
licenses.
|
|
93
|
+
|
|
94
|
+
**trademark** means trademarks, service marks, and similar rights.
|
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# @murumets-ee/pdf
|
|
2
|
+
|
|
3
|
+
A React element tree in, PDF bytes plus a SHA-256 digest out. Domain-free and server-only in
|
|
4
|
+
intent: it knows nothing about contracts, invoices, entities, storage buckets or blocks.
|
|
5
|
+
|
|
6
|
+
Engine rationale, flip conditions and the determinism policy are locked in
|
|
7
|
+
[`plan/rent/decisions/D028-pdf-package-and-engine.md`](../../plan/rent/decisions/D028-pdf-package-and-engine.md);
|
|
8
|
+
the legal background is [R06](../../plan/rent/research/R06-contract-legal-and-pdf.md).
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
import { createReactPdfRenderer, DEFAULT_FONT_FAMILY } from '@murumets-ee/pdf'
|
|
14
|
+
import { Document, Page, StyleSheet, Text, View } from '@murumets-ee/pdf/primitives'
|
|
15
|
+
|
|
16
|
+
const styles = StyleSheet.create({
|
|
17
|
+
page: { fontFamily: DEFAULT_FONT_FAMILY, padding: 40, fontSize: 11 },
|
|
18
|
+
heading: { fontFamily: DEFAULT_FONT_FAMILY, fontWeight: 700, fontSize: 16 },
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const renderer = createReactPdfRenderer()
|
|
22
|
+
|
|
23
|
+
const { bytes, sha256, byteLength } = await renderer.render(
|
|
24
|
+
<Document>
|
|
25
|
+
<Page size="A4" style={styles.page}>
|
|
26
|
+
<View>
|
|
27
|
+
<Text style={styles.heading}>Üürileping</Text>
|
|
28
|
+
<Text>Договор аренды</Text>
|
|
29
|
+
</View>
|
|
30
|
+
</Page>
|
|
31
|
+
</Document>,
|
|
32
|
+
{ creationDate: signedAt, producer: 'Lumi CMS', creator: 'Lumi CMS' },
|
|
33
|
+
)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Build documents out of `@murumets-ee/pdf/primitives` — `Document`, `Page`, `View`, `Text`,
|
|
37
|
+
`Image`, `StyleSheet`, `Font`, plus the `Style` / `Styles` types. Consumers should never add a
|
|
38
|
+
direct `@react-pdf/renderer` dependency; the engine seam is this package.
|
|
39
|
+
|
|
40
|
+
`creationDate` is **required**. There is no wall-clock default: an implicit `new Date()` is the
|
|
41
|
+
single biggest source of gratuitous non-determinism, and a caller that wants "now" should say so.
|
|
42
|
+
|
|
43
|
+
## The digest is not a reproducibility claim
|
|
44
|
+
|
|
45
|
+
`sha256` identifies **the bytes it was returned with**. Re-rendering the same tree with the same
|
|
46
|
+
options is *not* expected to reproduce it, so **re-render-and-compare is not a valid integrity
|
|
47
|
+
check** — it will report a false mismatch on a document nobody touched.
|
|
48
|
+
|
|
49
|
+
Two independent reasons, both inside the engine:
|
|
50
|
+
|
|
51
|
+
1. `@react-pdf/renderer` vendors a pdfkit fork whose six-letter font-subset tag (`ABCDEF+NotoSans`)
|
|
52
|
+
comes from `Math.random()`. Upstream pdfkit derives it from the font's object id; the fork has
|
|
53
|
+
not picked that up. We deliberately do **not** patch or seed the global RNG — it is
|
|
54
|
+
process-wide and would corrupt any concurrent render in the same worker.
|
|
55
|
+
2. pdfkit compresses each stream through an **async** `zlib.createDeflate()` and writes each object
|
|
56
|
+
when *its* deflate finishes, so two streams of different sizes can complete in either order.
|
|
57
|
+
The `FontFile2` and `ToUnicode` objects are observed swapping places between runs.
|
|
58
|
+
|
|
59
|
+
What *is* pinned, and covered by tests: the Info dictionary (`/CreationDate`, `/ModificationDate`,
|
|
60
|
+
`/Producer`, `/Creator`), the file `/ID` that pdfkit derives from `md5(info)`, the total byte
|
|
61
|
+
length, and every indirect object's content.
|
|
62
|
+
|
|
63
|
+
The policy, per [R06](../../plan/rent/research/R06-contract-legal-and-pdf.md) §2: **hash on write,
|
|
64
|
+
persist the exact bytes, never re-render for comparison.** ISO 32000-1 §14.4 says outright that
|
|
65
|
+
*"the calculation of the file identifier need not be reproducible"*, and no e-signature vendor
|
|
66
|
+
re-renders from source to verify — they all hash the stored bytes.
|
|
67
|
+
|
|
68
|
+
## Fonts
|
|
69
|
+
|
|
70
|
+
**Noto Sans**, regular + bold, shipped inside the package. A render never fetches a font over the
|
|
71
|
+
network.
|
|
72
|
+
|
|
73
|
+
One file covers Latin, Latin Extended (Estonian `õäöüšž`), Greek and Cyrillic — these are the
|
|
74
|
+
[`notofonts/latin-greek-cyrillic`](https://github.com/notofonts/latin-greek-cyrillic) static
|
|
75
|
+
builds, and `src/fonts.test.ts` asserts glyph coverage per script rather than assuming it. That
|
|
76
|
+
single-file property is load-bearing: `@react-pdf/renderer` has no `unicode-range` mechanism, so a
|
|
77
|
+
per-script split would need a fallback chain it cannot express, and a missing glyph renders as
|
|
78
|
+
`.notdef` **silently**.
|
|
79
|
+
|
|
80
|
+
`loadDefaultFonts()` reads both weights off disk *before* the render rather than letting react-pdf
|
|
81
|
+
load them lazily mid-layout — the lazy path changes pdfkit's object emission order, so the first
|
|
82
|
+
render in a process would otherwise differ structurally from every later one.
|
|
83
|
+
|
|
84
|
+
The fonts are licensed under the **SIL Open Font License 1.1**; the licence text ships with them at
|
|
85
|
+
[`fonts/OFL.txt`](./fonts/OFL.txt). Copyright 2022 The Noto Project Authors. Install weight: ~1.2 MB
|
|
86
|
+
of font files, plus `@react-pdf/renderer` (~33 MB, 67 transitive packages, no native build, no
|
|
87
|
+
postinstall, no browser download).
|
|
88
|
+
|
|
89
|
+
## Not `server-only`-tagged — deliberately
|
|
90
|
+
|
|
91
|
+
The package is server-only in intent, but the entry does **not** `import 'server-only'`.
|
|
92
|
+
|
|
93
|
+
Its consumers reach it from an admin route, which is reachable from a plugin's static entry — the
|
|
94
|
+
exact chain `pnpm verify:plugin-loads` loads under plain Node, where `server-only` throws
|
|
95
|
+
unconditionally. `@murumets-ee/rent`'s `plugin.ts` already dynamic-imports its server-only runtime
|
|
96
|
+
deps to stay jiti-safe; tagging this package would force the same escape hatch on every consumer
|
|
97
|
+
for no security gain, since nothing here reads a request, a session or the database.
|
|
98
|
+
|
|
99
|
+
`@murumets-ee/storage` carries the tag on `./` precisely because it keeps a *separate*, clean
|
|
100
|
+
`./plugin` entry for the CLI path. This package has no such split — the renderer is the whole
|
|
101
|
+
surface.
|
|
102
|
+
|
|
103
|
+
Keep it out of any `*-ui/src/pages/**` static-import path (or add it to that package's tsdown
|
|
104
|
+
`neverBundle`) so it is never inlined into a server-pages bundle.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { ReactElement } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/digest.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Lowercase hex SHA-256 of exactly the bytes in `bytes`.
|
|
6
|
+
*
|
|
7
|
+
* Respects `byteOffset` / `byteLength`, so a `Buffer` that is a window into a
|
|
8
|
+
* larger pooled `ArrayBuffer` hashes its own contents and nothing else.
|
|
9
|
+
*
|
|
10
|
+
* See {@link PdfRenderResult.sha256} for what this digest does and does not
|
|
11
|
+
* attest to — it identifies a specific byte sequence, not a rendering input.
|
|
12
|
+
*/
|
|
13
|
+
declare function sha256Hex(bytes: Uint8Array): string;
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/fonts.d.ts
|
|
16
|
+
/**
|
|
17
|
+
* The one font family this package registers.
|
|
18
|
+
*
|
|
19
|
+
* **Noto Sans** (SIL Open Font License 1.1). A single file covers Latin,
|
|
20
|
+
* Latin Extended (Estonian `õäöüšž`), Greek and Cyrillic — these are the
|
|
21
|
+
* `notofonts/latin-greek-cyrillic` static builds, verified glyph-by-glyph in
|
|
22
|
+
* `fonts.test.ts`. That matters because `@react-pdf/renderer` has no
|
|
23
|
+
* `unicode-range` mechanism: a per-script split would need a fallback chain it
|
|
24
|
+
* cannot express, and missing glyphs render as `.notdef` **silently**.
|
|
25
|
+
*/
|
|
26
|
+
declare const DEFAULT_FONT_FAMILY = "Noto Sans";
|
|
27
|
+
/**
|
|
28
|
+
* Absolute paths to the bundled font files. They ship **inside the package**;
|
|
29
|
+
* a render never fetches a font over the network.
|
|
30
|
+
*/
|
|
31
|
+
declare const NOTO_SANS_FILES: {
|
|
32
|
+
readonly regular: string;
|
|
33
|
+
readonly bold: string;
|
|
34
|
+
};
|
|
35
|
+
/** The SIL Open Font License 1.1 text that covers {@link NOTO_SANS_FILES}. */
|
|
36
|
+
declare const NOTO_SANS_LICENSE_FILE: string;
|
|
37
|
+
/**
|
|
38
|
+
* Register {@link DEFAULT_FONT_FAMILY} on react-pdf's font store.
|
|
39
|
+
*
|
|
40
|
+
* Idempotent: react-pdf's store is a module singleton, and `register` appends
|
|
41
|
+
* to a family's source list rather than replacing it, so a naive repeat call
|
|
42
|
+
* would stack duplicates. The guard reads the store instead of tracking local
|
|
43
|
+
* state, so it stays correct across a caller's `Font.reset()`.
|
|
44
|
+
*/
|
|
45
|
+
declare function registerDefaultFonts(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Register the default family AND read both weights off disk before returning.
|
|
48
|
+
*
|
|
49
|
+
* This is a determinism fix, not a performance one. react-pdf loads a font
|
|
50
|
+
* lazily, mid-layout, the first time a glyph needs it — and that extra `await`
|
|
51
|
+
* changes the order in which pdfkit emits objects, so the FIRST render in a
|
|
52
|
+
* process produces a different object numbering (and different xref offsets)
|
|
53
|
+
* than every render after it. Measured: renders 2/3/4 were byte-identical once
|
|
54
|
+
* the subset tag is normalised away, render 1 was not. Pre-loading collapses
|
|
55
|
+
* that difference.
|
|
56
|
+
*
|
|
57
|
+
* Bounded by construction: exactly the two weights this package registers, no
|
|
58
|
+
* fan-out over a caller-supplied list. A consumer that registers additional
|
|
59
|
+
* families is responsible for warming them the same way.
|
|
60
|
+
*/
|
|
61
|
+
declare function loadDefaultFonts(): Promise<void>;
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/types.d.ts
|
|
64
|
+
/**
|
|
65
|
+
* Document-level metadata pinned onto every render.
|
|
66
|
+
*
|
|
67
|
+
* Everything here exists to remove *gratuitous* non-determinism from the
|
|
68
|
+
* output. It is deliberately small: this package renders a React tree, it does
|
|
69
|
+
* not model documents.
|
|
70
|
+
*/
|
|
71
|
+
interface PdfRenderOptions {
|
|
72
|
+
/**
|
|
73
|
+
* Pinned document timestamps. REQUIRED — callers must pass a deliberate value.
|
|
74
|
+
* There is no wall-clock default: an implicit `new Date()` is the single biggest
|
|
75
|
+
* source of gratuitous non-determinism, and a caller that wants "now" should say so.
|
|
76
|
+
*/
|
|
77
|
+
readonly creationDate: Date;
|
|
78
|
+
readonly modificationDate?: Date;
|
|
79
|
+
readonly producer?: string;
|
|
80
|
+
readonly creator?: string;
|
|
81
|
+
}
|
|
82
|
+
interface PdfRenderResult {
|
|
83
|
+
readonly bytes: Uint8Array;
|
|
84
|
+
/**
|
|
85
|
+
* Lowercase hex SHA-256 of `bytes`, computed over exactly what is returned.
|
|
86
|
+
*
|
|
87
|
+
* ## This digest identifies THESE BYTES — it is not a reproducibility claim
|
|
88
|
+
*
|
|
89
|
+
* Re-rendering the same React tree with the same {@link PdfRenderOptions} is
|
|
90
|
+
* **not** expected to reproduce this digest, and **re-render-and-compare is
|
|
91
|
+
* therefore not a valid integrity check**. Doing so will report a false
|
|
92
|
+
* mismatch on a document that was never touched.
|
|
93
|
+
*
|
|
94
|
+
* The concrete reason: `@react-pdf/renderer` vendors a fork of pdfkit whose
|
|
95
|
+
* font-subset tag (the six-letter `ABCDEF+` prefix on `/BaseFont`) comes from
|
|
96
|
+
* `Math.random()`. Upstream pdfkit derives that tag from the font's object id;
|
|
97
|
+
* the fork has not picked the change up. We deliberately do **not** patch or
|
|
98
|
+
* seed the global RNG to work around it — see
|
|
99
|
+
* `plan/rent/decisions/D028-pdf-package-and-engine.md`.
|
|
100
|
+
*
|
|
101
|
+
* The policy, per `plan/rent/research/R06-contract-legal-and-pdf.md` §2:
|
|
102
|
+
* **hash on write, persist the exact bytes, never re-render for comparison.**
|
|
103
|
+
* That is also the legally correct model and matches universal industry
|
|
104
|
+
* practice — ISO 32000-1 §14.4 states outright that *"the calculation of the
|
|
105
|
+
* file identifier need not be reproducible"*, and no e-signature vendor
|
|
106
|
+
* re-renders from source to verify; they all hash the stored bytes.
|
|
107
|
+
*
|
|
108
|
+
* To verify a stored document: hash the stored bytes and compare against the
|
|
109
|
+
* digest recorded at write time. Never regenerate the document.
|
|
110
|
+
*/
|
|
111
|
+
readonly sha256: string;
|
|
112
|
+
readonly byteLength: number;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The engine seam. One implementation ships today
|
|
116
|
+
* (`createReactPdfRenderer`); the interface exists so a swap costs one
|
|
117
|
+
* package rather than every consumer.
|
|
118
|
+
*/
|
|
119
|
+
interface PdfRenderer {
|
|
120
|
+
render(document: ReactElement, options: PdfRenderOptions): Promise<PdfRenderResult>;
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/react-pdf-renderer.d.ts
|
|
124
|
+
/**
|
|
125
|
+
* The `@react-pdf/renderer` implementation of {@link PdfRenderer}.
|
|
126
|
+
*
|
|
127
|
+
* Stateless and cheap to construct — react-pdf's font store is a module
|
|
128
|
+
* singleton, so several renderers share one registration. Renders happen
|
|
129
|
+
* entirely in-process: no browser, no sidecar, no network (the fonts ship
|
|
130
|
+
* inside this package), and the document never executes script.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```ts
|
|
134
|
+
* const renderer = createReactPdfRenderer()
|
|
135
|
+
* const { bytes, sha256 } = await renderer.render(<Document>…</Document>, {
|
|
136
|
+
* creationDate: signedAt, // a deliberate instant, never `new Date()` by default
|
|
137
|
+
* })
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
declare function createReactPdfRenderer(): PdfRenderer;
|
|
141
|
+
//#endregion
|
|
142
|
+
export { DEFAULT_FONT_FAMILY, NOTO_SANS_FILES, NOTO_SANS_LICENSE_FILE, type PdfRenderOptions, type PdfRenderResult, type PdfRenderer, createReactPdfRenderer, loadDefaultFonts, registerDefaultFonts, sha256Hex };
|
|
143
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/digest.ts","../src/fonts.ts","../src/types.ts","../src/react-pdf-renderer.ts"],"mappings":";;;;;;AAWA;;;;AAA2C;;iBAA3B,SAAA,CAAU,KAAiB,EAAV,UAAU;;;;;;AAA3C;;;;AAA2C;;;cCI9B,mBAAA;AAAb;;;;AAAA,cAiBa,eAAA;EAAA,SAGH,OAAA;EAAA,SAAA,IAAA;AAAA;;cAGG,sBAAA;AAAb;;;;AAA+D;AAU/D;;;AAVA,iBAUgB,oBAAA;AAAoB;AAuCpC;;;;AAAiD;;;;AC9EjD;;;;;;ADuCoC,iBAuCd,gBAAA,IAAoB,OAAO;;;;;AD5EjD;;;;AAA2C;UEF1B,gBAAA;;;ADMjB;;;WCAW,YAAA,EAAc,IAAA;EAAA,SACd,gBAAA,GAAmB,IAAI;EAAA,SACvB,QAAA;EAAA,SACA,OAAA;AAAA;AAAA,UAGM,eAAA;EAAA,SACN,KAAA,EAAO,UAAU;EDgBf;;;;AAAkD;AAU/D;;;;AAAoC;AAuCpC;;;;AAAiD;;;;AC9EjD;;;;;;;;;ED6Ba,SCYF,MAAA;EAAA,SACA,UAAA;AAAA;AA9BX;;;;;AAAA,UAsCiB,WAAA;EACf,MAAA,CAAO,QAAA,EAAU,YAAA,EAAc,OAAA,EAAS,gBAAA,GAAmB,OAAA,CAAQ,eAAA;AAAA;;;;;AFjDrE;;;;AAA2C;;;;ACI3C;;;;AAAgC;AAiBhC;iBEwBgB,sBAAA,IAA0B,WAAW"}
|