@mat3ra/esse 2026.9.5-0 → 2026.9.6-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.
@@ -139,7 +139,7 @@ function categorizationSchemesFragment(graph) {
139
139
  const rest = graph.nodes.filter((node) => node.id.startsWith(prefix) && !reachesCategories(node.id));
140
140
  const tiered = total - rest.length;
141
141
  if (!tiered)
142
- return [`\`${domain}\``, String(total), "none — compositional"];
142
+ return [`\`${domain}\``, String(total), "M-CODE — compositional"];
143
143
  // Today the untiered remainder is entirely enum holders; say so only while true.
144
144
  const allEnums = rest.every((node) => /\/enum-options$|\/enums$/.test(node.id));
145
145
  const remainder = rest.length
@@ -245,13 +245,15 @@ function renderPage(page, pages, html) {
245
245
  </head>
246
246
  <body>
247
247
  <div id="titlebar">
248
- <span class="app-name">AI4Materials<span class="app-name-suffix">: data standards</span></span>
248
+ <span class="app-name">AI4Materials</span>
249
249
  <nav id="surfaces">
250
250
  <a href="index.html" class="current">Docs</a>
251
251
  <a href="../index.html">Explorer</a>
252
+ <a href="../index.html#/categories">Categories</a>
253
+ <a href="../index.html#/directories">Directories</a>
252
254
  <a href="../map/index.html">Ontology</a>
253
255
  </nav>
254
- <span id="titlebar-tagline">ESSE &middot; materials science ontology</span>
256
+ <span id="titlebar-tagline">ESSE &middot; materials science data standards and ontology</span>
255
257
  </div>
256
258
  <div id="workspace">
257
259
  <aside id="docs-nav">
@@ -308,15 +310,16 @@ body {
308
310
  z-index: 5;
309
311
  }
310
312
  #titlebar .app-name { color: var(--text-primary); font-weight: 600; }
311
- /* Completes the wordmark so the name never reads as a dangling fragment. */
312
- #titlebar .app-name-suffix { color: var(--text-muted); font-weight: 400; }
313
- @media (max-width: 700px) { #titlebar .app-name-suffix { display: none; } }
314
- #surfaces { display: flex; gap: 14px; }
313
+ /* Five items do not fit a phone: the nav shrinks and scrolls inside the bar rather than
314
+ being clipped or pushing the page sideways. */
315
+ #surfaces { display: flex; gap: 14px; flex: 0 1 auto; min-width: 0; overflow-x: auto; scrollbar-width: none; }
316
+ #surfaces::-webkit-scrollbar { display: none; }
317
+ @media (max-width: 700px) { #surfaces { gap: 10px; } }
315
318
  #surfaces a { color: var(--text-muted); text-decoration: none; }
316
319
  #surfaces a:hover { color: var(--text-primary); }
317
320
  #surfaces a.current { color: var(--text-primary); border-bottom: 2px solid var(--accent); }
318
- #titlebar-tagline { margin-left: auto; color: var(--text-muted); }
319
- @media (max-width: 640px) { #titlebar-tagline { display: none; } }
321
+ #titlebar-tagline { margin-left: auto; color: var(--text-muted); white-space: nowrap; }
322
+ @media (max-width: 900px) { #titlebar-tagline { display: none; } }
320
323
  #workspace { display: flex; flex: 1; align-items: flex-start; }
321
324
  #docs-nav {
322
325
  width: 250px;
@@ -0,0 +1,28 @@
1
+ import type { EntityGraph } from "./buildEntityGraph";
2
+ /**
3
+ * One openable file in a view.
4
+ *
5
+ * `segments` is the folder chain with the leaf label last. An array rather than a joined
6
+ * string because leaf labels themselves contain slashes — a catalogue entry filed under a
7
+ * vocabulary node reads `models_directory/gga.json` — and joining would invent folders.
8
+ */
9
+ export interface ExplorerViewEntry {
10
+ segments: string[];
11
+ /** Site-relative path of the file to open, e.g. "schema/models_directory/gga.json". */
12
+ path: string;
13
+ }
14
+ export interface ExplorerViews {
15
+ categories: ExplorerViewEntry[];
16
+ directories: ExplorerViewEntry[];
17
+ }
18
+ export declare function buildExplorerViews(graph: EntityGraph): ExplorerViews;
19
+ /**
20
+ * L12 — the views stay in step with the corpus.
21
+ *
22
+ * A view that silently drops a schema is worse than no view: it tells the reader the corpus
23
+ * does not contain something it does contain. Every browsable schema must appear, and every
24
+ * row must open a file that exists.
25
+ */
26
+ export declare function lintExplorerViews(graph: EntityGraph, views: ExplorerViews): string[];
27
+ /** Writes `views.json` into the given directory, creating it when missing. */
28
+ export declare function writeExplorerViews(views: ExplorerViews, outputDir: string): string;
@@ -0,0 +1,410 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.buildExplorerViews = buildExplorerViews;
7
+ exports.lintExplorerViews = lintExplorerViews;
8
+ exports.writeExplorerViews = writeExplorerViews;
9
+ /**
10
+ * Builds the Explorer's Categories and Directories trees from the entity graph.
11
+ *
12
+ * The Explorer browses by file path, which is the one arrangement that cannot show what the
13
+ * categorization layer is *for*: a CateCom tier ladder is a chain of `allOf`s, and M-CODE's
14
+ * three axes are three different ways through the same 17 recipes. Neither is a directory
15
+ * listing. These views are virtual trees over the same files — same widget, same editor, same
16
+ * tabs — so a coordinate becomes something you can walk.
17
+ *
18
+ * Built here rather than in the browser so the shape is testable and lint-able, and so the
19
+ * client stays a renderer. The output is `views.json`, written beside `graph.json`.
20
+ */
21
+ const fs_1 = __importDefault(require("fs"));
22
+ const path_1 = __importDefault(require("path"));
23
+ const MODELS_ROOT = "Models · CateCom";
24
+ const METHODS_ROOT = "Methods · CateCom";
25
+ const MATERIALS_ROOT = "Materials · M-CODE";
26
+ /** Display names for the catalogues; the entry count is appended at build time. */
27
+ const CATALOGUE_LABELS = {
28
+ properties: "Properties",
29
+ models: "Models",
30
+ methods: "Methods",
31
+ software: "Software",
32
+ "context-providers": "Context providers",
33
+ };
34
+ /** Titles are written for a schema reader; a tree row wants the noun, not the file kind. */
35
+ function shortTitle(node) {
36
+ return node.title
37
+ .replace(/\s+(category|configuration)?\s*schema$/i, "")
38
+ .replace(/\s+category$/i, "")
39
+ .trim();
40
+ }
41
+ /**
42
+ * The published file name, not the source one. They differ: a source basename may contain a
43
+ * dash, which round-trips to an underscore through `$id` (`p-norm.json` is published as
44
+ * `p_norm.json`). Labelling a row with the source name names a file the site does not serve
45
+ * and that searching for the published name cannot find.
46
+ */
47
+ function fileName(node) {
48
+ return node.publishedPath.split("/").pop();
49
+ }
50
+ function slugOf(node) {
51
+ return node.id.split("/").pop();
52
+ }
53
+ /**
54
+ * `pb · physics-based model`. The title is dropped when it adds nothing — either because it
55
+ * is the slug, or because it merely ends with it, which would otherwise print the slug twice
56
+ * (`nstruct · Unstructured meshing category nstruct`).
57
+ */
58
+ function vocabularyLabel(node) {
59
+ const slug = slugOf(node);
60
+ const title = shortTitle(node)
61
+ .replace(new RegExp(`\\s*\\b${slug}$`, "i"), "")
62
+ .trim();
63
+ return title && title.toLowerCase() !== slug.toLowerCase() ? `${slug} · ${title}` : slug;
64
+ }
65
+ function examplePath(node) {
66
+ return node.hasExample ? node.publishedPath.replace(/^schema\//, "example/") : undefined;
67
+ }
68
+ function buildContext(graph) {
69
+ const byId = new Map(graph.nodes.map((node) => [node.id, node]));
70
+ const ladderParents = new Map();
71
+ const filedUnder = new Map();
72
+ const filedEntries = new Map();
73
+ graph.edges.forEach((edge) => {
74
+ var _a, _b;
75
+ const source = byId.get(edge.source);
76
+ const target = byId.get(edge.target);
77
+ if (!source || !target)
78
+ return;
79
+ if (edge.kind === "extends" && source.layer === "category" && target.layer === "category") {
80
+ const parents = (_a = ladderParents.get(edge.source)) !== null && _a !== void 0 ? _a : [];
81
+ parents.push(edge.target);
82
+ ladderParents.set(edge.source, parents);
83
+ }
84
+ if (edge.kind === "contains" &&
85
+ edge.label === "categories" &&
86
+ source.layer === "directory" &&
87
+ target.layer === "category") {
88
+ filedUnder.set(edge.source, edge.target);
89
+ const entries = (_b = filedEntries.get(edge.target)) !== null && _b !== void 0 ? _b : [];
90
+ entries.push(edge.source);
91
+ filedEntries.set(edge.target, entries);
92
+ }
93
+ });
94
+ ladderParents.forEach((parents) => parents.sort());
95
+ filedEntries.forEach((entries) => entries.sort());
96
+ return { byId, ladderParents, filedUnder, filedEntries };
97
+ }
98
+ /**
99
+ * The chain of vocabulary schemas from the top of the ladder down to `nodeId`.
100
+ *
101
+ * This is the `allOf` chain, not the directory path: it is how the corpus itself defines the
102
+ * ladder, and it is right where the path is not. `methods_category/mathematical/regression`
103
+ * narrows a lower field than its depth suggests, so reading position off the path — or off
104
+ * which facet fields are present — misplaces it.
105
+ */
106
+ function ladderChain(nodeId, context) {
107
+ var _a;
108
+ const chain = [];
109
+ const seen = new Set();
110
+ let current = nodeId;
111
+ while (current && !seen.has(current)) {
112
+ seen.add(current);
113
+ const node = context.byId.get(current);
114
+ if (!node)
115
+ break;
116
+ chain.push(node);
117
+ [current] = (_a = context.ladderParents.get(current)) !== null && _a !== void 0 ? _a : [];
118
+ }
119
+ return chain.reverse();
120
+ }
121
+ function isEnumHolder(node) {
122
+ var _a;
123
+ return ((_a = node.facets) === null || _a === void 0 ? void 0 : _a.role) === "enum-options";
124
+ }
125
+ // ── Categories ────────────────────────────────────────────────────────────────
126
+ function categoriesView(graph, context) {
127
+ const entries = [];
128
+ const push = (segments, node) => {
129
+ entries.push({ segments: [...segments, fileName(node)], path: node.publishedPath });
130
+ const example = examplePath(node);
131
+ if (example) {
132
+ entries.push({ segments: [...segments, `${fileName(node)} · example`], path: example });
133
+ }
134
+ };
135
+ graph.nodes
136
+ .filter((node) => { var _a; return ((_a = node.facets) === null || _a === void 0 ? void 0 : _a.role) === "vocabulary"; })
137
+ .forEach((node) => {
138
+ var _a, _b;
139
+ const root = node.domain === "models_category" ? MODELS_ROOT : METHODS_ROOT;
140
+ // The branch is the first path segment but not part of the `allOf` chain, so it
141
+ // has to be prepended for a methods coordinate to be complete.
142
+ const branch = (_a = node.facets) === null || _a === void 0 ? void 0 : _a.branch;
143
+ const folders = [
144
+ root,
145
+ ...(branch ? [branch] : []),
146
+ ...ladderChain(node.id, context).map(vocabularyLabel),
147
+ ];
148
+ push(folders, node);
149
+ ((_b = context.filedEntries.get(node.id)) !== null && _b !== void 0 ? _b : []).forEach((entryId) => {
150
+ const entry = context.byId.get(entryId);
151
+ if (!entry)
152
+ return;
153
+ // Named with its domain: inside `gga · DFT GGA functional` this sits beside
154
+ // the vocabulary schema's own `gga.json`, and the two must not read alike.
155
+ const label = `${entry.domain}/${fileName(entry)}`;
156
+ entries.push({ segments: [...folders, label], path: entry.publishedPath });
157
+ const example = examplePath(entry);
158
+ if (example) {
159
+ entries.push({
160
+ segments: [...folders, `${label} · example`],
161
+ path: example,
162
+ });
163
+ }
164
+ });
165
+ });
166
+ // M-CODE's axes are orthogonal, so a recipe is filed under each of them rather than
167
+ // once: reaching a slab by dimensionality and by structural class are both legitimate.
168
+ graph.nodes
169
+ .filter((node) => { var _a; return ((_a = node.facets) === null || _a === void 0 ? void 0 : _a.role) === "recipe"; })
170
+ .forEach((node) => {
171
+ var _a;
172
+ const { structuralClass, dimensionality, operation } = (_a = node.facets) !== null && _a !== void 0 ? _a : {};
173
+ if (structuralClass && dimensionality) {
174
+ push([MATERIALS_ROOT, "by structural class", structuralClass, dimensionality], node);
175
+ push([MATERIALS_ROOT, "by dimensionality", dimensionality, structuralClass], node);
176
+ }
177
+ if (operation)
178
+ push([MATERIALS_ROOT, "by operation", operation], node);
179
+ });
180
+ graph.nodes
181
+ .filter((node) => { var _a; return ((_a = node.facets) === null || _a === void 0 ? void 0 : _a.role) === "entity"; })
182
+ .forEach((node) => {
183
+ var _a;
184
+ const { entityRole, dimensionality } = (_a = node.facets) !== null && _a !== void 0 ? _a : {};
185
+ push([
186
+ MATERIALS_ROOT,
187
+ "components",
188
+ "entities",
189
+ ...(entityRole ? [entityRole] : []),
190
+ ...(dimensionality ? [dimensionality] : []),
191
+ ], node);
192
+ });
193
+ graph.nodes
194
+ .filter((node) => { var _a; return ((_a = node.facets) === null || _a === void 0 ? void 0 : _a.role) === "operation"; })
195
+ .forEach((node) => {
196
+ var _a;
197
+ const { operationKind } = (_a = node.facets) !== null && _a !== void 0 ? _a : {};
198
+ push([
199
+ MATERIALS_ROOT,
200
+ "components",
201
+ "operations",
202
+ ...(operationKind ? [operationKind] : []),
203
+ ], node);
204
+ });
205
+ return entries;
206
+ }
207
+ // ── Directories ───────────────────────────────────────────────────────────────
208
+ /** Where a catalogue entry sits below its catalogue root. */
209
+ function directoryGroup(node, context) {
210
+ var _a, _b, _c;
211
+ const facets = (_a = node.facets) !== null && _a !== void 0 ? _a : {};
212
+ if (facets.legacy === "true")
213
+ return ["legacy"];
214
+ switch (facets.catalogue) {
215
+ case "properties":
216
+ return facets.valueShape ? [facets.valueShape] : ["uncategorized"];
217
+ case "software":
218
+ return facets.softwareKind ? [facets.softwareKind] : ["uncategorized"];
219
+ case "context-providers":
220
+ return [
221
+ ...(facets.scope ? [facets.scope] : ["uncategorized"]),
222
+ ...(facets.application ? [facets.application] : []),
223
+ ];
224
+ case "models":
225
+ case "methods": {
226
+ // The coordinate, slugs only — a position on the ladder, not a label for one.
227
+ const target = context.filedUnder.get(node.id);
228
+ if (!target)
229
+ return ["uncategorized"];
230
+ const branch = (_c = (_b = context.byId.get(target)) === null || _b === void 0 ? void 0 : _b.facets) === null || _c === void 0 ? void 0 : _c.branch;
231
+ return [...(branch ? [branch] : []), ...ladderChain(target, context).map(slugOf)];
232
+ }
233
+ default:
234
+ return ["uncategorized"];
235
+ }
236
+ }
237
+ function directoriesView(graph, context) {
238
+ const directories = graph.nodes.filter((node) => node.layer === "directory" && !isEnumHolder(node));
239
+ const counts = new Map();
240
+ directories.forEach((node) => {
241
+ var _a, _b, _c;
242
+ const catalogue = (_b = (_a = node.facets) === null || _a === void 0 ? void 0 : _a.catalogue) !== null && _b !== void 0 ? _b : "other";
243
+ counts.set(catalogue, ((_c = counts.get(catalogue)) !== null && _c !== void 0 ? _c : 0) + 1);
244
+ });
245
+ const entries = [];
246
+ directories.forEach((node) => {
247
+ var _a, _b, _c, _d;
248
+ const catalogue = (_b = (_a = node.facets) === null || _a === void 0 ? void 0 : _a.catalogue) !== null && _b !== void 0 ? _b : "other";
249
+ const label = (_c = CATALOGUE_LABELS[catalogue]) !== null && _c !== void 0 ? _c : catalogue;
250
+ const root = `${label} (${(_d = counts.get(catalogue)) !== null && _d !== void 0 ? _d : 0})`;
251
+ const folders = [root, ...directoryGroup(node, context)];
252
+ entries.push({ segments: [...folders, fileName(node)], path: node.publishedPath });
253
+ const example = examplePath(node);
254
+ if (example) {
255
+ entries.push({ segments: [...folders, `${fileName(node)} · example`], path: example });
256
+ }
257
+ });
258
+ return entries;
259
+ }
260
+ /**
261
+ * Makes every leaf label unique within its folder.
262
+ *
263
+ * File names in this corpus are often generic: all 17 M-CODE recipes are `configuration.json`
264
+ * and the recipe name is the folder above them, so grouping by facet lands four indentical
265
+ * rows side by side. Where that happens, as much of the source path is prefixed as it takes
266
+ * to tell them apart — `adatom/configuration.json`, `island/configuration.json`.
267
+ */
268
+ function disambiguateLeaves(entries) {
269
+ const groups = new Map();
270
+ entries.forEach((entry) => {
271
+ const key = entry.segments.join("\u0000");
272
+ const group = groups.get(key);
273
+ if (group)
274
+ group.push(entry);
275
+ else
276
+ groups.set(key, [entry]);
277
+ });
278
+ groups.forEach((group) => {
279
+ if (group.length < 2)
280
+ return;
281
+ const directoriesOf = (entry) => entry.path.split("/").slice(0, -1);
282
+ const deepest = Math.max(...group.map((entry) => directoriesOf(entry).length));
283
+ for (let depth = 1; depth <= deepest; depth += 1) {
284
+ const labelled = group.map((entry) => {
285
+ const leaf = entry.segments[entry.segments.length - 1];
286
+ const prefix = directoriesOf(entry).slice(-depth).join("/");
287
+ return prefix ? `${prefix}/${leaf}` : leaf;
288
+ });
289
+ if (new Set(labelled).size === group.length) {
290
+ group.forEach((entry, index) => {
291
+ entry.segments = [...entry.segments.slice(0, -1), labelled[index]];
292
+ });
293
+ return;
294
+ }
295
+ }
296
+ });
297
+ return entries;
298
+ }
299
+ function compareEntries(a, b) {
300
+ const depth = Math.min(a.segments.length, b.segments.length);
301
+ for (let i = 0; i < depth; i += 1) {
302
+ const order = a.segments[i].localeCompare(b.segments[i]);
303
+ if (order !== 0)
304
+ return order;
305
+ }
306
+ return a.segments.length - b.segments.length || a.path.localeCompare(b.path);
307
+ }
308
+ function buildExplorerViews(graph) {
309
+ const context = buildContext(graph);
310
+ return {
311
+ categories: disambiguateLeaves(categoriesView(graph, context)).sort(compareEntries),
312
+ directories: disambiguateLeaves(directoriesView(graph, context)).sort(compareEntries),
313
+ };
314
+ }
315
+ /**
316
+ * L12 — the views stay in step with the corpus.
317
+ *
318
+ * A view that silently drops a schema is worse than no view: it tells the reader the corpus
319
+ * does not contain something it does contain. Every browsable schema must appear, and every
320
+ * row must open a file that exists.
321
+ */
322
+ function lintExplorerViews(graph, views) {
323
+ const failures = [];
324
+ const files = new Set();
325
+ graph.nodes.forEach((node) => {
326
+ files.add(node.publishedPath);
327
+ const example = examplePath(node);
328
+ if (example)
329
+ files.add(example);
330
+ });
331
+ const check = (name, entries) => {
332
+ const rows = new Map();
333
+ entries.forEach((entry) => {
334
+ if (!files.has(entry.path)) {
335
+ failures.push(`L12 views.json ${name}: "${entry.path}" is not a published file`);
336
+ }
337
+ if (entry.segments.length < 2) {
338
+ failures.push(`L12 views.json ${name}: "${entry.path}" has no folder above its leaf`);
339
+ }
340
+ // The label must name the file the row opens. Checked because it is the one
341
+ // thing here NOT derivable from the inputs: everything else in this function
342
+ // rebuilds `entry.path` from the same node fields that produced it, and so
343
+ // holds by construction. Reading the source path instead of the published one
344
+ // shipped four rows labelled `p-norm.json` that open `p_norm.json`.
345
+ const leaf = entry.segments[entry.segments.length - 1]
346
+ .replace(/ · example$/, "")
347
+ .split("/")
348
+ .pop();
349
+ if (leaf !== entry.path.split("/").pop()) {
350
+ failures.push(`L12 views.json ${name}: row "${entry.segments.join(" / ")}" is labelled ` +
351
+ `"${leaf}" but opens ${entry.path}`);
352
+ }
353
+ // Two rows reading the same in the same folder are indistinguishable to a
354
+ // reader, whichever files they point at.
355
+ const row = entry.segments.join(" / ");
356
+ const claimed = rows.get(row);
357
+ if (claimed && claimed !== entry.path) {
358
+ failures.push(`L12 views.json ${name}: "${row}" is used by both ${claimed} and ${entry.path}`);
359
+ }
360
+ rows.set(row, entry.path);
361
+ });
362
+ };
363
+ check("categories", views.categories);
364
+ check("directories", views.directories);
365
+ const countBy = (entries) => {
366
+ const counts = new Map();
367
+ entries.forEach((entry) => { var _a; return counts.set(entry.path, ((_a = counts.get(entry.path)) !== null && _a !== void 0 ? _a : 0) + 1); });
368
+ return counts;
369
+ };
370
+ // Every browsable category schema, not only the vocabulary pass: the recipe, entity and
371
+ // operation passes produce 145 of the 250 rows and were policed by nothing, so a schema
372
+ // one of them silently skipped would vanish from the tree with a green build. "At least
373
+ // once" rather than "exactly once" because M-CODE files a recipe under each axis it has.
374
+ const categoryCounts = countBy(views.categories);
375
+ graph.nodes
376
+ .filter((node) => node.layer === "category" && !isEnumHolder(node))
377
+ .forEach((node) => {
378
+ var _a, _b, _c;
379
+ const seen = (_a = categoryCounts.get(node.publishedPath)) !== null && _a !== void 0 ? _a : 0;
380
+ if (seen < 1) {
381
+ failures.push(`L12 views.json categories: ${node.id} (role ` +
382
+ `${(_c = (_b = node.facets) === null || _b === void 0 ? void 0 : _b.role) !== null && _c !== void 0 ? _c : "none"}) appears nowhere in the tree`);
383
+ }
384
+ });
385
+ const directoryCounts = countBy(views.directories);
386
+ graph.nodes
387
+ .filter((node) => node.layer === "directory" && !isEnumHolder(node))
388
+ .forEach((node) => {
389
+ var _a;
390
+ const seen = (_a = directoryCounts.get(node.publishedPath)) !== null && _a !== void 0 ? _a : 0;
391
+ if (seen !== 1) {
392
+ failures.push(`L12 views.json directories: catalogue entry ${node.id} appears ${seen} ` +
393
+ `time(s), expected exactly 1`);
394
+ }
395
+ });
396
+ const holders = graph.nodes.filter(isEnumHolder).map((node) => node.publishedPath);
397
+ holders.forEach((held) => {
398
+ if (categoryCounts.has(held) || directoryCounts.has(held)) {
399
+ failures.push(`L12 views.json: enum holder ${held} should not be browsable`);
400
+ }
401
+ });
402
+ return failures;
403
+ }
404
+ /** Writes `views.json` into the given directory, creating it when missing. */
405
+ function writeExplorerViews(views, outputDir) {
406
+ fs_1.default.mkdirSync(outputDir, { recursive: true });
407
+ const outputPath = path_1.default.join(outputDir, "views.json");
408
+ fs_1.default.writeFileSync(outputPath, `${JSON.stringify(views, null, 4)}\n`, "utf8");
409
+ return outputPath;
410
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mat3ra/esse",
3
- "version": "2026.9.5-0",
3
+ "version": "2026.9.6-0",
4
4
  "description": "Mat3ra's Excellent Source of Schemas and Examples (ESSE) for Digital Materials R&D",
5
5
  "scripts": {
6
6
  "transpile": "tsc -p tsconfig-transpile.json",