@nodecraft/rendermite 1.0.5 → 1.0.7

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.
Files changed (112) hide show
  1. package/README.md +5 -0
  2. package/dist/atlas/registry.d.ts +3 -3
  3. package/dist/atlas/sources.d.ts +1 -1
  4. package/dist/{blend-Qvrn6YgK.d.ts → blend-CEHoJ7tM.d.ts} +3 -2
  5. package/dist/{blend-CHP2-j9U.js → blend-D0XWgVQr.js} +20 -8
  6. package/dist/cache-Cr2ON3RQ.js +66 -0
  7. package/dist/cli.js +12 -6
  8. package/dist/compose-lWPrB7wh.js +104 -0
  9. package/dist/descriptor-DWebtkI1.js +26 -0
  10. package/dist/{discovery-D89pVVJA.d.ts → discovery-wiJBGryo.d.ts} +1 -1
  11. package/dist/dynamic/generators.d.ts +1 -1
  12. package/dist/dynamic/generators.js +1 -1
  13. package/dist/{encode-C5kdAm45.d.ts → encode-CaSK8-mi.d.ts} +1 -1
  14. package/dist/{encode-3u49i-eD.js → encode-Df_tWzkl.js} +44 -16
  15. package/dist/{entity-fallback-DfL0_eyf.js → entity-fallback-D9QTGU9X.js} +1 -1
  16. package/dist/{face-CpVdnf-w.d.ts → face-C49L66sQ.d.ts} +10 -4
  17. package/dist/{face-D-N1amV4.js → face-Cl1O4m2i.js} +24 -11
  18. package/dist/{generators-CcVRG30y.js → generators-BlG4KmtP.js} +1 -1
  19. package/dist/{generators-CCB8h0mP.d.ts → generators-DhRov4RB.d.ts} +1 -1
  20. package/dist/{geo-Dq2zve09.js → geo-DHiWBa-C.js} +7 -4
  21. package/dist/index.d.ts +14 -14
  22. package/dist/index.js +12 -12
  23. package/dist/{item-styles-DJ3LWZS2.js → item-styles-upgEO1yP.js} +1 -1
  24. package/dist/items/discovery.d.ts +1 -1
  25. package/dist/{jar-6T3xdgaL.d.ts → jar-CFkA-VgT.d.ts} +2 -1
  26. package/dist/{jar-Bt3i0ls4.js → jar-D9z-X7tA.js} +12 -12
  27. package/dist/{legacy-reshape-CewShfF3.js → legacy-reshape-C05SfAPR.js} +1 -1
  28. package/dist/{loader-A-BZa8J4.js → loader-B8NJO7pR.js} +12 -3
  29. package/dist/{loader-CBzn3Koc.js → loader-CfXMxcEV.js} +37 -7
  30. package/dist/{loader-C-6QN3xv.d.ts → loader-D22zZME7.d.ts} +2 -2
  31. package/dist/{loader-TFFNV4Jl.d.ts → loader-Myy22TDL.d.ts} +8 -2
  32. package/dist/{manager-dfI7b9N4.js → manager-CkletR5K.js} +29 -2
  33. package/dist/{manager-CoZaeQTu.d.ts → manager-Cv1NeFyF.d.ts} +28 -1
  34. package/dist/models/loader.d.ts +1 -1
  35. package/dist/models/loader.js +1 -1
  36. package/dist/mods/prepare.d.ts +1 -1
  37. package/dist/mods/prepare.js +1 -1
  38. package/dist/mods/synthesize.d.ts +2 -2
  39. package/dist/mods/synthesize.js +1 -1
  40. package/dist/mods/tints.d.ts +2 -2
  41. package/dist/mods/tints.js +1 -1
  42. package/dist/output/encode.d.ts +1 -1
  43. package/dist/output/encode.js +1 -1
  44. package/dist/output/item-styles.js +1 -1
  45. package/dist/pipeline-Da2Kavba.js +221 -0
  46. package/dist/pipeline-HiA3DC71.d.ts +58 -0
  47. package/dist/pool-CKGp1qup.js +131 -0
  48. package/dist/{prepare-BeUpXwaP.js → prepare-BESRrh6K.js} +4 -4
  49. package/dist/{prepare-gfR1aIjt.d.ts → prepare-CPxO0kKz.d.ts} +1 -1
  50. package/dist/{prepare-C8wRjAXP.js → prepare-Cc8QZ3j0.js} +4 -4
  51. package/dist/{prepare-wfWtaz_X.d.ts → prepare-Cs6QDqie.d.ts} +1 -1
  52. package/dist/{rasterizer-BHWDrmGz.js → rasterizer-76qO8TDh.js} +42 -4
  53. package/dist/{rasterizer-Dh35qXbn.d.ts → rasterizer-B9ZGJI3-.d.ts} +21 -2
  54. package/dist/render/blend.d.ts +1 -1
  55. package/dist/render/blend.js +1 -1
  56. package/dist/render/compose.d.ts +2 -2
  57. package/dist/render/compose.js +1 -1
  58. package/dist/render/entity-fallback.d.ts +6 -6
  59. package/dist/render/entity-fallback.js +1 -1
  60. package/dist/render/face.d.ts +2 -2
  61. package/dist/render/face.js +1 -1
  62. package/dist/render/fluid-bucket.d.ts +2 -2
  63. package/dist/render/geo.d.ts +4 -4
  64. package/dist/render/geo.js +1 -1
  65. package/dist/render/legacy-tints.d.ts +2 -2
  66. package/dist/render/obj.d.ts +5 -5
  67. package/dist/render/obj.js +1 -1
  68. package/dist/render/pipeline.d.ts +2 -0
  69. package/dist/render/pipeline.js +2 -0
  70. package/dist/render/rasterizer.d.ts +2 -2
  71. package/dist/render/rasterizer.js +2 -2
  72. package/dist/render/renderer.d.ts +2 -2
  73. package/dist/render/renderer.js +1 -1
  74. package/dist/render/tints.d.ts +1 -1
  75. package/dist/{renderer-nbMCw1Pl.d.ts → renderer-B20ssjRm.d.ts} +23 -6
  76. package/dist/{renderer-LDhBNHcl.js → renderer-gQW_I7YG.js} +146 -63
  77. package/dist/resources/cache.d.ts +30 -0
  78. package/dist/resources/cache.js +2 -0
  79. package/dist/resources/descriptor.d.ts +2 -0
  80. package/dist/resources/descriptor.js +2 -0
  81. package/dist/resources/jar.d.ts +1 -1
  82. package/dist/resources/jar.js +1 -1
  83. package/dist/resources/manager.d.ts +1 -1
  84. package/dist/resources/manager.js +1 -1
  85. package/dist/resources/synthetic.d.ts +1 -1
  86. package/dist/resources/synthetic.js +1 -1
  87. package/dist/resources/zip-reader.d.ts +5 -0
  88. package/dist/resources/zip-reader.js +2 -0
  89. package/dist/run-B0qPw3zf.js +209 -0
  90. package/dist/{run-BlSZw9Qk.d.ts → run-CT3VtFqg.d.ts} +12 -2
  91. package/dist/run.d.ts +1 -1
  92. package/dist/run.js +1 -1
  93. package/dist/{sources-DH9dk3KF.d.ts → sources-B9X9w3qP.d.ts} +1 -1
  94. package/dist/{synthesize-CRgSEmEp.js → synthesize-3Q0aaOnK.js} +1 -1
  95. package/dist/{synthetic-CDsj--li.d.ts → synthetic-Qk-BSUXO.d.ts} +5 -1
  96. package/dist/{synthetic-DC2bSnGe.js → synthetic-ZAUjXT7E.js} +0 -0
  97. package/dist/textures/loader.d.ts +1 -1
  98. package/dist/textures/loader.js +1 -1
  99. package/dist/{tints-CuG6GwQJ.js → tints-B8UJmZMu.js} +2 -2
  100. package/dist/{tints-txt-8xI5.d.ts → tints-BH9l0ND0.d.ts} +2 -2
  101. package/dist/versions/legacy-reshape.d.ts +1 -1
  102. package/dist/versions/legacy-reshape.js +1 -1
  103. package/dist/versions/prepare.d.ts +1 -1
  104. package/dist/versions/prepare.js +1 -1
  105. package/dist/workers/pool.d.ts +36 -5
  106. package/dist/workers/pool.js +2 -67
  107. package/dist/workers/render-worker.d.ts +15 -0
  108. package/dist/workers/render-worker.js +40 -0
  109. package/dist/zip-reader-BSbJKUT1.js +156 -0
  110. package/package.json +4 -2
  111. package/dist/compose-qjhyBvnS.js +0 -41
  112. package/dist/run-CfnWIRY4.js +0 -316
