@morphemeris/armilla-core 0.1.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/dist/types.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * few aliases that have no Rust counterpart.
8
8
  */
9
9
  export * from './types.generated.js';
10
- import type { Scene, Workspace } from './types.generated.js';
10
+ import type { PackManifest, Scene, Workspace } from './types.generated.js';
11
11
  /** The scene schema version this binding understands (spec §3.9). */
12
12
  export declare const SCENE_SCHEMA_VERSION: 1;
13
13
  /** A resolved paint: a color string or `'none'` (spec §10.5). */
@@ -26,8 +26,12 @@ export type WorkspaceDoc = Workspace;
26
26
  export type ChartInputDoc = unknown;
27
27
  /** A theme document (spec §3.4); passed through to the core. */
28
28
  export type ThemeDoc = unknown;
29
- /** A glyph pack manifest (spec §3.8); passed through to the core. */
30
- export type PackManifestDoc = unknown;
29
+ /**
30
+ * A glyph pack manifest (spec §3.8), typed since 2026-09-21 — the generated
31
+ * `PackManifest` shape. The core still validates; extra `GlyphGeometry`
32
+ * fields pass through untouched (the authoring-spec extension seam).
33
+ */
34
+ export type PackManifestDoc = PackManifest;
31
35
  /**
32
36
  * One §7.3 tag key in the predicate vocabulary (spec §13.5, §19): its value
33
37
  * space — the closed values expanded (`role` lists the §7.2 roles, `house`
@@ -991,6 +991,77 @@ export type LayoutOptionsDoc = {
991
991
  */
992
992
  frame?: string;
993
993
  };
994
+ export type JsonValue = number | string | boolean | ReadonlyArray<JsonValue> | {
995
+ [key in string]: JsonValue;
996
+ } | null;
997
+ export type BBox = {
998
+ /**
999
+ * Left edge.
1000
+ */
1001
+ x: number;
1002
+ /**
1003
+ * Top edge (y-down).
1004
+ */
1005
+ y: number;
1006
+ /**
1007
+ * Width.
1008
+ */
1009
+ w: number;
1010
+ /**
1011
+ * Height.
1012
+ */
1013
+ h: number;
1014
+ };
1015
+ export type GlyphGeometry = {
1016
+ /**
1017
+ * SVG path data, origin-centered, y-down.
1018
+ */
1019
+ d: string;
1020
+ } & ({
1021
+ [key in string]: number | string | boolean | ReadonlyArray<JsonValue> | {
1022
+ [key in string]: JsonValue;
1023
+ } | null;
1024
+ });
1025
+ export type Glyph = {
1026
+ /**
1027
+ * Authoring box in pack units, origin-centered.
1028
+ */
1029
+ bbox: BBox;
1030
+ /**
1031
+ * Geometry per variant; at least `regular`.
1032
+ */
1033
+ variants: {
1034
+ [key in string]: GlyphGeometry;
1035
+ };
1036
+ /**
1037
+ * Pack-level rendering hint carried into style (spec §10.5).
1038
+ */
1039
+ stroke_only: boolean;
1040
+ };
1041
+ export type PackManifest = {
1042
+ /**
1043
+ * Always [`PACK_SCHEMA_VERSION`].
1044
+ */
1045
+ schema_version: number;
1046
+ /**
1047
+ * Pack id.
1048
+ */
1049
+ id: string;
1050
+ /**
1051
+ * Pack version string.
1052
+ */
1053
+ version: string;
1054
+ /**
1055
+ * The box size glyph geometry is authored in (10 for wheel-style packs).
1056
+ */
1057
+ units: number;
1058
+ /**
1059
+ * Glyphs by id.
1060
+ */
1061
+ glyphs: {
1062
+ [key in string]: Glyph;
1063
+ };
1064
+ };
994
1065
  export type TableKind = "positions" | "cusps" | "aspects";
