@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/README.md +34 -7
- package/THIRD_PARTY_NOTICES.md +23 -0
- package/dist/artifacts.d.ts +39 -5
- package/dist/artifacts.js +6 -4
- package/dist/{chunk-MKPSQSMX.js → chunk-73YPFDPC.js} +1 -1
- package/dist/{chunk-BL3FK4GO.js → chunk-7C4F6GAX.js} +1 -1
- package/dist/chunk-EJPVUSX4.js +1825 -0
- package/dist/{chunk-XXVI42UZ.js → chunk-QAMFAM3S.js} +1178 -39
- package/dist/{chunk-XS5Q4IS2.js → chunk-UCBGTEQV.js} +2 -2
- package/dist/{chunk-RSBV4L2Y.js → chunk-UTRO4IBQ.js} +1 -1
- package/dist/{chunk-LNCTVFUD.js → chunk-XHJBCVBB.js} +6 -1
- package/dist/fonts.d.ts +1 -1
- package/dist/fonts.js +2 -2
- package/dist/icons.d.ts +337 -3
- package/dist/icons.js +24 -4
- package/dist/index.d.ts +5 -53
- package/dist/index.js +21 -580
- package/dist/inspect.js +2 -2
- package/dist/server.d.ts +2 -1
- package/dist/server.js +5 -5
- package/dist/validation.d.ts +2 -0
- package/dist/validation.js +1 -1
- package/docs/local-comparison-workbench-and-compiler-inspection.md +6 -5
- package/docs/native-artifacts.md +258 -0
- package/docs/native-icons.md +129 -0
- package/package.json +3 -3
- package/dist/chunk-NUHSYLLS.js +0 -986
- package/dist/icons-CkLVtaP-.d.ts +0 -164
|
@@ -2,15 +2,16 @@ import {
|
|
|
2
2
|
inspectTileflowPmtilesForLocalUse
|
|
3
3
|
} from "./chunk-ONYQNLNA.js";
|
|
4
4
|
import {
|
|
5
|
+
TileflowFontCompilationError,
|
|
5
6
|
getTileflowFontWatchPaths,
|
|
6
7
|
joinTileflowPublicUrl,
|
|
7
8
|
prepareTileflowStyleFonts,
|
|
8
9
|
replaceTileflowStyleFontSources
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-UTRO4IBQ.js";
|
|
10
11
|
import {
|
|
11
12
|
getTileflowIconWatchPaths,
|
|
12
13
|
prepareTileflowCatalogIcons
|
|
13
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-EJPVUSX4.js";
|
|
14
15
|
import {
|
|
15
16
|
TileflowStyleValidationError,
|
|
16
17
|
normalizeTileflowStyleValidationIssues,
|
|
@@ -27,7 +28,7 @@ import {
|
|
|
27
28
|
} from "./chunk-D4OE4ITL.js";
|
|
28
29
|
|
|
29
30
|
// src/artifacts.ts
|
|
30
|
-
import { createHash } from "crypto";
|
|
31
|
+
import { createHash as createHash3 } from "crypto";
|
|
31
32
|
import { realpathSync as realpathSync2 } from "fs";
|
|
32
33
|
import {
|
|
33
34
|
copyFile as copyFile2,
|
|
@@ -45,8 +46,8 @@ import { createRequire } from "module";
|
|
|
45
46
|
import { dirname as dirname2, relative as relative2, resolve as resolve3, sep as sep2 } from "path";
|
|
46
47
|
import {
|
|
47
48
|
compareCodeUnits as compareCodeUnits2,
|
|
48
|
-
getTileflowStyleFontFaces,
|
|
49
|
-
serializeCanonicalJson,
|
|
49
|
+
getTileflowStyleFontFaces as getTileflowStyleFontFaces2,
|
|
50
|
+
serializeCanonicalJson as serializeCanonicalJson4,
|
|
50
51
|
tileflowMapIdSchema,
|
|
51
52
|
tileflowThemeNameSchema
|
|
52
53
|
} from "@tileflow/core";
|
|
@@ -59,6 +60,13 @@ import {
|
|
|
59
60
|
tileflowMapBuildManifestFileName
|
|
60
61
|
} from "@tileflow/core/build";
|
|
61
62
|
import { parseTileflowRuntimeManifest } from "@tileflow/core/manifest";
|
|
63
|
+
import {
|
|
64
|
+
createTileflowNativeBuildRecord as createTileflowNativeBuildRecord2,
|
|
65
|
+
createTileflowNativeDiagnostic as createTileflowNativeDiagnostic4,
|
|
66
|
+
resolveTileflowRenderer,
|
|
67
|
+
tileflowNativeBuildRecordFileName,
|
|
68
|
+
TileflowNativeCompatibilityError as TileflowNativeCompatibilityError5
|
|
69
|
+
} from "@tileflow/core/native-profile";
|
|
62
70
|
|
|
63
71
|
// src/local-tilesets.ts
|
|
64
72
|
import { randomUUID } from "crypto";
|
|
@@ -375,14 +383,802 @@ function hasErrorCode(error, code) {
|
|
|
375
383
|
return Boolean(error && typeof error === "object" && error.code === code);
|
|
376
384
|
}
|
|
377
385
|
|
|
386
|
+
// src/native-artifacts.ts
|
|
387
|
+
import { createHash } from "crypto";
|
|
388
|
+
import {
|
|
389
|
+
convertFilter,
|
|
390
|
+
validateStyleMin
|
|
391
|
+
} from "@maplibre/maplibre-gl-style-spec";
|
|
392
|
+
import {
|
|
393
|
+
getTileflowStyleFontFaces,
|
|
394
|
+
serializeCanonicalJson,
|
|
395
|
+
tileflowStyleFontFacesMetadataKey,
|
|
396
|
+
tileflowIconIdPattern,
|
|
397
|
+
tileflowIconPackageLimits
|
|
398
|
+
} from "@tileflow/core";
|
|
399
|
+
import {
|
|
400
|
+
createTileflowNativeDiagnostic,
|
|
401
|
+
TileflowNativeCompatibilityError,
|
|
402
|
+
tileflowNativeProfileLimits,
|
|
403
|
+
validateTileflowNativeStyle,
|
|
404
|
+
validateTileflowNativePreparedStyle
|
|
405
|
+
} from "@tileflow/core/native-profile";
|
|
406
|
+
|
|
407
|
+
// src/native-lowering.ts
|
|
408
|
+
var nativeLoweringLimits = Object.freeze({
|
|
409
|
+
maximumDecisionDepth: 16,
|
|
410
|
+
maximumDecisionNodes: 512,
|
|
411
|
+
maximumPropertyBranches: 16,
|
|
412
|
+
maximumPathsPerProperty: 64,
|
|
413
|
+
maximumLayerVariants: 32,
|
|
414
|
+
maximumZoomIntervals: 32,
|
|
415
|
+
maximumLayers: 4096,
|
|
416
|
+
maximumDashLength: 64
|
|
417
|
+
});
|
|
418
|
+
var NativeLoweringError = class extends Error {
|
|
419
|
+
constructor(path, reason = "shape") {
|
|
420
|
+
super(
|
|
421
|
+
reason === "budget" ? "Native decision lowering exceeds its finite budget." : "This native decision cannot be lowered without changing its meaning."
|
|
422
|
+
);
|
|
423
|
+
this.path = path;
|
|
424
|
+
this.reason = reason;
|
|
425
|
+
this.name = "NativeLoweringError";
|
|
426
|
+
}
|
|
427
|
+
path;
|
|
428
|
+
reason;
|
|
429
|
+
};
|
|
430
|
+
function fail(path, reason = "shape") {
|
|
431
|
+
throw new NativeLoweringError(path, reason);
|
|
432
|
+
}
|
|
433
|
+
function object(value) {
|
|
434
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
435
|
+
}
|
|
436
|
+
function operation(value, name, length) {
|
|
437
|
+
return Array.isArray(value) && value[0] === name && (length === void 0 || value.length === length);
|
|
438
|
+
}
|
|
439
|
+
function finite(value) {
|
|
440
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
441
|
+
}
|
|
442
|
+
function key(value) {
|
|
443
|
+
return JSON.stringify(value);
|
|
444
|
+
}
|
|
445
|
+
function negate(value) {
|
|
446
|
+
return typeof value === "boolean" ? !value : operation(value, "!", 2) ? value[1] : ["!", value];
|
|
447
|
+
}
|
|
448
|
+
function combine(operator, values) {
|
|
449
|
+
const identity = operator === "all";
|
|
450
|
+
const result = [];
|
|
451
|
+
const seen = /* @__PURE__ */ new Set();
|
|
452
|
+
for (const value of values.flatMap(
|
|
453
|
+
(item) => operation(item, operator) ? item.slice(1) : [item]
|
|
454
|
+
)) {
|
|
455
|
+
if (value === identity) continue;
|
|
456
|
+
if (value === !identity || seen.has(key(negate(value)))) return !identity;
|
|
457
|
+
const serialized = key(value);
|
|
458
|
+
if (!seen.has(serialized)) {
|
|
459
|
+
seen.add(serialized);
|
|
460
|
+
result.push(value);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return result.length === 0 ? identity : result.length === 1 ? result[0] : [operator, ...result];
|
|
464
|
+
}
|
|
465
|
+
function choose(when, yes, no) {
|
|
466
|
+
if (when === true) return yes;
|
|
467
|
+
if (when === false) return no;
|
|
468
|
+
if (key(yes) === key(no)) return yes;
|
|
469
|
+
if (yes === true) return combine("any", [when, no]);
|
|
470
|
+
if (yes === false) return combine("all", [negate(when), no]);
|
|
471
|
+
if (no === true) return combine("any", [negate(when), yes]);
|
|
472
|
+
if (no === false) return combine("all", [when, yes]);
|
|
473
|
+
return ["case", when, yes, no];
|
|
474
|
+
}
|
|
475
|
+
function decision(arms, fallback) {
|
|
476
|
+
const active = [];
|
|
477
|
+
for (const arm of arms) {
|
|
478
|
+
if (arm.when === false) continue;
|
|
479
|
+
if (arm.when === true) {
|
|
480
|
+
fallback = arm.value;
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
const previous = active.at(-1);
|
|
484
|
+
if (previous && key(previous.value) === key(arm.value)) {
|
|
485
|
+
previous.when = combine("any", [previous.when, arm.when]);
|
|
486
|
+
} else active.push({ ...arm });
|
|
487
|
+
}
|
|
488
|
+
while (active.length && key(active.at(-1).value) === key(fallback)) active.pop();
|
|
489
|
+
if (!active.length) return fallback;
|
|
490
|
+
if (active.length === 1) return choose(active[0].when, active[0].value, fallback);
|
|
491
|
+
return ["case", ...active.flatMap(({ when, value }) => [when, value]), fallback];
|
|
492
|
+
}
|
|
493
|
+
var Decisions = class {
|
|
494
|
+
constructor(path, vector) {
|
|
495
|
+
this.path = path;
|
|
496
|
+
this.vector = vector;
|
|
497
|
+
}
|
|
498
|
+
path;
|
|
499
|
+
vector;
|
|
500
|
+
visited = 0;
|
|
501
|
+
cuts = /* @__PURE__ */ new Set();
|
|
502
|
+
tick(depth) {
|
|
503
|
+
if (++this.visited > nativeLoweringLimits.maximumDecisionNodes || depth > nativeLoweringLimits.maximumDecisionDepth) {
|
|
504
|
+
fail(this.path, "budget");
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
tree(value, property, depth = 0) {
|
|
508
|
+
this.tick(depth);
|
|
509
|
+
const leaf = operation(value, "literal", 2) ? value[1] : value;
|
|
510
|
+
if (property === "line-cap" && typeof leaf === "string" && ["butt", "round", "square"].includes(leaf)) {
|
|
511
|
+
return { kind: "leaf", value: leaf };
|
|
512
|
+
}
|
|
513
|
+
if (property === "line-dasharray" && Array.isArray(leaf) && leaf.length > 0 && leaf.length <= nativeLoweringLimits.maximumDashLength && leaf.every((item) => finite(item) && item >= 0)) {
|
|
514
|
+
return { kind: "leaf", value: [...leaf] };
|
|
515
|
+
}
|
|
516
|
+
if (operation(value, "case") && value.length >= 4 && value.length % 2 === 0) {
|
|
517
|
+
const arms = [];
|
|
518
|
+
for (let index = 1; index < value.length - 1; index += 2) {
|
|
519
|
+
const when = this.predicate(value[index], depth + 1);
|
|
520
|
+
arms.push({ when, value: this.tree(value[index + 1], property, depth + 1) });
|
|
521
|
+
}
|
|
522
|
+
return { kind: "case", arms, fallback: this.tree(value.at(-1), property, depth + 1) };
|
|
523
|
+
}
|
|
524
|
+
if (operation(value, "match") && value.length >= 5 && value.length % 2 === 1) {
|
|
525
|
+
this.scalar(value[1], depth + 1);
|
|
526
|
+
const labels = this.matchLabels(value, depth + 1);
|
|
527
|
+
return {
|
|
528
|
+
kind: "case",
|
|
529
|
+
arms: labels.map((when, index) => ({
|
|
530
|
+
when,
|
|
531
|
+
value: this.tree(value[index * 2 + 3], property, depth + 1)
|
|
532
|
+
})),
|
|
533
|
+
fallback: this.tree(value.at(-1), property, depth + 1)
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
if (operation(value, "step") && value.length >= 5 && value.length % 2 === 1) {
|
|
537
|
+
if (this.scalar(value[1], depth + 1) !== "number") fail(this.path);
|
|
538
|
+
const stops = [];
|
|
539
|
+
let previous = -Infinity;
|
|
540
|
+
for (let index = 3; index < value.length; index += 2) {
|
|
541
|
+
const at = value[index];
|
|
542
|
+
if (!finite(at) || at <= previous) fail(this.path);
|
|
543
|
+
previous = at;
|
|
544
|
+
if (operation(value[1], "zoom", 1)) this.cuts.add(Math.ceil(at));
|
|
545
|
+
stops.push({ at, value: this.tree(value[index + 1], property, depth + 1) });
|
|
546
|
+
}
|
|
547
|
+
return {
|
|
548
|
+
kind: "step",
|
|
549
|
+
input: value[1],
|
|
550
|
+
fallback: this.tree(value[2], property, depth + 1),
|
|
551
|
+
stops
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
return fail(this.path);
|
|
555
|
+
}
|
|
556
|
+
scalar(value, depth) {
|
|
557
|
+
this.tick(depth);
|
|
558
|
+
if (finite(value)) return "number";
|
|
559
|
+
if (value === null || typeof value === "boolean" || typeof value === "string") return "scalar";
|
|
560
|
+
if (operation(value, "zoom", 1)) return "number";
|
|
561
|
+
if (operation(value, "get", 2) && typeof value[1] === "string" && this.vector) return "scalar";
|
|
562
|
+
if (operation(value, "geometry-type", 1) || operation(value, "id", 1) && this.vector)
|
|
563
|
+
return "scalar";
|
|
564
|
+
if (operation(value, "to-string", 2)) {
|
|
565
|
+
if (operation(value[1], "zoom", 1)) fail(this.path);
|
|
566
|
+
this.scalar(value[1], depth + 1);
|
|
567
|
+
return "scalar";
|
|
568
|
+
}
|
|
569
|
+
if (operation(value, "coalesce") && value.length >= 3) {
|
|
570
|
+
for (const child of value.slice(1)) {
|
|
571
|
+
if (operation(child, "zoom", 1)) fail(this.path);
|
|
572
|
+
this.scalar(child, depth + 1);
|
|
573
|
+
}
|
|
574
|
+
return "scalar";
|
|
575
|
+
}
|
|
576
|
+
if (operation(value, "to-number", 3) && finite(value[2])) {
|
|
577
|
+
if (operation(value[1], "zoom", 1)) fail(this.path);
|
|
578
|
+
this.scalar(value[1], depth + 1);
|
|
579
|
+
return "number";
|
|
580
|
+
}
|
|
581
|
+
return fail(this.path);
|
|
582
|
+
}
|
|
583
|
+
matchLabels(value, depth) {
|
|
584
|
+
const seen = /* @__PURE__ */ new Set();
|
|
585
|
+
let labelType;
|
|
586
|
+
const predicates = [];
|
|
587
|
+
for (let index = 2; index < value.length - 1; index += 2) {
|
|
588
|
+
const labels = Array.isArray(value[index]) ? value[index] : [value[index]];
|
|
589
|
+
if (!labels.length) fail(this.path);
|
|
590
|
+
const terms = [];
|
|
591
|
+
for (const label of labels) {
|
|
592
|
+
this.tick(depth);
|
|
593
|
+
if (!(typeof label === "string" || finite(label) && Number.isInteger(label)))
|
|
594
|
+
fail(this.path);
|
|
595
|
+
if (labelType !== void 0 && labelType !== typeof label) fail(this.path);
|
|
596
|
+
labelType = typeof label;
|
|
597
|
+
const encoded = key(label);
|
|
598
|
+
if (seen.has(encoded)) fail(this.path);
|
|
599
|
+
seen.add(encoded);
|
|
600
|
+
if (operation(value[1], "zoom", 1)) {
|
|
601
|
+
if (!finite(label)) fail(this.path);
|
|
602
|
+
this.cuts.add(label);
|
|
603
|
+
this.cuts.add(label + 1);
|
|
604
|
+
}
|
|
605
|
+
terms.push(["==", value[1], label]);
|
|
606
|
+
}
|
|
607
|
+
predicates.push(combine("any", terms));
|
|
608
|
+
}
|
|
609
|
+
return predicates;
|
|
610
|
+
}
|
|
611
|
+
predicate(value, depth) {
|
|
612
|
+
this.tick(depth);
|
|
613
|
+
if (typeof value === "boolean") return value;
|
|
614
|
+
if (operation(value, "literal", 2) && typeof value[1] === "boolean") return value[1];
|
|
615
|
+
if (operation(value, "all") || operation(value, "any")) {
|
|
616
|
+
return combine(
|
|
617
|
+
value[0],
|
|
618
|
+
value.slice(1).map((child) => this.predicate(child, depth + 1))
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
if (operation(value, "!", 2)) return negate(this.predicate(value[1], depth + 1));
|
|
622
|
+
if (operation(value, "has", 2) && typeof value[1] === "string") return value;
|
|
623
|
+
if (operation(value, "match") && value.length >= 5 && value.length % 2 === 1) {
|
|
624
|
+
this.scalar(value[1], depth + 1);
|
|
625
|
+
const conditions = this.matchLabels(value, depth + 1);
|
|
626
|
+
let remaining = true;
|
|
627
|
+
const terms = [];
|
|
628
|
+
for (const [index, condition] of conditions.entries()) {
|
|
629
|
+
terms.push(combine("all", [condition, this.predicate(value[index * 2 + 3], depth + 1)]));
|
|
630
|
+
remaining = combine("all", [remaining, negate(condition)]);
|
|
631
|
+
}
|
|
632
|
+
terms.push(combine("all", [remaining, this.predicate(value.at(-1), depth + 1)]));
|
|
633
|
+
return combine("any", terms);
|
|
634
|
+
}
|
|
635
|
+
if (operation(value, "case") && value.length >= 4 && value.length % 2 === 0) {
|
|
636
|
+
let remaining = true;
|
|
637
|
+
const terms = [];
|
|
638
|
+
for (let index = 1; index < value.length - 1; index += 2) {
|
|
639
|
+
const condition = this.predicate(value[index], depth + 1);
|
|
640
|
+
terms.push(
|
|
641
|
+
combine("all", [remaining, condition, this.predicate(value[index + 1], depth + 1)])
|
|
642
|
+
);
|
|
643
|
+
remaining = combine("all", [remaining, negate(condition)]);
|
|
644
|
+
}
|
|
645
|
+
terms.push(combine("all", [remaining, this.predicate(value.at(-1), depth + 1)]));
|
|
646
|
+
return combine("any", terms);
|
|
647
|
+
}
|
|
648
|
+
if (operation(value, "step") && value.length >= 5 && value.length % 2 === 1) {
|
|
649
|
+
if (this.scalar(value[1], depth + 1) !== "number") fail(this.path);
|
|
650
|
+
let previous = -Infinity;
|
|
651
|
+
let remaining = true;
|
|
652
|
+
let output = this.predicate(value[2], depth + 1);
|
|
653
|
+
const terms = [];
|
|
654
|
+
for (let index = 3; index < value.length; index += 2) {
|
|
655
|
+
const at = value[index];
|
|
656
|
+
if (!finite(at) || at <= previous) fail(this.path);
|
|
657
|
+
previous = at;
|
|
658
|
+
if (operation(value[1], "zoom", 1)) this.cuts.add(Math.ceil(at));
|
|
659
|
+
const below = ["<", value[1], at];
|
|
660
|
+
terms.push(combine("all", [remaining, below, output]));
|
|
661
|
+
remaining = combine("all", [remaining, negate(below)]);
|
|
662
|
+
output = this.predicate(value[index + 1], depth + 1);
|
|
663
|
+
}
|
|
664
|
+
terms.push(combine("all", [remaining, output]));
|
|
665
|
+
return combine("any", terms);
|
|
666
|
+
}
|
|
667
|
+
if (operation(value, "in", 3) && operation(value[2], "literal", 2) && Array.isArray(value[2][1])) {
|
|
668
|
+
this.scalar(value[1], depth + 1);
|
|
669
|
+
if (operation(value[1], "zoom", 1)) fail(this.path);
|
|
670
|
+
for (const item of value[2][1]) {
|
|
671
|
+
this.tick(depth + 1);
|
|
672
|
+
if (!(item === null || typeof item === "boolean" || typeof item === "string" || finite(item)))
|
|
673
|
+
fail(this.path);
|
|
674
|
+
}
|
|
675
|
+
return value;
|
|
676
|
+
}
|
|
677
|
+
if (Array.isArray(value) && value.length === 3 && ["==", "!=", "<", "<=", ">", ">="].includes(String(value[0]))) {
|
|
678
|
+
const left = this.scalar(value[1], depth + 1);
|
|
679
|
+
const right = this.scalar(value[2], depth + 1);
|
|
680
|
+
if (!["==", "!="].includes(String(value[0])) && (left !== "number" || right !== "number"))
|
|
681
|
+
fail(this.path);
|
|
682
|
+
if (operation(value[1], "zoom", 1) || operation(value[2], "zoom", 1)) {
|
|
683
|
+
const constant = operation(value[1], "zoom", 1) ? value[2] : value[1];
|
|
684
|
+
if (!finite(constant)) fail(this.path);
|
|
685
|
+
this.cuts.add(Math.ceil(constant));
|
|
686
|
+
this.cuts.add(Math.floor(constant) + 1);
|
|
687
|
+
}
|
|
688
|
+
return value;
|
|
689
|
+
}
|
|
690
|
+
return fail(this.path);
|
|
691
|
+
}
|
|
692
|
+
atZoom(value, zoom) {
|
|
693
|
+
if (typeof value === "boolean") return value;
|
|
694
|
+
const expr = value;
|
|
695
|
+
if (expr[0] === "all" || expr[0] === "any")
|
|
696
|
+
return combine(
|
|
697
|
+
expr[0],
|
|
698
|
+
expr.slice(1).map((child) => this.atZoom(child, zoom))
|
|
699
|
+
);
|
|
700
|
+
if (expr[0] === "!") return negate(this.atZoom(expr[1], zoom));
|
|
701
|
+
if (operation(expr[1], "zoom", 1) || operation(expr[2], "zoom", 1)) {
|
|
702
|
+
const left = operation(expr[1], "zoom", 1) ? zoom : expr[1];
|
|
703
|
+
const right = operation(expr[2], "zoom", 1) ? zoom : expr[2];
|
|
704
|
+
switch (expr[0]) {
|
|
705
|
+
case "==":
|
|
706
|
+
return left === right;
|
|
707
|
+
case "!=":
|
|
708
|
+
return left !== right;
|
|
709
|
+
case "<":
|
|
710
|
+
return left < right;
|
|
711
|
+
case "<=":
|
|
712
|
+
return left <= right;
|
|
713
|
+
case ">":
|
|
714
|
+
return left > right;
|
|
715
|
+
case ">=":
|
|
716
|
+
return left >= right;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
return expr;
|
|
720
|
+
}
|
|
721
|
+
outcomes(tree, zoom) {
|
|
722
|
+
const leaves = [];
|
|
723
|
+
const walk = (node, guard) => {
|
|
724
|
+
if (guard === false) return;
|
|
725
|
+
if (node.kind === "leaf") {
|
|
726
|
+
leaves.push({ when: guard, value: node.value });
|
|
727
|
+
if (leaves.length > nativeLoweringLimits.maximumPathsPerProperty) fail(this.path, "budget");
|
|
728
|
+
} else if (node.kind === "case") {
|
|
729
|
+
let remaining = guard;
|
|
730
|
+
for (const arm of node.arms) {
|
|
731
|
+
const condition = this.atZoom(arm.when, zoom);
|
|
732
|
+
walk(arm.value, combine("all", [remaining, condition]));
|
|
733
|
+
remaining = combine("all", [remaining, negate(condition)]);
|
|
734
|
+
}
|
|
735
|
+
walk(node.fallback, remaining);
|
|
736
|
+
} else if (operation(node.input, "zoom", 1)) {
|
|
737
|
+
let selected = node.fallback;
|
|
738
|
+
for (const stop of node.stops) {
|
|
739
|
+
if (zoom < stop.at) break;
|
|
740
|
+
selected = stop.value;
|
|
741
|
+
}
|
|
742
|
+
walk(selected, guard);
|
|
743
|
+
} else {
|
|
744
|
+
let previous = node.fallback;
|
|
745
|
+
let remaining = guard;
|
|
746
|
+
for (const stop of node.stops) {
|
|
747
|
+
const below = ["<", node.input, stop.at];
|
|
748
|
+
walk(previous, combine("all", [remaining, below]));
|
|
749
|
+
remaining = combine("all", [remaining, negate(below)]);
|
|
750
|
+
previous = stop.value;
|
|
751
|
+
}
|
|
752
|
+
walk(previous, remaining);
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
walk(tree, true);
|
|
756
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
757
|
+
for (const outcome of leaves) {
|
|
758
|
+
const id = key(outcome.value);
|
|
759
|
+
if (!grouped.has(id)) grouped.set(id, { when: true, value: outcome.value });
|
|
760
|
+
}
|
|
761
|
+
if (grouped.size > nativeLoweringLimits.maximumPropertyBranches) fail(this.path, "budget");
|
|
762
|
+
return [...grouped.entries()].map(([id, outcome]) => ({
|
|
763
|
+
value: outcome.value,
|
|
764
|
+
when: this.accepts(tree, id, zoom)
|
|
765
|
+
}));
|
|
766
|
+
}
|
|
767
|
+
accepts(tree, outcome, zoom) {
|
|
768
|
+
if (tree.kind === "leaf") return key(tree.value) === outcome;
|
|
769
|
+
if (tree.kind === "case") {
|
|
770
|
+
return decision(
|
|
771
|
+
tree.arms.map((arm) => ({
|
|
772
|
+
when: this.atZoom(arm.when, zoom),
|
|
773
|
+
value: this.accepts(arm.value, outcome, zoom)
|
|
774
|
+
})),
|
|
775
|
+
this.accepts(tree.fallback, outcome, zoom)
|
|
776
|
+
);
|
|
777
|
+
}
|
|
778
|
+
if (operation(tree.input, "zoom", 1)) {
|
|
779
|
+
let selected = tree.fallback;
|
|
780
|
+
for (const stop of tree.stops) {
|
|
781
|
+
if (zoom < stop.at) break;
|
|
782
|
+
selected = stop.value;
|
|
783
|
+
}
|
|
784
|
+
return this.accepts(selected, outcome, zoom);
|
|
785
|
+
}
|
|
786
|
+
return decision(
|
|
787
|
+
tree.stops.map((stop, index) => ({
|
|
788
|
+
when: ["<", tree.input, stop.at],
|
|
789
|
+
value: this.accepts(
|
|
790
|
+
index === 0 ? tree.fallback : tree.stops[index - 1].value,
|
|
791
|
+
outcome,
|
|
792
|
+
zoom
|
|
793
|
+
)
|
|
794
|
+
})),
|
|
795
|
+
this.accepts(tree.stops.at(-1).value, outcome, zoom)
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
};
|
|
799
|
+
function isConstant(value, property) {
|
|
800
|
+
return value === void 0 || (property === "line-cap" ? typeof value === "string" : Array.isArray(value) && value.every((item) => typeof item === "number"));
|
|
801
|
+
}
|
|
802
|
+
function lowerLayer(layer, index, vector) {
|
|
803
|
+
if (layer.type !== "line") return;
|
|
804
|
+
const properties = [];
|
|
805
|
+
let cap;
|
|
806
|
+
let dash;
|
|
807
|
+
for (const [group, property] of [
|
|
808
|
+
["layout", "line-cap"],
|
|
809
|
+
["paint", "line-dasharray"]
|
|
810
|
+
]) {
|
|
811
|
+
const value = layer[group]?.[property];
|
|
812
|
+
if (isConstant(value, property)) continue;
|
|
813
|
+
const decisions = new Decisions(`/layers/${index}/${group}/${property}`, vector);
|
|
814
|
+
const tree = decisions.tree(value, property);
|
|
815
|
+
properties.push(property);
|
|
816
|
+
if (property === "line-cap") cap = { decisions, tree };
|
|
817
|
+
else dash = { decisions, tree };
|
|
818
|
+
}
|
|
819
|
+
if (!properties.length) return;
|
|
820
|
+
const minimum = layer.minzoom ?? 0;
|
|
821
|
+
const maximum = layer.maxzoom ?? 24;
|
|
822
|
+
if (!finite(minimum) || !finite(maximum) || minimum < 0 || maximum > 24 || minimum >= maximum) {
|
|
823
|
+
fail(`/layers/${index}/minzoom`);
|
|
824
|
+
}
|
|
825
|
+
const cuts = [
|
|
826
|
+
.../* @__PURE__ */ new Set([
|
|
827
|
+
minimum,
|
|
828
|
+
maximum,
|
|
829
|
+
...[...cap?.decisions.cuts ?? [], ...dash?.decisions.cuts ?? []].filter(
|
|
830
|
+
(cut) => cut > minimum && cut < maximum
|
|
831
|
+
)
|
|
832
|
+
])
|
|
833
|
+
].sort((a, b) => a - b);
|
|
834
|
+
if (cuts.length - 1 > nativeLoweringLimits.maximumZoomIntervals)
|
|
835
|
+
fail(`/layers/${index}`, "budget");
|
|
836
|
+
const variants = [];
|
|
837
|
+
const tails = /* @__PURE__ */ new Map();
|
|
838
|
+
for (let part = 0; part < cuts.length - 1; part++) {
|
|
839
|
+
const low = cuts[part];
|
|
840
|
+
const high = cuts[part + 1];
|
|
841
|
+
const caps = cap ? cap.decisions.outcomes(cap.tree, Math.floor(low)) : [{ when: true, value: void 0 }];
|
|
842
|
+
const dashes = dash ? dash.decisions.outcomes(dash.tree, Math.floor(low)) : [{ when: true, value: void 0 }];
|
|
843
|
+
for (const c of caps)
|
|
844
|
+
for (const d of dashes) {
|
|
845
|
+
const when = combine("all", [c.when, d.when]);
|
|
846
|
+
if (when === false) continue;
|
|
847
|
+
const signature = key([when, c.value, d.value]);
|
|
848
|
+
const tail = tails.get(signature);
|
|
849
|
+
if (tail && tail.maximum === low) {
|
|
850
|
+
tail.maximum = high;
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
const variant = {
|
|
854
|
+
minimum: low,
|
|
855
|
+
maximum: high,
|
|
856
|
+
when,
|
|
857
|
+
...cap ? { cap: c.value } : {},
|
|
858
|
+
...dash ? { dash: d.value } : {}
|
|
859
|
+
};
|
|
860
|
+
variants.push(variant);
|
|
861
|
+
tails.set(signature, variant);
|
|
862
|
+
if (variants.length > nativeLoweringLimits.maximumLayerVariants)
|
|
863
|
+
fail(`/layers/${index}`, "budget");
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
return { variants, properties };
|
|
867
|
+
}
|
|
868
|
+
function lowerNativeStyleRepresentation(input, normalizeFilter = (filter) => filter) {
|
|
869
|
+
if (!Array.isArray(input.layers) || input.layers.length > nativeLoweringLimits.maximumLayers)
|
|
870
|
+
fail("/layers", "budget");
|
|
871
|
+
const originalLayers = input.layers;
|
|
872
|
+
const reserved = /* @__PURE__ */ new Set();
|
|
873
|
+
for (const [index, layer] of originalLayers.entries()) {
|
|
874
|
+
if (!object(layer) || typeof layer.id !== "string" || reserved.has(layer.id))
|
|
875
|
+
fail(`/layers/${index}/id`);
|
|
876
|
+
reserved.add(layer.id);
|
|
877
|
+
}
|
|
878
|
+
let projection = "none";
|
|
879
|
+
if (input.projection !== void 0) {
|
|
880
|
+
if (!object(input.projection) || Object.keys(input.projection).length !== 1 || typeof input.projection.type !== "string" || !["globe", "mercator"].includes(input.projection.type))
|
|
881
|
+
fail("/projection");
|
|
882
|
+
projection = input.projection.type === "globe" ? "globe-to-mercator" : "mercator-to-implicit";
|
|
883
|
+
}
|
|
884
|
+
const layers = [];
|
|
885
|
+
const transformations = [];
|
|
886
|
+
for (const [index, layer] of originalLayers.entries()) {
|
|
887
|
+
const source = object(input.sources) && typeof layer.source === "string" ? input.sources[layer.source] : void 0;
|
|
888
|
+
const lowered = lowerLayer(layer, index, object(source) && source.type === "vector");
|
|
889
|
+
if (!lowered) {
|
|
890
|
+
layers.push(layer);
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
if (layers.length + lowered.variants.length + originalLayers.length - index - 1 > nativeLoweringLimits.maximumLayers) {
|
|
894
|
+
fail(`/layers/${index}`, "budget");
|
|
895
|
+
}
|
|
896
|
+
const originalFilter = layer.filter === void 0 ? void 0 : normalizeFilter(layer.filter);
|
|
897
|
+
const outputStart = layers.length;
|
|
898
|
+
for (const [branch, variant] of lowered.variants.entries()) {
|
|
899
|
+
const stem = `${layer.id}--native-v1-${branch}`;
|
|
900
|
+
let id = stem;
|
|
901
|
+
let collision = 0;
|
|
902
|
+
while (reserved.has(id)) id = `${stem}-${++collision}`;
|
|
903
|
+
reserved.add(id);
|
|
904
|
+
const next = { ...layer, id };
|
|
905
|
+
if (variant.minimum !== (layer.minzoom ?? 0)) next.minzoom = variant.minimum;
|
|
906
|
+
if (variant.maximum !== (layer.maxzoom ?? 24)) next.maxzoom = variant.maximum;
|
|
907
|
+
if (variant.when !== true)
|
|
908
|
+
next.filter = originalFilter === void 0 ? variant.when : ["all", originalFilter, variant.when];
|
|
909
|
+
if (variant.cap !== void 0) next.layout = { ...layer.layout, "line-cap": variant.cap };
|
|
910
|
+
if (variant.dash !== void 0) next.paint = { ...layer.paint, "line-dasharray": variant.dash };
|
|
911
|
+
layers.push(next);
|
|
912
|
+
}
|
|
913
|
+
transformations.push({
|
|
914
|
+
inputLayer: index,
|
|
915
|
+
outputStart,
|
|
916
|
+
outputCount: lowered.variants.length,
|
|
917
|
+
properties: lowered.properties
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
const changed = projection !== "none" || transformations.length > 0;
|
|
921
|
+
const output = changed ? { ...input, layers } : input;
|
|
922
|
+
if (changed && projection !== "none") delete output.projection;
|
|
923
|
+
return {
|
|
924
|
+
style: output,
|
|
925
|
+
projection,
|
|
926
|
+
layers: transformations,
|
|
927
|
+
inputLayers: originalLayers.length,
|
|
928
|
+
outputLayers: layers.length
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// src/native-artifacts.ts
|
|
933
|
+
function documentUrl(map, theme) {
|
|
934
|
+
return `https://artifacts.invalid/native/styles/${map}/${theme}.json`;
|
|
935
|
+
}
|
|
936
|
+
function withContext(issue, map, theme) {
|
|
937
|
+
const prefix = `/maps/${map}/themes/${theme}/style`;
|
|
938
|
+
return { ...issue, path: prefix.length + issue.path.length <= 300 ? prefix + issue.path : prefix };
|
|
939
|
+
}
|
|
940
|
+
function lowerTileflowNativeCompiledStyles(input) {
|
|
941
|
+
const styles = {};
|
|
942
|
+
const transformations = [];
|
|
943
|
+
for (const map of Object.keys(input).sort()) {
|
|
944
|
+
styles[map] = {};
|
|
945
|
+
for (const theme of Object.keys(input[map]).sort()) {
|
|
946
|
+
const original = input[map][theme];
|
|
947
|
+
try {
|
|
948
|
+
const options = { documentUrl: documentUrl(map, theme), deferFontClosure: true };
|
|
949
|
+
const pending = validateTileflowNativeStyle(original, options);
|
|
950
|
+
const blockers = pending.filter(
|
|
951
|
+
(issue) => issue.code !== "NATIVE_UNSUPPORTED_STYLE" || !(issue.path === "/projection" || /^\/layers\/\d+\/(?:layout\/line-cap|paint\/line-dasharray)(?:\/|$)/u.test(issue.path))
|
|
952
|
+
);
|
|
953
|
+
if (blockers.length)
|
|
954
|
+
throw new TileflowNativeCompatibilityError(
|
|
955
|
+
blockers.map((issue) => withContext(issue, map, theme))
|
|
956
|
+
);
|
|
957
|
+
const syntax = validateStyleMin(JSON.parse(serializeCanonicalJson(original)));
|
|
958
|
+
if (syntax.length)
|
|
959
|
+
throw new TileflowNativeCompatibilityError(
|
|
960
|
+
syntax.map((issue) => {
|
|
961
|
+
const match = /^layers\[(\d+)\]\.(layout\.line-cap|paint\.line-dasharray)(?=[:.[]|$)/u.exec(
|
|
962
|
+
issue.message
|
|
963
|
+
);
|
|
964
|
+
const path = match ? `/layers/${match[1]}/${match[2].replace(".", "/")}` : issue.message.startsWith("projection") ? "/projection" : "";
|
|
965
|
+
return withContext(
|
|
966
|
+
createTileflowNativeDiagnostic("NATIVE_UNSUPPORTED_STYLE", path),
|
|
967
|
+
map,
|
|
968
|
+
theme
|
|
969
|
+
);
|
|
970
|
+
})
|
|
971
|
+
);
|
|
972
|
+
const lowered = lowerNativeStyleRepresentation(
|
|
973
|
+
original,
|
|
974
|
+
(filter) => convertFilter(structuredClone(filter))
|
|
975
|
+
);
|
|
976
|
+
const issues = validateTileflowNativePreparedStyle(lowered.style, options);
|
|
977
|
+
if (issues.length)
|
|
978
|
+
throw new TileflowNativeCompatibilityError(
|
|
979
|
+
issues.map((issue) => withContext(issue, map, theme))
|
|
980
|
+
);
|
|
981
|
+
styles[map][theme] = lowered.style;
|
|
982
|
+
transformations.push({
|
|
983
|
+
map,
|
|
984
|
+
theme,
|
|
985
|
+
inputStyleSha256: createHash("sha256").update(serializeCanonicalJson(original)).digest("hex"),
|
|
986
|
+
loweredStyleSha256: createHash("sha256").update(serializeCanonicalJson(lowered.style)).digest("hex"),
|
|
987
|
+
inputLayers: lowered.inputLayers,
|
|
988
|
+
outputLayers: lowered.outputLayers,
|
|
989
|
+
projection: lowered.projection,
|
|
990
|
+
layers: lowered.layers
|
|
991
|
+
});
|
|
992
|
+
} catch (error) {
|
|
993
|
+
if (!(error instanceof NativeLoweringError)) throw error;
|
|
994
|
+
throw new TileflowNativeCompatibilityError([
|
|
995
|
+
withContext(
|
|
996
|
+
{
|
|
997
|
+
...createTileflowNativeDiagnostic("NATIVE_UNSUPPORTED_STYLE", error.path),
|
|
998
|
+
message: error.message,
|
|
999
|
+
suggestion: error.reason === "budget" ? "Reduce the decision expansion or style size; native lowering never raises its resource limits automatically." : error.path === "/projection" ? "Use a fixed globe or Mercator projection for native-v1; adaptive projections are not supported." : "Use finite case/match/step decisions with constant leaves and supported scalar predicates, or select web."
|
|
1000
|
+
},
|
|
1001
|
+
map,
|
|
1002
|
+
theme
|
|
1003
|
+
)
|
|
1004
|
+
]);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
return { styles, transformations };
|
|
1009
|
+
}
|
|
1010
|
+
function assertTileflowNativeCompiledStyles(project, styles) {
|
|
1011
|
+
const issues = [];
|
|
1012
|
+
for (const mapName of Object.keys(project.maps).sort()) {
|
|
1013
|
+
for (const theme of Object.keys(styles[mapName] ?? {}).sort()) {
|
|
1014
|
+
issues.push(
|
|
1015
|
+
...validateTileflowNativePreparedStyle(styles[mapName][theme], {
|
|
1016
|
+
documentUrl: documentUrl(mapName, theme),
|
|
1017
|
+
deferFontClosure: true
|
|
1018
|
+
}).map((issue) => withContext(issue, mapName, theme))
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
if (issues.length) throw new TileflowNativeCompatibilityError(issues);
|
|
1023
|
+
}
|
|
1024
|
+
function prepareTileflowNativeStyles(input, assets) {
|
|
1025
|
+
const issues = [];
|
|
1026
|
+
const result = {};
|
|
1027
|
+
for (const map of Object.keys(input).sort()) {
|
|
1028
|
+
result[map] = {};
|
|
1029
|
+
if (!validNativeSprites(map, assets)) {
|
|
1030
|
+
issues.push({
|
|
1031
|
+
...createTileflowNativeDiagnostic("NATIVE_UNSUPPORTED_STYLE", `/maps/${map}/icons`),
|
|
1032
|
+
suggestion: "Rebuild the icon sources into a complete, bounded 1x/2x sprite pair."
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
for (const theme of Object.keys(input[map]).sort()) {
|
|
1036
|
+
const original = input[map][theme];
|
|
1037
|
+
const preflight = validateTileflowNativePreparedStyle(original, {
|
|
1038
|
+
documentUrl: documentUrl(map, theme),
|
|
1039
|
+
deferFontClosure: true
|
|
1040
|
+
});
|
|
1041
|
+
if (preflight.length) {
|
|
1042
|
+
issues.push(...preflight.map((issue) => withContext(issue, map, theme)));
|
|
1043
|
+
continue;
|
|
1044
|
+
}
|
|
1045
|
+
const metadata = { ...original.metadata };
|
|
1046
|
+
const faces = getTileflowStyleFontFaces(original);
|
|
1047
|
+
const nativeFaces = {};
|
|
1048
|
+
for (const face of faces) {
|
|
1049
|
+
const asset = assets.find(
|
|
1050
|
+
(item) => item.fileName.startsWith("fonts/") && (face.source === item.fileName || face.source.endsWith(`/${item.fileName}`))
|
|
1051
|
+
);
|
|
1052
|
+
if (!asset || !isNativeFontAsset(asset)) {
|
|
1053
|
+
issues.push(
|
|
1054
|
+
withContext(
|
|
1055
|
+
createTileflowNativeDiagnostic("NATIVE_FONT_UNAVAILABLE", "/font-faces"),
|
|
1056
|
+
map,
|
|
1057
|
+
theme
|
|
1058
|
+
)
|
|
1059
|
+
);
|
|
1060
|
+
continue;
|
|
1061
|
+
}
|
|
1062
|
+
if (Object.hasOwn(nativeFaces, face.family)) {
|
|
1063
|
+
issues.push(
|
|
1064
|
+
withContext(
|
|
1065
|
+
createTileflowNativeDiagnostic("NATIVE_FONT_UNAVAILABLE", "/font-faces"),
|
|
1066
|
+
map,
|
|
1067
|
+
theme
|
|
1068
|
+
)
|
|
1069
|
+
);
|
|
1070
|
+
continue;
|
|
1071
|
+
}
|
|
1072
|
+
Object.defineProperty(nativeFaces, face.family, { value: face.source, enumerable: true });
|
|
1073
|
+
}
|
|
1074
|
+
delete metadata[tileflowStyleFontFacesMetadataKey];
|
|
1075
|
+
const { projection: _projection, ...rest } = original;
|
|
1076
|
+
const style = {
|
|
1077
|
+
...rest,
|
|
1078
|
+
metadata,
|
|
1079
|
+
...faces.length ? { "font-faces": nativeFaces } : {}
|
|
1080
|
+
};
|
|
1081
|
+
issues.push(
|
|
1082
|
+
...validateTileflowNativePreparedStyle(style, {
|
|
1083
|
+
documentUrl: documentUrl(map, theme)
|
|
1084
|
+
}).map((issue) => withContext(issue, map, theme))
|
|
1085
|
+
);
|
|
1086
|
+
result[map][theme] = style;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
if (issues.length) throw new TileflowNativeCompatibilityError(issues);
|
|
1090
|
+
return result;
|
|
1091
|
+
}
|
|
1092
|
+
function isNativeFontAsset(asset) {
|
|
1093
|
+
const bytes = typeof asset.source === "string" ? Buffer.from(asset.source) : asset.source;
|
|
1094
|
+
if (bytes.byteLength < 12 || bytes.byteLength > tileflowNativeProfileLimits.maximumFontBytes)
|
|
1095
|
+
return false;
|
|
1096
|
+
const signature = Buffer.from(bytes.subarray(0, 4)).toString("hex");
|
|
1097
|
+
if (!/\.(?:ttf|otf)$/u.test(asset.fileName) || !["00010000", "4f54544f"].includes(signature))
|
|
1098
|
+
return false;
|
|
1099
|
+
const expectedHash = /-([a-f0-9]{64})\.(?:ttf|otf)$/u.exec(asset.fileName)?.[1];
|
|
1100
|
+
return expectedHash === createHash("sha256").update(bytes).digest("hex");
|
|
1101
|
+
}
|
|
1102
|
+
function assertTileflowNativeGeneratedStyle(style, map, theme) {
|
|
1103
|
+
const issues = validateTileflowNativePreparedStyle(style, { documentUrl: documentUrl(map, theme) });
|
|
1104
|
+
if (issues.length) {
|
|
1105
|
+
throw new TileflowNativeCompatibilityError(
|
|
1106
|
+
issues.map((issue) => withContext(issue, map, theme))
|
|
1107
|
+
);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
function replaceTileflowNativeFontSources(style, replace) {
|
|
1111
|
+
const faces = style["font-faces"];
|
|
1112
|
+
if (!faces) return style;
|
|
1113
|
+
return {
|
|
1114
|
+
...style,
|
|
1115
|
+
"font-faces": Object.fromEntries(
|
|
1116
|
+
Object.entries(faces).map(([name, source]) => [name, replace(source)])
|
|
1117
|
+
)
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
function validNativeSprites(map, assets) {
|
|
1121
|
+
const prefix = `icons/${map}/`;
|
|
1122
|
+
const files = assets.filter((asset) => asset.fileName.startsWith(prefix));
|
|
1123
|
+
if (!files.length) return true;
|
|
1124
|
+
try {
|
|
1125
|
+
if (files.length !== 4) return false;
|
|
1126
|
+
let totalBytes = 0;
|
|
1127
|
+
const indexes = [];
|
|
1128
|
+
for (const ratio of [1, 2]) {
|
|
1129
|
+
const suffix = ratio === 1 ? "" : "@2x";
|
|
1130
|
+
const image = files.find((asset) => asset.fileName === `${prefix}sprite${suffix}.png`);
|
|
1131
|
+
const index = files.find((asset) => asset.fileName === `${prefix}sprite${suffix}.json`);
|
|
1132
|
+
if (!image || !index) return false;
|
|
1133
|
+
const imageBytes = Buffer.from(
|
|
1134
|
+
typeof image.source === "string" ? new TextEncoder().encode(image.source) : image.source
|
|
1135
|
+
);
|
|
1136
|
+
const indexBytes = Buffer.from(
|
|
1137
|
+
typeof index.source === "string" ? new TextEncoder().encode(index.source) : index.source
|
|
1138
|
+
);
|
|
1139
|
+
for (const bytes of [imageBytes, indexBytes]) {
|
|
1140
|
+
if (bytes.length > tileflowIconPackageLimits.maxGeneratedFileBytes) return false;
|
|
1141
|
+
totalBytes += bytes.length;
|
|
1142
|
+
}
|
|
1143
|
+
if (imageBytes.length < 33 || imageBytes.subarray(0, 8).toString("hex") !== "89504e470d0a1a0a" || imageBytes.toString("ascii", 12, 16) !== "IHDR")
|
|
1144
|
+
return false;
|
|
1145
|
+
const width = imageBytes.readUInt32BE(16);
|
|
1146
|
+
const height = imageBytes.readUInt32BE(20);
|
|
1147
|
+
if (!width || !height || width > tileflowIconPackageLimits.maxAtlasDimension || height > tileflowIconPackageLimits.maxAtlasDimension)
|
|
1148
|
+
return false;
|
|
1149
|
+
const data = JSON.parse(indexBytes.toString("utf8"));
|
|
1150
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) return false;
|
|
1151
|
+
const names = Object.keys(data).sort();
|
|
1152
|
+
if (!names.length || names.length > tileflowIconPackageLimits.maxIconCount) return false;
|
|
1153
|
+
indexes.push(names);
|
|
1154
|
+
for (const name of names) {
|
|
1155
|
+
if (!tileflowIconIdPattern.test(name)) return false;
|
|
1156
|
+
const entry = data[name];
|
|
1157
|
+
if (!entry || entry.pixelRatio !== ratio) return false;
|
|
1158
|
+
const { x, y, width: w, height: h } = entry;
|
|
1159
|
+
if (![x, y, w, h].every((value) => typeof value === "number" && Number.isSafeInteger(value)))
|
|
1160
|
+
return false;
|
|
1161
|
+
if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > width || y + h > height)
|
|
1162
|
+
return false;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
return totalBytes <= tileflowIconPackageLimits.maxGeneratedPackageBytes && JSON.stringify(indexes[0]) === JSON.stringify(indexes[1]);
|
|
1166
|
+
} catch {
|
|
1167
|
+
return false;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
378
1171
|
// src/session.ts
|
|
379
1172
|
import { watch } from "chokidar";
|
|
380
|
-
import { realpathSync } from "fs";
|
|
381
|
-
import { dirname, extname, isAbsolute, relative, resolve as resolve2, sep, win32 } from "path";
|
|
1173
|
+
import { realpathSync, statSync } from "fs";
|
|
1174
|
+
import { basename as basename2, dirname, extname, isAbsolute, relative, resolve as resolve2, sep, win32 } from "path";
|
|
1175
|
+
import { tileflowIconMetadataFileName } from "@tileflow/core";
|
|
1176
|
+
import { TileflowNativeCompatibilityError as TileflowNativeCompatibilityError2 } from "@tileflow/core/native-profile";
|
|
382
1177
|
var tileflowArtifactSessionSchemaVersion = 1;
|
|
383
1178
|
var configExtensions = /* @__PURE__ */ new Set([".cjs", ".cts", ".js", ".json", ".mjs", ".mts", ".ts", ".tsx"]);
|
|
384
1179
|
var iconExtensions = /* @__PURE__ */ new Set([".jpeg", ".jpg", ".png", ".svg", ".webp"]);
|
|
385
1180
|
var fontExtensions = /* @__PURE__ */ new Set([".otf", ".ttf", ".woff2"]);
|
|
1181
|
+
var writeStabilityMs = 50;
|
|
386
1182
|
var excludedDirectories = /* @__PURE__ */ new Set([
|
|
387
1183
|
".cache",
|
|
388
1184
|
".git",
|
|
@@ -416,6 +1212,7 @@ var TileflowArtifactSessionImpl = class {
|
|
|
416
1212
|
#closed = false;
|
|
417
1213
|
#closing;
|
|
418
1214
|
#debounceTimer;
|
|
1215
|
+
#startupCheckTimer;
|
|
419
1216
|
#generation = 0;
|
|
420
1217
|
#current;
|
|
421
1218
|
#artifactGenerations = /* @__PURE__ */ new Set();
|
|
@@ -424,13 +1221,17 @@ var TileflowArtifactSessionImpl = class {
|
|
|
424
1221
|
#refreshToken = 0;
|
|
425
1222
|
#state = { generation: 0, status: "building" };
|
|
426
1223
|
#watcher;
|
|
1224
|
+
#watchedArtifactDirectories = /* @__PURE__ */ new Map();
|
|
427
1225
|
#watchedArtifactPaths = /* @__PURE__ */ new Set();
|
|
428
1226
|
constructor(options, buildArtifacts, discoverWatchPaths) {
|
|
429
1227
|
this.#cwd = resolve2(options.cwd ?? process.cwd());
|
|
430
|
-
|
|
1228
|
+
const configPath = resolve2(this.#cwd, options.config ?? "tileflow.config.ts");
|
|
1229
|
+
this.#configPath = resolve2(canonicalWatchPath(dirname(configPath)), basename2(configPath));
|
|
431
1230
|
this.#debounceMs = Math.max(0, Math.min(options.debounceMs ?? 75, 1e3));
|
|
432
1231
|
this.#watchEnabled = options.watch ?? false;
|
|
433
|
-
this.#ignoredPaths = (options.ignoredPaths ?? []).map(
|
|
1232
|
+
this.#ignoredPaths = (options.ignoredPaths ?? []).map(
|
|
1233
|
+
(path) => canonicalWatchPath(resolve2(this.#cwd, path))
|
|
1234
|
+
);
|
|
434
1235
|
this.#buildArtifacts = buildArtifacts;
|
|
435
1236
|
this.#discoverWatchPaths = discoverWatchPaths;
|
|
436
1237
|
this.#buildOptions = {
|
|
@@ -438,7 +1239,10 @@ var TileflowArtifactSessionImpl = class {
|
|
|
438
1239
|
assetBaseUrl: options.assetBaseUrl,
|
|
439
1240
|
config: options.config,
|
|
440
1241
|
cwd: this.#cwd,
|
|
1242
|
+
// Trusted shared Icon Set resolution settings, supplied once by the owning command.
|
|
1243
|
+
...options.icons ? { icons: options.icons } : {},
|
|
441
1244
|
inspection: options.inspection,
|
|
1245
|
+
...options.renderer === void 0 ? {} : { renderer: options.renderer },
|
|
442
1246
|
styleBaseUrl: options.styleBaseUrl,
|
|
443
1247
|
target: options.target
|
|
444
1248
|
};
|
|
@@ -446,7 +1250,7 @@ var TileflowArtifactSessionImpl = class {
|
|
|
446
1250
|
async start() {
|
|
447
1251
|
if (this.#watchEnabled) {
|
|
448
1252
|
this.#watcher = watch(dirname(this.#configPath), {
|
|
449
|
-
awaitWriteFinish: { pollInterval: 20, stabilityThreshold:
|
|
1253
|
+
awaitWriteFinish: { pollInterval: 20, stabilityThreshold: writeStabilityMs },
|
|
450
1254
|
followSymlinks: false,
|
|
451
1255
|
ignoreInitial: true,
|
|
452
1256
|
ignored: (path, stats) => this.#ignoreWatchPath(path, stats?.isDirectory()),
|
|
@@ -474,6 +1278,22 @@ var TileflowArtifactSessionImpl = class {
|
|
|
474
1278
|
if (!this.#shouldRefreshForWatchEvent(path)) return;
|
|
475
1279
|
this.#scheduleRefresh(path);
|
|
476
1280
|
});
|
|
1281
|
+
this.#watcher.on("raw", (event, path, details) => {
|
|
1282
|
+
if (event !== "rename" && event !== "change") return;
|
|
1283
|
+
const resolvedPath = resolveRawWatchPath(path, details);
|
|
1284
|
+
if (!resolvedPath || this.#ignoreWatchPath(resolvedPath, void 0)) return;
|
|
1285
|
+
const directoryChanged = this.#watchedArtifactDirectories.has(resolvedPath);
|
|
1286
|
+
const metadataChanged = event === "rename" && basename2(resolvedPath) === tileflowIconMetadataFileName && [...this.#watchedArtifactDirectories.keys()].some(
|
|
1287
|
+
(directory) => isSameOrInside(directory, resolvedPath)
|
|
1288
|
+
);
|
|
1289
|
+
if (!directoryChanged && !metadataChanged) return;
|
|
1290
|
+
if (directoryChanged && event === "change") {
|
|
1291
|
+
const modified = directoryModificationTime(resolvedPath);
|
|
1292
|
+
if (modified === this.#watchedArtifactDirectories.get(resolvedPath)) return;
|
|
1293
|
+
this.#watchedArtifactDirectories.set(resolvedPath, modified);
|
|
1294
|
+
}
|
|
1295
|
+
this.#scheduleRefresh(resolvedPath);
|
|
1296
|
+
});
|
|
477
1297
|
this.#watcher.on("error", (error) => {
|
|
478
1298
|
if (this.#closed) return;
|
|
479
1299
|
this.#refreshToken += 1;
|
|
@@ -482,6 +1302,19 @@ var TileflowArtifactSessionImpl = class {
|
|
|
482
1302
|
await watcherReady;
|
|
483
1303
|
}
|
|
484
1304
|
await this.refresh("initial");
|
|
1305
|
+
if (this.#watcher && !this.#closed) {
|
|
1306
|
+
this.#startupCheckTimer = setTimeout(() => {
|
|
1307
|
+
this.#startupCheckTimer = void 0;
|
|
1308
|
+
if (this.#closed) return;
|
|
1309
|
+
for (const [path, observed] of this.#watchedArtifactDirectories) {
|
|
1310
|
+
if (this.#ignoreWatchPath(path, true)) continue;
|
|
1311
|
+
const modified = directoryModificationTime(path);
|
|
1312
|
+
if (modified === observed) continue;
|
|
1313
|
+
this.#watchedArtifactDirectories.set(path, modified);
|
|
1314
|
+
this.#scheduleRefresh(path);
|
|
1315
|
+
}
|
|
1316
|
+
}, writeStabilityMs);
|
|
1317
|
+
}
|
|
485
1318
|
}
|
|
486
1319
|
getState() {
|
|
487
1320
|
return this.#state;
|
|
@@ -575,6 +1408,7 @@ var TileflowArtifactSessionImpl = class {
|
|
|
575
1408
|
this.#closed = true;
|
|
576
1409
|
this.#refreshToken += 1;
|
|
577
1410
|
if (this.#debounceTimer) clearTimeout(this.#debounceTimer);
|
|
1411
|
+
if (this.#startupCheckTimer) clearTimeout(this.#startupCheckTimer);
|
|
578
1412
|
this.#listeners.clear();
|
|
579
1413
|
await this.#watcher?.close();
|
|
580
1414
|
const retained = [...this.#artifactGenerations];
|
|
@@ -587,7 +1421,7 @@ var TileflowArtifactSessionImpl = class {
|
|
|
587
1421
|
await Promise.all(retained.map((generation) => generation.completion));
|
|
588
1422
|
}
|
|
589
1423
|
#ignoreWatchPath(path, isDirectory) {
|
|
590
|
-
const resolvedPath =
|
|
1424
|
+
const resolvedPath = canonicalWatchPath(path);
|
|
591
1425
|
const relativePath = relative(dirname(this.#configPath), resolvedPath);
|
|
592
1426
|
const segments = relativePath.split(sep).filter(Boolean);
|
|
593
1427
|
if (this.#ignoredPaths.some((path2) => isSameOrInside(path2, resolvedPath))) return true;
|
|
@@ -601,7 +1435,7 @@ var TileflowArtifactSessionImpl = class {
|
|
|
601
1435
|
return true;
|
|
602
1436
|
}
|
|
603
1437
|
#shouldRefreshForWatchEvent(path) {
|
|
604
|
-
const resolvedPath =
|
|
1438
|
+
const resolvedPath = canonicalWatchPath(path);
|
|
605
1439
|
const extension = extname(resolvedPath).toLowerCase();
|
|
606
1440
|
if (!iconExtensions.has(extension) && !fontExtensions.has(extension)) return true;
|
|
607
1441
|
return [...this.#watchedArtifactPaths].some((path2) => isSameOrInside(path2, resolvedPath));
|
|
@@ -629,9 +1463,20 @@ var TileflowArtifactSessionImpl = class {
|
|
|
629
1463
|
}
|
|
630
1464
|
#updateArtifactWatchPaths(paths) {
|
|
631
1465
|
if (!this.#watcher) return;
|
|
632
|
-
const next = new Set(paths.map((path) => resolve2(this.#cwd, path)));
|
|
1466
|
+
const next = new Set(paths.map((path) => canonicalWatchPath(resolve2(this.#cwd, path))));
|
|
633
1467
|
const previous = this.#watchedArtifactPaths;
|
|
634
1468
|
this.#watchedArtifactPaths = next;
|
|
1469
|
+
const previousDirectories = this.#watchedArtifactDirectories;
|
|
1470
|
+
this.#watchedArtifactDirectories = /* @__PURE__ */ new Map();
|
|
1471
|
+
for (const path of next) {
|
|
1472
|
+
const modified = directoryModificationTime(path);
|
|
1473
|
+
if (modified !== void 0) {
|
|
1474
|
+
this.#watchedArtifactDirectories.set(
|
|
1475
|
+
path,
|
|
1476
|
+
previousDirectories.has(path) ? previousDirectories.get(path) : modified
|
|
1477
|
+
);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
635
1480
|
for (const path of previous) {
|
|
636
1481
|
if (!next.has(path) && !isSameOrInside(dirname(this.#configPath), path)) {
|
|
637
1482
|
void this.#watcher.unwatch(path);
|
|
@@ -686,7 +1531,41 @@ function isSameOrInside(root, candidate) {
|
|
|
686
1531
|
const path = relative(resolve2(root), resolve2(candidate));
|
|
687
1532
|
return path === "" || !isAbsolute(path) && path !== ".." && !path.startsWith(`..${sep}`);
|
|
688
1533
|
}
|
|
1534
|
+
function directoryModificationTime(path) {
|
|
1535
|
+
try {
|
|
1536
|
+
const stats = statSync(path);
|
|
1537
|
+
return stats.isDirectory() ? stats.mtimeMs : void 0;
|
|
1538
|
+
} catch {
|
|
1539
|
+
return void 0;
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
function resolveRawWatchPath(path, details) {
|
|
1543
|
+
if (typeof path !== "string" || !path) return void 0;
|
|
1544
|
+
if (isAbsolute(path)) return canonicalWatchPath(path);
|
|
1545
|
+
if (!details || typeof details !== "object") return void 0;
|
|
1546
|
+
const watchedPath = details.watchedPath;
|
|
1547
|
+
if (typeof watchedPath !== "string" || !isAbsolute(watchedPath)) return void 0;
|
|
1548
|
+
return canonicalWatchPath(
|
|
1549
|
+
basename2(watchedPath) === path ? watchedPath : resolve2(watchedPath, path)
|
|
1550
|
+
);
|
|
1551
|
+
}
|
|
1552
|
+
function canonicalWatchPath(path) {
|
|
1553
|
+
const resolved = resolve2(path);
|
|
1554
|
+
let ancestor = resolved;
|
|
1555
|
+
while (true) {
|
|
1556
|
+
try {
|
|
1557
|
+
return resolve2(realpathSync.native(ancestor), relative(ancestor, resolved));
|
|
1558
|
+
} catch {
|
|
1559
|
+
const parent = dirname(ancestor);
|
|
1560
|
+
if (parent === ancestor) return resolved;
|
|
1561
|
+
ancestor = parent;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
689
1565
|
function createTileflowArtifactDiagnostics(error, cwd) {
|
|
1566
|
+
if (error instanceof TileflowNativeCompatibilityError2) {
|
|
1567
|
+
return error.issues.map((issue) => ({ ...issue }));
|
|
1568
|
+
}
|
|
690
1569
|
const inheritedCode = optionalDiagnosticField(error, "code");
|
|
691
1570
|
const inheritedPath = optionalDiagnosticPath(error);
|
|
692
1571
|
const inheritedPhase = optionalDiagnosticField(error, "phase") ?? optionalNestedDiagnosticField(error, "phase");
|
|
@@ -717,8 +1596,8 @@ function createTileflowArtifactDiagnostics(error, cwd) {
|
|
|
717
1596
|
function getIssueList(error) {
|
|
718
1597
|
if (!error || typeof error !== "object") return [];
|
|
719
1598
|
const containers = [];
|
|
720
|
-
for (const
|
|
721
|
-
const value = error[
|
|
1599
|
+
for (const key2 of ["messages", "issues"]) {
|
|
1600
|
+
const value = error[key2];
|
|
722
1601
|
if (Array.isArray(value)) containers.push(value);
|
|
723
1602
|
}
|
|
724
1603
|
const details = error.details;
|
|
@@ -809,14 +1688,14 @@ function normalizeDiagnostics(diagnostics) {
|
|
|
809
1688
|
(diagnostic, index) => index === 0 || JSON.stringify(diagnostic) !== JSON.stringify(bounded[index - 1])
|
|
810
1689
|
).slice(0, 32);
|
|
811
1690
|
}
|
|
812
|
-
function optionalDiagnosticField(value,
|
|
1691
|
+
function optionalDiagnosticField(value, key2) {
|
|
813
1692
|
if (!value || typeof value !== "object") return void 0;
|
|
814
|
-
const field = value[
|
|
1693
|
+
const field = value[key2];
|
|
815
1694
|
return typeof field === "string" && field.trim() ? boundDiagnosticField(field) : void 0;
|
|
816
1695
|
}
|
|
817
|
-
function optionalNestedDiagnosticField(value,
|
|
1696
|
+
function optionalNestedDiagnosticField(value, key2) {
|
|
818
1697
|
if (!value || typeof value !== "object") return void 0;
|
|
819
|
-
return optionalDiagnosticField(value.details,
|
|
1698
|
+
return optionalDiagnosticField(value.details, key2);
|
|
820
1699
|
}
|
|
821
1700
|
function boundDiagnosticField(value) {
|
|
822
1701
|
return boundDiagnosticText(value.replace(/[^A-Za-z0-9_-]/g, ""), 64);
|
|
@@ -825,6 +1704,213 @@ function boundDiagnosticText(value, limit) {
|
|
|
825
1704
|
return value.length <= limit ? value : `${value.slice(0, limit - 1)}\u2026`;
|
|
826
1705
|
}
|
|
827
1706
|
|
|
1707
|
+
// src/hosted-native-artifacts.ts
|
|
1708
|
+
import { createHash as createHash2 } from "crypto";
|
|
1709
|
+
import { serializeCanonicalJson as serializeCanonicalJson3 } from "@tileflow/core";
|
|
1710
|
+
import {
|
|
1711
|
+
inferTileflowDataRequirements,
|
|
1712
|
+
inferTileflowSourceRequirements,
|
|
1713
|
+
parseTileflowRendererDeploymentArtifact
|
|
1714
|
+
} from "@tileflow/core/build";
|
|
1715
|
+
import {
|
|
1716
|
+
createTileflowNativeBuildRecord,
|
|
1717
|
+
TileflowNativeCompatibilityError as TileflowNativeCompatibilityError4,
|
|
1718
|
+
createTileflowNativeDiagnostic as createTileflowNativeDiagnostic3
|
|
1719
|
+
} from "@tileflow/core/native-profile";
|
|
1720
|
+
|
|
1721
|
+
// src/native-hosted-provenance.ts
|
|
1722
|
+
import { serializeCanonicalJson as serializeCanonicalJson2 } from "@tileflow/core";
|
|
1723
|
+
import {
|
|
1724
|
+
createTileflowNativeDiagnostic as createTileflowNativeDiagnostic2,
|
|
1725
|
+
TileflowNativeCompatibilityError as TileflowNativeCompatibilityError3
|
|
1726
|
+
} from "@tileflow/core/native-profile";
|
|
1727
|
+
var overlayKeys = [
|
|
1728
|
+
"above-water",
|
|
1729
|
+
"below-roads",
|
|
1730
|
+
"above-roads",
|
|
1731
|
+
"above-buildings",
|
|
1732
|
+
"below-labels",
|
|
1733
|
+
"above-labels"
|
|
1734
|
+
];
|
|
1735
|
+
function object2(value) {
|
|
1736
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error();
|
|
1737
|
+
return value;
|
|
1738
|
+
}
|
|
1739
|
+
function identifier(layer) {
|
|
1740
|
+
if (typeof layer.id !== "string" || !layer.id.length) throw new Error();
|
|
1741
|
+
return layer.id;
|
|
1742
|
+
}
|
|
1743
|
+
function finalizeNativeHostedProvenance(input, lowered, changes) {
|
|
1744
|
+
try {
|
|
1745
|
+
const result = JSON.parse(serializeCanonicalJson2(lowered));
|
|
1746
|
+
const originalIndexes = /* @__PURE__ */ new Map();
|
|
1747
|
+
const finalIndexes = /* @__PURE__ */ new Map();
|
|
1748
|
+
const starts = /* @__PURE__ */ new Map();
|
|
1749
|
+
const transformed = /* @__PURE__ */ new Map();
|
|
1750
|
+
let previous = -1;
|
|
1751
|
+
for (const change of changes) {
|
|
1752
|
+
if (!Number.isSafeInteger(change.inputLayer) || change.inputLayer <= previous || change.inputLayer >= input.layers.length || !Number.isSafeInteger(change.outputStart) || !Number.isSafeInteger(change.outputCount) || change.outputCount < 1 || change.outputCount > 32 || !Array.isArray(change.properties) || !["line-cap", "line-dasharray", "line-cap,line-dasharray"].includes(
|
|
1753
|
+
change.properties.join(",")
|
|
1754
|
+
))
|
|
1755
|
+
throw new Error();
|
|
1756
|
+
previous = change.inputLayer;
|
|
1757
|
+
transformed.set(change.inputLayer, change);
|
|
1758
|
+
}
|
|
1759
|
+
for (const [index, layer] of result.layers.entries()) {
|
|
1760
|
+
const id = identifier(layer);
|
|
1761
|
+
if (finalIndexes.has(id)) throw new Error();
|
|
1762
|
+
finalIndexes.set(id, index);
|
|
1763
|
+
}
|
|
1764
|
+
let offset = 0;
|
|
1765
|
+
for (const [index, layer] of input.layers.entries()) {
|
|
1766
|
+
const id = identifier(layer);
|
|
1767
|
+
if (originalIndexes.has(id)) throw new Error();
|
|
1768
|
+
originalIndexes.set(id, index);
|
|
1769
|
+
starts.set(id, offset);
|
|
1770
|
+
const change = transformed.get(index);
|
|
1771
|
+
if (change && (change.outputStart !== offset || layer.type !== "line")) throw new Error();
|
|
1772
|
+
const count = change?.outputCount ?? 1;
|
|
1773
|
+
if (offset + count > result.layers.length) throw new Error();
|
|
1774
|
+
for (let branch = 0; branch < count; branch++) {
|
|
1775
|
+
const candidate = result.layers[offset + branch];
|
|
1776
|
+
if (!change && identifier(candidate) !== id) throw new Error();
|
|
1777
|
+
if (candidate.type !== layer.type || candidate.source !== layer.source || candidate["source-layer"] !== layer["source-layer"])
|
|
1778
|
+
throw new Error();
|
|
1779
|
+
}
|
|
1780
|
+
offset += count;
|
|
1781
|
+
}
|
|
1782
|
+
if (offset !== result.layers.length) throw new Error();
|
|
1783
|
+
const metadata = result.metadata === void 0 ? void 0 : object2(result.metadata);
|
|
1784
|
+
const originalMetadata = input.metadata === void 0 ? void 0 : object2(input.metadata);
|
|
1785
|
+
if (originalMetadata?.["tileflow:overlay-placement-manifest"] !== void 0) {
|
|
1786
|
+
if (!metadata) throw new Error();
|
|
1787
|
+
const overlay = object2(originalMetadata["tileflow:overlay-placement-manifest"]);
|
|
1788
|
+
const anchors = object2(overlay.anchors);
|
|
1789
|
+
if (overlay.schemaVersion !== 1 || Object.keys(anchors).length !== overlayKeys.length || overlayKeys.some((key2) => !Object.hasOwn(anchors, key2)))
|
|
1790
|
+
throw new Error();
|
|
1791
|
+
const updated = {};
|
|
1792
|
+
for (const key2 of overlayKeys) {
|
|
1793
|
+
const anchor = anchors[key2];
|
|
1794
|
+
if (anchor === null) {
|
|
1795
|
+
updated[key2] = null;
|
|
1796
|
+
continue;
|
|
1797
|
+
}
|
|
1798
|
+
if (typeof anchor !== "string" || !starts.has(anchor)) throw new Error();
|
|
1799
|
+
updated[key2] = identifier(result.layers[starts.get(anchor)]);
|
|
1800
|
+
}
|
|
1801
|
+
metadata["tileflow:overlay-placement-manifest"] = { ...overlay, anchors: updated };
|
|
1802
|
+
}
|
|
1803
|
+
if (originalMetadata?.["tileflow:interaction-manifest"] !== void 0) {
|
|
1804
|
+
if (!metadata) throw new Error();
|
|
1805
|
+
const manifest = object2(
|
|
1806
|
+
JSON.parse(serializeCanonicalJson2(originalMetadata["tileflow:interaction-manifest"]))
|
|
1807
|
+
);
|
|
1808
|
+
if (manifest.version !== 2) throw new Error();
|
|
1809
|
+
const domains = object2(manifest.domains);
|
|
1810
|
+
if (domains.poi !== void 0) {
|
|
1811
|
+
const poi = object2(domains.poi);
|
|
1812
|
+
if (!Array.isArray(poi.layers) || poi.layers.length > 256) throw new Error();
|
|
1813
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1814
|
+
poi.layers = poi.layers.map((value) => {
|
|
1815
|
+
const entry = object2(value);
|
|
1816
|
+
if (typeof entry.layerId !== "string" || seen.has(entry.layerId)) throw new Error();
|
|
1817
|
+
const index = originalIndexes.get(entry.layerId);
|
|
1818
|
+
if (index === void 0 || index !== entry.priority || transformed.has(index) || !finalIndexes.has(entry.layerId))
|
|
1819
|
+
throw new Error();
|
|
1820
|
+
const original = input.layers[index];
|
|
1821
|
+
if (original.source !== entry.source || original["source-layer"] !== entry.sourceLayer)
|
|
1822
|
+
throw new Error();
|
|
1823
|
+
seen.add(entry.layerId);
|
|
1824
|
+
return { ...entry, priority: finalIndexes.get(entry.layerId) };
|
|
1825
|
+
});
|
|
1826
|
+
}
|
|
1827
|
+
metadata["tileflow:interaction-manifest"] = manifest;
|
|
1828
|
+
}
|
|
1829
|
+
return result;
|
|
1830
|
+
} catch {
|
|
1831
|
+
throw new TileflowNativeCompatibilityError3([
|
|
1832
|
+
createTileflowNativeDiagnostic2("NATIVE_UNSUPPORTED_STYLE", "/metadata")
|
|
1833
|
+
]);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
// src/hosted-native-artifacts.ts
|
|
1838
|
+
var digest = (value) => createHash2("sha256").update(serializeCanonicalJson3(value)).digest("hex");
|
|
1839
|
+
var invalidSource = () => new TileflowNativeCompatibilityError4([
|
|
1840
|
+
createTileflowNativeDiagnostic3("NATIVE_UNSUPPORTED_SOURCE", "/sources")
|
|
1841
|
+
]);
|
|
1842
|
+
async function prepareTileflowHostedNativeDeployment(input) {
|
|
1843
|
+
const webStyles = JSON.parse(serializeCanonicalJson3(input.styles));
|
|
1844
|
+
const validationStyles = JSON.parse(
|
|
1845
|
+
serializeCanonicalJson3(webStyles)
|
|
1846
|
+
);
|
|
1847
|
+
for (const style of Object.values(validationStyles)) {
|
|
1848
|
+
for (const [sourceId, binding] of Object.entries(input.teamSources)) {
|
|
1849
|
+
const source = style.sources[sourceId];
|
|
1850
|
+
if (!source || source.type !== binding.type || source.url !== `tileflow://hosted-sources/${sourceId}` || source.tiles !== void 0)
|
|
1851
|
+
throw invalidSource();
|
|
1852
|
+
source.url = `https://artifacts.invalid/tiles/${sourceId}/tiles.json`;
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
const lowered = lowerTileflowNativeCompiledStyles({ [input.mapId]: validationStyles });
|
|
1856
|
+
for (const transformation of lowered.transformations) {
|
|
1857
|
+
const name = transformation.theme;
|
|
1858
|
+
lowered.styles[input.mapId][name] = finalizeNativeHostedProvenance(
|
|
1859
|
+
validationStyles[name],
|
|
1860
|
+
lowered.styles[input.mapId][name],
|
|
1861
|
+
transformation.layers
|
|
1862
|
+
);
|
|
1863
|
+
}
|
|
1864
|
+
const projected = prepareTileflowNativeStyles(lowered.styles, input.assets)[input.mapId];
|
|
1865
|
+
for (const [name, style] of Object.entries(projected)) {
|
|
1866
|
+
for (const sourceId of Object.keys(input.teamSources)) {
|
|
1867
|
+
const source = style.sources[sourceId];
|
|
1868
|
+
if (!source) throw invalidSource();
|
|
1869
|
+
source.url = `tileflow://hosted-sources/${sourceId}`;
|
|
1870
|
+
}
|
|
1871
|
+
if (serializeCanonicalJson3(style.sources) !== serializeCanonicalJson3(webStyles[name].sources))
|
|
1872
|
+
throw invalidSource();
|
|
1873
|
+
}
|
|
1874
|
+
const nativeBuild = JSON.parse(
|
|
1875
|
+
serializeCanonicalJson3(input.buildManifest)
|
|
1876
|
+
);
|
|
1877
|
+
const mapBuild = nativeBuild.maps[input.mapId];
|
|
1878
|
+
if (!mapBuild) throw invalidSource();
|
|
1879
|
+
for (const [name, style] of Object.entries(projected)) {
|
|
1880
|
+
const theme = mapBuild.themes[name];
|
|
1881
|
+
if (!theme) throw invalidSource();
|
|
1882
|
+
mapBuild.themes[name] = {
|
|
1883
|
+
...theme,
|
|
1884
|
+
styleSha256: digest(style),
|
|
1885
|
+
dataRequirements: inferTileflowDataRequirements(style),
|
|
1886
|
+
sourceRequirements: inferTileflowSourceRequirements(style)
|
|
1887
|
+
};
|
|
1888
|
+
}
|
|
1889
|
+
const transformations = lowered.transformations.map((item) => ({
|
|
1890
|
+
...item,
|
|
1891
|
+
inputStyleSha256: digest(webStyles[item.theme]),
|
|
1892
|
+
loweredStyleSha256: digest({
|
|
1893
|
+
...lowered.styles[input.mapId][item.theme],
|
|
1894
|
+
sources: webStyles[item.theme].sources
|
|
1895
|
+
})
|
|
1896
|
+
}));
|
|
1897
|
+
return parseTileflowRendererDeploymentArtifact({
|
|
1898
|
+
kind: "tileflow-map-deployment",
|
|
1899
|
+
schemaVersion: 2,
|
|
1900
|
+
mapId: input.mapId,
|
|
1901
|
+
teamSources: input.teamSources,
|
|
1902
|
+
...input.view ? { view: input.view } : {},
|
|
1903
|
+
renderers: {
|
|
1904
|
+
web: { buildManifest: input.buildManifest, styles: webStyles },
|
|
1905
|
+
native: {
|
|
1906
|
+
buildManifest: nativeBuild,
|
|
1907
|
+
buildRecord: createTileflowNativeBuildRecord(digest(nativeBuild), transformations),
|
|
1908
|
+
styles: projected
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
|
|
828
1914
|
// src/artifacts.ts
|
|
829
1915
|
var tileflowArtifactPlanSchemaVersion = 1;
|
|
830
1916
|
var tileflowArtifactInventoryFileName = ".tileflow-artifacts.json";
|
|
@@ -903,31 +1989,50 @@ function assertValidTileflowStyles(project, styles) {
|
|
|
903
1989
|
}
|
|
904
1990
|
}
|
|
905
1991
|
async function createTileflowArtifactPlan(prepared, options = {}) {
|
|
906
|
-
|
|
1992
|
+
const renderer = resolveTileflowRenderer(options.renderer);
|
|
1993
|
+
if (renderer === "native" && options.inspection) {
|
|
1994
|
+
throw new TileflowNativeCompatibilityError5([
|
|
1995
|
+
createTileflowNativeDiagnostic4("NATIVE_RENDERER_UNSUPPORTED", "/inspection")
|
|
1996
|
+
]);
|
|
1997
|
+
}
|
|
1998
|
+
if (renderer === "native") {
|
|
1999
|
+
assertTileflowNativeCompiledStyles(prepared.project, {});
|
|
2000
|
+
} else if (options.target === "production" && hasLocalTilesetSources(prepared.project)) {
|
|
907
2001
|
throw new TileflowLocalTilesetProductionError();
|
|
908
2002
|
}
|
|
909
2003
|
const inspected = options.inspection ? createTileflowStylesWithInspection(prepared.project, {
|
|
910
2004
|
apiBaseUrl: options.apiBaseUrl,
|
|
911
2005
|
mapAssets: prepared.mapAssets
|
|
912
2006
|
}) : void 0;
|
|
913
|
-
const
|
|
2007
|
+
const compileStyles = renderer === "native" ? createStylesFromCatalog : createTileflowStyles;
|
|
2008
|
+
const compiledStyles = inspected?.styles ?? compileStyles(prepared.project, {
|
|
914
2009
|
apiBaseUrl: options.apiBaseUrl,
|
|
915
2010
|
mapAssets: prepared.mapAssets
|
|
916
2011
|
});
|
|
917
|
-
const
|
|
918
|
-
|
|
2012
|
+
const nativeLowering = renderer === "native" ? lowerTileflowNativeCompiledStyles(compiledStyles) : void 0;
|
|
2013
|
+
const rendererStyles = nativeLowering?.styles ?? compiledStyles;
|
|
2014
|
+
if (renderer === "native") assertTileflowNativeCompiledStyles(prepared.project, rendererStyles);
|
|
2015
|
+
const localTilesets = await prepareTileflowLocalTilesets(prepared.project, rendererStyles, {
|
|
2016
|
+
assetBaseUrl: resolveRendererAssetBaseUrl(options),
|
|
919
2017
|
baseDirectory: prepared.baseDirectory,
|
|
920
2018
|
cwd: prepared.cwd
|
|
921
2019
|
});
|
|
922
2020
|
try {
|
|
923
2021
|
const preparedFonts = await prepareTileflowStyleFonts(prepared.project, localTilesets.styles, {
|
|
924
|
-
assetBaseUrl:
|
|
2022
|
+
assetBaseUrl: resolveRendererAssetBaseUrl(options),
|
|
925
2023
|
baseDirectory: prepared.baseDirectory,
|
|
926
2024
|
cwd: prepared.cwd,
|
|
927
2025
|
target: "local"
|
|
2026
|
+
}).catch((error) => {
|
|
2027
|
+
if (renderer === "native" && error instanceof TileflowFontCompilationError) {
|
|
2028
|
+
throw new TileflowNativeCompatibilityError5([
|
|
2029
|
+
createTileflowNativeDiagnostic4("NATIVE_FONT_UNAVAILABLE", "/fonts")
|
|
2030
|
+
]);
|
|
2031
|
+
}
|
|
2032
|
+
throw error;
|
|
928
2033
|
});
|
|
929
|
-
const styles = preparedFonts.styles;
|
|
930
2034
|
const assets = [...prepared.assets, ...preparedFonts.assets];
|
|
2035
|
+
const styles = renderer === "native" ? prepareTileflowNativeStyles(preparedFonts.styles, assets) : preparedFonts.styles;
|
|
931
2036
|
const provenance = await createTileflowBuildProvenance(prepared.cwd);
|
|
932
2037
|
const buildManifest = await createTileflowMapBuildManifest(
|
|
933
2038
|
Object.fromEntries(
|
|
@@ -948,7 +2053,8 @@ async function createTileflowArtifactPlan(prepared, options = {}) {
|
|
|
948
2053
|
map,
|
|
949
2054
|
sourceAssets: {
|
|
950
2055
|
fonts: preparedFonts.sourceIdentities[mapName] ?? [],
|
|
951
|
-
icons: prepared.mapIconSources[mapName] ?? []
|
|
2056
|
+
icons: prepared.mapIconSources[mapName] ?? [],
|
|
2057
|
+
...prepared.mapIconCompositions[mapName] ? { iconComposition: prepared.mapIconCompositions[mapName] } : {}
|
|
952
2058
|
},
|
|
953
2059
|
styles: style
|
|
954
2060
|
}
|
|
@@ -957,7 +2063,9 @@ async function createTileflowArtifactPlan(prepared, options = {}) {
|
|
|
957
2063
|
),
|
|
958
2064
|
{ provenance }
|
|
959
2065
|
);
|
|
960
|
-
const stableManifest =
|
|
2066
|
+
const stableManifest = renderer === "native" ? createManifest(prepared.project, {
|
|
2067
|
+
styleBaseUrl: nativePublicBase(options.styleBaseUrl ?? ".")
|
|
2068
|
+
}) : createFontAwareManifest(
|
|
961
2069
|
createManifest(prepared.project, { styleBaseUrl: options.styleBaseUrl }),
|
|
962
2070
|
styles
|
|
963
2071
|
);
|
|
@@ -966,12 +2074,20 @@ async function createTileflowArtifactPlan(prepared, options = {}) {
|
|
|
966
2074
|
[...prepared.watchPaths, ...preparedFonts.watchPaths].map(canonicalInputPath)
|
|
967
2075
|
),
|
|
968
2076
|
files: uniqueStrings(
|
|
969
|
-
[...options.inputFiles ?? [], ...localTilesets.watchPaths].map(
|
|
2077
|
+
[...options.inputFiles ?? [], ...prepared.watchFiles, ...localTilesets.watchPaths].map(
|
|
2078
|
+
canonicalInputPath
|
|
2079
|
+
)
|
|
970
2080
|
)
|
|
971
2081
|
};
|
|
972
2082
|
const partial = {
|
|
973
2083
|
assets,
|
|
974
2084
|
buildManifest,
|
|
2085
|
+
...renderer === "native" ? {
|
|
2086
|
+
nativeBuild: createTileflowNativeBuildRecord2(
|
|
2087
|
+
hashBytes(serializeCanonicalJson4(buildManifest)),
|
|
2088
|
+
nativeLowering.transformations
|
|
2089
|
+
)
|
|
2090
|
+
} : {},
|
|
975
2091
|
dispose: localTilesets.dispose,
|
|
976
2092
|
manifest: stableManifest,
|
|
977
2093
|
project: prepared.project,
|
|
@@ -981,7 +2097,8 @@ async function createTileflowArtifactPlan(prepared, options = {}) {
|
|
|
981
2097
|
watchPaths: uniqueStrings([...inputs.files, ...inputs.directories])
|
|
982
2098
|
};
|
|
983
2099
|
const generation = hashArtifactGeneration(getStableTileflowArtifactFiles(partial));
|
|
984
|
-
const
|
|
2100
|
+
const generatedManifest = createGenerationManifest(stableManifest, generation, assets);
|
|
2101
|
+
const manifest = renderer === "native" ? parseTileflowRuntimeManifest(generatedManifest) : generatedManifest;
|
|
985
2102
|
const generationArtifacts = { ...partial, manifest };
|
|
986
2103
|
return {
|
|
987
2104
|
...generationArtifacts,
|
|
@@ -997,6 +2114,7 @@ async function createTileflowArtifactPlan(prepared, options = {}) {
|
|
|
997
2114
|
}
|
|
998
2115
|
}
|
|
999
2116
|
async function createTileflowBuildArtifacts(options = {}) {
|
|
2117
|
+
resolveTileflowRenderer(options.renderer);
|
|
1000
2118
|
const loaded = await loadValidTileflowConfigWithInputs(
|
|
1001
2119
|
options.config ?? defaultTileflowConfigPath,
|
|
1002
2120
|
{
|
|
@@ -1005,9 +2123,10 @@ async function createTileflowBuildArtifacts(options = {}) {
|
|
|
1005
2123
|
}
|
|
1006
2124
|
);
|
|
1007
2125
|
const prepared = await prepareTileflowCatalogIcons(loaded.project, {
|
|
1008
|
-
assetBaseUrl:
|
|
2126
|
+
assetBaseUrl: resolveRendererAssetBaseUrl(options),
|
|
1009
2127
|
baseDirectory: dirname2(loaded.configFile),
|
|
1010
|
-
cwd: options.cwd ?? process.cwd()
|
|
2128
|
+
cwd: options.cwd ?? process.cwd(),
|
|
2129
|
+
...options.icons ? { icons: options.icons } : {}
|
|
1011
2130
|
});
|
|
1012
2131
|
return await createTileflowArtifactPlan(prepared, { ...options, inputFiles: loaded.inputFiles });
|
|
1013
2132
|
}
|
|
@@ -1022,7 +2141,7 @@ function getStableTileflowArtifactFiles(artifacts) {
|
|
|
1022
2141
|
{
|
|
1023
2142
|
contentType: "application/json; charset=utf-8",
|
|
1024
2143
|
fileName: tileflowMapBuildManifestFileName,
|
|
1025
|
-
source: `${
|
|
2144
|
+
source: `${serializeCanonicalJson4(artifacts.buildManifest)}
|
|
1026
2145
|
`
|
|
1027
2146
|
},
|
|
1028
2147
|
{
|
|
@@ -1039,7 +2158,15 @@ function getStableTileflowArtifactFiles(artifacts) {
|
|
|
1039
2158
|
`
|
|
1040
2159
|
}))
|
|
1041
2160
|
),
|
|
1042
|
-
...artifacts.assets
|
|
2161
|
+
...artifacts.assets,
|
|
2162
|
+
...artifacts.nativeBuild ? [
|
|
2163
|
+
{
|
|
2164
|
+
contentType: "application/json; charset=utf-8",
|
|
2165
|
+
fileName: tileflowNativeBuildRecordFileName,
|
|
2166
|
+
source: `${serializeCanonicalJson4(artifacts.nativeBuild)}
|
|
2167
|
+
`
|
|
2168
|
+
}
|
|
2169
|
+
] : []
|
|
1043
2170
|
];
|
|
1044
2171
|
return validateArtifactFiles(files);
|
|
1045
2172
|
}
|
|
@@ -1103,10 +2230,13 @@ function createGenerationArtifactFiles(artifacts, generation) {
|
|
|
1103
2230
|
// Every theme in one logical map shares the same immutable sprite closure.
|
|
1104
2231
|
sprite: isRelativePublicUrl(sprite) ? sprite : insertGenerationInPublicUrl(sprite, `icons/${mapName}/sprite`, generation)
|
|
1105
2232
|
} : style;
|
|
1106
|
-
const
|
|
2233
|
+
const replaceFontSources = artifacts.nativeBuild ? replaceTileflowNativeFontSources : replaceTileflowStyleFontSources;
|
|
2234
|
+
const generationStyle = replaceFontSources(
|
|
1107
2235
|
generationStyleWithSprite,
|
|
1108
2236
|
(source) => retargetLocalFontAssetUrl(source, artifacts.assets, generation)
|
|
1109
2237
|
);
|
|
2238
|
+
if (artifacts.nativeBuild)
|
|
2239
|
+
assertTileflowNativeGeneratedStyle(generationStyle, mapName, themeName);
|
|
1110
2240
|
return {
|
|
1111
2241
|
contentType: "application/json; charset=utf-8",
|
|
1112
2242
|
fileName: `${prefix}/styles/${mapName}/${themeName}.json`,
|
|
@@ -1132,7 +2262,7 @@ function createFontAwareManifest(manifest, styles) {
|
|
|
1132
2262
|
if (!style) {
|
|
1133
2263
|
throw new Error(`Missing compiled Tileflow style for ${mapName}/${themeName}.`);
|
|
1134
2264
|
}
|
|
1135
|
-
const definitions =
|
|
2265
|
+
const definitions = getTileflowStyleFontFaces2(style);
|
|
1136
2266
|
return [themeName, { ...theme, fontFaces: definitions }];
|
|
1137
2267
|
})
|
|
1138
2268
|
)
|
|
@@ -1162,7 +2292,7 @@ function getMapBuildAssets(mapName, assets, fontAssets) {
|
|
|
1162
2292
|
);
|
|
1163
2293
|
}
|
|
1164
2294
|
function hashBytes(source) {
|
|
1165
|
-
return
|
|
2295
|
+
return createHash3("sha256").update(typeof source === "string" ? Buffer.from(source, "utf8") : Buffer.from(source)).digest("hex");
|
|
1166
2296
|
}
|
|
1167
2297
|
async function createTileflowBuildProvenance(cwd) {
|
|
1168
2298
|
const projectDirectory = resolve3(cwd);
|
|
@@ -1297,7 +2427,8 @@ async function disposeTileflowBuildArtifacts(artifacts) {
|
|
|
1297
2427
|
async function writeTileflowArtifactPlan(artifacts, options) {
|
|
1298
2428
|
assertNoLocalTilesetsForProduction(artifacts);
|
|
1299
2429
|
const cwd = options.cwd ?? process.cwd();
|
|
1300
|
-
const
|
|
2430
|
+
const outDir = artifacts.nativeBuild ? `${options.outDir.replace(/[\\/]$/u, "")}/native` : options.outDir;
|
|
2431
|
+
const output = await prepareTileflowOutputDirectory(cwd, outDir);
|
|
1301
2432
|
const files = getTileflowArtifactFiles(artifacts);
|
|
1302
2433
|
await assertTileflowSelfHostedManifestTarget(resolve3(output.outDir, "manifest.json"), options);
|
|
1303
2434
|
await commitTileflowArtifactFiles(output, files);
|
|
@@ -1584,7 +2715,7 @@ function assertTileflowArtifactFileName(fileName) {
|
|
|
1584
2715
|
const logicalName = fileName.replace(/^generations\/[a-f0-9]{64}\//, "");
|
|
1585
2716
|
const styleMatch = /^styles\/([^/]+)\/([^/]+)\.json$/u.exec(logicalName);
|
|
1586
2717
|
const iconMatch = /^icons\/([^/]+)\/sprite(?:@2x)?\.(?:json|png)$/u.exec(logicalName);
|
|
1587
|
-
if (fileName === "manifest.json" || fileName === tileflowMapBuildManifestFileName || styleMatch && tileflowMapIdSchema.safeParse(styleMatch[1]).success && tileflowThemeNameSchema.safeParse(styleMatch[2]).success || iconMatch && tileflowMapIdSchema.safeParse(iconMatch[1]).success || /^tilesets\/[A-Za-z0-9][A-Za-z0-9._-]{0,127}\.pmtiles$/u.test(logicalName) || /^fonts\/[a-z0-9]+(?:-[a-z0-9]+)*-[a-f0-9]{64}\.(?:otf|ttf|woff2)$/.test(logicalName) || /^fonts\/licenses\/license-[a-f0-9]{64}\.txt$/.test(logicalName)) {
|
|
2718
|
+
if (fileName === "manifest.json" || fileName === tileflowMapBuildManifestFileName || fileName === tileflowNativeBuildRecordFileName || styleMatch && tileflowMapIdSchema.safeParse(styleMatch[1]).success && tileflowThemeNameSchema.safeParse(styleMatch[2]).success || iconMatch && tileflowMapIdSchema.safeParse(iconMatch[1]).success || /^tilesets\/[A-Za-z0-9][A-Za-z0-9._-]{0,127}\.pmtiles$/u.test(logicalName) || /^fonts\/[a-z0-9]+(?:-[a-z0-9]+)*-[a-f0-9]{64}\.(?:otf|ttf|woff2)$/.test(logicalName) || /^fonts\/licenses\/license-[a-f0-9]{64}\.txt$/.test(logicalName)) {
|
|
1588
2719
|
return;
|
|
1589
2720
|
}
|
|
1590
2721
|
throw new Error(`Unexpected Tileflow managed artifact file name: ${fileName}`);
|
|
@@ -1624,7 +2755,7 @@ async function removeManagedFile(path) {
|
|
|
1624
2755
|
}
|
|
1625
2756
|
}
|
|
1626
2757
|
function hashArtifactGeneration(files) {
|
|
1627
|
-
const hash =
|
|
2758
|
+
const hash = createHash3("sha256");
|
|
1628
2759
|
for (const file of files) {
|
|
1629
2760
|
const source = typeof file.source === "string" ? Buffer.from(file.source, "utf8") : Buffer.from(file.source);
|
|
1630
2761
|
hash.update(`${file.fileName}\0${file.contentType}\0${source.byteLength}\0`, "utf8");
|
|
@@ -1632,6 +2763,13 @@ function hashArtifactGeneration(files) {
|
|
|
1632
2763
|
}
|
|
1633
2764
|
return hash.digest("hex");
|
|
1634
2765
|
}
|
|
2766
|
+
function resolveRendererAssetBaseUrl(options) {
|
|
2767
|
+
const base = resolveAssetBaseUrl(options);
|
|
2768
|
+
return options.renderer === "native" ? nativePublicBase(base) : base;
|
|
2769
|
+
}
|
|
2770
|
+
function nativePublicBase(base) {
|
|
2771
|
+
return isRelativePublicUrl(base) ? base : `${base.replace(/\/+$/u, "")}/native`;
|
|
2772
|
+
}
|
|
1635
2773
|
function resolveAssetBaseUrl(options) {
|
|
1636
2774
|
if (options.assetBaseUrl !== void 0) return options.assetBaseUrl;
|
|
1637
2775
|
if (options.styleBaseUrl && !isRelativePublicUrl(options.styleBaseUrl)) {
|
|
@@ -1665,6 +2803,7 @@ export {
|
|
|
1665
2803
|
prepareTileflowLocalTilesets,
|
|
1666
2804
|
tileflowArtifactSessionSchemaVersion,
|
|
1667
2805
|
createTileflowArtifactDiagnostics,
|
|
2806
|
+
prepareTileflowHostedNativeDeployment,
|
|
1668
2807
|
tileflowArtifactPlanSchemaVersion,
|
|
1669
2808
|
tileflowArtifactInventoryFileName,
|
|
1670
2809
|
TileflowHostedManifestOverwriteError,
|