@@ -0,0 +1,209 @@
1
+ import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
2
+ import { n as discoverItems } from "./discovery-0SM0cTLw.js";
3
+ import { r as encodeRasterAsset } from "./encode-Df_tWzkl.js";
4
+ import { t as classifyItemStyle } from "./item-styles-upgEO1yP.js";
5
+ import { t as minifyStylesheet } from "./minify-bjMRmr5J.js";
6
+ import { t as generateStylesheet } from "./stylesheet-CovT3B2S.js";
7
+ import { t as ItemPipeline } from "./pipeline-Da2Kavba.js";
8
+ import { t as describeResources } from "./descriptor-DWebtkI1.js";
9
+ import { t as WorkerRenderPool } from "./pool-CKGp1qup.js";
10
+ import path from "node:path";
11
+ import os from "node:os";
12
+ import { mkdir, writeFile } from "node:fs/promises";
13
+ import { fileURLToPath } from "node:url";
14
+ //#region src/run.ts
15
+ /**
16
+ * Render-run orchestration: discover items, evaluate definitions, render,
17
+ * encode, write images + stylesheet + item list. Unlike upstream, failures are
18
+ * per-item, collected, and reported — a run summary counts files, never log lines.
19
+ *
20
+ * Items render in worker threads when the resource tree can describe itself
21
+ * (see resources/descriptor.ts); otherwise the same pipeline runs in-process.
22
+ */
23
+ async function run(options) {
24
+ const namespaces = options.namespaces ?? ["minecraft"];
25
+ const size = options.size ?? 64;
26
+ const quality = options.quality ?? 2;
27
+ const prefix = options.prefix ?? "rc-";
28
+ const format = options.format ?? "webp";
29
+ const fallback = options.fallback ?? true;
30
+ const lightingProfile = options.lightingProfile ?? "modern";
31
+ const settings = {
32
+ builtin: options.builtin,
33
+ output: options.output,
34
+ size,
35
+ quality,
36
+ format,
37
+ gif: options.gif,
38
+ avif: options.avif,
39
+ lightingProfile,
40
+ animation: options.animation
41
+ };
42
+ const concurrency = Math.max(1, options.concurrency ?? 8);
43
+ const requestedWorkers = resolveWorkerCount(options.workers);
44
+ const descriptor = requestedWorkers > 1 ? describeResources(options.resources) : null;
45
+ const pool = descriptor ? new WorkerRenderPool({
46
+ workerUrl: workerUrl(),
47
+ workerData: {
48
+ resources: descriptor,
49
+ settings,
50
+ sharpConcurrency: Math.max(1, Math.round(os.availableParallelism() / requestedWorkers))
51
+ },
52
+ workers: requestedWorkers,
53
+ inFlight: concurrency
54
+ }) : null;
55
+ const usedWorkers = pool ? requestedWorkers : 1;
56
+ const pipeline = await ItemPipeline.create(options.resources, settings).catch(async (err) => {
57
+ await pool?.close();
58
+ throw err;
59
+ });
60
+ const { resources } = pipeline;
61
+ const glint = await resolveEnchantmentGlint(resources);
62
+ if (!glint) throw new Error("Missing enchantment glint texture (minecraft:misc/enchanted_glint_item or enchanted_item_glint)");
63
+ const encodedGlint = await encodeRasterAsset(glint, fallback ? ["png", format] : [format]);
64
+ const ids = [];
65
+ const seen = /* @__PURE__ */ new Set();
66
+ if (options.builtin) for (const placeholder of ["renderchest:unknown", "renderchest:empty"]) {
67
+ seen.add(placeholder);
68
+ ids.push(placeholder);
69
+ }
70
+ for (const namespace of namespaces) for (const item of await discoverItems(resources, namespace, { filterUntranslatedModels: options.filterUntranslatedModels })) {
71
+ if (seen.has(item.id)) continue;
72
+ if (options.builtin && !await itemExistsIn(options.resources, item.id)) continue;
73
+ seen.add(item.id);
74
+ ids.push(item.id);
75
+ }
76
+ if (pipeline.dynamicItems && namespaces.includes("minecraft")) {
77
+ const hasTrims = await resources.hasResource("texture", ResourceLocator.parse("minecraft:trims/color_palettes/trim_palette"));
78
+ const groupAvailable = {
79
+ _armor_trims: hasTrims,
80
+ _leather_armor: hasTrims,
81
+ _decorated_pot: await resources.hasResource("texture", ResourceLocator.parse("minecraft:entity/decorated_pot/decorated_pot_base")),
82
+ _crossbow: await resources.hasResource("model", ResourceLocator.parse("minecraft:item/crossbow_arrow"))
83
+ };
84
+ for (const id of pipeline.dynamicItems) if ((groupAvailable[id.split(":", 1)[0]] ?? true) && !seen.has(id)) {
85
+ seen.add(id);
86
+ ids.push(id);
87
+ }
88
+ }
89
+ const rendered = [];
90
+ const failed = [];
91
+ const spriteFallbacks = [];
92
+ const skippedTemplates = [];
93
+ const styles = [];
94
+ let writtenFiles = 0;
95
+ await mkdir(options.output, { recursive: true });
96
+ for (const [extension, buffer] of encodedGlint) {
97
+ await writeFile(path.join(options.output, `${prefix}enchantment.${extension}`), buffer);
98
+ writtenFiles++;
99
+ }
100
+ let templateParents = null;
101
+ const loadTemplateParents = async () => {
102
+ const parents = /* @__PURE__ */ new Set();
103
+ for (const namespace of namespaces) for (const modelLocator of await resources.listResources("model", namespace)) {
104
+ if (!modelLocator.path.startsWith("item/")) continue;
105
+ const raw = await resources.getResource("model", modelLocator);
106
+ if (!raw) continue;
107
+ try {
108
+ const parent = JSON.parse(raw.toString("utf8")).parent;
109
+ if (typeof parent === "string") parents.add(ResourceLocator.parse(parent).toString());
110
+ } catch {}
111
+ }
112
+ return parents;
113
+ };
114
+ const isTemplateModel = async (id) => {
115
+ const locator = ResourceLocator.parse(id);
116
+ if (await resources.hasResource("itemDefinition", locator)) return false;
117
+ templateParents ??= loadTemplateParents();
118
+ return (await templateParents).has(new ResourceLocator(locator.namespace, `item/${locator.path}`).toString());
119
+ };
120
+ let completed = 0;
121
+ const record = async (id, outcome, error) => {
122
+ if (outcome) {
123
+ writtenFiles += outcome.written;
124
+ rendered.push(id);
125
+ if (outcome.usedFallback) spriteFallbacks.push(id);
126
+ } else if (await isTemplateModel(id).catch(() => false)) skippedTemplates.push(id);
127
+ else failed.push({
128
+ id,
129
+ error: error ?? "Unknown render failure"
130
+ });
131
+ completed++;
132
+ options.onProgress?.(completed, ids.length, id, outcome ? void 0 : error);
133
+ };
134
+ if (pool) {
135
+ const pending = [];
136
+ await pool.run(ids.map((id) => {
137
+ return { id };
138
+ }), (message) => {
139
+ pending.push(record(message.id, message.result ?? null, message.error));
140
+ });
141
+ await Promise.all(pending);
142
+ } else {
143
+ let nextIndex = 0;
144
+ await Promise.all(Array.from({ length: Math.min(concurrency, ids.length) }, async () => {
145
+ while (nextIndex < ids.length) {
146
+ const id = ids[nextIndex++];
147
+ try {
148
+ await record(id, await pipeline.renderItem(id));
149
+ } catch (err) {
150
+ await record(id, null, err.message);
151
+ }
152
+ }
153
+ }));
154
+ }
155
+ rendered.sort((left, right) => left.localeCompare(right));
156
+ const renderedSet = new Set(rendered);
157
+ for (const id of rendered) {
158
+ const style = classifyItemStyle(id, renderedSet);
159
+ if (style) styles.push(style);
160
+ }
161
+ const stylesheet = generateStylesheet(styles, {
162
+ prefix,
163
+ format,
164
+ fallback
165
+ });
166
+ const finalStylesheet = options.minify === false ? stylesheet : minifyStylesheet(stylesheet, (message) => options.onProgress?.(0, 0, "stylesheet", message));
167
+ await writeFile(path.join(options.output, `${prefix}${namespaces[0]}-${size}.css`), finalStylesheet);
168
+ if (options.itemList !== false) {
169
+ await writeFile(path.join(options.output, `${prefix}${namespaces[0]}.json`), JSON.stringify(rendered));
170
+ await writeFile(path.join(options.output, `${prefix}${namespaces[0]}-failures.json`), JSON.stringify(failed, null, " "));
171
+ }
172
+ return {
173
+ rendered,
174
+ failed,
175
+ spriteFallbacks,
176
+ skippedTemplates,
177
+ writtenFiles,
178
+ workers: usedWorkers
179
+ };
180
+ }
181
+ /**
182
+ * One thread per core less the orchestrator, capped at 8: measured throughput
183
+ * on a 16-thread host peaks around 4-8 workers and falls off above that, where
184
+ * per-worker asset caches and libvips threads start competing for the same cores.
185
+ */
186
+ function resolveWorkerCount(requested) {
187
+ if (requested !== void 0) return Math.max(1, Math.floor(requested));
188
+ return Math.min(8, Math.max(1, os.availableParallelism() - 1));
189
+ }
190
+ /** The worker's own file, in whichever form this module was loaded from. */
191
+ function workerUrl() {
192
+ const extension = path.extname(fileURLToPath(import.meta.url)) || ".js";
193
+ return new URL(`./workers/render-worker${extension}`, import.meta.url);
194
+ }
195
+ async function itemExistsIn(resources, id) {
196
+ const locator = ResourceLocator.parse(id);
197
+ if (await resources.hasResource("itemDefinition", locator)) return true;
198
+ return resources.hasResource("model", new ResourceLocator(locator.namespace, `item/${locator.path}`));
199
+ }
200
+ /** Resolve both names used on either side of Minecraft 1.19.3. */
201
+ async function resolveEnchantmentGlint(resources) {
202
+ for (const path of ["misc/enchanted_glint_item", "misc/enchanted_item_glint"]) {
203
+ const texture = await resources.getResource("texture", new ResourceLocator("minecraft", path));
204
+ if (texture) return texture;
205
+ }
206
+ return null;
207
+ }
208
+ //#endregion
209
+ export { run as t };
@@ -1,5 +1,6 @@
1
- import { a as ResourceManager } from "./manager-CoZaeQTu.js";
1
+ import { a as ResourceManager } from "./manager-Cv1NeFyF.js";
2
2
  import { a as LightingProfileName } from "./lighting-3FGRC3Af.js";
