@tileflow/dev 0.1.0-alpha.0 → 0.1.0-alpha.10

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 CHANGED
@@ -2,12 +2,23 @@
2
2
 
3
3
  Node utilities used by the Tileflow CLI and build integrations.
4
4
 
5
+ Most apps should use `@tileflow/cli`, `@tileflow/vite`, or `@tileflow/next` directly. Use this
6
+ package when you are building a custom dev server, bundler plugin, or deployment adapter that needs
7
+ to load and compile a `tileflow.config.ts`.
8
+
5
9
  ```ts
6
- import { createTileflowBuildArtifacts } from "@tileflow/dev";
10
+ import {createTileflowBuildArtifacts} from '@tileflow/dev';
7
11
 
8
12
  const artifacts = await createTileflowBuildArtifacts({
9
- config: "tileflow.config.ts",
13
+ config: 'tileflow.config.ts',
10
14
  });
11
15
  ```
12
16
 
17
+ If a map uses `icons: './icons/brand'`, build artifacts include the generated
18
+ MapLibre `sprite.json`, `sprite.png`, `sprite@2x.json`, and `sprite@2x.png`
19
+ assets alongside the styles.
20
+
21
+ This package is public, but it is an integration layer. Map styling primitives live in
22
+ `@tileflow/core`; React rendering lives in `@tileflow/react`.
23
+
13
24
  Docs: https://tileflow.dev/docs
package/dist/index.d.ts CHANGED
@@ -1,5 +1,12 @@
1
+ import { IncomingMessage, ServerResponse } from 'node:http';
1
2
  import { TileflowManifest, MapLibreStyle, ValidationMessage, TileflowProjectConfig, TileflowStyleOptions } from '@tileflow/core';
2
3
 
4
+ type TileflowBuildAsset = {
5
+ contentType: string;
6
+ fileName: string;
7
+ source: string | Uint8Array;
8
+ };
9
+
3
10
  declare const defaultTileflowApiUrl = "https://api.tileflow.dev";
4
11
  declare const defaultTileflowConfigPath = "tileflow.config.ts";
5
12
  declare const defaultTileflowManifestPath = "public/tileflow/manifest.json";
@@ -21,14 +28,17 @@ type TileflowDevRequestHandlerOptions = {
21
28
  tileBaseUrl?: string;
22
29
  };
23
30
  type TileflowBuildArtifactsOptions = {
31
+ assetBaseUrl?: string;
24
32
  config?: string;
25
33
  cwd?: string;
26
34
  styleBaseUrl?: string;
27
35
  tileBaseUrl?: string;
28
36
  };
29
37
  type TileflowBuildArtifacts = {
38
+ assets: TileflowBuildAsset[];
30
39
  manifest: TileflowManifest;
31
40
  styles: Record<string, MapLibreStyle>;
41
+ watchPaths: string[];
32
42
  };
