@loaders.gl/tile-converter 5.0.0-alpha.11 → 5.0.0-alpha.12

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.
Files changed (83) hide show
  1. package/README.md +178 -2
  2. package/bin/converter-v5.js +2 -0
  3. package/dist/converter.min.cjs +162 -163
  4. package/dist/i3s-server/bin/i3s-server.min.cjs +73 -73
  5. package/dist/index.cjs +1749 -2152
  6. package/dist/index.js +1748 -2162
  7. package/dist/v5/adapters.cjs +1550 -128
  8. package/dist/v5/adapters.d.ts +16 -0
  9. package/dist/v5/adapters.js +1558 -117
  10. package/dist/v5/archive-source.d.ts +36 -0
  11. package/dist/v5/browser-tileset-source.d.ts +55 -11
  12. package/dist/v5/browser.cjs +18 -0
  13. package/dist/v5/browser.d.ts +14 -0
  14. package/dist/v5/browser.js +26 -0
  15. package/dist/v5/conversion-api.d.ts +9 -0
  16. package/dist/v5/converter-cli.d.ts +1 -0
  17. package/dist/v5/converter-cli.js +1214 -0
  18. package/dist/v5/core.cjs +471 -60
  19. package/dist/v5/core.d.ts +2 -0
  20. package/dist/v5/core.js +471 -60
  21. package/dist/v5/i3s-mesh-conversion.d.ts +4 -0
  22. package/dist/v5/i3s-selection-source.d.ts +70 -0
  23. package/dist/v5/index.cjs +10 -0
  24. package/dist/v5/index.d.ts +8 -0
  25. package/dist/v5/index.js +14 -0
  26. package/dist/v5/mesh-conversion.d.ts +18 -0
  27. package/dist/v5/mesh-hierarchy.d.ts +47 -0
  28. package/dist/v5/mesh-source-features.d.ts +2 -1
  29. package/dist/v5/mesh-source-schema.d.ts +12 -0
  30. package/dist/v5/mesh-source.d.ts +5 -2
  31. package/dist/v5/mesh-tileset-sink.d.ts +5 -0
  32. package/dist/v5/node-cli.d.ts +9 -0
  33. package/dist/v5/node-conversion.d.ts +32 -0
  34. package/dist/v5/node-feature-schema.d.ts +3 -0
  35. package/dist/v5/node-i3s-source.d.ts +41 -0
  36. package/dist/v5/node-source.d.ts +47 -0
  37. package/dist/v5/node.cjs +900 -0
  38. package/dist/v5/node.d.ts +6 -0
  39. package/dist/v5/node.js +877 -0
  40. package/dist/v5/point-cloud-file.d.ts +83 -0
  41. package/dist/v5/point-cloud-potree.d.ts +47 -0
  42. package/dist/v5/point-cloud-tileset.d.ts +44 -0
  43. package/dist/v5/resource-manifest.d.ts +10 -1
  44. package/dist/v5/selected-mesh-source.d.ts +53 -0
  45. package/package.json +33 -25
  46. package/src/index.ts +4 -1
  47. package/src/v4/3d-tiles-converter/helpers/load-i3s.ts +2 -4
  48. package/src/v4/converter-cli.ts +8 -2
  49. package/src/v4/i3s-converter/helpers/load-3d-tiles.ts +3 -51
  50. package/src/v4/i3s-converter/helpers/node-index-document.ts +5 -2
  51. package/src/v4/i3s-converter/helpers/preprocess-3d-tiles.ts +1 -1
  52. package/src/v4/i3s-converter/json-templates/store.ts +2 -1
  53. package/src/v5/adapters.ts +48 -0
  54. package/src/v5/archive-source.ts +235 -0
  55. package/src/v5/browser-tileset-source.ts +418 -48
  56. package/src/v5/browser.ts +45 -0
  57. package/src/v5/conversion-api.ts +69 -9
  58. package/src/v5/converter-cli.ts +28 -0
  59. package/src/v5/core.ts +6 -0
  60. package/src/v5/i3s-mesh-conversion.ts +40 -2
  61. package/src/v5/i3s-mesh-sink.ts +1 -1
  62. package/src/v5/i3s-mesh-source.ts +1 -0
  63. package/src/v5/i3s-selection-source.ts +298 -0
  64. package/src/v5/index.ts +26 -0
  65. package/src/v5/mesh-conversion.ts +20 -0
  66. package/src/v5/mesh-hierarchy.ts +648 -0
  67. package/src/v5/mesh-source-features.ts +39 -7
  68. package/src/v5/mesh-source-schema.ts +134 -0
  69. package/src/v5/mesh-source.ts +16 -4
  70. package/src/v5/mesh-tileset-sink.ts +1 -1
  71. package/src/v5/node-cli.ts +197 -0
  72. package/src/v5/node-conversion.ts +345 -0
  73. package/src/v5/node-feature-schema.ts +155 -0
  74. package/src/v5/node-i3s-source.ts +181 -0
  75. package/src/v5/node-source.ts +328 -0
  76. package/src/v5/node.ts +16 -0
  77. package/src/v5/point-cloud-file.ts +306 -0
  78. package/src/v5/point-cloud-potree.ts +130 -0
  79. package/src/v5/point-cloud-tileset.ts +137 -0
  80. package/src/v5/resource-fetcher.ts +4 -1
  81. package/src/v5/resource-manifest.ts +40 -5
  82. package/src/v5/selected-mesh-source.ts +384 -0
  83. package/src/v5/spatial-conversion.ts +88 -17
@@ -20,26 +20,40 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/v5/adapters.ts
21
21
  var adapters_exports = {};
22
22
  __export(adapters_exports, {
23
+ SELECTED_MESH_LIMITS: () => SELECTED_MESH_LIMITS,
23
24
  convertFeatureAttributesToArrowBatches: () => convertFeatureAttributesToArrowBatches,
24
25
  convertPointCloudSource: () => convertPointCloudSource,
26
+ convertPointCloudToPotree: () => convertPointCloudToPotree,
27
+ convertPointCloudToTileset: () => convertPointCloudToTileset,
25
28
  createI3SMeshConversionCodec: () => createI3SMeshConversionCodec,
26
29
  createI3SMeshSink: () => createI3SMeshSink,
27
30
  createI3SMeshTilesetConversionSource: () => createI3SMeshTilesetConversionSource,
28
31
  createMeshConversionCodec: () => createMeshConversionCodec,
32
+ createMeshHierarchyTilesetSink: () => createMeshHierarchyTilesetSink,
33
+ createMeshSourceFeatureOptions: () => createMeshSourceFeatureOptions,
29
34
  createMeshTilesetConversionSource: () => createMeshTilesetConversionSource,
30
35
  createMeshTilesetSink: () => createMeshTilesetSink,
31
36
  createPointCloudTilesetSink: () => createPointCloudTilesetSink,
37
+ createSelectedI3SMeshConversionSource: () => createSelectedI3SMeshConversionSource,
38
+ createSelectedMeshConversionSource: () => createSelectedMeshConversionSource,
32
39
  createSingleMeshI3SSink: () => createSingleMeshI3SSink,
33
40
  createSingleMeshTilesetArchive: () => createSingleMeshTilesetArchive,
34
41
  createSingleMeshTilesetSink: () => createSingleMeshTilesetSink,
35
42
  createTileConversionArchive: () => createTileConversionArchive,
43
+ createTileConversionArchiveTransport: () => createTileConversionArchiveTransport,
36
44
  createTileConversionResourceFetcher: () => createTileConversionResourceFetcher,
37
45
  encodeMeshTile: () => encodeMeshTile,
38
46
  encodePointCloudSource: () => encodePointCloudSource,
39
47
  encodePointCloudSourceTile: () => encodePointCloudSourceTile,
40
48
  encodePointCloudTile: () => encodePointCloudTile,
41
49
  encodePointCloudTileWithMetadata: () => encodePointCloudTileWithMetadata,
50
+ encodePotreeArchive: () => encodePotreeArchive,
42
51
  encodeTileConversionArchiveInBatches: () => encodeTileConversionArchiveInBatches,
52
+ extractMeshVertices: () => extractMeshVertices,
53
+ inspectI3SMeshConversionSource: () => inspectI3SMeshConversionSource,
54
+ measureMeshBytes: () => measureMeshBytes,
55
+ openPointCloudFile: () => openPointCloudFile,
56
+ openTileConversionArchive: () => openTileConversionArchive,
43
57
  transformPointCloudSourceTile: () => transformPointCloudSourceTile
44
58
  });
45
59
  module.exports = __toCommonJS(adapters_exports);
@@ -1676,6 +1690,8 @@ function createMeshConversionCodec(options) {
1676
1690
  }
