@tileflow/static 0.1.0-alpha.21 → 0.1.0-alpha.23

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.
@@ -0,0 +1,175 @@
1
+ import {
2
+ normalizeStaticOverlay,
3
+ roundNumber
4
+ } from "./chunk-6DWVCK64.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 symbol(input) {
20
+ return normalizeStaticOverlay({ ...input, type: "symbol" });
21
+ }
22
+ function compileStaticOverlays(overlays, options = {}) {
23
+ if (options.longitudeOffsets !== void 0 && (options.longitudeOffsets.length !== overlays.length || options.longitudeOffsets.some(
24
+ (offset) => !Number.isInteger(offset) || Math.abs(offset) > 360 || offset % 360 !== 0
25
+ ))) {
26
+ throw new Error(
27
+ "Static overlay longitude offsets must contain one -360, 0, or 360 per overlay"
28
+ );
29
+ }
30
+ const sources = {};
31
+ const layers = [];
32
+ const placements = {};
33
+ const symbols = [];
34
+ const pushLayer = (layer, overlay) => {
35
+ const layerId = String(layer.id);
36
+ layers.push(layer);
37
+ placements[layerId] = overlay.placement ?? "above-labels";
38
+ };
39
+ for (const [index, overlay] of overlays.entries()) {
40
+ const longitudeOffset = options.longitudeOffsets?.[index] ?? 0;
41
+ const id = safeLayerId(`tileflow-static-overlay-${index + 1}-${overlay.id ?? overlay.type}`);
42
+ const sourceId = `${id}-source`;
43
+ if (overlay.type === "line") {
44
+ sources[sourceId] = {
45
+ data: feature("LineString", shiftLongitudes(overlay.coordinates, longitudeOffset)),
46
+ type: "geojson"
47
+ };
48
+ pushLayer(
49
+ {
50
+ id,
51
+ layout: { "line-cap": "round", "line-join": "round" },
52
+ paint: {
53
+ "line-color": overlay.color,
54
+ "line-opacity": overlay.opacity,
55
+ "line-width": overlay.width
56
+ },
57
+ source: sourceId,
58
+ type: "line"
59
+ },
60
+ overlay
61
+ );
62
+ continue;
63
+ }
64
+ if (overlay.type === "polygon") {
65
+ sources[sourceId] = {
66
+ data: feature("Polygon", shiftLongitudes(overlay.coordinates, longitudeOffset)),
67
+ type: "geojson"
68
+ };
69
+ pushLayer(
70
+ {
71
+ id,
72
+ paint: {
73
+ "fill-color": overlay.fill,
74
+ "fill-opacity": overlay.opacity
75
+ },
76
+ source: sourceId,
77
+ type: "fill"
78
+ },
79
+ overlay
80
+ );
81
+ if (overlay.stroke && overlay.strokeWidth > 0) {
82
+ pushLayer(
83
+ {
84
+ id: `${id}-stroke`,
85
+ layout: { "line-cap": "round", "line-join": "round" },
86
+ paint: {
87
+ "line-color": overlay.stroke,
88
+ "line-width": overlay.strokeWidth
89
+ },
90
+ source: sourceId,
91
+ type: "line"
92
+ },
93
+ overlay
94
+ );
95
+ }
96
+ continue;
97
+ }
98
+ sources[sourceId] = {
99
+ data: feature("Point", shiftLongitudes(overlay.coordinate, longitudeOffset)),
100
+ type: "geojson"
101
+ };
102
+ if (overlay.type === "symbol") {
103
+ const composed = overlay.label !== void 0;
104
+ const imageId = composed ? safeLayerId(`__tileflow-static-symbol-${index + 1}-${overlay.id}`) : overlay.icon;
105
+ const alwaysVisible = overlay.collision === "always-visible";
106
+ const iconSize = composed ? 1 : overlay.scale;
107
+ const iconOffset = composed ? overlay.offset : overlay.offset.map((value) => roundNumber(value / overlay.scale));
108
+ pushLayer(
109
+ {
110
+ id,
111
+ layout: {
112
+ "icon-allow-overlap": alwaysVisible,
113
+ "icon-anchor": overlay.anchor,
114
+ "icon-ignore-placement": alwaysVisible,
115
+ "icon-image": imageId,
116
+ "icon-offset": iconOffset,
117
+ "icon-pitch-alignment": "viewport",
118
+ "icon-rotation-alignment": "viewport",
119
+ "icon-size": iconSize,
120
+ "symbol-placement": "point",
121
+ "symbol-z-order": "source"
122
+ },
123
+ paint: { "icon-opacity": overlay.opacity },
124
+ source: sourceId,
125
+ type: "symbol"
126
+ },
127
+ overlay
128
+ );
129
+ symbols.push({ imageId, overlayIndex: index });
130
+ continue;
131
+ }
132
+ pushLayer(
133
+ {
134
+ id,
135
+ paint: {
136
+ "circle-color": overlay.color,
137
+ "circle-opacity": overlay.type === "circle" ? overlay.opacity : 1,
138
+ "circle-radius": overlay.radius,
139
+ "circle-stroke-color": overlay.strokeColor ?? overlay.color,
140
+ "circle-stroke-width": overlay.strokeWidth
141
+ },
142
+ source: sourceId,
143
+ type: "circle"
144
+ },
145
+ overlay
146
+ );
147
+ }
148
+ return { layers, placements, sources, symbols };
149
+ }
150
+ function shiftLongitudes(value, offset) {
151
+ if (!Array.isArray(value)) return value;
152
+ if (value.length === 2 && typeof value[0] === "number" && typeof value[1] === "number") {
153
+ return [roundNumber(value[0] + offset), value[1]];
154
+ }
155
+ return value.map((entry) => shiftLongitudes(entry, offset));
156
+ }
157
+ function feature(type, coordinates) {
158
+ return {
159
+ geometry: { coordinates, type },
160
+ properties: {},
161
+ type: "Feature"
162
+ };
163
+ }
164
+ function safeLayerId(value) {
165
+ return value.replace(/[^a-zA-Z0-9_-]/g, "-").slice(0, 80);
166
+ }
167
+
168
+ export {
169
+ line,
170
+ circle,
171
+ marker,
172
+ polygon,
173
+ symbol,
174
+ compileStaticOverlays
175
+ };
@@ -1,19 +1,23 @@
1
1
  import {
2
+ StaticMapError,
2
3
  StaticMapRequestError,
4
+ staticMapErrorResponseSchema,
3
5
  staticMapRequestErrorResponseSchema,
4
6
  validateStaticScene
5
- } from "./chunk-NHQK5UZ2.js";
7
+ } from "./chunk-TBI5AED6.js";
6
8
  import {
7
9
  isSafeHttpUrl,
10
+ jsonByteLength,
8
11
  stableStringify
9
- } from "./chunk-MGQCHCU3.js";
12
+ } from "./chunk-6DWVCK64.js";
10
13
 
