@morphemeris/armilla-core 0.2.0 → 0.3.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.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Versioned default-theme history (spec §10.2, §13.5): the binding ships every
3
+ * historical default-theme snapshot so hosts can pin a theme's base via
4
+ * `extends: "default_v<n>"`. Build the WASM first: `pnpm build:wasm`.
5
+ *
6
+ * The history gate: when `crates/core/themes/default.json` changes content,
7
+ * the latest snapshot no longer matches and this suite fails until a new
8
+ * snapshot is cut (`node scripts/gen-default-themes.mjs bump`).
9
+ */
10
+ import { readdirSync, readFileSync } from 'node:fs';
11
+ import { describe, expect, it } from 'vitest';
12
+ import { DEFAULT_THEME_VERSION, defaultTheme, layout } from './index.js';
13
+ const historyDir = new URL('../../../crates/core/themes/history/', import.meta.url);
14
+ const natal = JSON.parse(readFileSync(new URL('../../../fixtures/charts/natal_timed.json', import.meta.url), 'utf-8'));
15
+ const environment = {
16
+ medium: 'screen',
17
+ scheme: 'light',
18
+ contrast: 'normal',
19
+ motion: 'full',
20
+ density: 'normal',
21
+ };
22
+ const emptyView = { schema_version: 1 };
23
+ const historyFiles = () => readdirSync(historyDir).sort();
24
+ const readSnapshot = (version) => JSON.parse(readFileSync(new URL(`default_v${String(version)}.json`, historyDir), 'utf-8'));
25
+ describe('the history on disk', () => {
26
+ it('is contiguous default_v1..default_vN with N = DEFAULT_THEME_VERSION', () => {
27
+ const expected = Array.from({ length: DEFAULT_THEME_VERSION }, (_, i) => `default_v${String(i + 1)}.json`).sort();
28
+ expect(historyFiles()).toEqual(expected);
29
+ });
30
+ it('gives every snapshot the id of its filename and no extends', () => {
31
+ for (let v = 1; v <= DEFAULT_THEME_VERSION; v += 1) {
32
+ const doc = readSnapshot(v);
33
+ expect(doc['id']).toBe(`default_v${String(v)}`);
34
+ expect(doc['extends']).toBeUndefined();
35
+ }
36
+ });
37
+ it('keeps snapshot version strings distinct (each content change bumps the doc version)', () => {
38
+ const versions = Array.from({ length: DEFAULT_THEME_VERSION }, (_, i) => String(readSnapshot(i + 1)['version']));
39
+ expect(new Set(versions).size).toBe(versions.length);
40
+ });
41
+ it('matches the current built-in default in its latest snapshot, modulo id (the bump gate)', () => {
42
+ const latest = readSnapshot(DEFAULT_THEME_VERSION);
43
+ const current = defaultTheme();
44
+ expect({ ...latest, id: 'default' }).toEqual(current);
45
+ });
46
+ });
47
+ describe('defaultTheme(version)', () => {
48
+ it('returns the current default with the version omitted, unchanged behavior', () => {
49
+ const doc = defaultTheme();
50
+ expect(doc['id']).toBe('default');
51
+ });
52
+ it('returns the pinned snapshot for a history version, verbatim', () => {
53
+ const doc = defaultTheme(1);
54
+ expect(doc).toEqual(readSnapshot(1));
55
+ expect(doc['id']).toBe('default_v1');
56
+ });
57
+ it('rejects versions outside 1..DEFAULT_THEME_VERSION', () => {
58
+ expect(() => defaultTheme(0)).toThrow(/version/);
59
+ expect(() => defaultTheme(DEFAULT_THEME_VERSION + 1)).toThrow(/version/);
60
+ expect(() => defaultTheme(1.5)).toThrow(/version/);
61
+ });
62
+ });
63
+ describe('pinned chains', () => {
64
+ const pinned = {
65
+ schema_version: 1,
66
+ id: 'pin_probe',
67
+ name: 'Pin probe',
68
+ version: '0.0.1',
69
+ author: 'test',
70
+ license: 'MIT',
71
+ extends: `default_v${String(DEFAULT_THEME_VERSION)}`,
72
+ rules: [],
73
+ };
74
+ it('lays out with the snapshot supplied as the pinned base', () => {
75
+ const scene = layout(natal, [defaultTheme(DEFAULT_THEME_VERSION), pinned], emptyView, environment, { kind: 'wheel' });
76
+ expect(scene.marks.length).toBeGreaterThan(0);
77
+ const chainIds = scene.provenance.theme_chain.map(([id]) => id);
78
+ expect(chainIds).toEqual(['default', `default_v${String(DEFAULT_THEME_VERSION)}`, 'pin_probe']);
79
+ });
80
+ it('fails loudly (REF), never drifts silently, when the pin is not resolved by the host', () => {
81
+ expect(() => layout(natal, [pinned], emptyView, environment, { kind: 'wheel' })).toThrow(/extends/);
82
+ });
83
+ });
package/dist/surface.d.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  * this wholesale and differ only in how the WASM artifact loads.
5
5
  */