1677
1691
  yield {
1678
1692
  id: resource.id,
1693
+ contentResourceId: resource.contentResourceId,
1694
+ contentPrimitive: resource.contentPrimitive,
1679
1695
  glb: encoded.glb,
1680
1696
  origin,
1681
1697
  boundingBox: [minimum, maximum],
@@ -1987,9 +2003,9 @@ async function createSingleMeshTilesetArchive(files, options) {
1987
2003
  }
1988
2004
 
1989
2005
  // src/v5/mesh-source.ts
1990
- var import_core18 = require("@math.gl/core");
1991
- var import_core19 = require("@loaders.gl/tile-converter/v5/core");
2006
+ var import_core19 = require("@math.gl/core");
1992
2007
  var import_core20 = require("@loaders.gl/tile-converter/v5/core");
2008
+ var import_core21 = require("@loaders.gl/tile-converter/v5/core");
1993
2009
 
1994
2010
  // src/v5/mesh-source-texture.ts
1995
2011
  var import_images2 = require("@loaders.gl/images");
@@ -2097,99 +2113,194 @@ async function resolveMeshSourceTexture(textureInfo, readExternalResource, conte
2097
2113
  return mapMeshSourceTexture(resolved);
2098
2114
  }
2099
2115
 
2100
- // src/v5/mesh-source-features.ts
2116
+ // src/v5/mesh-source-schema.ts
2101
2117
  var import_core17 = require("@loaders.gl/tile-converter/v5/core");
2118
+ var SCALAR_TYPES = {
2119
+ INT8: "int8",
2120
+ UINT8: "uint8",
2121
+ INT16: "int16",
2122
+ UINT16: "uint16",
2123
+ INT32: "int32",
2124
+ UINT32: "uint32",
2125
+ INT64: "int64",
2126
+ UINT64: "uint64",
2127
+ FLOAT32: "float32",
2128
+ FLOAT64: "float64"
2129
+ };
2130
+ function createMeshSourceFeatureOptions(gltf, options) {
2131
+ const metadata = gltf.extensions?.EXT_structural_metadata;
2132
+ const table = metadata?.propertyTables?.[0];
2133
+ const properties = metadata?.schema?.classes?.[options.metadataClass]?.properties;
2134
+ if (!options.metadataClass || metadata?.propertyTables?.length !== 1 || table?.class !== options.metadataClass || !properties || !Number.isSafeInteger(table.count) || table.count < 1 || table.count > 4294967295 || metadata.propertyTextures?.length || metadata.propertyAttributes?.length) {
2135
+ throw new import_core17.TileConversionError(
2136
+ "MESH_FEATURE_SCHEMA_REQUIRED",
2137
+ "Schema mapping requires one inline class, one matching property table and a positive uint32 row count"
2138
+ );
2139
+ }
2140
+ if (Object.keys(table.properties || {}).some((name) => !Object.hasOwn(properties, name))) {
2141
+ throw new import_core17.TileConversionError(
2142
+ "MESH_FEATURE_PROPERTY_UNMAPPED",
2143
+ "Property table columns must be declared in the selected class"
2144
+ );
2145
+ }
2146
+ const fields = Object.entries(properties).map(([name, property]) => {
2147
+ const declaration = property;
2148
+ const column = table.properties?.[name];
2149
+ if (declaration.array || !["SCALAR", "STRING"].includes(declaration.type) || declaration.normalized || declaration.offset !== void 0 || declaration.scale !== void 0 || column?.offset !== void 0 || column?.scale !== void 0 || declaration.required && (declaration.noData !== void 0 || declaration.default !== void 0)) {
2150
+ throw new import_core17.TileConversionError(
2151
+ "MESH_FEATURE_PROPERTY_UNSUPPORTED",
2152
+ `Property ${name} requires an explicit separately qualified mapping`
2153
+ );
2154
+ }
2155
+ const type = declaration.type === "STRING" ? "utf8" : Object.hasOwn(SCALAR_TYPES, declaration.componentType || "") ? SCALAR_TYPES[declaration.componentType] : void 0;
2156
+ if (!type) {
2157
+ throw new import_core17.TileConversionError(
2158
+ "MESH_FEATURE_PROPERTY_UNSUPPORTED",
2159
+ `Property ${name} has no supported declared scalar width`
2160
+ );
2161
+ }
2162
+ return {
2163
+ name,
2164
+ type,
2165
+ nullable: declaration.required !== true,
2166
+ metadata: { "loaders.gl:structural-property": JSON.stringify(declaration) }
2167
+ };
2168
+ });
2169
+ const identifierName = options.featureIdField || "feature_id";
2170
+ const sourceIdentifier = options.sourceFeatureIdProperty;
2171
+ const sourceField = sourceIdentifier ? fields.find((field) => field.name === sourceIdentifier) : void 0;
2172
+ if (sourceIdentifier && !sourceField || fields.some((field) => field.name === identifierName && field !== sourceField) || sourceField && !["utf8", "int8", "uint8", "int16", "uint16", "int32", "uint32", "int64", "uint64"].includes(
2173
+ String(sourceField.type)
2174
+ )) {
2175
+ throw new import_core17.TileConversionError(
2176
+ "MESH_FEATURE_ID_PROPERTY_REQUIRED",
2177
+ "Select a declared string/integer identifier and a distinct output identifier name"
2178
+ );
2179
+ }
2180
+ return {
2181
+ ...options,
2182
+ schema: {
2183
+ fields: [
2184
+ {
2185
+ ...sourceField,
2186
+ name: identifierName,
2187
+ type: sourceField?.type || "uint32",
2188
+ nullable: false
2189
+ },
2190
+ ...fields.filter((field) => field !== sourceField)
2191
+ ],
2192
+ metadata: { "loaders.gl:source-schema": JSON.stringify(metadata.schema) }
2193
+ }
2194
+ };
2195
+ }
2196
+
2197
+ // src/v5/mesh-source-features.ts
2198
+ var import_core18 = require("@loaders.gl/tile-converter/v5/core");
2102
2199
  function extractMeshFeatures(gltf, primitive, payload, mesh, options) {
2103
2200
  const declarations = primitive.extensions?.EXT_mesh_features?.featureIds;
2104
2201
  const metadata = gltf.extensions?.EXT_structural_metadata;
2105
2202
  const legacy = primitive.attributes._BATCHID;
2106
2203
  if (!declarations && !legacy && !metadata && !Object.keys(payload.batchTableJson || {}).length && !payload.header?.batchLength) {
2107
2204
  if (Object.keys(primitive.attributes).some((name) => /^_FEATURE_ID_\d+$/.test(name)))
2108
- throw new import_core17.TileConversionError(
2205
+ throw new import_core18.TileConversionError(
2109
2206
  "MESH_FEATURE_MAPPING_REQUIRED",
2110
2207
  "Feature attributes require an EXT_mesh_features declaration"
2111
2208
  );
2112
2209
  return void 0;
2113
2210
  }
2114
2211
  if (!options)
2115
- throw new import_core17.TileConversionError(
2212
+ throw new import_core18.TileConversionError(
2116
2213
  "MESH_FEATURE_SCHEMA_REQUIRED",
2117
- "Feature extraction requires an explicit Arrow schema and class mapping"
2214
+ "Feature extraction requires a class selection and a declared Arrow or inline source schema"
2118
2215
  );
2119
2216
  if (!options.metadataClass)
2120
- throw new import_core17.TileConversionError(
2217
+ throw new import_core18.TileConversionError(
2121
2218
  "MESH_FEATURE_CLASS_REQUIRED",
2122
2219
  "Feature metadata class must be explicit"
2123
2220
  );
2221
+ const mapping = "schema" in options ? options : createMeshSourceFeatureOptions(gltf, options);
2124
2222
  let count;
2125
2223
  let attribute = legacy;
2126
2224
  let properties;
2127
2225
  if (declarations) {
2128
2226
  if (legacy || declarations.length !== 1 || metadata?.propertyTables?.length !== 1 || metadata.propertyTextures?.length || metadata.propertyAttributes?.length)
2129
- throw new import_core17.TileConversionError(
2227
+ throw new import_core18.TileConversionError(
2130
2228
  "MESH_FEATURE_MAPPING_UNSUPPORTED",
2131
2229
  "Only one attribute-backed feature set and one property table are supported"
2132
2230
  );
2133
2231
  const declaration = declarations[0];
2134
2232
  if (!Number.isInteger(declaration.attribute) || declaration.attribute < 0 || declaration.propertyTable !== 0 || declaration.texture || declaration.implicit || declaration.nullFeatureId !== void 0)
2135
- throw new import_core17.TileConversionError(
2233
+ throw new import_core18.TileConversionError(
2136
2234
  "MESH_FEATURE_MAPPING_UNSUPPORTED",
2137
2235
  "Features require an attribute and property table 0 without null/texture/implicit IDs"
2138
2236
  );
2139
2237
  attribute = primitive.attributes[`_FEATURE_ID_${declaration.attribute}`];
2140
2238
  const table = metadata.propertyTables[0];
2141
2239
  if (table.class !== options.metadataClass || table.count !== declaration.featureCount)
2142
- throw new import_core17.TileConversionError(
2240
+ throw new import_core18.TileConversionError(
2143
2241
  "MESH_FEATURE_CLASS_MISMATCH",
2144
2242
  "Feature class and declared table counts must agree"
2145
2243
  );
2146
2244
  count = table.count;
2245
+ if (!Number.isSafeInteger(count) || count <= 0 || count > mesh.attributes.POSITION.value.length / 3) {
2246
+ throw new import_core18.TileConversionError(
2247
+ "MESH_FEATURE_IDS_INVALID",
2248
+ "Feature row count exceeds the available mesh vertices"
2249
+ );
2250
+ }
2147
2251
  properties = {};
2148
2252
  const classProperties = metadata.schema?.classes?.[table.class]?.properties;
2149
2253
  if (!classProperties)
2150
- throw new import_core17.TileConversionError(
2254
+ throw new import_core18.TileConversionError(
2151
2255
  "MESH_FEATURE_SCHEMA_REQUIRED",
2152
2256
  "A decoded inline structural metadata class is required"
2153
2257
  );
2154
2258
  if (Object.keys(table.properties || {}).some((name) => !Object.hasOwn(classProperties, name)))
2155
- throw new import_core17.TileConversionError(
2259
+ throw new import_core18.TileConversionError(
2156
2260
  "MESH_FEATURE_PROPERTY_UNMAPPED",
2157
2261
  "Property table columns must be declared in the metadata class"
2158
2262
  );
2159
2263
  for (const [name, property] of Object.entries(classProperties)) {
2160
2264
  const column = table.properties?.[name];
2161
2265
  if (!["SCALAR", "STRING"].includes(property.type) || property.array || property.noData !== void 0 && (property.normalized || property.offset !== void 0 || property.scale !== void 0 || column?.offset !== void 0 || column?.scale !== void 0) || ["INT64", "UINT64"].includes(property.componentType || "") && (property.normalized || property.offset !== void 0 || property.scale !== void 0 || column?.offset !== void 0 || column?.scale !== void 0))
2162
- throw new import_core17.TileConversionError(
2266
+ throw new import_core18.TileConversionError(
2163
2267
  "MESH_FEATURE_PROPERTY_UNSUPPORTED",
2164
2268
  `Property ${name} requires a separately qualified semantic mapping`
2165
2269
  );
2166
2270
  const data = column?.data;
2167
- if (data === void 0 && property.default === void 0)
2168
- throw new import_core17.TileConversionError(
2271
+ if (column !== void 0 && (data === void 0 || data === null) || data === void 0 && property.required === true)
2272
+ throw new import_core18.TileConversionError(
2169
2273
  "MESH_FEATURE_PROPERTY_UNAVAILABLE",
2170
2274
  `Property ${name} has not been decoded`
2171
2275
  );
2172
- const decoded = data ?? Array.from({ length: count }, () => property.default);
2276
+ const integer64 = ["INT64", "UINT64"].includes(property.componentType || "");
2277
+ if (integer64 && property.default !== void 0 && (typeof property.default !== "number" || !Number.isSafeInteger(property.default))) {
2278
+ throw new import_core18.TileConversionError(
2279
+ "MESH_FEATURE_PROPERTY_UNSUPPORTED",
2280
+ `Property ${name} requires an exact 64-bit default`
2281
+ );
2282
+ }
2283
+ const defaultValue = integer64 && property.default !== void 0 ? BigInt(property.default) : property.default ?? null;
2284
+ const decoded = data ?? Array.from({ length: count }, () => defaultValue);
2173
2285
  if (property.noData === void 0 || data === void 0) {
2174
2286
  properties[name] = decoded;
2175
2287
  continue;
2176
2288
  }
2177
2289
  const sentinel = property.noData;
2178
- const integer64 = ["INT64", "UINT64"].includes(property.componentType || "");
2179
2290
  if ((property.type === "STRING" ? typeof sentinel !== "string" : typeof sentinel !== "number" || !Number.isFinite(sentinel) || integer64 && !Number.isSafeInteger(sentinel)) || !decoded || !Array.isArray(decoded) && !ArrayBuffer.isView(decoded) || decoded.length !== count)
2180
- throw new import_core17.TileConversionError(
2291
+ throw new import_core18.TileConversionError(
2181
2292
  "MESH_FEATURE_PROPERTY_UNSUPPORTED",
2182
2293
  `Property ${name} requires a valid decoded noData mapping`
2183
2294
  );
2184
2295
  const comparable = integer64 ? BigInt(sentinel) : sentinel;
2185
2296
  properties[name] = Array.from(
2186
2297
  decoded,
2187
- (value) => value === comparable ? property.default ?? null : value
2298
+ (value) => value === comparable ? defaultValue : value
2188
2299
  );
2189
2300
  }
2190
2301
  } else {
2191
2302
  if (metadata)
2192
- throw new import_core17.TileConversionError(
2303
+ throw new import_core18.TileConversionError(
2193
2304
  "MESH_FEATURE_MAPPING_UNSUPPORTED",
2194
2305
  "Structural metadata requires an attribute-backed feature declaration"
2195
2306
  );
@@ -2200,7 +2311,7 @@ function extractMeshFeatures(gltf, primitive, payload, mesh, options) {
2200
2311
  if (!Number.isSafeInteger(count) || count <= 0 || !attribute || attribute.components !== 1 || attribute.normalized || attribute.value.length !== vertexCount || Array.from(attribute.value).some(
2201
2312
  (value) => !Number.isSafeInteger(value) || value < 0 || value >= count
2202
2313
  ))
2203
- throw new import_core17.TileConversionError(
2314
+ throw new import_core18.TileConversionError(
2204
2315
  "MESH_FEATURE_IDS_INVALID",
2205
2316
  "Feature IDs must be one valid property-table row index per vertex"
2206
2317
  );
@@ -2208,20 +2319,20 @@ function extractMeshFeatures(gltf, primitive, payload, mesh, options) {
2208
2319
  if (Object.keys(primitive.attributes).some(
2209
2320
  (name) => (name === "_BATCHID" || /^_FEATURE_ID_\d+$/.test(name)) && name !== selectedAttribute
2210
2321
  ))
2211
- throw new import_core17.TileConversionError(
2322
+ throw new import_core18.TileConversionError(
2212
2323
  "MESH_FEATURE_MAPPING_UNSUPPORTED",
2213
2324
  "Additional feature ID sets require separate mappings"
2214
2325
  );
2215
2326
  for (const [name, values] of Object.entries(properties)) {
2216
2327
  if (!values || !Array.isArray(values) && !ArrayBuffer.isView(values) || values.length !== count)
2217
- throw new import_core17.TileConversionError(
2328
+ throw new import_core18.TileConversionError(
2218
2329
  "MESH_FEATURE_PROPERTY_UNAVAILABLE",
2219
2330
  `Property ${name} must be a decoded column matching the table count`
2220
2331
  );
2221
2332
  }
2222
2333
  const sourceIdentifier = options.sourceFeatureIdProperty;
2223
2334
  if (sourceIdentifier && !properties[sourceIdentifier])
2224
- throw new import_core17.TileConversionError(
2335
+ throw new import_core18.TileConversionError(
2225
2336
  "MESH_FEATURE_ID_PROPERTY_REQUIRED",
2226
2337
  "Stable source identifier property is missing"
2227
2338
  );
@@ -2233,7 +2344,7 @@ function extractMeshFeatures(gltf, primitive, payload, mesh, options) {
2233
2344
  Object.entries(properties).filter(([name]) => name !== sourceIdentifier).map(([name, values]) => [name, values[index]])
2234
2345
  )
2235
2346
  })),
2236
- options
2347
+ mapping
2237
2348
  );
2238
2349
  const indices = mesh.indices?.value;
2239
2350
  const triangleFeatureIndices = new Uint32Array((indices?.length ?? vertexCount) / 3);
@@ -2243,7 +2354,7 @@ function extractMeshFeatures(gltf, primitive, payload, mesh, options) {
2243
2354
  );
2244
2355
  const rows = corners.map((corner) => attribute.value[corner]);
2245
2356
  if (rows.some((row) => row !== rows[0]) && new Set(corners).size === 3)
2246
- throw new import_core17.TileConversionError(
2357
+ throw new import_core18.TileConversionError(
2247
2358
  "MESH_FEATURE_TRIANGLE_MIXED",
2248
2359
  "All vertices of a nondegenerate triangle must reference the same feature row"
2249
2360
  );
@@ -2259,20 +2370,20 @@ function extractMeshFeatures(gltf, primitive, payload, mesh, options) {
2259
2370
 
2260
2371
  // src/v5/mesh-source.ts
2261
2372
  function createMeshTilesetConversionSource(tileset, options = {}) {
2262
- const source = (0, import_core20.createTilesetConversionSource)(tileset, options);
2373
+ const source = (0, import_core21.createTilesetConversionSource)(tileset, options);
2263
2374
  return {
2264
2375
  /** Validates the source frame and supported dataset metadata before traversal. */
2265
2376
  async inspect(signal) {
2266
2377
  const metadata = await source.inspect(signal);
2267
2378
  const reference = metadata.spatialReference;
2268
2379
  if (metadata.type !== "TILES3D" || !reference || reference.status !== "native" || reference.sourceCrs !== "EPSG:4978" || reference.heightReference !== "ellipsoidal" || reference.axisOrder !== "xyz" || reference.verticalUnitScale !== 1 || reference.units?.some((unit) => unit !== "meter")) {
2269
- throw new import_core19.TileConversionError(
2380
+ throw new import_core20.TileConversionError(
2270
2381
  "MESH_SOURCE_FRAME_UNSUPPORTED",
2271
2382
  "Mesh extraction requires native EPSG:4978 xyz coordinates in meters and ellipsoidal heights"
2272
2383
  );
2273
2384
  }
2274
2385
  if (metadata.tileset.metadata || metadata.tileset.groups?.length) {
2275
- throw new import_core19.TileConversionError(
2386
+ throw new import_core20.TileConversionError(
2276
2387
  "MESH_SOURCE_METADATA_UNSUPPORTED",
2277
2388
  "Tileset/group metadata requires a separate mapping"
2278
2389
  );
@@ -2284,25 +2395,25 @@ function createMeshTilesetConversionSource(tileset, options = {}) {
2284
2395
  for await (const item of source.read(inspection, signal)) {
2285
2396
  const placementReference = item.tile.header._spatialReference || tileset.spatialReference;
2286
2397
  if (placementReference && (placementReference.status !== "native" || placementReference.sourceCrs !== "EPSG:4978"))
2287
- throw new import_core19.TileConversionError(
2398
+ throw new import_core20.TileConversionError(
2288
2399
  "MESH_SOURCE_FRAME_UNSUPPORTED",
2289
2400
  "Nested mesh placements require native ECEF coordinates"
2290
2401
  );
2291
2402
  if (item.tile.header.metadata || item.tile.header.implicitMetadata)
2292
- throw new import_core19.TileConversionError(
2403
+ throw new import_core20.TileConversionError(
2293
2404
  "MESH_SOURCE_METADATA_UNSUPPORTED",
2294
2405
  "Tile metadata requires a separate mapping"
2295
2406
  );
2296
2407
  for (const content of item.contents) {
2297
2408
  signal?.throwIfAborted();
2298
2409
  if (content.metadata || content.group !== void 0)
2299
- throw new import_core19.TileConversionError(
2410
+ throw new import_core20.TileConversionError(
2300
2411
  "MESH_SOURCE_METADATA_UNSUPPORTED",
2301
2412
  "Content metadata requires a separate mapping"
2302
2413
  );
2303
2414
  const payload = content.payload;
2304
2415
  if (!payload?.gltf || !["glTF", "gltf", "b3dm"].includes(payload.type || content.type || "")) {
2305
- throw new import_core19.TileConversionError(
2416
+ throw new import_core20.TileConversionError(
2306
2417
  "MESH_SOURCE_CONTENT_UNSUPPORTED",
2307
2418
  "Mesh extraction requires decoded GLB/glTF or B3DM content"
2308
2419
  );
@@ -2311,36 +2422,40 @@ function createMeshTilesetConversionSource(tileset, options = {}) {
2311
2422
  if (gltf.animations?.length || gltf.skins?.length || Object.keys(gltf.extensions || {}).some(
2312
2423
  (name) => !["EXT_structural_metadata", "CESIUM_RTC"].includes(name)
2313
2424
  )) {
2314
- throw new import_core19.TileConversionError(
2425
+ throw new import_core20.TileConversionError(
2315
2426
  "MESH_SOURCE_SCENE_UNSUPPORTED",
2316
2427
  "Animated/skinned scenes and unmapped scene extensions are unsupported"
2317
2428
  );
2318
2429
  }
2319
2430
  const axis = payload.gltfUpAxis || "Y";
2320
2431
  if (!["X", "Y", "Z"].includes(axis))
2321
- throw new import_core19.TileConversionError("MESH_SOURCE_AXIS_UNSUPPORTED", "Unknown glTF up axis");
2322
- const placement = new import_core18.Matrix4(item.tile.computedTransform);
2432
+ throw new import_core20.TileConversionError("MESH_SOURCE_AXIS_UNSUPPORTED", "Unknown glTF up axis");
2433
+ const placement = new import_core19.Matrix4(item.tile.computedTransform);
2323
2434
  const rtc = payload.rtcCenter || gltf.extensions?.CESIUM_RTC?.center;
2324
2435
  if (rtc) {
2325
2436
  if (rtc.length !== 3 || rtc.some((value) => !Number.isFinite(value)))
2326
- throw new import_core19.TileConversionError(
2437
+ throw new import_core20.TileConversionError(
2327
2438
  "MESH_SOURCE_TRANSFORM_INVALID",
2328
2439
  "RTC center must contain finite xyz coordinates"
2329
2440
  );
2330
2441
  placement.translate(rtc);
2331
2442
  }
2332
2443
  if (axis === "Y")
2333
- placement.multiplyRight(new import_core18.Matrix4([1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1]));
2444
+ placement.multiplyRight(new import_core19.Matrix4([1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1]));
2334
2445
  if (axis === "X")
2335
- placement.multiplyRight(new import_core18.Matrix4([0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 1]));
2446
+ placement.multiplyRight(new import_core19.Matrix4([0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 1]));
2336
2447
  const roots = gltf.scene?.nodes || (gltf.scenes?.length === 1 ? gltf.scenes[0].nodes : void 0);
2337
2448
  if (!roots?.length)
2338
- throw new import_core19.TileConversionError(
2449
+ throw new import_core20.TileConversionError(
2339
2450
  "MESH_SOURCE_SCENE_UNSUPPORTED",
2340
2451
  "A default scene or exactly one scene is required"
2341
2452
  );
2342
2453
  let primitiveIndex = 0;
2343
- for (const selected of traverseMeshNodes(roots, placement)) {
2454
+ const primitives = traverseMeshNodes(roots, placement);
2455
+ let current = primitives.next();
2456
+ while (!current.done) {
2457
+ const selected = current.value;
2458
+ const next = primitives.next();
2344
2459
  const resource = await extractPrimitive(
2345
2460
  selected.primitive,
2346
2461
  selected.transform,
@@ -2356,10 +2471,15 @@ function createMeshTilesetConversionSource(tileset, options = {}) {
2356
2471
  resource.mesh,
2357
2472
  options.features
2358
2473
  );
2359
- yield { ...resource, features };
2474
+ yield {
2475
+ ...resource,
2476
+ features,
2477
+ contentPrimitive: { index: primitiveIndex - 1, last: Boolean(next.done) }
2478
+ };
2479
+ current = next;
2360
2480
  }
2361
2481
  if (!primitiveIndex)
2362
- throw new import_core19.TileConversionError(
2482
+ throw new import_core20.TileConversionError(
2363
2483
  "MESH_SOURCE_CONTENT_EMPTY",
2364
2484
  "Declared content contains no supported mesh primitive"
2365
2485
  );
@@ -2371,16 +2491,16 @@ function createMeshTilesetConversionSource(tileset, options = {}) {
2371
2491
  function* traverseMeshNodes(nodes, parent, ancestors = /* @__PURE__ */ new Set()) {
2372
2492
  for (const node of nodes) {
2373
2493
  if (ancestors.has(node) || node.skin || node.weights || Object.keys(node.extensions || {}).length)
2374
- throw new import_core19.TileConversionError(
2494
+ throw new import_core20.TileConversionError(
2375
2495
  "MESH_SOURCE_NODE_UNSUPPORTED",
2376
2496
  "Cyclic, skinned, morphed, or extended nodes are unsupported"
2377
2497
  );
2378
- const transform = new import_core18.Matrix4(parent).multiplyRight(
2379
- node.matrix ? new import_core18.Matrix4(node.matrix) : new import_core18.Matrix4().translate(node.translation || [0, 0, 0]).multiplyRight(new import_core18.Matrix4().fromQuaternion(node.rotation || [0, 0, 0, 1])).scale(node.scale || [1, 1, 1])
2498
+ const transform = new import_core19.Matrix4(parent).multiplyRight(
2499
+ node.matrix ? new import_core19.Matrix4(node.matrix) : new import_core19.Matrix4().translate(node.translation || [0, 0, 0]).multiplyRight(new import_core19.Matrix4().fromQuaternion(node.rotation || [0, 0, 0, 1])).scale(node.scale || [1, 1, 1])
2380
2500
  );
2381
2501
  if (node.mesh) {
2382
2502
  if (node.mesh.weights || Object.keys(node.mesh.extensions || {}).length)
2383
- throw new import_core19.TileConversionError(
2503
+ throw new import_core20.TileConversionError(
2384
2504
  "MESH_SOURCE_NODE_UNSUPPORTED",
2385
2505
  "Morph weights and mesh extensions are unsupported"
2386
2506
  );
@@ -2391,12 +2511,12 @@ function* traverseMeshNodes(nodes, parent, ancestors = /* @__PURE__ */ new Set()
2391
2511
  }
2392
2512
  async function extractPrimitive(primitive, transform, id, readExternalResource, contentUri, signal) {
2393
2513
  if (primitive.targets?.length || Object.keys(primitive.extensions || {}).some((name) => name !== "EXT_mesh_features"))
2394
- throw new import_core19.TileConversionError(
2514
+ throw new import_core20.TileConversionError(
2395
2515
  "MESH_SOURCE_PRIMITIVE_UNSUPPORTED",
2396
2516
  "Morph targets and unmapped primitive extensions are unsupported"
2397
2517
  );
2398
2518
  if (Array.from(transform).some((value) => !Number.isFinite(value)) || transform[3] !== 0 || transform[7] !== 0 || transform[11] !== 0 || transform[15] !== 1 || transform.determinant() <= 0)
2399
- throw new import_core19.TileConversionError(
2519
+ throw new import_core20.TileConversionError(
2400
2520
  "MESH_SOURCE_TRANSFORM_INVALID",
2401
2521
  "Mesh placement requires a finite invertible affine transform with positive determinant"
2402
2522
  );
@@ -2404,7 +2524,7 @@ async function extractPrimitive(primitive, transform, id, readExternalResource,
2404
2524
  for (const [name, accessor] of Object.entries(primitive.attributes)) {
2405
2525
  if (name === "_BATCHID" || /^_FEATURE_ID_\d+$/.test(name)) continue;
2406
2526
  if (!["POSITION", "NORMAL", "COLOR_0", "TEXCOORD_0"].includes(name))
2407
- throw new import_core19.TileConversionError(
2527
+ throw new import_core20.TileConversionError(
2408
2528
  "MESH_SOURCE_ATTRIBUTE_UNSUPPORTED",
2409
2529
  `Unsupported mesh attribute ${name}`
2410
2530
  );
@@ -2440,7 +2560,7 @@ async function extractPrimitive(primitive, transform, id, readExternalResource,
2440
2560
  mesh.attributes.POSITION = { value: positions, size: 3 };
2441
2561
  if (mesh.attributes.NORMAL) {
2442
2562
  const values = mesh.attributes.NORMAL.value;
2443
- const normalMatrix = new import_core18.Matrix4(transform).invert().transpose();
2563
+ const normalMatrix = new import_core19.Matrix4(transform).invert().transpose();
2444
2564
  const normals = new Float32Array(values.length);
2445
2565
  for (let index = 0; index < values.length; index += 3) {
2446
2566
  const normal = normalMatrix.transformAsVector(Array.from(values.subarray(index, index + 3)));
@@ -2481,7 +2601,7 @@ async function mapMeshSourceMaterial(material, readExternalResource, contentUri,
2481
2601
  ) || Object.entries(pbr || {}).some(
2482
2602
  ([name, value]) => value !== void 0 && !["baseColorFactor", "baseColorTexture", "metallicFactor", "roughnessFactor"].includes(name)
2483
2603
  ) || material.emissiveFactor?.some((value) => value !== 0))
2484
- throw new import_core19.TileConversionError(
2604
+ throw new import_core20.TileConversionError(
2485
2605
  "MESH_SOURCE_MATERIAL_UNSUPPORTED",
2486
2606
  "Only metallic-roughness factors and one embedded base-color image are supported"
2487
2607
  );
@@ -2512,7 +2632,7 @@ function createTriangleIndices(primitive, vertexCount) {
2512
2632
  const sourceIndices = validateMeshIndices(attribute, vertexCount, 5);
2513
2633
  const indexCount = sourceIndices?.length ?? vertexCount;
2514
2634
  if (!Number.isSafeInteger(indexCount) || indexCount < 3) {
2515
- throw new import_core19.TileConversionError(
2635
+ throw new import_core20.TileConversionError(
2516
2636
  "MESH_TRIANGLE_COUNT_INVALID",
2517
2637
  "Triangle strips require at least three vertices"
2518
2638
  );
@@ -2531,13 +2651,19 @@ function createTriangleIndices(primitive, vertexCount) {
2531
2651
 
2532
2652
  // src/v5/i3s-mesh-conversion.ts
2533
2653
  var import_i3s = require("@loaders.gl/i3s");
2534
- var import_core21 = require("@loaders.gl/tile-converter/v5/core");
2535
2654
  var import_core22 = require("@loaders.gl/tile-converter/v5/core");
2655
+ var import_core23 = require("@loaders.gl/tile-converter/v5/core");
2536
2656
  function createI3SMeshConversionCodec(options) {
2657
+ if (options.integratedMeshPolicy !== void 0 && options.integratedMeshPolicy !== "as-3d-object") {
2658
+ throw new import_core22.TileConversionError(
2659
+ "I3S_LAYER_TYPE_POLICY_INVALID",
2660
+ "Unknown IntegratedMesh conversion policy"
2661
+ );
2662
+ }
2537
2663
  const reference = options.spatialContext.spatialReference;
2538
2664
  const height = reference.targetHeightReference === "native" ? reference.heightReference : reference.targetHeightReference;
2539
2665
  if (!["native", "transformed"].includes(reference.status) || (reference.targetCrs || reference.sourceCrs) !== "EPSG:4978" || height !== "ellipsoidal" || reference.status === "native" && (!["xyz", "unknown"].includes(reference.axisOrder) || reference.verticalUnitScale !== 1 || reference.units?.some((unit) => unit !== "meter")))
2540
- throw new import_core21.TileConversionError(
2666
+ throw new import_core22.TileConversionError(
2541
2667
  "I3S_MESH_FRAME_UNSUPPORTED",
2542
2668
  "I3S mesh authoring requires resolved ECEF coordinates and ellipsoidal heights"
2543
2669
  );
@@ -2546,6 +2672,12 @@ function createI3SMeshConversionCodec(options) {
2546
2672
  /** Places a source mesh in ECEF and authors one layer without modifying source arrays. */
2547
2673
  async *convert(resource, _inspection, signal) {
2548
2674
  signal?.throwIfAborted();
2675
+ if (resource.sourceLayerType === "IntegratedMesh" && options.integratedMeshPolicy !== "as-3d-object") {
2676
+ throw new import_core22.TileConversionError(
2677
+ "I3S_LAYER_TYPE_CHANGE_REQUIRED",
2678
+ "IntegratedMesh to 3DObject conversion requires integratedMeshPolicy=as-3d-object"
2679
+ );
2680
+ }
2549
2681
  const mesh = validateMeshGeometry(resource.mesh, true);
2550
2682
  const positions = mesh.attributes.POSITION.value;
2551
2683
  const context = options.spatialContext;
@@ -2562,6 +2694,7 @@ function createI3SMeshConversionCodec(options) {
2562
2694
  if (mesh.attributes.NORMAL) mesh.attributes.NORMAL = { value: transformed.normals, size: 3 };
2563
2695
  let layer;
2564
2696
  try {
2697
+ await (0, import_i3s.preloadI3SMeshCompression)();
2565
2698
  const writerOptions = {
2566
2699
  ...options,
2567
2700
  material: resource.material,
@@ -2570,18 +2703,26 @@ function createI3SMeshConversionCodec(options) {
2570
2703
  };
2571
2704
  layer = options.draco === false ? (0, import_i3s.encodeI3SMeshLayer)(mesh, writerOptions) : await (0, import_i3s.encodeI3SMeshLayerWithDraco)(mesh, writerOptions, options.dracoLibraryOptions);
2572
2705
  } catch (error) {
2573
- throw new import_core21.TileConversionError(
2706
+ throw new import_core22.TileConversionError(
2574
2707
  "I3S_MESH_PROFILE_UNSUPPORTED",
2575
2708
  error instanceof Error ? error.message : String(error)
2576
2709
  );
2577
2710
  }
2578
2711
  signal?.throwIfAborted();
2579
2712
  objectIdOffset += resource.features?.batches.reduce((total, batch) => total + batch.data.numRows, 0) ?? 1;
2580
- yield { id: resource.id, ...layer };
2713
+ yield { id: resource.id, ...layer, sourceLayerType: resource.sourceLayerType };
2581
2714
  },
2582
2715
  /** Reports measured precision and explicitly authorized integer representation changes. */
2583
2716
  async validateOutput(resource) {
2584
2717
  return [
2718
+ ...resource.sourceLayerType === "IntegratedMesh" ? [
2719
+ {
2720
+ code: "I3S_LAYER_TYPE_CHANGED",
2721
+ message: "Explicitly authored IntegratedMesh geometry as I3S 1.7 3DObject; integrated surface layer semantics and source LOD are not preserved",
2722
+ severity: "warning",
2723
+ resourceId: resource.id
2724
+ }
2725
+ ] : [],
2585
2726
  ...resource.maximumPositionError > 0 ? [
2586
2727
  {
2587
2728
  code: "I3S_MESH_POSITION_ROUNDING",
@@ -2601,7 +2742,7 @@ function createI3SMeshConversionCodec(options) {
2601
2742
  };
2602
2743
  }
2603
2744
  function createSingleMeshI3SSink(options) {
2604
- const memory = (0, import_core22.createBoundedMemoryTileConversionSink)(options);
2745
+ const memory = (0, import_core23.createBoundedMemoryTileConversionSink)(options);
2605
2746
  let state = "empty";
2606
2747
  let completed = false;
2607
2748
  return {
@@ -2611,7 +2752,7 @@ function createSingleMeshI3SSink(options) {
2611
2752
  async write(resource, signal) {
2612
2753
  signal?.throwIfAborted();
2613
2754
  if (state !== "empty")
2614
- throw new import_core21.TileConversionError(
2755
+ throw new import_core22.TileConversionError(
2615
2756
  "SINGLE_I3S_MESH_UNAVAILABLE",
2616
2757
  "The initial I3S sink accepts exactly one mesh layer"
2617
2758
  );
@@ -2630,7 +2771,7 @@ function createSingleMeshI3SSink(options) {
2630
2771
  /** Commits a complete single-layer result. */
2631
2772
  async finalize(report) {
2632
2773
  if (state !== "written")
2633
- throw new import_core21.TileConversionError(
2774
+ throw new import_core22.TileConversionError(
2634
2775
  "SINGLE_I3S_MESH_INCOMPLETE",
2635
2776
  "Finalization requires exactly one successfully written layer"
2636
2777
  );
@@ -2650,16 +2791,16 @@ function createSingleMeshI3SSink(options) {
2650
2791
  // src/v5/i3s-mesh-sink.ts
2651
2792
  var import_geospatial = require("@math.gl/geospatial");
2652
2793
  var import_compression = require("@loaders.gl/compression");
2653
- var import_core23 = require("@loaders.gl/tile-converter/v5/core");
2794
+ var import_core24 = require("@loaders.gl/tile-converter/v5/core");
2654
2795
  function createI3SMeshSink(options) {
2655
2796
  const { maxMeshes, maxResourceBytes } = options;
2656
2797
  if (!Number.isInteger(maxMeshes) || maxMeshes < 1 || maxMeshes > 64 || !Number.isSafeInteger(maxResourceBytes) || maxResourceBytes < 1)
2657
- throw new import_core23.TileConversionError(
2798
+ throw new import_core24.TileConversionError(
2658
2799
  "I3S_COLLECTION_OPTIONS_INVALID",
2659
2800
  "I3S collections require 1\u201364 meshes and a positive resource byte limit"
2660
2801
  );
2661
- const memory = (0, import_core23.createBoundedMemoryTileConversionSink)(options);
2662
- const compressor = new import_compression.GZipCompressor({ useNative: false, gzip: { mtime: 0 } });
2802
+ const memory = (0, import_core24.createBoundedMemoryTileConversionSink)(options);
2803
+ const compressor = new import_compression.GZipCompressor({ useNative: false });
2663
2804
  const decompressor = new import_compression.GZipDecompressor({ useNative: false });
2664
2805
  const leaves = [];
2665
2806
  const identifiers = /* @__PURE__ */ new Set();
@@ -2671,7 +2812,7 @@ function createI3SMeshSink(options) {
2671
2812
  async function readBytes(bytes) {
2672
2813
  const output = await decompressor.decompress(bytes);
2673
2814
  if (output.byteLength > maxResourceBytes)
2674
- throw new import_core23.TileConversionError(
2815
+ throw new import_core24.TileConversionError(
2675
2816
  "I3S_COLLECTION_RESOURCE_TOO_LARGE",
2676
2817
  "I3S metadata exceeds maxResourceBytes"
2677
2818
  );
@@ -2683,7 +2824,7 @@ function createI3SMeshSink(options) {
2683
2824
  async function writeJson(resourceId, value) {
2684
2825
  const bytes = new TextEncoder().encode(JSON.stringify(value));
2685
2826
  if (bytes.byteLength > maxResourceBytes)
2686
- throw new import_core23.TileConversionError(
2827
+ throw new import_core24.TileConversionError(
2687
2828
  "I3S_COLLECTION_RESOURCE_TOO_LARGE",
2688
2829
  "I3S index exceeds maxResourceBytes"
2689
2830
  );
@@ -2700,7 +2841,7 @@ function createI3SMeshSink(options) {
2700
2841
  async write(resource, signal) {
2701
2842
  signal?.throwIfAborted();
2702
2843
  if (state !== "open" || identifiers.has(resource.id) || leaves.length >= maxMeshes)
2703
- throw new import_core23.TileConversionError(
2844
+ throw new import_core24.TileConversionError(
2704
2845
  "I3S_COLLECTION_UNAVAILABLE",
2705
2846
  "I3S collection is busy, closed, full, or repeats a placement"
2706
2847
  );
@@ -2713,7 +2854,7 @@ function createI3SMeshSink(options) {
2713
2854
  authored.store.defaultGeometrySchema
2714
2855
  ]);
2715
2856
  if (schemaKey !== void 0 && key !== schemaKey)
2716
- throw new import_core23.TileConversionError(
2857
+ throw new import_core24.TileConversionError(
2717
2858
  "I3S_COLLECTION_SCHEMA_MISMATCH",
2718
2859
  "Mesh nodes must share feature fields and the legacy geometry layout"
2719
2860
  );
@@ -2725,7 +2866,7 @@ function createI3SMeshSink(options) {
2725
2866
  if (objectIds.byteLength !== 4 + count * 4 || !count || objectIdRanges.some(([start, end]) => first < end && first + count > start) || Array.from({ length: count }, (_, index2) => objectIds.getUint32(4 + index2 * 4, true)).some(
2726
2867
  (value, index2) => value !== first + index2
2727
2868
  ))
2728
- throw new import_core23.TileConversionError(
2869
+ throw new import_core24.TileConversionError(
2729
2870
  "I3S_COLLECTION_IDS_INVALID",
2730
2871
  "Generated object IDs must be contiguous and disjoint across nodes"
2731
2872
  );
@@ -2736,7 +2877,7 @@ function createI3SMeshSink(options) {
2736
2877
  "nodes/1/3dNodeIndexDocument.json.gz"
2737
2878
  );
2738
2879
  if (node.obb.quaternion.join(",") !== "0,0,0,1" || [...node.obb.center, ...node.obb.halfSize].some((value) => !Number.isFinite(value)) || node.obb.halfSize.some((value) => value < 0))
2739
- throw new import_core23.TileConversionError(
2880
+ throw new import_core24.TileConversionError(
2740
2881
  "I3S_COLLECTION_BOUNDS_INVALID",
2741
2882
  "Expected finite axis-aligned bounds from the I3S mesh writer"
2742
2883
  );
@@ -2787,7 +2928,7 @@ function createI3SMeshSink(options) {
2787
2928
  /** Rebuilds both indices and layer bounds only after every leaf has completed. */
2788
2929
  async finalize(report) {
2789
2930
  if (state !== "open" || !layer || !leaves.length)
2790
- throw new import_core23.TileConversionError(
2931
+ throw new import_core24.TileConversionError(
2791
2932
  "I3S_COLLECTION_INCOMPLETE",
2792
2933
  "Finalization requires successfully written mesh nodes"
2793
2934
  );
@@ -2812,7 +2953,7 @@ function createI3SMeshSink(options) {
2812
2953
  quaternion: [0, 0, 0, 1]
2813
2954
  };
2814
2955
  if ([...obb.center, ...obb.halfSize].some((value) => !Number.isFinite(value)))
2815
- throw new import_core23.TileConversionError(
2956
+ throw new import_core24.TileConversionError(
2816
2957
  "I3S_COLLECTION_BOUNDS_INVALID",
2817
2958
  "Collection bounds cannot be represented"
2818
2959
  );
@@ -2872,7 +3013,7 @@ function createI3SMeshSink(options) {
2872
3013
  }
2873
3014
 
2874
3015
  // src/v5/conversion-api.ts
2875
- var TileConversionError17 = class extends Error {
3016
+ var TileConversionError18 = class extends Error {
2876
3017
  /** Stable machine-readable failure code. */
2877
3018
  code;
2878
3019
  /** Diagnostics associated with this failure. */
@@ -2890,7 +3031,7 @@ var TileConversionError17 = class extends Error {
2890
3031
  function createTileConversionResourceFetcher(options) {
2891
3032
  let consumed = options.initialInputBytes ?? 0;
2892
3033
  if (!Number.isSafeInteger(options.maxInputBytes) || options.maxInputBytes < 1 || !Number.isSafeInteger(consumed) || consumed < 0 || consumed > options.maxInputBytes)
2893
- throw new TileConversionError17(
3034
+ throw new TileConversionError18(
2894
3035
  "INVALID_INPUT_MEMORY_LIMIT",
2895
3036
  "Resource fetch requires a positive aggregate byte budget and valid initial bytes"
2896
3037
  );
@@ -2899,7 +3040,7 @@ function createTileConversionResourceFetcher(options) {
2899
3040
  options.signal?.throwIfAborted();
2900
3041
  const url = new URL(input instanceof Request ? input.url : String(input));
2901
3042
  if (!["http:", "https:", "blob:", "data:"].includes(url.protocol))
2902
- throw new TileConversionError17(
3043
+ throw new TileConversionError18(
2903
3044
  "EXTERNAL_RESOURCE_UNSUPPORTED",
2904
3045
  "Conversion transport requires HTTP(S), Blob or embedded data resources"
2905
3046
  );
@@ -2908,14 +3049,14 @@ function createTileConversionResourceFetcher(options) {
2908
3049
  const response = await fetcher(input, { ...requestOptions, signal });
2909
3050
  if (!response.ok) {
2910
3051
  await response.body?.cancel();
2911
- throw new TileConversionError17(
3052
+ throw new TileConversionError18(
2912
3053
  "BROWSER_SOURCE_FETCH_FAILED",
2913
3054
  `Could not fetch conversion resource (${response.status})`
2914
3055
  );
2915
3056
  }
2916
3057
  const reader = response.body?.getReader();
2917
3058
  if (!reader)
2918
- throw new TileConversionError17(
3059
+ throw new TileConversionError18(
2919
3060
  "BROWSER_SOURCE_STREAM_UNAVAILABLE",
2920
3061
  "Conversion resources require readable response streams"
2921
3062
  );
@@ -2933,7 +3074,7 @@ function createTileConversionResourceFetcher(options) {
2933
3074
  signal?.throwIfAborted();
2934
3075
  if (done) break;
2935
3076
  if (value.byteLength > options.maxInputBytes - consumed)
2936
- throw new TileConversionError17(
3077
+ throw new TileConversionError18(
2937
3078
  "INPUT_RESOURCE_TOO_LARGE",
2938
3079
  "Selected contents and external dependencies exceed the aggregate input byte budget"
2939
3080
  );
@@ -2954,25 +3095,27 @@ function createTileConversionResourceFetcher(options) {
2954
3095
  bytes.set(part, offset);
2955
3096
  offset += part.byteLength;
2956
3097
  }
2957
- return new Response(bytes, {
3098
+ const boundedResponse = new Response(bytes, {
2958
3099
  status: response.status,
2959
3100
  statusText: response.statusText,
2960
3101
  headers: response.headers
2961
3102
  });
3103
+ Object.defineProperty(boundedResponse, "url", { value: response.url });
3104
+ return boundedResponse;
2962
3105
  };
2963
3106
  }
2964
3107
 
2965
3108
  // src/v5/point-cloud-tileset-sink.ts
2966
- var import_core24 = require("@loaders.gl/tile-converter/v5/core");
3109
+ var import_core25 = require("@loaders.gl/tile-converter/v5/core");
2967
3110
  function createPointCloudTilesetSink(options) {
2968
3111
  const { maxTiles, geometricError } = options;
2969
3112
  if (!Number.isSafeInteger(maxTiles) || maxTiles < 1 || !Number.isFinite(geometricError) || geometricError < 0) {
2970
- throw new import_core24.TileConversionError(
3113
+ throw new import_core25.TileConversionError(
2971
3114
  "POINT_CLOUD_TILESET_OPTIONS_INVALID",
2972
3115
  "Positive maxTiles and finite nonnegative geometricError are required"
2973
3116
  );
2974
3117
  }
2975
- const memory = (0, import_core24.createBoundedMemoryTileConversionSink)(options);
3118
+ const memory = (0, import_core25.createBoundedMemoryTileConversionSink)(options);
2976
3119
  const children = [];
2977
3120
  const identifiers = /* @__PURE__ */ new Set();
2978
3121
  const minimum = [Infinity, Infinity, Infinity];
@@ -2986,12 +3129,12 @@ function createPointCloudTilesetSink(options) {
2986
3129
  async write(resource, signal) {
2987
3130
  signal?.throwIfAborted();
2988
3131
  if (state !== "open")
2989
- throw new import_core24.TileConversionError(
3132
+ throw new import_core25.TileConversionError(
2990
3133
  "POINT_CLOUD_TILESET_SINK_UNAVAILABLE",
2991
3134
  "Point sink is busy or closed"
2992
3135
  );
2993
3136
  if (!resource.id || identifiers.has(resource.id) || children.length >= maxTiles) {
2994
- throw new import_core24.TileConversionError(
3137
+ throw new import_core25.TileConversionError(
2995
3138
  "POINT_CLOUD_TILESET_PLACEMENT_INVALID",
2996
3139
  "Placements require unique nonempty identifiers within maxTiles"
2997
3140
  );
@@ -3004,7 +3147,7 @@ function createPointCloudTilesetSink(options) {
3004
3147
  signal
3005
3148
  );
3006
3149
  if (state !== "writing")
3007
- throw new import_core24.TileConversionError(
3150
+ throw new import_core25.TileConversionError(
3008
3151
  "POINT_CLOUD_TILESET_SINK_UNAVAILABLE",
3009
3152
  "Point sink was aborted during writing"
3010
3153
  );
@@ -3023,7 +3166,7 @@ function createPointCloudTilesetSink(options) {
3023
3166
  /** Serializes the enclosing root after all placements have completed. */
3024
3167
  async finalize(report) {
3025
3168
  if (state !== "open" || !children.length)
3026
- throw new import_core24.TileConversionError(
3169
+ throw new import_core25.TileConversionError(
3027
3170
  "POINT_CLOUD_TILESET_SINK_INCOMPLETE",
3028
3171
  "Finalization requires successfully written point tiles"
3029
3172
  );
@@ -3033,7 +3176,7 @@ function createPointCloudTilesetSink(options) {
3033
3176
  Math.hypot(...minimum.map((value, axis) => maximum[axis] - value))
3034
3177
  );
3035
3178
  if (!Number.isFinite(rootError))
3036
- throw new import_core24.TileConversionError(
3179
+ throw new import_core25.TileConversionError(
3037
3180
  "POINT_CLOUD_TILESET_BOUNDS_INVALID",
3038
3181
  "Enclosing bounds must have a finite diagonal"
3039
3182
  );
@@ -3053,13 +3196,13 @@ function createPointCloudTilesetSink(options) {
3053
3196
  contentType: "application/json"
3054
3197
  });
3055
3198
  if (state !== "writing")
3056
- throw new import_core24.TileConversionError(
3199
+ throw new import_core25.TileConversionError(
3057
3200
  "POINT_CLOUD_TILESET_SINK_UNAVAILABLE",
3058
3201
  "Point sink was aborted during finalization"
3059
3202
  );
3060
3203
  await memory.finalize(report);
3061
3204
  if (state !== "writing")
3062
- throw new import_core24.TileConversionError(
3205
+ throw new import_core25.TileConversionError(
3063
3206
  "POINT_CLOUD_TILESET_SINK_UNAVAILABLE",
3064
3207
  "Point sink was aborted during finalization"
3065
3208
  );
@@ -3080,13 +3223,13 @@ function getPointTileBounds(resource, geometricError) {
3080
3223
  const reference = resource.spatialReference;
3081
3224
  const identity = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
3082
3225
  if (!reference || !["native", "transformed"].includes(reference.status) || (reference.targetCrs || reference.sourceCrs) !== "EPSG:4978" || (reference.targetHeightReference === "native" ? reference.heightReference : reference.targetHeightReference) !== "ellipsoidal" || resource.coordinateSystem !== "cartesian" || resource.cartographicOrigin.length !== 3 || resource.cartographicOrigin.some((value) => value !== 0) || resource.modelMatrix && (resource.modelMatrix.length !== 16 || resource.modelMatrix.some((value, index) => value !== identity[index])) || reference.status === "native" && (reference.coordinateFrame !== "geocentric" || reference.axisOrder !== "xyz" || reference.verticalUnitScale !== 1 || reference.units?.some((unit) => unit !== "meter"))) {
3083
- throw new import_core24.TileConversionError(
3226
+ throw new import_core25.TileConversionError(
3084
3227
  "POINT_CLOUD_TILESET_FRAME_UNSUPPORTED",
3085
3228
  "Point tiles require declared EPSG:4978 xyz meters, ellipsoidal heights and no additional source placement"
3086
3229
  );
3087
3230
  }
3088
3231
  if (!Number.isFinite(resource.maximumPositionError) || resource.maximumPositionError < 0 || resource.maximumPositionError > geometricError) {
3089
- throw new import_core24.TileConversionError(
3232
+ throw new import_core25.TileConversionError(
3090
3233
  "POINT_CLOUD_GEOMETRIC_ERROR_INVALID",
3091
3234
  "geometricError must include measured position reconstruction error"
3092
3235
  );
@@ -3094,7 +3237,7 @@ function getPointTileBounds(resource, geometricError) {
3094
3237
  const [minimum, maximum] = resource.localBoundingBox;
3095
3238
  const center = resource.rtcCenter;
3096
3239
  if (center.length !== 3 || minimum.length !== 3 || maximum.length !== 3 || [...center, ...minimum, ...maximum].some((value) => !Number.isFinite(value)) || minimum.some((value, axis) => value > maximum[axis])) {
3097
- throw new import_core24.TileConversionError(
3240
+ throw new import_core25.TileConversionError(
3098
3241
  "POINT_CLOUD_TILESET_BOUNDS_INVALID",
3099
3242
  "RTC center and encoded xyz bounds must be finite and ordered"
3100
3243
  );
@@ -3102,7 +3245,7 @@ function getPointTileBounds(resource, geometricError) {
3102
3245
  const lower = minimum.map((value, axis) => value + center[axis]);
3103
3246
  const upper = maximum.map((value, axis) => value + center[axis]);
3104
3247
  if ([...lower, ...upper].some((value) => !Number.isFinite(value)))
3105
- throw new import_core24.TileConversionError(
3248
+ throw new import_core25.TileConversionError(
3106
3249
  "POINT_CLOUD_TILESET_BOUNDS_INVALID",
3107
3250
  "Reconstructed ECEF bounds must be finite"
3108
3251
  );
@@ -3115,11 +3258,11 @@ function createPointBoundsBox(minimum, maximum) {
3115
3258
  }
3116
3259
 
3117
3260
  // src/v5/i3s-mesh-source.ts
3118
- var import_core27 = require("@loaders.gl/tile-converter/v5/core");
3261
+ var import_core28 = require("@loaders.gl/tile-converter/v5/core");
3119
3262
 
3120
3263
  // src/v5/i3s-mesh-source-material.ts
3121
3264
  var import_images3 = require("@loaders.gl/images");
3122
- var import_core25 = require("@loaders.gl/tile-converter/v5/core");
3265
+ var import_core26 = require("@loaders.gl/tile-converter/v5/core");
3123
3266
  async function mapI3SMeshSourceMaterial(content, header, readExternalResource, contentUri, signal) {
3124
3267
  const material = Object.fromEntries(
3125
3268
  Object.entries(content.material || {}).filter(([, value]) => value !== void 0)
@@ -3129,7 +3272,7 @@ async function mapI3SMeshSourceMaterial(content, header, readExternalResource, c
3129
3272
  ) : {};
3130
3273
  material.pbrMetallicRoughness = metallicRoughness;
3131
3274
  if (material.cullFace !== void 0 && material.cullFace !== "back") {
3132
- throw new import_core25.TileConversionError(
3275
+ throw new import_core26.TileConversionError(
3133
3276
  "I3S_MESH_SOURCE_MATERIAL_UNSUPPORTED",
3134
3277
  "Only back-face culling is supported"
3135
3278
  );
@@ -3148,7 +3291,7 @@ async function mapI3SMeshSourceMaterial(content, header, readExternalResource, c
3148
3291
  }
3149
3292
  const encodedTextures = content.textures || {};
3150
3293
  if (!textureInfo && (resources.length || content.texture || Object.keys(encodedTextures).length)) {
3151
- throw new import_core25.TileConversionError(
3294
+ throw new import_core26.TileConversionError(
3152
3295
  "I3S_MESH_SOURCE_TEXTURE_UNSUPPORTED",
3153
3296
  "Texture resources require a base-color mapping"
3154
3297
  );
@@ -3161,7 +3304,7 @@ async function mapI3SMeshSourceMaterial(content, header, readExternalResource, c
3161
3304
  ) || ["wrapS", "wrapT"].some(
3162
3305
  (name) => textureInfo[name] !== void 0 && (typeof textureInfo[name] !== "string" || !Object.hasOwn(wrappingModes, textureInfo[name]))
3163
3306
  ) || resources.length > 1 || resources.some((resource) => resource.textureSetDefinitionId !== definitionId) || Object.keys(encodedTextures).some((key) => key !== String(definitionId))) {
3164
- throw new import_core25.TileConversionError(
3307
+ throw new import_core26.TileConversionError(
3165
3308
  "I3S_MESH_SOURCE_TEXTURE_UNSUPPORTED",
3166
3309
  "Only one base-color texture set on TEXCOORD_0 with qualified wrapping is supported"
3167
3310
  );
@@ -3169,7 +3312,7 @@ async function mapI3SMeshSourceMaterial(content, header, readExternalResource, c
3169
3312
  let encoded = encodedTextures[String(definitionId)] || content.texture || textureInfo.texture?.source?.image;
3170
3313
  if (!encoded) {
3171
3314
  if (!resources.length || !readExternalResource) {
3172
- throw new import_core25.TileConversionError(
3315
+ throw new import_core26.TileConversionError(
3173
3316
  "I3S_MESH_SOURCE_TEXTURE_UNSUPPORTED",
3174
3317
  "Encoded texture bytes or an explicit bounded resource reader are required"
3175
3318
  );
@@ -3188,7 +3331,7 @@ async function mapI3SMeshSourceMaterial(content, header, readExternalResource, c
3188
3331
  }
3189
3332
  const declaredFormat = resources[0]?.textureFormat;
3190
3333
  if (!image || !["image/png", "image/jpeg"].includes(image.mimeType) || declaredFormat && (declaredFormat === "png" ? image.mimeType !== "image/png" : declaredFormat === "jpg" ? image.mimeType !== "image/jpeg" : true)) {
3191
- throw new import_core25.TileConversionError(
3334
+ throw new import_core26.TileConversionError(
3192
3335
  "I3S_MESH_SOURCE_TEXTURE_UNSUPPORTED",
3193
3336
  "I3S base-color bytes must have matching PNG/JPEG headers"
3194
3337
  );
@@ -3211,7 +3354,7 @@ async function mapI3SMeshSourceMaterial(content, header, readExternalResource, c
3211
3354
  signal
3212
3355
  );
3213
3356
  if (mapped?.baseColorTexture && !content.sourceAttributes?.uv0) {
3214
- throw new import_core25.TileConversionError(
3357
+ throw new import_core26.TileConversionError(
3215
3358
  "MESH_TEXCOORD_REQUIRED",
3216
3359
  "Base-color textures require TEXCOORD_0"
3217
3360
  );
@@ -3221,7 +3364,7 @@ async function mapI3SMeshSourceMaterial(content, header, readExternalResource, c
3221
3364
 
3222
3365
  // src/v5/i3s-mesh-source-features.ts
3223
3366
  var import_i3s2 = require("@loaders.gl/i3s");
3224
- var import_core26 = require("@loaders.gl/tile-converter/v5/core");
3367
+ var import_core27 = require("@loaders.gl/tile-converter/v5/core");
3225
3368
  async function extractI3SMeshFeatures(content, item, metadata, options, readExternalResource, contentUri, signal) {
3226
3369
  const maximumAttributeBytes = options.maxAttributeBytes;
3227
3370
  const storage = metadata.tileset.attributeStorageInfo;
@@ -3229,7 +3372,7 @@ async function extractI3SMeshFeatures(content, item, metadata, options, readExte
3229
3372
  if (!options.metadataClass || !options.objectIdProperty || options.rawMetadataField || !Number.isSafeInteger(maximumAttributeBytes) || maximumAttributeBytes <= 0 || !storage?.length || attributeUrls?.length !== storage.length || !readExternalResource || new Set(storage.map((attribute2) => attribute2.name)).size !== storage.length || new Set(storage.map((attribute2) => attribute2.key)).size !== storage.length || !storage.some((attribute2) => attribute2.name === options.objectIdProperty) || (metadata.tileset.fields || []).some(
3230
3373
  (field) => field.domain || field.type === "esriFieldTypeDate" || !storage.some((attribute2) => attribute2.name === field.name)
3231
3374
  )) {
3232
- throw new import_core26.TileConversionError(
3375
+ throw new import_core27.TileConversionError(
3233
3376
  "I3S_MESH_SOURCE_FEATURE_MAPPING_REQUIRED",
3234
3377
  "I3S attributes require an explicit scalar schema, object ID column, byte budget and resource reader; domains/dates/raw metadata need a custom mapper"
3235
3378
  );
@@ -3252,7 +3395,7 @@ async function extractI3SMeshFeatures(content, item, metadata, options, readExte
3252
3395
  ) || attribute2.ordering && JSON.stringify(
3253
3396
  attribute2.ordering.map((value) => value === "ObjectIds" ? "objectIds" : value)
3254
3397
  ) !== JSON.stringify(expectedOrdering) || type === "String" && (values.encoding !== "UTF-8" || attribute2.attributeByteCounts?.valueType !== "UInt32" || attribute2.attributeByteCounts.valuesPerElement !== 1) || !attributeUrls[index]) {
3255
- throw new import_core26.TileConversionError(
3398
+ throw new import_core27.TileConversionError(
3256
3399
  "I3S_MESH_SOURCE_ATTRIBUTE_UNSUPPORTED",
3257
3400
  `Attribute ${attribute2.name} requires a standard scalar/string resource layout`
3258
3401
  );
@@ -3261,7 +3404,7 @@ async function extractI3SMeshFeatures(content, item, metadata, options, readExte
3261
3404
  signal?.throwIfAborted();
3262
3405
  inputBytes += bytes instanceof Uint8Array ? bytes.byteLength : Infinity;
3263
3406
  if (!(bytes instanceof Uint8Array) || inputBytes > maximumAttributeBytes) {
3264
- throw new import_core26.TileConversionError(
3407
+ throw new import_core27.TileConversionError(
3265
3408
  "INPUT_RESOURCE_TOO_LARGE",
3266
3409
  "I3S node attributes exceed maxAttributeBytes"
3267
3410
  );
@@ -3274,14 +3417,14 @@ async function extractI3SMeshFeatures(content, item, metadata, options, readExte
3274
3417
  i3s: { attributeValues: "exact" }
3275
3418
  });
3276
3419
  } catch (error) {
3277
- throw new import_core26.TileConversionError(
3420
+ throw new import_core27.TileConversionError(
3278
3421
  "I3S_MESH_SOURCE_ATTRIBUTE_INVALID",
3279
3422
  `Invalid attribute ${attribute2.name}: ${error instanceof Error ? error.message : String(error)}`
3280
3423
  );
3281
3424
  }
3282
3425
  const column = parsed[attribute2.name];
3283
3426
  if (!column || count !== void 0 && column.length !== count || !column.length) {
3284
- throw new import_core26.TileConversionError(
3427
+ throw new import_core27.TileConversionError(
3285
3428
  "I3S_MESH_SOURCE_ATTRIBUTE_COUNT_INVALID",
3286
3429
  "Every attribute must have the same nonzero row count"
3287
3430
  );
@@ -3289,7 +3432,7 @@ async function extractI3SMeshFeatures(content, item, metadata, options, readExte
3289
3432
  if (Array.from(column).some(
3290
3433
  (value) => typeof value === "number" && !Number.isFinite(value)
3291
3434
  )) {
3292
- throw new import_core26.TileConversionError(
3435
+ throw new import_core27.TileConversionError(
3293
3436
  "I3S_MESH_SOURCE_ATTRIBUTE_UNSUPPORTED",
3294
3437
  "Numeric noData values require an explicit custom mapper"
3295
3438
  );
@@ -3302,7 +3445,7 @@ async function extractI3SMeshFeatures(content, item, metadata, options, readExte
3302
3445
  for (let index = 0; index < count; index++) {
3303
3446
  const identifier = identifiers[index];
3304
3447
  if (!(typeof identifier === "bigint" || typeof identifier === "number" && Number.isSafeInteger(identifier)) || attributeRows.has(BigInt(identifier))) {
3305
- throw new import_core26.TileConversionError(
3448
+ throw new import_core27.TileConversionError(
3306
3449
  "I3S_MESH_SOURCE_FEATURE_ID_UNSUPPORTED",
3307
3450
  "Object IDs must be unique exact integers"
3308
3451
  );
@@ -3313,7 +3456,7 @@ async function extractI3SMeshFeatures(content, item, metadata, options, readExte
3313
3456
  const geometryIdentifiers = content.featureIds;
3314
3457
  const attribute = content.sourceAttributes?.id;
3315
3458
  if (geometryIdentifiers.length !== vertexCount || attribute?.size !== 1 || attribute.normalized || attribute.value.length !== vertexCount || Array.from(geometryIdentifiers).some((value, index) => value !== attribute.value[index])) {
3316
- throw new import_core26.TileConversionError(
3459
+ throw new import_core27.TileConversionError(
3317
3460
  "I3S_MESH_SOURCE_FEATURE_ID_UNSUPPORTED",
3318
3461
  "Geometry requires one exact unnormalized object ID per vertex"
3319
3462
  );
@@ -3324,7 +3467,7 @@ async function extractI3SMeshFeatures(content, item, metadata, options, readExte
3324
3467
  const covered = new Uint8Array(triangleFeatureIndices.length);
3325
3468
  for (const range of content.drawRanges) {
3326
3469
  if (!Number.isSafeInteger(range.firstPrimitive) || range.firstPrimitive < 0 || !Number.isSafeInteger(range.primitiveCount) || range.primitiveCount <= 0 || range.firstPrimitive + range.primitiveCount > covered.length) {
3327
- throw new import_core26.TileConversionError(
3470
+ throw new import_core27.TileConversionError(
3328
3471
  "I3S_MESH_SOURCE_FEATURE_ASSOCIATION_INVALID",
3329
3472
  "Feature draw ranges must cover valid triangles"
3330
3473
  );
@@ -3332,7 +3475,7 @@ async function extractI3SMeshFeatures(content, item, metadata, options, readExte
3332
3475
  for (let triangle = range.firstPrimitive; triangle < range.firstPrimitive + range.primitiveCount; triangle++) {
3333
3476
  const vertex = indices?.[triangle * 3] ?? triangle * 3;
3334
3477
  if (covered[triangle] || geometryIdentifiers[vertex] !== range.featureId) {
3335
- throw new import_core26.TileConversionError(
3478
+ throw new import_core27.TileConversionError(
3336
3479
  "I3S_MESH_SOURCE_FEATURE_ASSOCIATION_INVALID",
3337
3480
  "Feature draw ranges must agree with decoded ownership without overlapping"
3338
3481
  );
@@ -3341,7 +3484,7 @@ async function extractI3SMeshFeatures(content, item, metadata, options, readExte
3341
3484
  }
3342
3485
  }
3343
3486
  if (covered.some((value) => value === 0)) {
3344
- throw new import_core26.TileConversionError(
3487
+ throw new import_core27.TileConversionError(
3345
3488
  "I3S_MESH_SOURCE_FEATURE_ASSOCIATION_INVALID",
3346
3489
  "Feature draw ranges must cover every triangle"
3347
3490
  );
@@ -3355,7 +3498,7 @@ async function extractI3SMeshFeatures(content, item, metadata, options, readExte
3355
3498
  const featureIds = corners.map((corner) => geometryIdentifiers[corner]);
3356
3499
  const row = attributeRows.get(BigInt(featureIds[0]));
3357
3500
  if (row === void 0 || featureIds.some((identifier) => identifier !== featureIds[0])) {
3358
- throw new import_core26.TileConversionError(
3501
+ throw new import_core27.TileConversionError(
3359
3502
  "I3S_MESH_SOURCE_FEATURE_ASSOCIATION_INVALID",
3360
3503
  "Each triangle must reference one declared attribute row"
3361
3504
  );
@@ -3364,14 +3507,14 @@ async function extractI3SMeshFeatures(content, item, metadata, options, readExte
3364
3507
  usedRows.add(row);
3365
3508
  }
3366
3509
  if (usedRows.size !== count) {
3367
- throw new import_core26.TileConversionError(
3510
+ throw new import_core27.TileConversionError(
3368
3511
  "I3S_MESH_SOURCE_FEATURE_ASSOCIATION_INVALID",
3369
3512
  "Attribute rows without geometry require a separately qualified mapping"
3370
3513
  );
3371
3514
  }
3372
3515
  const stableProperty = options.sourceFeatureIdProperty ?? options.objectIdProperty;
3373
3516
  if (!Object.hasOwn(columns, stableProperty)) {
3374
- throw new import_core26.TileConversionError(
3517
+ throw new import_core27.TileConversionError(
3375
3518
  "I3S_MESH_SOURCE_FEATURE_MAPPING_REQUIRED",
3376
3519
  "Stable identifier column is missing"
3377
3520
  );
@@ -3392,7 +3535,7 @@ async function extractI3SMeshFeatures(content, item, metadata, options, readExte
3392
3535
  for (let index = 0; index < column.length; index++) {
3393
3536
  const identifier = column.get(index);
3394
3537
  if (stableIds.has(identifier)) {
3395
- throw new import_core26.TileConversionError(
3538
+ throw new import_core27.TileConversionError(
3396
3539
  "I3S_MESH_SOURCE_FEATURE_ID_UNSUPPORTED",
3397
3540
  "Stable IDs must be unique"
3398
3541
  );
@@ -3410,25 +3553,25 @@ async function extractI3SMeshFeatures(content, item, metadata, options, readExte
3410
3553
 
3411
3554
  // src/v5/i3s-mesh-source.ts
3412
3555
  function createI3SMeshTilesetConversionSource(tileset, options = {}) {
3413
- const source = (0, import_core27.createTilesetConversionSource)(tileset, options);
3556
+ const source = (0, import_core28.createTilesetConversionSource)(tileset, options);
3414
3557
  return {
3415
3558
  /** Validates the layer profile and explicit source decode policy before reading. */
3416
3559
  async inspect(signal) {
3417
3560
  if (options.features && options.getFeatures) {
3418
- throw new import_core27.TileConversionError(
3561
+ throw new import_core28.TileConversionError(
3419
3562
  "I3S_MESH_SOURCE_FEATURE_MAPPING_REQUIRED",
3420
3563
  "Choose explicit resource features or a custom getFeatures mapper, not both"
3421
3564
  );
3422
3565
  }
3423
3566
  const metadata = await source.inspect(signal);
3424
3567
  if (metadata.type !== "I3S" || !["3DObject", "IntegratedMesh"].includes(metadata.tileset.layerType) || tileset.options.i3s?.geometryMode !== "source") {
3425
- throw new import_core27.TileConversionError(
3568
+ throw new import_core28.TileConversionError(
3426
3569
  "I3S_MESH_SOURCE_PROFILE_UNSUPPORTED",
3427
3570
  "I3S mesh extraction requires a 3DObject/IntegratedMesh runtime with geometryMode=source"
3428
3571
  );
3429
3572
  }
3430
3573
  if (!metadata.spatialReference?.sourceCrs) {
3431
- throw new import_core27.TileConversionError(
3574
+ throw new import_core28.TileConversionError(
3432
3575
  "I3S_MESH_SOURCE_REFERENCE_REQUIRED",
3433
3576
  "I3S conversion requires an explicit source CRS"
3434
3577
  );
@@ -3456,7 +3599,7 @@ function createI3SMeshTilesetConversionSource(tileset, options = {}) {
3456
3599
  ].some(
3457
3600
  (field) => JSON.stringify(content.spatialReference?.[field]) !== JSON.stringify(metadata.spatialReference?.[field])
3458
3601
  )) {
3459
- throw new import_core27.TileConversionError(
3602
+ throw new import_core28.TileConversionError(
3460
3603
  "I3S_MESH_SOURCE_GEOMETRY_UNSUPPORTED",
3461
3604
  "I3S extraction requires original source-frame Float64 triangle geometry"
3462
3605
  );
@@ -3464,7 +3607,7 @@ function createI3SMeshTilesetConversionSource(tileset, options = {}) {
3464
3607
  if (!["earth-centered", "vertex-reference-frame"].includes(
3465
3608
  content.normalReferenceFrame || ""
3466
3609
  ) || content.normalReferenceFrame !== (metadata.tileset.store?.normalReferenceFrame || "earth-centered")) {
3467
- throw new import_core27.TileConversionError(
3610
+ throw new import_core28.TileConversionError(
3468
3611
  "I3S_MESH_SOURCE_NORMAL_FRAME_UNSUPPORTED",
3469
3612
  "Unknown I3S vector basis"
3470
3613
  );
@@ -3472,20 +3615,20 @@ function createI3SMeshTilesetConversionSource(tileset, options = {}) {
3472
3615
  if (content.meshSegmentation || Object.entries(attributes).some(
3473
3616
  ([name, value]) => value && !["position", "normal", "uv0", "id", "faceRange"].includes(name)
3474
3617
  )) {
3475
- throw new import_core27.TileConversionError(
3618
+ throw new import_core28.TileConversionError(
3476
3619
  "I3S_MESH_SOURCE_ATTRIBUTE_UNSUPPORTED",
3477
3620
  "Only positions, normals, TEXCOORD_0 and explicitly mapped feature associations are supported"
3478
3621
  );
3479
3622
  }
3480
3623
  const hasFeatures = content.featureIds.length > 0 || metadata.tileset.fields?.length || metadata.tileset.attributeStorageInfo?.length;
3481
3624
  if (Array.from(content.featureIds).some((value) => !Number.isSafeInteger(value))) {
3482
- throw new import_core27.TileConversionError(
3625
+ throw new import_core28.TileConversionError(
3483
3626
  "I3S_MESH_SOURCE_FEATURE_ID_UNSUPPORTED",
3484
3627
  "Decoded numeric feature IDs must be exact safe integers"
3485
3628
  );
3486
3629
  }
3487
3630
  if (hasFeatures && !options.getFeatures && !options.features) {
3488
- throw new import_core27.TileConversionError(
3631
+ throw new import_core28.TileConversionError(
3489
3632
  "I3S_MESH_SOURCE_FEATURE_MAPPING_REQUIRED",
3490
3633
  "I3S feature data requires explicit resource features or a custom Arrow mapper"
3491
3634
  );
@@ -3504,7 +3647,7 @@ function createI3SMeshTilesetConversionSource(tileset, options = {}) {
3504
3647
  true
3505
3648
  );
3506
3649
  if (mesh.attributes.POSITION.value.length / 3 !== content.vertexCount) {
3507
- throw new import_core27.TileConversionError(
3650
+ throw new import_core28.TileConversionError(
3508
3651
  "I3S_MESH_SOURCE_COUNT_INVALID",
3509
3652
  "I3S vertexCount must match the decoded geometry"
3510
3653
  );
@@ -3520,7 +3663,7 @@ function createI3SMeshTilesetConversionSource(tileset, options = {}) {
3520
3663
  ) : void 0;
3521
3664
  signal?.throwIfAborted();
3522
3665
  if (hasFeatures && !features) {
3523
- throw new import_core27.TileConversionError(
3666
+ throw new import_core28.TileConversionError(
3524
3667
  "I3S_MESH_SOURCE_FEATURE_MAPPING_REQUIRED",
3525
3668
  "The feature mapper must return all declared feature data"
3526
3669
  );
@@ -3536,6 +3679,7 @@ function createI3SMeshTilesetConversionSource(tileset, options = {}) {
3536
3679
  yield {
3537
3680
  id: `${item.tile.id}/${entry.index}`,
3538
3681
  origin: [0, 0, 0],
3682
+ sourceLayerType: metadata.tileset.layerType,
3539
3683
  mesh,
3540
3684
  normalReferenceFrame: content.normalReferenceFrame === "vertex-reference-frame" ? "vertex-reference-frame" : "earth-centered",
3541
3685
  features,
@@ -3546,3 +3690,1281 @@ function createI3SMeshTilesetConversionSource(tileset, options = {}) {
3546
3690
  }
3547
3691
  };
3548
3692
  }
3693
+
3694
+ // src/v5/point-cloud-tileset.ts
3695
+ var import_point_cloud_tile_source = require("@loaders.gl/tiles/point-cloud-tile-source");
3696
+ var import_core29 = require("@loaders.gl/tile-converter/v5/core");
3697
+ async function convertPointCloudToTileset(input, options) {
3698
+ const reference = options.spatialReference;
3699
+ if (!reference || reference.status !== "native" || reference.sourceCrs !== "EPSG:4978" || reference.targetCrs && reference.targetCrs !== "EPSG:4978" || reference.coordinateFrame !== "geocentric" || reference.axisOrder !== "xyz" || reference.heightReference !== "ellipsoidal" || reference.verticalUnitScale !== 1 || reference.units?.some((unit) => unit !== "meter")) {
3700
+ throw new import_core29.TileConversionError(
3701
+ "POINT_CLOUD_TILESET_FRAME_UNSUPPORTED",
3702
+ "Point authoring requires explicit native EPSG:4978 xyz meters and ellipsoidal heights"
3703
+ );
3704
+ }
3705
+ if (options.archive && (!Number.isSafeInteger(options.archive.maxArchiveBytes) || options.archive.maxArchiveBytes < 0)) {
3706
+ throw new import_core29.TileConversionError(
3707
+ "INVALID_ARCHIVE_BYTE_LIMIT",
3708
+ "Archive byte limit must be a non-negative safe integer"
3709
+ );
3710
+ }
3711
+ options.signal?.throwIfAborted();
3712
+ const sink = createPointCloudTilesetSink(options);
3713
+ let source;
3714
+ try {
3715
+ source = new import_point_cloud_tile_source.PointCloudTileSource(input, {
3716
+ pointCloudTiler: { ...options.tiling, signal: options.signal, spatialReference: reference }
3717
+ });
3718
+ const { pointCount } = await source.getMetadata();
3719
+ const report = await convertPointCloudSource(source, {
3720
+ sink,
3721
+ signal: options.signal,
3722
+ onProgress: options.onProgress,
3723
+ measureInputBytes: (tile) => {
3724
+ const table = tile.content?.data.data;
3725
+ return table ? table.schema.fields.reduce(
3726
+ (bytes, _field, index) => bytes + (table.getChildAt(index)?.byteLength ?? 0),
3727
+ 0
3728
+ ) : 0;
3729
+ },
3730
+ getTileEncodingOptions: (tile) => ({
3731
+ rtcCenter: [
3732
+ tile.header.boundingVolume.center[0],
3733
+ tile.header.boundingVolume.center[1],
3734
+ tile.header.boundingVolume.center[2]
3735
+ ],
3736
+ maxPositionError: options.geometricError
3737
+ })
3738
+ });
3739
+ options.signal?.throwIfAborted();
3740
+ const files = sink.getFiles();
3741
+ const archive = options.archive ? await createTileConversionArchive(files, {
3742
+ format: "3tz",
3743
+ maxArchiveBytes: options.archive.maxArchiveBytes,
3744
+ signal: options.signal
3745
+ }) : void 0;
3746
+ options.signal?.throwIfAborted();
3747
+ return { pointCount, report, files, archive };
3748
+ } catch (error) {
3749
+ await sink.abort(error);
3750
+ throw error;
3751
+ } finally {
3752
+ source?.close();
3753
+ }
3754
+ }
3755
+
3756
+ // src/v5/point-cloud-file.ts
3757
+ var import_copc = require("@loaders.gl/copc");
3758
+ var import_potree = require("@loaders.gl/potree");
3759
+ var import_schema_utils5 = require("@loaders.gl/schema-utils");
3760
+ var import_zip = require("@loaders.gl/zip");
3761
+ async function encodePotreeArchive(input, options) {
3762
+ validateLimit(options.maxArchiveBytes);
3763
+ const dataset = await (0, import_potree.encodePotreeDataset)(input, { potree: options.potree });
3764
+ return encodePotreeDatasetArchive(dataset, options.maxArchiveBytes, options.potree?.signal);
3765
+ }
3766
+ async function encodePotreeDatasetArchive(dataset, maxArchiveBytes, signal) {
3767
+ validateLimit(maxArchiveBytes);
3768
+ for (const path of dataset.files.keys()) validatePath(path);
3769
+ signal?.throwIfAborted();
3770
+ const archive = await (0, import_zip.encodeZip)(Object.fromEntries(dataset.files), {
3771
+ compression: "store",
3772
+ zip64: "never",
3773
+ maxEntries: 65535,
3774
+ maxArchiveBytes: BigInt(maxArchiveBytes),
3775
+ signal
3776
+ });
3777
+ signal?.throwIfAborted();
3778
+ return new Blob([archive], { type: "application/zip" });
3779
+ }
3780
+ async function openPointCloudFile(input, options) {
3781
+ validateLimit(options.maxFileBytes);
3782
+ if (input.size > options.maxFileBytes) throw new RangeError("Point file input budget exceeded");
3783
+ options.signal?.throwIfAborted();
3784
+ if (options.format === "copc") {
3785
+ const source2 = import_copc.COPCSourceLoader.createDataSource(input, options.sourceOptions || {});
3786
+ try {
3787
+ await source2.metadata;
3788
+ options.signal?.throwIfAborted();
3789
+ return { source: source2, close: () => source2.close() };
3790
+ } catch (error) {
3791
+ await source2.close();
3792
+ throw error;
3793
+ }
3794
+ }
3795
+ if (options.format !== "potree-zip") throw new TypeError("Unsupported point file format");
3796
+ const maxEntries = options.maxEntries ?? 1e5;
3797
+ const maxResourceBytes = options.maxResourceBytes ?? 64 * 1024 * 1024;
3798
+ validateLimit(maxEntries);
3799
+ validateLimit(maxResourceBytes);
3800
+ const controller = new AbortController();
3801
+ const signals = [controller.signal, options.signal, options.sourceOptions?.potree?.signal].filter(
3802
+ (value) => Boolean(value)
3803
+ );
3804
+ const signal = AbortSignal.any(signals);
3805
+ let archive;
3806
+ const entries = /* @__PURE__ */ new Map();
3807
+ let source;
3808
+ try {
3809
+ archive = await (0, import_zip.openZipArchive)(input, {
3810
+ maxArchiveBytes: BigInt(options.maxFileBytes),
3811
+ maxEntries,
3812
+ maxReadBytes: Math.max(65557, maxResourceBytes),
3813
+ maxCompressedBytes: BigInt(maxResourceBytes),
3814
+ maxDecodedBytes: BigInt(maxResourceBytes),
3815
+ signal
3816
+ });
3817
+ for await (const entry of archive.entries()) {
3818
+ if (entry.directory) continue;
3819
+ validatePath(entry.path);
3820
+ if (entry.compression !== "store")
3821
+ throw new Error("Potree bundles require unencrypted STORE entries");
3822
+ entries.set(entry.path, entry);
3823
+ }
3824
+ const candidates = ["metadata.json", "cloud.js"].filter((path) => entries.has(path));
3825
+ const metadataPath = options.metadataPath || (candidates.length === 1 ? candidates[0] : "");
3826
+ validatePath(metadataPath);
3827
+ if (!entries.has(metadataPath) || !/(^|\/)(metadata\.json|cloud\.js)$/.test(metadataPath))
3828
+ throw new Error("Potree ZIP requires one metadata entry or an explicit metadataPath");
3829
+ const prefix = "https://point-bundle.loaders.gl.invalid/";
3830
+ const fetchResource = async (resource, request = {}) => {
3831
+ signal.throwIfAborted();
3832
+ request.signal?.throwIfAborted();
3833
+ const url = new URL(resource);
3834
+ if (url.origin !== new URL(prefix).origin || url.search || url.hash)
3835
+ throw new Error("Resource outside the Potree bundle");
3836
+ const path = decodeURIComponent(url.pathname.slice(1));
3837
+ validatePath(path);
3838
+ const entry = entries.get(path);
3839
+ if (!entry) throw new Error(`Missing Potree bundle resource: ${path}`);
3840
+ const range = new Headers(request.headers).get("range");
3841
+ const match = range?.match(/^bytes=(\d+)-(\d+)$/);
3842
+ if (range && !match) throw new RangeError("Unsupported Potree bundle byte range");
3843
+ const begin = match ? Number(match[1]) : 0;
3844
+ const end = match ? Number(match[2]) + 1 : Number(entry.size);
3845
+ if (!Number.isSafeInteger(begin) || !Number.isSafeInteger(end) || begin < 0 || end < begin || BigInt(end) > entry.size || end - begin > maxResourceBytes)
3846
+ throw new RangeError("Potree bundle resource/range budget exceeded");
3847
+ const headers = new Headers({
3848
+ "content-length": String(end - begin),
3849
+ "accept-ranges": "bytes"
3850
+ });
3851
+ if (match) headers.set("content-range", `bytes ${begin}-${end - 1}/${entry.size}`);
3852
+ const bytes = match ? await archive.readRange(entry, {
3853
+ start: BigInt(begin),
3854
+ end: BigInt(end),
3855
+ signal: request.signal ?? void 0
3856
+ }) : await archive.read(entry, { signal: request.signal ?? void 0 });
3857
+ const response = new Response(bytes, {
3858
+ status: match ? 206 : 200,
3859
+ headers
3860
+ });
3861
+ Object.defineProperty(response, "url", { value: url.href });
3862
+ signal.throwIfAborted();
3863
+ return response;
3864
+ };
3865
+ source = import_potree.PotreeSourceLoader.createDataSource(`${prefix}${metadataPath}`, {
3866
+ ...options.sourceOptions,
3867
+ core: { ...options.sourceOptions?.core, fetch: fetchResource },
3868
+ potree: { ...options.sourceOptions?.potree, signal }
3869
+ });
3870
+ await source.initialize();
3871
+ signal.throwIfAborted();
3872
+ return {
3873
+ source,
3874
+ async close() {
3875
+ controller.abort();
3876
+ source?.close();
3877
+ await archive?.close();
3878
+ }
3879
+ };
3880
+ } catch (error) {
3881
+ controller.abort();
3882
+ source?.close();
3883
+ await archive?.close();
3884
+ throw error;
3885
+ }
3886
+ }
3887
+ function extractMeshVertices(mesh, options) {
3888
+ validateLimit(options.maxPoints);
3889
+ const position = mesh.attributes.POSITION;
3890
+ const pointCount = position?.value.length / 3;
3891
+ if (mesh.topology !== "triangle-list" || mesh.mode !== 4 || !position || position.size !== 3 || position.normalized || position.transform || position.byteStride || position.byteOffset || !(position.value instanceof Float32Array || position.value instanceof Float64Array) || !Number.isSafeInteger(pointCount) || pointCount > options.maxPoints || position.value.some((value) => !Number.isFinite(value)))
3892
+ throw new Error(
3893
+ "Mesh vertex harvesting requires packed finite triangle XYZ within the point budget"
3894
+ );
3895
+ const attributes = { POSITION: position };
3896
+ for (const name of options.attributes) {
3897
+ const attribute = mesh.attributes[name];
3898
+ if (!attribute || attribute.transform || attribute.byteStride || attribute.byteOffset || !Number.isSafeInteger(attribute.size) || attribute.size < 1 || attribute.value.length !== pointCount * attribute.size)
3899
+ throw new Error(`Invalid selected mesh vertex attribute: ${name}`);
3900
+ attributes[name] = attribute;
3901
+ }
3902
+ return { topology: "point-list", mode: 0, attributes, schema: (0, import_schema_utils5.deduceMeshSchema)(attributes) };
3903
+ }
3904
+ function validatePath(path) {
3905
+ if (!path || !/^[A-Za-z0-9_.\/-]+$/.test(path) || path.split("/").some((part) => !part || part === "." || part === ".."))
3906
+ throw new Error("Potree bundle paths must be canonical relative ASCII paths");
3907
+ }
3908
+ function validateLimit(value) {
3909
+ if (!Number.isSafeInteger(value) || value < 1)
3910
+ throw new RangeError("Point file limits must be positive safe integers");
3911
+ }
3912
+
3913
+ // src/v5/point-cloud-potree.ts
3914
+ var import_schema_utils6 = require("@loaders.gl/schema-utils");
3915
+ var import_potree2 = require("@loaders.gl/potree");
3916
+ async function convertPointCloudToPotree(input, options) {
3917
+ if (typeof options.projection !== "string" || !options.projection.trim()) {
3918
+ throw new Error("Potree authoring requires an explicit native CRS declaration");
3919
+ }
3920
+ for (const limit of [
3921
+ options.maxGatheredBytes,
3922
+ options.maxOutputBytes,
3923
+ options.archive?.maxArchiveBytes
3924
+ ]) {
3925
+ if (limit !== void 0 && (!Number.isSafeInteger(limit) || limit < 1)) {
3926
+ throw new RangeError("Potree authoring byte limits must be positive safe integers");
3927
+ }
3928
+ }
3929
+ if (options.maxGatheredBytes === void 0 || options.maxOutputBytes === void 0) {
3930
+ throw new RangeError("Potree authoring requires gathered and encoded byte limits");
3931
+ }
3932
+ options.signal?.throwIfAborted();
3933
+ const tiler = new import_schema_utils6.PointCloudTiler(input, {
3934
+ ...options.tiling,
3935
+ maxNodes: Math.min(options.tiling?.maxNodes ?? 1e5, options.potree?.maxNodes ?? 1e5),
3936
+ maxTileBytes: Math.min(
3937
+ options.tiling?.maxTileBytes ?? 64 * 1024 * 1024,
3938
+ options.maxGatheredBytes
3939
+ ),
3940
+ signal: options.signal
3941
+ });
3942
+ const nodes = [];
3943
+ let gatheredBytes = 0;
3944
+ let pointByteLength = 0;
3945
+ let pointCount;
3946
+ try {
3947
+ const pending = [await tiler.getRootNode()];
3948
+ pointCount = tiler.pointCount;
3949
+ while (pending.length) {
3950
+ options.signal?.throwIfAborted();
3951
+ const node = pending.pop();
3952
+ if (node.pointCount * pointByteLength > options.maxGatheredBytes - gatheredBytes) {
3953
+ throw new RangeError("Potree gathered node byte budget exceeded");
3954
+ }
3955
+ const mesh = await tiler.getTileMesh(node.id);
3956
+ const nodeBytes = Array.from(
3957
+ new Set(Object.values(mesh.attributes).map((attribute) => attribute.value.buffer))
3958
+ ).reduce((bytes, buffer) => bytes + buffer.byteLength, 0);
3959
+ if (nodeBytes > options.maxGatheredBytes - gatheredBytes) {
3960
+ throw new RangeError("Potree gathered node byte budget exceeded");
3961
+ }
3962
+ if (node.pointCount) pointByteLength = nodeBytes / node.pointCount;
3963
+ gatheredBytes += nodeBytes;
3964
+ nodes.push({ id: node.id, mesh });
3965
+ const children = await tiler.getChildNodes(node.id);
3966
+ for (let index = children.length - 1; index >= 0; index--) pending.push(children[index]);
3967
+ }
3968
+ } finally {
3969
+ tiler.close();
3970
+ }
3971
+ const dataset = await (0, import_potree2.encodePotreeDataset)(nodes, {
3972
+ potree: {
3973
+ ...options.potree,
3974
+ projection: options.projection,
3975
+ maxOutputBytes: options.maxOutputBytes,
3976
+ signal: options.signal
3977
+ }
3978
+ });
3979
+ const archive = options.archive ? await encodePotreeDatasetArchive(dataset, options.archive.maxArchiveBytes, options.signal) : void 0;
3980
+ options.signal?.throwIfAborted();
3981
+ return { pointCount, nodeCount: nodes.length, dataset, archive };
3982
+ }
3983
+
3984
+ // src/v5/mesh-hierarchy.ts
3985
+ var import_core30 = require("@math.gl/core");
3986
+ var import_tiles3 = require("@loaders.gl/tiles");
3987
+ var import_core31 = require("@loaders.gl/tile-converter/v5/core");
3988
+ function prepareMeshHierarchy(inspection, resourceIds) {
3989
+ const rejectHierarchy = (message) => {
3990
+ throw new import_core31.TileConversionError("MESH_HIERARCHY_UNSUPPORTED", message);
3991
+ };
3992
+ if (!inspection.documents?.length || inspection.documents.length > 32)
3993
+ rejectHierarchy(
3994
+ "Hierarchy preservation requires expanded metadata inspection of at most 32 explicit document placements"
3995
+ );
3996
+ if (!resourceIds.length || resourceIds.length > 64 || new Set(resourceIds).size !== resourceIds.length || resourceIds.length !== inspection.resources.length || inspection.resources.some((resource) => !resourceIds.includes(resource.resourceId)))
3997
+ rejectHierarchy(
3998
+ "Hierarchy preservation requires every content placement, without duplicates (at most 64)"
3999
+ );
4000
+ const entryTransform = new import_core30.Matrix4();
4001
+ if (inspection.documents[0].parentResourceId !== void 0 || !Array.isArray(inspection.documents[0].parentTransform) || inspection.documents[0].parentTransform.length !== 16 || inspection.documents[0].parentTransform.some((value, index) => value !== entryTransform[index]))
4002
+ rejectHierarchy("The entry document must have identity parent placement");
4003
+ const documents = inspection.documents.map((placement) => placement.tileset);
4004
+ const document = documents[0];
4005
+ const externalDocuments = /* @__PURE__ */ new Map();
4006
+ for (let index = 1; index < inspection.documents.length; index++) {
4007
+ const identifier = inspection.documents[index].parentResourceId;
4008
+ if (!identifier || externalDocuments.has(identifier))
4009
+ rejectHierarchy(
4010
+ "Every external document requires a distinct inspected parent content placement"
4011
+ );
4012
+ externalDocuments.set(identifier, index);
4013
+ }
4014
+ for (const document2 of documents) {
4015
+ if (document2.metadata || document2.groups || document2.schema || document2.schemaUri || Object.keys(document2.extensions ?? {}).some((name) => name !== "EXT_geospatial_crs") || (document2.extensionsRequired ?? []).some((name) => name !== "EXT_geospatial_crs"))
4016
+ rejectHierarchy(
4017
+ "Dataset metadata and unmapped extensions require a separate hierarchy profile"
4018
+ );
4019
+ if (!["1.0", "1.1"].includes(document2.asset?.version ?? ""))
4020
+ rejectHierarchy("Hierarchy preservation requires a 3D Tiles 1.0 or 1.1 document");
4021
+ if (document2.geometricError !== void 0 && (typeof document2.geometricError !== "number" || !Number.isFinite(document2.geometricError) || document2.geometricError < 0))
4022
+ rejectHierarchy("Dataset geometricError must be finite nonnegative meters");
4023
+ }
4024
+ const spatialReferences = documents.map(
4025
+ (document2) => (0, import_tiles3.get3DTilesSpatialReference)(document2)
4026
+ );
4027
+ const spatialReference = (0, import_core31.createTiles3DConversionSpatialContext)(
4028
+ spatialReferences[0]
4029
+ ).spatialReference;
4030
+ if (spatialReference.status !== "native" || spatialReference.sourceCrs !== "EPSG:4978" || spatialReference.heightReference !== "ellipsoidal" || spatialReference.axisOrder !== "xyz" || spatialReference.verticalUnitScale !== 1 || spatialReference.units?.some((unit) => unit !== "meter"))
4031
+ rejectHierarchy(
4032
+ "Hierarchy preservation requires native EPSG:4978 xyz meters and ellipsoidal heights"
4033
+ );
4034
+ for (const reference of spatialReferences.slice(1)) {
4035
+ const inherited = reference.crs.state === "absent" && reference.coordinateEpoch === void 0 && !reference.verticalCrs && !reference.warnings.length;
4036
+ const context = (0, import_core31.createTiles3DConversionSpatialContext)(inherited ? spatialReference : reference);
4037
+ if (context.spatialReference.status !== "native" || context.spatialReference.sourceCrs !== spatialReference.sourceCrs || context.spatialReference.heightReference !== spatialReference.heightReference || context.spatialReference.axisOrder !== spatialReference.axisOrder || context.spatialReference.verticalUnitScale !== 1 || context.spatialReference.units?.some((unit) => unit !== "meter") || context.spatialReference.coordinateEpoch !== spatialReference.coordinateEpoch)
4038
+ rejectHierarchy("External document CRS and coordinate epoch must match the entry frame");
4039
+ }
4040
+ const nodes = [];
4041
+ const visitedDocuments = /* @__PURE__ */ new Set([0]);
4042
+ const stack = [
4043
+ {
4044
+ tile: document.root,
4045
+ documentIndex: 0,
4046
+ prefix: "",
4047
+ path: [],
4048
+ parent: -1,
4049
+ transform: new import_core30.Matrix4(),
4050
+ refine: void 0
4051
+ }
4052
+ ];
4053
+ while (stack.length) {
4054
+ const {
4055
+ tile,
4056
+ path,
4057
+ parent,
4058
+ documentIndex,
4059
+ prefix,
4060
+ transform: parentTransform,
4061
+ refine: inheritedRefine
4062
+ } = stack.pop();
4063
+ const document2 = documents[documentIndex];
4064
+ if (nodes.length >= 256) rejectHierarchy("Hierarchy preservation supports at most 256 tiles");
4065
+ if (!tile || typeof tile !== "object" || Array.isArray(tile) || tile.implicitTiling !== void 0 || tile.extensions !== void 0 || tile.metadata !== void 0 || tile.viewerRequestVolume !== void 0 || tile.content !== void 0 && tile.contents !== void 0 || tile.contents !== void 0 && (!Array.isArray(tile.contents) || !tile.contents.length || document2.asset?.version !== "1.1") || tile.children !== void 0 && !Array.isArray(tile.children))
4066
+ rejectHierarchy(
4067
+ "Hierarchy tiles require explicit children, exclusive content/contents declarations and no unmapped metadata, extensions or request volumes"
4068
+ );
4069
+ const refine = tile.refine ?? inheritedRefine;
4070
+ if (refine !== "ADD" && refine !== "REPLACE")
4071
+ rejectHierarchy("Root refinement must be explicit ADD or REPLACE; children may inherit");
4072
+ const matrix = tile.transform;
4073
+ if (matrix !== void 0 && (!Array.isArray(matrix) || matrix.length !== 16 || matrix.some((value) => typeof value !== "number" || !Number.isFinite(value)) || matrix[3] !== 0 || matrix[7] !== 0 || matrix[11] !== 0 || matrix[15] !== 1))
4074
+ rejectHierarchy("Hierarchy transforms must be finite affine matrices");
4075
+ const transform = new import_core30.Matrix4(parentTransform).multiplyRight(matrix ?? new import_core30.Matrix4());
4076
+ const columns = [0, 1, 2].map(
4077
+ (column) => [0, 1, 2].reduce((sum, row) => sum + Math.abs(transform[column * 4 + row]), 0)
4078
+ );
4079
+ const rows = [0, 1, 2].map(
4080
+ (row) => [0, 1, 2].reduce((sum, column) => sum + Math.abs(transform[column * 4 + row]), 0)
4081
+ );
4082
+ const scale = document2.asset?.version === "1.0" ? 1 : Math.sqrt(Math.max(...columns)) * Math.sqrt(Math.max(...rows));
4083
+ const geometricError2 = (tile.geometricError ?? NaN) * scale;
4084
+ if (typeof tile.geometricError !== "number" || (tile.geometricError ?? -1) < 0 || !Number.isFinite(geometricError2) || Array.from(transform).some((value) => !Number.isFinite(value)))
4085
+ rejectHierarchy("Every hierarchy tile must declare a finite nonnegative metric error");
4086
+ const contents = tile.contents ?? (tile.content ? [tile.content] : []);
4087
+ const identifiers = contents.map(
4088
+ (_, contentIndex) => `${prefix}tile-${path.length ? path.join("-") : "root"}-content-${contentIndex}`
4089
+ );
4090
+ const externalIndexes = identifiers.map((identifier) => externalDocuments.get(identifier));
4091
+ const externalIndex = externalIndexes.find((index2) => index2 !== void 0);
4092
+ for (const [contentIndex, content] of contents.entries()) {
4093
+ if (!content || typeof content !== "object" || Array.isArray(content) || content.metadata !== void 0 || content.group !== void 0 || content.extensions !== void 0)
4094
+ rejectHierarchy("Content metadata, groups and extensions require a separate mapping");
4095
+ if (externalIndexes[contentIndex] === void 0 && !inspection.resources.some(
4096
+ (resource) => resource.resourceId === identifiers[contentIndex] && resource.documentIndex === documentIndex && resource.uri === (content.uri ?? content.url) && resource.tilePath.length === path.length && resource.tilePath.every((value, index2) => value === path[index2])
4097
+ ))
4098
+ rejectHierarchy("Hierarchy content must match the inspected placement");
4099
+ }
4100
+ const resourceId = identifiers[0];
4101
+ if (externalIndex !== void 0) {
4102
+ if (contents.length !== 1 || tile.contents !== void 0 || tile.children?.length || visitedDocuments.has(externalIndex))
4103
+ rejectHierarchy(
4104
+ "External references require a single content, no source children and a unique document placement"
4105
+ );
4106
+ visitedDocuments.add(externalIndex);
4107
+ const placement = inspection.documents[externalIndex];
4108
+ if (!Array.isArray(placement.parentTransform) || placement.parentTransform.length !== 16 || placement.parentTransform.some((value, index2) => value !== transform[index2]))
4109
+ rejectHierarchy("External document placement must match its referencing tile transform");
4110
+ const contentUri = tile.content?.uri ?? tile.content?.url;
4111
+ if (!contentUri || !new URL(contentUri, inspection.documents[documentIndex].url).pathname.toLowerCase().endsWith(".json"))
4112
+ rejectHierarchy("External documents require an inspected .json content reference");
4113
+ }
4114
+ const index = nodes.length;
4115
+ nodes.push({
4116
+ children: [],
4117
+ resourceIds: externalIndex === void 0 ? identifiers : [],
4118
+ geometricError: Math.max(
4119
+ geometricError2,
4120
+ externalIndex === void 0 ? 0 : documents[externalIndex].geometricError ?? 0
4121
+ ),
4122
+ refine
4123
+ });
4124
+ if (parent !== -1) nodes[parent].children.push(index);
4125
+ if (externalIndex !== void 0) {
4126
+ stack.push({
4127
+ tile: documents[externalIndex].root,
4128
+ path: [],
4129
+ parent: index,
4130
+ documentIndex: externalIndex,
4131
+ prefix: resourceId + "/",
4132
+ transform,
4133
+ refine: void 0
4134
+ });
4135
+ }
4136
+ if (nodes.length + stack.length + (tile.children?.length ?? 0) > 256)
4137
+ rejectHierarchy("Hierarchy preservation supports at most 256 tiles");
4138
+ for (let child = (tile.children?.length ?? 0) - 1; child >= 0; child--)
4139
+ stack.push({
4140
+ tile: tile.children[child],
4141
+ documentIndex,
4142
+ prefix,
4143
+ path: [...path, child],
4144
+ parent: index,
4145
+ transform,
4146
+ refine
4147
+ });
4148
+ }
4149
+ if (visitedDocuments.size !== documents.length || nodes.reduce((count, node) => count + node.resourceIds.length, 0) !== resourceIds.length || nodes.some((node) => !node.resourceIds.length && !node.children.length))
4150
+ rejectHierarchy("Contentless leaves or unmatched content placements cannot be preserved");
4151
+ const geometricError = document.geometricError ?? nodes[0].geometricError;
4152
+ if (typeof geometricError !== "number" || !Number.isFinite(geometricError) || geometricError < 0)
4153
+ rejectHierarchy("Dataset geometricError must be finite nonnegative meters");
4154
+ return {
4155
+ nodes,
4156
+ geometricError: Math.max(geometricError, nodes[0].geometricError),
4157
+ coordinateEpoch: spatialReference.coordinateEpoch
4158
+ };
4159
+ }
4160
+ function createMeshHierarchyTilesetSink(options) {
4161
+ const hierarchy = prepareMeshHierarchy(options.inspection, options.resourceIds);
4162
+ const extensions = structuredClone(
4163
+ options.inspection.tileset.extensions
4164
+ );
4165
+ const extensionsRequired = structuredClone(
4166
+ options.inspection.tileset.extensionsRequired
4167
+ );
4168
+ const { maxPositionError } = options;
4169
+ if (!Number.isFinite(maxPositionError) || maxPositionError < 0 || hierarchy.nodes.some((node) => !Number.isFinite(node.geometricError + maxPositionError)) || !Number.isFinite(hierarchy.geometricError + maxPositionError))
4170
+ throw new import_core31.TileConversionError(
4171
+ "MESH_GEOMETRIC_ERROR_INVALID",
4172
+ "Hierarchy position allowance and resulting errors must be finite nonnegative meters"
4173
+ );
4174
+ const memory = (0, import_core31.createBoundedMemoryTileConversionSink)(options);
4175
+ const placements = /* @__PURE__ */ new Map();
4176
+ const completedContents = /* @__PURE__ */ new Set();
4177
+ const primitiveIdentifiers = /* @__PURE__ */ new Set();
4178
+ let state = "open";
4179
+ let completed = false;
4180
+ return {
4181
+ /** Exposes files only after complete finalization. */
4182
+ getFiles: () => completed ? memory.getFiles() : [],
4183
+ /** Retains ordered codec outputs and requires a terminal primitive for each content. */
4184
+ async write(resource, signal) {
4185
+ signal?.throwIfAborted();
4186
+ const identifier = resource.contentResourceId;
4187
+ const { index: primitiveIndex, last: lastPrimitive } = resource.contentPrimitive ?? {
4188
+ index: 0,
4189
+ last: true
4190
+ };
4191
+ if (state !== "open" || !identifier || completedContents.has(identifier) || primitiveIdentifiers.has(resource.id) || primitiveIdentifiers.size >= 64 || !Number.isSafeInteger(primitiveIndex) || primitiveIndex !== (placements.get(identifier)?.length ?? 0) || typeof lastPrimitive !== "boolean" || !hierarchy.nodes.some((node2) => node2.resourceIds.includes(identifier)))
4192
+ throw new import_core31.TileConversionError(
4193
+ "MESH_HIERARCHY_PLACEMENT_INVALID",
4194
+ "Hierarchy writes require ordered, distinct primitives for declared contents before close (at most 64)"
4195
+ );
4196
+ if (resource.spatialReference.coordinateEpoch !== hierarchy.coordinateEpoch)
4197
+ throw new import_core31.TileConversionError(
4198
+ "MESH_SOURCE_EPOCH_MISMATCH",
4199
+ "Hierarchy output coordinate epoch must match its source"
4200
+ );
4201
+ const tile = createMeshTileset(resource, maxPositionError).root;
4202
+ const nodeIndex = hierarchy.nodes.findIndex((node2) => node2.resourceIds.includes(identifier));
4203
+ const node = hierarchy.nodes[nodeIndex];
4204
+ const sibling = node.resourceIds.map((identifier2) => placements.get(identifier2)?.[0]).find(Boolean);
4205
+ if (sibling && sibling.origin.some((value, axis) => value !== resource.origin[axis]))
4206
+ throw new import_core31.TileConversionError(
4207
+ "MESH_HIERARCHY_PLACEMENT_INVALID",
4208
+ "Contents on one tile must share an encoded origin"
4209
+ );
4210
+ const suffix = node.resourceIds.length > 1 ? "-" + node.resourceIds.indexOf(identifier) : "";
4211
+ const primitiveSuffix = primitiveIndex > 0 || !lastPrimitive ? "-primitive-" + primitiveIndex : "";
4212
+ const uri = `meshes/${nodeIndex}${suffix}${primitiveSuffix}.glb`;
4213
+ state = "writing";
4214
+ await memory.write(
4215
+ { resourceId: uri, parts: [resource.glb], contentType: "model/gltf-binary" },
4216
+ signal
4217
+ );
4218
+ const contentPlacements = placements.get(identifier) ?? [];
4219
+ contentPlacements.push({
4220
+ transform: [...tile.transform],
4221
+ origin: [...resource.origin],
4222
+ localBoundingBox: resource.localBoundingBox.map((bound) => [...bound]),
4223
+ uri
4224
+ });
4225
+ placements.set(identifier, contentPlacements);
4226
+ primitiveIdentifiers.add(resource.id);
4227
+ if (lastPrimitive) completedContents.add(identifier);
4228
+ state = "open";
4229
+ },
4230
+ /** Publishes original topology only after every source content has completed. */
4231
+ async finalize(report) {
4232
+ if (state !== "open" || completedContents.size !== hierarchy.nodes.reduce((count, node) => count + node.resourceIds.length, 0))
4233
+ throw new import_core31.TileConversionError(
4234
+ "MESH_HIERARCHY_INCOMPLETE",
4235
+ "Finalization requires every declared hierarchy content"
4236
+ );
4237
+ state = "writing";
4238
+ const tiles = new Array(hierarchy.nodes.length);
4239
+ const bounds = new Array(hierarchy.nodes.length);
4240
+ const transforms = hierarchy.nodes.map((node) => {
4241
+ const placement = placements.get(node.resourceIds[0])?.[0];
4242
+ return placement ? new import_core30.Matrix4(placement.transform) : new import_core30.Matrix4();
4243
+ });
4244
+ for (let index = hierarchy.nodes.length - 1; index >= 0; index--) {
4245
+ const node = hierarchy.nodes[index];
4246
+ const placement = placements.get(node.resourceIds[0])?.[0];
4247
+ const minimum = [Infinity, Infinity, Infinity];
4248
+ const maximum = [-Infinity, -Infinity, -Infinity];
4249
+ const contributors = node.children.map((child) => bounds[child]);
4250
+ const nodePlacements = node.resourceIds.flatMap((identifier) => placements.get(identifier));
4251
+ for (const placement2 of nodePlacements) {
4252
+ contributors.push(
4253
+ placement2.localBoundingBox.map(
4254
+ (bound, boundIndex) => bound.map((value, axis) => {
4255
+ const padding = 8 * Number.EPSILON * Math.max(
4256
+ 1,
4257
+ Math.abs(placement2.origin[axis]),
4258
+ ...placement2.localBoundingBox.map((limit) => Math.abs(limit[axis]))
4259
+ );
4260
+ return value + placement2.origin[axis] + (boundIndex ? padding : -padding);
4261
+ })
4262
+ )
4263
+ );
4264
+ }
4265
+ for (const [lower, upper] of contributors)
4266
+ for (let axis = 0; axis < 3; axis++) {
4267
+ minimum[axis] = Math.min(minimum[axis], lower[axis]);
4268
+ maximum[axis] = Math.max(maximum[axis], upper[axis]);
4269
+ }
4270
+ if ([...minimum, ...maximum].some((value) => !Number.isFinite(value)))
4271
+ throw new import_core31.TileConversionError(
4272
+ "MESH_TILESET_BOUNDS_INVALID",
4273
+ "Hierarchy absolute bounds must remain finite"
4274
+ );
4275
+ bounds[index] = [minimum, maximum];
4276
+ const inverse = new import_core30.Matrix4(transforms[index]).invert();
4277
+ const center = Array.from(
4278
+ inverse.transformAsPoint(minimum.map((value, axis) => value / 2 + maximum[axis] / 2))
4279
+ );
4280
+ const halfSize = minimum.map((value, axis) => maximum[axis] / 2 - value / 2);
4281
+ const halfAxes = [0, 1, 2].flatMap(
4282
+ (axis) => Array.from(
4283
+ inverse.transformAsVector(
4284
+ halfSize.map((value, coordinate) => coordinate === axis ? value : 0)
4285
+ )
4286
+ )
4287
+ );
4288
+ if ([...center, ...halfAxes].some((value) => !Number.isFinite(value)))
4289
+ throw new import_core31.TileConversionError(
4290
+ "MESH_TILESET_BOUNDS_INVALID",
4291
+ "Hierarchy local bounds must remain finite"
4292
+ );
4293
+ tiles[index] = {
4294
+ transform: Array.from(transforms[index]),
4295
+ boundingVolume: { box: [...center, ...halfAxes] },
4296
+ geometricError: node.geometricError + maxPositionError,
4297
+ refine: node.refine,
4298
+ ...nodePlacements.length > 1 ? { contents: nodePlacements.map((placement2) => ({ uri: placement2.uri })) } : placement ? { content: { uri: placement.uri } } : {},
4299
+ ...node.children.length ? {
4300
+ children: node.children.map((child) => {
4301
+ tiles[child].transform = Array.from(
4302
+ new import_core30.Matrix4(inverse).multiplyRight(transforms[child])
4303
+ );
4304
+ if (tiles[child].transform.some((value) => !Number.isFinite(value)))
4305
+ throw new import_core31.TileConversionError(
4306
+ "MESH_TILESET_BOUNDS_INVALID",
4307
+ "Relative hierarchy transforms must remain finite"
4308
+ );
4309
+ return tiles[child];
4310
+ })
4311
+ } : {}
4312
+ };
4313
+ }
4314
+ const json = JSON.stringify({
4315
+ asset: { version: "1.1" },
4316
+ ...extensions && Object.keys(extensions).length ? { extensions, extensionsUsed: Object.keys(extensions) } : {},
4317
+ ...extensionsRequired?.length ? { extensionsRequired } : {},
4318
+ geometricError: hierarchy.geometricError + maxPositionError,
4319
+ root: tiles[0]
4320
+ });
4321
+ await memory.write({
4322
+ resourceId: "tileset.json",
4323
+ parts: [json],
4324
+ contentType: "application/json"
4325
+ });
4326
+ await memory.finalize(report);
4327
+ state = "closed";
4328
+ completed = true;
4329
+ },
4330
+ /** Discards retained resources and hides all files after failure. */
4331
+ async abort(error) {
4332
+ state = "closed";
4333
+ completed = false;
4334
+ placements.clear();
4335
+ completedContents.clear();
4336
+ primitiveIdentifiers.clear();
4337
+ await memory.abort(error);
4338
+ }
4339
+ };
4340
+ }
4341
+
4342
+ // src/v5/selected-mesh-source.ts
4343
+ var import_core32 = require("@loaders.gl/core");
4344
+ var import_d_tiles2 = require("@loaders.gl/3d-tiles");
4345
+ var import_tiles4 = require("@loaders.gl/tiles");
4346
+ var import_core33 = require("@loaders.gl/tile-converter/v5/core");
4347
+ var import_adapters = require("@loaders.gl/tile-converter/v5/adapters");
4348
+ var SELECTED_MESH_LIMITS = {
4349
+ /** Root JSON plus selected content transport bytes; also the decoded geometry/image/feature byte gate. */
4350
+ maxInputBytes: 16 * 1024 * 1024,
4351
+ /** Maximum declared content placements inspected. */
4352
+ maxInputResources: 1e3,
4353
+ /** Maximum inspected root/external document placements. */
4354
+ maxTilesetDocuments: 32,
4355
+ /** Maximum explicitly selected content placements in one partial archive. */
4356
+ maxSelectedResources: 64,
4357
+ /** Maximum primitive placements authored across all selected contents. */
4358
+ maxMeshResources: 64,
4359
+ /** Maximum retained file bytes and final archive bytes. */
4360
+ maxOutputBytes: 32 * 1024 * 1024,
4361
+ /** Maximum measured reconstruction error in meters. */
4362
+ maxPositionError: 0.01
4363
+ };
4364
+ async function createSelectedMeshConversionSource(inspection, options) {
4365
+ const {
4366
+ resourceIds,
4367
+ signal,
4368
+ dracoLibraryOptions,
4369
+ features,
4370
+ preserveHierarchy,
4371
+ fetcher = fetch
4372
+ } = options;
4373
+ const maxInputBytes = options.maxInputBytes ?? SELECTED_MESH_LIMITS.maxInputBytes;
4374
+ if (!Number.isSafeInteger(maxInputBytes) || maxInputBytes < 1 || maxInputBytes > SELECTED_MESH_LIMITS.maxInputBytes)
4375
+ throw new import_core33.TileConversionError(
4376
+ "INVALID_SELECTED_MESH_OPTIONS",
4377
+ "Selected mesh input budget must be a positive safe integer at most 16 MiB"
4378
+ );
4379
+ if (!resourceIds.length || resourceIds.length > SELECTED_MESH_LIMITS.maxSelectedResources || new Set(resourceIds).size !== resourceIds.length || resourceIds.some(
4380
+ (identifier) => !inspection.resources.some((resource) => resource.resourceId === identifier)
4381
+ ))
4382
+ throw new Error("Select between 1 and 64 distinct content placements.");
4383
+ if (preserveHierarchy !== void 0 && typeof preserveHierarchy !== "boolean")
4384
+ throw new import_core33.TileConversionError(
4385
+ "MESH_HIERARCHY_UNSUPPORTED",
4386
+ "preserveHierarchy must be a boolean"
4387
+ );
4388
+ if (preserveHierarchy) prepareMeshHierarchy(inspection, resourceIds);
4389
+ const descriptors = inspection.resources.filter(
4390
+ (resource) => resourceIds.includes(resource.resourceId)
4391
+ );
4392
+ const selections = descriptors.map(
4393
+ (descriptor) => (0, import_core33.selectBrowserTilesetContent)(inspection, descriptor)
4394
+ );
4395
+ const selectedContents = new Map(
4396
+ descriptors.map((descriptor, index) => [descriptor.resourceId, selections[index]])
4397
+ );
4398
+ if (!preserveHierarchy && resourceIds.length > 1 && selections.some((selection) => !selection.isLeaf))
4399
+ throw new Error(
4400
+ "Multi-selection requires leaf contents; source LOD hierarchy is not exported."
4401
+ );
4402
+ signal.throwIfAborted();
4403
+ const boundedFetcher = (0, import_adapters.createTileConversionResourceFetcher)({
4404
+ maxInputBytes,
4405
+ initialInputBytes: inspection.tilesetBytes,
4406
+ fetcher,
4407
+ signal
4408
+ });
4409
+ const qualification = createSelectedRuntime(
4410
+ inspection.tileset,
4411
+ new Uint8Array(),
4412
+ signal,
4413
+ boundedFetcher,
4414
+ inspection.rootUrl,
4415
+ dracoLibraryOptions
4416
+ );
4417
+ let spatialContext;
4418
+ try {
4419
+ await qualification.tilesetInitializationPromise;
4420
+ const metadata = await (0, import_adapters.createMeshTilesetConversionSource)(qualification).inspect(signal);
4421
+ spatialContext = (0, import_core33.createTiles3DConversionSpatialContext)(metadata.spatialReference);
4422
+ } finally {
4423
+ qualification.destroy();
4424
+ }
4425
+ const rawSource = options.source ?? (0, import_core33.createBrowserTilesetConversionSource)({
4426
+ input: inspection.rootUrl,
4427
+ ...SELECTED_MESH_LIMITS,
4428
+ maxInputBytes,
4429
+ fetcher: boundedFetcher
4430
+ });
4431
+ const selectedInspection = { ...inspection, resources: descriptors };
4432
+ const source = {
4433
+ /** Reuses inspected declarations without fetching the root again. */
4434
+ inspect: async (inspectionSignal) => {
4435
+ signal.throwIfAborted();
4436
+ inspectionSignal?.throwIfAborted();
4437
+ return selectedInspection;
4438
+ },
4439
+ /** Decodes and releases each selected placement in declaration order. */
4440
+ async *read(_inspection, readSignal) {
4441
+ const signal2 = readSignal ? AbortSignal.any([options.signal, readSignal]) : options.signal;
4442
+ signal2.throwIfAborted();
4443
+ let decodedBytes = 0;
4444
+ let primitiveCount = 0;
4445
+ const tileOrigins = /* @__PURE__ */ new Map();
4446
+ for await (const raw of rawSource.read(selectedInspection, signal2)) {
4447
+ const selection = selectedContents.get(raw.resourceId);
4448
+ if (!selection)
4449
+ throw new import_core33.TileConversionError(
4450
+ "INVALID_BROWSER_SELECTION",
4451
+ "Source yielded an uninspected content placement"
4452
+ );
4453
+ const contentUrl = raw.contentUrl ?? selection.contentUrl;
4454
+ const runtime = createSelectedRuntime(
4455
+ selection.tileset,
4456
+ raw.data,
4457
+ signal2,
4458
+ boundedFetcher,
4459
+ contentUrl,
4460
+ dracoLibraryOptions,
4461
+ spatialContext.spatialReference
4462
+ );
4463
+ try {
4464
+ await runtime.tilesetInitializationPromise;
4465
+ const meshSource = (0, import_adapters.createMeshTilesetConversionSource)(runtime, {
4466
+ unloadContent: true,
4467
+ features,
4468
+ readExternalResource: async (uri, _contentUri, resourceSignal) => {
4469
+ const response = await boundedFetcher(new URL(uri, contentUrl), {
4470
+ signal: resourceSignal
4471
+ });
4472
+ return new Uint8Array(await response.arrayBuffer());
4473
+ }
4474
+ });
4475
+ const metadata = await meshSource.inspect(signal2);
4476
+ const selectedContext = (0, import_core33.createTiles3DConversionSpatialContext)(metadata.spatialReference);
4477
+ if (selectedContext.spatialReference.coordinateEpoch !== spatialContext.spatialReference.coordinateEpoch)
4478
+ throw new import_core33.TileConversionError(
4479
+ "MESH_SOURCE_EPOCH_MISMATCH",
4480
+ "Selected content coordinate epoch must match the root; epoch transformations are unsupported"
4481
+ );
4482
+ let meshCount = 0;
4483
+ for await (const resource of meshSource.read(metadata, signal2)) {
4484
+ meshCount++;
4485
+ if (++primitiveCount > SELECTED_MESH_LIMITS.maxMeshResources)
4486
+ throw new Error("Selected contents exceed the mesh primitive placement limit.");
4487
+ decodedBytes += measureMeshBytes(resource);
4488
+ if (decodedBytes > maxInputBytes)
4489
+ throw new import_core33.TileConversionError(
4490
+ "INPUT_RESOURCE_TOO_LARGE",
4491
+ "Selected decoded geometry and encoded images exceed the aggregate input byte limit."
4492
+ );
4493
+ const tileIdentifier = raw.resourceId.replace(/content-\d+$/, "");
4494
+ if (preserveHierarchy && !tileOrigins.has(tileIdentifier))
4495
+ tileOrigins.set(tileIdentifier, [...resource.origin]);
4496
+ yield {
4497
+ ...resource,
4498
+ origin: preserveHierarchy ? tileOrigins.get(tileIdentifier) : resource.origin,
4499
+ id: `${raw.resourceId}/${resource.id}`,
4500
+ contentResourceId: raw.resourceId
4501
+ };
4502
+ }
4503
+ if (!meshCount) throw new Error("Selected content has no mesh primitive.");
4504
+ } finally {
4505
+ runtime.destroy();
4506
+ }
4507
+ }
4508
+ }
4509
+ };
4510
+ return {
4511
+ source,
4512
+ spatialContext,
4513
+ geometricError: Math.max(...selections.map((selection) => selection.geometricError)) + SELECTED_MESH_LIMITS.maxPositionError
4514
+ };
4515
+ }
4516
+ var SelectedTiles3DSource = class extends import_tiles4.Tiles3DSource {
4517
+ /** Parent frame; explicit, unknown or unresolved child declarations are never replaced. */
4518
+ inheritedSpatialReference;
4519
+ /** Creates a selected source with optional external-document frame inheritance. */
4520
+ constructor(input, options, inheritedSpatialReference) {
4521
+ super(input, options);
4522
+ this.inheritedSpatialReference = inheritedSpatialReference;
4523
+ }
4524
+ /** Returns source metadata with the same absent-CRS inheritance used by external tilesets. */
4525
+ getMetadata() {
4526
+ const metadata = super.getMetadata();
4527
+ const reference = metadata.spatialReference;
4528
+ if (this.inheritedSpatialReference && reference?.crs.state === "absent" && reference.coordinateEpoch === void 0 && !reference.verticalCrs && !reference.warnings.length)
4529
+ return { ...metadata, spatialReference: this.inheritedSpatialReference };
4530
+ return metadata;
4531
+ }
4532
+ };
4533
+ function createSelectedRuntime(document, data, signal, boundedFetcher, contentUrl, dracoLibraryOptions, inheritedSpatialReference) {
4534
+ const {
4535
+ modules,
4536
+ decoderProfile,
4537
+ CDN: contentDeliveryNetwork,
4538
+ useLocalLibraries
4539
+ } = dracoLibraryOptions;
4540
+ const loadOptions = {
4541
+ modules,
4542
+ core: {
4543
+ worker: false,
4544
+ fetch: boundedFetcher,
4545
+ CDN: contentDeliveryNetwork,
4546
+ useLocalLibraries
4547
+ },
4548
+ // Reuse the full decoder already bundled for output verification.
4549
+ draco: { decoderProfile },
4550
+ // Preserve authored UVs and the transform for the converter's qualified GLB writer.
4551
+ gltf: {
4552
+ loadImages: false,
4553
+ decompressMeshes: true,
4554
+ excludeExtensions: {
4555
+ KHR_texture_transform: false,
4556
+ // Only Draco input compression is qualified by this conversion profile.
4557
+ EXT_meshopt_compression: false,
4558
+ KHR_meshopt_compression: false
4559
+ }
4560
+ },
4561
+ "3d-tiles": { loadGLTF: true }
4562
+ };
4563
+ return new import_tiles4.Tileset3D(
4564
+ new SelectedTiles3DSource(
4565
+ {
4566
+ url: contentUrl,
4567
+ loader: import_d_tiles2.Tiles3DLoader,
4568
+ resolver: {
4569
+ /** Normalizes the selected hierarchy through the format loader, retaining ancestor transforms. */
4570
+ loadRoot: async (_url, loader, options) => (0, import_core32.parse)(new TextEncoder().encode(JSON.stringify(document)), loader, options),
4571
+ /** Decodes only the selected bounded payload; no sibling content can be requested. */
4572
+ loadResource: async (_url, loader, options) => {
4573
+ signal.throwIfAborted();
4574
+ return (0, import_core32.parse)(data, loader, options, {
4575
+ url: contentUrl,
4576
+ baseUrl: contentUrl.startsWith("blob:") ? contentUrl : new URL(".", contentUrl).href,
4577
+ fetch: boundedFetcher,
4578
+ coreApi: import_core32.coreApi,
4579
+ _parse: import_core32.parse
4580
+ });
4581
+ }
4582
+ }
4583
+ },
4584
+ loadOptions,
4585
+ inheritedSpatialReference
4586
+ )
4587
+ );
4588
+ }
4589
+ function measureMeshBytes(resource) {
4590
+ const featureBytes = resource.features ? resource.features.triangleFeatureIndices.byteLength + resource.features.batches.reduce(
4591
+ (bytes, batch) => bytes + batch.data.schema.fields.reduce(
4592
+ (columnBytes, field) => columnBytes + (batch.data.getChild(field.name)?.byteLength ?? 0),
4593
+ 0
4594
+ ),
4595
+ 0
4596
+ ) : 0;
4597
+ return Object.values(resource.mesh.attributes).reduce(
4598
+ (bytes, attribute) => bytes + attribute.value.byteLength,
4599
+ (resource.mesh.indices?.value.byteLength ?? 0) + (resource.material?.baseColorTexture?.data.byteLength ?? 0) + featureBytes
4600
+ );
4601
+ }
4602
+
4603
+ // src/v5/archive-source.ts
4604
+ var import_loader_utils = require("@loaders.gl/loader-utils");
4605
+ var import_i3s3 = require("@loaders.gl/i3s");
4606
+ var import_d_tiles3 = require("@loaders.gl/3d-tiles");
4607
+ var import_core34 = require("@loaders.gl/tile-converter/v5/core");
4608
+ async function openTileConversionArchive(descriptor, maxInputBytes, signal, fetcher = fetch) {
4609
+ signal.throwIfAborted();
4610
+ if (!["slpk", "3tz"].includes(descriptor.format))
4611
+ throw new import_core34.TileConversionError("INVALID_ARCHIVE_SOURCE", "Select SLPK or 3TZ archive input");
4612
+ validateArchiveEntityTag(descriptor.identity?.etag);
4613
+ const transport = createTileConversionResourceFetcher({
4614
+ maxInputBytes,
4615
+ signal,
4616
+ fetcher: async (url, options) => {
4617
+ const response = await fetcher(url, options);
4618
+ if (response.status !== 206) {
4619
+ await response.body?.cancel();
4620
+ throw new Error("Remote conversion archives require HTTP 206 byte-range responses.");
4621
+ }
4622
+ try {
4623
+ validateArchiveResponseValidator(response.headers);
4624
+ } catch (error) {
4625
+ await response.body?.cancel(error);
4626
+ throw error;
4627
+ }
4628
+ return response;
4629
+ }
4630
+ });
4631
+ let file;
4632
+ let closePromise;
4633
+ async function closeFile() {
4634
+ if (file) {
4635
+ closePromise ??= file.close();
4636
+ await closePromise;
4637
+ }
4638
+ }
4639
+ try {
4640
+ if (typeof descriptor.input === "string") {
4641
+ if (!["http:", "https:"].includes(new URL(descriptor.input).protocol))
4642
+ throw new Error("Use an HTTP(S) archive URL.");
4643
+ const remote = new import_loader_utils.HttpFile(descriptor.input, {
4644
+ fetch: (url, options) => transport(url, options),
4645
+ consistency: "strict",
4646
+ ...descriptor.identity
4647
+ });
4648
+ file = remote;
4649
+ await remote.open(signal);
4650
+ } else file = new import_loader_utils.BlobFile(descriptor.input);
4651
+ const ownedFile = file;
4652
+ file = void 0;
4653
+ return await createTileConversionArchiveTransport(
4654
+ ownedFile,
4655
+ descriptor.format,
4656
+ maxInputBytes,
4657
+ signal,
4658
+ descriptor
4659
+ );
4660
+ } catch (error) {
4661
+ await closeFile();
4662
+ throw error;
4663
+ }
4664
+ }
4665
+ async function createTileConversionArchiveTransport(file, format, maxInputBytes, signal, descriptor) {
4666
+ const rootUrl = `https://conversion.invalid/input.${format}`;
4667
+ let closed = false;
4668
+ let releasePromise;
4669
+ let closePromise;
4670
+ const closeFile = () => {
4671
+ closed = true;
4672
+ closePromise ??= file.close();
4673
+ return closePromise;
4674
+ };
4675
+ const checkOpen = () => {
4676
+ signal.throwIfAborted();
4677
+ if (closed)
4678
+ throw new import_core34.TileConversionError("ARCHIVE_SOURCE_CLOSED", "Conversion archive is closed");
4679
+ };
4680
+ try {
4681
+ checkOpen();
4682
+ if (!["slpk", "3tz"].includes(format) || !Number.isSafeInteger(maxInputBytes) || maxInputBytes < 1)
4683
+ throw new import_core34.TileConversionError(
4684
+ "INVALID_ARCHIVE_SOURCE",
4685
+ "Archive source requires a supported format and positive safe byte budget"
4686
+ );
4687
+ if (!Number.isSafeInteger(file.size) || file.size < 0 || file.size > maxInputBytes)
4688
+ throw new Error("Conversion archive input must be at most 16 MiB.");
4689
+ let readBytes = 0;
4690
+ const boundedFile = {
4691
+ handle: file.handle,
4692
+ size: file.size,
4693
+ bigsize: file.bigsize,
4694
+ url: file.url,
4695
+ ...file.stat ? { stat: () => file.stat() } : {},
4696
+ close: closeFile,
4697
+ /** Checks indexed reads before allocation and forwards operation cancellation. */
4698
+ read: async (start, length) => {
4699
+ checkOpen();
4700
+ if (!Number.isSafeInteger(length) || length < 0 || length > maxInputBytes - readBytes)
4701
+ throw new import_core34.TileConversionError(
4702
+ "INPUT_RESOURCE_TOO_LARGE",
4703
+ "Archive indexed reads exceed the input budget"
4704
+ );
4705
+ readBytes += length;
4706
+ const bytes = await file.read(start, length, signal);
4707
+ checkOpen();
4708
+ return bytes;
4709
+ }
4710
+ };
4711
+ const archive = format === "slpk" ? await (0, import_i3s3.parseSLPKArchive)(boundedFile) : await (0, import_d_tiles3.parse3DTilesArchive)(boundedFile);
4712
+ const root = new URL(rootUrl);
4713
+ return {
4714
+ rootUrl: format === "3tz" ? `${rootUrl}/tileset.json` : rootUrl,
4715
+ descriptor: descriptor ? {
4716
+ ...descriptor,
4717
+ ...file instanceof import_loader_utils.HttpFile ? { identity: file.getIdentitySnapshot() } : {}
4718
+ } : void 0,
4719
+ /** Resolves only resources belonging to this archive, without external network fallback. */
4720
+ fetcher: (async (input, options) => {
4721
+ checkOpen();
4722
+ options?.signal?.throwIfAborted();
4723
+ const url = new URL(input instanceof Request ? input.url : String(input));
4724
+ if (url.origin !== root.origin || url.pathname !== root.pathname && !url.pathname.startsWith(`${root.pathname}/`))
4725
+ throw new Error("Conversion dependencies must resolve inside the selected archive.");
4726
+ const path = decodeURIComponent(
4727
+ url.pathname.slice(root.pathname.length).replace(/^\//, "")
4728
+ );
4729
+ if (path.includes("\\") || path.split("/").some((segment) => segment === ".." || segment === "."))
4730
+ throw new Error("Archive dependency path cannot escape its root.");
4731
+ const bytes = format === "slpk" ? await archive.getFile(path, "http") : await archive.getFile(path);
4732
+ checkOpen();
4733
+ options?.signal?.throwIfAborted();
4734
+ return new Response(bytes);
4735
+ }),
4736
+ /** Releases the readable file on success, failure or cancellation. */
4737
+ close: async () => {
4738
+ closed = true;
4739
+ releasePromise ??= archive.close();
4740
+ await releasePromise;
4741
+ }
4742
+ };
4743
+ } catch (error) {
4744
+ await closeFile();
4745
+ throw error;
4746
+ }
4747
+ }
4748
+ function validateArchiveEntityTag(entityTag) {
4749
+ if (entityTag !== void 0 && entityTag !== null && !/^"[\x21\x23-\x7e\x80-\xff]*"$/.test(entityTag))
4750
+ throw new import_core34.TileConversionError(
4751
+ "ARCHIVE_VALIDATOR_UNSUPPORTED",
4752
+ "Remote archives require a strong quoted ETag, or Last-Modified without an ETag; weak/malformed ETags are unsupported"
4753
+ );
4754
+ }
4755
+ function validateArchiveResponseValidator(headers) {
4756
+ const entityTag = headers.get("etag");
4757
+ validateArchiveEntityTag(entityTag);
4758
+ if (entityTag !== null) return;
4759
+ const lastModified = headers.get("last-modified");
4760
+ const responseDate = headers.get("date");
4761
+ const modifiedTime = parseArchiveHttpDate(lastModified);
4762
+ const responseTime = parseArchiveHttpDate(responseDate);
4763
+ if (!Number.isFinite(modifiedTime) || !Number.isFinite(responseTime) || responseTime - modifiedTime < 6e4)
4764
+ throw new import_core34.TileConversionError(
4765
+ "ARCHIVE_VALIDATOR_UNSUPPORTED",
4766
+ "Last-Modified archive identity requires a valid Date at least 60 seconds later; otherwise supply a strong ETag"
4767
+ );
4768
+ }
4769
+ function parseArchiveHttpDate(value) {
4770
+ const milliseconds = value === null ? NaN : Date.parse(value);
4771
+ return Number.isFinite(milliseconds) && new Date(milliseconds).toUTCString() === value ? milliseconds : NaN;
4772
+ }
4773
+
4774
+ // src/v5/i3s-selection-source.ts
4775
+ var import_core35 = require("@loaders.gl/core");
4776
+ var import_i3s4 = require("@loaders.gl/i3s");
4777
+ var import_tiles5 = require("@loaders.gl/tiles");
4778
+ var import_core36 = require("@loaders.gl/tile-converter/v5/core");
4779
+ function createRuntime(transport, layer, header) {
4780
+ const libraries = transport.dracoLibraryOptions;
4781
+ const sourceUrl = new URL(transport.rootUrl);
4782
+ sourceUrl.search = "";
4783
+ sourceUrl.hash = "";
4784
+ const options = {
4785
+ modules: libraries.modules,
4786
+ core: {
4787
+ worker: false,
4788
+ fetch: transport.fetcher,
4789
+ CDN: libraries.CDN,
4790
+ useLocalLibraries: libraries.useLocalLibraries
4791
+ },
4792
+ draco: { decoderProfile: libraries.decoderProfile },
4793
+ // The injected transport owns credential scope; runtime query parameters apply to every URL.
4794
+ i3s: { geometryMode: "source", decodeTextures: false, useCompressedTextures: false }
4795
+ };
4796
+ const loadResource = async (url, loader, loadOptions) => {
4797
+ const response = await transport.fetcher(url);
4798
+ const contextUrl = new URL(url);
4799
+ contextUrl.search = "";
4800
+ contextUrl.hash = "";
4801
+ return (0, import_core35.parse)(await response.arrayBuffer(), loader, loadOptions, {
4802
+ url: contextUrl.href,
4803
+ fetch: transport.fetcher,
4804
+ coreApi: import_core35.coreApi,
4805
+ _parse: import_core35.parse
4806
+ });
4807
+ };
4808
+ const source = new import_tiles5.I3SSource(
4809
+ header && layer ? {
4810
+ ...layer,
4811
+ root: { ...header, children: [] },
4812
+ type: "I3S",
4813
+ url: sourceUrl.href,
4814
+ basePath: sourceUrl.href,
4815
+ loader: import_i3s4.I3SLoader,
4816
+ coreApi: import_core35.coreApi
4817
+ } : {
4818
+ url: sourceUrl.href,
4819
+ loader: import_i3s4.I3SLoader,
4820
+ coreApi: import_core35.coreApi,
4821
+ resolver: { loadRoot: loadResource, loadResource }
4822
+ },
4823
+ options
4824
+ );
4825
+ source.coreApi = { ...import_core35.coreApi, load: loadResource };
4826
+ return new import_tiles5.Tileset3D(source, { i3s: options.i3s });
4827
+ }
4828
+ async function inspectI3SMeshConversionSource(options) {
4829
+ const { signal } = options;
4830
+ validateSelectionOptions(options);
4831
+ signal.throwIfAborted();
4832
+ const transport = options;
4833
+ let runtime;
4834
+ try {
4835
+ const response = await transport.fetcher(transport.rootUrl);
4836
+ const layer = await response.json();
4837
+ if (!["3DObject", "IntegratedMesh"].includes(layer.layerType))
4838
+ throw new Error("Conversion accepts I3S 3DObject or IntegratedMesh layers.");
4839
+ const rootFetcher = transport.fetcher;
4840
+ const cachedFetcher = (url, options2) => {
4841
+ const requestUrl = new URL(url instanceof Request ? url.url : String(url));
4842
+ const root = new URL(transport.rootUrl);
4843
+ return requestUrl.origin === root.origin && requestUrl.pathname === root.pathname ? Promise.resolve(new Response(JSON.stringify(layer))) : rootFetcher(url, options2);
4844
+ };
4845
+ runtime = createRuntime({ ...transport, fetcher: cachedFetcher });
4846
+ await runtime.tilesetInitializationPromise;
4847
+ await createI3SMeshTilesetConversionSource(runtime).inspect(signal);
4848
+ const stack = [runtime.root];
4849
+ const identifiers = /* @__PURE__ */ new Set();
4850
+ const resources = [];
4851
+ while (stack.length) {
4852
+ signal.throwIfAborted();
4853
+ const tile = stack.pop();
4854
+ if (!["string", "number"].includes(typeof tile.header.id) || String(tile.header.id).length === 0)
4855
+ throw new Error("I3S nodes require a declared identity.");
4856
+ const identifier = String(tile.header.id);
4857
+ if (identifiers.has(identifier))
4858
+ throw new Error("Repeated I3S node identity is unsupported.");
4859
+ identifiers.add(identifier);
4860
+ if (identifiers.size + stack.length + (tile.header.children?.length ?? 0) > (options.maxInputResources ?? SELECTED_MESH_LIMITS.maxInputResources))
4861
+ throw new Error("I3S inspection exceeds the 1,000 node limit.");
4862
+ await runtime.source.loadTileChildrenForTraversal(tile, signal);
4863
+ if (!tile.children.length && tile.contentUrl)
4864
+ resources.push({
4865
+ resourceId: identifier,
4866
+ uri: tile.contentUrl,
4867
+ header: structuredClone(tile.header)
4868
+ });
4869
+ for (const child of [...tile.children].reverse()) stack.push(child);
4870
+ }
4871
+ signal.throwIfAborted();
4872
+ if (!resources.length) throw new Error("No supported leaf mesh nodes are declared.");
4873
+ return {
4874
+ kind: "i3s",
4875
+ rootUrl: transport.rootUrl,
4876
+ layer,
4877
+ resources,
4878
+ metadataBytes: 0
4879
+ };
4880
+ } finally {
4881
+ runtime?.destroy();
4882
+ }
4883
+ }
4884
+ async function createSelectedI3SMeshConversionSource(inspection, options) {
4885
+ const { resourceIds, signal, features } = options;
4886
+ validateSelectionOptions(options);
4887
+ signal.throwIfAborted();
4888
+ if (inspection.rootUrl !== options.rootUrl)
4889
+ throw new import_core36.TileConversionError(
4890
+ "INVALID_I3S_SELECTION",
4891
+ "Selection transport must match the inspected layer"
4892
+ );
4893
+ if (!resourceIds.length || resourceIds.length > SELECTED_MESH_LIMITS.maxSelectedResources || new Set(resourceIds).size !== resourceIds.length || resourceIds.some(
4894
+ (identifier) => !inspection.resources.some((resource) => resource.resourceId === identifier)
4895
+ ))
4896
+ throw new Error("Select between 1 and 64 distinct leaf mesh nodes.");
4897
+ const selected = inspection.resources.filter(
4898
+ (resource) => resourceIds.includes(resource.resourceId)
4899
+ );
4900
+ if (selected.some(
4901
+ (resource) => resource.header.children?.length || !resource.header.contentUrl || resource.uri !== resource.header.contentUrl
4902
+ ))
4903
+ throw new Error("Only inspected leaf mesh headers can be converted.");
4904
+ const transport = options;
4905
+ let decodedBytes = 0;
4906
+ let meshCount = 0;
4907
+ {
4908
+ const qualification = createRuntime(transport, inspection.layer, selected[0].header);
4909
+ let spatialContext;
4910
+ try {
4911
+ await qualification.tilesetInitializationPromise;
4912
+ const metadata = await createI3SMeshTilesetConversionSource(qualification).inspect(signal);
4913
+ spatialContext = (0, import_core36.createI3SConversionSpatialContext)(metadata.spatialReference, {
4914
+ targetCrs: "EPSG:4978"
4915
+ });
4916
+ } finally {
4917
+ qualification.destroy();
4918
+ }
4919
+ const source = {
4920
+ /** Reuses only immutable inspected declarations. */
4921
+ inspect: async () => inspection,
4922
+ /** Releases each dedicated node runtime before moving on or closing early. */
4923
+ async *read() {
4924
+ for (const descriptor of selected) {
4925
+ const runtime = createRuntime(transport, inspection.layer, descriptor.header);
4926
+ try {
4927
+ await runtime.tilesetInitializationPromise;
4928
+ const meshSource = createI3SMeshTilesetConversionSource(runtime, {
4929
+ unloadContent: true,
4930
+ features,
4931
+ readExternalResource: async (uri) => new Uint8Array(
4932
+ await (await transport.fetcher(runtime.source.getTileUrl(uri))).arrayBuffer()
4933
+ )
4934
+ });
4935
+ const metadata = await meshSource.inspect(signal);
4936
+ for await (const resource of meshSource.read(metadata, signal)) {
4937
+ decodedBytes += measureMeshBytes(resource);
4938
+ if (++meshCount > SELECTED_MESH_LIMITS.maxMeshResources || decodedBytes > (options.maxInputBytes ?? SELECTED_MESH_LIMITS.maxInputBytes))
4939
+ throw new import_core36.TileConversionError(
4940
+ "INPUT_RESOURCE_TOO_LARGE",
4941
+ "Selected I3S decoded resources exceed the input/mesh budget"
4942
+ );
4943
+ yield { ...resource, id: descriptor.resourceId };
4944
+ }
4945
+ } finally {
4946
+ runtime.destroy();
4947
+ }
4948
+ }
4949
+ }
4950
+ };
4951
+ return { source, spatialContext };
4952
+ }
4953
+ }
4954
+ function validateSelectionOptions(options) {
4955
+ for (const [value, maximum] of [
4956
+ [
4957
+ options.maxInputBytes ?? SELECTED_MESH_LIMITS.maxInputBytes,
4958
+ SELECTED_MESH_LIMITS.maxInputBytes
4959
+ ],
4960
+ [
4961
+ options.maxInputResources ?? SELECTED_MESH_LIMITS.maxInputResources,
4962
+ SELECTED_MESH_LIMITS.maxInputResources
4963
+ ]
4964
+ ])
4965
+ if (!Number.isSafeInteger(value) || value < 1 || value > maximum)
4966
+ throw new import_core36.TileConversionError(
4967
+ "INVALID_I3S_SELECTION_LIMIT",
4968
+ "I3S selection limits must be positive safe integers within the bounded profile"
4969
+ );
4970
+ }