@nodecraft/rendermite 1.0.1 → 1.0.2

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,69 @@
1
+ {
2
+ "parent": "block/block",
3
+ "display": {
4
+ "gui": {
5
+ "rotation": [30, 225, 0],
6
+ "translation": [2.5, -1.5, 0],
7
+ "scale": [0.625, 0.625, 0.625]
8
+ },
9
+ "fixed": {
10
+ "translation": [0, 0, -4],
11
+ "scale": [0.5, 0.5, 0.5]
12
+ },
13
+ "on_shelf": {
14
+ "rotation": [0, 180, 0],
15
+ "translation": [0, 0, 4]
16
+ },
17
+ "firstperson_righthand": {
18
+ "rotation": [0, 135, 0],
19
+ "translation": [0, -2, 0],
20
+ "scale": [0.5, 0.5, 0.5]
21
+ },
22
+ "thirdperson_righthand": {
23
+ "rotation": [75, 135, 0],
24
+ "translation": [-3, 1, 0],
25
+ "scale": [0.4, 0.4, 0.4]
26
+ }
27
+ },
28
+ "textures": {
29
+ "particle": "#particle"
30
+ },
31
+ "elements": [
32
+ {
33
+ "from": [0, 0, 13],
34
+ "to": [16, 16, 16],
35
+ "faces": {
36
+ "north": {"uv": [0, 2, 8, 6], "texture": "#all"},
37
+ "east": {"uv": [8, 0, 9.5, 8], "texture": "#all", "cullface": "east"},
38
+ "south": {"uv": [8, 0, 16, 8], "texture": "#all", "cullface": "south"},
39
+ "west": {"uv": [14.5, 0, 16, 8], "texture": "#all", "cullface": "west"},
40
+ "up": {"uv": [16, 5, 8, 3.5], "texture": "#all"},
41
+ "down": {"uv": [16, 6, 8, 4.5], "texture": "#all", "cullface": "down"}
42
+ }
43
+ },
44
+ {
45
+ "from": [0, 0, 11],
46
+ "to": [16, 4, 13],
47
+ "faces": {
48
+ "north": {"uv": [0, 6, 8, 8], "texture": "#all"},
49
+ "east": {"uv": [1.5, 6, 2.5, 8], "texture": "#all", "cullface": "east"},
50
+ "south": {"uv": [0, 0, 8, 1], "texture": "#all", "cullface": "south"},
51
+ "west": {"uv": [5.5, 6, 6.5, 8], "texture": "#all", "cullface": "west"},
52
+ "up": {"uv": [8, 3.5, 16, 4.5], "texture": "#all"},
53
+ "down": {"uv": [16, 4.5, 8, 3.5], "texture": "#all", "cullface": "down"}
54
+ }
55
+ },
56
+ {
57
+ "from": [0, 12, 11],
58
+ "to": [16, 16, 13],
59
+ "faces": {
60
+ "north": {"uv": [0, 0, 8, 2], "texture": "#all"},
61
+ "east": {"uv": [1.5, 0, 2.5, 2], "texture": "#all", "cullface": "east"},
62
+ "south": {"uv": [0, 0, 8, 1], "texture": "#all", "cullface": "south"},
63
+ "west": {"uv": [5.5, 0, 6.5, 2], "texture": "#all", "cullface": "west"},
64
+ "up": {"uv": [16, 6, 8, 5], "texture": "#all", "cullface": "up"},
65
+ "down": {"uv": [8, 5, 16, 6], "texture": "#all"}
66
+ }
67
+ }
68
+ ]
69
+ }
@@ -0,0 +1,68 @@
1
+ {
2
+ "texture_size": [64, 64],
3
+ "gui_light": "side",
4
+ "elements": [
5
+ {
6
+ "from": [0, 3, -8],
7
+ "to": [16, 9, 8],
8
+ "faces": {
9
+ "north": {"uv": [1.5, 1.5, 5.5, 0], "texture": "#texture"},
10
+ "east": {"uv": [5.5, 1.5, 7, 5.5], "rotation": 90, "texture": "#texture"},
11
+ "west": {"uv": [0, 1.5, 1.5, 5.5], "rotation": 270, "texture": "#texture"},
12
+ "up": {"uv": [1.5, 1.5, 5.5, 5.5], "texture": "#texture"},
13
+ "down": {"uv": [7, 1.5, 11, 5.5], "texture": "#texture"}
14
+ }
15
+ },
16
+ {
17
+ "from": [0, 3, 8],
18
+ "to": [16, 9, 24],
19
+ "faces": {
20
+ "east": {"uv": [5.5, 7, 7, 11], "rotation": 90, "texture": "#texture"},
21
+ "south": {"uv": [5.5, 7, 9.5, 5.5], "texture": "#texture"},
22
+ "west": {"uv": [0, 7, 1.5, 11], "rotation": 270, "texture": "#texture"},
23
+ "up": {"uv": [1.5, 7, 5.5, 11], "texture": "#texture"},
24
+ "down": {"uv": [7, 7, 11, 11], "texture": "#texture"}
25
+ }
26
+ },
27
+ {
28
+ "from": [0, 0, 21],
29
+ "to": [3, 3, 24],
30
+ "faces": {
31
+ "north": {"uv": [14.75, 2.25, 15.5, 3], "texture": "#texture"},
32
+ "east": {"uv": [14, 2.25, 14.75, 3], "texture": "#texture"},
33
+ "south": {"uv": [13.25, 2.25, 14, 3], "texture": "#texture"},
34
+ "west": {"uv": [12.5, 2.25, 13.25, 3], "texture": "#texture"},
35
+ "down": {"uv": [14, 1.5, 14.75, 2.25], "texture": "#texture"}
36
+ }
37
+ },
38
+ {
39
+ "from": [13, 0, 21],
40
+ "to": [16, 3, 24],
41
+ "faces": {
42
+ "north": {"uv": [14, 3.75, 14.75, 4.5], "texture": "#texture"},
43
+ "east": {"uv": [13.25, 3.75, 14, 4.5], "texture": "#texture"},
44
+ "south": {"uv": [12.5, 3.75, 13.25, 4.5], "texture": "#texture"},
45
+ "west": {"uv": [14.75, 3.75, 15.5, 4.5], "texture": "#texture"},
46
+ "down": {"uv": [14, 3, 14.75, 3.75], "texture": "#texture"}
47
+ }
48
+ },
49
+ {
50
+ "from": [13, 0, -8],
51
+ "to": [16, 3, -5],
52
+ "faces": {
53
+ "north": {"uv": [13.25, 5.25, 14, 6], "texture": "#texture"},
54
+ "east": {"uv": [12.5, 5.25, 13.25, 6], "texture": "#texture"},
55
+ "south": {"uv": [14.75, 5.25, 15.5, 6], "texture": "#texture"},
56
+ "west": {"uv": [14, 5.25, 14.75, 6], "texture": "#texture"},
57
+ "down": {"uv": [14, 4.5, 14.75, 5.25], "texture": "#texture"}
58
+ }
59
+ }
60
+ ],
61
+ "display": {
62
+ "gui": {
63
+ "rotation": [30, -20, 0],
64
+ "translation": [0, 1.75, 0],
65
+ "scale": [0.6, 0.59, 0.59]
66
+ }
67
+ }
68
+ }
@@ -1,6 +1,5 @@
1
1
  {
2
2
  "credit": "Made with Blockbench",
3
- "parent": "minecraft:item/template_chest",
4
3
  "texture_size": [64, 64],
5
4
  "gui_light": "side",
6
5
  "elements": [
@@ -43,5 +42,11 @@
43
42
  "down": {"uv": [0.75, 0, 1.25, 0.25], "texture": "#texture"}
44
43
  }
45
44
  }
