@tileflow/dev 0.1.0-alpha.24 → 0.1.0-alpha.26

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.
package/dist/index.js CHANGED
@@ -4,14 +4,14 @@ import {
4
4
  inspectTileflowConfig,
5
5
  inspectTileflowFeatures,
6
6
  sanitizeTileflowInspectableValue
7
- } from "./chunk-MKPSQSMX.js";
7
+ } from "./chunk-73YPFDPC.js";
8
8
  import {
9
9
  createTileflowCommandFailureDocument,
10
10
  createTileflowCommandSummary,
11
11
  createTileflowStructuredDiagnostics,
12
12
  serializeTileflowCommandDocument,
13
13
  tileflowCommandDocumentSchemaVersion
14
- } from "./chunk-LNCTVFUD.js";
14
+ } from "./chunk-XHJBCVBB.js";
15
15
  import {
16
16
  TileflowPreviewSelectionError,
17
17
  normalizeTileflowLandmarkManifest,
@@ -31,7 +31,7 @@ import {
31
31
  isTileflowRequestUrl,
32
32
  tileflowComparisonSchemaVersion,
33
33
  writeTileflowNodeResponse
34
- } from "./chunk-XS5Q4IS2.js";
34
+ } from "./chunk-UCBGTEQV.js";
35
35
  import {
36
36
  TileflowHostedManifestOverwriteError,
37
37
  TileflowLocalTilesetProductionError,
@@ -49,6 +49,7 @@ import {
49
49
  getTileflowArtifactFiles,
50
50
  getTileflowWatchPaths,
51
51
  isTileflowArtifactInputPath,
52
+ prepareTileflowHostedNativeDeployment,
52
53
  prepareTileflowLocalTilesets,
53
54
  refreshTileflowArtifactSession,
54
55
  tileflowArtifactInventoryFileName,
@@ -56,7 +57,7 @@ import {
56
57
  tileflowArtifactSessionSchemaVersion,
57
58
  writeTileflowArtifactPlan,
58
59
  writeTileflowBuildArtifacts
59
- } from "./chunk-XXVI42UZ.js";
60
+ } from "./chunk-QAMFAM3S.js";
60
61
  import {
61
62
  inspectTileflowPmtiles,
62
63
  inspectTileflowPmtilesForLocalUse
@@ -72,19 +73,27 @@ import {
72
73
  prepareTileflowStyleFonts,
73
74
  replaceTileflowStyleFontSources,
74
75
  resolveTileflowArtifactPublicUrls
75
- } from "./chunk-RSBV4L2Y.js";
76
+ } from "./chunk-UTRO4IBQ.js";
76
77
  import {
77
78
  TileflowIconCompilationError,
78
79
  compileTileflowIconPackages,
80
+ composeTileflowIconSources,
81
+ getTileflowIconCacheDirectory,
79
82
  getTileflowIconWatchPaths,
80
83
  iconFileExtensions,
81
84
  inspectTileflowIconCatalogs,
82
- loadSharp,
85
+ loadTileflowIconSetArtifact,
83
86
  packTileflowRenderedIcons,
84
87
  prepareTileflowCatalogIcons,
85
- readTileflowIconDirectory
86
- } from "./chunk-NUHSYLLS.js";
87
- import "./chunk-BL3FK4GO.js";
88
+ readTileflowIconDirectory,
89
+ readTileflowIconsLockfile,
90
+ readTileflowIconsLockfileText,
91
+ renderTileflowIconAtlasPreview,
92
+ storeTileflowIconSetArtifact,
93
+ verifyTileflowIconArtifact,
94
+ writeTileflowIconsLockfile
95
+ } from "./chunk-EJPVUSX4.js";
96
+ import "./chunk-7C4F6GAX.js";
88
97
  import {
89
98
  TileflowStyleValidationError,
90
99
  assertValidTileflowStyle,
@@ -105,577 +114,6 @@ import {
105
114
  loadValidTileflowConfigWithInputs
106
115
  } from "./chunk-6MGFMKG3.js";
107
116
  import "./chunk-D4OE4ITL.js";
108
-
109
- // src/icon-composition.ts
110
- import { resolve as resolve3 } from "path";
111
- import { tileflowIconsLockfileName as tileflowIconsLockfileName2 } from "@tileflow/core";
112
- import {
113
- collectTileflowIconSetReferences,
114
- compareCodeUnits as compareCodeUnits2,
115
- parseTileflowIconComposition,
116
- parseTileflowIconsLockfile as parseTileflowIconsLockfile2,
117
- TileflowIconSetError as TileflowIconSetError4,
118
- tileflowIconPackageLimits as tileflowIconPackageLimits2
119
- } from "@tileflow/core";
120
-
121
- // src/icon-cache.ts
122
- import { hashTileflowIconPackageManifest as hashTileflowIconPackageManifest2 } from "@tileflow/core";
123
- import { constants } from "fs";
124
- import { lstat, mkdir, mkdtemp, open, readdir, realpath, rename, rm } from "fs/promises";
125
- import { homedir, platform } from "os";
126
- import { join, resolve } from "path";
127
- import {
128
- TileflowIconSetError as TileflowIconSetError2,
129
- tileflowIconSetPinSchema
130
- } from "@tileflow/core";
131
-
132
- // src/icon-artifact.ts
133
- import {
134
- tileflowIconSpriteIndexSchema as indexSchema
135
- } from "@tileflow/core";
136
- import {
137
- compareCodeUnits,
138
- hashTileflowIconPackageManifest,
139
- hashTileflowRenderedIconPixels,
140
- parseTileflowIconJson,
141
- sha256Hex,
142
- TileflowIconSetError,
143
- tileflowIconPackageLimits,
144
- tileflowIconPackageManifestSchema
145
- } from "@tileflow/core";
146
- async function verifyTileflowIconArtifact(input) {
147
- try {
148
- const manifest = tileflowIconPackageManifestSchema.parse(input.manifest);
149
- const contentHash = input.contentHash;
150
- if (input.files.length !== 4 || new Set(input.files.map((file) => file.fileName)).size !== 4)
151
- throw new Error("An artifact must contain exactly the four generated files");
152
- const files = manifest.files.map((expected) => {
153
- const source = input.files.find((file) => file.fileName === expected.name);
154
- if (!source || source.contentType !== expected.contentType || source.source.byteLength !== expected.byteLength)
155
- throw new Error("Generated file identity does not match the manifest");
156
- return {
157
- contentType: source.contentType,
158
- fileName: source.fileName,
159
- source: new Uint8Array(source.source)
160
- };
161
- });
162
- if (await hashTileflowIconPackageManifest(manifest) !== contentHash)
163
- throw new Error("Artifact manifest hash mismatch");
164
- for (const [index, file] of files.entries()) {
165
- if (await sha256Hex(file.source) !== manifest.files[index].sha256)
166
- throw new Error(`Generated ${file.fileName} checksum mismatch`);
167
- }
168
- const decoder = new TextDecoder("utf-8", { fatal: true });
169
- const oneIndex = indexSchema.parse(
170
- parseTileflowIconJson(
171
- decoder.decode(files[0].source),
172
- tileflowIconPackageLimits.maxGeneratedFileBytes
173
- )
174
- );
175
- const twoIndex = indexSchema.parse(
176
- parseTileflowIconJson(
177
- decoder.decode(files[2].source),
178
- tileflowIconPackageLimits.maxGeneratedFileBytes
179
- )
180
- );
181
- for (const index of [oneIndex, twoIndex]) {
182
- const names = Object.keys(index).sort(compareCodeUnits);
183
- if (names.length !== manifest.iconNames.length || names.some((name, ordinal) => name !== manifest.iconNames[ordinal]))
184
- throw new Error("Sprite index does not exactly match the manifest icon names");
185
- }
186
- for (const [index, dimensions, ratio] of [
187
- [oneIndex, manifest.sprites.oneX, 1],
188
- [twoIndex, manifest.sprites.twoX, 2]
189
- ]) {
190
- const rectangles = Object.values(index);
191
- for (const [ordinal, rect] of rectangles.entries()) {
192
- if (rect.pixelRatio !== ratio || rect.x + rect.width > dimensions.width || rect.y + rect.height > dimensions.height)
193
- throw new Error("Sprite rectangle is outside the atlas or has the wrong density");
194
- for (const other of rectangles.slice(0, ordinal)) {
195
- if (rect.x < other.x + other.width && other.x < rect.x + rect.width && rect.y < other.y + other.height && other.y < rect.y + rect.height)
196
- throw new Error("Sprite rectangles must not overlap");
197
- }
198
- }
199
- }
200
- for (const name of manifest.iconNames) {
201
- const one = oneIndex[name];
202
- const two = twoIndex[name];
203
- if (two.x !== one.x * 2 || two.y !== one.y * 2 || two.width !== one.width * 2 || two.height !== one.height * 2)
204
- throw new Error("Sprite densities must have exactly doubled geometry");
205
- }
206
- const oneAtlas = await decodeAtlas(files[1].source, manifest.sprites.oneX);
207
- const twoAtlas = await decodeAtlas(files[3].source, manifest.sprites.twoX);
208
- const icons = [];
209
- for (const [ordinal, id] of manifest.iconNames.entries()) {
210
- const oneX = extractCell(oneAtlas, oneIndex[id]);
211
- const twoX = extractCell(twoAtlas, twoIndex[id]);
212
- const expected = manifest.renderedIcons[ordinal].pixelSha256;
213
- if (await hashTileflowRenderedIconPixels({ ...oneX, pixelRatio: 1 }) !== expected.oneX || await hashTileflowRenderedIconPixels({ ...twoX, pixelRatio: 2 }) !== expected.twoX)
214
- throw new Error(`Rendered pixels do not match the manifest for ${id}`);
215
- icons.push({ id, oneX, twoX });
216
- }
217
- return { package: { contentHash, manifest, files }, icons };
218
- } catch (cause) {
219
- throw new TileflowIconSetError(
220
- "ICON_PACKAGE_INTEGRITY",
221
- "Generated icon artifact verification failed",
222
- { cause }
223
- );
224
- }
225
- }
226
- async function decodeAtlas(bytes, dimensions) {
227
- if ([137, 80, 78, 71, 13, 10, 26, 10].some((byte, index) => bytes[index] !== byte))
228
- throw new Error("Expected PNG bytes");
229
- const sharp = await loadSharp();
230
- const image = sharp(bytes, {
231
- failOn: "error",
232
- limitInputPixels: tileflowIconPackageLimits.maxAtlasDimension ** 2
233
- });
234
- const metadata = await image.metadata();
235
- if (metadata.format !== "png" || (metadata.pages ?? 1) !== 1 || metadata.width !== dimensions.width || metadata.height !== dimensions.height)
236
- throw new Error("PNG geometry does not match the manifest");
237
- const { data, info } = await image.ensureAlpha().raw().toBuffer({ resolveWithObject: true });
238
- if (info.channels !== 4 || info.width !== dimensions.width || info.height !== dimensions.height || data.length !== dimensions.width * dimensions.height * 4)
239
- throw new Error("Expected bounded RGBA atlas pixels");
240
- return { ...dimensions, rgba: new Uint8Array(data) };
241
- }
242
- function extractCell(atlas, rect) {
243
- const rgba = new Uint8Array(rect.width * rect.height * 4);
244
- const rowBytes = rect.width * 4;
245
- for (let row = 0; row < rect.height; row += 1) {
246
- const start = ((rect.y + row) * atlas.width + rect.x) * 4;
247
- rgba.set(atlas.rgba.subarray(start, start + rowBytes), row * rowBytes);
248
- }
249
- return { height: rect.height, width: rect.width, rgba };
250
- }
251
-
252
- // src/icon-cache.ts
253
- function getTileflowIconCacheDirectory(options = {}) {
254
- const defaultRoot = platform() === "darwin" ? join(homedir(), "Library", "Caches") : platform() === "win32" ? process.env.LOCALAPPDATA ?? join(homedir(), "AppData", "Local") : process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache");
255
- return join(
256
- resolve(options.cacheRoot ?? process.env.TILEFLOW_ICON_CACHE_DIR ?? defaultRoot),
257
- "tileflow",
258
- "icons",
259
- "v1"
260
- );
261
- }
262
- async function loadTileflowIconSetArtifact(input, options = {}) {
263
- const pin = tileflowIconSetPinSchema.parse(input);
264
- if (await hashTileflowIconPackageManifest2(pin.manifest) !== pin.contentHash)
265
- throw new TileflowIconSetError2("ICON_LOCK_INVALID", "Locked manifest hash mismatch");
266
- let cacheFailure;
267
- try {
268
- return await readCached(pin, options);
269
- } catch (cause) {
270
- if (cause instanceof TileflowIconSetError2 && cause.code === "ICON_CACHE_UNSAFE") throw cause;
271
- cacheFailure = cause;
272
- }
273
- if (options.offline) {
274
- throw new TileflowIconSetError2(
275
- isMissing(cacheFailure) ? "ICON_CACHE_MISS" : "ICON_PACKAGE_INTEGRITY",
276
- "The exact verified icon artifact is unavailable offline; hydrate this pin explicitly",
277
- { cause: cacheFailure }
278
- );
279
- }
280
- const url = new URL(pin.spriteUrl);
281
- const allowed = options.deliveryOrigins ?? ["https://api.tileflow.dev"];
282
- if (!allowed.some((origin) => origin === url.origin && new URL(origin).href === `${origin}/`)) {
283
- throw new TileflowIconSetError2(
284
- "ICON_DOWNLOAD_FAILED",
285
- "Icon artifact delivery origin is not trusted"
286
- );
287
- }
288
- const files = [];
289
- const signal = options.signal ? AbortSignal.any([options.signal, AbortSignal.timeout(3e4)]) : AbortSignal.timeout(3e4);
290
- try {
291
- for (const expected of pin.manifest.files) {
292
- signal.throwIfAborted();
293
- const fileUrl = `${pin.spriteUrl}${expected.name.slice("sprite".length)}`;
294
- const response = await (options.fetch ?? globalThis.fetch)(fileUrl, {
295
- method: "GET",
296
- credentials: "omit",
297
- redirect: "error",
298
- headers: { Accept: expected.contentType, "Accept-Encoding": "identity" },
299
- signal
300
- });
301
- if (response.status !== 200 || response.redirected || response.url && response.url !== fileUrl || !response.body)
302
- throw new Error("Unexpected artifact response");
303
- const type = response.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase();
304
- const length = response.headers.get("content-length");
305
- const encoding = response.headers.get("content-encoding")?.trim().toLowerCase();
306
- const isIdentityEncoding = encoding === void 0 || encoding === "" || encoding === "identity";
307
- if (type !== expected.contentType || length !== null && (!/^\d+$/u.test(length) || isIdentityEncoding && Number(length) !== expected.byteLength)) {
308
- await response.body.cancel();
309
- throw new Error("Artifact response headers do not match the manifest");
310
- }
311
- const reader = response.body.getReader();
312
- const source = new Uint8Array(expected.byteLength);
313
- let offset = 0;
314
- try {
315
- while (true) {
316
- const chunk = await reader.read();
317
- if (chunk.done) break;
318
- if (offset + chunk.value.byteLength > source.byteLength)
319
- throw new Error("Artifact response exceeds its exact byte length");
320
- source.set(chunk.value, offset);
321
- offset += chunk.value.byteLength;
322
- }
323
- if (offset !== source.byteLength) throw new Error("Artifact response was truncated");
324
- } catch (cause) {
325
- await reader.cancel().catch(() => void 0);
326
- throw cause;
327
- } finally {
328
- reader.releaseLock();
329
- }
330
- files.push({ fileName: expected.name, contentType: expected.contentType, source });
331
- }
332
- } catch (cause) {
333
- throw new TileflowIconSetError2("ICON_DOWNLOAD_FAILED", "Exact icon artifact download failed", {
334
- cause
335
- });
336
- }
337
- return storeTileflowIconSetArtifact(
338
- pin,
339
- { contentHash: pin.contentHash, manifest: pin.manifest, files },
340
- options
341
- );
342
- }
343
- async function storeTileflowIconSetArtifact(input, artifact, options = {}) {
344
- const pin = tileflowIconSetPinSchema.parse(input);
345
- if (await hashTileflowIconPackageManifest2(pin.manifest) !== pin.contentHash)
346
- throw new TileflowIconSetError2("ICON_LOCK_INVALID", "Locked manifest hash mismatch");
347
- if (artifact.contentHash !== pin.contentHash)
348
- throw new TileflowIconSetError2(
349
- "ICON_PACKAGE_INTEGRITY",
350
- "Artifact does not match the selected pin"
351
- );
352
- const verified = await verifyTileflowIconArtifact({
353
- contentHash: pin.contentHash,
354
- manifest: pin.manifest,
355
- files: artifact.files
356
- });
357
- const root = await safeCacheRoot(options, true);
358
- const target = join(root, pin.contentHash);
359
- const temporary = await mkdtemp(join(root, `.incoming-${pin.contentHash}-`));
360
- try {
361
- for (const file of verified.package.files) {
362
- const handle = await open(join(temporary, file.fileName), "wx", 384);
363
- try {
364
- await handle.writeFile(file.source);
365
- await handle.sync();
366
- } finally {
367
- await handle.close();
368
- }
369
- }
370
- for (let attempt = 0; attempt < 8; attempt += 1) {
371
- try {
372
- await rename(temporary, target);
373
- return verified;
374
- } catch (cause) {
375
- if (!hasCode(cause, "EEXIST") && !hasCode(cause, "ENOTEMPTY") && !hasCode(cause, "EPERM"))
376
- throw cause;
377
- }
378
- try {
379
- return await readCached(pin, options);
380
- } catch (cause) {
381
- if (cause instanceof TileflowIconSetError2 && cause.code === "ICON_CACHE_UNSAFE")
382
- throw cause;
383
- }
384
- try {
385
- await assertDirectory(target);
386
- const retired = await mkdtemp(join(root, `.retired-${pin.contentHash}-`));
387
- try {
388
- await rename(target, join(retired, "entry"));
389
- } catch (cause) {
390
- if (!isMissing(cause)) throw cause;
391
- } finally {
392
- await rm(retired, { recursive: true, force: true });
393
- }
394
- } catch (cause) {
395
- if (!isMissing(cause)) throw cause;
396
- }
397
- }
398
- throw new TileflowIconSetError2(
399
- "ICON_CACHE_UNSAFE",
400
- "Concurrent cache repairs did not converge; retry this exact pin"
401
- );
402
- } finally {
403
- await rm(temporary, { recursive: true, force: true });
404
- }
405
- }
406
- async function readCached(pin, options) {
407
- const root = await safeCacheRoot(options, false);
408
- const directory = join(root, pin.contentHash);
409
- await assertDirectory(directory);
410
- const entries = (await readdir(directory)).sort();
411
- const expectedNames = pin.manifest.files.map((file) => file.name).sort();
412
- if (entries.length !== 4 || entries.some((name, index) => name !== expectedNames[index]))
413
- throw new TileflowIconSetError2(
414
- "ICON_PACKAGE_INTEGRITY",
415
- "Cache entry does not contain exactly four generated files"
416
- );
417
- const files = [];
418
- for (const expected of pin.manifest.files) {
419
- files.push({
420
- fileName: expected.name,
421
- contentType: expected.contentType,
422
- source: await readSafeFile(join(directory, expected.name), expected.byteLength)
423
- });
424
- }
425
- return verifyTileflowIconArtifact({ contentHash: pin.contentHash, manifest: pin.manifest, files });
426
- }
427
- async function safeCacheRoot(options, create) {
428
- const root = getTileflowIconCacheDirectory(options);
429
- const base = resolve(root, "..", "..", "..");
430
- if (create) await mkdir(base, { recursive: true, mode: 448 });
431
- await assertDirectory(base);
432
- let current = await realpath(base);
433
- for (const segment of ["tileflow", "icons", "v1"]) {
434
- current = join(current, segment);
435
- if (create)
436
- await mkdir(current, { mode: 448 }).catch((cause) => {
437
- if (!hasCode(cause, "EEXIST")) throw cause;
438
- });
439
- await assertDirectory(current);
440
- }
441
- return current;
442
- }
443
- async function assertDirectory(path) {
444
- const entry = await lstat(path);
445
- if (!entry.isDirectory() || entry.isSymbolicLink())
446
- throw new TileflowIconSetError2(
447
- "ICON_CACHE_UNSAFE",
448
- "Icon cache paths must be directories, not symlinks"
449
- );
450
- }
451
- async function readSafeFile(path, maximumBytes, exact = true) {
452
- const entry = await lstat(path);
453
- if (!entry.isFile() || entry.isSymbolicLink())
454
- throw new TileflowIconSetError2(
455
- "ICON_CACHE_UNSAFE",
456
- "Icon files must be regular files, not symlinks"
457
- );
458
- const handle = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
459
- try {
460
- const opened = await handle.stat();
461
- if (!opened.isFile() || opened.ino !== entry.ino || opened.dev !== entry.dev || opened.size > maximumBytes || exact && opened.size !== maximumBytes)
462
- throw new TileflowIconSetError2(
463
- "ICON_PACKAGE_INTEGRITY",
464
- "Icon file size or identity changed"
465
- );
466
- const buffer = new Uint8Array(maximumBytes + 1);
467
- let offset = 0;
468
- while (offset < buffer.length) {
469
- const { bytesRead } = await handle.read(buffer, offset, buffer.length - offset, null);
470
- if (bytesRead === 0) break;
471
- offset += bytesRead;
472
- }
473
- if (offset > maximumBytes || exact && offset !== maximumBytes)
474
- throw new TileflowIconSetError2("ICON_PACKAGE_INTEGRITY", "Icon file byte length changed");
475
- return buffer.slice(0, offset);
476
- } finally {
477
- await handle.close();
478
- }
479
- }
480
- function hasCode(error, code) {
481
- return error !== null && typeof error === "object" && "code" in error && error.code === code;
482
- }
483
- function isMissing(error) {
484
- return hasCode(error, "ENOENT");
485
- }
486
-
487
- // src/icon-lockfile.ts
488
- import { lstat as lstat2, mkdir as mkdir2, mkdtemp as mkdtemp2, open as open2, rename as rename2, rm as rm2, rmdir } from "fs/promises";
489
- import { join as join2, resolve as resolve2 } from "path";
490
- import {
491
- parseTileflowIconsLockfile,
492
- serializeTileflowIconsLockfile,
493
- TileflowIconSetError as TileflowIconSetError3,
494
- tileflowIconsLockfileMaximumBytes,
495
- tileflowIconsLockfileName
496
- } from "@tileflow/core";
497
- async function readTileflowIconsLockfile(baseDirectory, sources) {
498
- const text = await readLockText(join2(resolve2(baseDirectory), tileflowIconsLockfileName));
499
- if (text === null)
500
- throw new TileflowIconSetError3(
501
- "ICON_LOCK_MISSING",
502
- "Missing tileflow.icons.lock.json; install exact icon set revisions explicitly"
503
- );
504
- return parseTileflowIconsLockfile(text, sources);
505
- }
506
- async function writeTileflowIconsLockfile(baseDirectory, input, expectedContents) {
507
- const content = await serializeTileflowIconsLockfile(input);
508
- const directory = resolve2(baseDirectory);
509
- const destination = join2(directory, tileflowIconsLockfileName);
510
- const guard = join2(directory, `.${tileflowIconsLockfileName}.writing`);
511
- try {
512
- await mkdir2(guard, { mode: 448 });
513
- } catch (cause) {
514
- throw new TileflowIconSetError3(
515
- "ICON_LOCK_CONFLICT",
516
- "Another icon lock writer is active; a stale .writing guard may be removed only after confirming its writer has exited",
517
- { cause }
518
- );
519
- }
520
- let temporary;
521
- try {
522
- const actual = await readLockText(destination);
523
- if (actual !== expectedContents)
524
- throw new TileflowIconSetError3(
525
- "ICON_LOCK_CONFLICT",
526
- "Icon lock changed since it was read; reload and retry without discarding other pins"
527
- );
528
- if (actual === content) return;
529
- temporary = await mkdtemp2(join2(directory, ".tileflow-icons-lock-"));
530
- const file = join2(temporary, "lock");
531
- const handle = await open2(file, "wx", 384);
532
- try {
533
- await handle.writeFile(content, "utf8");
534
- await handle.sync();
535
- } finally {
536
- await handle.close();
537
- }
538
- if (await readLockText(destination) !== actual)
539
- throw new TileflowIconSetError3(
540
- "ICON_LOCK_CONFLICT",
541
- "Icon lock changed while preparing an update"
542
- );
543
- await rename2(file, destination);
544
- } finally {
545
- if (temporary) await rm2(temporary, { recursive: true, force: true });
546
- await rmdir(guard);
547
- }
548
- }
549
- async function readLockText(path) {
550
- try {
551
- const entry = await lstat2(path);
552
- if (!entry.isFile() || entry.isSymbolicLink())
553
- throw new TileflowIconSetError3(
554
- "ICON_LOCK_INVALID",
555
- "The icon lock must be a regular file, not a symlink"
556
- );
557
- return new TextDecoder("utf-8", { fatal: true }).decode(
558
- await readSafeFile(path, tileflowIconsLockfileMaximumBytes, false)
559
- );
560
- } catch (cause) {
561
- if (cause !== null && typeof cause === "object" && "code" in cause && cause.code === "ENOENT")
562
- return null;
563
- throw cause;
564
- }
565
- }
566
-
567
- // src/icon-composition.ts
568
- async function composeTileflowIconSources(input, options) {
569
- const sources = structuredClone(input);
570
- const references = collectTileflowIconSetReferences(sources);
571
- const lock = references.length > 0 ? options.lock === void 0 ? await readTileflowIconsLockfile(options.baseDirectory ?? options.cwd, sources) : await parseTileflowIconsLockfile2(options.lock, sources) : await parseTileflowIconsLockfile2(options.lock ?? { lockfileVersion: 1, sets: {} }, sources);
572
- const contributors = new Array(sources.length);
573
- const winners = /* @__PURE__ */ new Map();
574
- const watchPaths = /* @__PURE__ */ new Set();
575
- if (references.length > 0 && options.lock === void 0)
576
- watchPaths.add(resolve3(options.baseDirectory ?? options.cwd, tileflowIconsLockfileName2));
577
- let reused;
578
- let pixelBytes = 0;
579
- let localSourceBytes = 0;
580
- for (let ordinal = sources.length - 1; ordinal >= 0; ordinal -= 1) {
581
- const source = sources[ordinal];
582
- if (typeof source === "object" && source.kind === "icon-set") {
583
- const pin = lock.sets[source.reference];
584
- const loaded = await loadTileflowIconSetArtifact(pin, options);
585
- contributors[ordinal] = {
586
- kind: "icon-set",
587
- reference: source.reference,
588
- teamId: pin.teamId,
589
- setId: pin.setId,
590
- versionId: pin.versionId,
591
- version: pin.version,
592
- packageId: pin.packageId,
593
- contentHash: pin.contentHash,
594
- iconIds: [...pin.manifest.iconNames]
595
- };
596
- if (sources.length === 1) reused = loaded.package;
597
- for (const icon of loaded.icons) {
598
- if (winners.has(icon.id)) continue;
599
- const pixels = pin.manifest.renderedIcons.find(
600
- (entry) => entry.name === icon.id
601
- ).pixelSha256;
602
- addWinner(ordinal, icon, {
603
- kind: "rendered-icon",
604
- id: icon.id,
605
- width: icon.oneX.width,
606
- height: icon.oneX.height,
607
- pixelSha256: { ...pixels }
608
- });
609
- }
610
- } else {
611
- const directory = await readTileflowIconDirectory(source, {
612
- cwd: options.cwd,
613
- baseDirectory: options.baseDirectory ?? options.cwd,
614
- target: options.target ?? "local"
615
- });
616
- if (directory.watchPath) watchPaths.add(directory.watchPath);
617
- contributors[ordinal] = typeof source === "string" ? { kind: "local", iconIds: directory.iconIds } : { kind: "package", package: source.package, iconIds: directory.iconIds };
618
- const selected = directory.iconIds.filter((id) => !winners.has(id));
619
- if (winners.size + selected.length > tileflowIconPackageLimits2.maxIconCount)
620
- throw new TileflowIconSetError4(
621
- "ICON_COMPOSITION_INVALID",
622
- "Composed icon set exceeds 256 effective icons"
623
- );
624
- for (const item of await directory.render(selected)) {
625
- localSourceBytes += item.sourceBytes;
626
- if (localSourceBytes > tileflowIconPackageLimits2.maxSourceBytes)
627
- throw new TileflowIconSetError4(
628
- "ICON_COMPOSITION_INVALID",
629
- "Effective local icon originals exceed the source-byte limit"
630
- );
631
- addWinner(ordinal, item.icon, item.identity);
632
- }
633
- }
634
- }
635
- const sorted = [...winners.values()].sort(
636
- (left, right) => compareCodeUnits2(left.icon.id, right.icon.id)
637
- );
638
- const iconPackage = sorted.length ? reused ?? await packTileflowRenderedIcons(sorted.map((winner) => winner.icon)) : null;
639
- const composition = references.length && iconPackage ? parseTileflowIconComposition({
640
- format: "tileflow-icon-composition-v1",
641
- compositionVersion: 1,
642
- packageHash: iconPackage.contentHash,
643
- contributors,
644
- winners: sorted.map(({ ordinal, icon }, index) => ({
645
- id: icon.id,
646
- contributor: ordinal,
647
- width: icon.oneX.width,
648
- height: icon.oneX.height,
649
- pixelSha256: iconPackage.manifest.renderedIcons[index].pixelSha256
650
- }))
651
- }) : null;
652
- const previous = /* @__PURE__ */ new Map();
653
- const replacements = [];
654
- for (const [ordinal, contributor] of contributors.entries()) {
655
- for (const id of contributor.iconIds) {
656
- const replaced = previous.get(id);
657
- if (replaced !== void 0) replacements.push({ id, replaced, winner: ordinal });
658
- previous.set(id, ordinal);
659
- }
660
- }
661
- return {
662
- package: iconPackage,
663
- composition,
664
- sourceIdentities: sorted.map((winner) => winner.identity),
665
- replacements,
666
- watchPaths: [...watchPaths].sort(compareCodeUnits2)
667
- };
668
- function addWinner(ordinal, icon, identity) {
669
- pixelBytes += icon.oneX.rgba.byteLength + icon.twoX.rgba.byteLength;
670
- const maximumPixelBytes = (tileflowIconPackageLimits2.maxAtlasDimension ** 2 + (tileflowIconPackageLimits2.maxAtlasDimension / 2) ** 2) * 4;
671
- if (winners.size >= tileflowIconPackageLimits2.maxIconCount || pixelBytes > maximumPixelBytes)
672
- throw new TileflowIconSetError4(
673
- "ICON_COMPOSITION_INVALID",
674
- "Composed rendered cells exceed the final sprite capacity"
675
- );
676
- winners.set(icon.id, { ordinal, icon, identity });
677
- }
678
- }
679
117
  export {
680
118
  TileflowConfigInspectionError,
681
119
  TileflowFontCompilationError,
@@ -742,13 +180,16 @@ export {
742
180
  normalizeTileflowLandmarkManifest,
743
181
  packTileflowRenderedIcons,
744
182
  prepareTileflowCatalogIcons,
183
+ prepareTileflowHostedNativeDeployment,
745
184
  prepareTileflowLocalTilesets,
746
185
  prepareTileflowStyleFonts,
747
186
  readBoundedTileflowJsonResponse,
748
187
  readBoundedTileflowResponse,
749
188
  readTileflowIconDirectory,
750
189
  readTileflowIconsLockfile,
190
+ readTileflowIconsLockfileText,
751
191
  refreshTileflowArtifactSession,
192
+ renderTileflowIconAtlasPreview,
752
193
  renderTileflowPreviewHtml,
753
194
  replaceTileflowStyleFontSources,
754
195
  resolveTileflowArtifactPublicUrls,
package/dist/inspect.js CHANGED
@@ -4,8 +4,8 @@ import {
4
4
  inspectTileflowConfig,
5
5
  inspectTileflowFeatures,
6
6
  sanitizeTileflowInspectableValue
7
- } from "./chunk-MKPSQSMX.js";
8
- import "./chunk-LNCTVFUD.js";
7
+ } from "./chunk-73YPFDPC.js";
8
+ import "./chunk-XHJBCVBB.js";
9
9
  import "./chunk-KDFTE4Q5.js";
10
10
  import "./chunk-6MGFMKG3.js";
11
11
  import "./chunk-D4OE4ITL.js";
package/dist/server.d.ts CHANGED
@@ -4,7 +4,8 @@ import { IncomingMessage, ServerResponse } from 'node:http';
4
4
  import '@tileflow/core';
5
5
  import '@tileflow/core/build';
6
6
  import '@tileflow/core/manifest';
7
- import './icons-CkLVtaP-.js';
7
+ import '@tileflow/core/native-profile';
8
+ import './icons.js';
8
9
  import './config.js';
9
10
 
10
11
  declare const tileflowComparisonSchemaVersion: 1;