@tileflow/dev 0.1.0-alpha.27 → 0.1.0-alpha.29
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 +11 -4
- package/dist/artifacts.d.ts +42 -2
- package/dist/artifacts.js +9 -1
- package/dist/{chunk-6OJICHB3.js → chunk-CJGM6HTT.js} +70 -8
- package/dist/{chunk-QAMFAM3S.js → chunk-LXSFLPIU.js} +465 -255
- package/dist/{chunk-UCBGTEQV.js → chunk-QYM2CUZB.js} +13 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +11 -3
- package/dist/preview.d.ts +2 -0
- package/dist/preview.js +1 -1
- package/dist/server.js +2 -2
- package/docs/native-artifacts.md +18 -1
- package/package.json +3 -3
|
@@ -28,26 +28,26 @@ import {
|
|
|
28
28
|
} from "./chunk-D4OE4ITL.js";
|
|
29
29
|
|
|
30
30
|
// src/artifacts.ts
|
|
31
|
-
import { createHash as
|
|
31
|
+
import { createHash as createHash4 } from "crypto";
|
|
32
32
|
import { realpathSync as realpathSync2 } from "fs";
|
|
33
33
|
import {
|
|
34
34
|
copyFile as copyFile2,
|
|
35
|
-
lstat as
|
|
36
|
-
mkdir as
|
|
35
|
+
lstat as lstat3,
|
|
36
|
+
mkdir as mkdir3,
|
|
37
37
|
mkdtemp,
|
|
38
38
|
open as open2,
|
|
39
|
-
readFile,
|
|
39
|
+
readFile as readFile2,
|
|
40
40
|
realpath as realpath2,
|
|
41
|
-
rename,
|
|
42
|
-
rm as
|
|
41
|
+
rename as rename2,
|
|
42
|
+
rm as rm3,
|
|
43
43
|
unlink
|
|
44
44
|
} from "fs/promises";
|
|
45
45
|
import { createRequire } from "module";
|
|
46
|
-
import { dirname as dirname2, relative as relative2, resolve as
|
|
46
|
+
import { dirname as dirname2, relative as relative2, resolve as resolve4, sep as sep2 } from "path";
|
|
47
47
|
import {
|
|
48
48
|
compareCodeUnits as compareCodeUnits2,
|
|
49
49
|
getTileflowStyleFontFaces as getTileflowStyleFontFaces2,
|
|
50
|
-
serializeCanonicalJson as
|
|
50
|
+
serializeCanonicalJson as serializeCanonicalJson3,
|
|
51
51
|
tileflowMapIdSchema,
|
|
52
52
|
tileflowThemeNameSchema
|
|
53
53
|
} from "@tileflow/core";
|
|
@@ -62,17 +62,200 @@ import {
|
|
|
62
62
|
import { parseTileflowRuntimeManifest } from "@tileflow/core/manifest";
|
|
63
63
|
import {
|
|
64
64
|
createTileflowNativeBuildRecord as createTileflowNativeBuildRecord2,
|
|
65
|
-
createTileflowNativeDiagnostic as
|
|
65
|
+
createTileflowNativeDiagnostic as createTileflowNativeDiagnostic3,
|
|
66
66
|
resolveTileflowRenderer,
|
|
67
67
|
tileflowNativeBuildRecordFileName,
|
|
68
|
-
TileflowNativeCompatibilityError as
|
|
68
|
+
TileflowNativeCompatibilityError as TileflowNativeCompatibilityError4
|
|
69
69
|
} from "@tileflow/core/native-profile";
|
|
70
70
|
|
|
71
|
+
// src/line-background-glyphs.ts
|
|
72
|
+
import { createHash, randomUUID } from "crypto";
|
|
73
|
+
import { lstat, mkdir, readFile, rename, rm, writeFile } from "fs/promises";
|
|
74
|
+
import { resolve } from "path";
|
|
75
|
+
import {
|
|
76
|
+
deriveTileflowLineBackgroundGlyphs,
|
|
77
|
+
parseTileflowLineBackgroundFontStack
|
|
78
|
+
} from "@tileflow/core";
|
|
79
|
+
var tileflowLineBackgroundGlyphRoute = "__glyphs";
|
|
80
|
+
var RANGE = /^\d{1,6}-\d{1,6}$/u;
|
|
81
|
+
var FONT_STACK = /^[\p{L}\p{N} _.,-]{1,256}$/u;
|
|
82
|
+
var MAP_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
|
|
83
|
+
var MAX_RANGE_BYTES = 8 * 1024 * 1024;
|
|
84
|
+
var MAX_DERIVED_ENTRIES = 512;
|
|
85
|
+
var CACHE_SEGMENTS = [".tileflow", "cache", "glyphs", "v1"];
|
|
86
|
+
function usesTileflowLineBackgroundGlyphs(style) {
|
|
87
|
+
return style.layers.some((layer) => {
|
|
88
|
+
const layout = layer.layout;
|
|
89
|
+
const font = layout?.["text-font"];
|
|
90
|
+
return Array.isArray(font) && font.length === 1 && typeof font[0] === "string" && parseTileflowLineBackgroundFontStack(font[0]) !== void 0;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function prepareTileflowLineBackgroundGlyphs(styles, options) {
|
|
94
|
+
const base = absoluteHttpUrl(options.assetBaseUrl);
|
|
95
|
+
if (!base) return { providers: {}, styles };
|
|
96
|
+
const providers = {};
|
|
97
|
+
const output = {};
|
|
98
|
+
for (const [mapName, themes] of Object.entries(styles)) {
|
|
99
|
+
const family = Object.values(themes);
|
|
100
|
+
const upstream = family.find((style) => typeof style.glyphs === "string")?.glyphs;
|
|
101
|
+
if (!MAP_NAME.test(mapName) || !upstream || !absoluteHttpUrl(upstream) || !family.some(usesTileflowLineBackgroundGlyphs) || family.some((style) => style.glyphs !== upstream)) {
|
|
102
|
+
output[mapName] = themes;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
providers[mapName] = upstream;
|
|
106
|
+
const glyphs = `${base.replace(/\/+$/u, "")}/${tileflowLineBackgroundGlyphRoute}/${mapName}/{fontstack}/{range}.pbf`;
|
|
107
|
+
output[mapName] = Object.fromEntries(
|
|
108
|
+
Object.entries(themes).map(([themeName, style]) => [themeName, { ...style, glyphs }])
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
return { providers, styles: output };
|
|
112
|
+
}
|
|
113
|
+
function parseTileflowLineBackgroundGlyphPath(path) {
|
|
114
|
+
const match = /^\/__glyphs\/([^/]+)\/([^/]+)\/([^/]+)\.pbf$/u.exec(path);
|
|
115
|
+
if (!match) return void 0;
|
|
116
|
+
let fontStack;
|
|
117
|
+
try {
|
|
118
|
+
fontStack = decodeURIComponent(match[2]);
|
|
119
|
+
} catch {
|
|
120
|
+
return void 0;
|
|
121
|
+
}
|
|
122
|
+
if (!MAP_NAME.test(match[1]) || !FONT_STACK.test(fontStack) || !RANGE.test(match[3])) {
|
|
123
|
+
return void 0;
|
|
124
|
+
}
|
|
125
|
+
return { fontStack, mapName: match[1], range: match[3] };
|
|
126
|
+
}
|
|
127
|
+
function createTileflowLineBackgroundGlyphResponder(options) {
|
|
128
|
+
const fetchRange = options.fetch ?? globalThis.fetch;
|
|
129
|
+
const derived = /* @__PURE__ */ new Map();
|
|
130
|
+
const upstreamRanges = /* @__PURE__ */ new Map();
|
|
131
|
+
const readUpstream = (upstream, fontStack, range) => {
|
|
132
|
+
const key2 = `${upstream}
|
|
133
|
+
${fontStack}
|
|
134
|
+
${range}`;
|
|
135
|
+
let pending = upstreamRanges.get(key2);
|
|
136
|
+
if (!pending) {
|
|
137
|
+
pending = loadUpstreamRange(options.cwd, fetchRange, upstream, fontStack, range).finally(
|
|
138
|
+
() => upstreamRanges.delete(key2)
|
|
139
|
+
);
|
|
140
|
+
upstreamRanges.set(key2, pending);
|
|
141
|
+
}
|
|
142
|
+
return pending;
|
|
143
|
+
};
|
|
144
|
+
return async (upstream, fontStack, range) => {
|
|
145
|
+
if (!absoluteHttpUrl(upstream) || !FONT_STACK.test(fontStack) || !RANGE.test(range)) {
|
|
146
|
+
return notFound();
|
|
147
|
+
}
|
|
148
|
+
const backed = parseTileflowLineBackgroundFontStack(fontStack);
|
|
149
|
+
if (!backed) {
|
|
150
|
+
if (fontStack.split(",").some((stack) => parseTileflowLineBackgroundFontStack(stack))) {
|
|
151
|
+
return notFound();
|
|
152
|
+
}
|
|
153
|
+
return glyphResponse(await readUpstream(upstream, fontStack, range));
|
|
154
|
+
}
|
|
155
|
+
const key2 = `${upstream}
|
|
156
|
+
${fontStack}
|
|
157
|
+
${range}`;
|
|
158
|
+
let pending = derived.get(key2);
|
|
159
|
+
if (!pending) {
|
|
160
|
+
pending = readUpstream(upstream, backed.source, range).then(
|
|
161
|
+
(source) => source ? deriveTileflowLineBackgroundGlyphs(source, backed.metrics) : void 0
|
|
162
|
+
);
|
|
163
|
+
pending.catch(() => derived.delete(key2));
|
|
164
|
+
derived.set(key2, pending);
|
|
165
|
+
if (derived.size > MAX_DERIVED_ENTRIES) derived.delete(derived.keys().next().value);
|
|
166
|
+
}
|
|
167
|
+
return glyphResponse(await pending);
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
async function loadUpstreamRange(cwd, fetchRange, upstream, fontStack, range) {
|
|
171
|
+
const directory = await cacheDirectory(cwd, upstream);
|
|
172
|
+
const file = resolve(directory, `${digest(fontStack)}-${range}.pbf`);
|
|
173
|
+
const cached = await readFile(file).catch((error) => {
|
|
174
|
+
if (hasCode(error, "ENOENT")) return void 0;
|
|
175
|
+
throw error;
|
|
176
|
+
});
|
|
177
|
+
if (cached) return new Uint8Array(cached);
|
|
178
|
+
const url = upstream.replace("{fontstack}", encodeURIComponent(fontStack)).replace("{range}", range);
|
|
179
|
+
const response = await fetchRange(url, {
|
|
180
|
+
credentials: "omit",
|
|
181
|
+
headers: { Accept: "application/x-protobuf" },
|
|
182
|
+
redirect: "error",
|
|
183
|
+
signal: AbortSignal.timeout(3e4)
|
|
184
|
+
});
|
|
185
|
+
if (response.status === 404) return void 0;
|
|
186
|
+
if (response.status !== 200) {
|
|
187
|
+
throw new Error(`Glyph provider responded ${response.status} for ${fontStack} ${range}.`);
|
|
188
|
+
}
|
|
189
|
+
const length = Number(response.headers.get("content-length") ?? 0);
|
|
190
|
+
if (length > MAX_RANGE_BYTES) throw new Error(`Glyph range ${range} exceeds the size limit.`);
|
|
191
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
192
|
+
if (bytes.byteLength > MAX_RANGE_BYTES)
|
|
193
|
+
throw new Error(`Glyph range ${range} exceeds the size limit.`);
|
|
194
|
+
const temporary = `${file}.${randomUUID()}.tmp`;
|
|
195
|
+
try {
|
|
196
|
+
await writeFile(temporary, bytes, { flag: "wx" });
|
|
197
|
+
await rename(temporary, file);
|
|
198
|
+
} catch {
|
|
199
|
+
await rm(temporary, { force: true }).catch(() => void 0);
|
|
200
|
+
}
|
|
201
|
+
return bytes;
|
|
202
|
+
}
|
|
203
|
+
async function cacheDirectory(cwd, upstream) {
|
|
204
|
+
let current = resolve(cwd);
|
|
205
|
+
for (const segment of [...CACHE_SEGMENTS, digest(upstream)]) {
|
|
206
|
+
current = resolve(current, segment);
|
|
207
|
+
try {
|
|
208
|
+
const stats = await lstat(current);
|
|
209
|
+
if (!stats.isDirectory() || stats.isSymbolicLink()) {
|
|
210
|
+
throw new Error(`Glyph cache path is unsafe: ${current}`);
|
|
211
|
+
}
|
|
212
|
+
} catch (error) {
|
|
213
|
+
if (!hasCode(error, "ENOENT")) throw error;
|
|
214
|
+
await mkdir(current).catch((mkdirError) => {
|
|
215
|
+
if (!hasCode(mkdirError, "EEXIST")) throw mkdirError;
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return current;
|
|
220
|
+
}
|
|
221
|
+
function glyphResponse(bytes) {
|
|
222
|
+
if (!bytes) return notFound();
|
|
223
|
+
return new Response(new Uint8Array(bytes), {
|
|
224
|
+
headers: {
|
|
225
|
+
"Access-Control-Allow-Origin": "*",
|
|
226
|
+
"Cache-Control": "no-cache",
|
|
227
|
+
"Content-Type": "application/x-protobuf"
|
|
228
|
+
},
|
|
229
|
+
status: 200
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
function notFound() {
|
|
233
|
+
return new Response(JSON.stringify({ error: "Unknown glyph range." }), {
|
|
234
|
+
headers: { "Content-Type": "application/json; charset=utf-8" },
|
|
235
|
+
status: 404
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function absoluteHttpUrl(value) {
|
|
239
|
+
if (!value) return void 0;
|
|
240
|
+
try {
|
|
241
|
+
const url = new URL(value.replace("{fontstack}", "stack").replace("{range}", "0-255"));
|
|
242
|
+
return url.protocol === "http:" || url.protocol === "https:" ? value : void 0;
|
|
243
|
+
} catch {
|
|
244
|
+
return void 0;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function digest(value) {
|
|
248
|
+
return createHash("sha256").update(value).digest("hex").slice(0, 32);
|
|
249
|
+
}
|
|
250
|
+
function hasCode(error, code) {
|
|
251
|
+
return Boolean(error && typeof error === "object" && "code" in error && error.code === code);
|
|
252
|
+
}
|
|
253
|
+
|
|
71
254
|
// src/local-tilesets.ts
|
|
72
|
-
import { randomUUID } from "crypto";
|
|
255
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
73
256
|
import { constants } from "fs";
|
|
74
|
-
import { chmod, copyFile, lstat, mkdir, open, readdir, realpath, rm, stat } from "fs/promises";
|
|
75
|
-
import { basename, resolve } from "path";
|
|
257
|
+
import { chmod, copyFile, lstat as lstat2, mkdir as mkdir2, open, readdir, realpath, rm as rm2, stat } from "fs/promises";
|
|
258
|
+
import { basename, resolve as resolve2 } from "path";
|
|
76
259
|
import { compareCodeUnits } from "@tileflow/core";
|
|
77
260
|
var minimumLocalPmtilesBytes = 127n;
|
|
78
261
|
var maximumLocalPmtilesBytes = 8000000000n;
|
|
@@ -83,14 +266,14 @@ async function prepareTileflowLocalTilesets(project, styles, options) {
|
|
|
83
266
|
if (!hasSources) {
|
|
84
267
|
return { dispose: async () => void 0, files: [], styles, watchPaths: [] };
|
|
85
268
|
}
|
|
86
|
-
const logicalProjectRoot =
|
|
269
|
+
const logicalProjectRoot = resolve2(options.cwd);
|
|
87
270
|
const projectRoot = await realpath(logicalProjectRoot).catch(() => logicalProjectRoot);
|
|
88
|
-
const snapshotStore =
|
|
271
|
+
const snapshotStore = resolve2(logicalProjectRoot, ".tileflow/cache/pmtiles-snapshots/v1");
|
|
89
272
|
await assertSafeSnapshotStore(logicalProjectRoot, snapshotStore);
|
|
90
273
|
await collectAbandonedSnapshotGenerations(snapshotStore);
|
|
91
|
-
const generationId = `snapshot-${process.pid}-${
|
|
92
|
-
const generationDirectory =
|
|
93
|
-
await
|
|
274
|
+
const generationId = `snapshot-${process.pid}-${randomUUID2().replaceAll("-", "")}`;
|
|
275
|
+
const generationDirectory = resolve2(snapshotStore, generationId);
|
|
276
|
+
await mkdir2(generationDirectory);
|
|
94
277
|
const bindings = /* @__PURE__ */ new Map();
|
|
95
278
|
const files = /* @__PURE__ */ new Map();
|
|
96
279
|
const snapshots = /* @__PURE__ */ new Map();
|
|
@@ -109,7 +292,7 @@ async function prepareTileflowLocalTilesets(project, styles, options) {
|
|
|
109
292
|
if (!isPathWithin(snapshotStore, generationDirectory)) {
|
|
110
293
|
throw new Error("Local PMTiles snapshot generation escaped its store.");
|
|
111
294
|
}
|
|
112
|
-
await
|
|
295
|
+
await rm2(generationDirectory, { force: true, recursive: true });
|
|
113
296
|
};
|
|
114
297
|
try {
|
|
115
298
|
for (const mapName of Object.keys(project.maps).sort(compareCodeUnits)) {
|
|
@@ -117,7 +300,7 @@ async function prepareTileflowLocalTilesets(project, styles, options) {
|
|
|
117
300
|
for (const [sourceId, source] of Object.entries(map.sources ?? {}).sort(
|
|
118
301
|
([left], [right]) => compareCodeUnits(left, right)
|
|
119
302
|
)) {
|
|
120
|
-
const requestedPath =
|
|
303
|
+
const requestedPath = resolve2(options.baseDirectory, source.local);
|
|
121
304
|
const sourcePath = await realpath(requestedPath).catch(() => requestedPath);
|
|
122
305
|
if (!isPathWithin(projectRoot, sourcePath)) {
|
|
123
306
|
throw localTilesetError(
|
|
@@ -126,7 +309,7 @@ async function prepareTileflowLocalTilesets(project, styles, options) {
|
|
|
126
309
|
"Local hosted tileset must stay inside the project root."
|
|
127
310
|
);
|
|
128
311
|
}
|
|
129
|
-
const sourceStat = await
|
|
312
|
+
const sourceStat = await lstat2(sourcePath).catch(() => null);
|
|
130
313
|
if (!sourceStat?.isFile() || sourceStat.isSymbolicLink()) {
|
|
131
314
|
throw localTilesetError(
|
|
132
315
|
"TF_LOCAL_TILESET_NOT_FOUND",
|
|
@@ -197,7 +380,7 @@ async function prepareTileflowLocalTilesets(project, styles, options) {
|
|
|
197
380
|
}
|
|
198
381
|
async function createLocalTilesetSnapshot(sourcePath, generationDirectory, index) {
|
|
199
382
|
const sourceHandle = await open(sourcePath, "r");
|
|
200
|
-
const snapshotPath =
|
|
383
|
+
const snapshotPath = resolve2(generationDirectory, `${index}.pmtiles`);
|
|
201
384
|
try {
|
|
202
385
|
const before = sourceIdentity(await sourceHandle.stat({ bigint: true }));
|
|
203
386
|
if (before.size < minimumLocalPmtilesBytes || before.size > maximumLocalPmtilesBytes) {
|
|
@@ -325,32 +508,32 @@ async function collectAbandonedSnapshotGenerations(store) {
|
|
|
325
508
|
if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
|
|
326
509
|
const match = /^snapshot-(\d+)-[a-f0-9]{32}$/u.exec(entry.name);
|
|
327
510
|
if (!match?.[1] || processIsAlive(Number(match[1]))) continue;
|
|
328
|
-
const path =
|
|
329
|
-
if (isPathWithin(store, path)) await
|
|
511
|
+
const path = resolve2(store, entry.name);
|
|
512
|
+
if (isPathWithin(store, path)) await rm2(path, { force: true, recursive: true });
|
|
330
513
|
}
|
|
331
514
|
}
|
|
332
515
|
async function assertSafeSnapshotStore(projectRoot, store) {
|
|
333
516
|
const segments = [".tileflow", "cache", "pmtiles-snapshots", "v1"];
|
|
334
517
|
let current = projectRoot;
|
|
335
518
|
for (const segment of segments) {
|
|
336
|
-
current =
|
|
519
|
+
current = resolve2(current, segment);
|
|
337
520
|
try {
|
|
338
|
-
const value = await
|
|
521
|
+
const value = await lstat2(current);
|
|
339
522
|
if (!value.isDirectory() || value.isSymbolicLink()) {
|
|
340
523
|
throw new Error(`Local PMTiles snapshot path is unsafe: ${current}`);
|
|
341
524
|
}
|
|
342
525
|
} catch (error) {
|
|
343
526
|
if (!hasErrorCode(error, "ENOENT")) throw error;
|
|
344
|
-
await
|
|
527
|
+
await mkdir2(current).catch((mkdirError) => {
|
|
345
528
|
if (!hasErrorCode(mkdirError, "EEXIST")) throw mkdirError;
|
|
346
529
|
});
|
|
347
|
-
const created = await
|
|
530
|
+
const created = await lstat2(current);
|
|
348
531
|
if (!created.isDirectory() || created.isSymbolicLink()) {
|
|
349
532
|
throw new Error(`Local PMTiles snapshot path is unsafe: ${current}`);
|
|
350
533
|
}
|
|
351
534
|
}
|
|
352
535
|
}
|
|
353
|
-
if (
|
|
536
|
+
if (resolve2(current) !== resolve2(store)) {
|
|
354
537
|
throw new Error("Local PMTiles snapshot path escaped its project root.");
|
|
355
538
|
}
|
|
356
539
|
}
|
|
@@ -384,7 +567,7 @@ function hasErrorCode(error, code) {
|
|
|
384
567
|
}
|
|
385
568
|
|
|
386
569
|
// src/native-artifacts.ts
|
|
387
|
-
import { createHash } from "crypto";
|
|
570
|
+
import { createHash as createHash2 } from "crypto";
|
|
388
571
|
import {
|
|
389
572
|
convertFilter,
|
|
390
573
|
validateStyleMin
|
|
@@ -404,6 +587,114 @@ import {
|
|
|
404
587
|
validateTileflowNativePreparedStyle
|
|
405
588
|
} from "@tileflow/core/native-profile";
|
|
406
589
|
|
|
590
|
+
// src/native-layer-references.ts
|
|
591
|
+
var overlayKeys = [
|
|
592
|
+
"above-water",
|
|
593
|
+
"below-roads",
|
|
594
|
+
"above-roads",
|
|
595
|
+
"above-buildings",
|
|
596
|
+
"below-labels",
|
|
597
|
+
"above-labels"
|
|
598
|
+
];
|
|
599
|
+
function object(value) {
|
|
600
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error();
|
|
601
|
+
return value;
|
|
602
|
+
}
|
|
603
|
+
function identifier(layer) {
|
|
604
|
+
if (typeof layer.id !== "string" || !layer.id.length) throw new Error();
|
|
605
|
+
return layer.id;
|
|
606
|
+
}
|
|
607
|
+
function rebindNativeLayerReferences(input, lowered, changes) {
|
|
608
|
+
try {
|
|
609
|
+
const originalIndexes = /* @__PURE__ */ new Map();
|
|
610
|
+
const finalIndexes = /* @__PURE__ */ new Map();
|
|
611
|
+
const starts = /* @__PURE__ */ new Map();
|
|
612
|
+
const transformed = /* @__PURE__ */ new Map();
|
|
613
|
+
let previous = -1;
|
|
614
|
+
for (const change of changes) {
|
|
615
|
+
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(
|
|
616
|
+
change.properties.join(",")
|
|
617
|
+
))
|
|
618
|
+
throw new Error();
|
|
619
|
+
previous = change.inputLayer;
|
|
620
|
+
transformed.set(change.inputLayer, change);
|
|
621
|
+
}
|
|
622
|
+
for (const [index, layer] of lowered.layers.entries()) {
|
|
623
|
+
const id = identifier(layer);
|
|
624
|
+
if (finalIndexes.has(id)) throw new Error();
|
|
625
|
+
finalIndexes.set(id, index);
|
|
626
|
+
}
|
|
627
|
+
let offset = 0;
|
|
628
|
+
for (const [index, layer] of input.layers.entries()) {
|
|
629
|
+
const id = identifier(layer);
|
|
630
|
+
if (originalIndexes.has(id)) throw new Error();
|
|
631
|
+
originalIndexes.set(id, index);
|
|
632
|
+
starts.set(id, offset);
|
|
633
|
+
const change = transformed.get(index);
|
|
634
|
+
if (change && (change.outputStart !== offset || layer.type !== "line")) throw new Error();
|
|
635
|
+
const count = change?.outputCount ?? 1;
|
|
636
|
+
if (offset + count > lowered.layers.length) throw new Error();
|
|
637
|
+
for (let branch = 0; branch < count; branch++) {
|
|
638
|
+
const candidate = lowered.layers[offset + branch];
|
|
639
|
+
if (!change && identifier(candidate) !== id) throw new Error();
|
|
640
|
+
if (candidate.type !== layer.type || candidate.source !== layer.source || candidate["source-layer"] !== layer["source-layer"])
|
|
641
|
+
throw new Error();
|
|
642
|
+
}
|
|
643
|
+
offset += count;
|
|
644
|
+
}
|
|
645
|
+
if (offset !== lowered.layers.length) throw new Error();
|
|
646
|
+
const metadata = lowered.metadata === void 0 ? void 0 : { ...object(lowered.metadata) };
|
|
647
|
+
const originalMetadata = input.metadata === void 0 ? void 0 : object(input.metadata);
|
|
648
|
+
if (originalMetadata?.["tileflow:overlay-placement-manifest"] !== void 0) {
|
|
649
|
+
if (!metadata) throw new Error();
|
|
650
|
+
const overlay = object(originalMetadata["tileflow:overlay-placement-manifest"]);
|
|
651
|
+
const anchors = object(overlay.anchors);
|
|
652
|
+
if (overlay.schemaVersion !== 1 || Object.keys(anchors).length !== overlayKeys.length || overlayKeys.some((key2) => !Object.hasOwn(anchors, key2)))
|
|
653
|
+
throw new Error();
|
|
654
|
+
const updated = {};
|
|
655
|
+
for (const key2 of overlayKeys) {
|
|
656
|
+
const anchor = anchors[key2];
|
|
657
|
+
if (anchor === null) {
|
|
658
|
+
updated[key2] = null;
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
if (typeof anchor !== "string" || !starts.has(anchor)) throw new Error();
|
|
662
|
+
updated[key2] = identifier(lowered.layers[starts.get(anchor)]);
|
|
663
|
+
}
|
|
664
|
+
metadata["tileflow:overlay-placement-manifest"] = { ...overlay, anchors: updated };
|
|
665
|
+
}
|
|
666
|
+
if (originalMetadata?.["tileflow:interaction-manifest"] !== void 0) {
|
|
667
|
+
if (!metadata) throw new Error();
|
|
668
|
+
const manifest = object(originalMetadata["tileflow:interaction-manifest"]);
|
|
669
|
+
if (manifest.version !== 2) throw new Error();
|
|
670
|
+
const domains = object(manifest.domains);
|
|
671
|
+
let rebound = manifest;
|
|
672
|
+
if (domains.poi !== void 0) {
|
|
673
|
+
const poi = object(domains.poi);
|
|
674
|
+
if (!Array.isArray(poi.layers) || poi.layers.length > 256) throw new Error();
|
|
675
|
+
const seen = /* @__PURE__ */ new Set();
|
|
676
|
+
const layers = poi.layers.map((value) => {
|
|
677
|
+
const entry = object(value);
|
|
678
|
+
if (typeof entry.layerId !== "string" || seen.has(entry.layerId)) throw new Error();
|
|
679
|
+
const index = originalIndexes.get(entry.layerId);
|
|
680
|
+
if (index === void 0 || index !== entry.priority || transformed.has(index) || !finalIndexes.has(entry.layerId))
|
|
681
|
+
throw new Error();
|
|
682
|
+
const original = input.layers[index];
|
|
683
|
+
if (original.source !== entry.source || original["source-layer"] !== entry.sourceLayer)
|
|
684
|
+
throw new Error();
|
|
685
|
+
seen.add(entry.layerId);
|
|
686
|
+
return { ...entry, priority: finalIndexes.get(entry.layerId) };
|
|
687
|
+
});
|
|
688
|
+
rebound = { ...manifest, domains: { ...domains, poi: { ...poi, layers } } };
|
|
689
|
+
}
|
|
690
|
+
metadata["tileflow:interaction-manifest"] = rebound;
|
|
691
|
+
}
|
|
692
|
+
return metadata === void 0 ? { ...lowered } : { ...lowered, metadata };
|
|
693
|
+
} catch {
|
|
694
|
+
return void 0;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
407
698
|
// src/native-lowering.ts
|
|
408
699
|
var nativeLoweringLimits = Object.freeze({
|
|
409
700
|
maximumDecisionDepth: 16,
|
|
@@ -430,7 +721,7 @@ var NativeLoweringError = class extends Error {
|
|
|
430
721
|
function fail(path, reason = "shape") {
|
|
431
722
|
throw new NativeLoweringError(path, reason);
|
|
432
723
|
}
|
|
433
|
-
function
|
|
724
|
+
function object2(value) {
|
|
434
725
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
435
726
|
}
|
|
436
727
|
function operation(value, name, length) {
|
|
@@ -871,21 +1162,21 @@ function lowerNativeStyleRepresentation(input, normalizeFilter = (filter) => fil
|
|
|
871
1162
|
const originalLayers = input.layers;
|
|
872
1163
|
const reserved = /* @__PURE__ */ new Set();
|
|
873
1164
|
for (const [index, layer] of originalLayers.entries()) {
|
|
874
|
-
if (!
|
|
1165
|
+
if (!object2(layer) || typeof layer.id !== "string" || reserved.has(layer.id))
|
|
875
1166
|
fail(`/layers/${index}/id`);
|
|
876
1167
|
reserved.add(layer.id);
|
|
877
1168
|
}
|
|
878
1169
|
let projection = "none";
|
|
879
1170
|
if (input.projection !== void 0) {
|
|
880
|
-
if (!
|
|
1171
|
+
if (!object2(input.projection) || Object.keys(input.projection).length !== 1 || typeof input.projection.type !== "string" || !["globe", "mercator"].includes(input.projection.type))
|
|
881
1172
|
fail("/projection");
|
|
882
1173
|
projection = input.projection.type === "globe" ? "globe-to-mercator" : "mercator-to-implicit";
|
|
883
1174
|
}
|
|
884
1175
|
const layers = [];
|
|
885
1176
|
const transformations = [];
|
|
886
1177
|
for (const [index, layer] of originalLayers.entries()) {
|
|
887
|
-
const source =
|
|
888
|
-
const lowered = lowerLayer(layer, index,
|
|
1178
|
+
const source = object2(input.sources) && typeof layer.source === "string" ? input.sources[layer.source] : void 0;
|
|
1179
|
+
const lowered = lowerLayer(layer, index, object2(source) && source.type === "vector");
|
|
889
1180
|
if (!lowered) {
|
|
890
1181
|
layers.push(layer);
|
|
891
1182
|
continue;
|
|
@@ -933,11 +1224,17 @@ function lowerNativeStyleRepresentation(input, normalizeFilter = (filter) => fil
|
|
|
933
1224
|
function documentUrl(map, theme) {
|
|
934
1225
|
return `https://artifacts.invalid/native/styles/${map}/${theme}.json`;
|
|
935
1226
|
}
|
|
1227
|
+
function urlOptions(map, theme, policy) {
|
|
1228
|
+
return {
|
|
1229
|
+
documentUrl: documentUrl(map, theme),
|
|
1230
|
+
...policy.developmentOrigin === void 0 ? {} : { developmentOrigin: policy.developmentOrigin }
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
936
1233
|
function withContext(issue, map, theme) {
|
|
937
1234
|
const prefix = `/maps/${map}/themes/${theme}/style`;
|
|
938
1235
|
return { ...issue, path: prefix.length + issue.path.length <= 300 ? prefix + issue.path : prefix };
|
|
939
1236
|
}
|
|
940
|
-
function lowerTileflowNativeCompiledStyles(input) {
|
|
1237
|
+
function lowerTileflowNativeCompiledStyles(input, policy = {}) {
|
|
941
1238
|
const styles = {};
|
|
942
1239
|
const transformations = [];
|
|
943
1240
|
for (const map of Object.keys(input).sort()) {
|
|
@@ -945,7 +1242,7 @@ function lowerTileflowNativeCompiledStyles(input) {
|
|
|
945
1242
|
for (const theme of Object.keys(input[map]).sort()) {
|
|
946
1243
|
const original = input[map][theme];
|
|
947
1244
|
try {
|
|
948
|
-
const options = {
|
|
1245
|
+
const options = { ...urlOptions(map, theme, policy), deferFontClosure: true };
|
|
949
1246
|
const pending = validateTileflowNativeStyle(original, options);
|
|
950
1247
|
const blockers = pending.filter(
|
|
951
1248
|
(issue) => issue.code !== "NATIVE_UNSUPPORTED_STYLE" || !(issue.path === "/projection" || /^\/layers\/\d+\/(?:layout\/line-cap|paint\/line-dasharray)(?:\/|$)/u.test(issue.path))
|
|
@@ -973,17 +1270,34 @@ function lowerTileflowNativeCompiledStyles(input) {
|
|
|
973
1270
|
original,
|
|
974
1271
|
(filter) => convertFilter(structuredClone(filter))
|
|
975
1272
|
);
|
|
976
|
-
const
|
|
1273
|
+
const style = rebindNativeLayerReferences(
|
|
1274
|
+
original,
|
|
1275
|
+
lowered.style,
|
|
1276
|
+
lowered.layers
|
|
1277
|
+
);
|
|
1278
|
+
if (!style)
|
|
1279
|
+
throw new TileflowNativeCompatibilityError([
|
|
1280
|
+
withContext(
|
|
1281
|
+
{
|
|
1282
|
+
...createTileflowNativeDiagnostic("NATIVE_UNSUPPORTED_STYLE", "/metadata"),
|
|
1283
|
+
message: "Tileflow POI interaction or overlay placement metadata does not match the compiled layers.",
|
|
1284
|
+
suggestion: "Rebuild with the Tileflow compiler; native lowering only rebinds POI priorities and overlay anchors that match the compiled layer order."
|
|
1285
|
+
},
|
|
1286
|
+
map,
|
|
1287
|
+
theme
|
|
1288
|
+
)
|
|
1289
|
+
]);
|
|
1290
|
+
const issues = validateTileflowNativePreparedStyle(style, options);
|
|
977
1291
|
if (issues.length)
|
|
978
1292
|
throw new TileflowNativeCompatibilityError(
|
|
979
1293
|
issues.map((issue) => withContext(issue, map, theme))
|
|
980
1294
|
);
|
|
981
|
-
styles[map][theme] =
|
|
1295
|
+
styles[map][theme] = style;
|
|
982
1296
|
transformations.push({
|
|
983
1297
|
map,
|
|
984
1298
|
theme,
|
|
985
|
-
inputStyleSha256:
|
|
986
|
-
loweredStyleSha256:
|
|
1299
|
+
inputStyleSha256: createHash2("sha256").update(serializeCanonicalJson(original)).digest("hex"),
|
|
1300
|
+
loweredStyleSha256: createHash2("sha256").update(serializeCanonicalJson(style)).digest("hex"),
|
|
987
1301
|
inputLayers: lowered.inputLayers,
|
|
988
1302
|
outputLayers: lowered.outputLayers,
|
|
989
1303
|
projection: lowered.projection,
|
|
@@ -1007,13 +1321,13 @@ function lowerTileflowNativeCompiledStyles(input) {
|
|
|
1007
1321
|
}
|
|
1008
1322
|
return { styles, transformations };
|
|
1009
1323
|
}
|
|
1010
|
-
function assertTileflowNativeCompiledStyles(project, styles) {
|
|
1324
|
+
function assertTileflowNativeCompiledStyles(project, styles, policy = {}) {
|
|
1011
1325
|
const issues = [];
|
|
1012
1326
|
for (const mapName of Object.keys(project.maps).sort()) {
|
|
1013
1327
|
for (const theme of Object.keys(styles[mapName] ?? {}).sort()) {
|
|
1014
1328
|
issues.push(
|
|
1015
1329
|
...validateTileflowNativePreparedStyle(styles[mapName][theme], {
|
|
1016
|
-
|
|
1330
|
+
...urlOptions(mapName, theme, policy),
|
|
1017
1331
|
deferFontClosure: true
|
|
1018
1332
|
}).map((issue) => withContext(issue, mapName, theme))
|
|
1019
1333
|
);
|
|
@@ -1021,7 +1335,7 @@ function assertTileflowNativeCompiledStyles(project, styles) {
|
|
|
1021
1335
|
}
|
|
1022
1336
|
if (issues.length) throw new TileflowNativeCompatibilityError(issues);
|
|
1023
1337
|
}
|
|
1024
|
-
function prepareTileflowNativeStyles(input, assets) {
|
|
1338
|
+
function prepareTileflowNativeStyles(input, assets, policy = {}) {
|
|
1025
1339
|
const issues = [];
|
|
1026
1340
|
const result = {};
|
|
1027
1341
|
for (const map of Object.keys(input).sort()) {
|
|
@@ -1035,7 +1349,7 @@ function prepareTileflowNativeStyles(input, assets) {
|
|
|
1035
1349
|
for (const theme of Object.keys(input[map]).sort()) {
|
|
1036
1350
|
const original = input[map][theme];
|
|
1037
1351
|
const preflight = validateTileflowNativePreparedStyle(original, {
|
|
1038
|
-
|
|
1352
|
+
...urlOptions(map, theme, policy),
|
|
1039
1353
|
deferFontClosure: true
|
|
1040
1354
|
});
|
|
1041
1355
|
if (preflight.length) {
|
|
@@ -1079,9 +1393,9 @@ function prepareTileflowNativeStyles(input, assets) {
|
|
|
1079
1393
|
...faces.length ? { "font-faces": nativeFaces } : {}
|
|
1080
1394
|
};
|
|
1081
1395
|
issues.push(
|
|
1082
|
-
...validateTileflowNativePreparedStyle(style,
|
|
1083
|
-
|
|
1084
|
-
|
|
1396
|
+
...validateTileflowNativePreparedStyle(style, urlOptions(map, theme, policy)).map(
|
|
1397
|
+
(issue) => withContext(issue, map, theme)
|
|
1398
|
+
)
|
|
1085
1399
|
);
|
|
1086
1400
|
result[map][theme] = style;
|
|
1087
1401
|
}
|
|
@@ -1097,10 +1411,10 @@ function isNativeFontAsset(asset) {
|
|
|
1097
1411
|
if (!/\.(?:ttf|otf)$/u.test(asset.fileName) || !["00010000", "4f54544f"].includes(signature))
|
|
1098
1412
|
return false;
|
|
1099
1413
|
const expectedHash = /-([a-f0-9]{64})\.(?:ttf|otf)$/u.exec(asset.fileName)?.[1];
|
|
1100
|
-
return expectedHash ===
|
|
1414
|
+
return expectedHash === createHash2("sha256").update(bytes).digest("hex");
|
|
1101
1415
|
}
|
|
1102
|
-
function assertTileflowNativeGeneratedStyle(style, map, theme) {
|
|
1103
|
-
const issues = validateTileflowNativePreparedStyle(style,
|
|
1416
|
+
function assertTileflowNativeGeneratedStyle(style, map, theme, policy = {}) {
|
|
1417
|
+
const issues = validateTileflowNativePreparedStyle(style, urlOptions(map, theme, policy));
|
|
1104
1418
|
if (issues.length) {
|
|
1105
1419
|
throw new TileflowNativeCompatibilityError(
|
|
1106
1420
|
issues.map((issue) => withContext(issue, map, theme))
|
|
@@ -1171,7 +1485,7 @@ function validNativeSprites(map, assets) {
|
|
|
1171
1485
|
// src/session.ts
|
|
1172
1486
|
import { watch } from "chokidar";
|
|
1173
1487
|
import { realpathSync, statSync } from "fs";
|
|
1174
|
-
import { basename as basename2, dirname, extname, isAbsolute, relative, resolve as
|
|
1488
|
+
import { basename as basename2, dirname, extname, isAbsolute, relative, resolve as resolve3, sep, win32 } from "path";
|
|
1175
1489
|
import { tileflowIconMetadataFileName } from "@tileflow/core";
|
|
1176
1490
|
import { TileflowNativeCompatibilityError as TileflowNativeCompatibilityError2 } from "@tileflow/core/native-profile";
|
|
1177
1491
|
var tileflowArtifactSessionSchemaVersion = 1;
|
|
@@ -1224,13 +1538,13 @@ var TileflowArtifactSessionImpl = class {
|
|
|
1224
1538
|
#watchedArtifactDirectories = /* @__PURE__ */ new Map();
|
|
1225
1539
|
#watchedArtifactPaths = /* @__PURE__ */ new Set();
|
|
1226
1540
|
constructor(options, buildArtifacts, discoverWatchPaths) {
|
|
1227
|
-
this.#cwd =
|
|
1228
|
-
const configPath =
|
|
1229
|
-
this.#configPath =
|
|
1541
|
+
this.#cwd = resolve3(options.cwd ?? process.cwd());
|
|
1542
|
+
const configPath = resolve3(this.#cwd, options.config ?? "tileflow.config.ts");
|
|
1543
|
+
this.#configPath = resolve3(canonicalWatchPath(dirname(configPath)), basename2(configPath));
|
|
1230
1544
|
this.#debounceMs = Math.max(0, Math.min(options.debounceMs ?? 75, 1e3));
|
|
1231
1545
|
this.#watchEnabled = options.watch ?? false;
|
|
1232
1546
|
this.#ignoredPaths = (options.ignoredPaths ?? []).map(
|
|
1233
|
-
(path) => canonicalWatchPath(
|
|
1547
|
+
(path) => canonicalWatchPath(resolve3(this.#cwd, path))
|
|
1234
1548
|
);
|
|
1235
1549
|
this.#buildArtifacts = buildArtifacts;
|
|
1236
1550
|
this.#discoverWatchPaths = discoverWatchPaths;
|
|
@@ -1239,6 +1553,7 @@ var TileflowArtifactSessionImpl = class {
|
|
|
1239
1553
|
assetBaseUrl: options.assetBaseUrl,
|
|
1240
1554
|
config: options.config,
|
|
1241
1555
|
cwd: this.#cwd,
|
|
1556
|
+
...options.developmentOrigin === void 0 ? {} : { developmentOrigin: options.developmentOrigin },
|
|
1242
1557
|
// Trusted shared Icon Set resolution settings, supplied once by the owning command.
|
|
1243
1558
|
...options.icons ? { icons: options.icons } : {},
|
|
1244
1559
|
inspection: options.inspection,
|
|
@@ -1463,7 +1778,7 @@ var TileflowArtifactSessionImpl = class {
|
|
|
1463
1778
|
}
|
|
1464
1779
|
#updateArtifactWatchPaths(paths) {
|
|
1465
1780
|
if (!this.#watcher) return;
|
|
1466
|
-
const next = new Set(paths.map((path) => canonicalWatchPath(
|
|
1781
|
+
const next = new Set(paths.map((path) => canonicalWatchPath(resolve3(this.#cwd, path))));
|
|
1467
1782
|
const previous = this.#watchedArtifactPaths;
|
|
1468
1783
|
this.#watchedArtifactPaths = next;
|
|
1469
1784
|
const previousDirectories = this.#watchedArtifactDirectories;
|
|
@@ -1528,7 +1843,7 @@ async function disposeArtifacts(artifacts) {
|
|
|
1528
1843
|
await dispose?.();
|
|
1529
1844
|
}
|
|
1530
1845
|
function isSameOrInside(root, candidate) {
|
|
1531
|
-
const path = relative(
|
|
1846
|
+
const path = relative(resolve3(root), resolve3(candidate));
|
|
1532
1847
|
return path === "" || !isAbsolute(path) && path !== ".." && !path.startsWith(`..${sep}`);
|
|
1533
1848
|
}
|
|
1534
1849
|
function directoryModificationTime(path) {
|
|
@@ -1546,15 +1861,15 @@ function resolveRawWatchPath(path, details) {
|
|
|
1546
1861
|
const watchedPath = details.watchedPath;
|
|
1547
1862
|
if (typeof watchedPath !== "string" || !isAbsolute(watchedPath)) return void 0;
|
|
1548
1863
|
return canonicalWatchPath(
|
|
1549
|
-
basename2(watchedPath) === path ? watchedPath :
|
|
1864
|
+
basename2(watchedPath) === path ? watchedPath : resolve3(watchedPath, path)
|
|
1550
1865
|
);
|
|
1551
1866
|
}
|
|
1552
1867
|
function canonicalWatchPath(path) {
|
|
1553
|
-
const resolved =
|
|
1868
|
+
const resolved = resolve3(path);
|
|
1554
1869
|
let ancestor = resolved;
|
|
1555
1870
|
while (true) {
|
|
1556
1871
|
try {
|
|
1557
|
-
return
|
|
1872
|
+
return resolve3(realpathSync.native(ancestor), relative(ancestor, resolved));
|
|
1558
1873
|
} catch {
|
|
1559
1874
|
const parent = dirname(ancestor);
|
|
1560
1875
|
if (parent === ancestor) return resolved;
|
|
@@ -1661,15 +1976,15 @@ function removeSemanticResolutionPath(message, path) {
|
|
|
1661
1976
|
return message.replace(` at ${path}:`, ":");
|
|
1662
1977
|
}
|
|
1663
1978
|
function localPathAliases(path) {
|
|
1664
|
-
return [.../* @__PURE__ */ new Set([
|
|
1979
|
+
return [.../* @__PURE__ */ new Set([resolve3(path), canonicalPath(path)])].sort(
|
|
1665
1980
|
(left, right) => right.length - left.length
|
|
1666
1981
|
);
|
|
1667
1982
|
}
|
|
1668
1983
|
function canonicalPath(path) {
|
|
1669
1984
|
try {
|
|
1670
|
-
return realpathSync.native(
|
|
1985
|
+
return realpathSync.native(resolve3(path));
|
|
1671
1986
|
} catch {
|
|
1672
|
-
return
|
|
1987
|
+
return resolve3(path);
|
|
1673
1988
|
}
|
|
1674
1989
|
}
|
|
1675
1990
|
function normalizeDiagnostics(diagnostics) {
|
|
@@ -1705,8 +2020,8 @@ function boundDiagnosticText(value, limit) {
|
|
|
1705
2020
|
}
|
|
1706
2021
|
|
|
1707
2022
|
// src/hosted-native-artifacts.ts
|
|
1708
|
-
import { createHash as
|
|
1709
|
-
import { serializeCanonicalJson as
|
|
2023
|
+
import { createHash as createHash3 } from "crypto";
|
|
2024
|
+
import { serializeCanonicalJson as serializeCanonicalJson2 } from "@tileflow/core";
|
|
1710
2025
|
import {
|
|
1711
2026
|
inferTileflowDataRequirements,
|
|
1712
2027
|
inferTileflowSourceRequirements,
|
|
@@ -1714,135 +2029,17 @@ import {
|
|
|
1714
2029
|
} from "@tileflow/core/build";
|
|
1715
2030
|
import {
|
|
1716
2031
|
createTileflowNativeBuildRecord,
|
|
1717
|
-
TileflowNativeCompatibilityError as
|
|
1718
|
-
createTileflowNativeDiagnostic as
|
|
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
|
|
2032
|
+
TileflowNativeCompatibilityError as TileflowNativeCompatibilityError3,
|
|
2033
|
+
createTileflowNativeDiagnostic as createTileflowNativeDiagnostic2
|
|
1726
2034
|
} from "@tileflow/core/native-profile";
|
|
1727
|
-
var
|
|
1728
|
-
|
|
1729
|
-
"
|
|
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")
|
|
2035
|
+
var digest2 = (value) => createHash3("sha256").update(serializeCanonicalJson2(value)).digest("hex");
|
|
2036
|
+
var invalidSource = () => new TileflowNativeCompatibilityError3([
|
|
2037
|
+
createTileflowNativeDiagnostic2("NATIVE_UNSUPPORTED_SOURCE", "/sources")
|
|
1841
2038
|
]);
|
|
1842
2039
|
async function prepareTileflowHostedNativeDeployment(input) {
|
|
1843
|
-
const webStyles = JSON.parse(
|
|
2040
|
+
const webStyles = JSON.parse(serializeCanonicalJson2(input.styles));
|
|
1844
2041
|
const validationStyles = JSON.parse(
|
|
1845
|
-
|
|
2042
|
+
serializeCanonicalJson2(webStyles)
|
|
1846
2043
|
);
|
|
1847
2044
|
for (const style of Object.values(validationStyles)) {
|
|
1848
2045
|
for (const [sourceId, binding] of Object.entries(input.teamSources)) {
|
|
@@ -1853,14 +2050,6 @@ async function prepareTileflowHostedNativeDeployment(input) {
|
|
|
1853
2050
|
}
|
|
1854
2051
|
}
|
|
1855
2052
|
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
2053
|
const projected = prepareTileflowNativeStyles(lowered.styles, input.assets)[input.mapId];
|
|
1865
2054
|
for (const [name, style] of Object.entries(projected)) {
|
|
1866
2055
|
for (const sourceId of Object.keys(input.teamSources)) {
|
|
@@ -1868,11 +2057,11 @@ async function prepareTileflowHostedNativeDeployment(input) {
|
|
|
1868
2057
|
if (!source) throw invalidSource();
|
|
1869
2058
|
source.url = `tileflow://hosted-sources/${sourceId}`;
|
|
1870
2059
|
}
|
|
1871
|
-
if (
|
|
2060
|
+
if (serializeCanonicalJson2(style.sources) !== serializeCanonicalJson2(webStyles[name].sources))
|
|
1872
2061
|
throw invalidSource();
|
|
1873
2062
|
}
|
|
1874
2063
|
const nativeBuild = JSON.parse(
|
|
1875
|
-
|
|
2064
|
+
serializeCanonicalJson2(input.buildManifest)
|
|
1876
2065
|
);
|
|
1877
2066
|
const mapBuild = nativeBuild.maps[input.mapId];
|
|
1878
2067
|
if (!mapBuild) throw invalidSource();
|
|
@@ -1881,15 +2070,15 @@ async function prepareTileflowHostedNativeDeployment(input) {
|
|
|
1881
2070
|
if (!theme) throw invalidSource();
|
|
1882
2071
|
mapBuild.themes[name] = {
|
|
1883
2072
|
...theme,
|
|
1884
|
-
styleSha256:
|
|
2073
|
+
styleSha256: digest2(style),
|
|
1885
2074
|
dataRequirements: inferTileflowDataRequirements(style),
|
|
1886
2075
|
sourceRequirements: inferTileflowSourceRequirements(style)
|
|
1887
2076
|
};
|
|
1888
2077
|
}
|
|
1889
2078
|
const transformations = lowered.transformations.map((item) => ({
|
|
1890
2079
|
...item,
|
|
1891
|
-
inputStyleSha256:
|
|
1892
|
-
loweredStyleSha256:
|
|
2080
|
+
inputStyleSha256: digest2(webStyles[item.theme]),
|
|
2081
|
+
loweredStyleSha256: digest2({
|
|
1893
2082
|
...lowered.styles[input.mapId][item.theme],
|
|
1894
2083
|
sources: webStyles[item.theme].sources
|
|
1895
2084
|
})
|
|
@@ -1904,7 +2093,7 @@ async function prepareTileflowHostedNativeDeployment(input) {
|
|
|
1904
2093
|
web: { buildManifest: input.buildManifest, styles: webStyles },
|
|
1905
2094
|
native: {
|
|
1906
2095
|
buildManifest: nativeBuild,
|
|
1907
|
-
buildRecord: createTileflowNativeBuildRecord(
|
|
2096
|
+
buildRecord: createTileflowNativeBuildRecord(digest2(nativeBuild), transformations),
|
|
1908
2097
|
styles: projected
|
|
1909
2098
|
}
|
|
1910
2099
|
}
|
|
@@ -1991,12 +2180,16 @@ function assertValidTileflowStyles(project, styles) {
|
|
|
1991
2180
|
async function createTileflowArtifactPlan(prepared, options = {}) {
|
|
1992
2181
|
const renderer = resolveTileflowRenderer(options.renderer);
|
|
1993
2182
|
if (renderer === "native" && options.inspection) {
|
|
1994
|
-
throw new
|
|
1995
|
-
|
|
2183
|
+
throw new TileflowNativeCompatibilityError4([
|
|
2184
|
+
createTileflowNativeDiagnostic3("NATIVE_RENDERER_UNSUPPORTED", "/inspection")
|
|
1996
2185
|
]);
|
|
1997
2186
|
}
|
|
2187
|
+
assertNativeDevelopmentOrigin(options);
|
|
2188
|
+
const nativeUrls = {
|
|
2189
|
+
developmentOrigin: options.developmentOrigin
|
|
2190
|
+
};
|
|
1998
2191
|
if (renderer === "native") {
|
|
1999
|
-
assertTileflowNativeCompiledStyles(prepared.project, {});
|
|
2192
|
+
assertTileflowNativeCompiledStyles(prepared.project, {}, nativeUrls);
|
|
2000
2193
|
} else if (options.target === "production" && hasLocalTilesetSources(prepared.project)) {
|
|
2001
2194
|
throw new TileflowLocalTilesetProductionError();
|
|
2002
2195
|
}
|
|
@@ -2009,9 +2202,10 @@ async function createTileflowArtifactPlan(prepared, options = {}) {
|
|
|
2009
2202
|
apiBaseUrl: options.apiBaseUrl,
|
|
2010
2203
|
mapAssets: prepared.mapAssets
|
|
2011
2204
|
});
|
|
2012
|
-
const nativeLowering = renderer === "native" ? lowerTileflowNativeCompiledStyles(compiledStyles) : void 0;
|
|
2205
|
+
const nativeLowering = renderer === "native" ? lowerTileflowNativeCompiledStyles(compiledStyles, nativeUrls) : void 0;
|
|
2013
2206
|
const rendererStyles = nativeLowering?.styles ?? compiledStyles;
|
|
2014
|
-
if (renderer === "native")
|
|
2207
|
+
if (renderer === "native")
|
|
2208
|
+
assertTileflowNativeCompiledStyles(prepared.project, rendererStyles, nativeUrls);
|
|
2015
2209
|
const localTilesets = await prepareTileflowLocalTilesets(prepared.project, rendererStyles, {
|
|
2016
2210
|
assetBaseUrl: resolveRendererAssetBaseUrl(options),
|
|
2017
2211
|
baseDirectory: prepared.baseDirectory,
|
|
@@ -2025,14 +2219,17 @@ async function createTileflowArtifactPlan(prepared, options = {}) {
|
|
|
2025
2219
|
target: "local"
|
|
2026
2220
|
}).catch((error) => {
|
|
2027
2221
|
if (renderer === "native" && error instanceof TileflowFontCompilationError) {
|
|
2028
|
-
throw new
|
|
2029
|
-
|
|
2222
|
+
throw new TileflowNativeCompatibilityError4([
|
|
2223
|
+
createTileflowNativeDiagnostic3("NATIVE_FONT_UNAVAILABLE", "/fonts")
|
|
2030
2224
|
]);
|
|
2031
2225
|
}
|
|
2032
2226
|
throw error;
|
|
2033
2227
|
});
|
|
2034
2228
|
const assets = [...prepared.assets, ...preparedFonts.assets];
|
|
2035
|
-
const
|
|
2229
|
+
const lineBackgroundGlyphs = renderer === "native" || options.target === "production" ? { providers: {}, styles: preparedFonts.styles } : prepareTileflowLineBackgroundGlyphs(preparedFonts.styles, {
|
|
2230
|
+
assetBaseUrl: resolveRendererAssetBaseUrl(options)
|
|
2231
|
+
});
|
|
2232
|
+
const styles = renderer === "native" ? prepareTileflowNativeStyles(preparedFonts.styles, assets, nativeUrls) : lineBackgroundGlyphs.styles;
|
|
2036
2233
|
const provenance = await createTileflowBuildProvenance(prepared.cwd);
|
|
2037
2234
|
const buildManifest = await createTileflowMapBuildManifest(
|
|
2038
2235
|
Object.fromEntries(
|
|
@@ -2084,7 +2281,7 @@ async function createTileflowArtifactPlan(prepared, options = {}) {
|
|
|
2084
2281
|
buildManifest,
|
|
2085
2282
|
...renderer === "native" ? {
|
|
2086
2283
|
nativeBuild: createTileflowNativeBuildRecord2(
|
|
2087
|
-
hashBytes(
|
|
2284
|
+
hashBytes(serializeCanonicalJson3(buildManifest)),
|
|
2088
2285
|
nativeLowering.transformations
|
|
2089
2286
|
)
|
|
2090
2287
|
} : {},
|
|
@@ -2092,6 +2289,7 @@ async function createTileflowArtifactPlan(prepared, options = {}) {
|
|
|
2092
2289
|
manifest: stableManifest,
|
|
2093
2290
|
project: prepared.project,
|
|
2094
2291
|
...localTilesets.files.length > 0 ? { localTilesets: localTilesets.files } : {},
|
|
2292
|
+
...Object.keys(lineBackgroundGlyphs.providers).length > 0 ? { lineBackgroundGlyphs: lineBackgroundGlyphs.providers } : {},
|
|
2095
2293
|
...inspected ? { styleInspections: inspected.inspections } : {},
|
|
2096
2294
|
styles,
|
|
2097
2295
|
watchPaths: uniqueStrings([...inputs.files, ...inputs.directories])
|
|
@@ -2102,7 +2300,7 @@ async function createTileflowArtifactPlan(prepared, options = {}) {
|
|
|
2102
2300
|
const generationArtifacts = { ...partial, manifest };
|
|
2103
2301
|
return {
|
|
2104
2302
|
...generationArtifacts,
|
|
2105
|
-
files: createGenerationArtifactFiles(generationArtifacts, generation),
|
|
2303
|
+
files: createGenerationArtifactFiles(generationArtifacts, generation, nativeUrls),
|
|
2106
2304
|
inputs,
|
|
2107
2305
|
project: prepared.project,
|
|
2108
2306
|
schemaVersion: tileflowArtifactPlanSchemaVersion,
|
|
@@ -2115,6 +2313,7 @@ async function createTileflowArtifactPlan(prepared, options = {}) {
|
|
|
2115
2313
|
}
|
|
2116
2314
|
async function createTileflowBuildArtifacts(options = {}) {
|
|
2117
2315
|
resolveTileflowRenderer(options.renderer);
|
|
2316
|
+
assertNativeDevelopmentOrigin(options);
|
|
2118
2317
|
const loaded = await loadValidTileflowConfigWithInputs(
|
|
2119
2318
|
options.config ?? defaultTileflowConfigPath,
|
|
2120
2319
|
{
|
|
@@ -2141,7 +2340,7 @@ function getStableTileflowArtifactFiles(artifacts) {
|
|
|
2141
2340
|
{
|
|
2142
2341
|
contentType: "application/json; charset=utf-8",
|
|
2143
2342
|
fileName: tileflowMapBuildManifestFileName,
|
|
2144
|
-
source: `${
|
|
2343
|
+
source: `${serializeCanonicalJson3(artifacts.buildManifest)}
|
|
2145
2344
|
`
|
|
2146
2345
|
},
|
|
2147
2346
|
{
|
|
@@ -2163,7 +2362,7 @@ function getStableTileflowArtifactFiles(artifacts) {
|
|
|
2163
2362
|
{
|
|
2164
2363
|
contentType: "application/json; charset=utf-8",
|
|
2165
2364
|
fileName: tileflowNativeBuildRecordFileName,
|
|
2166
|
-
source: `${
|
|
2365
|
+
source: `${serializeCanonicalJson3(artifacts.nativeBuild)}
|
|
2167
2366
|
`
|
|
2168
2367
|
}
|
|
2169
2368
|
] : []
|
|
@@ -2216,7 +2415,7 @@ function createGenerationManifest(manifest, generation, assets) {
|
|
|
2216
2415
|
)
|
|
2217
2416
|
};
|
|
2218
2417
|
}
|
|
2219
|
-
function createGenerationArtifactFiles(artifacts, generation) {
|
|
2418
|
+
function createGenerationArtifactFiles(artifacts, generation, nativeUrls) {
|
|
2220
2419
|
const prefix = `generations/${generation}`;
|
|
2221
2420
|
const stableFiles = getStableTileflowArtifactFiles(artifacts);
|
|
2222
2421
|
const immutableStyles = Object.entries(artifacts.styles).flatMap(
|
|
@@ -2236,7 +2435,7 @@ function createGenerationArtifactFiles(artifacts, generation) {
|
|
|
2236
2435
|
(source) => retargetLocalFontAssetUrl(source, artifacts.assets, generation)
|
|
2237
2436
|
);
|
|
2238
2437
|
if (artifacts.nativeBuild)
|
|
2239
|
-
assertTileflowNativeGeneratedStyle(generationStyle, mapName, themeName);
|
|
2438
|
+
assertTileflowNativeGeneratedStyle(generationStyle, mapName, themeName, nativeUrls);
|
|
2240
2439
|
return {
|
|
2241
2440
|
contentType: "application/json; charset=utf-8",
|
|
2242
2441
|
fileName: `${prefix}/styles/${mapName}/${themeName}.json`,
|
|
@@ -2292,12 +2491,12 @@ function getMapBuildAssets(mapName, assets, fontAssets) {
|
|
|
2292
2491
|
);
|
|
2293
2492
|
}
|
|
2294
2493
|
function hashBytes(source) {
|
|
2295
|
-
return
|
|
2494
|
+
return createHash4("sha256").update(typeof source === "string" ? Buffer.from(source, "utf8") : Buffer.from(source)).digest("hex");
|
|
2296
2495
|
}
|
|
2297
2496
|
async function createTileflowBuildProvenance(cwd) {
|
|
2298
|
-
const projectDirectory =
|
|
2497
|
+
const projectDirectory = resolve4(cwd);
|
|
2299
2498
|
const projectRequire = createRequire(
|
|
2300
|
-
|
|
2499
|
+
resolve4(projectDirectory, "__tileflow_build_provenance__.cjs")
|
|
2301
2500
|
);
|
|
2302
2501
|
const toolRequire = createRequire(import.meta.url);
|
|
2303
2502
|
const packages = {};
|
|
@@ -2331,7 +2530,7 @@ async function readPackageVersion(expectedName, path) {
|
|
|
2331
2530
|
if (!path) throw new Error(`Unable to resolve build package provenance for ${expectedName}.`);
|
|
2332
2531
|
let parsed;
|
|
2333
2532
|
try {
|
|
2334
|
-
parsed = JSON.parse(await
|
|
2533
|
+
parsed = JSON.parse(await readFile2(path, "utf8"));
|
|
2335
2534
|
} catch {
|
|
2336
2535
|
throw new Error(`Unable to read build package provenance for ${expectedName}.`);
|
|
2337
2536
|
}
|
|
@@ -2354,7 +2553,7 @@ async function findNearestLockfile(cwd) {
|
|
|
2354
2553
|
try {
|
|
2355
2554
|
return {
|
|
2356
2555
|
format: candidate.format,
|
|
2357
|
-
source: await
|
|
2556
|
+
source: await readFile2(resolve4(directory, candidate.fileName))
|
|
2358
2557
|
};
|
|
2359
2558
|
} catch (error) {
|
|
2360
2559
|
if (!hasErrorCode2(error, "ENOENT")) throw error;
|
|
@@ -2430,7 +2629,7 @@ async function writeTileflowArtifactPlan(artifacts, options) {
|
|
|
2430
2629
|
const outDir = artifacts.nativeBuild ? `${options.outDir.replace(/[\\/]$/u, "")}/native` : options.outDir;
|
|
2431
2630
|
const output = await prepareTileflowOutputDirectory(cwd, outDir);
|
|
2432
2631
|
const files = getTileflowArtifactFiles(artifacts);
|
|
2433
|
-
await assertTileflowSelfHostedManifestTarget(
|
|
2632
|
+
await assertTileflowSelfHostedManifestTarget(resolve4(output.outDir, "manifest.json"), options);
|
|
2434
2633
|
await commitTileflowArtifactFiles(output, files);
|
|
2435
2634
|
}
|
|
2436
2635
|
function assertNoLocalTilesetsForProduction(artifacts) {
|
|
@@ -2443,7 +2642,7 @@ async function assertTileflowSelfHostedManifestTarget(manifestPath, options = {}
|
|
|
2443
2642
|
if (options.overwriteHostedManifest) return;
|
|
2444
2643
|
let source;
|
|
2445
2644
|
try {
|
|
2446
|
-
source = await
|
|
2645
|
+
source = await readFile2(resolve4(manifestPath), "utf8");
|
|
2447
2646
|
} catch (error) {
|
|
2448
2647
|
if (hasErrorCode2(error, "ENOENT")) return;
|
|
2449
2648
|
throw error;
|
|
@@ -2451,7 +2650,7 @@ async function assertTileflowSelfHostedManifestTarget(manifestPath, options = {}
|
|
|
2451
2650
|
try {
|
|
2452
2651
|
const manifest = parseTileflowRuntimeManifest(JSON.parse(source));
|
|
2453
2652
|
if (isHostedRuntimeManifest(manifest)) {
|
|
2454
|
-
throw new TileflowHostedManifestOverwriteError(
|
|
2653
|
+
throw new TileflowHostedManifestOverwriteError(resolve4(manifestPath));
|
|
2455
2654
|
}
|
|
2456
2655
|
} catch (error) {
|
|
2457
2656
|
if (error instanceof TileflowHostedManifestOverwriteError) throw error;
|
|
@@ -2499,15 +2698,15 @@ async function commitTileflowArtifactFiles(output, files) {
|
|
|
2499
2698
|
...staleNames,
|
|
2500
2699
|
tileflowArtifactInventoryFileName
|
|
2501
2700
|
]);
|
|
2502
|
-
const stageDir = await mkdtemp(
|
|
2503
|
-
const newDir =
|
|
2504
|
-
const backupDir =
|
|
2701
|
+
const stageDir = await mkdtemp(resolve4(outDir, ".tileflow-stage-"));
|
|
2702
|
+
const newDir = resolve4(stageDir, "new");
|
|
2703
|
+
const backupDir = resolve4(stageDir, "backup");
|
|
2505
2704
|
const backedUp = /* @__PURE__ */ new Set();
|
|
2506
2705
|
const installed = /* @__PURE__ */ new Set();
|
|
2507
2706
|
try {
|
|
2508
2707
|
for (const file of stagedFiles) {
|
|
2509
2708
|
const stagePath = managedPath(newDir, file.fileName);
|
|
2510
|
-
await
|
|
2709
|
+
await mkdir3(dirname2(stagePath), { recursive: true });
|
|
2511
2710
|
await writeFileDurably(stagePath, file.source);
|
|
2512
2711
|
}
|
|
2513
2712
|
for (const name of affectedNames) {
|
|
@@ -2515,7 +2714,7 @@ async function commitTileflowArtifactFiles(output, files) {
|
|
|
2515
2714
|
const targetPath = managedPath(outDir, name);
|
|
2516
2715
|
if (!await isFile(targetPath)) continue;
|
|
2517
2716
|
const backupPath = managedPath(backupDir, name);
|
|
2518
|
-
await
|
|
2717
|
+
await mkdir3(dirname2(backupPath), { recursive: true });
|
|
2519
2718
|
await copyFile2(targetPath, backupPath);
|
|
2520
2719
|
backedUp.add(name);
|
|
2521
2720
|
}
|
|
@@ -2529,8 +2728,8 @@ async function commitTileflowArtifactFiles(output, files) {
|
|
|
2529
2728
|
)) {
|
|
2530
2729
|
const sourcePath = managedPath(newDir, name);
|
|
2531
2730
|
const targetPath = managedPath(outDir, name);
|
|
2532
|
-
await
|
|
2533
|
-
await
|
|
2731
|
+
await mkdir3(dirname2(targetPath), { recursive: true });
|
|
2732
|
+
await rename2(sourcePath, targetPath);
|
|
2534
2733
|
installed.add(name);
|
|
2535
2734
|
}
|
|
2536
2735
|
for (const name of staleNames) {
|
|
@@ -2538,13 +2737,13 @@ async function commitTileflowArtifactFiles(output, files) {
|
|
|
2538
2737
|
}
|
|
2539
2738
|
const stagedInventoryPath = managedPath(newDir, tileflowArtifactInventoryFileName);
|
|
2540
2739
|
const inventoryPath = managedPath(outDir, tileflowArtifactInventoryFileName);
|
|
2541
|
-
await
|
|
2740
|
+
await rename2(stagedInventoryPath, inventoryPath);
|
|
2542
2741
|
installed.add(tileflowArtifactInventoryFileName);
|
|
2543
2742
|
} catch (error) {
|
|
2544
2743
|
await rollbackManagedFiles(outDir, backupDir, affectedNames, backedUp, installed);
|
|
2545
2744
|
throw error;
|
|
2546
2745
|
} finally {
|
|
2547
|
-
await
|
|
2746
|
+
await rm3(stageDir, { force: true, recursive: true });
|
|
2548
2747
|
}
|
|
2549
2748
|
}
|
|
2550
2749
|
function getArtifactGeneration(files) {
|
|
@@ -2576,7 +2775,7 @@ async function rollbackManagedFiles(outDir, backupDir, affectedNames, backedUp,
|
|
|
2576
2775
|
await removeManagedFile(targetPath);
|
|
2577
2776
|
}
|
|
2578
2777
|
if (backedUp.has(name)) {
|
|
2579
|
-
await
|
|
2778
|
+
await mkdir3(dirname2(targetPath), { recursive: true });
|
|
2580
2779
|
await copyFile2(managedPath(backupDir, name), targetPath);
|
|
2581
2780
|
}
|
|
2582
2781
|
} catch (error) {
|
|
@@ -2591,7 +2790,7 @@ async function readTileflowArtifactInventory(outDir) {
|
|
|
2591
2790
|
const inventoryPath = managedPath(outDir, tileflowArtifactInventoryFileName);
|
|
2592
2791
|
let source;
|
|
2593
2792
|
try {
|
|
2594
|
-
source = await
|
|
2793
|
+
source = await readFile2(inventoryPath, "utf8");
|
|
2595
2794
|
} catch (error) {
|
|
2596
2795
|
if (hasErrorCode2(error, "ENOENT")) return void 0;
|
|
2597
2796
|
throw error;
|
|
@@ -2625,7 +2824,7 @@ async function readTileflowArtifactInventory(outDir) {
|
|
|
2625
2824
|
}
|
|
2626
2825
|
async function assertManagedTargetIsSafe(outDir, fileName) {
|
|
2627
2826
|
assertSafeManagedFileName(fileName);
|
|
2628
|
-
const rootStats = await
|
|
2827
|
+
const rootStats = await lstat3(outDir);
|
|
2629
2828
|
if (rootStats.isSymbolicLink()) {
|
|
2630
2829
|
throw new Error(`Tileflow refuses to write through a symbolic link: ${outDir}`);
|
|
2631
2830
|
}
|
|
@@ -2635,9 +2834,9 @@ async function assertManagedTargetIsSafe(outDir, fileName) {
|
|
|
2635
2834
|
const segments = fileName.split("/");
|
|
2636
2835
|
let current = outDir;
|
|
2637
2836
|
for (let index = 0; index < segments.length; index += 1) {
|
|
2638
|
-
current =
|
|
2837
|
+
current = resolve4(current, segments[index]);
|
|
2639
2838
|
try {
|
|
2640
|
-
const stats = await
|
|
2839
|
+
const stats = await lstat3(current);
|
|
2641
2840
|
if (stats.isSymbolicLink()) {
|
|
2642
2841
|
throw new Error(`Tileflow refuses to write through a symbolic link: ${fileName}`);
|
|
2643
2842
|
}
|
|
@@ -2654,12 +2853,12 @@ async function assertManagedTargetIsSafe(outDir, fileName) {
|
|
|
2654
2853
|
}
|
|
2655
2854
|
}
|
|
2656
2855
|
async function prepareTileflowOutputDirectory(cwd, requestedOutDir) {
|
|
2657
|
-
const logicalRoot =
|
|
2658
|
-
const logicalOutDir =
|
|
2856
|
+
const logicalRoot = resolve4(cwd);
|
|
2857
|
+
const logicalOutDir = resolve4(logicalRoot, requestedOutDir);
|
|
2659
2858
|
if (!isPathWithin(logicalRoot, logicalOutDir)) {
|
|
2660
2859
|
throw new Error(`Tileflow artifact output escapes its working directory: ${logicalOutDir}`);
|
|
2661
2860
|
}
|
|
2662
|
-
const rootStats = await
|
|
2861
|
+
const rootStats = await lstat3(logicalRoot);
|
|
2663
2862
|
if (!rootStats.isDirectory()) {
|
|
2664
2863
|
throw new Error(`Tileflow artifact working directory is not a directory: ${logicalRoot}`);
|
|
2665
2864
|
}
|
|
@@ -2668,14 +2867,14 @@ async function prepareTileflowOutputDirectory(cwd, requestedOutDir) {
|
|
|
2668
2867
|
const segments = relativeOutDir ? relativeOutDir.split(sep2) : [];
|
|
2669
2868
|
let current = rootDir;
|
|
2670
2869
|
for (const segment of segments) {
|
|
2671
|
-
current =
|
|
2870
|
+
current = resolve4(current, segment);
|
|
2672
2871
|
let stats;
|
|
2673
2872
|
try {
|
|
2674
|
-
stats = await
|
|
2873
|
+
stats = await lstat3(current);
|
|
2675
2874
|
} catch (error) {
|
|
2676
2875
|
if (!hasErrorCode2(error, "ENOENT")) throw error;
|
|
2677
|
-
await
|
|
2678
|
-
stats = await
|
|
2876
|
+
await mkdir3(current);
|
|
2877
|
+
stats = await lstat3(current);
|
|
2679
2878
|
}
|
|
2680
2879
|
if (stats.isSymbolicLink()) {
|
|
2681
2880
|
throw new Error(`Tileflow refuses to write through a symbolic link: ${current}`);
|
|
@@ -2693,7 +2892,7 @@ async function assertTileflowOutputDirectoryIsSafe(output) {
|
|
|
2693
2892
|
if (!isPathWithin(output.rootDir, output.outDir)) {
|
|
2694
2893
|
throw new Error(`Tileflow artifact output escapes its working directory: ${output.outDir}`);
|
|
2695
2894
|
}
|
|
2696
|
-
const stats = await
|
|
2895
|
+
const stats = await lstat3(output.outDir);
|
|
2697
2896
|
if (stats.isSymbolicLink()) {
|
|
2698
2897
|
throw new Error(`Tileflow refuses to write through a symbolic link: ${output.outDir}`);
|
|
2699
2898
|
}
|
|
@@ -2722,7 +2921,7 @@ function assertTileflowArtifactFileName(fileName) {
|
|
|
2722
2921
|
}
|
|
2723
2922
|
function managedPath(root, fileName) {
|
|
2724
2923
|
assertSafeManagedFileName(fileName);
|
|
2725
|
-
const path =
|
|
2924
|
+
const path = resolve4(root, ...fileName.split("/"));
|
|
2726
2925
|
if (!isPathWithin(root, path)) {
|
|
2727
2926
|
throw new Error(`Unsafe Tileflow artifact file name: ${fileName}`);
|
|
2728
2927
|
}
|
|
@@ -2739,7 +2938,7 @@ async function writeFileDurably(path, source) {
|
|
|
2739
2938
|
}
|
|
2740
2939
|
async function isFile(path) {
|
|
2741
2940
|
try {
|
|
2742
|
-
return (await
|
|
2941
|
+
return (await lstat3(path)).isFile();
|
|
2743
2942
|
} catch (error) {
|
|
2744
2943
|
if (hasErrorCode2(error, "ENOENT")) return false;
|
|
2745
2944
|
throw error;
|
|
@@ -2747,7 +2946,7 @@ async function isFile(path) {
|
|
|
2747
2946
|
}
|
|
2748
2947
|
async function removeManagedFile(path) {
|
|
2749
2948
|
try {
|
|
2750
|
-
const stats = await
|
|
2949
|
+
const stats = await lstat3(path);
|
|
2751
2950
|
if (!stats.isFile()) throw new Error(`Refusing to remove non-file artifact target: ${path}`);
|
|
2752
2951
|
await unlink(path);
|
|
2753
2952
|
} catch (error) {
|
|
@@ -2755,7 +2954,7 @@ async function removeManagedFile(path) {
|
|
|
2755
2954
|
}
|
|
2756
2955
|
}
|
|
2757
2956
|
function hashArtifactGeneration(files) {
|
|
2758
|
-
const hash =
|
|
2957
|
+
const hash = createHash4("sha256");
|
|
2759
2958
|
for (const file of files) {
|
|
2760
2959
|
const source = typeof file.source === "string" ? Buffer.from(file.source, "utf8") : Buffer.from(file.source);
|
|
2761
2960
|
hash.update(`${file.fileName}\0${file.contentType}\0${source.byteLength}\0`, "utf8");
|
|
@@ -2763,6 +2962,13 @@ function hashArtifactGeneration(files) {
|
|
|
2763
2962
|
}
|
|
2764
2963
|
return hash.digest("hex");
|
|
2765
2964
|
}
|
|
2965
|
+
function assertNativeDevelopmentOrigin(options) {
|
|
2966
|
+
if (options.developmentOrigin !== void 0 && (resolveTileflowRenderer(options.renderer) !== "native" || options.target === "production")) {
|
|
2967
|
+
throw new TileflowNativeCompatibilityError4([
|
|
2968
|
+
createTileflowNativeDiagnostic3("NATIVE_RENDERER_UNSUPPORTED", "/developmentOrigin")
|
|
2969
|
+
]);
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2766
2972
|
function resolveRendererAssetBaseUrl(options) {
|
|
2767
2973
|
const base = resolveAssetBaseUrl(options);
|
|
2768
2974
|
return options.renderer === "native" ? nativePublicBase(base) : base;
|
|
@@ -2787,12 +2993,12 @@ function hasErrorCode2(error, code) {
|
|
|
2787
2993
|
return Boolean(error && typeof error === "object" && "code" in error && error.code === code);
|
|
2788
2994
|
}
|
|
2789
2995
|
function canonicalInputPath(path) {
|
|
2790
|
-
const resolved =
|
|
2996
|
+
const resolved = resolve4(path);
|
|
2791
2997
|
try {
|
|
2792
2998
|
return realpathSync2.native(resolved);
|
|
2793
2999
|
} catch {
|
|
2794
3000
|
try {
|
|
2795
|
-
return
|
|
3001
|
+
return resolve4(realpathSync2.native(dirname2(resolved)), resolved.split(/[\\/]/).at(-1));
|
|
2796
3002
|
} catch {
|
|
2797
3003
|
return resolved;
|
|
2798
3004
|
}
|
|
@@ -2800,6 +3006,10 @@ function canonicalInputPath(path) {
|
|
|
2800
3006
|
}
|
|
2801
3007
|
|
|
2802
3008
|
export {
|
|
3009
|
+
tileflowLineBackgroundGlyphRoute,
|
|
3010
|
+
usesTileflowLineBackgroundGlyphs,
|
|
3011
|
+
parseTileflowLineBackgroundGlyphPath,
|
|
3012
|
+
createTileflowLineBackgroundGlyphResponder,
|
|
2803
3013
|
prepareTileflowLocalTilesets,
|
|
2804
3014
|
tileflowArtifactSessionSchemaVersion,
|
|
2805
3015
|
createTileflowArtifactDiagnostics,
|