3
+ import { n as AnimationLimits } from "./renderer-B20ssjRm.js";
3
4
  //#region src/run.d.ts
4
5
  interface RunOptions {
5
6
  resources: ResourceManager;
@@ -22,8 +23,15 @@ interface RunOptions {
22
23
  avif?: boolean;
23
24
  /** Minify the stylesheet via lightningcss (default true). */
24
25
  minify?: boolean;
25
- /** In-flight items (encode overlaps on libvips' threadpool; default 8). */
26
+ /** In-flight items per thread (encode overlaps on libvips' threadpool; default 8). */
26
27
  concurrency?: number;
28
+ /**
29
+ * Render threads. Defaults to one per core (minus the main thread). 1 renders
30
+ * in-process; a resource tree that cannot be described falls back to that too.
31
+ */
32
+ workers?: number;
33
+ /** Bounds on animated timelines (frame count). */
34
+ animation?: AnimationLimits;
27
35
  /**
28
36
  * Drop classic item models with no matching translation (default true).
29
37
  * Disable for a prepared asset tree whose own preprocessing has already
@@ -54,6 +62,8 @@ interface RunResult {
54
62
  skippedTemplates: string[];
55
63
  /** Image files actually written, the only trustworthy success signal. */
56
64
  writtenFiles: number;
65
+ /** Worker threads used; 1 means everything rendered in-process. */
66
+ workers: number;
57
67
  }
58
68
  declare function run(options: RunOptions): Promise<RunResult>;
59
69
  //#endregion
package/dist/run.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import { l as evaluateItemModel } from "./definition-BwX6Xxv0.js";
2
- import { i as run, n as RunOptions, r as RunResult, t as ItemFailure } from "./run-BlSZw9Qk.js";
2
+ import { i as run, n as RunOptions, r as RunResult, t as ItemFailure } from "./run-CT3VtFqg.js";
3
3
  export { ItemFailure, RunOptions, RunResult, evaluateItemModel, run };
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-CfnWIRY4.js";
2
+ import { t as run } from "./run-B0qPw3zf.js";
3
3
  export { evaluateItemModel, run };
@@ -1,5 +1,5 @@
1
1
  import { n as ResourceLocator } from "./locator-jDbdUeTd.js";
2
- import { r as Texture } from "./loader-TFFNV4Jl.js";
2
+ import { r as Texture } from "./loader-Myy22TDL.js";
3
3
  //#region src/atlas/sources.d.ts
4
4
  declare class AtlasSourceError extends Error {}
5
5
  interface AtlasSource {
@@ -1,5 +1,5 @@
1
1
  import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
2
- import { n as MapResourceManager } from "./synthetic-DC2bSnGe.js";
2
+ import { n as MapResourceManager } from "./synthetic-ZAUjXT7E.js";
3
3
  //#region src/mods/synthesize.ts
4
4
  /**
5
5
  * Model synthesis for mods that ship textures without item models, ported from
@@ -1,5 +1,5 @@
1
1
  import { n as ResourceLocator } from "./locator-jDbdUeTd.js";
2
- import { a as ResourceManager, i as ResourceKindName } from "./manager-CoZaeQTu.js";
2
+ import { a as ResourceManager, i as ResourceKindName, s as ResourceDescriptor } from "./manager-Cv1NeFyF.js";
3
3
  //#region src/resources/synthetic.d.ts
4
4
  /**
5
5
  * In-memory resources, used for era shims (synthesized `models/item/generated.json`,
@@ -7,6 +7,8 @@ import { a as ResourceManager, i as ResourceKindName } from "./manager-CoZaeQTu.
7
7
  * mutating asset trees on disk like the old pipeline did.
8
8
  */
9
9
  declare class MapResourceManager implements ResourceManager {
10
+ /** Rebuild from a describe() payload (worker side). */
11
+ static fromEntries(entries: Iterable<[string, Uint8Array]>): MapResourceManager;
10
12
  private readonly entries;
11
13
  set(kind: ResourceKindName, locator: string | ResourceLocator, content: Buffer | string): this;
12
14
  getResource(kind: ResourceKindName, locator: ResourceLocator): Promise<Buffer | null>;
@@ -14,6 +16,7 @@ declare class MapResourceManager implements ResourceManager {
14
16
  getAllResources(kind: ResourceKindName, locator: ResourceLocator): Promise<Buffer[]>;
15
17
  listResources(kind: ResourceKindName, namespace: string): Promise<ResourceLocator[]>;
16
18
  listNamespaces(): Promise<string[]>;
19
+ describe(): ResourceDescriptor;
17
20
  }
18
21
  /**
19
22
  * Restricts a manager to specific resource kinds — donor versions may only
@@ -29,6 +32,7 @@ declare class KindFilteredResourceManager implements ResourceManager {
29
32
  getAllResources(kind: ResourceKindName, locator: ResourceLocator): Promise<Buffer[]>;
30
33
  listResources(kind: ResourceKindName, namespace: string): Promise<ResourceLocator[]>;
31
34
  listNamespaces(): Promise<string[]>;
35
+ describe(): ResourceDescriptor | null;
32
36
  }
33
37
  //#endregion
34
38
  export { MapResourceManager as n, KindFilteredResourceManager as t };
@@ -1,2 +1,2 @@
1
- import { a as TextureMeta, i as TextureLoader, n as AtlasResolverLike, o as TextureResolutionError, r as Texture, t as AnimatedTexture } from "../loader-TFFNV4Jl.js";
1
+ import { a as TextureMeta, i as TextureLoader, n as AtlasResolverLike, o as TextureResolutionError, r as Texture, t as AnimatedTexture } from "../loader-Myy22TDL.js";
2
2
  export { AnimatedTexture, AtlasResolverLike, Texture, TextureLoader, TextureMeta, TextureResolutionError };
@@ -1,2 +1,2 @@
1
- import { n as TextureLoader, r as TextureResolutionError, t as AnimatedTexture } from "../loader-CBzn3Koc.js";
1
+ import { n as TextureLoader, r as TextureResolutionError, t as AnimatedTexture } from "../loader-CfXMxcEV.js";
2
2
  export { AnimatedTexture, TextureLoader, TextureResolutionError };
@@ -1,8 +1,8 @@
1
1
  import { r as modTintPackPath } from "./builtin-caYjESh0.js";
2
2
  import { a as defaultProperties, o as evaluateItemModel } from "./definition-3MBm8ar_.js";
3
3
  import { n as ResourceLocator } from "./locator-ycZzh_6z.js";
4
- import { t as FolderResourceManager } from "./manager-dfI7b9N4.js";
5
- import { n as MapResourceManager } from "./synthetic-DC2bSnGe.js";
4
+ import { t as FolderResourceManager } from "./manager-CkletR5K.js";
5
+ import { n as MapResourceManager } from "./synthetic-ZAUjXT7E.js";
6
6
  //#region src/mods/tints.ts
7
7
  /**
8
8
  * Tints for mod items whose colors live in client code rather than asset JSON.
@@ -1,5 +1,5 @@
1
- import { i as TextureLoader } from "./loader-TFFNV4Jl.js";
2
- import { n as TintColor } from "./face-CpVdnf-w.js";
1
+ import { i as TextureLoader } from "./loader-Myy22TDL.js";
2
+ import { r as TintColor } from "./face-C49L66sQ.js";
3
3
  import { o as TintDefinition } from "./definition-BwX6Xxv0.js";
4
4
  //#region src/render/tints.d.ts
5
5
  declare class InvalidTinterDefinitionError extends Error {}
@@ -1,4 +1,4 @@
1
- import { a as ResourceManager } from "../manager-CoZaeQTu.js";
1
+ import { a as ResourceManager } from "../manager-Cv1NeFyF.js";
2
2
  //#region src/versions/legacy-reshape.d.ts
3
3
  /**
4
4
  * Build an in-memory texture layer for a pre-1.6 jar. `sheets` selects the
@@ -1,2 +1,2 @@
1
- import { t as reshapeLegacyAssets } from "../legacy-reshape-CewShfF3.js";
1
+ import { t as reshapeLegacyAssets } from "../legacy-reshape-C05SfAPR.js";
2
2
  export { reshapeLegacyAssets };
@@ -1,2 +1,2 @@
1
- import { a as compareVersions, i as PrepareOptions, n as EraName, o as eraForVersion, r as GEOMETRY_DONOR_VERSIONS, s as prepareVersionResources, t as Era } from "../prepare-wfWtaz_X.js";
1
+ import { a as compareVersions, i as PrepareOptions, n as EraName, o as eraForVersion, r as GEOMETRY_DONOR_VERSIONS, s as prepareVersionResources, t as Era } from "../prepare-Cs6QDqie.js";
2
2
  export { Era, EraName, GEOMETRY_DONOR_VERSIONS, PrepareOptions, compareVersions, eraForVersion, prepareVersionResources };
@@ -1,2 +1,2 @@
1
- import { a as prepareVersionResources, i as eraForVersion, n as GEOMETRY_DONOR_VERSIONS, r as compareVersions, t as Era } from "../prepare-C8wRjAXP.js";
1
+ import { a as prepareVersionResources, i as eraForVersion, n as GEOMETRY_DONOR_VERSIONS, r as compareVersions, t as Era } from "../prepare-Cc8QZ3j0.js";
2
2
  export { Era, GEOMETRY_DONOR_VERSIONS, compareVersions, eraForVersion, prepareVersionResources };
@@ -2,8 +2,14 @@
2
2
  /**
3
3
  * Worker-thread pool for parallel item rendering. Design goals born from the
4
4
  * PHP tool's failure modes (docs/research/mod-failure-taxonomy.md): errors
5
- * cross the worker boundary as structured data, a dead worker's queue items are
6
- * requeued not orphaned, and results stream back so output lands incrementally.
5
+ * cross the worker boundary as structured data, a dead worker's in-flight items
6
+ * are reported rather than orphaned, and results stream back so output lands
7
+ * incrementally.
8
+ *
9
+ * Workers spawn as soon as the pool is constructed, so their asset indexing
10
+ * overlaps the caller's own discovery pass; each holds several tasks at once so
11
+ * native encoding overlaps the next item's rasterization; and the queue drains
12
+ * through a cursor, because shift() on a 400k-item array is quadratic.
7
13
  */
8
14
  interface WorkerTask {
9
15
  id: string;
@@ -16,8 +22,33 @@ interface WorkerResult<T> {
16
22
  interface PoolOptions {
17
23
  workerUrl: URL;
18
24
  workerData: unknown;
19
- concurrency: number;
25
+ /** Worker threads to spawn. */
26
+ workers: number;
27
+ /** Tasks held by each worker at once (default 2). */
28
+ inFlight?: number;
29
+ }
30
+ declare class WorkerRenderPool<T> {
31
+ private readonly options;
32
+ private readonly inFlightLimit;
33
+ private readonly workers;
34
+ private tasks;
35
+ private cursor;
36
+ private started;
37
+ private onResult;
38
+ private settle;
39
+ private settled;
40
+ /** Workers dying before completing anything mean a bad config, not bad luck. */
41
+ private startupFailures;
42
+ constructor(options: PoolOptions);
43
+ private spawn;
44
+ private pump;
45
+ private retire;
46
+ private fail;
47
+ private finish;
48
+ private abort;
49
+ /** Run every task, calling back as results arrive. Terminates the pool after. */
50
+ run(tasks: WorkerTask[], onResult: (result: WorkerResult<T>) => void): Promise<void>;
51
+ close(): Promise<void>;
20
52
  }
21
- declare function runPool<T>(tasks: WorkerTask[], options: PoolOptions, onResult: (result: WorkerResult<T>) => void): Promise<void>;
22
53
  //#endregion
23
- export { PoolOptions, WorkerResult, WorkerTask, runPool };
54
+ export { PoolOptions, WorkerRenderPool, WorkerResult, WorkerTask };
@@ -1,67 +1,2 @@
1
- import { Worker } from "node:worker_threads";
2
- //#region src/workers/pool.ts
3
- /**
4
- * Worker-thread pool for parallel item rendering. Design goals born from the
5
- * PHP tool's failure modes (docs/research/mod-failure-taxonomy.md): errors
6
- * cross the worker boundary as structured data, a dead worker's queue items are
7
- * requeued not orphaned, and results stream back so output lands incrementally.
8
- */
9
- async function runPool(tasks, options, onResult) {
10
- const queue = [...tasks];
11
- const inFlight = /* @__PURE__ */ new Map();
12
- await new Promise((resolve) => {
13
- let active = 0;
14
- const spawn = () => {
15
- const worker = new Worker(options.workerUrl, { workerData: options.workerData });
16
- active++;
17
- const next = () => {
18
- const task = queue.shift();
19
- if (task === void 0) {
20
- worker.terminate().catch(() => {});
21
- active--;
22
- if (active === 0) resolve();
23
- return;
24
- }
25
- inFlight.set(worker, task);
26
- worker.postMessage(task);
27
- };
28
- worker.on("message", (message) => {
29
- inFlight.delete(worker);
30
- onResult(message);
31
- next();
32
- });
33
- worker.on("error", (err) => {
34
- const task = inFlight.get(worker);
35
- inFlight.delete(worker);
36
- if (task) onResult({
37
- id: task.id,
38
- error: `Worker crashed: ${err.message}`
39
- });
40
- active--;
41
- if (queue.length > 0) spawn();
42
- else if (active === 0) resolve();
43
- });
44
- worker.on("exit", (code) => {
45
- const task = inFlight.get(worker);
46
- if (task && code !== 0) {
47
- inFlight.delete(worker);
48
- onResult({
49
- id: task.id,
50
- error: `Worker exited with code ${code}`
51
- });
52
- active--;
53
- if (queue.length > 0) spawn();
54
- else if (active === 0) resolve();
55
- }
56
- });
57
- next();
58
- };
59
- if (queue.length === 0) {
60
- resolve();
61
- return;
62
- }
63
- for (let i = 0; i < Math.min(options.concurrency, queue.length); i++) spawn();
64
- });
65
- }
66
- //#endregion
67
- export { runPool };
1
+ import { t as WorkerRenderPool } from "../pool-CKGp1qup.js";
2
+ export { WorkerRenderPool };
@@ -0,0 +1,15 @@
1
+ import { s as ResourceDescriptor } from "../manager-Cv1NeFyF.js";
2
+ import { n as ItemRenderOutcome, r as PipelineSettings } from "../pipeline-HiA3DC71.js";
3
+ //#region src/workers/render-worker.d.ts
4
+ interface RenderWorkerData {
5
+ resources: ResourceDescriptor;
6
+ settings: PipelineSettings;
7
+ /** libvips threads per worker; the pool already saturates the CPU. */
8
+ sharpConcurrency: number;
9
+ }
10
+ interface RenderWorkerTask {
11
+ id: string;
12
+ }
13
+ type RenderWorkerOutcome = ItemRenderOutcome;
14
+ //#endregion
15
+ export { RenderWorkerData, RenderWorkerOutcome, RenderWorkerTask };
@@ -0,0 +1,40 @@
1
+ import { t as ItemPipeline } from "../pipeline-Da2Kavba.js";
2
+ import { n as restoreResources } from "../descriptor-DWebtkI1.js";
3
+ import sharp from "sharp";
4
+ import { parentPort, workerData } from "node:worker_threads";
5
+ //#region src/workers/render-worker.ts
6
+ /**
7
+ * Worker entry: rebuilds the run's resource tree from its descriptor, then
8
+ * renders, encodes and writes items on demand. Items are written here rather
9
+ * than shipped back as buffers — the main thread only needs the outcome, and
10
+ * the file write is the run's success signal either way.
11
+ *
12
+ * Messages in are `{ id }`; messages out are `{ id, result }` or `{ id, error }`.
13
+ * Errors cross as strings because Error instances lose their type across the
14
+ * structured-clone boundary.
15
+ */
16
+ if (!parentPort) throw new Error("render-worker must be started as a worker thread");
17
+ const port = parentPort;
18
+ const data = workerData;
19
+ sharp.concurrency(data.sharpConcurrency);
20
+ const pipeline = await ItemPipeline.create(restoreResources(data.resources), data.settings);
21
+ async function handle(task) {
22
+ try {
23
+ const result = await pipeline.renderItem(task.id);
24
+ port.postMessage({
25
+ id: task.id,
26
+ result
27
+ });
28
+ } catch (err) {
29
+ port.postMessage({
30
+ id: task.id,
31
+ error: err.message
32
+ });
33
+ }
34
+ }
35
+ port.on("message", (task) => {
36
+ handle(task).catch(() => {});
37
+ });
38
+ port.postMessage({ ready: true });
39
+ //#endregion
40
+ export {};