33
43
  type WriteTileflowBuildArtifactsOptions = TileflowBuildArtifactsOptions & {
34
44
  outDir: string;
@@ -47,8 +57,15 @@ declare function getTileflowStyleMapName(path: string): string;
47
57
  declare function createTileflowManifest(project: TileflowProjectConfig, options?: TileflowManifestOptions): TileflowManifest;
48
58
  declare function createTileflowStyle(project: TileflowProjectConfig, mapName: string, options?: TileflowStyleOptions): MapLibreStyle;
49
59
  declare function createTileflowBuildArtifacts(options?: TileflowBuildArtifactsOptions): Promise<TileflowBuildArtifacts>;
60
+ declare function getTileflowWatchPaths(options?: Pick<TileflowBuildArtifactsOptions, 'config' | 'cwd'>): Promise<string[]>;
50
61
  declare function writeTileflowBuildArtifacts(options: WriteTileflowBuildArtifactsOptions): Promise<TileflowBuildArtifacts>;
62
+ declare function isTileflowRequestUrl(url: string | undefined, basePath: string): boolean;
63
+ declare function createTileflowNodeRequest(request: IncomingMessage): Request;
64
+ declare function writeTileflowNodeResponse(response: ServerResponse, tileflowResponse: Response): Promise<void>;
65
+ declare function joinTileflowPublicUrl(publicBase: string, basePath: string): string;
66
+ declare function getTileflowAssetBasePath(basePath: string): string;
67
+ declare function getTileflowAssetFileName(basePath: string, fileName: string): string;
51
68
  declare function createTileflowDevRequestHandler(options?: TileflowDevRequestHandlerOptions): (request: Request) => Promise<Response>;
52
69
  declare function normalizeTileflowBasePath(value?: string): string;
53
70
 
54
- export { type LoadTileflowConfigOptions, type TileflowBuildArtifacts, type TileflowBuildArtifactsOptions, type TileflowDevRequestHandlerOptions, type TileflowManifestOptions, TileflowValidationError, type WriteTileflowBuildArtifactsOptions, asTileflowProjectConfig, assertValidTileflowConfig, createTileflowBuildArtifacts, createTileflowDevRequestHandler, createTileflowManifest, createTileflowStyle, defaultTileflowApiUrl, defaultTileflowConfigPath, defaultTileflowManifestPath, defaultTileflowTileset, getFirstTileflowMapName, getTileflowMapNames, getTileflowStyleMapName, loadTileflowConfig, loadValidTileflowConfig, normalizeTileflowBasePath, writeTileflowBuildArtifacts };
71
+ export { type LoadTileflowConfigOptions, type TileflowBuildArtifacts, type TileflowBuildArtifactsOptions, type TileflowDevRequestHandlerOptions, type TileflowManifestOptions, TileflowValidationError, type WriteTileflowBuildArtifactsOptions, asTileflowProjectConfig, assertValidTileflowConfig, createTileflowBuildArtifacts, createTileflowDevRequestHandler, createTileflowManifest, createTileflowNodeRequest, createTileflowStyle, defaultTileflowApiUrl, defaultTileflowConfigPath, defaultTileflowManifestPath, defaultTileflowTileset, getFirstTileflowMapName, getTileflowAssetBasePath, getTileflowAssetFileName, getTileflowMapNames, getTileflowStyleMapName, getTileflowWatchPaths, isTileflowRequestUrl, joinTileflowPublicUrl, loadTileflowConfig, loadValidTileflowConfig, normalizeTileflowBasePath, writeTileflowBuildArtifacts, writeTileflowNodeResponse };
package/dist/index.js CHANGED
@@ -1,13 +1,206 @@
1
1
  // src/index.ts
2
+ import { createJiti } from "jiti";
3
+ import { existsSync as existsSync2 } from "fs";
4
+ import { mkdir, writeFile } from "fs/promises";
5
+ import { dirname, resolve as resolve2 } from "path";
2
6
  import {
3
7
  createManifest,
4
8
  createStyleFromProject,
5
9
  validateConfig
6
10
  } from "@tileflow/core";
11
+
12
+ // src/icons.ts
7
13
  import { existsSync } from "fs";
8
- import { mkdir, writeFile } from "fs/promises";
9
- import { resolve } from "path";
10
- import { createJiti } from "jiti";
14
+ import { readdir } from "fs/promises";
15
+ import { extname, resolve } from "path";
16
+ var iconFileExtensions = /* @__PURE__ */ new Set([".svg", ".png", ".jpg", ".jpeg", ".webp"]);
17
+ var iconSpriteSize = 24;
18
+ async function prepareTileflowProjectIcons(project, options) {
19
+ const nextProject = cloneProject(project);
20
+ const localSources = getLocalIconSources(project, options.cwd);
21
+ for (const localSource of localSources) {
22
+ const mapConfig = nextProject.maps[localSource.mapName];
23
+ if (!mapConfig) {
24
+ continue;
25
+ }
26
+ const iconSet = resolveIconSet(mapConfig.icons, nextProject.icons);
27
+ if (!iconSet) {
28
+ continue;
29
+ }
30
+ const spriteUrl = joinUrl(options.assetBaseUrl, `icons/${localSource.mapName}/sprite`);
31
+ mapConfig.icons = {
32
+ ...iconSet,
33
+ sprite: spriteUrl
34
+ };
35
+ }
36
+ if (localSources.length === 0) {
37
+ return {
38
+ assets: [],
39
+ project: nextProject,
40
+ watchPaths: []
41
+ };
42
+ }
43
+ const assets = (await Promise.all(
44
+ localSources.map(async (source) => buildSpriteAssets(source.sourceDir, source.mapName))
45
+ )).flat();
46
+ return {
47
+ assets,
48
+ project: nextProject,
49
+ watchPaths: localSources.map((source) => source.sourceDir)
50
+ };
51
+ }
52
+ function getTileflowIconWatchPaths(project, cwd) {
53
+ return uniqueStrings(getLocalIconSources(project, cwd).map((source) => source.sourceDir));
54
+ }
55
+ function getLocalIconSources(project, cwd) {
56
+ return Object.entries(project.maps).flatMap(([mapName, mapConfig]) => {
57
+ const iconSet = resolveIconSet(mapConfig.icons, project.icons);
58
+ const sourceDir = resolveLocalIconSource(iconSet, cwd);
59
+ return sourceDir ? [{ mapName, sourceDir }] : [];
60
+ });
61
+ }
62
+ function resolveLocalIconSource(iconSet, cwd) {
63
+ if (!iconSet?.source) {
64
+ return null;
65
+ }
66
+ return resolve(cwd, iconSet.source);
67
+ }
68
+ function resolveIconSet(icons, iconSets, path = []) {
69
+ if (!icons) {
70
+ return void 0;
71
+ }
72
+ if (typeof icons === "string") {
73
+ const referenced = iconSets?.[icons];
74
+ if (referenced) {
75
+ if (path.includes(icons)) {
76
+ throw new Error(`Circular Tileflow icon set extends: ${[...path, icons].join(" -> ")}`);
77
+ }
78
+ return resolveIconSet(referenced, iconSets, [...path, icons]);
79
+ }
80
+ return isRemoteSpriteReference(icons) ? { sprite: icons } : { source: icons };
81
+ }
82
+ const extended = icons.extends ? resolveIconSet(icons.extends, iconSets, path) : void 0;
83
+ return {
84
+ ...extended,
85
+ ...icons,
86
+ mapping: {
87
+ ...extended?.mapping,
88
+ ...icons.mapping
89
+ }
90
+ };
91
+ }
92
+ async function buildSpriteAssets(sourceDir, mapName) {
93
+ const icons = await readIconFiles(sourceDir);
94
+ if (icons.length === 0) {
95
+ throw new Error(`No SVG or PNG icons found in ${sourceDir}.`);
96
+ }
97
+ const [sprite, sprite2x] = await Promise.all([
98
+ buildSprite(sourceDir, icons, 1),
99
+ buildSprite(sourceDir, icons, 2)
100
+ ]);
101
+ return [
102
+ jsonAsset(`icons/${mapName}/sprite.json`, sprite.index),
103
+ binaryAsset(`icons/${mapName}/sprite.png`, sprite.image, "image/png"),
104
+ jsonAsset(`icons/${mapName}/sprite@2x.json`, sprite2x.index),
105
+ binaryAsset(`icons/${mapName}/sprite@2x.png`, sprite2x.image, "image/png")
106
+ ];
107
+ }
108
+ async function readIconFiles(sourceDir) {
109
+ if (!existsSync(sourceDir)) {
110
+ throw new Error(`Icon source not found: ${sourceDir}`);
111
+ }
112
+ const entries = await readdir(sourceDir, { withFileTypes: true });
113
+ return entries.filter((entry) => entry.isFile() && iconFileExtensions.has(extname(entry.name).toLowerCase())).map((entry) => ({
114
+ name: entry.name.slice(0, -extname(entry.name).length),
115
+ path: resolve(sourceDir, entry.name)
116
+ })).sort((left, right) => left.name.localeCompare(right.name));
117
+ }
118
+ async function buildSprite(sourceDir, icons, pixelRatio) {
119
+ const sharp = await loadSharp();
120
+ const cellSize = iconSpriteSize * pixelRatio;
121
+ const columns = Math.ceil(Math.sqrt(icons.length));
122
+ const rows = Math.ceil(icons.length / columns);
123
+ const width = columns * cellSize;
124
+ const height = rows * cellSize;
125
+ const index = {};
126
+ const composite = await Promise.all(
127
+ icons.map(async (icon, indexNumber) => {
128
+ const column = indexNumber % columns;
129
+ const row = Math.floor(indexNumber / columns);
130
+ const left = column * cellSize;
131
+ const top = row * cellSize;
132
+ index[icon.name] = {
133
+ height: cellSize,
134
+ pixelRatio,
135
+ width: cellSize,
136
+ x: left,
137
+ y: top
138
+ };
139
+ return {
140
+ input: await sharp(icon.path, { density: 72 * pixelRatio }).resize(cellSize, cellSize, {
141
+ background: { alpha: 0, b: 0, g: 0, r: 0 },
142
+ fit: "contain"
143
+ }).png().toBuffer(),
144
+ left,
145
+ top
146
+ };
147
+ })
148
+ );
149
+ const image = await sharp({
150
+ create: {
151
+ background: { alpha: 0, b: 0, g: 0, r: 0 },
152
+ channels: 4,
153
+ height,
154
+ width
155
+ }
156
+ }).composite(composite).png().toBuffer();
157
+ return {
158
+ image,
159
+ index,
160
+ sourceDir
161
+ };
162
+ }
163
+ async function loadSharp() {
164
+ try {
165
+ return (await import("sharp")).default;
166
+ } catch (error) {
167
+ throw new Error(
168
+ 'Local icon sprites require the optional "sharp" package. Install sharp or disable local icons.',
169
+ { cause: error }
170
+ );
171
+ }
172
+ }
173
+ function jsonAsset(fileName, value) {
174
+ return {
175
+ contentType: "application/json; charset=utf-8",
176
+ fileName,
177
+ source: `${JSON.stringify(value, null, 2)}
178
+ `
179
+ };
180
+ }
181
+ function binaryAsset(fileName, source, contentType) {
182
+ return {
183
+ contentType,
184
+ fileName,
185
+ source
186
+ };
187
+ }
188
+ function cloneProject(project) {
189
+ return JSON.parse(JSON.stringify(project));
190
+ }
191
+ function isRemoteSpriteReference(value) {
192
+ return value.startsWith("http://") || value.startsWith("https://") || value.startsWith("/") || value.startsWith("data:");
193
+ }
194
+ function joinUrl(base, path) {
195
+ const trimmedBase = base.replace(/\/+$/g, "");
196
+ const trimmedPath = path.replace(/^\/+/g, "");
197
+ return trimmedBase ? `${trimmedBase}/${trimmedPath}` : trimmedPath;
198
+ }
199
+ function uniqueStrings(values) {
200
+ return [...new Set(values)];
201
+ }
202
+
203
+ // src/index.ts
11
204
  var defaultTileflowApiUrl = "https://api.tileflow.dev";
12
205
  var defaultTileflowConfigPath = "tileflow.config.ts";
13
206
  var defaultTileflowManifestPath = "public/tileflow/manifest.json";
@@ -22,8 +215,8 @@ var TileflowValidationError = class extends Error {
22
215
  };
23
216
  async function loadTileflowConfig(configPath = defaultTileflowConfigPath, options = {}) {
24
217
  const cwd = options.cwd ?? process.cwd();
25
- const resolvedPath = resolve(cwd, configPath);
26
- if (!existsSync(resolvedPath)) {
218
+ const resolvedPath = resolve2(cwd, configPath);
219
+ if (!existsSync2(resolvedPath)) {
27
220
  throw new Error(`Config not found: ${configPath}`);
28
221
  }
29
222
  const jiti = createJiti(import.meta.url, {
@@ -81,50 +274,104 @@ function createTileflowStyle(project, mapName, options = {}) {
81
274
  return createStyleFromProject(project, mapName, options);
82
275
  }
83
276
  async function createTileflowBuildArtifacts(options = {}) {
84
- const project = await loadValidTileflowConfig(
85
- options.config ?? defaultTileflowConfigPath,
86
- {
87
- cwd: options.cwd,
88
- fresh: true
89
- }
90
- );
277
+ const project = await loadValidTileflowConfig(options.config ?? defaultTileflowConfigPath, {
278
+ cwd: options.cwd,
279
+ fresh: true
280
+ });
281
+ const prepared = await prepareTileflowProjectIcons(project, {
282
+ assetBaseUrl: resolveAssetBaseUrl(options),
283
+ cwd: options.cwd ?? process.cwd()
284
+ });
91
285
  const styleOptions = { tileBaseUrl: options.tileBaseUrl };
92
286
  const styles = Object.fromEntries(
93
- getTileflowMapNames(project).map((mapName) => [
287
+ getTileflowMapNames(prepared.project).map((mapName) => [
94
288
  mapName,
95
- createStyleFromProject(project, mapName, styleOptions)
289
+ createStyleFromProject(prepared.project, mapName, styleOptions)
96
290
  ])
97
291
  );
98
292
  return {
99
- manifest: createManifest(project, {
293
+ assets: prepared.assets,
294
+ manifest: createManifest(prepared.project, {
100
295
  styleBaseUrl: options.styleBaseUrl,
101
296
  tileBaseUrl: options.tileBaseUrl
102
297
  }),
103
- styles
298
+ styles,
299
+ watchPaths: prepared.watchPaths
104
300
  };
105
301
  }
302
+ async function getTileflowWatchPaths(options = {}) {
303
+ const cwd = options.cwd ?? process.cwd();
304
+ const project = await loadValidTileflowConfig(options.config ?? defaultTileflowConfigPath, {
305
+ cwd,
306
+ fresh: true
307
+ });
308
+ return getTileflowIconWatchPaths(project, cwd);
309
+ }
106
310
  async function writeTileflowBuildArtifacts(options) {
107
311
  const cwd = options.cwd ?? process.cwd();
108
- const outDir = resolve(cwd, options.outDir);
109
- const stylesDir = resolve(outDir, "styles");
312
+ const outDir = resolve2(cwd, options.outDir);
313
+ const stylesDir = resolve2(outDir, "styles");
110
314
  const artifacts = await createTileflowBuildArtifacts(options);
111
315
  await mkdir(stylesDir, { recursive: true });
112
316
  for (const [mapName, style] of Object.entries(artifacts.styles)) {
113
317
  await writeFile(
114
- resolve(stylesDir, `${mapName}.json`),
318
+ resolve2(stylesDir, `${mapName}.json`),
115
319
  `${JSON.stringify(style, null, 2)}
116
320
  `,
117
321
  "utf8"
118
322
  );
119
323
  }
324
+ for (const asset of artifacts.assets) {
325
+ const outputPath = resolve2(outDir, asset.fileName);
326
+ await mkdir(dirname(outputPath), { recursive: true });
327
+ await writeFile(outputPath, asset.source);
328
+ }
120
329
  await writeFile(
121
- resolve(outDir, "manifest.json"),
330
+ resolve2(outDir, "manifest.json"),
122
331
  `${JSON.stringify(artifacts.manifest, null, 2)}
123
332
  `,
124
333
  "utf8"
125
334
  );
126
335
  return artifacts;
127
336
  }
337
+ function isTileflowRequestUrl(url, basePath) {
338
+ const pathname = (url ?? "/").split("?")[0] ?? "/";
339
+ if (!basePath) {
340
+ return pathname === "/manifest.json" || pathname === "/style.json" || pathname === "/" || pathname.startsWith("/icons/") || pathname.startsWith("/styles/");
341
+ }
342
+ return pathname === basePath || pathname.startsWith(`${basePath}/`);
343
+ }
344
+ function createTileflowNodeRequest(request) {
345
+ const protocol = getNodeHeader(request, "x-forwarded-proto") ?? "http";
346
+ const host = getNodeHeader(request, "host") ?? "localhost";
347
+ return new Request(`${protocol}://${host}${request.url ?? "/"}`, {
348
+ method: request.method
349
+ });
350
+ }
351
+ async function writeTileflowNodeResponse(response, tileflowResponse) {
352
+ response.statusCode = tileflowResponse.status;
353
+ tileflowResponse.headers.forEach((value, key) => {
354
+ response.setHeader(key, value);
355
+ });
356
+ response.end(Buffer.from(await tileflowResponse.arrayBuffer()));
357
+ }
358
+ function joinTileflowPublicUrl(publicBase, basePath) {
359
+ const trimmedBase = publicBase.replace(/\/+$/g, "");
360
+ const trimmedPath = basePath.replace(/^\/+|\/+$/g, "");
361
+ if (!trimmedPath) {
362
+ return trimmedBase || "";
363
+ }
364
+ if (!trimmedBase || trimmedBase === ".") {
365
+ return `/${trimmedPath}`;
366
+ }
367
+ return `${trimmedBase}/${trimmedPath}`;
368
+ }
369
+ function getTileflowAssetBasePath(basePath) {
370
+ return basePath.replace(/^\/+|\/+$/g, "");
371
+ }
372
+ function getTileflowAssetFileName(basePath, fileName) {
373
+ return basePath ? `${basePath}/${fileName}` : fileName;
374
+ }
128
375
  function createTileflowDevRequestHandler(options = {}) {
129
376
  const basePath = normalizeTileflowBasePath(options.basePath);
130
377
  const configPath = options.config ?? defaultTileflowConfigPath;
@@ -136,6 +383,16 @@ function createTileflowDevRequestHandler(options = {}) {
136
383
  }
137
384
  const url = new URL(request.url);
138
385
  const path = stripTileflowBasePath(url.pathname, basePath);
386
+ const loadPreparedProject = async () => {
387
+ const project = await loadValidTileflowConfig(configPath, {
388
+ cwd,
389
+ fresh: true
390
+ });
391
+ return prepareTileflowProjectIcons(project, {
392
+ assetBaseUrl: `${url.origin}${basePath}`,
393
+ cwd
394
+ });
395
+ };
139
396
  if (path === null) {
140
397
  return jsonResponse({ error: "Not found" }, 404);
141
398
  }
@@ -145,9 +402,7 @@ function createTileflowDevRequestHandler(options = {}) {
145
402
  cwd,
146
403
  fresh: true
147
404
  });
148
- return htmlResponse(
149
- previewHtml(getFirstTileflowMapName(project), basePath)
150
- );
405
+ return htmlResponse(previewHtml(getFirstTileflowMapName(project), basePath));
151
406
  }
152
407
  if (path === "/manifest.json") {
153
408
  const project = await loadValidTileflowConfig(configPath, {
@@ -163,31 +418,34 @@ function createTileflowDevRequestHandler(options = {}) {
163
418
  );
164
419
  }
165
420
  if (path === "/style.json") {
166
- const project = await loadValidTileflowConfig(configPath, {
167
- cwd,
168
- fresh: true
169
- });
421
+ const prepared = await loadPreparedProject();
170
422
  return jsonResponse(
171
- createStyleFromProject(project, getFirstTileflowMapName(project), {
423
+ createStyleFromProject(prepared.project, getFirstTileflowMapName(prepared.project), {
172
424
  tileBaseUrl
173
425
  })
174
426
  );
175
427
  }
176
428
  if (path.startsWith("/styles/")) {
177
- const project = await loadValidTileflowConfig(configPath, {
178
- cwd,
179
- fresh: true
180
- });
429
+ const prepared = await loadPreparedProject();
181
430
  const mapName = getTileflowStyleMapName(path);
182
- if (!project.maps[mapName]) {
431
+ if (!prepared.project.maps[mapName]) {
183
432
  return jsonResponse({ error: `Unknown map: ${mapName}` }, 404);
184
433
  }
185
434
  return jsonResponse(
186
- createStyleFromProject(project, mapName, {
435
+ createStyleFromProject(prepared.project, mapName, {
187
436
  tileBaseUrl
188
437
  })
189
438
  );
190
439
  }
440
+ if (path.startsWith("/icons/")) {
441
+ const prepared = await loadPreparedProject();
442
+ const assetName = path.replace(/^\/+/, "");
443
+ const asset = prepared.assets.find((candidate) => candidate.fileName === assetName);
444
+ if (!asset) {
445
+ return jsonResponse({ error: `Unknown icon asset: ${assetName}` }, 404);
446
+ }
447
+ return assetResponse(asset);
448
+ }
191
449
  return jsonResponse({ error: "Not found" }, 404);
192
450
  } catch (error) {
193
451
  options.onError?.(error);
@@ -195,6 +453,10 @@ function createTileflowDevRequestHandler(options = {}) {
195
453
  }
196
454
  };
197
455
  }
456
+ function getNodeHeader(request, name) {
457
+ const value = request.headers[name];
458
+ return Array.isArray(value) ? value[0] : value;
459
+ }
198
460
  function normalizeTileflowBasePath(value = "") {
199
461
  const trimmed = value.trim().replace(/^\/+|\/+$/g, "");
200
462
  return trimmed ? `/${trimmed}` : "";
@@ -245,6 +507,28 @@ function htmlResponse(body, status = 200) {
245
507
  status
246
508
  });
247
509
  }
510
+ function assetResponse(asset) {
511
+ const body = typeof asset.source === "string" ? asset.source : Buffer.from(asset.source);
512
+ return new Response(body, {
513
+ headers: {
514
+ "Access-Control-Allow-Origin": "*",
515
+ "Cache-Control": "no-store",
516
+ "Content-Type": asset.contentType
517
+ }
518
+ });
519
+ }
520
+ function resolveAssetBaseUrl(options) {
521
+ if (options.assetBaseUrl !== void 0) {
522
+ return options.assetBaseUrl;
523
+ }
524
+ if (options.styleBaseUrl && !isRelativePublicUrl(options.styleBaseUrl)) {
525
+ return options.styleBaseUrl;
526
+ }
527
+ return "..";
528
+ }
529
+ function isRelativePublicUrl(value) {
530
+ return !(value.startsWith("http://") || value.startsWith("https://") || value.startsWith("/") || value.startsWith("data:"));
531
+ }
248
532
  function previewHtml(mapName, basePath) {
249
533
  const styleUrl = `${basePath}/styles/${mapName}.json`;
250
534
  return `<!doctype html>
@@ -266,9 +550,9 @@ function previewHtml(mapName, basePath) {
266
550
  top: 16px;
267
551
  z-index: 1;
268
552
  border-radius: 8px;
269
- background: rgba(248, 246, 241, 0.92);
270
- border: 1px solid rgba(46, 46, 46, 0.12);
271
- color: #2E2E2E;
553
+ background: rgba(246, 247, 243, 0.92);
554
+ border: 1px solid rgba(60, 64, 67, 0.12);
555
+ color: #3C4043;
272
556
  padding: 10px 12px;
273
557
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12);
274
558
  }
@@ -299,16 +583,23 @@ export {
299
583
  createTileflowBuildArtifacts,
300
584
  createTileflowDevRequestHandler,
301
585
  createTileflowManifest,
586
+ createTileflowNodeRequest,
302
587
  createTileflowStyle,
303
588
  defaultTileflowApiUrl,
304
589
  defaultTileflowConfigPath,
305
590
  defaultTileflowManifestPath,
306
591
  defaultTileflowTileset,
307
592
  getFirstTileflowMapName,
593
+ getTileflowAssetBasePath,
594
+ getTileflowAssetFileName,
308
595
  getTileflowMapNames,
309
596
  getTileflowStyleMapName,
597
+ getTileflowWatchPaths,
598
+ isTileflowRequestUrl,
599
+ joinTileflowPublicUrl,
310
600
  loadTileflowConfig,
311
601
  loadValidTileflowConfig,
312
602
  normalizeTileflowBasePath,
313
- writeTileflowBuildArtifacts
603
+ writeTileflowBuildArtifacts,
604
+ writeTileflowNodeResponse
314
605
  };
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@tileflow/dev",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.10",
4
4
  "description": "Node development utilities for Tileflow configs and builds.",
5
5
  "type": "module",
6
+ "sideEffects": false,
6
7
  "homepage": "https://tileflow.dev",
7
8
  "repository": {
8
9
  "type": "git",
@@ -36,10 +37,13 @@
36
37
  },
37
38
  "dependencies": {
38
39
  "jiti": "^2.7.0",
39
- "@tileflow/core": "0.1.0-alpha.0"
40
+ "@tileflow/core": "0.1.0-alpha.10"
41
+ },
42
+ "optionalDependencies": {
43
+ "sharp": "^0.34.5"
40
44
  },
41
45
  "scripts": {
42
- "build": "tsup src/index.ts --format esm --dts --clean --platform node",
46
+ "build": "tsup src/index.ts --format esm --dts --clean --platform node --external sharp",
43
47
  "typecheck": "tsc -p tsconfig.json --noEmit",
44
48
  "lint": "tsc -p tsconfig.json --noEmit"
45
49
  }