11
14
  // src/client.ts
12
15
  import { z } from "zod";
13
16
  var maxStaticMapResponseBytes = 64 * 1024;
14
17
  var staticMapIdempotencyKeyPattern = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$/;
18
+ var STATIC_MAP_RESULT_MEDIA_TYPE = "application/vnd.tileflow.static-map-result+json;version=1";
15
19
  var preparedStaticMapRequestBodies = /* @__PURE__ */ new WeakMap();
16
- var staticMapReadyResultSchema = z.object({
20
+ var staticMapReadyResultBaseSchema = z.object({
17
21
  cached: z.boolean(),
18
22
  hash: z.string().regex(/^[A-Za-z0-9_-]{43}$/),
19
23
  imageUrl: z.string().trim().url().max(2048).refine(isSafeHttpUrl, { message: "Expected an http(s) URL without credentials" }),
@@ -22,6 +26,39 @@ var staticMapReadyResultSchema = z.object({
22
26
  status: z.literal("ready"),
23
27
  unitCost: z.literal(15)
24
28
  }).strict();
29
+ var safeAttributionTextSchema = z.string().min(1).max(16384).refine(hasNoControlCharacters, { message: "Attribution text contains control characters" });
30
+ var staticMapAttributionEntrySchema = z.object({
31
+ authority: z.enum(["platform-notice", "team-declared"]),
32
+ links: z.array(
33
+ z.object({
34
+ label: safeAttributionTextSchema,
35
+ url: z.string().trim().url().max(2048).refine(isSafeHttpUrl, {
36
+ message: "Expected an http(s) URL without credentials or a fragment"
37
+ })
38
+ }).strict()
39
+ ).max(8),
40
+ text: safeAttributionTextSchema
41
+ }).strict();
42
+ var staticMapAttributionResultSchema = z.object({
43
+ entries: z.array(staticMapAttributionEntrySchema).max(18),
44
+ mode: z.enum(["embedded", "external"]),
45
+ position: z.enum(["top-left", "top-right", "bottom-left", "bottom-right"]).nullable()
46
+ }).strict().superRefine((result, context) => {
47
+ if (result.mode === "external" && result.position !== null || result.mode === "embedded" && result.position === null) {
48
+ context.addIssue({
49
+ code: "custom",
50
+ message: "Attribution position must match its mode",
51
+ path: ["position"]
52
+ });
53
+ }
54
+ if (jsonByteLength(result) > 32 * 1024) {
55
+ context.addIssue({ code: "custom", message: "Attribution result exceeds 32 KiB" });
56
+ }
57
+ });
58
+ var staticMapHostedResultSchema = staticMapReadyResultBaseSchema.extend({
59
+ attribution: staticMapAttributionResultSchema,
60
+ resultVersion: z.literal(1)
61
+ }).strict();
25
62
  var staticMapProcessingResultSchema = z.object({
26
63
  operationId: z.string().min(20).max(68),
27
64
  retryAfterMs: z.number().int().min(0).max(5e3),
@@ -92,6 +129,7 @@ async function requestStaticMapUntilReady(request, options) {
92
129
  const fetcher = options.fetch ?? fetch;
93
130
  const createUrl = normalizeStaticMapEndpointUrl(options.createUrl);
94
131
  const headers = {
132
+ Accept: STATIC_MAP_RESULT_MEDIA_TYPE,
95
133
  "Content-Type": "application/json",
96
134
  "Idempotency-Key": idempotency.key
97
135
  };
@@ -109,17 +147,24 @@ async function requestStaticMapUntilReady(request, options) {
109
147
  });
110
148
  throwIfAborted(signal);
111
149
  if (!response.ok) {
112
- const requestError = response.status === 422 ? await readStaticMapRequestErrorResponse(response, 8 * 1024) : await discardBoundedResponse(response, 8 * 1024);
150
+ const errorBody = await readStaticMapErrorBody(response, 8 * 1024);
113
151
  throwIfAborted(signal);
114
- if (requestError) {
115
- throw new StaticMapRequestError(requestError, response.status);
152
+ if (response.status === 422) {
153
+ const requestError = staticMapRequestErrorResponseSchema.safeParse(errorBody);
154
+ if (requestError.success) {
155
+ throw new StaticMapRequestError(requestError.data, response.status);
156
+ }
157
+ }
158
+ const structured = staticMapErrorResponseSchema.safeParse(errorBody);
159
+ if (structured.success) {
160
+ throw new StaticMapError(structured.data, response.status);
116
161
  }
117
162
  throw new Error(`Tileflow static map failed (${response.status}).`);
118
163
  }
119
164
  const json = await readJsonResponse(response);
120
165
  throwIfAborted(signal);
121
166
  if (response.status !== 202) {
122
- const parsed = staticMapReadyResultSchema.safeParse(json);
167
+ const parsed = staticMapHostedResultSchema.safeParse(json);
123
168
  if (!parsed.success) {
124
169
  throw new Error(
125
170
  `Tileflow static map returned an invalid response: ${parsed.error.message}`
@@ -140,6 +185,12 @@ async function requestStaticMapUntilReady(request, options) {
140
185
  }
141
186
  });
142
187
  }
188
+ function hasNoControlCharacters(value) {
189
+ return !Array.from(value).some((character) => {
190
+ const codePoint = character.codePointAt(0) ?? 0;
191
+ return codePoint <= 31 || codePoint === 127;
192
+ });
193
+ }
143
194
  function boundedClientDuration(value, fallback, minimum, maximum, name) {
144
195
  const resolved = value ?? fallback;
145
196
  if (!Number.isSafeInteger(resolved) || resolved < minimum || resolved > maximum) {
@@ -157,22 +208,14 @@ async function readJsonResponse(response) {
157
208
  throw new Error("Tileflow static map returned an invalid response: expected JSON");
158
209
  }
159
210
  }
160
- async function readStaticMapRequestErrorResponse(response, maximumBytes) {
211
+ async function readStaticMapErrorBody(response, maximumBytes) {
161
212
  try {
162
213
  const source = await readBoundedResponseText(response, maximumBytes, `${maximumBytes} bytes`);
163
- const parsed = staticMapRequestErrorResponseSchema.safeParse(JSON.parse(source));
164
- return parsed.success ? parsed.data : null;
214
+ return JSON.parse(source);
165
215
  } catch {
166
216
  return null;
167
217
  }
168
218
  }
169
- async function discardBoundedResponse(response, maximumBytes) {
170
- try {
171
- await readBoundedResponseText(response, maximumBytes, `${maximumBytes} bytes`);
172
- } catch {
173
- }
174
- return null;
175
- }
176
219
  function isStaticMapRequestFailure(value) {
177
220
  if (!("code" in value)) return false;
178
221
  const { ok: _ok, ...response } = value;
@@ -317,7 +360,10 @@ function hasControlCharacter(value) {
317
360
  }
318
361
 
319
362
  export {
320
- staticMapReadyResultSchema,
363
+ STATIC_MAP_RESULT_MEDIA_TYPE,
364
+ staticMapAttributionEntrySchema,
365
+ staticMapAttributionResultSchema,
366
+ staticMapHostedResultSchema,
321
367
  staticMapProcessingResultSchema,
322
368
  validateStaticMapIdempotencyKey,
323
369
  createStaticMapIdempotencyKey,
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  MAX_OVERLAY_LATITUDE,
3
+ isSupportedStaticSymbolCodePoint,
3
4
  jsonByteLength,
4
5
  normalizeStaticCoordinate,
5
6
  normalizeStaticOverlay,
@@ -8,75 +9,166 @@ import {
8
9
  staticSceneLimits,
9
10
  staticSceneSchema,
10
11
  stripUndefined
11
- } from "./chunk-MGQCHCU3.js";
12
+ } from "./chunk-6DWVCK64.js";
12
13
 
13
14
  // src/auto-fit.ts
15
+ import { z as z2 } from "zod";
16
+
17
+ // src/errors.ts
14
18
  import { z } from "zod";
15
- var autoFitOverlayReferenceSchema = z.object({
16
- id: z.string().min(1).max(64),
17
- index: z.number().int().min(0).max(23),
18
- type: z.enum(["circle", "line", "marker", "polygon"])
19
+ var safeErrorTextSchema = z.string().min(1).max(512).refine(hasNoControlCharacters, { message: "Expected no control characters" });
20
+ var safeErrorIdentifierSchema = z.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/u);
21
+ var staticMapErrorResponseSchema = z.object({
22
+ code: safeErrorIdentifierSchema.optional(),
23
+ error: safeErrorTextSchema,
24
+ remainingUnits: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(),
25
+ requestId: safeErrorIdentifierSchema.optional(),
26
+ retryable: z.boolean().optional()
27
+ }).strip();
28
+ var StaticMapError = class extends Error {
29
+ code;
30
+ remainingUnits;
31
+ requestId;
32
+ response;
33
+ retryable;
34
+ status;
35
+ constructor(response, status) {
36
+ const parsed = staticMapErrorResponseSchema.parse(response);
37
+ super(parsed.error);
38
+ this.name = "StaticMapError";
39
+ this.code = parsed.code ?? null;
40
+ this.remainingUnits = parsed.remainingUnits ?? null;
41
+ this.requestId = parsed.requestId ?? null;
42
+ this.response = Object.freeze(parsed);
43
+ this.retryable = parsed.retryable ?? null;
44
+ this.status = status;
45
+ }
46
+ };
47
+ function hasNoControlCharacters(value) {
48
+ return !Array.from(value).some((character) => {
49
+ const codePoint = character.codePointAt(0) ?? 0;
50
+ return codePoint <= 31 || codePoint === 127;
51
+ });
52
+ }
53
+
54
+ // src/auto-fit.ts
55
+ var autoFitOverlayReferenceSchema = z2.object({
56
+ id: z2.string().min(1).max(64),
57
+ index: z2.number().int().min(0).max(23),
58
+ type: z2.enum(["circle", "line", "marker", "polygon", "symbol"])
19
59
  }).strict();
20
- var autoFitViewportSchema = z.object({
21
- height: z.number().int().min(64).max(1280),
22
- width: z.number().int().min(64).max(1280)
60
+ var autoFitViewportSchema = z2.object({
61
+ height: z2.number().int().min(64).max(1280),
62
+ width: z2.number().int().min(64).max(1280)
23
63
  }).strict();
24
- var autoFitDetailsSchema = z.object({
64
+ var autoFitDetailsSchema = z2.object({
25
65
  overlay: autoFitOverlayReferenceSchema.optional(),
26
66
  padding: staticPaddingSchema.optional(),
27
- projection: z.enum(["globe", "mercator"]).optional(),
67
+ projection: z2.enum(["globe", "mercator"]).optional(),
28
68
  requiredInsets: staticPaddingSchema.optional(),
29
- ringIndex: z.number().int().min(0).max(15).optional(),
30
- segmentIndex: z.number().int().min(0).max(1999).optional(),
69
+ ringIndex: z2.number().int().min(0).max(15).optional(),
70
+ segmentIndex: z2.number().int().min(0).max(1999).optional(),
31
71
  viewport: autoFitViewportSchema.optional()
32
72
  }).strict();
33
- var staticOverlayInvalidDetailsSchema = z.object({
34
- latitude: z.number().finite(),
35
- limit: z.literal(MAX_OVERLAY_LATITUDE),
73
+ var staticOverlayInvalidDetailsSchema = z2.object({
74
+ latitude: z2.number().finite(),
75
+ limit: z2.literal(MAX_OVERLAY_LATITUDE),
36
76
  overlay: autoFitOverlayReferenceSchema
37
77
  }).strict();
78
+ var staticLabelUnsupportedDetailsSchema = z2.object({
79
+ codePoint: z2.number().int().min(0).max(1114111),
80
+ overlay: autoFitOverlayReferenceSchema.extend({ type: z2.literal("symbol") }).strict()
81
+ }).strict();
82
+ var staticIconUnavailableDetailsSchema = z2.object({
83
+ icon: z2.string().max(64).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/u),
84
+ overlay: autoFitOverlayReferenceSchema.extend({ type: z2.literal("symbol") }).strict()
85
+ }).strict();
86
+ var staticSymbolTooLargeDetailsSchema = z2.object({
87
+ height: z2.number().finite().positive().max(16384),
88
+ limit: z2.literal(2048),
89
+ overlay: autoFitOverlayReferenceSchema.extend({ type: z2.literal("symbol") }).strict(),
90
+ width: z2.number().finite().positive().max(16384)
91
+ }).strict();
38
92
  var autoFitErrorBase = {
39
- error: z.string().min(1).max(512),
40
- retryable: z.literal(false)
93
+ error: z2.string().min(1).max(512),
94
+ retryable: z2.literal(false)
41
95
  };
42
- var staticMapRequestErrorResponseSchema = z.discriminatedUnion("code", [
43
- z.object({
96
+ var staticMapRequestErrorResponseSchema = z2.discriminatedUnion("code", [
97
+ z2.object({
44
98
  ...autoFitErrorBase,
45
- code: z.literal("AUTO_FIT_EMPTY"),
46
- reason: z.literal("NO_FITTABLE_OVERLAYS")
99
+ code: z2.literal("AUTO_FIT_EMPTY"),
100
+ reason: z2.literal("NO_FITTABLE_OVERLAYS")
47
101
  }).strict(),
48
- z.object({
102
+ z2.object({
49
103
  ...autoFitErrorBase,
50
- code: z.literal("AUTO_FIT_AMBIGUOUS"),
104
+ code: z2.literal("AUTO_FIT_AMBIGUOUS"),
51
105
  details: autoFitDetailsSchema,
52
- reason: z.literal("ANTIMERIDIAN_WRAP_REQUIRED")
106
+ reason: z2.literal("ANTIMERIDIAN_WRAP_REQUIRED")
53
107
  }).strict(),
54
- z.object({
108
+ z2.object({
55
109
  ...autoFitErrorBase,
56
- code: z.literal("AUTO_FIT_IMPOSSIBLE"),
110
+ code: z2.literal("AUTO_FIT_IMPOSSIBLE"),
57
111
  details: autoFitDetailsSchema.optional(),
58
- reason: z.enum([
112
+ reason: z2.enum([
59
113
  "GLOBE_NOT_SIMULTANEOUSLY_VISIBLE",
60
114
  "INSUFFICIENT_VIEWPORT",
61
115
  "CAMERA_UNRESOLVABLE",
62
116
  "PROJECTED_FOOTPRINT_OUTSIDE_VIEWPORT"
63
117
  ])
64
118
  }).strict(),
65
- z.object({
119
+ z2.object({
66
120
  ...autoFitErrorBase,
67
- code: z.literal("STATIC_OVERLAY_INVALID"),
121
+ code: z2.literal("STATIC_OVERLAY_INVALID"),
68
122
  details: staticOverlayInvalidDetailsSchema,
69
- reason: z.literal("OVERLAY_LATITUDE_OUT_OF_RANGE")
123
+ reason: z2.literal("OVERLAY_LATITUDE_OUT_OF_RANGE")
124
+ }).strict(),
125
+ z2.object({
126
+ ...autoFitErrorBase,
127
+ code: z2.literal("STATIC_MAP_LABEL_UNSUPPORTED"),
128
+ details: staticLabelUnsupportedDetailsSchema,
129
+ reason: z2.literal("UNSUPPORTED_GLYPH")
130
+ }).strict(),
131
+ z2.object({
132
+ ...autoFitErrorBase,
133
+ code: z2.literal("STATIC_MAP_ICON_UNAVAILABLE"),
134
+ details: staticIconUnavailableDetailsSchema,
135
+ reason: z2.enum(["ICON_NOT_FOUND", "MANAGED_SPRITE_REQUIRED"])
136
+ }).strict(),
137
+ z2.object({
138
+ ...autoFitErrorBase,
139
+ code: z2.literal("STATIC_MAP_PLACEMENT_UNAVAILABLE"),
140
+ reason: z2.literal("MAP_REDEPLOY_REQUIRED")
141
+ }).strict(),
142
+ z2.object({
143
+ ...autoFitErrorBase,
144
+ code: z2.literal("STATIC_MAP_SYMBOL_TOO_LARGE"),
145
+ details: staticSymbolTooLargeDetailsSchema,
146
+ reason: z2.literal("COMPOSED_IMAGE_TOO_LARGE")
147
+ }).strict(),
148
+ z2.object({
149
+ ...autoFitErrorBase,
150
+ code: z2.literal("STATIC_MAP_ATTRIBUTION_UNSUPPORTED"),
151
+ reason: z2.enum(["UNSUPPORTED_DECLARATION", "UNSUPPORTED_GLYPH"])
152
+ }).strict(),
153
+ z2.object({
154
+ ...autoFitErrorBase,
155
+ code: z2.literal("STATIC_MAP_ATTRIBUTION_TOO_LARGE"),
156
+ reason: z2.literal("CONTENT_LIMIT_EXCEEDED")
157
+ }).strict(),
158
+ z2.object({
159
+ ...autoFitErrorBase,
160
+ code: z2.literal("STATIC_MAP_ATTRIBUTION_DOES_NOT_FIT"),
161
+ reason: z2.literal("BLOCK_DOES_NOT_FIT")
70
162
  }).strict()
71
163
  ]);
72
- var StaticMapRequestError = class extends Error {
164
+ var StaticMapRequestError = class extends StaticMapError {
73
165
  code;
74
166
  details;
75
167
  reason;
76
168
  retryable = false;
77
169
  status;
78
170
  constructor(response, status = 422) {
79
- super(response.error);
171
+ super(response, status);
80
172
  this.name = "StaticMapRequestError";
81
173
  this.code = response.code;
82
174
  this.details = "details" in response ? response.details : void 0;
@@ -84,14 +176,14 @@ var StaticMapRequestError = class extends Error {
84
176
  this.status = status;
85
177
  }
86
178
  };
87
- var staticAutoFitPlanSchema = z.object({
88
- bounds: z.tuple([
89
- z.number().finite(),
90
- z.number().finite().min(-90).max(90),
91
- z.number().finite(),
92
- z.number().finite().min(-90).max(90)
179
+ var staticAutoFitPlanSchema = z2.object({
180
+ bounds: z2.tuple([
181
+ z2.number().finite(),
182
+ z2.number().finite().min(-90).max(90),
183
+ z2.number().finite(),
184
+ z2.number().finite().min(-90).max(90)
93
185
  ]),
94
- longitudeOffsets: z.array(z.number().int().min(-360).max(360)).max(24),
186
+ longitudeOffsets: z2.array(z2.number().int().min(-360).max(360)).max(24),
95
187
  nominalInsets: staticPaddingSchema,
96
188
  requiredInsets: staticPaddingSchema
97
189
  }).strict();
@@ -199,7 +291,7 @@ function failure(response) {
199
291
  return { ...response, ok: false };
200
292
  }
201
293
  function overlayCoordinates(overlay) {
202
- if (overlay.type === "circle" || overlay.type === "marker") {
294
+ if (overlay.type === "circle" || overlay.type === "marker" || overlay.type === "symbol") {
203
295
  return [overlay.coordinate];
204
296
  }
205
297
  if (overlay.type === "line") {
@@ -208,7 +300,8 @@ function overlayCoordinates(overlay) {
208
300
  return overlay.coordinates.flat();
209
301
  }
210
302
  function findAmbiguousSegment(overlay) {
211
- if (overlay.type === "circle" || overlay.type === "marker") return null;
303
+ if (overlay.type === "circle" || overlay.type === "marker" || overlay.type === "symbol")
304
+ return null;
212
305
  const paths = overlay.type === "line" ? [overlay.coordinates] : overlay.coordinates;
213
306
  for (const [ringIndex, path] of paths.entries()) {
214
307
  for (let segmentIndex = 0; segmentIndex < path.length - 1; segmentIndex += 1) {
@@ -228,6 +321,7 @@ function nominalOverlayExpansion(overlay) {
228
321
  if (overlay.type === "circle" || overlay.type === "marker") {
229
322
  return overlay.radius + overlay.strokeWidth;
230
323
  }
324
+ if (overlay.type === "symbol") return 0;
231
325
  if (overlay.type === "line") {
232
326
  return overlay.width / 2 + 0.5;
233
327
  }
@@ -320,8 +414,8 @@ function findStaticOverlayLatitudeFailure(input) {
320
414
  if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) continue;
321
415
  const overlay = candidate;
322
416
  const type = overlay.type;
323
- if (!["circle", "line", "marker", "polygon"].includes(String(type))) continue;
324
- const coordinates = type === "circle" || type === "marker" ? [overlay.coordinate] : overlayCoordinateCandidates(type, overlay.coordinates);
417
+ if (!["circle", "line", "marker", "polygon", "symbol"].includes(String(type))) continue;
418
+ const coordinates = type === "circle" || type === "marker" || type === "symbol" ? [overlay.coordinate] : overlayCoordinateCandidates(type, overlay.coordinates);
325
419
  for (const coordinate of coordinates) {
326
420
  if (!Array.isArray(coordinate) || coordinate.length !== 2 || typeof coordinate[1] !== "number" || !Number.isFinite(coordinate[1]) || Math.abs(coordinate[1]) <= MAX_OVERLAY_LATITUDE) {
327
421
  continue;
@@ -347,6 +441,38 @@ function findStaticOverlayLatitudeFailure(input) {
347
441
  }
348
442
  return null;
349
443
  }
444
+ function findStaticSymbolLabelGlyphFailure(input) {
445
+ if (!input || typeof input !== "object" || Array.isArray(input)) return null;
446
+ const overlays = input.overlays;
447
+ if (!Array.isArray(overlays)) return null;
448
+ for (const [index, candidate] of overlays.entries()) {
449
+ if (index >= staticSceneLimits.maxOverlays) break;
450
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) continue;
451
+ const overlay = candidate;
452
+ if (overlay.type !== "symbol") continue;
453
+ const label = overlay.label;
454
+ const text = typeof label === "string" ? label : label && typeof label === "object" && !Array.isArray(label) ? label.text : void 0;
455
+ if (typeof text !== "string") continue;
456
+ const unsupported = Array.from(text).find(
457
+ (character) => !isSupportedStaticSymbolCodePoint(character.codePointAt(0))
458
+ );
459
+ if (!unsupported) continue;
460
+ const codePoint = unsupported.codePointAt(0);
461
+ const id = typeof overlay.id === "string" && overlay.id.trim() ? overlay.id.trim() : `overlay-${index + 1}`;
462
+ return {
463
+ code: "STATIC_MAP_LABEL_UNSUPPORTED",
464
+ details: {
465
+ codePoint,
466
+ overlay: { id: id.slice(0, 64), index, type: "symbol" }
467
+ },
468
+ error: `Overlay overlays.${index} label contains unsupported glyph U+${codePoint.toString(16).toUpperCase().padStart(4, "0")}`,
469
+ ok: false,
470
+ reason: "UNSUPPORTED_GLYPH",
471
+ retryable: false
472
+ };
473
+ }
474
+ return null;
475
+ }
350
476
  function overlayCoordinateCandidates(type, value) {
351
477
  if (!Array.isArray(value)) return [];
352
478
  if (type === "line") {
@@ -361,6 +487,8 @@ function overlayCoordinateCandidates(type, value) {
361
487
  function validateStaticScene(input) {
362
488
  const overlayLatitudeFailure = findStaticOverlayLatitudeFailure(input);
363
489
  if (overlayLatitudeFailure) return overlayLatitudeFailure;
490
+ const labelGlyphFailure = findStaticSymbolLabelGlyphFailure(input);
491
+ if (labelGlyphFailure) return labelGlyphFailure;
364
492
  const parsed = staticSceneSchema.safeParse(input);
365
493
  if (!parsed.success) {
366
494
  return {
@@ -387,6 +515,11 @@ function normalizeStaticScene(scene) {
387
515
  const { ok: _ok, ...response } = overlayLatitudeFailure;
388
516
  throw new StaticMapRequestError(response);
389
517
  }
518
+ const labelGlyphFailure = findStaticSymbolLabelGlyphFailure(scene);
519
+ if (labelGlyphFailure) {
520
+ const { ok: _ok, ...response } = labelGlyphFailure;
521
+ throw new StaticMapRequestError(response);
522
+ }
390
523
  const normalized = normalizeParsedStaticScene(staticSceneSchema.parse(scene));
391
524
  if (normalized.camera.type === "auto") {
392
525
  const analysis = analyzeStaticAutoFit(normalized);
@@ -398,6 +531,12 @@ function normalizeStaticScene(scene) {
398
531
  }
399
532
  function normalizeParsedStaticScene(parsed) {
400
533
  return stripUndefined({
534
+ ...parsed.attribution && Object.keys(parsed.attribution).length > 0 ? {
535
+ attribution: parsed.attribution.mode === "external" ? { mode: "external" } : stripUndefined({
536
+ mode: parsed.attribution.mode,
537
+ position: parsed.attribution.position
538
+ })
539
+ } : {},
401
540
  camera: parsed.camera.type === "center" ? {
402
541
  bearing: roundNumber(parsed.camera.bearing ?? 0),
403
542
  center: normalizeStaticCoordinate(parsed.camera.center),
@@ -414,6 +553,7 @@ function normalizeParsedStaticScene(parsed) {
414
553
  padding: normalizeAutoPadding(parsed.camera.padding, parsed.size),
415
554
  type: "auto"
416
555
  },
556
+ ...parsed.format && parsed.format !== "png" ? { format: parsed.format } : {},
417
557
  map: parsed.map,
418
558
  overlays: parsed.overlays.map(
419
559
  (overlay, index) => normalizeStaticOverlay({
@@ -446,6 +586,8 @@ function normalizeAutoPadding(padding, size) {
446
586
  }
447
587
 
448
588
  export {
589
+ staticMapErrorResponseSchema,
590
+ StaticMapError,
449
591
  staticMapRequestErrorResponseSchema,
450
592
  StaticMapRequestError,
451
593
  staticAutoFitPlanSchema,