@brandon_m_behring/book-scaffold-astro 3.2.0 → 3.4.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/LATEX_TO_MDX_MAPPING.md +130 -0
- package/dist/index.d.ts +70 -123
- package/dist/index.mjs +372 -138
- package/dist/schemas.d.ts +36 -2
- package/dist/schemas.mjs +183 -52
- package/package.json +3 -2
- package/pages/frontmatter/[...slug].astro +48 -0
- package/pages/print.astro +9 -1
- package/recipes/12-where-to-file-issues.md +58 -0
- package/scripts/build-bib.mjs +18 -0
- package/scripts/build-figures.mjs +19 -0
- package/scripts/build-labels.mjs +20 -0
- package/scripts/render-notebooks.mjs +19 -0
- package/scripts/validate.mjs +51 -37
- package/src/lib/freshness.ts +19 -4
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# LaTeX → MDX component mapping
|
|
2
|
+
|
|
3
|
+
A consumer-facing reference for converting LaTeX book sources into MDX that consumes `@brandon_m_behring/book-scaffold-astro` components.
|
|
4
|
+
|
|
5
|
+
The scaffold ships **38 components**. Without this map, a `.tex → .mdx` conversion ends up rediscovering them by grep — and frequently rebuilding duplicates. This doc is the canonical "if your LaTeX source has `\begin{<env>}`, here's the component" reference.
|
|
6
|
+
|
|
7
|
+
> Pair with [PACKAGE_DESIGN.md](./PACKAGE_DESIGN.md) §17 for the broader migration story and the `defineMdxComponents` helper for consumer-shipped extensions.
|
|
8
|
+
|
|
9
|
+
## Components shipped by the scaffold
|
|
10
|
+
|
|
11
|
+
| LaTeX construct | MDX component | Import path | Signature | Notes |
|
|
12
|
+
|---|---|---|---|---|
|
|
13
|
+
| `\begin{tcolorbox}[narrativebox]` | `SkillBox` | `…/components/SkillBox.astro` | `title: string` | Recipe / how-to box |
|
|
14
|
+
| `\begin{tcolorbox}[conceptbox]` | `ConceptBox` | `…/components/ConceptBox.astro` | `term: string` | Single-term definition |
|
|
15
|
+
| `\begin{tcolorbox}[insightbox]` | `InsightBox` | `…/components/InsightBox.astro` | `title?: string` | Non-obvious observation |
|
|
16
|
+
| `\begin{keyconcept}` | `KeyIdea` | `…/components/KeyIdea.astro` | — | Crystallized takeaway |
|
|
17
|
+
| `\begin{warnbox}` / `\warningmargin{}` | `WarnBox` | `…/components/WarnBox.astro` | `title?: string` | Caveats / failure modes |
|
|
18
|
+
| `\begin{notebox}` | `NoteBox` | `…/components/NoteBox.astro` | `title?: string` | Chapter overviews |
|
|
19
|
+
| `\begin{paperbox}` | `PaperBox` | `…/components/PaperBox.astro` | `title?: string` | Paper restatement |
|
|
20
|
+
| `\begin{counterbox}` | `CounterBox` | `…/components/CounterBox.astro` | `title?: string` | Counter-evidence |
|
|
21
|
+
| `\begin{examplebox}` | `ExampleBox` | `…/components/ExampleBox.astro` | `title?: string` | Extended walkthrough |
|
|
22
|
+
| `\begin{openquestion}` | `OpenQuestion` | `…/components/OpenQuestion.astro` | `title?: string` | Research questions |
|
|
23
|
+
| `\begin{trythis}` | `TryThis` | `…/components/TryThis.astro` | `title?: string` | Practice exercise |
|
|
24
|
+
| `\begin{tipbox}` | `TipBox` | `…/components/TipBox.astro` | `title?: string` | Pro tips / shortcuts |
|
|
25
|
+
| `\begin{dynconnect}` | `DynConnect` | `…/components/DynConnect.astro` | `title?: string` | Cross-domain connection |
|
|
26
|
+
| `\begin{theorem}` / `\begin{proposition}` / `\begin{lemma}` / `\begin{corollary}` / `\begin{definition}` / `\begin{remark}` / `\begin{proof}` | `Theorem` | `…/components/Theorem.astro` | `kind, n?, name?, id?` | amsthm family — single component dispatches via `kind` prop |
|
|
27
|
+
| `\marginnote{}` | `MarginNote` | `…/components/MarginNote.astro` | — | Side commentary |
|
|
28
|
+
| `\sidenote{}` | `Sidenote` | `…/components/Sidenote.astro` | — | Auto-numbered marginalia (Tufte) |
|
|
29
|
+
| `\includegraphics + \caption` | `Figure` | `…/components/Figure.astro` | `src, caption?, id?` | XRef-registered |
|
|
30
|
+
| `\cite{}` / `\parencite{}` | `Citation` | `…/components/Citation.astro` | `src, as?` | Resolves `sources` collection |
|
|
31
|
+
| `\cite{}` (inline) | `Cite` | `…/components/Cite.astro` | `key` | Inline citation key |
|
|
32
|
+
| `\xref{}` / `\cref{}` | `XRef` | `…/components/XRef.astro` | `id` | Cross-reference resolver |
|
|
33
|
+
| `\code{path:N}` / inline file refs | `CodeRef` | `…/components/CodeRef.astro` | `path, line?, lineEnd?` | GitHub-linked source ref |
|
|
34
|
+
| (custom Shiki blocks) | `CodeBlock` | `…/components/CodeBlock.astro` | `lang, title?` | Wrapped fenced code |
|
|
35
|
+
| `\recovery{}` | `Recovery` | `…/components/Recovery.astro` | `pattern, symptom?` | Anti-pattern escape |
|
|
36
|
+
| `\casestudy{}` | `CaseStudy` | `…/components/CaseStudy.astro` | `date, title?` | Dated anecdote |
|
|
37
|
+
| `\weekref{}` (academic) | `WeekRef` | `…/components/WeekRef.astro` | `week` | Cross-chapter week ref |
|
|
38
|
+
|
|
39
|
+
Component subset table for tools-profile-specific UI (volatility dashboards, convergence timelines):
|
|
40
|
+
|
|
41
|
+
| Construct | Component | Use case |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| Volatility badge | `Tag` | `volatility` enum chip in chapter meta |
|
|
44
|
+
| Tool comparison | `ToolFilter` (island) | Interactive comparison gate |
|
|
45
|
+
| Version selector | `VersionSelector` (island) | Switch between tool versions |
|
|
46
|
+
| Convergence event | `Convergence` | "All tools converged here" timeline marker |
|
|
47
|
+
| Divergence event | `Divergence` | "Tool X went its own way" annotation |
|
|
48
|
+
| Pattern timeline | `PatternTimeline` | Multi-event convergence dashboard |
|
|
49
|
+
| Status badge | `StatusBadge` | 7→3-state translation (academic) |
|
|
50
|
+
| Source archive | `SourceArchive` | Tier-tagged source listing |
|
|
51
|
+
|
|
52
|
+
## What is NOT shipped (extension candidates)
|
|
53
|
+
|
|
54
|
+
The scaffold deliberately doesn't ship these. Add to your consumer via the [`defineMdxComponents`](#consumer-side-extensions-definemdxcomponents) helper described below; surface as a tracked issue if the gap recurs across pilots.
|
|
55
|
+
|
|
56
|
+
- `\begin{problem}` / `\begin{solution}` — interview-prep problem cards
|
|
57
|
+
- `\begin{vignette}` — multi-step scenario walkthroughs
|
|
58
|
+
- `\begin{decisiontree}` — branching decision logic
|
|
59
|
+
- `\begin{interviewcontext}` — interview-tied learning-outcome callouts
|
|
60
|
+
- `<AnkiCard>` — flashcard widget
|
|
61
|
+
- `<Term>` — glossary term reference with tooltip
|
|
62
|
+
- `<RedFlag>` — escalated warning beyond `WarnBox`
|
|
63
|
+
- `<NarrativeBox>` (with extended props) — if a consumer needs richer narrative annotations beyond `SkillBox`
|
|
64
|
+
|
|
65
|
+
## Consumer-side extensions: `defineMdxComponents`
|
|
66
|
+
|
|
67
|
+
When your book uses custom MDX components, create `src/mdx-components.ts` (or `.js` / `.mjs`) at your project root. The scaffold auto-detects it and threads the components through all auto-injected routes (`/print`, future `/pdf`, `/epub`).
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
// consumer's src/mdx-components.ts
|
|
71
|
+
import { defineMdxComponents } from '@brandon_m_behring/book-scaffold-astro';
|
|
72
|
+
import AnkiCard from './components/AnkiCard.astro';
|
|
73
|
+
import NarrativeBox from './components/NarrativeBox.astro';
|
|
74
|
+
import Term from './components/Term.astro';
|
|
75
|
+
|
|
76
|
+
export default defineMdxComponents({
|
|
77
|
+
AnkiCard,
|
|
78
|
+
NarrativeBox,
|
|
79
|
+
Term,
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The `defineMdxComponents<T>()` helper is a TypeScript identity function — it returns the value unchanged, but preserves the exact key→component type mapping for IntelliSense. Same pattern as Vite/Astro `defineConfig`, Zod `z.object`, Drizzle `pgTable`.
|
|
84
|
+
|
|
85
|
+
To use a non-default path, pass it explicitly:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
// astro.config.mjs
|
|
89
|
+
export default defineBookConfig({
|
|
90
|
+
site: '...',
|
|
91
|
+
mdxComponentsModule: './src/my-custom-components.ts',
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Disabling auto-injected routes
|
|
96
|
+
|
|
97
|
+
The scaffold auto-injects per-profile defaults (see [PACKAGE_DESIGN.md](./PACKAGE_DESIGN.md) §6). Multi-book consumers (one Astro app, many books under `[book]/[chapter]`) typically want the flat `/chapters` route off:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
// astro.config.mjs
|
|
101
|
+
import { defineBookConfig } from '@brandon_m_behring/book-scaffold-astro';
|
|
102
|
+
|
|
103
|
+
export default defineBookConfig({
|
|
104
|
+
site: '...',
|
|
105
|
+
profile: 'course-notes',
|
|
106
|
+
routes: {
|
|
107
|
+
chapters: false, // override the profile default
|
|
108
|
+
convergence: false,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The shape is fixed (`references | search | print | chapters | convergence`) and TypeScript catches typos like `convergance: false`.
|
|
114
|
+
|
|
115
|
+
## Common conversion mistakes
|
|
116
|
+
|
|
117
|
+
Errors observed during the DLAI pilot (closed in v3.3.0 issue #5):
|
|
118
|
+
|
|
119
|
+
1. **Built `NarrativeBox` from scratch** → should have used `SkillBox`. Same vertical box semantic; `SkillBox` already has the `title` prop.
|
|
120
|
+
2. **Built `ConceptBox` (block container)** → conflicts with scaffold's `ConceptBox` (term-definition signature). Either rename the consumer one (`ConceptBlock`) or use scaffold's signature.
|
|
121
|
+
3. **Built `KeyConcept`** → should have used `KeyIdea`. Same crystallized-takeaway role; the scaffold's name comes from the Tufte-style margin-emphasis convention.
|
|
122
|
+
4. **Built `RedFlag`** → should have used `WarnBox`. Add a higher-severity variant via consumer-side `defineMdxComponents` if needed instead of duplicating WarnBox's semantic.
|
|
123
|
+
5. **Built `Sidenote` with category prop** → conflicts with scaffold's auto-numbered `Sidenote`. The scaffold uses CSS counters; consumer-side category metadata can wrap (e.g., `<TypedSidenote category="recovery"><Sidenote>...</Sidenote></TypedSidenote>`).
|
|
124
|
+
6. **Built duplicate `Citation` and `Figure`** → scaffold's versions are XRef-registered. Use them; extend behavior via wrapper components if richer attribution is needed.
|
|
125
|
+
|
|
126
|
+
## See also
|
|
127
|
+
|
|
128
|
+
- [PACKAGE_DESIGN.md](./PACKAGE_DESIGN.md) — full API contract + Phase A planning decisions
|
|
129
|
+
- [README.md](./README.md) — toolkit overview + getting started
|
|
130
|
+
- [CHANGELOG.md](../CHANGELOG.md) — release notes (issue #5 closed in v3.3.0)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,135 +1,82 @@
|
|
|
1
1
|
import { AstroUserConfig, AstroIntegration } from 'astro';
|
|
2
|
-
import {
|
|
3
|
-
export { B as
|
|
4
|
-
import
|
|
2
|
+
import { c as BookConfigOptions, f as BookScaffoldIntegrationOptions, y as volatilityLevels } from './types-Bb97Na9S.js';
|
|
3
|
+
export { A as AcademicChapter, B as BOOK_PRESETS, a as BOOK_PROFILES, b as BookConfigError, d as BookPreset, e as BookProfile, g as BookSchemasOptions, C as ChapterFor, h as CourseNotesChapter, M as MinimalChapter, P as ProfileDefinition, R as RouteToggles, T as ToolsChapter, i as academicChapterSchema, j as academicParts, k as changeKinds, l as changelogSchema, m as chapterStatus, n as courseNotesChapterSchema, o as defineProfile, p as minimalChapterSchema, q as patternCategories, r as patternsSchema, s as resolvePreset, t as resolveProfile, u as sourceTiers, v as sourcesSchema, w as toolSlugs, x as toolsChapterSchema } from './types-Bb97Na9S.js';
|
|
4
|
+
import 'astro/zod';
|
|
5
5
|
|
|
6
6
|
declare function defineBookConfig(opts: BookConfigOptions): Promise<AstroUserConfig>;
|
|
7
7
|
|
|
8
8
|
declare function bookScaffoldIntegration(opts: BookScaffoldIntegrationOptions): AstroIntegration;
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Identity helper — consumers wrap their mdx-components map for TS inference.
|
|
12
|
+
* Same pattern as Vite/Astro defineConfig: a generic identity function that
|
|
13
|
+
* preserves the exact shape (vs widening to Record<string, …>).
|
|
12
14
|
*
|
|
13
|
-
*
|
|
14
|
-
* be constructed at package-load time outside an Astro runtime context.
|
|
15
|
-
* `defineBookSchemas` in index.ts wraps these into Astro `defineCollection`
|
|
16
|
-
* calls at the consumer's content-config load time.
|
|
15
|
+
* export default defineMdxComponents({ AnkiCard, NarrativeBox });
|
|
17
16
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* The generic constraint Record<string, unknown> is intentionally loose:
|
|
18
|
+
* consumer components are `.astro` files whose runtime type
|
|
19
|
+
* (AstroComponentFactory) lives in `astro/runtime/server/index.js` — not a
|
|
20
|
+
* public Astro export, and importing from internals creates fragility. The
|
|
21
|
+
* looser constraint lets the helper compile cleanly across Astro versions;
|
|
22
|
+
* IntelliSense still surfaces exact keys.
|
|
20
23
|
*/
|
|
24
|
+
declare function defineMdxComponents<T extends Record<string, unknown>>(components: T): T;
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
declare
|
|
77
|
-
url: z.ZodString;
|
|
78
|
-
title: z.ZodString;
|
|
79
|
-
author: z.ZodOptional<z.ZodString>;
|
|
80
|
-
publish_date: z.ZodOptional<z.ZodDate>;
|
|
81
|
-
captured_at: z.ZodDate;
|
|
82
|
-
content_hash: z.ZodOptional<z.ZodString>;
|
|
83
|
-
tier: z.ZodEnum<{
|
|
84
|
-
"T1-official": "T1-official";
|
|
85
|
-
"T2-release-notes": "T2-release-notes";
|
|
86
|
-
"T3-practitioner": "T3-practitioner";
|
|
87
|
-
"T4-conjecture": "T4-conjecture";
|
|
88
|
-
}>;
|
|
89
|
-
tool: z.ZodEnum<{
|
|
90
|
-
"claude-code": "claude-code";
|
|
91
|
-
"gemini-cli": "gemini-cli";
|
|
92
|
-
"codex-cli": "codex-cli";
|
|
93
|
-
"cross-tool": "cross-tool";
|
|
94
|
-
}>;
|
|
95
|
-
perma_cc: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
96
|
-
local_cache: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
97
|
-
}, z.core.$strip>;
|
|
98
|
-
declare const changelogSchema: z.ZodObject<{
|
|
99
|
-
tool: z.ZodEnum<{
|
|
100
|
-
"claude-code": "claude-code";
|
|
101
|
-
"gemini-cli": "gemini-cli";
|
|
102
|
-
"codex-cli": "codex-cli";
|
|
103
|
-
"cross-tool": "cross-tool";
|
|
104
|
-
}>;
|
|
105
|
-
versions: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
106
|
-
version: z.ZodString;
|
|
107
|
-
date: z.ZodDate;
|
|
108
|
-
changes: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
109
|
-
pattern: z.ZodString;
|
|
110
|
-
kind: z.ZodEnum<{
|
|
111
|
-
added: "added";
|
|
112
|
-
removed: "removed";
|
|
113
|
-
changed: "changed";
|
|
114
|
-
deprecated: "deprecated";
|
|
115
|
-
}>;
|
|
116
|
-
note: z.ZodString;
|
|
117
|
-
source_key: z.ZodOptional<z.ZodString>;
|
|
118
|
-
}, z.core.$strip>>>;
|
|
119
|
-
}, z.core.$strip>>>;
|
|
120
|
-
}, z.core.$strip>;
|
|
121
|
-
declare const patternsSchema: z.ZodObject<{
|
|
122
|
-
name: z.ZodString;
|
|
123
|
-
description: z.ZodOptional<z.ZodString>;
|
|
124
|
-
category: z.ZodOptional<z.ZodEnum<{
|
|
125
|
-
safety: "safety";
|
|
126
|
-
scale: "scale";
|
|
127
|
-
context: "context";
|
|
128
|
-
interaction: "interaction";
|
|
129
|
-
extension: "extension";
|
|
130
|
-
other: "other";
|
|
131
|
-
}>>;
|
|
132
|
-
convergence_date: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
|
|
133
|
-
}, z.core.$strip>;
|
|
26
|
+
/**
|
|
27
|
+
* src/lib/freshness.ts — volatility-aware staleness computation.
|
|
28
|
+
*
|
|
29
|
+
* Each chapter carries a `last_verified` date and a `volatility` class.
|
|
30
|
+
* This module maps those to a freshness status the reader can trust.
|
|
31
|
+
*
|
|
32
|
+
* Thresholds chosen to align with Ch 15's source-tier audit cadences:
|
|
33
|
+
* stable-principle → 365 days (principles drift annually at most)
|
|
34
|
+
* architectural-pattern → 180 days (between annual and quarterly; "on
|
|
35
|
+
* major release" isn't derivable from
|
|
36
|
+
* frontmatter)
|
|
37
|
+
* feature-surface → 90 days (quarterly)
|
|
38
|
+
*
|
|
39
|
+
* Status bands (fraction of threshold):
|
|
40
|
+
* fresh (<75%) — green dot, unobtrusive
|
|
41
|
+
* verify-soon (75-100%) — yellow, mild warning
|
|
42
|
+
* stale (>100%) — amber/red, "verify before trusting"
|
|
43
|
+
*
|
|
44
|
+
* Example assertions (verified by visual inspection during Stage 3.1):
|
|
45
|
+
* getFreshness(today, 'feature-surface').status === 'fresh'
|
|
46
|
+
* getFreshness(today-70d, 'feature-surface').status === 'verify-soon'
|
|
47
|
+
* getFreshness(today-100d, 'feature-surface').status === 'stale'
|
|
48
|
+
* getFreshness(today-200d, 'stable-principle').status === 'fresh'
|
|
49
|
+
* getFreshness(today-300d, 'stable-principle').status === 'verify-soon'
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
type VolatilityLevel = (typeof volatilityLevels)[number];
|
|
53
|
+
type FreshnessStatus = 'fresh' | 'verify-soon' | 'stale';
|
|
54
|
+
interface Freshness {
|
|
55
|
+
status: FreshnessStatus;
|
|
56
|
+
daysOld: number;
|
|
57
|
+
thresholdDays: number;
|
|
58
|
+
/** Days until stale; negative when already stale. */
|
|
59
|
+
daysUntil: number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Compute freshness for a chapter given its last_verified date + volatility.
|
|
63
|
+
*
|
|
64
|
+
* Pure function; caller supplies `now` only in tests. Production callers omit.
|
|
65
|
+
*
|
|
66
|
+
* v3.3.0 (closes issue #1): tolerant of `lastVerified === undefined`. Returns
|
|
67
|
+
* `null` instead of crashing when the chapter schema omits the field (e.g.,
|
|
68
|
+
* academic profile chapters that don't track verification dates, or consumer
|
|
69
|
+
* schemas that don't declare last_verified).
|
|
70
|
+
*
|
|
71
|
+
* Callers compose with optional chaining:
|
|
72
|
+
* const status = getFreshness(d.last_verified, d.volatility)?.status;
|
|
73
|
+
*/
|
|
74
|
+
declare function getFreshness(lastVerified: Date | undefined, volatility: VolatilityLevel, now?: Date): Freshness | null;
|
|
75
|
+
/** Human-readable label for each status; used for ARIA + tooltips.
|
|
76
|
+
*
|
|
77
|
+
* v3.3.0: accepts `null` (the new return shape of getFreshness for undefined
|
|
78
|
+
* inputs). Returns a sentinel "unknown" label so callers can render a neutral
|
|
79
|
+
* affordance without a separate branch. */
|
|
80
|
+
declare function freshnessLabel(f: Freshness | null): string;
|
|
134
81
|
|
|
135
|
-
export { BookConfigOptions, BookScaffoldIntegrationOptions,
|
|
82
|
+
export { BookConfigOptions, BookScaffoldIntegrationOptions, type Freshness, type FreshnessStatus, type VolatilityLevel, bookScaffoldIntegration, defineBookConfig, defineMdxComponents, freshnessLabel, getFreshness, volatilityLevels };
|