@tileflow/static 0.1.0-alpha.2 → 0.1.0-alpha.21
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/GENERATED_OUTPUT_LICENSE.md +21 -0
- package/LICENSE +201 -0
- package/NOTICE +9 -0
- package/README.md +94 -2
- package/TRADEMARKS.md +10 -0
- package/dist/chunk-A23RC4BE.js +328 -0
- package/dist/chunk-KPT7PASB.js +122 -0
- package/dist/chunk-MGQCHCU3.js +228 -0
- package/dist/chunk-NHQK5UZ2.js +455 -0
- package/dist/chunk-S3WXE5P4.js +99 -0
- package/dist/client.d.ts +58 -0
- package/dist/client.js +25 -0
- package/dist/index.d.ts +6 -248
- package/dist/index.js +49 -375
- package/dist/manifest.d.ts +111 -0
- package/dist/manifest.js +18 -0
- package/dist/overlays.d.ts +16 -0
- package/dist/overlays.js +18 -0
- package/dist/scene-contract-DZMhLDnf.d.ts +159 -0
- package/dist/scene.d.ts +191 -0
- package/dist/scene.js +22 -0
- package/package.json +32 -5
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizeStaticOverlay,
|
|
3
|
+
roundNumber
|
|
4
|
+
} from "./chunk-MGQCHCU3.js";
|
|
5
|
+
|
|
6
|
+
// src/overlays.ts
|
|
7
|
+
function line(input) {
|
|
8
|
+
return normalizeStaticOverlay({ ...input, type: "line" });
|
|
9
|
+
}
|
|
10
|
+
function circle(input) {
|
|
11
|
+
return normalizeStaticOverlay({ ...input, type: "circle" });
|
|
12
|
+
}
|
|
13
|
+
function marker(input) {
|
|
14
|
+
return normalizeStaticOverlay({ ...input, type: "marker" });
|
|
15
|
+
}
|
|
16
|
+
function polygon(input) {
|
|
17
|
+
return normalizeStaticOverlay({ ...input, type: "polygon" });
|
|
18
|
+
}
|
|
19
|
+
function compileStaticOverlays(overlays, options = {}) {
|
|
20
|
+
if (options.longitudeOffsets !== void 0 && (options.longitudeOffsets.length !== overlays.length || options.longitudeOffsets.some(
|
|
21
|
+
(offset) => !Number.isInteger(offset) || Math.abs(offset) > 360 || offset % 360 !== 0
|
|
22
|
+
))) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
"Static overlay longitude offsets must contain one -360, 0, or 360 per overlay"
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
const sources = {};
|
|
28
|
+
const layers = [];
|
|
29
|
+
for (const [index, overlay] of overlays.entries()) {
|
|
30
|
+
const longitudeOffset = options.longitudeOffsets?.[index] ?? 0;
|
|
31
|
+
const id = safeLayerId(`overlay-${index + 1}-${overlay.id ?? overlay.type}`);
|
|
32
|
+
const sourceId = `${id}-source`;
|
|
33
|
+
if (overlay.type === "line") {
|
|
34
|
+
sources[sourceId] = {
|
|
35
|
+
data: feature("LineString", shiftLongitudes(overlay.coordinates, longitudeOffset)),
|
|
36
|
+
type: "geojson"
|
|
37
|
+
};
|
|
38
|
+
layers.push({
|
|
39
|
+
id,
|
|
40
|
+
layout: { "line-cap": "round", "line-join": "round" },
|
|
41
|
+
paint: {
|
|
42
|
+
"line-color": overlay.color,
|
|
43
|
+
"line-opacity": overlay.opacity,
|
|
44
|
+
"line-width": overlay.width
|
|
45
|
+
},
|
|
46
|
+
source: sourceId,
|
|
47
|
+
type: "line"
|
|
48
|
+
});
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (overlay.type === "polygon") {
|
|
52
|
+
sources[sourceId] = {
|
|
53
|
+
data: feature("Polygon", shiftLongitudes(overlay.coordinates, longitudeOffset)),
|
|
54
|
+
type: "geojson"
|
|
55
|
+
};
|
|
56
|
+
layers.push({
|
|
57
|
+
id,
|
|
58
|
+
paint: {
|
|
59
|
+
"fill-color": overlay.fill,
|
|
60
|
+
"fill-opacity": overlay.opacity
|
|
61
|
+
},
|
|
62
|
+
source: sourceId,
|
|
63
|
+
type: "fill"
|
|
64
|
+
});
|
|
65
|
+
if (overlay.stroke && overlay.strokeWidth > 0) {
|
|
66
|
+
layers.push({
|
|
67
|
+
id: `${id}-stroke`,
|
|
68
|
+
layout: { "line-cap": "round", "line-join": "round" },
|
|
69
|
+
paint: {
|
|
70
|
+
"line-color": overlay.stroke,
|
|
71
|
+
"line-width": overlay.strokeWidth
|
|
72
|
+
},
|
|
73
|
+
source: sourceId,
|
|
74
|
+
type: "line"
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
sources[sourceId] = {
|
|
80
|
+
data: feature("Point", shiftLongitudes(overlay.coordinate, longitudeOffset)),
|
|
81
|
+
type: "geojson"
|
|
82
|
+
};
|
|
83
|
+
layers.push({
|
|
84
|
+
id,
|
|
85
|
+
paint: {
|
|
86
|
+
"circle-color": overlay.color,
|
|
87
|
+
"circle-opacity": overlay.type === "circle" ? overlay.opacity : 1,
|
|
88
|
+
"circle-radius": overlay.radius,
|
|
89
|
+
"circle-stroke-color": overlay.strokeColor ?? overlay.color,
|
|
90
|
+
"circle-stroke-width": overlay.strokeWidth
|
|
91
|
+
},
|
|
92
|
+
source: sourceId,
|
|
93
|
+
type: "circle"
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return { layers, sources };
|
|
97
|
+
}
|
|
98
|
+
function shiftLongitudes(value, offset) {
|
|
99
|
+
if (!Array.isArray(value)) return value;
|
|
100
|
+
if (value.length === 2 && typeof value[0] === "number" && typeof value[1] === "number") {
|
|
101
|
+
return [roundNumber(value[0] + offset), value[1]];
|
|
102
|
+
}
|
|
103
|
+
return value.map((entry) => shiftLongitudes(entry, offset));
|
|
104
|
+
}
|
|
105
|
+
function feature(type, coordinates) {
|
|
106
|
+
return {
|
|
107
|
+
geometry: { coordinates, type },
|
|
108
|
+
properties: {},
|
|
109
|
+
type: "Feature"
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function safeLayerId(value) {
|
|
113
|
+
return value.replace(/[^a-zA-Z0-9_-]/g, "-").slice(0, 80);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export {
|
|
117
|
+
line,
|
|
118
|
+
circle,
|
|
119
|
+
marker,
|
|
120
|
+
polygon,
|
|
121
|
+
compileStaticOverlays
|
|
122
|
+
};
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// src/canonical.ts
|
|
2
|
+
function stableStringify(value) {
|
|
3
|
+
return JSON.stringify(sortJson(value));
|
|
4
|
+
}
|
|
5
|
+
function roundNumber(value) {
|
|
6
|
+
return Number(value.toFixed(6));
|
|
7
|
+
}
|
|
8
|
+
function stripUndefined(value) {
|
|
9
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== void 0));
|
|
13
|
+
}
|
|
14
|
+
function jsonByteLength(value) {
|
|
15
|
+
return new TextEncoder().encode(JSON.stringify(value)).length;
|
|
16
|
+
}
|
|
17
|
+
async function hashStableValue(value) {
|
|
18
|
+
const bytes = new TextEncoder().encode(stableStringify(value));
|
|
19
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
20
|
+
return base64Url(new Uint8Array(digest));
|
|
21
|
+
}
|
|
22
|
+
function isSafeHttpUrl(value) {
|
|
23
|
+
try {
|
|
24
|
+
const url = new URL(value);
|
|
25
|
+
return ["http:", "https:"].includes(url.protocol) && !url.username && !url.password && !url.hash;
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function sortJson(value) {
|
|
31
|
+
if (Array.isArray(value)) {
|
|
32
|
+
return value.map(sortJson);
|
|
33
|
+
}
|
|
34
|
+
if (value && typeof value === "object") {
|
|
35
|
+
return Object.fromEntries(
|
|
36
|
+
Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, sortJson(entry)])
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
function base64Url(bytes) {
|
|
42
|
+
let binary = "";
|
|
43
|
+
for (const byte of bytes) {
|
|
44
|
+
binary += String.fromCharCode(byte);
|
|
45
|
+
}
|
|
46
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/scene-contract.ts
|
|
50
|
+
import { z } from "zod";
|
|
51
|
+
var staticSceneSchemaVersion = 1;
|
|
52
|
+
var MAX_OVERLAY_LATITUDE = 85.051129;
|
|
53
|
+
var staticSceneLimits = {
|
|
54
|
+
maxDimension: 1280,
|
|
55
|
+
maxGeoJsonBytes: 96e3,
|
|
56
|
+
maxOverlays: 24,
|
|
57
|
+
maxPathCoordinates: 2e3,
|
|
58
|
+
maxPhysicalPixels: 1280 * 1280,
|
|
59
|
+
minDimension: 64
|
|
60
|
+
};
|
|
61
|
+
var coordinateSchema = z.tuple([z.number().finite(), z.number().finite()]).refine(([lng, lat]) => lng >= -180 && lng <= 180 && lat >= -90 && lat <= 90, {
|
|
62
|
+
message: "Expected [lng, lat] within world bounds"
|
|
63
|
+
});
|
|
64
|
+
var overlayCoordinateSchema = coordinateSchema.refine(
|
|
65
|
+
([, latitude]) => Math.abs(latitude) <= MAX_OVERLAY_LATITUDE,
|
|
66
|
+
{
|
|
67
|
+
message: `Static overlay latitude must be within \xB1${MAX_OVERLAY_LATITUDE}\xB0`
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
var colorSchema = z.string().regex(/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/, {
|
|
71
|
+
message: "Expected a hex color like #C6A15B"
|
|
72
|
+
});
|
|
73
|
+
var portableIdSchema = z.string().min(1).max(64).regex(/^[a-z][a-z0-9-]{0,63}$/u, {
|
|
74
|
+
message: "Expected lowercase kebab-case beginning with a letter"
|
|
75
|
+
}).refine(
|
|
76
|
+
(name) => !["constructor", "prototype"].includes(name) && !/^(?:AUX|CON|NUL|PRN|COM[1-9]|LPT[1-9])$/i.test(name),
|
|
77
|
+
"Expected a portable identifier that is not a reserved filename or prototype key"
|
|
78
|
+
);
|
|
79
|
+
var concreteThemeSchema = portableIdSchema.refine((value) => value !== "system", {
|
|
80
|
+
message: 'Static maps require a concrete theme; "system" is browser-only'
|
|
81
|
+
});
|
|
82
|
+
var sizeSchema = z.object({
|
|
83
|
+
dpr: z.literal(1).optional(),
|
|
84
|
+
height: z.number().int().min(staticSceneLimits.minDimension).max(staticSceneLimits.maxDimension),
|
|
85
|
+
width: z.number().int().min(staticSceneLimits.minDimension).max(staticSceneLimits.maxDimension)
|
|
86
|
+
}).refine(
|
|
87
|
+
(size) => size.width * size.height * (size.dpr ?? 1) <= staticSceneLimits.maxPhysicalPixels,
|
|
88
|
+
{
|
|
89
|
+
message: "Static map exceeds the maximum render pixel budget"
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
var centerCameraSchema = z.object({
|
|
93
|
+
bearing: z.number().finite().min(-180).max(180).optional(),
|
|
94
|
+
center: coordinateSchema,
|
|
95
|
+
type: z.literal("center"),
|
|
96
|
+
zoom: z.number().finite().min(0).max(22)
|
|
97
|
+
});
|
|
98
|
+
var boundsCameraSchema = z.object({
|
|
99
|
+
bearing: z.number().finite().min(-180).max(180).optional(),
|
|
100
|
+
bounds: z.tuple([z.number().finite(), z.number().finite(), z.number().finite(), z.number().finite()]).refine(
|
|
101
|
+
([west, south, east, north]) => west >= -180 && west <= 180 && east >= -180 && east <= 180 && south >= -90 && south <= 90 && north >= -90 && north <= 90 && west !== east && south !== north,
|
|
102
|
+
{ message: "Expected bounds as [west, south, east, north]" }
|
|
103
|
+
),
|
|
104
|
+
padding: z.number().int().min(0).max(256).optional(),
|
|
105
|
+
type: z.literal("bounds")
|
|
106
|
+
}).refine((camera) => camera.bounds[3] > camera.bounds[1], {
|
|
107
|
+
message: "Bounds north must be greater than south"
|
|
108
|
+
});
|
|
109
|
+
var staticPaddingSchema = z.object({
|
|
110
|
+
bottom: z.number().int().nonnegative(),
|
|
111
|
+
left: z.number().int().nonnegative(),
|
|
112
|
+
right: z.number().int().nonnegative(),
|
|
113
|
+
top: z.number().int().nonnegative()
|
|
114
|
+
}).strict();
|
|
115
|
+
var autoPaddingInputSchema = z.union([
|
|
116
|
+
z.number().int().nonnegative(),
|
|
117
|
+
z.object({
|
|
118
|
+
bottom: z.number().int().nonnegative().optional(),
|
|
119
|
+
left: z.number().int().nonnegative().optional(),
|
|
120
|
+
right: z.number().int().nonnegative().optional(),
|
|
121
|
+
top: z.number().int().nonnegative().optional()
|
|
122
|
+
}).strict()
|
|
123
|
+
]);
|
|
124
|
+
var autoCameraSchema = z.object({
|
|
125
|
+
bearing: z.number().finite().min(-180).max(180).optional(),
|
|
126
|
+
maxZoom: z.number().finite().min(0).max(22).optional(),
|
|
127
|
+
padding: autoPaddingInputSchema.optional(),
|
|
128
|
+
type: z.literal("auto")
|
|
129
|
+
});
|
|
130
|
+
var lineOverlaySchema = z.object({
|
|
131
|
+
color: colorSchema.default("#C6A15B"),
|
|
132
|
+
coordinates: z.array(overlayCoordinateSchema).min(2).max(staticSceneLimits.maxPathCoordinates),
|
|
133
|
+
id: z.string().trim().min(1).max(64).optional(),
|
|
134
|
+
opacity: z.number().finite().min(0).max(1).default(1),
|
|
135
|
+
type: z.literal("line"),
|
|
136
|
+
width: z.number().finite().min(0.5).max(32).default(4)
|
|
137
|
+
});
|
|
138
|
+
var circleOverlaySchema = z.object({
|
|
139
|
+
color: colorSchema.default("#C6A15B"),
|
|
140
|
+
coordinate: overlayCoordinateSchema,
|
|
141
|
+
id: z.string().trim().min(1).max(64).optional(),
|
|
142
|
+
opacity: z.number().finite().min(0).max(1).default(1),
|
|
143
|
+
radius: z.number().finite().min(1).max(64).default(6),
|
|
144
|
+
strokeColor: colorSchema.optional(),
|
|
145
|
+
strokeWidth: z.number().finite().min(0).max(16).default(0),
|
|
146
|
+
type: z.literal("circle")
|
|
147
|
+
});
|
|
148
|
+
var markerOverlaySchema = z.object({
|
|
149
|
+
color: colorSchema.default("#C6A15B"),
|
|
150
|
+
coordinate: overlayCoordinateSchema,
|
|
151
|
+
id: z.string().trim().min(1).max(64).optional(),
|
|
152
|
+
radius: z.number().finite().min(2).max(64).default(8),
|
|
153
|
+
strokeColor: colorSchema.default("#ffffff"),
|
|
154
|
+
strokeWidth: z.number().finite().min(0).max(16).default(2),
|
|
155
|
+
type: z.literal("marker")
|
|
156
|
+
});
|
|
157
|
+
var polygonRingSchema = z.array(overlayCoordinateSchema).min(4).max(staticSceneLimits.maxPathCoordinates).refine(
|
|
158
|
+
(ring) => {
|
|
159
|
+
const first = ring[0];
|
|
160
|
+
const last = ring[ring.length - 1];
|
|
161
|
+
return Boolean(first && last && first[0] === last[0] && first[1] === last[1]);
|
|
162
|
+
},
|
|
163
|
+
{ message: "Polygon rings must end at their starting coordinate" }
|
|
164
|
+
);
|
|
165
|
+
var polygonOverlaySchema = z.object({
|
|
166
|
+
coordinates: z.array(polygonRingSchema).min(1).max(16),
|
|
167
|
+
fill: colorSchema.default("#C6A15B"),
|
|
168
|
+
id: z.string().trim().min(1).max(64).optional(),
|
|
169
|
+
opacity: z.number().finite().min(0).max(1).default(0.28),
|
|
170
|
+
stroke: colorSchema.optional(),
|
|
171
|
+
strokeWidth: z.number().finite().min(0).max(16).default(0),
|
|
172
|
+
type: z.literal("polygon")
|
|
173
|
+
});
|
|
174
|
+
var staticOverlaySchema = z.discriminatedUnion("type", [
|
|
175
|
+
lineOverlaySchema,
|
|
176
|
+
circleOverlaySchema,
|
|
177
|
+
markerOverlaySchema,
|
|
178
|
+
polygonOverlaySchema
|
|
179
|
+
]);
|
|
180
|
+
var staticSceneSchema = z.object({
|
|
181
|
+
camera: z.discriminatedUnion("type", [centerCameraSchema, boundsCameraSchema, autoCameraSchema]),
|
|
182
|
+
map: portableIdSchema,
|
|
183
|
+
overlays: z.array(staticOverlaySchema).max(staticSceneLimits.maxOverlays).default([]),
|
|
184
|
+
size: sizeSchema,
|
|
185
|
+
theme: concreteThemeSchema
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// src/overlay-normalization.ts
|
|
189
|
+
function normalizeStaticOverlay(overlay) {
|
|
190
|
+
const parsed = staticOverlaySchema.parse(overlay);
|
|
191
|
+
const normalized = stripUndefined({ ...parsed });
|
|
192
|
+
if ("coordinate" in normalized) {
|
|
193
|
+
normalized.coordinate = normalizeStaticCoordinate(normalized.coordinate);
|
|
194
|
+
}
|
|
195
|
+
if ("coordinates" in normalized) {
|
|
196
|
+
normalized.coordinates = normalizeStaticCoordinates(normalized.coordinates);
|
|
197
|
+
}
|
|
198
|
+
return normalized;
|
|
199
|
+
}
|
|
200
|
+
function normalizeStaticCoordinate(coordinate) {
|
|
201
|
+
return [roundNumber(coordinate[0]), roundNumber(coordinate[1])];
|
|
202
|
+
}
|
|
203
|
+
function normalizeStaticCoordinates(value) {
|
|
204
|
+
if (!Array.isArray(value)) {
|
|
205
|
+
return value;
|
|
206
|
+
}
|
|
207
|
+
if (value.length === 2 && typeof value[0] === "number" && typeof value[1] === "number") {
|
|
208
|
+
return normalizeStaticCoordinate(value);
|
|
209
|
+
}
|
|
210
|
+
return value.map(normalizeStaticCoordinates);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export {
|
|
214
|
+
stableStringify,
|
|
215
|
+
roundNumber,
|
|
216
|
+
stripUndefined,
|
|
217
|
+
jsonByteLength,
|
|
218
|
+
hashStableValue,
|
|
219
|
+
isSafeHttpUrl,
|
|
220
|
+
staticSceneSchemaVersion,
|
|
221
|
+
MAX_OVERLAY_LATITUDE,
|
|
222
|
+
staticSceneLimits,
|
|
223
|
+
staticPaddingSchema,
|
|
224
|
+
staticOverlaySchema,
|
|
225
|
+
staticSceneSchema,
|
|
226
|
+
normalizeStaticOverlay,
|
|
227
|
+
normalizeStaticCoordinate
|
|
228
|
+
};
|