46
- ]
45
+ ],
46
+ "display": {
47
+ "gui": {
48
+ "rotation": [30, 45, 0],
49
+ "scale": [0.625, 0.625, 0.625]
50
+ }
51
+ }
47
52
  }
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { n as builtinPackPath, t as bedCompatPath } from "./builtin-C8GZEN40.js";
3
3
  import { n as LayeredResourceManager, t as FolderResourceManager } from "./manager-dfI7b9N4.js";
4
4
  import { t as JarResourceManager } from "./jar-Bt3i0ls4.js";
5
- import { t as run } from "./run-Dr6BfyN9.js";
5
+ import { t as run } from "./run-Cp_wk49z.js";
6
6
  import { t as downloadClientJar } from "./download-D9aARBFp.js";
7
7
  import { a as prepareVersionResources, i as eraForVersion, n as GEOMETRY_DONOR_VERSIONS } from "./prepare-DROxBZp1.js";
8
8
  import path from "node:path";
@@ -1,5 +1,5 @@
1
1
  import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
2
- import { i as renderModel } from "./renderer-CmOz6WI9.js";
2
+ import { i as renderModel } from "./renderer-IC9DBUKp.js";
3
3
  //#region src/render/entity-fallback.ts
4
4
  /**
5
5
  * Heuristic rendering for mod items that parent `builtin/entity` — chests,
@@ -14,6 +14,8 @@ import { i as renderModel } from "./renderer-CmOz6WI9.js";
14
14
  const FILLER_TOKENS = /* @__PURE__ */ new Set([
15
15
  "chest",
16
16
  "chests",
17
+ "crate",
18
+ "crates",
17
19
  "trapped",
18
20
  "big",
19
21
  "small",
@@ -46,12 +48,13 @@ async function findChestTexture(resources, namespace, itemName) {
46
48
  let bestScore = 0;
47
49
  let bestNoise = Infinity;
48
50
  for (const locator of await resources.listResources("texture", namespace)) {
49
- if (!locator.path.includes("chest")) continue;
51
+ if (!locator.path.includes("chest") && !locator.path.includes("crate")) continue;
50
52
  const candidate = tokens(locator.path);
51
53
  if (candidate.some((token) => SKIP_TOKENS.has(token))) continue;
52
54
  if (wantTrapped !== (candidate.includes("trapped") || candidate.includes("trap"))) continue;
53
55
  let score = 0;
54
56
  for (const token of wanted) if (candidate.includes(token)) score++;
57
+ if (locator.path.endsWith(`/${itemName}`) || locator.path.endsWith(`/${itemName}_0`)) score++;
55
58
  if (wanted.length > 0 && score === 0) continue;
56
59
  if (best === null || score > bestScore || score === bestScore && candidate.length < bestNoise) {
57
60
  best = locator;
@@ -96,6 +99,27 @@ const DYE_COLORS = {
96
99
  black: 1908001
97
100
  };
98
101
  const DYE_PREFIX = new RegExp(`^(${Object.keys(DYE_COLORS).join("|")})_(.+)$`);
102
+ /** Find a full modded bed entity sheet, preferring every distinguishing item-name token. */
103
+ async function findBedTexture(resources, namespace, itemName) {
104
+ const wanted = tokens(itemName).filter((token) => token !== "bed");
105
+ const hasDye = wanted.some((token) => Object.hasOwn(DYE_COLORS, token));
106
+ let best = null;
107
+ let bestScore = 0;
108
+ let bestNoise = Infinity;
109
+ for (const locator of await resources.listResources("texture", namespace)) {
110
+ if (!locator.path.startsWith("entity/") || !locator.path.includes("bed")) continue;
111
+ const candidate = tokens(locator.path);
112
+ let score = hasDye ? 0 : candidate.includes("undyed") ? 1 : 0;
113
+ for (const token of wanted) if (candidate.includes(token)) score++;
114
+ if (score === 0) continue;
115
+ if (best === null || score > bestScore || score === bestScore && candidate.length < bestNoise) {
116
+ best = locator;
117
+ bestScore = score;
118
+ bestNoise = candidate.length;
119
+ }
120
+ }
121
+ return best;
122
+ }
99
123
  /**
100
124
  * Dyed variants of vanilla items whose models mods generate at runtime (quark's
101
125
  * `black_frame_empty` parents a block model that exists nowhere on disk): render
@@ -229,7 +253,7 @@ async function renderEntityFallback(id, models, textures, resources, options) {
229
253
  }
230
254
  return null;
231
255
  }
232
- if (name.includes("chest") && !name.includes("chestplate")) {
256
+ if (name.includes("chest") && !name.includes("chestplate") || name.endsWith("_crate")) {
233
257
  const variant = name.includes("ender") ? "ender" : name.includes("trapped") ? "trapped" : "normal";
234
258
  const candidates = [[`renderchest:item/chest/${variant === "normal" ? "chest" : `${variant}_chest`}`, null], ["renderchest:item/chest", `minecraft:entity/chest/${variant}`]];
235
259
  const modTexture = await findChestTexture(resources, locator.namespace, name);
@@ -244,6 +268,15 @@ async function renderEntityFallback(id, models, textures, resources, options) {
244
268
  }
245
269
  return null;
246
270
  }
271
+ if (name === "bed" || name.endsWith("_bed")) {
272
+ const modTexture = await findBedTexture(resources, locator.namespace, name);
273
+ if (modTexture) try {
274
+ const model = await models.load("renderchest:item/bed");
275
+ return await renderModel(withTexture(model, "texture", modTexture), textures, options);
276
+ } catch {
277
+ return null;
278
+ }
279
+ }
247
280
  if (name.includes("shulker_box")) try {
248
281
  const model = await models.load("renderchest:item/shulker_box");
249
282
  return await renderModel(model, textures, options);
package/dist/index.js CHANGED
@@ -5,16 +5,16 @@ import { o as createAtlasResolver, s as parseAtlasSources, t as AtlasSourceError
5
5
  import { a as builtinDynamicResources } from "./generators-CcVRG30y.js";
6
6
  import { a as defaultProperties, i as UNKNOWN_MODEL, n as InvalidItemDefinitionError, o as evaluateItemModel } from "./definition-3MBm8ar_.js";
7
7
  import { n as discoverItems, r as resolveItem, t as ItemResolutionError } from "./discovery-BzaOti7l.js";
8
- import { a as salvageJson, i as resolveTexture, n as ModelResolutionError, t as ModelLoader } from "./loader-C-eqwp3M.js";
8
+ import { a as salvageJson, i as resolveTexture, n as ModelResolutionError, t as ModelLoader } from "./loader-BR58A4Ye.js";
9
9
  import { o as toByteTexture, s as toFloatTexture } from "./blend-CHP2-j9U.js";
10
10
  import { n as encodeIcon, t as encodeAnimatedIcon } from "./encode-3u49i-eD.js";
11
11
  import { t as minifyStylesheet } from "./minify-bjMRmr5J.js";
12
12
  import { n as itemClass, t as generateStylesheet } from "./stylesheet-CovT3B2S.js";
13
13
  import { c as lightsForProfile, r as LightingProfile } from "./lighting--5myudVu.js";
14
- import { a as renderModelFrames, i as renderModel, n as boxDownscale, r as pointResize, t as ItemRenderError } from "./renderer-CmOz6WI9.js";
15
- import { a as resolveTints, i as resolveTint, n as InvalidTinterDefinitionError, r as parseTintValue, t as DYE_COLORS } from "./tints-FQlxTzUL.js";
14
+ import { a as renderModelFrames, i as renderModel, n as boxDownscale, r as pointResize, t as ItemRenderError } from "./renderer-IC9DBUKp.js";
15
+ import { a as resolveTints, i as resolveTint, n as InvalidTinterDefinitionError, r as parseTintValue, t as DYE_COLORS } from "./tints-BtbHegpI.js";
16
16
  import { n as TextureLoader, r as TextureResolutionError, t as AnimatedTexture } from "./loader-CBzn3Koc.js";
17
- import { t as run } from "./run-Dr6BfyN9.js";
17
+ import { t as run } from "./run-Cp_wk49z.js";
18
18
  import { a as prepareVersionResources, i as eraForVersion, r as compareVersions } from "./prepare-DROxBZp1.js";
19
19
  import { n as prepareModResources } from "./prepare-Bvc5ve2m.js";
20
20
  export { AnimatedTexture, AtlasSourceError, DYE_COLORS, FolderResourceManager, InvalidItemDefinitionError, InvalidResourceLocatorError, InvalidTinterDefinitionError, ItemRenderError, ItemResolutionError, JarResourceManager, LayeredResourceManager, LightingProfile, ModelLoader, ModelResolutionError, ResourceKind, ResourceLocator, TextureLoader, TextureResolutionError, UNKNOWN_MODEL, UnsafeJarError, boxDownscale, builtinDynamicResources, compareVersions, createAtlasResolver, defaultProperties, discoverItems, encodeAnimatedIcon, encodeIcon, eraForVersion, evaluateItemModel, generateStylesheet, isUnsafeEntryPath, itemClass, lightsForProfile, minifyStylesheet, parseAtlasSources, parseTintValue, pointResize, prepareModResources, prepareVersionResources, renderModel, renderModelFrames, resolveItem, resolveTexture, resolveTint, resolveTints, run, salvageJson, toByteTexture, toFloatTexture };
@@ -3,6 +3,11 @@ import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
3
3
  var ModelResolutionError = class extends Error {};
4
4
  const MAX_PARENT_DEPTH = 64;
5
5
  const MAX_TEXTURE_REF_DEPTH = 16;
6
+ /** Forge's generated-item aliases are injected by the loader, not shipped as resources. */
7
+ const MODEL_PARENT_ALIASES = {
8
+ "forge:item/default": "minecraft:item/generated",
9
+ "forge:item/default-tool": "minecraft:item/handheld"
10
+ };
6
11
  /**
7
12
  * Per-mod synthesizers for loaders whose geometry lives entirely in Java but
8
13
  * whose icon shape is simple and stable enough to rebuild from the model json.
@@ -281,7 +286,7 @@ var ModelLoader = class {
281
286
  else if (isBuiltinParent(json.parent)) {
282
287
  builtinParent = normalizeBuiltin(json.parent);
283
288
  current = null;
284
- } else current = ResourceLocator.parse(json.parent);
289
+ } else current = ResourceLocator.parse(MODEL_PARENT_ALIASES[json.parent] ?? json.parent);
285
290
  }
286
291
  const textures = {};
287
292
  const display = {};
@@ -1,2 +1,2 @@
1
- import { a as salvageJson, i as resolveTexture, n as ModelResolutionError, r as flattenLoaderModel, t as ModelLoader } from "../loader-C-eqwp3M.js";
1
+ import { a as salvageJson, i as resolveTexture, n as ModelResolutionError, r as flattenLoaderModel, t as ModelLoader } from "../loader-BR58A4Ye.js";
2
2
  export { ModelLoader, ModelResolutionError, flattenLoaderModel, resolveTexture, salvageJson };
@@ -1,2 +1,2 @@
1
- import { n as renderEntityFallback, r as spawnEggTints, t as renderDyedVariantFallback } from "../entity-fallback-CodtI6v8.js";
1
+ import { n as renderEntityFallback, r as spawnEggTints, t as renderDyedVariantFallback } from "../entity-fallback-D9iiUkkS.js";
2
2
  export { renderDyedVariantFallback, renderEntityFallback, spawnEggTints };
@@ -1,2 +1,2 @@
1
- import { c as renderObjModel, o as parseMtl, s as parseObj } from "../renderer-CmOz6WI9.js";
1
+ import { c as renderObjModel, o as parseMtl, s as parseObj } from "../renderer-IC9DBUKp.js";
2
2
  export { parseMtl, parseObj, renderObjModel };
@@ -1,2 +1,2 @@
1
- import { a as renderModelFrames, i as renderModel, n as boxDownscale, r as pointResize, t as ItemRenderError } from "../renderer-CmOz6WI9.js";
1
+ import { a as renderModelFrames, i as renderModel, n as boxDownscale, r as pointResize, t as ItemRenderError } from "../renderer-IC9DBUKp.js";
2
2
  export { ItemRenderError, boxDownscale, pointResize, renderModel, renderModelFrames };
@@ -1,2 +1,2 @@
1
- import { a as resolveTints, i as resolveTint, n as InvalidTinterDefinitionError, r as parseTintValue, t as DYE_COLORS } from "../tints-FQlxTzUL.js";
1
+ import { a as resolveTints, i as resolveTint, n as InvalidTinterDefinitionError, r as parseTintValue, t as DYE_COLORS } from "../tints-BtbHegpI.js";
2
2
  export { DYE_COLORS, InvalidTinterDefinitionError, parseTintValue, resolveTint, resolveTints };
@@ -1,6 +1,6 @@
1
1
  import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
2
2
  import { c as normalize, g as transformVector, m as sub, n as displayMatrix, o as identity, r as dot, s as multiply, t as cross } from "./vector-CCxLae_T.js";
3
- import { i as resolveTexture } from "./loader-C-eqwp3M.js";
3
+ import { i as resolveTexture } from "./loader-BR58A4Ye.js";
4
4
  import { a as tintTexture, n as cloneFloatTexture, r as darkenTexture, s as toFloatTexture, t as blendPixel } from "./blend-CHP2-j9U.js";
5
5
  import { t as combineFaceBuffers } from "./compose-cph4bfdr.js";
6
6
  import { i as phpRound, n as createFaceBuffer, r as drawTexturedTriangle, t as RowCursor } from "./rasterizer-ABgsWTYN.js";
@@ -4,17 +4,17 @@ import { t as loadAtlasResolver } from "./registry-C9E7jyzG.js";
4
4
  import { a as builtinDynamicResources } from "./generators-CcVRG30y.js";
5
5
  import { a as defaultProperties } from "./definition-3MBm8ar_.js";
6
6
  import { n as discoverItems, r as resolveItem } from "./discovery-BzaOti7l.js";
7
- import { t as ModelLoader } from "./loader-C-eqwp3M.js";
7
+ import { t as ModelLoader } from "./loader-BR58A4Ye.js";
8
8
  import { s as toFloatTexture, t as blendPixel } from "./blend-CHP2-j9U.js";
9
9
  import { r as encodeRasterAsset, t as encodeAnimatedIcon } from "./encode-3u49i-eD.js";
10
10
  import { t as classifyItemStyle } from "./item-styles-DJ3LWZS2.js";
11
11
  import { t as minifyStylesheet } from "./minify-bjMRmr5J.js";
12
12
  import { t as generateStylesheet } from "./stylesheet-CovT3B2S.js";
13
- import { a as renderModelFrames, i as renderModel, n as boxDownscale, r as pointResize } from "./renderer-CmOz6WI9.js";
14
- import { n as renderEntityFallback, r as spawnEggTints, t as renderDyedVariantFallback } from "./entity-fallback-CodtI6v8.js";
13
+ import { a as renderModelFrames, i as renderModel, n as boxDownscale, r as pointResize } from "./renderer-IC9DBUKp.js";
14
+ import { n as renderEntityFallback, r as spawnEggTints, t as renderDyedVariantFallback } from "./entity-fallback-D9iiUkkS.js";
15
15
  import { i as renderGeo, t as findGeoForItem } from "./geo-B5VqGulc.js";
16
16
  import { t as legacyTintsForChain } from "./legacy-tints-B09J0Rxh.js";
17
- import { a as resolveTints } from "./tints-FQlxTzUL.js";
17
+ import { a as resolveTints } from "./tints-BtbHegpI.js";
18
18
  import { n as TextureLoader } from "./loader-CBzn3Koc.js";
19
19
  import path from "node:path";
20
20
  import { mkdir, writeFile } from "node:fs/promises";
package/dist/run.js CHANGED
@@ -1,3 +1,3 @@
1
1
  import { o as evaluateItemModel } from "./definition-3MBm8ar_.js";
2
- import { t as run } from "./run-Dr6BfyN9.js";
2
+ import { t as run } from "./run-Cp_wk49z.js";
3
3
  export { evaluateItemModel, run };
@@ -76,7 +76,7 @@ async function resolveTint(definition, context) {
76
76
  return hexColor(DYE_COLORS[index % DYE_COLORS.length]);
77
77
  }
78
78
  default:
79
- if (DEFAULT_READING_TYPES.has(type)) return parseTintValue(definition.default);
79
+ if (DEFAULT_READING_TYPES.has(type) || Object.hasOwn(definition, "default")) return parseTintValue(definition.default);
80
80
  throw new InvalidTinterDefinitionError(`Unknown tint type: ${definition.type}`);
81
81
  }
82
82
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodecraft/rendermite",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Minecraft item icon renderer: renders inventory-identical item icons from vanilla and modded assets across game versions, outputting optimized PNG/WebP/AVIF plus CSS and a JSON item library.",
5
5
  "type": "module",
6
6
  "license": "MIT",