@tileflow/dev 0.1.0-alpha.19 → 0.1.0-alpha.20
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 +88 -44
- package/dist/artifacts.d.ts +42 -3
- package/dist/artifacts.js +13 -4
- package/dist/{chunk-BKHIZ7H2.js → chunk-6MGFMKG3.js} +3 -6
- package/dist/{chunk-BALG4DSG.js → chunk-CENQR5Q2.js} +2 -2
- package/dist/{chunk-OEABXOJH.js → chunk-KH7CMHT3.js} +117 -39
- package/dist/{chunk-ZRI6PNFL.js → chunk-LNCTVFUD.js} +32 -7
- package/dist/{chunk-GWUMIHL6.js → chunk-MIGBHCXX.js} +29 -15
- package/dist/{chunk-ZOYFZ3MT.js → chunk-MKPSQSMX.js} +7 -3
- package/dist/chunk-ONYQNLNA.js +1108 -0
- package/dist/{chunk-VEB43KDS.js → chunk-RSBV4L2Y.js} +7 -6
- package/dist/{chunk-O7PE6FB2.js → chunk-XTRHREXO.js} +629 -151
- package/dist/config.js +1 -1
- package/dist/fonts.js +1 -1
- package/dist/icons.js +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +27 -9
- package/dist/inspect.d.ts +3 -1
- package/dist/inspect.js +7 -5
- package/dist/preview.d.ts +1 -1
- package/dist/preview.js +2 -2
- package/dist/server.d.ts +7 -3
- package/dist/server.js +6 -5
- package/dist/tilesets-C0tGeenP.d.ts +94 -0
- package/dist/tilesets.d.ts +1 -0
- package/dist/tilesets.js +6 -0
- package/dist/validation.d.ts +7 -1
- package/dist/validation.js +3 -1
- package/package.json +9 -4
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
inspectTileflowPmtilesForLocalUse
|
|
3
|
+
} from "./chunk-ONYQNLNA.js";
|
|
1
4
|
import {
|
|
2
5
|
getTileflowFontWatchPaths,
|
|
6
|
+
joinTileflowPublicUrl,
|
|
3
7
|
prepareTileflowStyleFonts,
|
|
4
8
|
replaceTileflowStyleFontSources
|
|
5
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-RSBV4L2Y.js";
|
|
6
10
|
import {
|
|
7
11
|
getTileflowIconWatchPaths,
|
|
8
12
|
prepareTileflowCatalogIcons
|
|
9
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-CENQR5Q2.js";
|
|
10
14
|
import {
|
|
11
15
|
TileflowStyleValidationError,
|
|
12
16
|
normalizeTileflowStyleValidationIssues,
|
|
@@ -17,7 +21,7 @@ import {
|
|
|
17
21
|
defaultTileflowConfigPath,
|
|
18
22
|
getTileflowMapNames,
|
|
19
23
|
loadValidTileflowConfigWithInputs
|
|
20
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-6MGFMKG3.js";
|
|
21
25
|
import {
|
|
22
26
|
isPathWithin
|
|
23
27
|
} from "./chunk-D4OE4ITL.js";
|
|
@@ -26,28 +30,27 @@ import {
|
|
|
26
30
|
import { createHash } from "crypto";
|
|
27
31
|
import { realpathSync as realpathSync2 } from "fs";
|
|
28
32
|
import {
|
|
29
|
-
copyFile,
|
|
30
|
-
lstat,
|
|
31
|
-
mkdir,
|
|
33
|
+
copyFile as copyFile2,
|
|
34
|
+
lstat as lstat2,
|
|
35
|
+
mkdir as mkdir2,
|
|
32
36
|
mkdtemp,
|
|
33
|
-
open,
|
|
37
|
+
open as open2,
|
|
34
38
|
readFile,
|
|
35
|
-
realpath,
|
|
39
|
+
realpath as realpath2,
|
|
36
40
|
rename,
|
|
37
|
-
rm,
|
|
41
|
+
rm as rm2,
|
|
38
42
|
unlink
|
|
39
43
|
} from "fs/promises";
|
|
40
44
|
import { createRequire } from "module";
|
|
41
|
-
import { dirname as dirname2, relative as relative2, resolve as
|
|
45
|
+
import { dirname as dirname2, relative as relative2, resolve as resolve3, sep as sep2 } from "path";
|
|
42
46
|
import {
|
|
43
|
-
compareCodeUnits,
|
|
47
|
+
compareCodeUnits as compareCodeUnits2,
|
|
44
48
|
getTileflowStyleFontFaces,
|
|
45
49
|
serializeCanonicalJson,
|
|
46
50
|
tileflowMapIdSchema,
|
|
47
51
|
tileflowThemeNameSchema
|
|
48
52
|
} from "@tileflow/core";
|
|
49
53
|
import {
|
|
50
|
-
collectTileflowMapBuildLineage,
|
|
51
54
|
createManifest,
|
|
52
55
|
createStyleFromCatalog,
|
|
53
56
|
createStylesFromCatalog,
|
|
@@ -57,10 +60,325 @@ import {
|
|
|
57
60
|
} from "@tileflow/core/build";
|
|
58
61
|
import { parseTileflowRuntimeManifest } from "@tileflow/core/manifest";
|
|
59
62
|
|
|
63
|
+
// src/local-tilesets.ts
|
|
64
|
+
import { randomUUID } from "crypto";
|
|
65
|
+
import { constants } from "fs";
|
|
66
|
+
import { chmod, copyFile, lstat, mkdir, open, readdir, realpath, rm, stat } from "fs/promises";
|
|
67
|
+
import { basename, resolve } from "path";
|
|
68
|
+
import { compareCodeUnits } from "@tileflow/core";
|
|
69
|
+
var minimumLocalPmtilesBytes = 127n;
|
|
70
|
+
var maximumLocalPmtilesBytes = 8000000000n;
|
|
71
|
+
async function prepareTileflowLocalTilesets(project, styles, options) {
|
|
72
|
+
const hasSources = Object.values(project.maps).some(
|
|
73
|
+
(map) => Object.keys(map.sources ?? {}).length > 0
|
|
74
|
+
);
|
|
75
|
+
if (!hasSources) {
|
|
76
|
+
return { dispose: async () => void 0, files: [], styles, watchPaths: [] };
|
|
77
|
+
}
|
|
78
|
+
const logicalProjectRoot = resolve(options.cwd);
|
|
79
|
+
const projectRoot = await realpath(logicalProjectRoot).catch(() => logicalProjectRoot);
|
|
80
|
+
const snapshotStore = resolve(logicalProjectRoot, ".tileflow/cache/pmtiles-snapshots/v1");
|
|
81
|
+
await assertSafeSnapshotStore(logicalProjectRoot, snapshotStore);
|
|
82
|
+
await collectAbandonedSnapshotGenerations(snapshotStore);
|
|
83
|
+
const generationId = `snapshot-${process.pid}-${randomUUID().replaceAll("-", "")}`;
|
|
84
|
+
const generationDirectory = resolve(snapshotStore, generationId);
|
|
85
|
+
await mkdir(generationDirectory);
|
|
86
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
87
|
+
const files = /* @__PURE__ */ new Map();
|
|
88
|
+
const snapshots = /* @__PURE__ */ new Map();
|
|
89
|
+
const output = Object.fromEntries(
|
|
90
|
+
Object.entries(styles).map(([mapName, family]) => [
|
|
91
|
+
mapName,
|
|
92
|
+
Object.fromEntries(
|
|
93
|
+
Object.entries(family).map(([themeName, style]) => [themeName, cloneStyle(style)])
|
|
94
|
+
)
|
|
95
|
+
])
|
|
96
|
+
);
|
|
97
|
+
let disposed = false;
|
|
98
|
+
const dispose = async () => {
|
|
99
|
+
if (disposed) return;
|
|
100
|
+
disposed = true;
|
|
101
|
+
if (!isPathWithin(snapshotStore, generationDirectory)) {
|
|
102
|
+
throw new Error("Local PMTiles snapshot generation escaped its store.");
|
|
103
|
+
}
|
|
104
|
+
await rm(generationDirectory, { force: true, recursive: true });
|
|
105
|
+
};
|
|
106
|
+
try {
|
|
107
|
+
for (const mapName of Object.keys(project.maps).sort(compareCodeUnits)) {
|
|
108
|
+
const map = project.maps[mapName];
|
|
109
|
+
for (const [sourceId, source] of Object.entries(map.sources ?? {}).sort(
|
|
110
|
+
([left], [right]) => compareCodeUnits(left, right)
|
|
111
|
+
)) {
|
|
112
|
+
const requestedPath = resolve(options.baseDirectory, source.local);
|
|
113
|
+
const sourcePath = await realpath(requestedPath).catch(() => requestedPath);
|
|
114
|
+
if (!isPathWithin(projectRoot, sourcePath)) {
|
|
115
|
+
throw localTilesetError(
|
|
116
|
+
"TF_LOCAL_TILESET_OUTSIDE_ROOT",
|
|
117
|
+
`maps.${mapName}.sources.${sourceId}.local`,
|
|
118
|
+
"Local hosted tileset must stay inside the project root."
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
const sourceStat = await lstat(sourcePath).catch(() => null);
|
|
122
|
+
if (!sourceStat?.isFile() || sourceStat.isSymbolicLink()) {
|
|
123
|
+
throw localTilesetError(
|
|
124
|
+
"TF_LOCAL_TILESET_NOT_FOUND",
|
|
125
|
+
`maps.${mapName}.sources.${sourceId}.local`,
|
|
126
|
+
`Local hosted tileset "${sourceId}" was not found.`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
let snapshot = snapshots.get(sourcePath);
|
|
130
|
+
if (!snapshot) {
|
|
131
|
+
snapshot = await createLocalTilesetSnapshot(
|
|
132
|
+
sourcePath,
|
|
133
|
+
generationDirectory,
|
|
134
|
+
snapshots.size
|
|
135
|
+
);
|
|
136
|
+
snapshots.set(sourcePath, snapshot);
|
|
137
|
+
}
|
|
138
|
+
const file = {
|
|
139
|
+
byteLength: snapshot.byteLength,
|
|
140
|
+
etag: snapshot.etag,
|
|
141
|
+
fileName: `tilesets/${source.tileset}.pmtiles`,
|
|
142
|
+
logicalId: source.tileset,
|
|
143
|
+
sourcePath: snapshot.sourcePath
|
|
144
|
+
};
|
|
145
|
+
const existing = bindings.get(source.tileset);
|
|
146
|
+
if (existing && existing.originalSourcePath !== sourcePath) {
|
|
147
|
+
throw localTilesetError(
|
|
148
|
+
"TF_LOCAL_TILESET_BINDING_CONFLICT",
|
|
149
|
+
`maps.${mapName}.sources.${sourceId}.local`,
|
|
150
|
+
`Logical tileset "${source.tileset}" resolves to different local archives in one generation.`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
bindings.set(source.tileset, { file, originalSourcePath: sourcePath });
|
|
154
|
+
files.set(file.fileName, files.get(file.fileName) ?? file);
|
|
155
|
+
validateSourceContract(
|
|
156
|
+
mapName,
|
|
157
|
+
sourceId,
|
|
158
|
+
source.type,
|
|
159
|
+
output[mapName] ?? {},
|
|
160
|
+
snapshot.inspection
|
|
161
|
+
);
|
|
162
|
+
const publicUrl = joinTileflowPublicUrl(options.assetBaseUrl, file.fileName);
|
|
163
|
+
for (const style of Object.values(output[mapName] ?? {})) {
|
|
164
|
+
const definition = style.sources[sourceId];
|
|
165
|
+
if (!definition) {
|
|
166
|
+
throw localTilesetError(
|
|
167
|
+
"TF_LOCAL_TILESET_SOURCE_MISSING",
|
|
168
|
+
`maps.${mapName}.sources.${sourceId}`,
|
|
169
|
+
`Compiled style is missing hosted source "${sourceId}".`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
definition.url = `tileflow-pmtiles://${publicUrl}`;
|
|
173
|
+
delete definition.tiles;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
dispose,
|
|
179
|
+
files: [...files.values()].sort(
|
|
180
|
+
(left, right) => compareCodeUnits(left.fileName, right.fileName)
|
|
181
|
+
),
|
|
182
|
+
styles: output,
|
|
183
|
+
watchPaths: [...snapshots.keys()].sort(compareCodeUnits)
|
|
184
|
+
};
|
|
185
|
+
} catch (error) {
|
|
186
|
+
await dispose().catch(() => void 0);
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async function createLocalTilesetSnapshot(sourcePath, generationDirectory, index) {
|
|
191
|
+
const sourceHandle = await open(sourcePath, "r");
|
|
192
|
+
const snapshotPath = resolve(generationDirectory, `${index}.pmtiles`);
|
|
193
|
+
try {
|
|
194
|
+
const before = sourceIdentity(await sourceHandle.stat({ bigint: true }));
|
|
195
|
+
if (before.size < minimumLocalPmtilesBytes || before.size > maximumLocalPmtilesBytes) {
|
|
196
|
+
throw localTilesetError(
|
|
197
|
+
"TF_TILESET_ARCHIVE_INVALID",
|
|
198
|
+
"archive",
|
|
199
|
+
"Expected one bounded PMTiles file."
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
await copyFile(sourcePath, snapshotPath, constants.COPYFILE_FICLONE);
|
|
203
|
+
await syncFile(snapshotPath);
|
|
204
|
+
const [afterHandle, afterPath, snapshotStat] = await Promise.all([
|
|
205
|
+
sourceHandle.stat({ bigint: true }).then(sourceIdentity),
|
|
206
|
+
stat(sourcePath, { bigint: true }).then(sourceIdentity),
|
|
207
|
+
stat(snapshotPath, { bigint: true })
|
|
208
|
+
]);
|
|
209
|
+
assertSourceIdentity(before, afterHandle);
|
|
210
|
+
assertSourceIdentity(before, afterPath);
|
|
211
|
+
if (!snapshotStat.isFile() || snapshotStat.size !== before.size) {
|
|
212
|
+
throw localTilesetError(
|
|
213
|
+
"TF_LOCAL_TILESET_CHANGED",
|
|
214
|
+
"local",
|
|
215
|
+
"Local hosted tileset changed while its immutable snapshot was prepared."
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
const inspection = await inspectTileflowPmtilesForLocalUse(snapshotPath);
|
|
219
|
+
await chmod(snapshotPath, 292);
|
|
220
|
+
return Object.freeze({
|
|
221
|
+
byteLength: Number(before.size),
|
|
222
|
+
etag: `"${generationIdFromDirectory(generationDirectory)}-${index}"`,
|
|
223
|
+
inspection,
|
|
224
|
+
sourcePath: snapshotPath
|
|
225
|
+
});
|
|
226
|
+
} finally {
|
|
227
|
+
await sourceHandle.close();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function generationIdFromDirectory(path) {
|
|
231
|
+
const generationId = basename(path);
|
|
232
|
+
if (!/^snapshot-\d+-[a-f0-9]{32}$/u.test(generationId)) {
|
|
233
|
+
throw new Error("Local PMTiles snapshot generation is invalid.");
|
|
234
|
+
}
|
|
235
|
+
return generationId;
|
|
236
|
+
}
|
|
237
|
+
function sourceIdentity(value) {
|
|
238
|
+
if (!value.isFile()) throw new Error("Local PMTiles source is not a file.");
|
|
239
|
+
return Object.freeze({
|
|
240
|
+
device: BigInt(value.dev),
|
|
241
|
+
inode: BigInt(value.ino),
|
|
242
|
+
mtimeNanoseconds: "mtimeNs" in value ? BigInt(value.mtimeNs) : BigInt(Math.round(value.mtimeMs * 1e6)),
|
|
243
|
+
size: BigInt(value.size)
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
function assertSourceIdentity(before, after) {
|
|
247
|
+
if (before.device !== after.device || before.inode !== after.inode || before.mtimeNanoseconds !== after.mtimeNanoseconds || before.size !== after.size) {
|
|
248
|
+
throw localTilesetError(
|
|
249
|
+
"TF_LOCAL_TILESET_CHANGED",
|
|
250
|
+
"local",
|
|
251
|
+
"Local hosted tileset changed while its immutable snapshot was prepared."
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function validateSourceContract(mapName, sourceId, sourceType, styles, inspection) {
|
|
256
|
+
const isVectorArchive = inspection.contract.tileType === "mlt" || inspection.contract.tileType === "mvt";
|
|
257
|
+
if (sourceType === "vector" !== isVectorArchive) {
|
|
258
|
+
throw localTilesetError(
|
|
259
|
+
"TF_SOURCE_TILE_TYPE_MISMATCH",
|
|
260
|
+
`maps.${mapName}.sources.${sourceId}.type`,
|
|
261
|
+
`Source "${sourceId}" declares ${sourceType} but the PMTiles archive contains ${inspection.contract.tileType}.`
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
if (!isVectorArchive || !inspection.contract.sourceLayersDeclared) return;
|
|
265
|
+
const availableLayers = new Map(
|
|
266
|
+
inspection.contract.sourceLayers.map((layer) => [layer.id, layer])
|
|
267
|
+
);
|
|
268
|
+
for (const style of Object.values(styles)) {
|
|
269
|
+
const requirements = asRecord(style.metadata?.["tileflow:sourceRequirements"]);
|
|
270
|
+
const sources = asRecord(requirements?.sources);
|
|
271
|
+
const source = asRecord(sources?.[sourceId]);
|
|
272
|
+
const sourceLayers = Array.isArray(source?.sourceLayers) ? source.sourceLayers : [];
|
|
273
|
+
for (const candidate of sourceLayers) {
|
|
274
|
+
const required = asRecord(candidate);
|
|
275
|
+
const layerId = required?.id;
|
|
276
|
+
if (typeof layerId !== "string") continue;
|
|
277
|
+
const available = availableLayers.get(layerId);
|
|
278
|
+
if (!available) {
|
|
279
|
+
const candidates = [...availableLayers.keys()].sort(compareCodeUnits);
|
|
280
|
+
throw localTilesetError(
|
|
281
|
+
"TF_SOURCE_LAYER_NOT_FOUND",
|
|
282
|
+
`maps.${mapName}.sources.${sourceId}.sourceLayers.${layerId}`,
|
|
283
|
+
`Source "${sourceId}" has no source-layer "${layerId}". Available: ${candidates.join(", ") || "(none)"}.`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
if (!available.fieldsDeclared) continue;
|
|
287
|
+
const availableFields = new Map(available.fields.map((field) => [field.name, field.type]));
|
|
288
|
+
const requiredFields = Array.isArray(required?.fields) ? required.fields : [];
|
|
289
|
+
for (const fieldCandidate of requiredFields) {
|
|
290
|
+
const field = asRecord(fieldCandidate);
|
|
291
|
+
const fieldName = field?.name;
|
|
292
|
+
if (typeof fieldName !== "string") continue;
|
|
293
|
+
if (!availableFields.has(fieldName)) {
|
|
294
|
+
const candidates = [...availableFields.keys()].sort(compareCodeUnits);
|
|
295
|
+
throw localTilesetError(
|
|
296
|
+
"TF_SOURCE_FIELD_NOT_FOUND",
|
|
297
|
+
`maps.${mapName}.sources.${sourceId}.sourceLayers.${layerId}.fields.${fieldName}`,
|
|
298
|
+
`Source-layer "${layerId}" has no field "${fieldName}". Available: ${candidates.join(", ") || "(none)"}.`
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
const requiredType = field?.type;
|
|
302
|
+
const actualType = availableFields.get(fieldName);
|
|
303
|
+
if (typeof requiredType === "string" && actualType !== void 0 && requiredType !== actualType) {
|
|
304
|
+
throw localTilesetError(
|
|
305
|
+
"TF_SOURCE_FIELD_TYPE_MISMATCH",
|
|
306
|
+
`maps.${mapName}.sources.${sourceId}.sourceLayers.${layerId}.fields.${fieldName}`,
|
|
307
|
+
`Field "${layerId}.${fieldName}" is ${actualType}; ${requiredType} is required.`
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
async function collectAbandonedSnapshotGenerations(store) {
|
|
315
|
+
const entries = await readdir(store, { withFileTypes: true }).catch(() => []);
|
|
316
|
+
for (const entry of entries) {
|
|
317
|
+
if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
|
|
318
|
+
const match = /^snapshot-(\d+)-[a-f0-9]{32}$/u.exec(entry.name);
|
|
319
|
+
if (!match?.[1] || processIsAlive(Number(match[1]))) continue;
|
|
320
|
+
const path = resolve(store, entry.name);
|
|
321
|
+
if (isPathWithin(store, path)) await rm(path, { force: true, recursive: true });
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
async function assertSafeSnapshotStore(projectRoot, store) {
|
|
325
|
+
const segments = [".tileflow", "cache", "pmtiles-snapshots", "v1"];
|
|
326
|
+
let current = projectRoot;
|
|
327
|
+
for (const segment of segments) {
|
|
328
|
+
current = resolve(current, segment);
|
|
329
|
+
try {
|
|
330
|
+
const value = await lstat(current);
|
|
331
|
+
if (!value.isDirectory() || value.isSymbolicLink()) {
|
|
332
|
+
throw new Error(`Local PMTiles snapshot path is unsafe: ${current}`);
|
|
333
|
+
}
|
|
334
|
+
} catch (error) {
|
|
335
|
+
if (!hasErrorCode(error, "ENOENT")) throw error;
|
|
336
|
+
await mkdir(current).catch((mkdirError) => {
|
|
337
|
+
if (!hasErrorCode(mkdirError, "EEXIST")) throw mkdirError;
|
|
338
|
+
});
|
|
339
|
+
const created = await lstat(current);
|
|
340
|
+
if (!created.isDirectory() || created.isSymbolicLink()) {
|
|
341
|
+
throw new Error(`Local PMTiles snapshot path is unsafe: ${current}`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (resolve(current) !== resolve(store)) {
|
|
346
|
+
throw new Error("Local PMTiles snapshot path escaped its project root.");
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
function processIsAlive(pid) {
|
|
350
|
+
try {
|
|
351
|
+
process.kill(pid, 0);
|
|
352
|
+
return true;
|
|
353
|
+
} catch (error) {
|
|
354
|
+
return !hasErrorCode(error, "ESRCH");
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
async function syncFile(path) {
|
|
358
|
+
const handle = await open(path, "r");
|
|
359
|
+
try {
|
|
360
|
+
await handle.sync();
|
|
361
|
+
} finally {
|
|
362
|
+
await handle.close();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
function cloneStyle(style) {
|
|
366
|
+
return JSON.parse(JSON.stringify(style));
|
|
367
|
+
}
|
|
368
|
+
function localTilesetError(code, path, message) {
|
|
369
|
+
return Object.assign(new Error(message), { code, path, phase: "local-tileset-resolution" });
|
|
370
|
+
}
|
|
371
|
+
function asRecord(value) {
|
|
372
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
373
|
+
}
|
|
374
|
+
function hasErrorCode(error, code) {
|
|
375
|
+
return Boolean(error && typeof error === "object" && error.code === code);
|
|
376
|
+
}
|
|
377
|
+
|
|
60
378
|
// src/session.ts
|
|
61
379
|
import { watch } from "chokidar";
|
|
62
380
|
import { realpathSync } from "fs";
|
|
63
|
-
import { dirname, extname, isAbsolute, relative, resolve, sep, win32 } from "path";
|
|
381
|
+
import { dirname, extname, isAbsolute, relative, resolve as resolve2, sep, win32 } from "path";
|
|
64
382
|
var tileflowArtifactSessionSchemaVersion = 1;
|
|
65
383
|
var configExtensions = /* @__PURE__ */ new Set([".cjs", ".cts", ".js", ".json", ".mjs", ".mts", ".ts", ".tsx"]);
|
|
66
384
|
var iconExtensions = /* @__PURE__ */ new Set([".jpeg", ".jpg", ".png", ".svg", ".webp"]);
|
|
@@ -96,20 +414,23 @@ var TileflowArtifactSessionImpl = class {
|
|
|
96
414
|
#ignoredPaths;
|
|
97
415
|
#watchEnabled;
|
|
98
416
|
#closed = false;
|
|
417
|
+
#closing;
|
|
99
418
|
#debounceTimer;
|
|
100
419
|
#generation = 0;
|
|
101
|
-
#
|
|
420
|
+
#current;
|
|
421
|
+
#artifactGenerations = /* @__PURE__ */ new Set();
|
|
102
422
|
#lastGoodGeneration;
|
|
423
|
+
#refreshes = /* @__PURE__ */ new Set();
|
|
103
424
|
#refreshToken = 0;
|
|
104
425
|
#state = { generation: 0, status: "building" };
|
|
105
426
|
#watcher;
|
|
106
427
|
#watchedArtifactPaths = /* @__PURE__ */ new Set();
|
|
107
428
|
constructor(options, buildArtifacts, discoverWatchPaths) {
|
|
108
|
-
this.#cwd =
|
|
109
|
-
this.#configPath =
|
|
429
|
+
this.#cwd = resolve2(options.cwd ?? process.cwd());
|
|
430
|
+
this.#configPath = resolve2(this.#cwd, options.config ?? "tileflow.config.ts");
|
|
110
431
|
this.#debounceMs = Math.max(0, Math.min(options.debounceMs ?? 75, 1e3));
|
|
111
432
|
this.#watchEnabled = options.watch ?? false;
|
|
112
|
-
this.#ignoredPaths = (options.ignoredPaths ?? []).map((path) =>
|
|
433
|
+
this.#ignoredPaths = (options.ignoredPaths ?? []).map((path) => resolve2(this.#cwd, path));
|
|
113
434
|
this.#buildArtifacts = buildArtifacts;
|
|
114
435
|
this.#discoverWatchPaths = discoverWatchPaths;
|
|
115
436
|
this.#buildOptions = {
|
|
@@ -118,7 +439,8 @@ var TileflowArtifactSessionImpl = class {
|
|
|
118
439
|
config: options.config,
|
|
119
440
|
cwd: this.#cwd,
|
|
120
441
|
inspection: options.inspection,
|
|
121
|
-
styleBaseUrl: options.styleBaseUrl
|
|
442
|
+
styleBaseUrl: options.styleBaseUrl,
|
|
443
|
+
target: options.target
|
|
122
444
|
};
|
|
123
445
|
}
|
|
124
446
|
async start() {
|
|
@@ -165,7 +487,27 @@ var TileflowArtifactSessionImpl = class {
|
|
|
165
487
|
return this.#state;
|
|
166
488
|
}
|
|
167
489
|
getLastGoodArtifacts() {
|
|
168
|
-
return this.#
|
|
490
|
+
return this.#current?.artifacts;
|
|
491
|
+
}
|
|
492
|
+
acquireArtifacts(generation) {
|
|
493
|
+
const current = this.#current;
|
|
494
|
+
if (this.#closed || !current?.accepting || generation !== void 0 && generation !== current.generation) {
|
|
495
|
+
return void 0;
|
|
496
|
+
}
|
|
497
|
+
current.references += 1;
|
|
498
|
+
let released = false;
|
|
499
|
+
return Object.freeze({
|
|
500
|
+
artifacts: current.artifacts,
|
|
501
|
+
generation: current.generation,
|
|
502
|
+
release: async () => {
|
|
503
|
+
if (released) return;
|
|
504
|
+
released = true;
|
|
505
|
+
current.references -= 1;
|
|
506
|
+
if (!current.accepting && current.references === 0) {
|
|
507
|
+
await this.#disposeGeneration(current);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
});
|
|
169
511
|
}
|
|
170
512
|
subscribe(listener) {
|
|
171
513
|
if (this.#closed) return () => void 0;
|
|
@@ -173,6 +515,15 @@ var TileflowArtifactSessionImpl = class {
|
|
|
173
515
|
return () => this.#listeners.delete(listener);
|
|
174
516
|
}
|
|
175
517
|
async refresh(_reason = "manual") {
|
|
518
|
+
const refresh = this.#refresh();
|
|
519
|
+
this.#refreshes.add(refresh);
|
|
520
|
+
try {
|
|
521
|
+
await refresh;
|
|
522
|
+
} finally {
|
|
523
|
+
this.#refreshes.delete(refresh);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
async #refresh() {
|
|
176
527
|
if (this.#closed) return;
|
|
177
528
|
const generation = ++this.#generation;
|
|
178
529
|
const token = ++this.#refreshToken;
|
|
@@ -183,10 +534,20 @@ var TileflowArtifactSessionImpl = class {
|
|
|
183
534
|
});
|
|
184
535
|
try {
|
|
185
536
|
const artifacts = await this.#buildArtifacts(this.#buildOptions);
|
|
186
|
-
if (this.#closed || token !== this.#refreshToken)
|
|
187
|
-
|
|
537
|
+
if (this.#closed || token !== this.#refreshToken) {
|
|
538
|
+
await disposeArtifacts(artifacts);
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
const previous = this.#current;
|
|
542
|
+
const current = createArtifactGeneration(generation, artifacts);
|
|
543
|
+
this.#artifactGenerations.add(current);
|
|
544
|
+
this.#current = current;
|
|
188
545
|
this.#lastGoodGeneration = generation;
|
|
189
546
|
this.#updateArtifactWatchPaths(artifacts.watchPaths);
|
|
547
|
+
if (previous) {
|
|
548
|
+
previous.accepting = false;
|
|
549
|
+
if (previous.references === 0) await this.#disposeGeneration(previous);
|
|
550
|
+
}
|
|
190
551
|
this.#publish({
|
|
191
552
|
artifacts,
|
|
192
553
|
generation,
|
|
@@ -206,16 +567,27 @@ var TileflowArtifactSessionImpl = class {
|
|
|
206
567
|
this.#publishInvalid(generation, error);
|
|
207
568
|
}
|
|
208
569
|
}
|
|
209
|
-
|
|
210
|
-
|
|
570
|
+
close() {
|
|
571
|
+
this.#closing ??= this.#close();
|
|
572
|
+
return this.#closing;
|
|
573
|
+
}
|
|
574
|
+
async #close() {
|
|
211
575
|
this.#closed = true;
|
|
212
576
|
this.#refreshToken += 1;
|
|
213
577
|
if (this.#debounceTimer) clearTimeout(this.#debounceTimer);
|
|
214
578
|
this.#listeners.clear();
|
|
215
579
|
await this.#watcher?.close();
|
|
580
|
+
const retained = [...this.#artifactGenerations];
|
|
581
|
+
this.#current = void 0;
|
|
582
|
+
for (const generation of retained) {
|
|
583
|
+
generation.accepting = false;
|
|
584
|
+
if (generation.references === 0) void this.#disposeGeneration(generation);
|
|
585
|
+
}
|
|
586
|
+
await Promise.allSettled([...this.#refreshes]);
|
|
587
|
+
await Promise.all(retained.map((generation) => generation.completion));
|
|
216
588
|
}
|
|
217
589
|
#ignoreWatchPath(path, isDirectory) {
|
|
218
|
-
const resolvedPath =
|
|
590
|
+
const resolvedPath = resolve2(path);
|
|
219
591
|
const relativePath = relative(dirname(this.#configPath), resolvedPath);
|
|
220
592
|
const segments = relativePath.split(sep).filter(Boolean);
|
|
221
593
|
if (this.#ignoredPaths.some((path2) => isSameOrInside(path2, resolvedPath))) return true;
|
|
@@ -229,7 +601,7 @@ var TileflowArtifactSessionImpl = class {
|
|
|
229
601
|
return true;
|
|
230
602
|
}
|
|
231
603
|
#shouldRefreshForWatchEvent(path) {
|
|
232
|
-
const resolvedPath =
|
|
604
|
+
const resolvedPath = resolve2(path);
|
|
233
605
|
const extension = extname(resolvedPath).toLowerCase();
|
|
234
606
|
if (!iconExtensions.has(extension) && !fontExtensions.has(extension)) return true;
|
|
235
607
|
return [...this.#watchedArtifactPaths].some((path2) => isSameOrInside(path2, resolvedPath));
|
|
@@ -257,7 +629,7 @@ var TileflowArtifactSessionImpl = class {
|
|
|
257
629
|
}
|
|
258
630
|
#updateArtifactWatchPaths(paths) {
|
|
259
631
|
if (!this.#watcher) return;
|
|
260
|
-
const next = new Set(paths.map((path) =>
|
|
632
|
+
const next = new Set(paths.map((path) => resolve2(this.#cwd, path)));
|
|
261
633
|
const previous = this.#watchedArtifactPaths;
|
|
262
634
|
this.#watchedArtifactPaths = next;
|
|
263
635
|
for (const path of previous) {
|
|
@@ -271,14 +643,54 @@ var TileflowArtifactSessionImpl = class {
|
|
|
271
643
|
}
|
|
272
644
|
}
|
|
273
645
|
}
|
|
646
|
+
#disposeGeneration(generation) {
|
|
647
|
+
if (generation.disposeStarted) return generation.completion;
|
|
648
|
+
generation.disposeStarted = true;
|
|
649
|
+
void disposeArtifacts(generation.artifacts).then(
|
|
650
|
+
() => {
|
|
651
|
+
this.#artifactGenerations.delete(generation);
|
|
652
|
+
generation.resolveCompletion();
|
|
653
|
+
},
|
|
654
|
+
(error) => {
|
|
655
|
+
this.#artifactGenerations.delete(generation);
|
|
656
|
+
generation.rejectCompletion(error);
|
|
657
|
+
}
|
|
658
|
+
);
|
|
659
|
+
return generation.completion;
|
|
660
|
+
}
|
|
274
661
|
};
|
|
662
|
+
function createArtifactGeneration(generation, artifacts) {
|
|
663
|
+
let rejectCompletion;
|
|
664
|
+
let resolveCompletion;
|
|
665
|
+
const completion = new Promise((resolvePromise, rejectPromise) => {
|
|
666
|
+
rejectCompletion = rejectPromise;
|
|
667
|
+
resolveCompletion = resolvePromise;
|
|
668
|
+
});
|
|
669
|
+
return {
|
|
670
|
+
accepting: true,
|
|
671
|
+
artifacts,
|
|
672
|
+
completion,
|
|
673
|
+
disposeStarted: false,
|
|
674
|
+
generation,
|
|
675
|
+
references: 0,
|
|
676
|
+
rejectCompletion,
|
|
677
|
+
resolveCompletion
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
async function disposeArtifacts(artifacts) {
|
|
681
|
+
const dispose = artifacts.dispose;
|
|
682
|
+
artifacts.dispose = void 0;
|
|
683
|
+
await dispose?.();
|
|
684
|
+
}
|
|
275
685
|
function isSameOrInside(root, candidate) {
|
|
276
|
-
const path = relative(
|
|
686
|
+
const path = relative(resolve2(root), resolve2(candidate));
|
|
277
687
|
return path === "" || !isAbsolute(path) && path !== ".." && !path.startsWith(`..${sep}`);
|
|
278
688
|
}
|
|
279
689
|
function createTileflowArtifactDiagnostics(error, cwd) {
|
|
280
690
|
const inheritedCode = optionalDiagnosticField(error, "code");
|
|
691
|
+
const inheritedPath = optionalDiagnosticPath(error);
|
|
281
692
|
const inheritedPhase = optionalDiagnosticField(error, "phase") ?? optionalNestedDiagnosticField(error, "phase");
|
|
693
|
+
const semanticResolutionPath = isTileflowMapResolutionDiagnostic(error, inheritedPath) ? sanitizeDiagnosticSecrets(inheritedPath) : void 0;
|
|
282
694
|
const issueList = getIssueList(error);
|
|
283
695
|
if (issueList.length > 0) {
|
|
284
696
|
return normalizeDiagnostics(
|
|
@@ -293,8 +705,11 @@ function createTileflowArtifactDiagnostics(error, cwd) {
|
|
|
293
705
|
return normalizeDiagnostics([
|
|
294
706
|
{
|
|
295
707
|
...inheritedCode ? { code: inheritedCode } : {},
|
|
296
|
-
message: sanitizeMessage(
|
|
297
|
-
|
|
708
|
+
message: sanitizeMessage(
|
|
709
|
+
error instanceof Error ? removeSemanticResolutionPath(error.message, semanticResolutionPath) : "Unknown build error",
|
|
710
|
+
cwd
|
|
711
|
+
),
|
|
712
|
+
path: semanticResolutionPath ?? sanitizePath(inheritedPath ?? "", cwd),
|
|
298
713
|
...inheritedPhase ? { phase: inheritedPhase } : {}
|
|
299
714
|
}
|
|
300
715
|
]);
|
|
@@ -348,20 +763,34 @@ function sanitizeMessage(value, cwd) {
|
|
|
348
763
|
function sanitizePath(value, cwd) {
|
|
349
764
|
if (!value) return "";
|
|
350
765
|
if (win32.isAbsolute(value) && !isAbsolute(value)) return "(external)";
|
|
351
|
-
if (!isAbsolute(value)) return value.replaceAll("\\", "/");
|
|
766
|
+
if (!isAbsolute(value)) return sanitizeDiagnosticSecrets(value.replaceAll("\\", "/"));
|
|
352
767
|
const local = relative(canonicalPath(cwd), canonicalPath(value)).replaceAll(sep, "/");
|
|
353
|
-
return local.startsWith("../") || local === ".." ? "(external)" : local || ".";
|
|
768
|
+
return local.startsWith("../") || local === ".." ? "(external)" : sanitizeDiagnosticSecrets(local || ".");
|
|
769
|
+
}
|
|
770
|
+
function optionalDiagnosticPath(value) {
|
|
771
|
+
if (!value || typeof value !== "object") return void 0;
|
|
772
|
+
const path = value.path;
|
|
773
|
+
return typeof path === "string" && path ? path : void 0;
|
|
774
|
+
}
|
|
775
|
+
function isTileflowMapResolutionDiagnostic(value, path) {
|
|
776
|
+
if (!value || typeof value !== "object" || !path) return false;
|
|
777
|
+
const record = value;
|
|
778
|
+
return record.name === "TileflowMapResolutionError" && typeof record.code === "string" && typeof record.mapId === "string" && path.startsWith("/modules/") && !/[\p{Cc}]/u.test(path) && !/~(?:[^01]|$)/u.test(path);
|
|
779
|
+
}
|
|
780
|
+
function removeSemanticResolutionPath(message, path) {
|
|
781
|
+
if (!path) return message;
|
|
782
|
+
return message.replace(` at ${path}:`, ":");
|
|
354
783
|
}
|
|
355
784
|
function localPathAliases(path) {
|
|
356
|
-
return [.../* @__PURE__ */ new Set([
|
|
785
|
+
return [.../* @__PURE__ */ new Set([resolve2(path), canonicalPath(path)])].sort(
|
|
357
786
|
(left, right) => right.length - left.length
|
|
358
787
|
);
|
|
359
788
|
}
|
|
360
789
|
function canonicalPath(path) {
|
|
361
790
|
try {
|
|
362
|
-
return realpathSync.native(
|
|
791
|
+
return realpathSync.native(resolve2(path));
|
|
363
792
|
} catch {
|
|
364
|
-
return
|
|
793
|
+
return resolve2(path);
|
|
365
794
|
}
|
|
366
795
|
}
|
|
367
796
|
function normalizeDiagnostics(diagnostics) {
|
|
@@ -408,6 +837,15 @@ var TileflowHostedManifestOverwriteError = class extends Error {
|
|
|
408
837
|
this.name = "TileflowHostedManifestOverwriteError";
|
|
409
838
|
}
|
|
410
839
|
};
|
|
840
|
+
var TileflowLocalTilesetProductionError = class extends Error {
|
|
841
|
+
code = "TF_LOCAL_TILESET_PRODUCTION_UNRESOLVED";
|
|
842
|
+
constructor() {
|
|
843
|
+
super(
|
|
844
|
+
"Production builds cannot publish local PMTiles sources. Publish the tileset explicitly or provide an application-owned production source."
|
|
845
|
+
);
|
|
846
|
+
this.name = "TileflowLocalTilesetProductionError";
|
|
847
|
+
}
|
|
848
|
+
};
|
|
411
849
|
function createTileflowManifest(project, options = {}) {
|
|
412
850
|
return createManifest(project, options);
|
|
413
851
|
}
|
|
@@ -452,10 +890,10 @@ function createTileflowStylesWithInspection(project, options = {}) {
|
|
|
452
890
|
}
|
|
453
891
|
function assertValidTileflowStyles(project, styles) {
|
|
454
892
|
const issues = [];
|
|
455
|
-
for (const mapName of getTileflowMapNames(project).sort(
|
|
893
|
+
for (const mapName of getTileflowMapNames(project).sort(compareCodeUnits2)) {
|
|
456
894
|
const family = styles[mapName];
|
|
457
895
|
if (!family) continue;
|
|
458
|
-
for (const themeName of Object.keys(family).sort(
|
|
896
|
+
for (const themeName of Object.keys(family).sort(compareCodeUnits2)) {
|
|
459
897
|
const style = family[themeName];
|
|
460
898
|
issues.push(...validateTileflowStyle(style, `${mapName}/${themeName}`));
|
|
461
899
|
}
|
|
@@ -465,6 +903,9 @@ function assertValidTileflowStyles(project, styles) {
|
|
|
465
903
|
}
|
|
466
904
|
}
|
|
467
905
|
async function createTileflowArtifactPlan(prepared, options = {}) {
|
|
906
|
+
if (options.target === "production" && hasLocalTilesetSources(prepared.project)) {
|
|
907
|
+
throw new TileflowLocalTilesetProductionError();
|
|
908
|
+
}
|
|
468
909
|
const inspected = options.inspection ? createTileflowStylesWithInspection(prepared.project, {
|
|
469
910
|
apiBaseUrl: options.apiBaseUrl,
|
|
470
911
|
mapAssets: prepared.mapAssets
|
|
@@ -473,71 +914,87 @@ async function createTileflowArtifactPlan(prepared, options = {}) {
|
|
|
473
914
|
apiBaseUrl: options.apiBaseUrl,
|
|
474
915
|
mapAssets: prepared.mapAssets
|
|
475
916
|
});
|
|
476
|
-
const
|
|
917
|
+
const localTilesets = await prepareTileflowLocalTilesets(prepared.project, compiledStyles, {
|
|
477
918
|
assetBaseUrl: resolveAssetBaseUrl(options),
|
|
478
919
|
baseDirectory: prepared.baseDirectory,
|
|
479
|
-
cwd: prepared.cwd
|
|
480
|
-
target: "local"
|
|
920
|
+
cwd: prepared.cwd
|
|
481
921
|
});
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
922
|
+
try {
|
|
923
|
+
const preparedFonts = await prepareTileflowStyleFonts(prepared.project, localTilesets.styles, {
|
|
924
|
+
assetBaseUrl: resolveAssetBaseUrl(options),
|
|
925
|
+
baseDirectory: prepared.baseDirectory,
|
|
926
|
+
cwd: prepared.cwd,
|
|
927
|
+
target: "local"
|
|
928
|
+
});
|
|
929
|
+
const styles = preparedFonts.styles;
|
|
930
|
+
const assets = [...prepared.assets, ...preparedFonts.assets];
|
|
931
|
+
const provenance = await createTileflowBuildProvenance(prepared.cwd);
|
|
932
|
+
const buildManifest = await createTileflowMapBuildManifest(
|
|
933
|
+
Object.fromEntries(
|
|
934
|
+
Object.keys(prepared.project.maps).sort(compareCodeUnits2).map((mapName) => {
|
|
935
|
+
const map = prepared.project.maps[mapName];
|
|
936
|
+
const style = styles[mapName];
|
|
937
|
+
return [
|
|
938
|
+
mapName,
|
|
939
|
+
{
|
|
940
|
+
assets: getMapBuildAssets(
|
|
941
|
+
mapName,
|
|
942
|
+
assets,
|
|
943
|
+
preparedFonts.bundles[mapName]?.files ?? []
|
|
944
|
+
),
|
|
945
|
+
lineage: prepared.project.mapMetadata?.[mapName]?.lineage ?? [
|
|
946
|
+
{ id: map.id, mapVersion: map.version }
|
|
947
|
+
],
|
|
948
|
+
map,
|
|
949
|
+
sourceAssets: {
|
|
950
|
+
fonts: preparedFonts.sourceIdentities[mapName] ?? [],
|
|
951
|
+
icons: prepared.mapIconSources[mapName] ?? []
|
|
952
|
+
},
|
|
953
|
+
styles: style
|
|
954
|
+
}
|
|
955
|
+
];
|
|
956
|
+
})
|
|
957
|
+
),
|
|
958
|
+
{ provenance }
|
|
959
|
+
);
|
|
960
|
+
const stableManifest = createFontAwareManifest(
|
|
961
|
+
createManifest(prepared.project, { styleBaseUrl: options.styleBaseUrl }),
|
|
962
|
+
styles
|
|
963
|
+
);
|
|
964
|
+
const inputs = {
|
|
965
|
+
directories: uniqueStrings(
|
|
966
|
+
[...prepared.watchPaths, ...preparedFonts.watchPaths].map(canonicalInputPath)
|
|
967
|
+
),
|
|
968
|
+
files: uniqueStrings(
|
|
969
|
+
[...options.inputFiles ?? [], ...localTilesets.watchPaths].map(canonicalInputPath)
|
|
970
|
+
)
|
|
971
|
+
};
|
|
972
|
+
const partial = {
|
|
973
|
+
assets,
|
|
974
|
+
buildManifest,
|
|
975
|
+
dispose: localTilesets.dispose,
|
|
976
|
+
manifest: stableManifest,
|
|
977
|
+
project: prepared.project,
|
|
978
|
+
...localTilesets.files.length > 0 ? { localTilesets: localTilesets.files } : {},
|
|
979
|
+
...inspected ? { styleInspections: inspected.inspections } : {},
|
|
980
|
+
styles,
|
|
981
|
+
watchPaths: uniqueStrings([...inputs.files, ...inputs.directories])
|
|
982
|
+
};
|
|
983
|
+
const generation = hashArtifactGeneration(getStableTileflowArtifactFiles(partial));
|
|
984
|
+
const manifest = createGenerationManifest(stableManifest, generation, assets);
|
|
985
|
+
const generationArtifacts = { ...partial, manifest };
|
|
986
|
+
return {
|
|
987
|
+
...generationArtifacts,
|
|
988
|
+
files: createGenerationArtifactFiles(generationArtifacts, generation),
|
|
989
|
+
inputs,
|
|
990
|
+
project: prepared.project,
|
|
991
|
+
schemaVersion: tileflowArtifactPlanSchemaVersion,
|
|
992
|
+
sourceProject: prepared.sourceProject
|
|
993
|
+
};
|
|
994
|
+
} catch (error) {
|
|
995
|
+
await localTilesets.dispose().catch(() => void 0);
|
|
996
|
+
throw error;
|
|
997
|
+
}
|
|
541
998
|
}
|
|
542
999
|
async function createTileflowBuildArtifacts(options = {}) {
|
|
543
1000
|
const loaded = await loadValidTileflowConfigWithInputs(
|
|
@@ -598,7 +1055,7 @@ function validateArtifactFiles(files) {
|
|
|
598
1055
|
}
|
|
599
1056
|
seen.add(file.fileName);
|
|
600
1057
|
}
|
|
601
|
-
return [...files].sort((left, right) =>
|
|
1058
|
+
return [...files].sort((left, right) => compareCodeUnits2(left.fileName, right.fileName));
|
|
602
1059
|
}
|
|
603
1060
|
function createGenerationManifest(manifest, generation, assets) {
|
|
604
1061
|
return {
|
|
@@ -701,16 +1158,16 @@ function getMapBuildAssets(mapName, assets, fontAssets) {
|
|
|
701
1158
|
selected.set(asset.fileName, asset);
|
|
702
1159
|
}
|
|
703
1160
|
return [...selected.values()].sort(
|
|
704
|
-
(left, right) =>
|
|
1161
|
+
(left, right) => compareCodeUnits2(left.fileName, right.fileName)
|
|
705
1162
|
);
|
|
706
1163
|
}
|
|
707
1164
|
function hashBytes(source) {
|
|
708
1165
|
return createHash("sha256").update(typeof source === "string" ? Buffer.from(source, "utf8") : Buffer.from(source)).digest("hex");
|
|
709
1166
|
}
|
|
710
1167
|
async function createTileflowBuildProvenance(cwd) {
|
|
711
|
-
const projectDirectory =
|
|
1168
|
+
const projectDirectory = resolve3(cwd);
|
|
712
1169
|
const projectRequire = createRequire(
|
|
713
|
-
|
|
1170
|
+
resolve3(projectDirectory, "__tileflow_build_provenance__.cjs")
|
|
714
1171
|
);
|
|
715
1172
|
const toolRequire = createRequire(import.meta.url);
|
|
716
1173
|
const packages = {};
|
|
@@ -767,10 +1224,10 @@ async function findNearestLockfile(cwd) {
|
|
|
767
1224
|
try {
|
|
768
1225
|
return {
|
|
769
1226
|
format: candidate.format,
|
|
770
|
-
source: await readFile(
|
|
1227
|
+
source: await readFile(resolve3(directory, candidate.fileName))
|
|
771
1228
|
};
|
|
772
1229
|
} catch (error) {
|
|
773
|
-
if (!
|
|
1230
|
+
if (!hasErrorCode2(error, "ENOENT")) throw error;
|
|
774
1231
|
}
|
|
775
1232
|
}
|
|
776
1233
|
const parent = dirname2(directory);
|
|
@@ -824,30 +1281,46 @@ function getArtifactInputGraph(artifacts) {
|
|
|
824
1281
|
return inputs && Array.isArray(inputs.files) && Array.isArray(inputs.directories) ? inputs : void 0;
|
|
825
1282
|
}
|
|
826
1283
|
async function writeTileflowBuildArtifacts(options) {
|
|
827
|
-
const artifacts = await createTileflowBuildArtifacts(options);
|
|
828
|
-
|
|
829
|
-
|
|
1284
|
+
const artifacts = await createTileflowBuildArtifacts({ ...options, target: "production" });
|
|
1285
|
+
try {
|
|
1286
|
+
await writeTileflowArtifactPlan(artifacts, options);
|
|
1287
|
+
return { ...artifacts, dispose: void 0, localTilesets: void 0 };
|
|
1288
|
+
} finally {
|
|
1289
|
+
await disposeTileflowBuildArtifacts(artifacts);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
async function disposeTileflowBuildArtifacts(artifacts) {
|
|
1293
|
+
const dispose = artifacts.dispose;
|
|
1294
|
+
artifacts.dispose = void 0;
|
|
1295
|
+
await dispose?.();
|
|
830
1296
|
}
|
|
831
1297
|
async function writeTileflowArtifactPlan(artifacts, options) {
|
|
1298
|
+
assertNoLocalTilesetsForProduction(artifacts);
|
|
832
1299
|
const cwd = options.cwd ?? process.cwd();
|
|
833
1300
|
const output = await prepareTileflowOutputDirectory(cwd, options.outDir);
|
|
834
1301
|
const files = getTileflowArtifactFiles(artifacts);
|
|
835
|
-
await assertTileflowSelfHostedManifestTarget(
|
|
1302
|
+
await assertTileflowSelfHostedManifestTarget(resolve3(output.outDir, "manifest.json"), options);
|
|
836
1303
|
await commitTileflowArtifactFiles(output, files);
|
|
837
1304
|
}
|
|
1305
|
+
function assertNoLocalTilesetsForProduction(artifacts) {
|
|
1306
|
+
if (artifacts.localTilesets?.length) throw new TileflowLocalTilesetProductionError();
|
|
1307
|
+
}
|
|
1308
|
+
function hasLocalTilesetSources(project) {
|
|
1309
|
+
return Object.values(project.maps).some((map) => Object.keys(map.sources ?? {}).length > 0);
|
|
1310
|
+
}
|
|
838
1311
|
async function assertTileflowSelfHostedManifestTarget(manifestPath, options = {}) {
|
|
839
1312
|
if (options.overwriteHostedManifest) return;
|
|
840
1313
|
let source;
|
|
841
1314
|
try {
|
|
842
|
-
source = await readFile(
|
|
1315
|
+
source = await readFile(resolve3(manifestPath), "utf8");
|
|
843
1316
|
} catch (error) {
|
|
844
|
-
if (
|
|
1317
|
+
if (hasErrorCode2(error, "ENOENT")) return;
|
|
845
1318
|
throw error;
|
|
846
1319
|
}
|
|
847
1320
|
try {
|
|
848
1321
|
const manifest = parseTileflowRuntimeManifest(JSON.parse(source));
|
|
849
1322
|
if (isHostedRuntimeManifest(manifest)) {
|
|
850
|
-
throw new TileflowHostedManifestOverwriteError(
|
|
1323
|
+
throw new TileflowHostedManifestOverwriteError(resolve3(manifestPath));
|
|
851
1324
|
}
|
|
852
1325
|
} catch (error) {
|
|
853
1326
|
if (error instanceof TileflowHostedManifestOverwriteError) throw error;
|
|
@@ -874,7 +1347,8 @@ async function commitTileflowArtifactFiles(output, files) {
|
|
|
874
1347
|
const generation = getArtifactGeneration(files);
|
|
875
1348
|
const retainedGenerations = previous ? generation === previous.generation ? getManagedGenerations(previous.files) : [previous.generation] : [];
|
|
876
1349
|
const retainedPrefixes = retainedGenerations.map((item) => `generations/${item}/`);
|
|
877
|
-
const
|
|
1350
|
+
const retainedGenerationNames = previous ? previous.files.filter((name) => retainedPrefixes.some((prefix) => name.startsWith(prefix))) : [];
|
|
1351
|
+
const retainedNames = uniqueStrings(retainedGenerationNames);
|
|
878
1352
|
const inventoryNames = uniqueStrings([...desiredNames, ...retainedNames]);
|
|
879
1353
|
const inventory = {
|
|
880
1354
|
files: inventoryNames,
|
|
@@ -894,15 +1368,15 @@ async function commitTileflowArtifactFiles(output, files) {
|
|
|
894
1368
|
...staleNames,
|
|
895
1369
|
tileflowArtifactInventoryFileName
|
|
896
1370
|
]);
|
|
897
|
-
const stageDir = await mkdtemp(
|
|
898
|
-
const newDir =
|
|
899
|
-
const backupDir =
|
|
1371
|
+
const stageDir = await mkdtemp(resolve3(outDir, ".tileflow-stage-"));
|
|
1372
|
+
const newDir = resolve3(stageDir, "new");
|
|
1373
|
+
const backupDir = resolve3(stageDir, "backup");
|
|
900
1374
|
const backedUp = /* @__PURE__ */ new Set();
|
|
901
1375
|
const installed = /* @__PURE__ */ new Set();
|
|
902
1376
|
try {
|
|
903
1377
|
for (const file of stagedFiles) {
|
|
904
1378
|
const stagePath = managedPath(newDir, file.fileName);
|
|
905
|
-
await
|
|
1379
|
+
await mkdir2(dirname2(stagePath), { recursive: true });
|
|
906
1380
|
await writeFileDurably(stagePath, file.source);
|
|
907
1381
|
}
|
|
908
1382
|
for (const name of affectedNames) {
|
|
@@ -910,21 +1384,21 @@ async function commitTileflowArtifactFiles(output, files) {
|
|
|
910
1384
|
const targetPath = managedPath(outDir, name);
|
|
911
1385
|
if (!await isFile(targetPath)) continue;
|
|
912
1386
|
const backupPath = managedPath(backupDir, name);
|
|
913
|
-
await
|
|
914
|
-
await
|
|
1387
|
+
await mkdir2(dirname2(backupPath), { recursive: true });
|
|
1388
|
+
await copyFile2(targetPath, backupPath);
|
|
915
1389
|
backedUp.add(name);
|
|
916
1390
|
}
|
|
917
1391
|
const commitOrder = stagedFiles.map((file) => file.fileName).sort((left, right) => {
|
|
918
1392
|
const leftRank = commitRank(left);
|
|
919
1393
|
const rightRank = commitRank(right);
|
|
920
|
-
return leftRank - rightRank ||
|
|
1394
|
+
return leftRank - rightRank || compareCodeUnits2(left, right);
|
|
921
1395
|
});
|
|
922
1396
|
for (const name of commitOrder.filter(
|
|
923
1397
|
(candidate) => candidate !== tileflowArtifactInventoryFileName
|
|
924
1398
|
)) {
|
|
925
1399
|
const sourcePath = managedPath(newDir, name);
|
|
926
1400
|
const targetPath = managedPath(outDir, name);
|
|
927
|
-
await
|
|
1401
|
+
await mkdir2(dirname2(targetPath), { recursive: true });
|
|
928
1402
|
await rename(sourcePath, targetPath);
|
|
929
1403
|
installed.add(name);
|
|
930
1404
|
}
|
|
@@ -939,7 +1413,7 @@ async function commitTileflowArtifactFiles(output, files) {
|
|
|
939
1413
|
await rollbackManagedFiles(outDir, backupDir, affectedNames, backedUp, installed);
|
|
940
1414
|
throw error;
|
|
941
1415
|
} finally {
|
|
942
|
-
await
|
|
1416
|
+
await rm2(stageDir, { force: true, recursive: true });
|
|
943
1417
|
}
|
|
944
1418
|
}
|
|
945
1419
|
function getArtifactGeneration(files) {
|
|
@@ -971,8 +1445,8 @@ async function rollbackManagedFiles(outDir, backupDir, affectedNames, backedUp,
|
|
|
971
1445
|
await removeManagedFile(targetPath);
|
|
972
1446
|
}
|
|
973
1447
|
if (backedUp.has(name)) {
|
|
974
|
-
await
|
|
975
|
-
await
|
|
1448
|
+
await mkdir2(dirname2(targetPath), { recursive: true });
|
|
1449
|
+
await copyFile2(managedPath(backupDir, name), targetPath);
|
|
976
1450
|
}
|
|
977
1451
|
} catch (error) {
|
|
978
1452
|
rollbackErrors.push(error);
|
|
@@ -988,7 +1462,7 @@ async function readTileflowArtifactInventory(outDir) {
|
|
|
988
1462
|
try {
|
|
989
1463
|
source = await readFile(inventoryPath, "utf8");
|
|
990
1464
|
} catch (error) {
|
|
991
|
-
if (
|
|
1465
|
+
if (hasErrorCode2(error, "ENOENT")) return void 0;
|
|
992
1466
|
throw error;
|
|
993
1467
|
}
|
|
994
1468
|
let candidate;
|
|
@@ -1020,7 +1494,7 @@ async function readTileflowArtifactInventory(outDir) {
|
|
|
1020
1494
|
}
|
|
1021
1495
|
async function assertManagedTargetIsSafe(outDir, fileName) {
|
|
1022
1496
|
assertSafeManagedFileName(fileName);
|
|
1023
|
-
const rootStats = await
|
|
1497
|
+
const rootStats = await lstat2(outDir);
|
|
1024
1498
|
if (rootStats.isSymbolicLink()) {
|
|
1025
1499
|
throw new Error(`Tileflow refuses to write through a symbolic link: ${outDir}`);
|
|
1026
1500
|
}
|
|
@@ -1030,9 +1504,9 @@ async function assertManagedTargetIsSafe(outDir, fileName) {
|
|
|
1030
1504
|
const segments = fileName.split("/");
|
|
1031
1505
|
let current = outDir;
|
|
1032
1506
|
for (let index = 0; index < segments.length; index += 1) {
|
|
1033
|
-
current =
|
|
1507
|
+
current = resolve3(current, segments[index]);
|
|
1034
1508
|
try {
|
|
1035
|
-
const stats = await
|
|
1509
|
+
const stats = await lstat2(current);
|
|
1036
1510
|
if (stats.isSymbolicLink()) {
|
|
1037
1511
|
throw new Error(`Tileflow refuses to write through a symbolic link: ${fileName}`);
|
|
1038
1512
|
}
|
|
@@ -1043,34 +1517,34 @@ async function assertManagedTargetIsSafe(outDir, fileName) {
|
|
|
1043
1517
|
throw new Error(`Tileflow artifact target is not a file: ${fileName}`);
|
|
1044
1518
|
}
|
|
1045
1519
|
} catch (error) {
|
|
1046
|
-
if (
|
|
1520
|
+
if (hasErrorCode2(error, "ENOENT")) return;
|
|
1047
1521
|
throw error;
|
|
1048
1522
|
}
|
|
1049
1523
|
}
|
|
1050
1524
|
}
|
|
1051
1525
|
async function prepareTileflowOutputDirectory(cwd, requestedOutDir) {
|
|
1052
|
-
const logicalRoot =
|
|
1053
|
-
const logicalOutDir =
|
|
1526
|
+
const logicalRoot = resolve3(cwd);
|
|
1527
|
+
const logicalOutDir = resolve3(logicalRoot, requestedOutDir);
|
|
1054
1528
|
if (!isPathWithin(logicalRoot, logicalOutDir)) {
|
|
1055
1529
|
throw new Error(`Tileflow artifact output escapes its working directory: ${logicalOutDir}`);
|
|
1056
1530
|
}
|
|
1057
|
-
const rootStats = await
|
|
1531
|
+
const rootStats = await lstat2(logicalRoot);
|
|
1058
1532
|
if (!rootStats.isDirectory()) {
|
|
1059
1533
|
throw new Error(`Tileflow artifact working directory is not a directory: ${logicalRoot}`);
|
|
1060
1534
|
}
|
|
1061
|
-
const rootDir = await
|
|
1535
|
+
const rootDir = await realpath2(logicalRoot);
|
|
1062
1536
|
const relativeOutDir = relative2(logicalRoot, logicalOutDir);
|
|
1063
1537
|
const segments = relativeOutDir ? relativeOutDir.split(sep2) : [];
|
|
1064
1538
|
let current = rootDir;
|
|
1065
1539
|
for (const segment of segments) {
|
|
1066
|
-
current =
|
|
1540
|
+
current = resolve3(current, segment);
|
|
1067
1541
|
let stats;
|
|
1068
1542
|
try {
|
|
1069
|
-
stats = await
|
|
1543
|
+
stats = await lstat2(current);
|
|
1070
1544
|
} catch (error) {
|
|
1071
|
-
if (!
|
|
1072
|
-
await
|
|
1073
|
-
stats = await
|
|
1545
|
+
if (!hasErrorCode2(error, "ENOENT")) throw error;
|
|
1546
|
+
await mkdir2(current);
|
|
1547
|
+
stats = await lstat2(current);
|
|
1074
1548
|
}
|
|
1075
1549
|
if (stats.isSymbolicLink()) {
|
|
1076
1550
|
throw new Error(`Tileflow refuses to write through a symbolic link: ${current}`);
|
|
@@ -1079,7 +1553,7 @@ async function prepareTileflowOutputDirectory(cwd, requestedOutDir) {
|
|
|
1079
1553
|
throw new Error(`Tileflow artifact output ancestor is not a directory: ${current}`);
|
|
1080
1554
|
}
|
|
1081
1555
|
}
|
|
1082
|
-
const outDir = await
|
|
1556
|
+
const outDir = await realpath2(current);
|
|
1083
1557
|
const output = { outDir, rootDir };
|
|
1084
1558
|
await assertTileflowOutputDirectoryIsSafe(output);
|
|
1085
1559
|
return output;
|
|
@@ -1088,14 +1562,14 @@ async function assertTileflowOutputDirectoryIsSafe(output) {
|
|
|
1088
1562
|
if (!isPathWithin(output.rootDir, output.outDir)) {
|
|
1089
1563
|
throw new Error(`Tileflow artifact output escapes its working directory: ${output.outDir}`);
|
|
1090
1564
|
}
|
|
1091
|
-
const stats = await
|
|
1565
|
+
const stats = await lstat2(output.outDir);
|
|
1092
1566
|
if (stats.isSymbolicLink()) {
|
|
1093
1567
|
throw new Error(`Tileflow refuses to write through a symbolic link: ${output.outDir}`);
|
|
1094
1568
|
}
|
|
1095
1569
|
if (!stats.isDirectory()) {
|
|
1096
1570
|
throw new Error(`Tileflow artifact output is not a directory: ${output.outDir}`);
|
|
1097
1571
|
}
|
|
1098
|
-
const canonicalOutDir = await
|
|
1572
|
+
const canonicalOutDir = await realpath2(output.outDir);
|
|
1099
1573
|
if (canonicalOutDir !== output.outDir || !isPathWithin(output.rootDir, canonicalOutDir)) {
|
|
1100
1574
|
throw new Error(`Tileflow artifact output escapes its working directory: ${output.outDir}`);
|
|
1101
1575
|
}
|
|
@@ -1110,21 +1584,21 @@ function assertTileflowArtifactFileName(fileName) {
|
|
|
1110
1584
|
const logicalName = fileName.replace(/^generations\/[a-f0-9]{64}\//, "");
|
|
1111
1585
|
const styleMatch = /^styles\/([^/]+)\/([^/]+)\.json$/u.exec(logicalName);
|
|
1112
1586
|
const iconMatch = /^icons\/([^/]+)\/sprite(?:@2x)?\.(?:json|png)$/u.exec(logicalName);
|
|
1113
|
-
if (fileName === "manifest.json" || fileName === tileflowMapBuildManifestFileName || styleMatch && tileflowMapIdSchema.safeParse(styleMatch[1]).success && tileflowThemeNameSchema.safeParse(styleMatch[2]).success || iconMatch && tileflowMapIdSchema.safeParse(iconMatch[1]).success || /^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)) {
|
|
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)) {
|
|
1114
1588
|
return;
|
|
1115
1589
|
}
|
|
1116
1590
|
throw new Error(`Unexpected Tileflow managed artifact file name: ${fileName}`);
|
|
1117
1591
|
}
|
|
1118
1592
|
function managedPath(root, fileName) {
|
|
1119
1593
|
assertSafeManagedFileName(fileName);
|
|
1120
|
-
const path =
|
|
1594
|
+
const path = resolve3(root, ...fileName.split("/"));
|
|
1121
1595
|
if (!isPathWithin(root, path)) {
|
|
1122
1596
|
throw new Error(`Unsafe Tileflow artifact file name: ${fileName}`);
|
|
1123
1597
|
}
|
|
1124
1598
|
return path;
|
|
1125
1599
|
}
|
|
1126
1600
|
async function writeFileDurably(path, source) {
|
|
1127
|
-
const handle = await
|
|
1601
|
+
const handle = await open2(path, "wx");
|
|
1128
1602
|
try {
|
|
1129
1603
|
await handle.writeFile(source);
|
|
1130
1604
|
await handle.sync();
|
|
@@ -1134,19 +1608,19 @@ async function writeFileDurably(path, source) {
|
|
|
1134
1608
|
}
|
|
1135
1609
|
async function isFile(path) {
|
|
1136
1610
|
try {
|
|
1137
|
-
return (await
|
|
1611
|
+
return (await lstat2(path)).isFile();
|
|
1138
1612
|
} catch (error) {
|
|
1139
|
-
if (
|
|
1613
|
+
if (hasErrorCode2(error, "ENOENT")) return false;
|
|
1140
1614
|
throw error;
|
|
1141
1615
|
}
|
|
1142
1616
|
}
|
|
1143
1617
|
async function removeManagedFile(path) {
|
|
1144
1618
|
try {
|
|
1145
|
-
const stats = await
|
|
1619
|
+
const stats = await lstat2(path);
|
|
1146
1620
|
if (!stats.isFile()) throw new Error(`Refusing to remove non-file artifact target: ${path}`);
|
|
1147
1621
|
await unlink(path);
|
|
1148
1622
|
} catch (error) {
|
|
1149
|
-
if (!
|
|
1623
|
+
if (!hasErrorCode2(error, "ENOENT")) throw error;
|
|
1150
1624
|
}
|
|
1151
1625
|
}
|
|
1152
1626
|
function hashArtifactGeneration(files) {
|
|
@@ -1169,18 +1643,18 @@ function isRelativePublicUrl(value) {
|
|
|
1169
1643
|
return !(value.startsWith("http://") || value.startsWith("https://") || value.startsWith("/") || value.startsWith("data:"));
|
|
1170
1644
|
}
|
|
1171
1645
|
function uniqueStrings(values) {
|
|
1172
|
-
return [...new Set(values)].sort(
|
|
1646
|
+
return [...new Set(values)].sort(compareCodeUnits2);
|
|
1173
1647
|
}
|
|
1174
|
-
function
|
|
1648
|
+
function hasErrorCode2(error, code) {
|
|
1175
1649
|
return Boolean(error && typeof error === "object" && "code" in error && error.code === code);
|
|
1176
1650
|
}
|
|
1177
1651
|
function canonicalInputPath(path) {
|
|
1178
|
-
const resolved =
|
|
1652
|
+
const resolved = resolve3(path);
|
|
1179
1653
|
try {
|
|
1180
1654
|
return realpathSync2.native(resolved);
|
|
1181
1655
|
} catch {
|
|
1182
1656
|
try {
|
|
1183
|
-
return
|
|
1657
|
+
return resolve3(realpathSync2.native(dirname2(resolved)), resolved.split(/[\\/]/).at(-1));
|
|
1184
1658
|
} catch {
|
|
1185
1659
|
return resolved;
|
|
1186
1660
|
}
|
|
@@ -1188,11 +1662,13 @@ function canonicalInputPath(path) {
|
|
|
1188
1662
|
}
|
|
1189
1663
|
|
|
1190
1664
|
export {
|
|
1665
|
+
prepareTileflowLocalTilesets,
|
|
1191
1666
|
tileflowArtifactSessionSchemaVersion,
|
|
1192
1667
|
createTileflowArtifactDiagnostics,
|
|
1193
1668
|
tileflowArtifactPlanSchemaVersion,
|
|
1194
1669
|
tileflowArtifactInventoryFileName,
|
|
1195
1670
|
TileflowHostedManifestOverwriteError,
|
|
1671
|
+
TileflowLocalTilesetProductionError,
|
|
1196
1672
|
createTileflowManifest,
|
|
1197
1673
|
createTileflowStyle,
|
|
1198
1674
|
createTileflowStyles,
|
|
@@ -1205,6 +1681,8 @@ export {
|
|
|
1205
1681
|
isTileflowArtifactInputPath,
|
|
1206
1682
|
refreshTileflowArtifactSession,
|
|
1207
1683
|
writeTileflowBuildArtifacts,
|
|
1684
|
+
disposeTileflowBuildArtifacts,
|
|
1208
1685
|
writeTileflowArtifactPlan,
|
|
1686
|
+
assertNoLocalTilesetsForProduction,
|
|
1209
1687
|
assertTileflowSelfHostedManifestTarget
|
|
1210
1688
|
};
|