995
1066
  export type TableOptions = {
996
1067
  /**
package/docs/changelog.md CHANGED
@@ -35,3 +35,10 @@ Additions within version 1 (optional fields; older documents parse unchanged):
35
35
  cascade channels — a later `dim`/`emphasize` no longer resurrects a hidden mark; only
36
36
  `show` cancels a hide (restoring normal emphasis, as before). Behavioral, no schema
37
37
  change; rule lists that relied on dim-after-hide re-showing marks now keep them hidden.
38
+ - 2026-09-21 — pack manifest (§3.8): `units` must be 10 at schema_version 1 (previously any
39
+ positive number was accepted but anything but 10 rendered mis-scaled — theme glyph scales
40
+ are scene units per pack unit, tuned against 10-unit boxes). The required-glyph registry
41
+ gains the four lunation phases the §6.13 calendar emits, and the registry is published as
42
+ the generated pack reference (`docs/packs.md` / `docs/packs.json`) with the three authoring
43
+ tiers (minimal / standard / complete). The binding's `PackManifestDoc` becomes the generated
44
+ `PackManifest` type instead of `unknown`.
@@ -0,0 +1,478 @@
1
+ {
2
+ "groups": {
3
+ "aspects": [
4
+ "conjunction",
5
+ "opposition",
6
+ "trine",
7
+ "square",
8
+ "sextile",
9
+ "semi_sextile",
10
+ "quincunx",
11
+ "semi_square",
12
+ "sesquiquadrate",
13
+ "quintile",
14
+ "bi_quintile",
15
+ "septile",
16
+ "bi_septile",
17
+ "tri_septile",
18
+ "novile",
19
+ "bi_novile",
20
+ "quad_novile",
21
+ "quindecile",
22
+ "parallel",
23
+ "contraparallel"
24
+ ],
25
+ "bodies": [
26
+ "sun",
27
+ "moon",
28
+ "mercury",
29
+ "venus",
30
+ "earth",
31
+ "mars",
32
+ "jupiter",
33
+ "saturn",
34
+ "uranus",
35
+ "neptune",
36
+ "pluto",
37
+ "ceres",
38
+ "pallas",
39
+ "juno",
40
+ "vesta",
41
+ "hygiea",
42
+ "astraea",
43
+ "eros",
44
+ "psyche",
45
+ "amor",
46
+ "sappho",
47
+ "urania",
48
+ "chiron",
49
+ "pholus",
50
+ "nessus",
51
+ "asbolus",
52
+ "chariklo",
53
+ "eris",
54
+ "sedna",
55
+ "haumea",
56
+ "makemake",
57
+ "quaoar",
58
+ "orcus",
59
+ "ixion",
60
+ "varuna",
61
+ "gonggong",
62
+ "mean_node",
63
+ "true_node",
64
+ "north_node",
65
+ "south_node",
66
+ "mean_south_node",
67
+ "true_south_node",
68
+ "mean_apogee",
69
+ "osculating_apogee",
70
+ "interpolated_apogee",
71
+ "interpolated_perigee",
72
+ "lilith",
73
+ "mean_lilith",
74
+ "true_lilith",
75
+ "black_moon_lilith",
76
+ "priapus",
77
+ "ascendant",
78
+ "descendant",
79
+ "midheaven",
80
+ "imum_coeli",
81
+ "vertex",
82
+ "antivertex",
83
+ "east_point",
84
+ "polar_ascendant",
85
+ "cupido",
86
+ "hades",
87
+ "zeus",
88
+ "kronos",
89
+ "apollon",
90
+ "admetos",
91
+ "vulkanus",
92
+ "poseidon",
93
+ "transpluto",
94
+ "regulus",
95
+ "spica",
96
+ "algol",
97
+ "aldebaran",
98
+ "antares",
99
+ "fomalhaut",
100
+ "sirius",
101
+ "vega",
102
+ "arcturus",
103
+ "betelgeuse",
104
+ "rigel",
105
+ "pollux",
106
+ "castor",
107
+ "procyon",
108
+ "alcyone",
109
+ "part_of_fortune",
110
+ "part_of_spirit",
111
+ "part_of_eros",
112
+ "part_of_necessity",
113
+ "part_of_courage",
114
+ "part_of_victory",
115
+ "part_of_nemesis"
116
+ ],
117
+ "events": [
118
+ "occultation",
119
+ "lunar_eclipse"
120
+ ],
121
+ "kinds": [
122
+ "luminary",
123
+ "planet",
124
+ "asteroid",
125
+ "centaur",
126
+ "tno",
127
+ "node",
128
+ "apsis",
129
+ "lot",
130
+ "angle",
131
+ "fixed_star",
132
+ "uranian",
133
+ "unknown"
134
+ ],
135
+ "marks": [
136
+ "retrograde",
137
+ "direct",
138
+ "stationary",
139
+ "indicator",
140
+ "midpoint",
141
+ "antiscion",
142
+ "contra_antiscion"
143
+ ],
144
+ "phases": [
145
+ "new_moon",
146
+ "full_moon",
147
+ "first_quarter",
148
+ "last_quarter"
149
+ ],
150
+ "signs": [
151
+ "aries",
152
+ "taurus",
153
+ "gemini",
154
+ "cancer",
155
+ "leo",
156
+ "virgo",
157
+ "libra",
158
+ "scorpio",
159
+ "sagittarius",
160
+ "capricorn",
161
+ "aquarius",
162
+ "pisces"
163
+ ]
164
+ },
165
+ "manifest": {
166
+ "max_glyphs": 4096,
167
+ "schema_version": 1,
168
+ "units": 10.0
169
+ },
170
+ "tiers": {
171
+ "complete": [
172
+ "aries",
173
+ "taurus",
174
+ "gemini",
175
+ "cancer",
176
+ "leo",
177
+ "virgo",
178
+ "libra",
179
+ "scorpio",
180
+ "sagittarius",
181
+ "capricorn",
182
+ "aquarius",
183
+ "pisces",
184
+ "conjunction",
185
+ "opposition",
186
+ "trine",
187
+ "square",
188
+ "sextile",
189
+ "semi_sextile",
190
+ "quincunx",
191
+ "semi_square",
192
+ "sesquiquadrate",
193
+ "quintile",
194
+ "bi_quintile",
195
+ "septile",
196
+ "bi_septile",
197
+ "tri_septile",
198
+ "novile",
199
+ "bi_novile",
200
+ "quad_novile",
201
+ "quindecile",
202
+ "parallel",
203
+ "contraparallel",
204
+ "retrograde",
205
+ "direct",
206
+ "stationary",
207
+ "indicator",
208
+ "midpoint",
209
+ "antiscion",
210
+ "contra_antiscion",
211
+ "new_moon",
212
+ "full_moon",
213
+ "first_quarter",
214
+ "last_quarter",
215
+ "occultation",
216
+ "lunar_eclipse",
217
+ "luminary",
218
+ "planet",
219
+ "asteroid",
220
+ "centaur",
221
+ "tno",
222
+ "node",
223
+ "apsis",
224
+ "lot",
225
+ "angle",
226
+ "fixed_star",
227
+ "uranian",
228
+ "unknown",
229
+ "sun",
230
+ "moon",
231
+ "mercury",
232
+ "venus",
233
+ "earth",
234
+ "mars",
235
+ "jupiter",
236
+ "saturn",
237
+ "uranus",
238
+ "neptune",
239
+ "pluto",
240
+ "ceres",
241
+ "pallas",
242
+ "juno",
243
+ "vesta",
244
+ "hygiea",
245
+ "astraea",
246
+ "eros",
247
+ "psyche",
248
+ "amor",
249
+ "sappho",
250
+ "urania",
251
+ "chiron",
252
+ "pholus",
253
+ "nessus",
254
+ "asbolus",
255
+ "chariklo",
256
+ "eris",
257
+ "sedna",
258
+ "haumea",
259
+ "makemake",
260
+ "quaoar",
261
+ "orcus",
262
+ "ixion",
263
+ "varuna",
264
+ "gonggong",
265
+ "mean_node",
266
+ "true_node",
267
+ "north_node",
268
+ "south_node",
269
+ "mean_south_node",
270
+ "true_south_node",
271
+ "mean_apogee",
272
+ "osculating_apogee",
273
+ "interpolated_apogee",
274
+ "interpolated_perigee",
275
+ "lilith",
276
+ "mean_lilith",
277
+ "true_lilith",
278
+ "black_moon_lilith",
279
+ "priapus",
280
+ "ascendant",
281
+ "descendant",
282
+ "midheaven",
283
+ "imum_coeli",
284
+ "vertex",
285
+ "antivertex",
286
+ "east_point",
287
+ "polar_ascendant",
288
+ "cupido",
289
+ "hades",
290
+ "zeus",
291
+ "kronos",
292
+ "apollon",
293
+ "admetos",
294
+ "vulkanus",
295
+ "poseidon",
296
+ "transpluto",
297
+ "regulus",
298
+ "spica",
299
+ "algol",
300
+ "aldebaran",
301
+ "antares",
302
+ "fomalhaut",
303
+ "sirius",
304
+ "vega",
305
+ "arcturus",
306
+ "betelgeuse",
307
+ "rigel",
308
+ "pollux",
309
+ "castor",
310
+ "procyon",
311
+ "alcyone",
312
+ "part_of_fortune",
313
+ "part_of_spirit",
314
+ "part_of_eros",
315
+ "part_of_necessity",
316
+ "part_of_courage",
317
+ "part_of_victory",
318
+ "part_of_nemesis"
319
+ ],
320
+ "minimal": [
321
+ "aries",
322
+ "taurus",
323
+ "gemini",
324
+ "cancer",
325
+ "leo",
326
+ "virgo",
327
+ "libra",
328
+ "scorpio",
329
+ "sagittarius",
330
+ "capricorn",
331
+ "aquarius",
332
+ "pisces",
333
+ "sun",
334
+ "moon",
335
+ "mercury",
336
+ "venus",
337
+ "earth",
338
+ "mars",
339
+ "jupiter",
340
+ "saturn",
341
+ "uranus",
342
+ "neptune",
343
+ "pluto",
344
+ "mean_node",
345
+ "true_node",
346
+ "north_node",
347
+ "south_node",
348
+ "mean_south_node",
349
+ "true_south_node",
350
+ "chiron",
351
+ "lilith",
352
+ "mean_lilith",
353
+ "true_lilith",
354
+ "black_moon_lilith",
355
+ "mean_apogee",
356
+ "ascendant",
357
+ "descendant",
358
+ "midheaven",
359
+ "imum_coeli",
360
+ "conjunction",
361
+ "opposition",
362
+ "trine",
363
+ "square",
364
+ "sextile",
365
+ "retrograde",
366
+ "direct",
367
+ "stationary",
368
+ "indicator",
369
+ "new_moon",
370
+ "full_moon",
371
+ "first_quarter",
372
+ "last_quarter"
373
+ ],
374
+ "standard": [
375
+ "aries",
376
+ "taurus",
377
+ "gemini",
378
+ "cancer",
379
+ "leo",
380
+ "virgo",
381
+ "libra",
382
+ "scorpio",
383
+ "sagittarius",
384
+ "capricorn",
385
+ "aquarius",
386
+ "pisces",
387
+ "sun",
388
+ "moon",
389
+ "mercury",
390
+ "venus",
391
+ "earth",
392
+ "mars",
393
+ "jupiter",
394
+ "saturn",
395
+ "uranus",
396
+ "neptune",
397
+ "pluto",
398
+ "mean_node",
399
+ "true_node",
400
+ "north_node",
401
+ "south_node",
402
+ "mean_south_node",
403
+ "true_south_node",
404
+ "chiron",
405
+ "lilith",
406
+ "mean_lilith",
407
+ "true_lilith",
408
+ "black_moon_lilith",
409
+ "mean_apogee",
410
+ "ascendant",
411
+ "descendant",
412
+ "midheaven",
413
+ "imum_coeli",
414
+ "conjunction",
415
+ "opposition",
416
+ "trine",
417
+ "square",
418
+ "sextile",
419
+ "retrograde",
420
+ "direct",
421
+ "stationary",
422
+ "indicator",
423
+ "new_moon",
424
+ "full_moon",
425
+ "first_quarter",
426
+ "last_quarter",
427
+ "semi_sextile",
428
+ "quincunx",
429
+ "semi_square",
430
+ "sesquiquadrate",
431
+ "quintile",
432
+ "bi_quintile",
433
+ "septile",
434
+ "bi_septile",
435
+ "tri_septile",
436
+ "novile",
437
+ "bi_novile",
438
+ "quad_novile",
439
+ "quindecile",
440
+ "parallel",
441
+ "contraparallel",
442
+ "ceres",
443
+ "pallas",
444
+ "juno",
445
+ "vesta",
446
+ "osculating_apogee",
447
+ "interpolated_apogee",
448
+ "interpolated_perigee",
449
+ "priapus",
450
+ "vertex",
451
+ "antivertex",
452
+ "east_point",
453
+ "polar_ascendant",
454
+ "part_of_fortune",
455
+ "part_of_spirit",
456
+ "part_of_eros",
457
+ "part_of_necessity",
458
+ "part_of_courage",
459
+ "part_of_victory",
460
+ "part_of_nemesis",
461
+ "midpoint",
462
+ "antiscion",
463
+ "contra_antiscion",
464
+ "luminary",
465
+ "planet",
466
+ "asteroid",
467
+ "centaur",
468
+ "tno",
469
+ "node",
470
+ "apsis",
471
+ "lot",
472
+ "angle",
473
+ "fixed_star",
474
+ "uranian",
475
+ "unknown"
476
+ ]
477
+ }
478
+ }