@tileflow/static 0.1.0-alpha.20 → 0.1.0-alpha.22

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.
@@ -1,15 +1,19 @@
1
1
  import {
2
+ StaticMapRequestError,
3
+ staticMapRequestErrorResponseSchema,
2
4
  validateStaticScene
3
- } from "./chunk-ROBEGDDY.js";
5
+ } from "./chunk-EMCKSFZV.js";
4
6
  import {
5
7
  isSafeHttpUrl,
8
+ jsonByteLength,
6
9
  stableStringify
7
- } from "./chunk-YUG3PBK2.js";
10
+ } from "./chunk-NM2JSFUF.js";
8
11
 
9
12
  // src/client.ts
10
13
  import { z } from "zod";
11
14
  var maxStaticMapResponseBytes = 64 * 1024;
12
15
  var staticMapIdempotencyKeyPattern = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$/;
16
+ var STATIC_MAP_RESULT_V2_MEDIA_TYPE = "application/vnd.tileflow.static-map-result+json;version=2";
13
17
  var preparedStaticMapRequestBodies = /* @__PURE__ */ new WeakMap();
14
18
  var staticMapReadyResultSchema = z.object({
15
19
  cached: z.boolean(),
@@ -20,6 +24,39 @@ var staticMapReadyResultSchema = z.object({
20
24
  status: z.literal("ready"),
21
25
  unitCost: z.literal(15)
22
26
  }).strict();
27
+ var safeAttributionTextSchema = z.string().min(1).max(16384).refine(hasNoControlCharacters, { message: "Attribution text contains control characters" });
28
+ var staticMapAttributionEntrySchema = z.object({
29
+ authority: z.enum(["platform-notice", "team-declared"]),
30
+ links: z.array(
31
+ z.object({
32
+ label: safeAttributionTextSchema,
33
+ url: z.string().trim().url().max(2048).refine(isSafeHttpUrl, {
34
+ message: "Expected an http(s) URL without credentials or a fragment"
35
+ })
36
+ }).strict()
37
+ ).max(8),
38
+ text: safeAttributionTextSchema
39
+ }).strict();
40
+ var staticMapAttributionResultSchema = z.object({
41
+ entries: z.array(staticMapAttributionEntrySchema).max(18),
42
+ mode: z.enum(["embedded", "external"]),
43
+ position: z.enum(["top-left", "top-right", "bottom-left", "bottom-right"]).nullable()
44
+ }).strict().superRefine((result, context) => {
45
+ if (result.mode === "external" && result.position !== null || result.mode === "embedded" && result.position === null) {
46
+ context.addIssue({
47
+ code: "custom",
48
+ message: "Attribution position must match its mode",
49
+ path: ["position"]
50
+ });
51
+ }
52
+ if (jsonByteLength(result) > 32 * 1024) {
53
+ context.addIssue({ code: "custom", message: "Attribution result exceeds 32 KiB" });
54
+ }
55
+ });
56
+ var staticMapHostedResultSchema = staticMapReadyResultSchema.extend({
57
+ attribution: staticMapAttributionResultSchema,
58
+ resultVersion: z.literal(2)
59
+ }).strict();
23
60
  var staticMapProcessingResultSchema = z.object({
24
61
  operationId: z.string().min(20).max(68),
25
62
  retryAfterMs: z.number().int().min(0).max(5e3),
@@ -44,6 +81,10 @@ function createStaticMapIdempotencyKey() {
44
81
  function prepareStaticMapRequest(scene) {
45
82
  const validation = validateStaticScene(scene);
46
83
  if (!validation.ok) {
84
+ if (isStaticMapRequestFailure(validation)) {
85
+ const { ok: _ok, ...response } = validation;
86
+ throw new StaticMapRequestError(response);
87
+ }
47
88
  throw new Error(`Invalid Tileflow static scene: ${validation.error}`);
48
89
  }
49
90
  const prepared = Object.freeze({
@@ -86,6 +127,7 @@ async function requestStaticMapUntilReady(request, options) {
86
127
  const fetcher = options.fetch ?? fetch;
87
128
  const createUrl = normalizeStaticMapEndpointUrl(options.createUrl);
88
129
  const headers = {
130
+ Accept: STATIC_MAP_RESULT_V2_MEDIA_TYPE,
89
131
  "Content-Type": "application/json",
90
132
  "Idempotency-Key": idempotency.key
91
133
  };
@@ -103,14 +145,17 @@ async function requestStaticMapUntilReady(request, options) {
103
145
  });
104
146
  throwIfAborted(signal);
105
147
  if (!response.ok) {
106
- await discardBoundedResponse(response, 8 * 1024);
148
+ const requestError = response.status === 422 ? await readStaticMapRequestErrorResponse(response, 8 * 1024) : await discardBoundedResponse(response, 8 * 1024);
107
149
  throwIfAborted(signal);
150
+ if (requestError) {
151
+ throw new StaticMapRequestError(requestError, response.status);
152
+ }
108
153
  throw new Error(`Tileflow static map failed (${response.status}).`);
109
154
  }
110
155
  const json = await readJsonResponse(response);
111
156
  throwIfAborted(signal);
112
157
  if (response.status !== 202) {
113
- const parsed = staticMapReadyResultSchema.safeParse(json);
158
+ const parsed = staticMapHostedResultSchema.safeParse(json);
114
159
  if (!parsed.success) {
115
160
  throw new Error(
116
161
  `Tileflow static map returned an invalid response: ${parsed.error.message}`
@@ -131,6 +176,12 @@ async function requestStaticMapUntilReady(request, options) {
131
176
  }
132
177
  });
133
178
  }
179
+ function hasNoControlCharacters(value) {
180
+ return !Array.from(value).some((character) => {
181
+ const codePoint = character.codePointAt(0) ?? 0;
182
+ return codePoint <= 31 || codePoint === 127;
183
+ });
184
+ }
134
185
  function boundedClientDuration(value, fallback, minimum, maximum, name) {
135
186
  const resolved = value ?? fallback;
136
187
  if (!Number.isSafeInteger(resolved) || resolved < minimum || resolved > maximum) {
@@ -148,11 +199,26 @@ async function readJsonResponse(response) {
148
199
  throw new Error("Tileflow static map returned an invalid response: expected JSON");
149
200
  }
150
201
  }
202
+ async function readStaticMapRequestErrorResponse(response, maximumBytes) {
203
+ try {
204
+ const source = await readBoundedResponseText(response, maximumBytes, `${maximumBytes} bytes`);
205
+ const parsed = staticMapRequestErrorResponseSchema.safeParse(JSON.parse(source));
206
+ return parsed.success ? parsed.data : null;
207
+ } catch {
208
+ return null;
209
+ }
210
+ }
151
211
  async function discardBoundedResponse(response, maximumBytes) {
152
212
  try {
153
213
  await readBoundedResponseText(response, maximumBytes, `${maximumBytes} bytes`);
154
214
  } catch {
155
215
  }
216
+ return null;
217
+ }
218
+ function isStaticMapRequestFailure(value) {
219
+ if (!("code" in value)) return false;
220
+ const { ok: _ok, ...response } = value;
221
+ return staticMapRequestErrorResponseSchema.safeParse(response).success;
156
222
  }
157
223
  async function readBoundedResponseText(response, maximumBytes, displayLimit) {
158
224
  if (!response.body) return "";
@@ -293,7 +359,11 @@ function hasControlCharacter(value) {
293
359
  }
294
360
 
295
361
  export {
362
+ STATIC_MAP_RESULT_V2_MEDIA_TYPE,
296
363
  staticMapReadyResultSchema,
364
+ staticMapAttributionEntrySchema,
365
+ staticMapAttributionResultSchema,
366
+ staticMapHostedResultSchema,
297
367
  staticMapProcessingResultSchema,
298
368
  validateStaticMapIdempotencyKey,
299
369
  createStaticMapIdempotencyKey,
@@ -49,17 +49,24 @@ function base64Url(bytes) {
49
49
  // src/scene-contract.ts
50
50
  import { z } from "zod";
51
51
  var staticSceneSchemaVersion = 1;
52
+ var MAX_OVERLAY_LATITUDE = 85.051129;
52
53
  var staticSceneLimits = {
53
- maxDimension: 1280,
54
+ maxDimension: 2048,
54
55
  maxGeoJsonBytes: 96e3,
55
56
  maxOverlays: 24,
56
57
  maxPathCoordinates: 2e3,
57
- maxPhysicalPixels: 1280 * 1280,
58
+ maxPhysicalPixels: 2048 * 2048 * 4,
58
59
  minDimension: 64
59
60
  };
60
61
  var coordinateSchema = z.tuple([z.number().finite(), z.number().finite()]).refine(([lng, lat]) => lng >= -180 && lng <= 180 && lat >= -90 && lat <= 90, {
61
62
  message: "Expected [lng, lat] within world bounds"
62
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
+ );
63
70
  var colorSchema = z.string().regex(/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/, {
64
71
  message: "Expected a hex color like #C6A15B"
65
72
  });
@@ -72,12 +79,32 @@ var portableIdSchema = z.string().min(1).max(64).regex(/^[a-z][a-z0-9-]{0,63}$/u
72
79
  var concreteThemeSchema = portableIdSchema.refine((value) => value !== "system", {
73
80
  message: 'Static maps require a concrete theme; "system" is browser-only'
74
81
  });
82
+ var staticMapFormatSchema = z.enum(["png", "jpeg", "webp"]);
83
+ var staticAttributionPositionSchema = z.enum([
84
+ "auto",
85
+ "top-left",
86
+ "top-right",
87
+ "bottom-left",
88
+ "bottom-right"
89
+ ]);
90
+ var staticAttributionRequestSchema = z.union([
91
+ z.object({
92
+ mode: z.literal("embedded").optional(),
93
+ position: staticAttributionPositionSchema.optional()
94
+ }).strict(),
95
+ z.object({
96
+ mode: z.literal("external")
97
+ }).strict()
98
+ ]);
75
99
  var sizeSchema = z.object({
76
- dpr: z.literal(1).optional(),
100
+ dpr: z.union([z.literal(1), z.literal(2)]).optional(),
77
101
  height: z.number().int().min(staticSceneLimits.minDimension).max(staticSceneLimits.maxDimension),
78
102
  width: z.number().int().min(staticSceneLimits.minDimension).max(staticSceneLimits.maxDimension)
79
103
  }).refine(
80
- (size) => size.width * size.height * (size.dpr ?? 1) <= staticSceneLimits.maxPhysicalPixels,
104
+ (size) => {
105
+ const dpr = size.dpr ?? 1;
106
+ return size.width * size.height * dpr * dpr <= staticSceneLimits.maxPhysicalPixels;
107
+ },
81
108
  {
82
109
  message: "Static map exceeds the maximum render pixel budget"
83
110
  }
@@ -99,9 +126,30 @@ var boundsCameraSchema = z.object({
99
126
  }).refine((camera) => camera.bounds[3] > camera.bounds[1], {
100
127
  message: "Bounds north must be greater than south"
101
128
  });
129
+ var staticPaddingSchema = z.object({
130
+ bottom: z.number().int().nonnegative(),
131
+ left: z.number().int().nonnegative(),
132
+ right: z.number().int().nonnegative(),
133
+ top: z.number().int().nonnegative()
134
+ }).strict();
135
+ var autoPaddingInputSchema = z.union([
136
+ z.number().int().nonnegative(),
137
+ z.object({
138
+ bottom: z.number().int().nonnegative().optional(),
139
+ left: z.number().int().nonnegative().optional(),
140
+ right: z.number().int().nonnegative().optional(),
141
+ top: z.number().int().nonnegative().optional()
142
+ }).strict()
143
+ ]);
144
+ var autoCameraSchema = z.object({
145
+ bearing: z.number().finite().min(-180).max(180).optional(),
146
+ maxZoom: z.number().finite().min(0).max(22).optional(),
147
+ padding: autoPaddingInputSchema.optional(),
148
+ type: z.literal("auto")
149
+ });
102
150
  var lineOverlaySchema = z.object({
103
151
  color: colorSchema.default("#C6A15B"),
104
- coordinates: z.array(coordinateSchema).min(2).max(staticSceneLimits.maxPathCoordinates),
152
+ coordinates: z.array(overlayCoordinateSchema).min(2).max(staticSceneLimits.maxPathCoordinates),
105
153
  id: z.string().trim().min(1).max(64).optional(),
106
154
  opacity: z.number().finite().min(0).max(1).default(1),
107
155
  type: z.literal("line"),
@@ -109,7 +157,7 @@ var lineOverlaySchema = z.object({
109
157
  });
110
158
  var circleOverlaySchema = z.object({
111
159
  color: colorSchema.default("#C6A15B"),
112
- coordinate: coordinateSchema,
160
+ coordinate: overlayCoordinateSchema,
113
161
  id: z.string().trim().min(1).max(64).optional(),
114
162
  opacity: z.number().finite().min(0).max(1).default(1),
115
163
  radius: z.number().finite().min(1).max(64).default(6),
@@ -119,14 +167,14 @@ var circleOverlaySchema = z.object({
119
167
  });
120
168
  var markerOverlaySchema = z.object({
121
169
  color: colorSchema.default("#C6A15B"),
122
- coordinate: coordinateSchema,
170
+ coordinate: overlayCoordinateSchema,
123
171
  id: z.string().trim().min(1).max(64).optional(),
124
172
  radius: z.number().finite().min(2).max(64).default(8),
125
173
  strokeColor: colorSchema.default("#ffffff"),
126
174
  strokeWidth: z.number().finite().min(0).max(16).default(2),
127
175
  type: z.literal("marker")
128
176
  });
129
- var polygonRingSchema = z.array(coordinateSchema).min(4).max(staticSceneLimits.maxPathCoordinates).refine(
177
+ var polygonRingSchema = z.array(overlayCoordinateSchema).min(4).max(staticSceneLimits.maxPathCoordinates).refine(
130
178
  (ring) => {
131
179
  const first = ring[0];
132
180
  const last = ring[ring.length - 1];
@@ -150,7 +198,9 @@ var staticOverlaySchema = z.discriminatedUnion("type", [
150
198
  polygonOverlaySchema
151
199
  ]);
152
200
  var staticSceneSchema = z.object({
153
- camera: z.discriminatedUnion("type", [centerCameraSchema, boundsCameraSchema]),
201
+ attribution: staticAttributionRequestSchema.optional(),
202
+ camera: z.discriminatedUnion("type", [centerCameraSchema, boundsCameraSchema, autoCameraSchema]),
203
+ format: staticMapFormatSchema.optional(),
154
204
  map: portableIdSchema,
155
205
  overlays: z.array(staticOverlaySchema).max(staticSceneLimits.maxOverlays).default([]),
156
206
  size: sizeSchema,
@@ -190,7 +240,11 @@ export {
190
240
  hashStableValue,
191
241
  isSafeHttpUrl,
192
242
  staticSceneSchemaVersion,
243
+ MAX_OVERLAY_LATITUDE,
193
244
  staticSceneLimits,
245
+ staticAttributionPositionSchema,
246
+ staticAttributionRequestSchema,
247
+ staticPaddingSchema,
194
248
  staticOverlaySchema,
195
249
  staticSceneSchema,
196
250
  normalizeStaticOverlay,
@@ -0,0 +1,287 @@
1
+ import {
2
+ analyzeStaticAutoFit,
3
+ normalizeStaticScene,
4
+ staticAutoFitPlanSchema,
5
+ validateStaticScene
6
+ } from "./chunk-EMCKSFZV.js";
7
+ import {
8
+ hashStableValue,
9
+ isSafeHttpUrl,
10
+ jsonByteLength,
11
+ stableStringify,
12
+ staticSceneSchema,
13
+ stripUndefined
14
+ } from "./chunk-NM2JSFUF.js";
15
+
16
+ // src/manifest.ts
17
+ import { z } from "zod";
18
+ var staticRendererSchemaVersionV1 = 1;
19
+ var staticRendererSchemaVersion = 2;
20
+ var staticAttributionPlanSchemaVersion = 1;
21
+ var staticAttributionLimits = {
22
+ maxBytes: 32 * 1024,
23
+ maxEntries: 18,
24
+ maxLinksPerEntry: 8,
25
+ maxTextLength: 16384,
26
+ maxUrlLength: 2048
27
+ };
28
+ var sha256HexSchema = z.string().regex(/^[0-9a-f]{64}$/u);
29
+ var sourceIdSchema = z.string().min(1).max(128);
30
+ var boundedIdentitySchema = z.string().min(1).max(512).refine(hasNoControlCharacters, {
31
+ message: "Expected no control characters"
32
+ });
33
+ var staticResolvedAttributionPositionSchema = z.enum([
34
+ "top-left",
35
+ "top-right",
36
+ "bottom-left",
37
+ "bottom-right"
38
+ ]);
39
+ var staticAttributionSegmentSchema = z.discriminatedUnion("kind", [
40
+ z.object({
41
+ kind: z.literal("text"),
42
+ text: z.string().min(1).max(staticAttributionLimits.maxTextLength).refine(hasNoControlCharacters, { message: "Attribution text contains control characters" })
43
+ }).strict(),
44
+ z.object({
45
+ kind: z.literal("link"),
46
+ label: z.string().min(1).max(staticAttributionLimits.maxTextLength).refine(hasNoControlCharacters, {
47
+ message: "Attribution link label contains control characters"
48
+ }),
49
+ url: z.string().trim().url().max(staticAttributionLimits.maxUrlLength).refine(isSafeHttpUrl, {
50
+ message: "Expected an http(s) URL without credentials or a fragment"
51
+ })
52
+ }).strict()
53
+ ]);
54
+ var attributionSourceSchema = z.object({
55
+ sourceId: sourceIdSchema,
56
+ sourceSelectionIdentity: boundedIdentitySchema
57
+ }).strict();
58
+ var attributionSourcesSchema = z.array(attributionSourceSchema).min(1).max(18).refine(isUniqueSortedSources, {
59
+ message: "Attribution sources must be unique and sorted"
60
+ });
61
+ var staticAttributionProvenanceSchema = z.discriminatedUnion("authority", [
62
+ z.object({
63
+ authority: z.literal("platform-notice"),
64
+ noticeId: boundedIdentitySchema,
65
+ noticeSha256: sha256HexSchema,
66
+ resource: z.enum(["terrain", "world"]),
67
+ sources: attributionSourcesSchema
68
+ }).strict(),
69
+ z.object({
70
+ authority: z.literal("team-declared"),
71
+ sources: z.array(
72
+ attributionSourceSchema.extend({
73
+ mapRevision: boundedIdentitySchema,
74
+ resourceId: boundedIdentitySchema
75
+ }).strict()
76
+ ).min(1).max(18).refine(isUniqueSortedSources, {
77
+ message: "Attribution sources must be unique and sorted"
78
+ })
79
+ }).strict()
80
+ ]);
81
+ var staticAttributionEntrySchema = z.object({
82
+ authority: z.enum(["platform-notice", "team-declared"]),
83
+ provenance: staticAttributionProvenanceSchema,
84
+ segments: z.array(staticAttributionSegmentSchema).min(1).max(17)
85
+ }).strict().superRefine((entry, context) => {
86
+ if (entry.authority !== entry.provenance.authority) {
87
+ context.addIssue({
88
+ code: "custom",
89
+ message: "Attribution entry authority must match its provenance",
90
+ path: ["authority"]
91
+ });
92
+ }
93
+ const links = entry.segments.filter((segment) => segment.kind === "link").length;
94
+ if (links > staticAttributionLimits.maxLinksPerEntry) {
95
+ context.addIssue({
96
+ code: "custom",
97
+ message: `Attribution entries can contain at most ${staticAttributionLimits.maxLinksPerEntry} links`,
98
+ path: ["segments"]
99
+ });
100
+ }
101
+ });
102
+ var staticAttributionPlanSchemaV1 = z.object({
103
+ entries: z.array(staticAttributionEntrySchema).max(staticAttributionLimits.maxEntries),
104
+ mode: z.enum(["embedded", "external"]),
105
+ position: z.union([z.literal("auto"), staticResolvedAttributionPositionSchema, z.null()]),
106
+ schemaVersion: z.literal(staticAttributionPlanSchemaVersion)
107
+ }).strict().superRefine((plan, context) => {
108
+ if (plan.mode === "external" && plan.position !== null || plan.mode === "embedded" && plan.position === null) {
109
+ context.addIssue({
110
+ code: "custom",
111
+ message: "Attribution position must match its mode",
112
+ path: ["position"]
113
+ });
114
+ }
115
+ if (jsonByteLength(plan) > staticAttributionLimits.maxBytes) {
116
+ context.addIssue({
117
+ code: "custom",
118
+ message: `Attribution plan exceeds ${staticAttributionLimits.maxBytes} bytes`
119
+ });
120
+ }
121
+ });
122
+ var staticRenderManifestBaseShape = {
123
+ autoFit: staticAutoFitPlanSchema.optional(),
124
+ mapId: z.string().trim().min(1).max(128),
125
+ rendererVersion: z.string().trim().min(1).max(64),
126
+ styleId: z.string().trim().min(1).max(128).optional(),
127
+ styleRevision: z.string().trim().min(1).max(128),
128
+ styleUrl: z.string().trim().url().max(512).refine(isSafeHttpUrl, {
129
+ message: "Expected an http(s) style URL without credentials or a fragment"
130
+ })
131
+ };
132
+ var staticSceneSchemaV1 = staticSceneSchema.omit({ attribution: true });
133
+ var staticRenderManifestV1Schema = z.object({
134
+ ...staticRenderManifestBaseShape,
135
+ schemaVersion: z.literal(staticRendererSchemaVersionV1),
136
+ scene: staticSceneSchemaV1
137
+ });
138
+ var staticRenderManifestV2Schema = z.object({
139
+ ...staticRenderManifestBaseShape,
140
+ attribution: staticAttributionPlanSchemaV1,
141
+ schemaVersion: z.literal(staticRendererSchemaVersion),
142
+ scene: staticSceneSchema
143
+ }).strict();
144
+ var staticRenderManifestSchema = z.discriminatedUnion("schemaVersion", [
145
+ staticRenderManifestV1Schema,
146
+ staticRenderManifestV2Schema
147
+ ]);
148
+ function validateStaticRenderManifest(input) {
149
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
150
+ return { error: "manifest: Expected an object", ok: false };
151
+ }
152
+ const schemaVersion = input.schemaVersion;
153
+ if (schemaVersion !== staticRendererSchemaVersionV1 && schemaVersion !== staticRendererSchemaVersion) {
154
+ return { error: "manifest.schemaVersion: Unsupported renderer schema version", ok: false };
155
+ }
156
+ const rawScene = input.scene;
157
+ const sceneValidation = validateVersionedScene(rawScene, schemaVersion);
158
+ if (!sceneValidation.ok) return sceneValidation;
159
+ const schema = schemaVersion === staticRendererSchemaVersionV1 ? staticRenderManifestV1Schema : staticRenderManifestV2Schema;
160
+ const parsed = schema.safeParse({
161
+ ...input,
162
+ scene: sceneValidation.scene
163
+ });
164
+ if (!parsed.success) return zodFailure(parsed.error);
165
+ const autoFit = expectedAutoFit(sceneValidation.scene);
166
+ if (autoFit === void 0 !== (parsed.data.autoFit === void 0) || autoFit !== void 0 && stableStringify(autoFit) !== stableStringify(parsed.data.autoFit)) {
167
+ return { error: "manifest.autoFit: Expected the exact plan derived from scene", ok: false };
168
+ }
169
+ if (schemaVersion === staticRendererSchemaVersion && (!("attribution" in parsed.data) || !attributionMatchesScene(parsed.data.attribution, sceneValidation.scene))) {
170
+ return {
171
+ error: "manifest.attribution: Expected the exact request resolved from scene",
172
+ ok: false
173
+ };
174
+ }
175
+ return {
176
+ manifest: {
177
+ ...parsed.data,
178
+ ...autoFit === void 0 ? {} : { autoFit },
179
+ scene: sceneValidation.scene
180
+ },
181
+ ok: true
182
+ };
183
+ }
184
+ function createRenderManifestV1(input) {
185
+ const rawScene = staticSceneSchemaV1.parse(input.scene);
186
+ const scene = normalizeStaticScene(rawScene);
187
+ const autoFit = expectedAutoFit(scene);
188
+ return staticRenderManifestV1Schema.parse(
189
+ stripUndefined({
190
+ autoFit,
191
+ mapId: input.mapId,
192
+ rendererVersion: input.rendererVersion,
193
+ schemaVersion: staticRendererSchemaVersionV1,
194
+ scene,
195
+ styleId: input.styleId,
196
+ styleRevision: input.styleRevision,
197
+ styleUrl: input.styleUrl
198
+ })
199
+ );
200
+ }
201
+ var createRenderManifest = createRenderManifestV1;
202
+ function createRenderManifestV2(input) {
203
+ const scene = normalizeStaticScene(input.scene);
204
+ const autoFit = expectedAutoFit(scene);
205
+ return staticRenderManifestV2Schema.parse(
206
+ stripUndefined({
207
+ attribution: input.attribution,
208
+ autoFit,
209
+ mapId: input.mapId,
210
+ rendererVersion: input.rendererVersion,
211
+ schemaVersion: staticRendererSchemaVersion,
212
+ scene,
213
+ styleId: input.styleId,
214
+ styleRevision: input.styleRevision,
215
+ styleUrl: input.styleUrl
216
+ })
217
+ );
218
+ }
219
+ async function hashRenderManifest(manifest) {
220
+ const validation = validateStaticRenderManifest(manifest);
221
+ if (!validation.ok) throw new Error(`Invalid Tileflow render manifest: ${validation.error}`);
222
+ return hashStableValue(validation.manifest);
223
+ }
224
+ async function hashStaticSceneRequest(scene) {
225
+ const validation = validateStaticScene(scene);
226
+ if (!validation.ok) throw new Error(`Invalid Tileflow static scene: ${validation.error}`);
227
+ return hashStableValue(validation.scene);
228
+ }
229
+ function validateVersionedScene(input, version) {
230
+ if (version === 1 && input && typeof input === "object" && !Array.isArray(input)) {
231
+ const { attribution: _attribution, ...legacy } = input;
232
+ return validateStaticScene(legacy);
233
+ }
234
+ return validateStaticScene(input);
235
+ }
236
+ function attributionMatchesScene(plan, scene) {
237
+ const request = scene.attribution;
238
+ const expectedMode = request?.mode === "external" ? "external" : "embedded";
239
+ const expectedPosition = expectedMode === "external" ? null : request && "position" in request ? request.position ?? "auto" : "auto";
240
+ return plan.mode === expectedMode && plan.position === expectedPosition;
241
+ }
242
+ function expectedAutoFit(scene) {
243
+ if (scene.camera.type !== "auto") return void 0;
244
+ const analysis = analyzeStaticAutoFit(scene);
245
+ if (!analysis.ok) throw new Error(`Invalid Tileflow auto-fit scene: ${analysis.error}`);
246
+ return analysis.plan;
247
+ }
248
+ function hasNoControlCharacters(value) {
249
+ return !Array.from(value).some((character) => {
250
+ const codePoint = character.codePointAt(0) ?? 0;
251
+ return codePoint <= 31 || codePoint === 127;
252
+ });
253
+ }
254
+ function isUniqueSortedSources(values) {
255
+ return values.every(
256
+ (value, index) => index === 0 || compareCodeUnits(values[index - 1].sourceId, value.sourceId) < 0
257
+ );
258
+ }
259
+ function compareCodeUnits(left, right) {
260
+ return left < right ? -1 : left > right ? 1 : 0;
261
+ }
262
+ function zodFailure(error) {
263
+ return {
264
+ error: error.issues.map((issue) => `${issue.path.join(".") || "manifest"}: ${issue.message}`).join("; "),
265
+ ok: false
266
+ };
267
+ }
268
+
269
+ export {
270
+ staticRendererSchemaVersionV1,
271
+ staticRendererSchemaVersion,
272
+ staticAttributionPlanSchemaVersion,
273
+ staticAttributionLimits,
274
+ staticAttributionSegmentSchema,
275
+ staticAttributionProvenanceSchema,
276
+ staticAttributionEntrySchema,
277
+ staticAttributionPlanSchemaV1,
278
+ staticRenderManifestV1Schema,
279
+ staticRenderManifestV2Schema,
280
+ staticRenderManifestSchema,
281
+ validateStaticRenderManifest,
282
+ createRenderManifestV1,
283
+ createRenderManifest,
284
+ createRenderManifestV2,
285
+ hashRenderManifest,
286
+ hashStaticSceneRequest
287
+ };