6
6
  export { ArmillaError, bookmark, defaultTheme, explain, layout, lint, mapLines, migrate, paintSvg, tableRows, unbookmark, validateWorkspace, version, } from './armilla.js';
7
+ export { DEFAULT_THEME_VERSION } from './default-themes.generated.js';
7
8
  export { naturalEarth110m } from './basemap.generated.js';
8
9
  export { vocabulary } from './vocabulary.generated.js';
9
10
  export type { Anchor, BandInfo, Basemap, BasemapFeature, BasemapKind, Baseline, CellSpec, ChartInputDoc, Decoration, EnvironmentDoc, ErrorRecord, Explanation, Finding, Geometry, GridLayout, LayoutOptionsDoc, MapLine, MapLinesModel, Mark, MarkKind, OriginSpec, Place, Region, PackManifestDoc, PackRef, Paint, Panel, PanelKind, PanelViewBinding, Provenance, Scene, SceneTypography, SchemaVersions, Segment, Space, Style, Subject, TableCell, TableColumn, TableKind, TableModel, TableOptions, TableRow, Tag, ThemeDoc, TrackInfo, TrackKind, Treatment, TreatRuleDoc, ViewStateDoc, Vocabulary, VocabularyKey, Workspace, WorkspaceDoc, } from './types.js';
package/dist/surface.js CHANGED
@@ -4,6 +4,7 @@
4
4
  * this wholesale and differ only in how the WASM artifact loads.
5
5
  */
6
6
  export { ArmillaError, bookmark, defaultTheme, explain, layout, lint, mapLines, migrate, paintSvg, tableRows, unbookmark, validateWorkspace, version, } from './armilla.js';
7
+ export { DEFAULT_THEME_VERSION } from './default-themes.generated.js';
7
8
  export { naturalEarth110m } from './basemap.generated.js';
8
9
  export { vocabulary } from './vocabulary.generated.js';
9
10
  export { SCENE_SCHEMA_VERSION, isScene } from './types.js';
package/docs/themes.md CHANGED
@@ -47,6 +47,18 @@ The predicate language (§9.1) is a conjunction of `key=value` terms and `measur
47
47
  comparisons. Keys and closed value sets are listed in the generated
48
48
  [vocabulary reference](vocabulary.md).
49
49
 
50
+ ### Pinning the default
51
+
52
+ The default theme's content evolves; a theme that says `extends: "default"` (or nothing) rides
53
+ the latest. To freeze the base it was designed against, extend a **versioned base** instead:
54
+ `extends: "default_v3"`. Every historical default ships in `@morphemeris/armilla-core` —
55
+ `defaultTheme(3)` returns the snapshot, `DEFAULT_THEME_VERSION` names the newest — and the host
56
+ supplies it as the chain's first document. Under the hood the current default still sits beneath
57
+ as the implicit root, so marks the pinned era never styled (kinds and roles added since) render
58
+ sensibly instead of unstyled. A pinned theme handed to a host that does not supply its snapshot
59
+ fails with a `REF` error rather than silently rendering against the wrong base. Pinning freezes
60
+ theme content only — layout behavior follows the core version you run.
61
+
50
62
  ## The palette
51
63
 
52
64
  `$token` in any color-valued property resolves to `palette[token][scheme]` for the environment's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morphemeris/armilla-core",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "TypeScript binding for the armilla layout core (WASM): generated types and typed wrappers, no logic.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -50,6 +50,7 @@
50
50
  "test:bdd": "node --import tsx ./node_modules/@cucumber/cucumber/bin/cucumber.js",
51
51
  "test": "pnpm test:unit && pnpm test:bdd",
52
52
  "bench": "node --import tsx bench/perf.ts",
53
+ "gen:default-themes": "node scripts/gen-default-themes.mjs",
53
54
  "build:wasm": "node scripts/build-wasm.mjs nodejs",
54
55
  "build:wasm:web": "node scripts/build-wasm.mjs web",
55
56
  "build:wasm:all": "node scripts/build-wasm.mjs all"