diff --git a/.github/workflows/deploy-server.yml b/.github/workflows/deploy-server.yml index 1533265ba..9fb926d25 100644 --- a/.github/workflows/deploy-server.yml +++ b/.github/workflows/deploy-server.yml @@ -16,7 +16,7 @@ jobs: outputs: build_label: ${{ steps.short_sha.outputs.sha }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - id: short_sha @@ -58,7 +58,7 @@ jobs: - run_migrations - export_commit steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index fb93f50df..f1c28a436 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -24,7 +24,7 @@ jobs: client: ${{ steps.changes.outputs.client }} build_label: ${{ steps.short_sha.outputs.sha }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - id: short_sha @@ -112,7 +112,7 @@ jobs: - detect_changes - run_migrations steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: @@ -168,7 +168,7 @@ jobs: needs: - detect_changes steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions/setup-node@v2 with: node-version: "18.6" diff --git a/.github/workflows/preview-build.yml b/.github/workflows/preview-build.yml index 28d00ed1a..e77de5d22 100644 --- a/.github/workflows/preview-build.yml +++ b/.github/workflows/preview-build.yml @@ -9,7 +9,7 @@ jobs: name: preview_client url: ${{ steps.wrangler.outputs.preview_url }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions/setup-node@v3 with: node-version: "18.6" diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index e51c084e5..c04477d61 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -16,7 +16,7 @@ jobs: ports: - "6379:6379" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions/setup-node@v3 with: node-version: "18.6" @@ -62,7 +62,7 @@ jobs: ports: - "6379:6379" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions/setup-node@v3 with: node-version: "18.6" @@ -102,7 +102,7 @@ jobs: name: Test React Client runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions/setup-node@v3 with: node-version: "18.6" @@ -131,7 +131,7 @@ jobs: name: Build React Client runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions/setup-node@v3 with: node-version: "18.6" @@ -176,7 +176,7 @@ jobs: # ports: # - "6379:6379" # steps: - # - uses: actions/checkout@v2 + # - uses: actions/checkout@v4 # - uses: actions/setup-node@v2 # with: # node-version: "18.6" @@ -292,7 +292,7 @@ jobs: # ports: # - "6379:6379" # steps: - # - uses: actions/checkout@v2 + # - uses: actions/checkout@v4 # - uses: actions/setup-node@v2 # with: # node-version: "18.6" diff --git a/.vscode/extensions.json b/.vscode/extensions.json index c983818ee..ebd6baeb0 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,7 +4,6 @@ // List of extensions which should be recommended for users of this workspace. "recommendations": [ - "ms-vscode.vscode-typescript-tslint-plugin", "esbenp.prettier-vscode", "bradymholt.pgformatter", "apollographql.vscode-apollo", diff --git a/.vscode/settings.json b/.vscode/settings.json index 57e762a9f..657bf3793 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -26,7 +26,8 @@ "**/dist/**": true, "**/infra/lib/*.d.ts": true, "**/infra/lib/*Stack.js": true, - "node_modules": true + "node_modules": true, + "packages/client/src/lang/**/*.json": true }, "eslint.workingDirectories": ["packages/client"], "[git-commit]": { diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 1c05e298a..5cf0c328c 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -67,6 +67,27 @@ "problemMatcher": [], "label": "Start Database & Redis", "detail": "docker-compose up -d" + }, + { + "type": "npm", + "script": "watch", + "path": "packages/mapbox-gl-esri-sources/", + "problemMatcher": [], + "label": "Rollup: mapbox-gl-esri-sources", + "runOptions": { + "runOn": "folderOpen" + } + }, + { + "type": "typescript", + "runOptions": { + "runOn": "folderOpen" + }, + "tsconfig": "packages/mapbox-gl-esri-sources/tsconfig.json", + "option": "watch", + "problemMatcher": ["$tsc-watch"], + "group": "build", + "label": "TypeScript: watch mapbox-gl-esri-sources" } ] } diff --git a/package.json b/package.json index 860a36a3e..2c7466b55 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "license": "BSD-3-Clause", "dependencies": { "form-data": "^4.0.0", - "node-fetch": "^2.6.7" + "node-fetch": "^2.6.7", + "typescript": "^5.2" } } diff --git a/packages/api/generated-schema-clean.gql b/packages/api/generated-schema-clean.gql index 45e2fe5a1..9e7e66994 100644 --- a/packages/api/generated-schema-clean.gql +++ b/packages/api/generated-schema-clean.gql @@ -209,6 +209,31 @@ enum ArcgisFeatureLayerFetchStrategy { TILED } +"""An input for mutations affecting `ArcgisImportItem`""" +input ArcgisImportItemInput { + id: Int + isFolder: Boolean + parentId: String + sourceId: Int + stableId: String + sublayerId: Int + title: String +} + +"""An input for mutations affecting `ArcgisImportSource`""" +input ArcgisImportSourceInput { + fetchStrategy: ArcgisFeatureLayerFetchStrategy + id: Int + type: ArcgisSourceType + url: String +} + +enum ArcgisSourceType { + ARCGIS_DYNAMIC_MAPSERVER + ARCGIS_RASTER_TILES + ARCGIS_VECTOR +} + """All input for the `archiveResponses` mutation.""" input ArchiveResponsesInput { """ @@ -295,6 +320,7 @@ type Basemap implements Node { """The method to use when ordering `MapBookmark`.""" orderBy: [MapBookmarksOrderBy!] = [NATURAL] ): MapBookmarksConnection! + maxzoom: Int """Label shown in the basemap picker interface""" name: String! @@ -461,6 +487,7 @@ input BasemapInput { Identify the labels layer lowest in the stack so that overlay layers may be placed underneath. """ labelsLayerId: String + maxzoom: Int """Label shown in the basemap picker interface""" name: String! @@ -533,6 +560,7 @@ input BasemapPatch { Identify the labels layer lowest in the stack so that overlay layers may be placed underneath. """ labelsLayerId: String + maxzoom: Int """Label shown in the basemap picker interface""" name: String @@ -2735,6 +2763,8 @@ input DataSourceInput { Represents an update to a `DataSource`. Fields that are set will be updated. """ input DataSourcePatch { + arcgisFetchStrategy: ArcgisFeatureLayerFetchStrategy + """ Contains an attribution to be displayed when the map is shown to a user. """ @@ -6400,6 +6430,33 @@ enum GroupsOrderBy { PROJECT_ID_DESC } +"""All input for the `importArcgisServices` mutation.""" +input ImportArcgisServicesInput { + """ + An arbitrary string value with no semantic meaning. Will be included in the + payload verbatim. May be used to track mutations by the client. + """ + clientMutationId: String + items: [ArcgisImportItemInput] + projectId: Int + sources: [ArcgisImportSourceInput] +} + +"""The output of our `importArcgisServices` mutation.""" +type ImportArcgisServicesPayload { + """ + The exact same `clientMutationId` that was provided in the mutation input, + unchanged and unused. May be used by a client to track mutations. + """ + clientMutationId: String + + """ + Our root query field type. Allows us to run any query from our mutation payload. + """ + query: Query + tableOfContentsItems: [TableOfContentsItem!] +} + type InteractivitySetting implements Node { """Reads and enables pagination through a set of `Basemap`.""" basemapsByInteractivitySettingsIdConnection( @@ -6514,6 +6571,7 @@ input InteractivitySettingPatch { } enum InteractivityType { + ALL_PROPERTIES_POPUP BANNER FIXED_BLOCK NONE @@ -7989,6 +8047,12 @@ type Mutation { """ input: GrantAdminAccessInput! ): GrantAdminAccessPayload + importArcgisServices( + """ + The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. + """ + input: ImportArcgisServicesInput! + ): ImportArcgisServicesPayload """ Adds current user to the list of participants for a project, sharing their @@ -9292,6 +9356,12 @@ enum OptionalBasemapLayersOrderBy { PRIMARY_KEY_DESC } +type OutstandingSurveyInvites { + projectId: Int! + surveyId: Int! + token: String! +} + """Information about pagination in a connection.""" type PageInfo { """When paginating forwards, the cursor to continue.""" @@ -13105,6 +13175,13 @@ type TableOfContentsItem implements Node { """ bounds: [BigFloat] + """ + Metadata will be returned as directly stored in the SeaSketch + database or computed by fetching from a 3rd party service, + depending on the data source type. + """ + computedMetadata: JSON + """ Reads a single `DataLayer` that is related to this `TableOfContentsItem`. """ @@ -13125,6 +13202,7 @@ type TableOfContentsItem implements Node { their children. Toggles can only be used to toggle children off """ isClickOffOnly: Boolean! + isCustomGlSource: Boolean """ Identifies whether this item is part of the draft table of contents edited by @@ -13180,6 +13258,7 @@ type TableOfContentsItem implements Node { """Name used in the table of contents rendering""" title: String! translatedProps: JSON! + usesDynamicMetadata: Boolean } """ @@ -13370,6 +13449,11 @@ enum TileScheme { XYZ } +type TocItemDetails { + id: Int! + type: SketchChildType! +} + """All input for the `toggleAdminAccess` mutation.""" input ToggleAdminAccessInput { """ diff --git a/packages/api/generated-schema.gql b/packages/api/generated-schema.gql index f10dfb1dd..9e7e66994 100644 --- a/packages/api/generated-schema.gql +++ b/packages/api/generated-schema.gql @@ -213,7 +213,7 @@ enum ArcgisFeatureLayerFetchStrategy { input ArcgisImportItemInput { id: Int isFolder: Boolean - parentId: Int + parentId: String sourceId: Int stableId: String sublayerId: Int @@ -320,6 +320,7 @@ type Basemap implements Node { """The method to use when ordering `MapBookmark`.""" orderBy: [MapBookmarksOrderBy!] = [NATURAL] ): MapBookmarksConnection! + maxzoom: Int """Label shown in the basemap picker interface""" name: String! @@ -486,6 +487,7 @@ input BasemapInput { Identify the labels layer lowest in the stack so that overlay layers may be placed underneath. """ labelsLayerId: String + maxzoom: Int """Label shown in the basemap picker interface""" name: String! @@ -558,6 +560,7 @@ input BasemapPatch { Identify the labels layer lowest in the stack so that overlay layers may be placed underneath. """ labelsLayerId: String + maxzoom: Int """Label shown in the basemap picker interface""" name: String @@ -2760,6 +2763,8 @@ input DataSourceInput { Represents an update to a `DataSource`. Fields that are set will be updated. """ input DataSourcePatch { + arcgisFetchStrategy: ArcgisFeatureLayerFetchStrategy + """ Contains an attribution to be displayed when the map is shown to a user. """ @@ -6566,6 +6571,7 @@ input InteractivitySettingPatch { } enum InteractivityType { + ALL_PROPERTIES_POPUP BANNER FIXED_BLOCK NONE @@ -9350,6 +9356,12 @@ enum OptionalBasemapLayersOrderBy { PRIMARY_KEY_DESC } +type OutstandingSurveyInvites { + projectId: Int! + surveyId: Int! + token: String! +} + """Information about pagination in a connection.""" type PageInfo { """When paginating forwards, the cursor to continue.""" @@ -13163,6 +13175,13 @@ type TableOfContentsItem implements Node { """ bounds: [BigFloat] + """ + Metadata will be returned as directly stored in the SeaSketch + database or computed by fetching from a 3rd party service, + depending on the data source type. + """ + computedMetadata: JSON + """ Reads a single `DataLayer` that is related to this `TableOfContentsItem`. """ @@ -13183,6 +13202,7 @@ type TableOfContentsItem implements Node { their children. Toggles can only be used to toggle children off """ isClickOffOnly: Boolean! + isCustomGlSource: Boolean """ Identifies whether this item is part of the draft table of contents edited by @@ -13238,6 +13258,7 @@ type TableOfContentsItem implements Node { """Name used in the table of contents rendering""" title: String! translatedProps: JSON! + usesDynamicMetadata: Boolean } """ @@ -13428,6 +13449,11 @@ enum TileScheme { XYZ } +type TocItemDetails { + id: Int! + type: SketchChildType! +} + """All input for the `toggleAdminAccess` mutation.""" input ToggleAdminAccessInput { """ diff --git a/packages/api/migrations/committed/000281.sql b/packages/api/migrations/committed/000281.sql new file mode 100644 index 000000000..611371c46 --- /dev/null +++ b/packages/api/migrations/committed/000281.sql @@ -0,0 +1,643 @@ +--! Previous: sha1:6084ad8c19dffb844ed6a49cab24caac6dc51841 +--! Hash: sha1:077e1ed1e4f045a9336afed1444ff35444bcd3db + +-- Enter migration here + +drop function if exists import_arcgis_services; +drop function if exists import_subtree; + +drop type if exists arcgis_import_item; +drop type if exists arcgis_import_source; +drop type if exists arcgis_source_type; + +create type arcgis_source_type as enum ( + 'arcgis-dynamic-mapserver', + 'arcgis-vector', + 'arcgis-raster-tiles' +); + +create type arcgis_import_item as ( + id int, + is_folder boolean, + title text, + source_id int, + parent_id text, + sublayer_id int, + stable_id text +); + +create type arcgis_import_source as ( + id int, + type arcgis_source_type, + url text, + fetch_strategy arcgis_feature_layer_fetch_strategy +); + + +create or replace function id_lookup_set_key(lookup jsonb, key int, value int) + returns jsonb + language plpgsql + as $$ + begin + if lookup is null then + raise exception 'lookup is null'; + else + raise warning 'key: %, value: %', key, value; + return jsonb_set( + lookup, + array[key::text], + value::text::jsonb + ); + end if; + end; + $$; + +create or replace function id_lookup_get_key(lookup jsonb, key int) + returns int + language plpgsql + as $$ + begin + if lookup is null then + raise exception 'lookup is null'; + else + if (lookup->key::text)::int is null then + raise exception 'key % not found in lookup. %', key::text, lookup; + else + return (lookup->key::text)::int; + end if; + end if; + end; + $$; + +create or replace function import_subtree("projectId" int, items arcgis_import_item[], layer_id_lookup jsonb, path ltree) + returns int + language plpgsql + security definer + as $$ + declare + item_path ltree; + layer_id int; + parent_sid text; + begin + for i in array_lower(items, 1)..array_upper(items, 1) loop + if (path is null and items[i].parent_id is null) or items[i].parent_id = subpath(path, -1, 1)::text then + if path is null then + item_path = items[i].stable_id::ltree; + parent_sid = null; + else + item_path = path || items[i].stable_id::ltree; + parent_sid = subpath(path, -1, 1)::text; + end if; + if items[i].is_folder = true then + layer_id = null; + elsif items[i].sublayer_id is not null then + layer_id = id_lookup_get_key(layer_id_lookup, items[i].id); + else + layer_id = id_lookup_get_key(layer_id_lookup, items[i].source_id); + end if; + insert into table_of_contents_items ( + project_id, + is_draft, + is_folder, + data_layer_id, + title, + stable_id, + path, + parent_stable_id + ) values ( + "projectId", + true, + items[i].is_folder, + layer_id, + items[i].title, + items[i].stable_id, + item_path, + parent_sid + ); + if items[i].is_folder = true then + perform import_subtree("projectId", items, layer_id_lookup, item_path); + end if; + end if; + end loop; + return 0; + end; + $$; + + +create or replace function import_arcgis_services("projectId" int, items arcgis_import_item[], sources arcgis_import_source[]) + returns setof table_of_contents_items + language plpgsql + security definer + as $$ + declare + folder_id int; + source arcgis_import_source; + source_id int; + layer_id int; + item_id int; + source_id_map jsonb; + layer_id_map jsonb; + item_id_map jsonb; + begin + source_id_map = '{}'::jsonb; + layer_id_map = '{}'::jsonb; + item_id_map = '{}'::jsonb; + if session_is_admin("projectId") then + for i in array_lower(sources, 1)..array_upper(sources, 1) loop + source = sources[i]; + if source.type = 'arcgis-vector' then + insert into data_sources ( + project_id, + type, + url, + arcgis_fetch_strategy + ) values ( + "projectId", + 'arcgis-vector', + source.url, + source.fetch_strategy + ) returning id into source_id; + select + id_lookup_set_key(source_id_map, source.id, source_id) + into source_id_map; + + insert into data_layers ( + project_id, + data_source_id + ) values ( + "projectId", + source_id + ) returning id into layer_id; + select + id_lookup_set_key(layer_id_map, source.id, layer_id) + into layer_id_map; + elsif source.type = 'arcgis-raster-tiles' then + insert into data_sources ( + project_id, + type, + url + ) values ( + "projectId", + 'arcgis-raster-tiles', + source.url + ) returning id into source_id; + insert into data_layers ( + project_id, + data_source_id + ) values ( + "projectId", + source_id + ) returning id into layer_id; + elsif source.type = 'arcgis-dynamic-mapserver' then + insert into data_sources ( + project_id, + type, + url + ) values ( + "projectId", + 'arcgis-dynamic-mapserver', + source.url + ) returning id into source_id; + -- create data layers for each sublayer + for i in array_lower(items, 1)..array_upper(items, 1) loop + if items[i].sublayer_id is not null then + insert into data_layers ( + project_id, + data_source_id, + sublayer + ) values ( + "projectId", + source_id, + items[i].sublayer_id + ) returning id into layer_id; + select + id_lookup_set_key(layer_id_map, items[i].id, layer_id) + into layer_id_map; + end if; + end loop; + else + raise exception 'Source type % not supported', source.type; + end if; + end loop; + if array_length(items, 1) > 1 then + perform import_subtree("projectId", items, layer_id_map, null); + else + insert into table_of_contents_items ( + project_id, + is_draft, + is_folder, + data_layer_id, + title, + stable_id, + path + ) values ( + "projectId", + true, + false, + layer_id, + items[1].title, + items[1].stable_id, + items[1].stable_id::ltree + ); + end if; + else + raise exception 'Only admins can import ArcGIS services'; + end if; + end; + $$; + + + + + +grant execute on function import_arcgis_services to seasketch_user; + + +CREATE or replace FUNCTION public.projects_imported_arcgis_services(p public.projects) RETURNS text[] + LANGUAGE plpgsql STABLE SECURITY DEFINER + AS $$ + declare + services text[]; + begin + if session_is_admin(p.id) then + select + array_agg(coalesce(url, original_source_url)) as url + into + services + from + ( + select + basemaps.url, data_sources.original_source_url + from basemaps, data_sources + where + ( + basemaps.project_id = p.id and + basemaps.is_arcgis_tiled_mapservice = true + ) or + ( + data_sources.project_id = p.id and + ( + data_sources.type = 'arcgis-raster-tiles' or + data_sources.type = 'arcgis-vector' or + data_sources.type = 'arcgis-dynamic-mapserver' + ) + ) + ) q; + return services; + else + raise exception 'Permission denied'; + end if; + end; + $$; + + +CREATE OR REPlACE FUNCTION public.projects_imported_arcgis_services(p public.projects) RETURNS text[] + LANGUAGE plpgsql STABLE SECURITY DEFINER + AS $$ + declare + data_sources_services text[]; + basemap_services text[]; + begin + if true or session_is_admin(p.id) then + select + array_agg(distinct(REGEXP_REPLACE(url, '/[0-9]+$', ''))) + into + basemap_services + from + basemaps + where + is_arcgis_tiled_mapservice = true and + project_id = p.id; + select + array_agg(distinct(REGEXP_REPLACE(url, '/[0-9]+$', ''))) + from + data_sources + into + data_sources_services + where + project_id = p.id and + type in ('arcgis-raster-tiles', 'arcgis-vector', 'arcgis-dynamic-mapserver'); + return array_cat(data_sources_services, basemap_services); + else + raise exception 'Permission denied'; + end if; + end; + $$; + + + +CREATE OR REPLACE FUNCTION public.publish_table_of_contents("projectId" integer) RETURNS SETOF public.table_of_contents_items + LANGUAGE plpgsql SECURITY DEFINER + AS $$ + declare + lid int; + item table_of_contents_items; + source_id int; + copied_source_id int; + acl_type access_control_list_type; + acl_id int; + orig_acl_id int; + new_toc_id int; + new_interactivity_settings_id int; + begin + -- check permissions + if session_is_admin("projectId") = false then + raise 'Permission denied. Must be a project admin'; + end if; + -- delete existing published table of contents items, layers, sources, and interactivity settings + delete from + interactivity_settings + where + id in ( + select + data_layers.interactivity_settings_id + from + data_layers + inner JOIN + table_of_contents_items + on + data_layers.id = table_of_contents_items.data_layer_id + where + table_of_contents_items.project_id = "projectId" and + is_draft = false + ); + + delete from data_sources where data_sources.id in ( + select + data_source_id + from + data_layers + inner JOIN + table_of_contents_items + on + data_layers.id = table_of_contents_items.data_layer_id + where + table_of_contents_items.project_id = "projectId" and + is_draft = false + ); + delete from data_layers where id in ( + select + data_layer_id + from + table_of_contents_items + where + project_id = "projectId" and + is_draft = false + ); + delete from + table_of_contents_items + where + project_id = "projectId" and + is_draft = false; + + -- one-by-one, copy related layers and link table of contents items + for item in + select + * + from + table_of_contents_items + where + is_draft = true and + project_id = "projectId" + loop + if item.is_folder = false then + -- copy interactivity settings first + insert into interactivity_settings ( + type, + short_template, + long_template, + cursor + ) select + type, + short_template, + long_template, + cursor + from + interactivity_settings + where + interactivity_settings.id = ( + select interactivity_settings_id from data_layers where data_layers.id = item.data_layer_id + ) + returning + id + into + new_interactivity_settings_id; + + insert into data_layers ( + project_id, + data_source_id, + source_layer, + sublayer, + render_under, + mapbox_gl_styles, + interactivity_settings_id, + z_index + ) + select "projectId", + data_source_id, + source_layer, + sublayer, + render_under, + mapbox_gl_styles, + new_interactivity_settings_id, + z_index + from + data_layers + where + id = item.data_layer_id + returning id into lid; + else + lid = item.data_layer_id; + end if; + -- TODO: this will have to be modified with the addition of any columns + insert into table_of_contents_items ( + is_draft, + project_id, + path, + stable_id, + parent_stable_id, + title, + is_folder, + show_radio_children, + is_click_off_only, + metadata, + bounds, + data_layer_id, + sort_index, + hide_children, + geoprocessing_reference_id, + translated_props + ) values ( + false, + "projectId", + item.path, + item.stable_id, + item.parent_stable_id, + item.title, + item.is_folder, + item.show_radio_children, + item.is_click_off_only, + item.metadata, + item.bounds, + lid, + item.sort_index, + item.hide_children, + item.geoprocessing_reference_id, + item.translated_props + ) returning id into new_toc_id; + select + type, id into acl_type, orig_acl_id + from + access_control_lists + where + table_of_contents_item_id = ( + select + id + from + table_of_contents_items + where is_draft = true and stable_id = item.stable_id + ); + -- copy access control list settings + if acl_type != 'public' then + update + access_control_lists + set type = acl_type + where table_of_contents_item_id = new_toc_id + returning id into acl_id; + if acl_type = 'group' then + insert into + access_control_list_groups ( + access_control_list_id, + group_id + ) + select + acl_id, + group_id + from + access_control_list_groups + where + access_control_list_id = orig_acl_id; + end if; + end if; + end loop; + -- one-by-one, copy related sources and update foreign keys of layers + for source_id in + select distinct(data_source_id) from data_layers where id in ( + select + data_layer_id + from + table_of_contents_items + where + is_draft = false and + project_id = "projectId" and + is_folder = false + ) + loop + -- TODO: This function will have to be updated whenever the schema + -- changes since these columns are hard coded... no way around it. + insert into data_sources ( + project_id, + type, + attribution, + bounds, + maxzoom, + minzoom, + url, + scheme, + tiles, + tile_size, + encoding, + buffer, + cluster, + cluster_max_zoom, + cluster_properties, + cluster_radius, + generate_id, + line_metrics, + promote_id, + tolerance, + coordinates, + urls, + query_parameters, + use_device_pixel_ratio, + import_type, + original_source_url, + enhanced_security, + byte_length, + supports_dynamic_layers, + uploaded_source_filename, + uploaded_source_layername, + normalized_source_object_key, + normalized_source_bytes, + geostats, + upload_task_id, + translated_props, + arcgis_fetch_strategy + ) + select + "projectId", + type, + attribution, + bounds, + maxzoom, + minzoom, + url, + scheme, + tiles, + tile_size, + encoding, + buffer, + cluster, + cluster_max_zoom, + cluster_properties, + cluster_radius, + generate_id, + line_metrics, + promote_id, + tolerance, + coordinates, + urls, + query_parameters, + use_device_pixel_ratio, + import_type, + original_source_url, + enhanced_security, + byte_length, + supports_dynamic_layers, + uploaded_source_filename, + uploaded_source_layername, + normalized_source_object_key, + normalized_source_bytes, + geostats, + upload_task_id, + translated_props, + arcgis_fetch_strategy + from + data_sources + where + id = source_id + returning id into copied_source_id; + -- update data_layers that should now reference the copy + update + data_layers + set data_source_id = copied_source_id + where + data_source_id = source_id and + data_layers.id in (( + select distinct(data_layer_id) from table_of_contents_items where is_draft = false and + project_id = "projectId" and + is_folder = false + )); + end loop; + update + projects + set + draft_table_of_contents_has_changes = false, + table_of_contents_last_published = now() + where + id = "projectId"; + -- return items + return query select * from table_of_contents_items + where project_id = "projectId" and is_draft = false; + end; + $$; diff --git a/packages/api/migrations/committed/000282.sql b/packages/api/migrations/committed/000282.sql new file mode 100644 index 000000000..1699589ba --- /dev/null +++ b/packages/api/migrations/committed/000282.sql @@ -0,0 +1,275 @@ +--! Previous: sha1:077e1ed1e4f045a9336afed1444ff35444bcd3db +--! Hash: sha1:6284aa014c05f12ad1b5920e5686ba8fa4be3ad4 + +-- Enter migration here +CREATE OR REPLACE FUNCTION public.before_insert_or_update_data_sources_trigger() RETURNS trigger + LANGUAGE plpgsql + AS $$ + declare + bucket_id text; + begin + if new.minzoom is not null and (new.type != 'vector' and new.type != 'raster' and new.type != 'raster-dem' and new.type != 'seasketch-mvt' and new.type != 'seasketch-raster' and new.type != 'arcgis-raster-tiles' ) then + raise 'minzoom may only be set for tiled sources (vector, raster, raster-dem)'; + end if; + if new.coordinates is null and (new.type = 'video' or new.type = 'image') then + raise 'coordinates must be set on image and video sources'; + end if; + if new.coordinates is not null and (new.type != 'video' and new.type != 'image') then + raise 'coordinates property can only be set on image and video sources'; + end if; + if new.maxzoom is not null and (new.type = 'image' or new.type = 'video') then + raise 'maxzoom cannot be set for image and video sources'; + end if; + if new.url is null and (new.type = 'geojson' or new.type = 'image' or new.type = 'arcgis-dynamic-mapserver' or new.type = 'arcgis-vector' or new.type = 'seasketch-mvt') then + raise 'url must be set for "%" sources', (new.type); + end if; + if new.scheme is not null and (new.type != 'raster' and new.type != 'raster-dem' and new.type != 'vector' and new.type != 'seasketch-mvt') then + raise 'scheme property is not allowed for "%" sources', (new.type); + end if; + if new.tiles is not null and (new.type != 'raster' and new.type != 'raster-dem' and new.type != 'vector' and new.type != 'seasketch-vector') then + raise 'tiles property is not allowed for "%" sources', (new.type); + end if; + if new.encoding is not null and new.type != 'raster-dem' then + raise 'encoding property only allowed on raster-dem sources'; + end if; + if new.tile_size is not null and (new.type != 'raster' and new.type != 'raster-dem' and new.type != 'vector' and new.type != 'seasketch-mvt' and new.type != 'seasketch-raster') then + raise 'tile_size property is not allowed for "%" sources', (new.type); + end if; + if (new.type != 'geojson' and new.type != 'seasketch-vector') and (new.buffer is not null or new.cluster is not null or new.cluster_max_zoom is not null or new.cluster_properties is not null or new.cluster_radius is not null or new.generate_id is not null or new.line_metrics is not null or new.promote_id is not null or new.tolerance is not null) then + raise 'geojson props such as buffer, cluster, generate_id, etc not allowed on % sources', (new.type); + end if; + if (new.byte_length is not null and new.type != 'seasketch-vector' and new.type != 'seasketch-mvt' and new.type != 'seasketch-raster') then + raise 'byte_length can only be set on seasketch-vector, seasketch_mvt and seasketch-raster sources'; + end if; + if (new.type = 'seasketch-vector' and new.type != 'seasketch-mvt' and new.byte_length is null) then + raise 'seasketch-vector and mvt sources must have byte_length set to an approximate value'; + end if; + if new.urls is not null and new.type != 'video' then + raise 'urls property not allowed on % sources', (new.type); + end if; + if new.query_parameters is not null and (new.type != 'arcgis-vector' and new.type != 'arcgis-dynamic-mapserver') then + raise 'query_parameters property not allowed on % sources', (new.type); + end if; + if new.use_device_pixel_ratio is not null and new.type != 'arcgis-dynamic-mapserver' then + raise 'use_device_pixel_ratio property not allowed on % sources', (new.type); + end if; + if new.import_type is not null and new.type != 'seasketch-vector' and new.type != 'seasketch-mvt' and new.type != 'seasketch-raster' then + raise 'import_type property is only allowed for seasketch-vector, seasketch-mvt, and seasketch-raster sources'; + end if; + if new.import_type is null and (new.type = 'seasketch-vector' or new.type = 'seasketch-mvt') then + raise 'import_type property is required for seasketch-vector sources'; + end if; + if new.original_source_url is not null and new.type != 'seasketch-vector' then + raise 'original_source_url may only be set on seasketch-vector sources'; + end if; + if new.enhanced_security is not null and new.type != 'seasketch-vector' then + raise 'enhanced_security may only be set on seasketch-vector sources'; + end if; + if old is null and new.type = 'seasketch-vector' then + if new.bucket_id is null then + new.bucket_id = (select data_sources_bucket_id from projects where id = new.project_id); + end if; + if new.object_key is null then + new.object_key = (select gen_random_uuid()); + end if; + new.tiles = null; + end if; + return new; + end; +$$; + +drop policy if exists data_sources_update on data_sources; +CREATE POLICY data_sources_update ON public.data_sources FOR UPDATE USING ((public.session_is_admin(project_id) AND ( SELECT (( SELECT 1 + FROM public.table_of_contents_items + WHERE ((table_of_contents_items.data_layer_id in ( SELECT data_layers.id + FROM public.data_layers + WHERE (data_layers.data_source_id = data_sources.id))) AND (table_of_contents_items.is_draft = false))) IS NULL)))) WITH CHECK (public.session_is_admin(project_id)); + + +create or replace function import_arcgis_services("projectId" int, items arcgis_import_item[], sources arcgis_import_source[]) + returns setof table_of_contents_items + language plpgsql + security definer + as $$ + declare + folder_id int; + source arcgis_import_source; + source_id int; + layer_id int; + item_id int; + source_id_map jsonb; + layer_id_map jsonb; + item_id_map jsonb; + interactive_layers int[]; + begin + source_id_map = '{}'::jsonb; + layer_id_map = '{}'::jsonb; + item_id_map = '{}'::jsonb; + if session_is_admin("projectId") then + for i in array_lower(sources, 1)..array_upper(sources, 1) loop + source = sources[i]; + if source.type = 'arcgis-vector' then + insert into data_sources ( + project_id, + type, + url, + arcgis_fetch_strategy + ) values ( + "projectId", + 'arcgis-vector', + source.url, + source.fetch_strategy + ) returning id into source_id; + select + id_lookup_set_key(source_id_map, source.id, source_id) + into source_id_map; + + insert into data_layers ( + project_id, + data_source_id + ) values ( + "projectId", + source_id + ) returning id into layer_id; + select + id_lookup_set_key(layer_id_map, source.id, layer_id) + into layer_id_map; + interactive_layers = array_append(interactive_layers, layer_id); + elsif source.type = 'arcgis-raster-tiles' then + insert into data_sources ( + project_id, + type, + url, + use_device_pixel_ratio + ) values ( + "projectId", + 'arcgis-raster-tiles', + source.url, + true + ) returning id into source_id; + insert into data_layers ( + project_id, + data_source_id + ) values ( + "projectId", + source_id + ) returning id into layer_id; + elsif source.type = 'arcgis-dynamic-mapserver' then + insert into data_sources ( + project_id, + type, + url, + use_device_pixel_ratio + ) values ( + "projectId", + 'arcgis-dynamic-mapserver', + source.url, + true + ) returning id into source_id; + -- create data layers for each sublayer + for i in array_lower(items, 1)..array_upper(items, 1) loop + if items[i].sublayer_id is not null then + insert into data_layers ( + project_id, + data_source_id, + sublayer + ) values ( + "projectId", + source_id, + items[i].sublayer_id + ) returning id into layer_id; + interactive_layers = array_append(interactive_layers, layer_id); + select + id_lookup_set_key(layer_id_map, items[i].id, layer_id) + into layer_id_map; + end if; + end loop; + else + raise exception 'Source type % not supported', source.type; + end if; + end loop; + if array_length(items, 1) > 1 then + perform import_subtree("projectId", items, layer_id_map, null); + else + insert into table_of_contents_items ( + project_id, + is_draft, + is_folder, + data_layer_id, + title, + stable_id, + path + ) values ( + "projectId", + true, + false, + layer_id, + items[1].title, + items[1].stable_id, + items[1].stable_id::ltree + ); + end if; + update interactivity_settings + set type = 'ALL_PROPERTIES_POPUP' + where id = any( + ( + select + interactivity_settings_id + from + data_layers + where + id = any(interactive_layers) + ) + ); + else + raise exception 'Only admins can import ArcGIS services'; + end if; + end; + $$; + +grant update (arcgis_fetch_strategy) on data_sources to seasketch_user; + +alter table basemaps add column if not exists maxzoom int; + +create or replace function table_of_contents_items_uses_dynamic_metadata(t table_of_contents_items) + returns boolean + language plpgsql + security definer + stable + as $$ + declare + uses_dynamic_metadata boolean; + begin + if t.metadata is not null then + return false; + end if; + if t.data_layer_id is null then + return false; + end if; + select type = 'arcgis-dynamic-mapserver' or type = 'arcgis-vector' or type = 'arcgis-raster-tiles' into uses_dynamic_metadata from data_sources where id = (select data_source_id from data_layers where id = t.data_layer_id); + return uses_dynamic_metadata; + end; + $$; + +grant execute on function table_of_contents_items_uses_dynamic_metadata(table_of_contents_items) to anon; + +create or replace function table_of_contents_items_is_custom_gl_source(t table_of_contents_items) + returns boolean + language plpgsql + security definer + stable + as $$ + declare + source_type text; + begin + if t.data_layer_id is null then + return null; + end if; + select type into source_type from data_sources where id = (select data_source_id from data_layers where id = t.data_layer_id); + return source_type = 'arcgis-dynamic-mapserver' or source_type = 'arcgis-vector' or source_type = 'arcgis-raster-tiles'; + end; + $$; + +grant execute on function table_of_contents_items_is_custom_gl_source(table_of_contents_items) to anon; + +ALTER TYPE interactivity_type ADD VALUE IF NOT EXISTS 'ALL_PROPERTIES_POPUP'; diff --git a/packages/api/package-lock.json b/packages/api/package-lock.json index f179734c7..277a2c701 100644 --- a/packages/api/package-lock.json +++ b/packages/api/package-lock.json @@ -29,10 +29,11 @@ "@types/lodash.flatten": "^4.4.7", "@types/lodash.snakecase": "^4.1.7", "@types/mapbox__tilebelt": "^1.0.0", - "@types/mapbox-gl": "^2.7.3", + "@types/mapbox-gl": "^2.7.15", "@types/mustache": "^4.1.1", "@types/pg-format": "^1.0.2", "@types/pluralize": "^0.0.29", + "@types/prosemirror-model": "^1.11.2", "@types/redis": "^2.8.25", "@types/sanitize-html": "^1.27.0", "@types/sharp": "^0.31.1", @@ -63,9 +64,9 @@ "pg-mem": "^1.9.9", "pgmock2": "^2.0.6", "pluralize": "^8.0.0", - "postgraphile": "^4.8.0", + "postgraphile": "^4.14.0", "postgraphile-plugin-upload-field": "^1.0.0-alpha.10", - "prosemirror-model": "^1.13.1", + "prosemirror-model": "^1.19.3", "prosemirror-schema-list": "^1.1.4", "puppeteer": "^19.7.2", "rate-limiter-flexible": "^2.4.1", @@ -76,7 +77,7 @@ "sqlite": "^4.1.1", "sqlite3": "^5.0.8", "tmp-promise": "^3.0.3", - "typescript": "^4.9.5", + "typescript": "^5.2", "unsplash-js": "^7.0.15", "uuid": "^8.3.0" }, @@ -95,7 +96,6 @@ "@types/ms": "^0.7.31", "@types/node-rsa": "^1.0.0", "@types/pg": "^7.14.4", - "@types/prosemirror-model": "^1.11.2", "@types/shortid": "0.0.29", "@types/slonik": "^22.1.4", "aws-sdk": "^2.713.0", @@ -2447,20 +2447,20 @@ "integrity": "sha512-jjcWBokl9eb1gVJ85QmoaQ73CQ52xAaOCF29ukRbYNl6lY+ts0ErTaDYOBlejcbUs2OpaiqYLO5uDhyLFzWw4w==" }, "node_modules/@graphile/lru": { - "version": "4.8.0-rc.0", - "resolved": "https://registry.npmjs.org/@graphile/lru/-/lru-4.8.0-rc.0.tgz", - "integrity": "sha512-/8a8qBItKbpoDkoiUcNPiLcn91wAniDA23ZsxTxoC51YD/FORF0znsISL19XGqw5Iq5b0jlFbDIpgtN0fQEbqQ==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@graphile/lru/-/lru-4.11.0.tgz", + "integrity": "sha512-Fakuk190EAKxWSa9YQyr/87g8mvAv8HBvk6yPCPuIoA3bYXF7n6kl0XSqKjSd5VfjEqhtnzQ6zJGzDf1Gv/tJg==", "dependencies": { - "tslib": "^1.13.0" + "tslib": "^2.0.1" }, "engines": { "node": ">=8.6" } }, "node_modules/@graphile/lru/node_modules/tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/@graphile/pg-pubsub": { "version": "4.11.0", @@ -4979,6 +4979,7 @@ "version": "8.5.0", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz", "integrity": "sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==", + "dev": true, "dependencies": { "@types/node": "*" } @@ -5041,9 +5042,9 @@ } }, "node_modules/@types/mapbox-gl": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-2.7.4.tgz", - "integrity": "sha512-usTVCWYhmLt+teQ5U3HhSBZIIHFLp6fQXH/1QOnd4xnDtvNTUWo8n6UyRA4kUj84CGpHSBFeF/MNcBdGB1pwog==", + "version": "2.7.18", + "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-2.7.18.tgz", + "integrity": "sha512-3Yq4v8IdigSs2oD70dRJuWY/6PoW9i6MyXOx8DvJNc6CuSXRMleVdNiFYa5E0w6tSCfL+b+cGau5Em09MIujtQ==", "dependencies": { "@types/geojson": "*" } @@ -5124,7 +5125,6 @@ "resolved": "https://registry.npmjs.org/@types/prosemirror-model/-/prosemirror-model-1.17.0.tgz", "integrity": "sha512-lG5xEMkE8r8Soa80KdWPTbCLUaSHBHVHpTIEsQiebfONpvmS5061IMGzHUdb1oWjgrwh8EJq0GgMNwXHUx5mVg==", "deprecated": "This is a stub types definition. prosemirror-model provides its own type definitions, so you do not need this installed.", - "dev": true, "dependencies": { "prosemirror-model": "*" } @@ -5249,9 +5249,9 @@ "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" }, "node_modules/@types/ws": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-6.0.4.tgz", - "integrity": "sha512-PpPrX7SZW9re6+Ha8ojZG4Se8AZXgf0GK6zmfqEuCsY49LFDNXO3SByp44X3dFEqtB73lkCDAdUazhAjVPiNwg==", + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", "dependencies": { "@types/node": "*" } @@ -9004,42 +9004,46 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, "node_modules/graphile-build": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/graphile-build/-/graphile-build-4.8.0.tgz", - "integrity": "sha512-hxnCQjM4Xihly+6IfJ6O10yZjc4AHCpJZ1pa84FED9PFJOSix16S89WymPkFUk/Tmm7XyoIfgcHcDl1QJypm5A==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/graphile-build/-/graphile-build-4.14.0.tgz", + "integrity": "sha512-IypfqZ+ugV6t6OXcIa7KGKC3LY59RDQ8aN9nJ5lPPWslSkqhURJN7lWIQ2YQZ0pjBeONl2itAW3HVRw2YFuKrw==", "dependencies": { - "@graphile/lru": "4.8.0-rc.0", + "@graphile/lru": "4.11.0", "chalk": "^2.4.2", "debug": "^4.1.1", - "graphql-parse-resolve-info": "4.8.0-rc.0", + "graphql-parse-resolve-info": "4.14.0", "iterall": "^1.2.2", "lodash": ">=4 <5", "lru-cache": "^5.0.0", "pluralize": "^7.0.0", - "semver": "^6.0.0" + "semver": "^7.5.2" }, "engines": { "node": ">=8.6" + }, + "peerDependencies": { + "graphql": ">=0.9 <0.14 || ^14.0.2 || ^15.4.0" } }, "node_modules/graphile-build-pg": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/graphile-build-pg/-/graphile-build-pg-4.8.0.tgz", - "integrity": "sha512-zCvFag/x2PZZlQGaKU4F6H+d3Iq/ELCay8aULxQiFePePN00Q1vRKW46FgiW5bz7xOyq87G82a0qVQFx8mInUA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/graphile-build-pg/-/graphile-build-pg-4.14.0.tgz", + "integrity": "sha512-sSsK0PRG3oCPLYqwSWXFpX5aGUIgd6ZLGCAbzdp1M65kW1dFtSITuZQEsQB987YjgxYO6+R4HaZe1GuN8l/gLQ==", "dependencies": { - "@graphile/lru": "4.8.0-rc.0", + "@graphile/lru": "4.11.0", "chalk": "^2.4.2", "debug": "^4.1.1", - "graphile-build": "4.8.0", - "graphql-iso-date": "^3.6.0", - "jsonwebtoken": "^8.5.1", + "graphile-build": "4.14.0", + "jsonwebtoken": "^9.0.0", "lodash": ">=4 <5", "lru-cache": ">=4 <5", - "pg-sql2": "4.8.0", - "postgres-interval": "^1.2.0" + "pg-sql2": "4.14.0" }, "engines": { "node": ">=8.6" + }, + "peerDependencies": { + "pg": ">=6.1.0 <9" } }, "node_modules/graphile-build-pg/node_modules/ansi-styles": { @@ -9077,14 +9081,43 @@ "node_modules/graphile-build-pg/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/graphile-build-pg/node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { - "ms": "^2.1.1" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/graphile-build-pg/node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" } }, "node_modules/graphile-build-pg/node_modules/lru-cache": { @@ -9096,10 +9129,40 @@ "yallist": "^2.1.2" } }, + "node_modules/graphile-build-pg/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/graphile-build-pg/node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/graphile-build-pg/node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/graphile-build-pg/node_modules/yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" }, "node_modules/graphile-build/node_modules/ansi-styles": { "version": "3.2.1", @@ -9136,14 +9199,22 @@ "node_modules/graphile-build/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/graphile-build/node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { - "ms": "^2.1.1" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/graphile-build/node_modules/pluralize": { @@ -9155,13 +9226,35 @@ } }, "node_modules/graphile-build/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, + "node_modules/graphile-build/node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/graphile-build/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/graphile-migrate": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/graphile-migrate/-/graphile-migrate-1.4.1.tgz", @@ -9186,30 +9279,42 @@ } }, "node_modules/graphile-utils": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/graphile-utils/-/graphile-utils-4.8.0.tgz", - "integrity": "sha512-ZXjz+F9jWSU/vVRr6GSx3KCQ0HuG6qRtfWGdBcP5na2yiz7/2oPEOO0rQz66gjTt7zmA2VM6EisCXvdLCFQNQg==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/graphile-utils/-/graphile-utils-4.14.0.tgz", + "integrity": "sha512-Ru8FfK8Z/RcRe1IBYd8rUSbiVIkJeSuudBr3RDMO7MdIaXN0ocaXTN515yr/RDoImDUbt6gZ/caleu3iuZHVUQ==", "dependencies": { "debug": "^4.1.1", - "graphql": ">=0.9 <0.14 || ^14.0.2", - "tslib": "^1.13.0" + "graphql": ">=0.9 <0.14 || ^14.0.2 || ^15.4.0", + "tslib": "^2.0.1" }, "engines": { "node": ">=8.6" + }, + "peerDependencies": { + "graphile-build": "^4.5.0", + "graphile-build-pg": "^4.5.0" } }, "node_modules/graphile-utils/node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { - "ms": "^2.1.1" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/graphile-utils/node_modules/tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/graphile-worker": { "version": "0.13.0", @@ -9331,35 +9436,41 @@ "node": ">= 6.x" } }, - "node_modules/graphql-iso-date": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/graphql-iso-date/-/graphql-iso-date-3.6.1.tgz", - "integrity": "sha512-AwFGIuYMJQXOEAgRlJlFL4H1ncFM8n8XmoVDTNypNOZyQ8LFDG2ppMFlsS862BSTCDcSUfHp8PD3/uJhv7t59Q==" - }, "node_modules/graphql-parse-resolve-info": { - "version": "4.8.0-rc.0", - "resolved": "https://registry.npmjs.org/graphql-parse-resolve-info/-/graphql-parse-resolve-info-4.8.0-rc.0.tgz", - "integrity": "sha512-JjL+w9wbYGCONDfR70VUt8Qi/7FxNIH4JlD58vc/OfYPiX22ZU/nepe7DkRP2vKR20EE2m64Kq2Qo2yF5fMTvA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/graphql-parse-resolve-info/-/graphql-parse-resolve-info-4.14.0.tgz", + "integrity": "sha512-5Fbquh3IZMciLYgtiWeFxAeZOwpPyonhbaN05fzL/Gll0HS0hMqJh1Q88NQLHiASD6//cJ3LTXLncuajRqsUcA==", "dependencies": { "debug": "^4.1.1", - "tslib": "^1.13.0" + "tslib": "^2.0.1" }, "engines": { "node": ">=8.6" + }, + "peerDependencies": { + "graphql": ">=0.9 <0.14 || ^14.0.2 || ^15.4.0 || ^16.3.0" } }, "node_modules/graphql-parse-resolve-info/node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { - "ms": "^2.1.1" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/graphql-parse-resolve-info/node_modules/tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/graphql-subscriptions": { "version": "1.2.1", @@ -9436,6 +9547,17 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/graphql-ws": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.14.2.tgz", + "integrity": "sha512-LycmCwhZ+Op2GlHz4BZDsUYHKRiiUz+3r9wbhBATMETNlORQJAaFlAgTFoeRh6xQoQegwYwIylVD1Qns9/DA3w==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "graphql": ">=0.11 <=16" + } + }, "node_modules/growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", @@ -11152,27 +11274,6 @@ "node": ">=10" } }, - "node_modules/jest-environment-jsdom/node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "dev": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/jest-environment-node": { "version": "26.1.0", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.1.0.tgz", @@ -16297,23 +16398,26 @@ "integrity": "sha512-1uYCckkuTfzz/FCefvavRywkowa6M5FohNMF5OjKrqo9PSR8gYc8poVmwwYQaBxhmQdBjhtP514eXy9/Us2xKg==" }, "node_modules/pg-sql2": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/pg-sql2/-/pg-sql2-4.8.0.tgz", - "integrity": "sha512-lMqds7zZ9K7Y/UFQjRuoDSlE0Zq3Mlw06b9zW4Vy5x/MnZbqxwDGwgPlGC15Fx21Mn8aWgXVmqNf1SAu0Geb3A==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/pg-sql2/-/pg-sql2-4.14.0.tgz", + "integrity": "sha512-CnKj0LE3jJaP5GWHhcQ5PFxISY44fqQ3TKmMD0k14VrYAcc/CI2ErYXEnnA6klbgXB8F+Lo1/erPSYnoibGFZw==", "dependencies": { - "@graphile/lru": "4.8.0-rc.0", - "@types/pg": ">=6 <8", + "@graphile/lru": "4.11.0", + "@types/pg": ">=6 <9", "debug": ">=3 <5", - "tslib": "^1.13.0" + "tslib": "^2.0.1" }, "engines": { "node": ">=8.6" + }, + "peerDependencies": { + "pg": ">=6.1.0 <9" } }, "node_modules/pg-sql2/node_modules/tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/pg-types": { "version": "2.2.0", @@ -16482,35 +16586,37 @@ } }, "node_modules/postgraphile": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/postgraphile/-/postgraphile-4.8.0.tgz", - "integrity": "sha512-NSETJhg5smqiu8mbr6AqATVypPb5WMVq95i7smlaE+XIzOT+bbJz4bAuB+Gvp399tOwFOwUyfOZxQvq5jxJg+A==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/postgraphile/-/postgraphile-4.14.0.tgz", + "integrity": "sha512-p/Se0cgS9bzeYPcZGAfd8wnzQHpIqZRhaQvuUkHAzxkKxKnrym71PlMspJ6+IzTuZBUJUMzfkWpOKhTFhwKhvQ==", "dependencies": { - "@graphile/lru": "4.8.0-rc.0", + "@graphile/lru": "4.11.0", "@types/json5": "^0.0.30", - "@types/jsonwebtoken": "^8.3.2", - "@types/koa": "^2.0.44", - "@types/pg": "^7.4.10", - "@types/ws": "^6.0.1", + "@types/jsonwebtoken": "^9.0.1", + "@types/pg": ">=6 <9", + "@types/ws": "^7.4.0", "body-parser": "^1.15.2", "chalk": "^2.4.2", "commander": "^2.19.0", "debug": "^4.1.1", "finalhandler": "^1.0.6", - "graphile-utils": "^4.8.0", - "graphql": "^0.6.0 || ^0.7.0 || ^0.8.0-b || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.2", + "graphile-build": "4.14.0", + "graphile-build-pg": "4.14.0", + "graphile-utils": "^4.14.0", + "graphql": "^0.6.0 || ^0.7.0 || ^0.8.0-b || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.2 || ^15.0.0", + "graphql-ws": "^5.6.2", "http-errors": "^1.5.1", "iterall": "^1.0.2", "json5": "^2.1.1", - "jsonwebtoken": "^8.0.0", + "jsonwebtoken": "^9.0.0", "parseurl": "^1.3.2", "pg": ">=6.1.0 <9", "pg-connection-string": "^2.0.0", - "pg-sql2": "4.8.0", - "postgraphile-core": "4.8.0", - "subscriptions-transport-ws": "^0.9.15", - "tslib": "^1.5.0", - "ws": "^6.1.3" + "pg-sql2": "4.14.0", + "postgraphile-core": "4.14.0", + "subscriptions-transport-ws": "^0.9.18", + "tslib": "^2.1.0", + "ws": "^7.4.2" }, "bin": { "postgraphile": "cli.js" @@ -16520,22 +16626,26 @@ } }, "node_modules/postgraphile-core": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/postgraphile-core/-/postgraphile-core-4.8.0.tgz", - "integrity": "sha512-k84OwyAiTR6GNg5SvmN/apd28WCLczbJ4kmLA4LENO9u3ybUgG3d+68TtEmajc6rV5TbrJyppIVyCV6L5yMLOg==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/postgraphile-core/-/postgraphile-core-4.14.0.tgz", + "integrity": "sha512-hhcVyyH0NnzR5DsEoAVv7MMjZviMxs8mfNv7ZhewK4u2dZ8QKznHx5ET/rentnehEWztZwfy/ktjRlN2pC0GjA==", "dependencies": { - "graphile-build": "4.8.0", - "graphile-build-pg": "4.8.0", - "tslib": "^1.13.0" + "graphile-build": "4.14.0", + "graphile-build-pg": "4.14.0", + "tslib": "^2.0.1" }, "engines": { "node": ">=8.6" + }, + "peerDependencies": { + "graphql": ">=0.9 <0.14 || ^14.0.2 || ^15.4.0", + "pg": ">=6.1.0 <9" } }, "node_modules/postgraphile-core/node_modules/tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/postgraphile-plugin-upload-field": { "version": "1.0.0-alpha.10", @@ -16545,6 +16655,14 @@ "node": ">=10" } }, + "node_modules/postgraphile/node_modules/@types/jsonwebtoken": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", + "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/postgraphile/node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -16590,6 +16708,62 @@ "ms": "^2.1.1" } }, + "node_modules/postgraphile/node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/postgraphile/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postgraphile/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postgraphile/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/postgraphile/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/postgres-array": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", @@ -16728,9 +16902,9 @@ } }, "node_modules/prosemirror-model": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.18.3.tgz", - "integrity": "sha512-yUVejauEY3F1r7PDy4UJKEGeIU+KFc71JQl5sNvG66CLVdKXRjhWpBW6KMeduGsmGOsw85f6EGrs6QxIKOVILA==", + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.19.3.tgz", + "integrity": "sha512-tgSnwN7BS7/UM0sSARcW+IQryx2vODKX4MI7xpqY2X+iaepJdKBPc7I4aACIsDV/LTaTjt12Z56MhDr9LsyuZQ==", "dependencies": { "orderedmap": "^2.0.0" } @@ -19848,15 +20022,15 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", + "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/ulid": { @@ -20424,11 +20598,23 @@ } }, "node_modules/ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "dependencies": { - "async-limiter": "~1.0.0" + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, "node_modules/xdg-basedir": { @@ -22737,17 +22923,17 @@ "integrity": "sha512-jjcWBokl9eb1gVJ85QmoaQ73CQ52xAaOCF29ukRbYNl6lY+ts0ErTaDYOBlejcbUs2OpaiqYLO5uDhyLFzWw4w==" }, "@graphile/lru": { - "version": "4.8.0-rc.0", - "resolved": "https://registry.npmjs.org/@graphile/lru/-/lru-4.8.0-rc.0.tgz", - "integrity": "sha512-/8a8qBItKbpoDkoiUcNPiLcn91wAniDA23ZsxTxoC51YD/FORF0znsISL19XGqw5Iq5b0jlFbDIpgtN0fQEbqQ==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@graphile/lru/-/lru-4.11.0.tgz", + "integrity": "sha512-Fakuk190EAKxWSa9YQyr/87g8mvAv8HBvk6yPCPuIoA3bYXF7n6kl0XSqKjSd5VfjEqhtnzQ6zJGzDf1Gv/tJg==", "requires": { - "tslib": "^1.13.0" + "tslib": "^2.0.1" }, "dependencies": { "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, @@ -24937,6 +25123,7 @@ "version": "8.5.0", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz", "integrity": "sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg==", + "dev": true, "requires": { "@types/node": "*" } @@ -24999,9 +25186,9 @@ } }, "@types/mapbox-gl": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-2.7.4.tgz", - "integrity": "sha512-usTVCWYhmLt+teQ5U3HhSBZIIHFLp6fQXH/1QOnd4xnDtvNTUWo8n6UyRA4kUj84CGpHSBFeF/MNcBdGB1pwog==", + "version": "2.7.18", + "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-2.7.18.tgz", + "integrity": "sha512-3Yq4v8IdigSs2oD70dRJuWY/6PoW9i6MyXOx8DvJNc6CuSXRMleVdNiFYa5E0w6tSCfL+b+cGau5Em09MIujtQ==", "requires": { "@types/geojson": "*" } @@ -25081,7 +25268,6 @@ "version": "1.17.0", "resolved": "https://registry.npmjs.org/@types/prosemirror-model/-/prosemirror-model-1.17.0.tgz", "integrity": "sha512-lG5xEMkE8r8Soa80KdWPTbCLUaSHBHVHpTIEsQiebfONpvmS5061IMGzHUdb1oWjgrwh8EJq0GgMNwXHUx5mVg==", - "dev": true, "requires": { "prosemirror-model": "*" } @@ -25204,9 +25390,9 @@ "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==" }, "@types/ws": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-6.0.4.tgz", - "integrity": "sha512-PpPrX7SZW9re6+Ha8ojZG4Se8AZXgf0GK6zmfqEuCsY49LFDNXO3SByp44X3dFEqtB73lkCDAdUazhAjVPiNwg==", + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", "requires": { "@types/node": "*" } @@ -28311,19 +28497,19 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, "graphile-build": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/graphile-build/-/graphile-build-4.8.0.tgz", - "integrity": "sha512-hxnCQjM4Xihly+6IfJ6O10yZjc4AHCpJZ1pa84FED9PFJOSix16S89WymPkFUk/Tmm7XyoIfgcHcDl1QJypm5A==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/graphile-build/-/graphile-build-4.14.0.tgz", + "integrity": "sha512-IypfqZ+ugV6t6OXcIa7KGKC3LY59RDQ8aN9nJ5lPPWslSkqhURJN7lWIQ2YQZ0pjBeONl2itAW3HVRw2YFuKrw==", "requires": { - "@graphile/lru": "4.8.0-rc.0", + "@graphile/lru": "4.11.0", "chalk": "^2.4.2", "debug": "^4.1.1", - "graphql-parse-resolve-info": "4.8.0-rc.0", + "graphql-parse-resolve-info": "4.14.0", "iterall": "^1.2.2", "lodash": ">=4 <5", "lru-cache": "^5.0.0", "pluralize": "^7.0.0", - "semver": "^6.0.0" + "semver": "^7.5.2" }, "dependencies": { "ansi-styles": { @@ -28355,14 +28541,14 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "pluralize": { @@ -28371,27 +28557,43 @@ "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==" }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, "graphile-build-pg": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/graphile-build-pg/-/graphile-build-pg-4.8.0.tgz", - "integrity": "sha512-zCvFag/x2PZZlQGaKU4F6H+d3Iq/ELCay8aULxQiFePePN00Q1vRKW46FgiW5bz7xOyq87G82a0qVQFx8mInUA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/graphile-build-pg/-/graphile-build-pg-4.14.0.tgz", + "integrity": "sha512-sSsK0PRG3oCPLYqwSWXFpX5aGUIgd6ZLGCAbzdp1M65kW1dFtSITuZQEsQB987YjgxYO6+R4HaZe1GuN8l/gLQ==", "requires": { - "@graphile/lru": "4.8.0-rc.0", + "@graphile/lru": "4.11.0", "chalk": "^2.4.2", "debug": "^4.1.1", - "graphile-build": "4.8.0", - "graphql-iso-date": "^3.6.0", - "jsonwebtoken": "^8.5.1", + "graphile-build": "4.14.0", + "jsonwebtoken": "^9.0.0", "lodash": ">=4 <5", "lru-cache": ">=4 <5", - "pg-sql2": "4.8.0", - "postgres-interval": "^1.2.0" + "pg-sql2": "4.14.0" }, "dependencies": { "ansi-styles": { @@ -28423,14 +28625,31 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" + } + }, + "jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" } }, "lru-cache": { @@ -28442,10 +28661,33 @@ "yallist": "^2.1.2" } }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" } } }, @@ -28470,27 +28712,27 @@ } }, "graphile-utils": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/graphile-utils/-/graphile-utils-4.8.0.tgz", - "integrity": "sha512-ZXjz+F9jWSU/vVRr6GSx3KCQ0HuG6qRtfWGdBcP5na2yiz7/2oPEOO0rQz66gjTt7zmA2VM6EisCXvdLCFQNQg==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/graphile-utils/-/graphile-utils-4.14.0.tgz", + "integrity": "sha512-Ru8FfK8Z/RcRe1IBYd8rUSbiVIkJeSuudBr3RDMO7MdIaXN0ocaXTN515yr/RDoImDUbt6gZ/caleu3iuZHVUQ==", "requires": { "debug": "^4.1.1", - "graphql": ">=0.9 <0.14 || ^14.0.2", - "tslib": "^1.13.0" + "graphql": ">=0.9 <0.14 || ^14.0.2 || ^15.4.0", + "tslib": "^2.0.1" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, @@ -28589,32 +28831,27 @@ "iterall": "^1.2.2" } }, - "graphql-iso-date": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/graphql-iso-date/-/graphql-iso-date-3.6.1.tgz", - "integrity": "sha512-AwFGIuYMJQXOEAgRlJlFL4H1ncFM8n8XmoVDTNypNOZyQ8LFDG2ppMFlsS862BSTCDcSUfHp8PD3/uJhv7t59Q==" - }, "graphql-parse-resolve-info": { - "version": "4.8.0-rc.0", - "resolved": "https://registry.npmjs.org/graphql-parse-resolve-info/-/graphql-parse-resolve-info-4.8.0-rc.0.tgz", - "integrity": "sha512-JjL+w9wbYGCONDfR70VUt8Qi/7FxNIH4JlD58vc/OfYPiX22ZU/nepe7DkRP2vKR20EE2m64Kq2Qo2yF5fMTvA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/graphql-parse-resolve-info/-/graphql-parse-resolve-info-4.14.0.tgz", + "integrity": "sha512-5Fbquh3IZMciLYgtiWeFxAeZOwpPyonhbaN05fzL/Gll0HS0hMqJh1Q88NQLHiASD6//cJ3LTXLncuajRqsUcA==", "requires": { "debug": "^4.1.1", - "tslib": "^1.13.0" + "tslib": "^2.0.1" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, @@ -28682,6 +28919,12 @@ } } }, + "graphql-ws": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.14.2.tgz", + "integrity": "sha512-LycmCwhZ+Op2GlHz4BZDsUYHKRiiUz+3r9wbhBATMETNlORQJAaFlAgTFoeRh6xQoQegwYwIylVD1Qns9/DA3w==", + "requires": {} + }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", @@ -30087,13 +30330,6 @@ "tr46": "^2.1.0", "webidl-conversions": "^6.1.0" } - }, - "ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "dev": true, - "requires": {} } } }, @@ -34176,20 +34412,20 @@ "integrity": "sha512-1uYCckkuTfzz/FCefvavRywkowa6M5FohNMF5OjKrqo9PSR8gYc8poVmwwYQaBxhmQdBjhtP514eXy9/Us2xKg==" }, "pg-sql2": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/pg-sql2/-/pg-sql2-4.8.0.tgz", - "integrity": "sha512-lMqds7zZ9K7Y/UFQjRuoDSlE0Zq3Mlw06b9zW4Vy5x/MnZbqxwDGwgPlGC15Fx21Mn8aWgXVmqNf1SAu0Geb3A==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/pg-sql2/-/pg-sql2-4.14.0.tgz", + "integrity": "sha512-CnKj0LE3jJaP5GWHhcQ5PFxISY44fqQ3TKmMD0k14VrYAcc/CI2ErYXEnnA6klbgXB8F+Lo1/erPSYnoibGFZw==", "requires": { - "@graphile/lru": "4.8.0-rc.0", - "@types/pg": ">=6 <8", + "@graphile/lru": "4.11.0", + "@types/pg": ">=6 <9", "debug": ">=3 <5", - "tslib": "^1.13.0" + "tslib": "^2.0.1" }, "dependencies": { "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, @@ -34323,37 +34559,47 @@ } }, "postgraphile": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/postgraphile/-/postgraphile-4.8.0.tgz", - "integrity": "sha512-NSETJhg5smqiu8mbr6AqATVypPb5WMVq95i7smlaE+XIzOT+bbJz4bAuB+Gvp399tOwFOwUyfOZxQvq5jxJg+A==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/postgraphile/-/postgraphile-4.14.0.tgz", + "integrity": "sha512-p/Se0cgS9bzeYPcZGAfd8wnzQHpIqZRhaQvuUkHAzxkKxKnrym71PlMspJ6+IzTuZBUJUMzfkWpOKhTFhwKhvQ==", "requires": { - "@graphile/lru": "4.8.0-rc.0", + "@graphile/lru": "4.11.0", "@types/json5": "^0.0.30", - "@types/jsonwebtoken": "^8.3.2", - "@types/koa": "^2.0.44", - "@types/pg": "^7.4.10", - "@types/ws": "^6.0.1", + "@types/jsonwebtoken": "^9.0.1", + "@types/pg": ">=6 <9", + "@types/ws": "^7.4.0", "body-parser": "^1.15.2", "chalk": "^2.4.2", "commander": "^2.19.0", "debug": "^4.1.1", "finalhandler": "^1.0.6", - "graphile-utils": "^4.8.0", - "graphql": "^0.6.0 || ^0.7.0 || ^0.8.0-b || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.2", + "graphile-build": "4.14.0", + "graphile-build-pg": "4.14.0", + "graphile-utils": "^4.14.0", + "graphql": "^0.6.0 || ^0.7.0 || ^0.8.0-b || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.2 || ^15.0.0", + "graphql-ws": "^5.6.2", "http-errors": "^1.5.1", "iterall": "^1.0.2", "json5": "^2.1.1", - "jsonwebtoken": "^8.0.0", + "jsonwebtoken": "^9.0.0", "parseurl": "^1.3.2", "pg": ">=6.1.0 <9", "pg-connection-string": "^2.0.0", - "pg-sql2": "4.8.0", - "postgraphile-core": "4.8.0", - "subscriptions-transport-ws": "^0.9.15", - "tslib": "^1.5.0", - "ws": "^6.1.3" + "pg-sql2": "4.14.0", + "postgraphile-core": "4.14.0", + "subscriptions-transport-ws": "^0.9.18", + "tslib": "^2.1.0", + "ws": "^7.4.2" }, "dependencies": { + "@types/jsonwebtoken": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", + "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", + "requires": { + "@types/node": "*" + } + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -34392,23 +34638,66 @@ "requires": { "ms": "^2.1.1" } + }, + "jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, "postgraphile-core": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/postgraphile-core/-/postgraphile-core-4.8.0.tgz", - "integrity": "sha512-k84OwyAiTR6GNg5SvmN/apd28WCLczbJ4kmLA4LENO9u3ybUgG3d+68TtEmajc6rV5TbrJyppIVyCV6L5yMLOg==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/postgraphile-core/-/postgraphile-core-4.14.0.tgz", + "integrity": "sha512-hhcVyyH0NnzR5DsEoAVv7MMjZviMxs8mfNv7ZhewK4u2dZ8QKznHx5ET/rentnehEWztZwfy/ktjRlN2pC0GjA==", "requires": { - "graphile-build": "4.8.0", - "graphile-build-pg": "4.8.0", - "tslib": "^1.13.0" + "graphile-build": "4.14.0", + "graphile-build-pg": "4.14.0", + "tslib": "^2.0.1" }, "dependencies": { "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" } } }, @@ -34519,9 +34808,9 @@ } }, "prosemirror-model": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.18.3.tgz", - "integrity": "sha512-yUVejauEY3F1r7PDy4UJKEGeIU+KFc71JQl5sNvG66CLVdKXRjhWpBW6KMeduGsmGOsw85f6EGrs6QxIKOVILA==", + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.19.3.tgz", + "integrity": "sha512-tgSnwN7BS7/UM0sSARcW+IQryx2vODKX4MI7xpqY2X+iaepJdKBPc7I4aACIsDV/LTaTjt12Z56MhDr9LsyuZQ==", "requires": { "orderedmap": "^2.0.0" } @@ -37070,9 +37359,9 @@ } }, "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", + "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==" }, "ulid": { "version": "2.3.0", @@ -37548,12 +37837,10 @@ } }, "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "requires": { - "async-limiter": "~1.0.0" - } + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "requires": {} }, "xdg-basedir": { "version": "4.0.0", diff --git a/packages/api/package.json b/packages/api/package.json index 710660ed0..456978b18 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -52,6 +52,7 @@ "@mapbox/mapbox-gl-style-spec": "^13.28.0", "@mapbox/tilebelt": "^1.0.2", "@seasketch/map-tile-cache-calculator": "^1.0.0", + "@seasketch/mapbox-gl-esri-sources": "^0.9.0", "@sentry/browser": "^7.44.2", "@sentry/node": "^6.17.9", "@sentry/tracing": "^7.44.2", @@ -64,11 +65,12 @@ "@types/jsdom": "^20.0.1", "@types/lodash.flatten": "^4.4.7", "@types/lodash.snakecase": "^4.1.7", - "@types/mapbox-gl": "^2.7.3", + "@types/mapbox-gl": "^2.7.15", "@types/mapbox__tilebelt": "^1.0.0", "@types/mustache": "^4.1.1", "@types/pg-format": "^1.0.2", "@types/pluralize": "^0.0.29", + "@types/prosemirror-model": "^1.11.2", "@types/redis": "^2.8.25", "@types/sanitize-html": "^1.27.0", "@types/sharp": "^0.31.1", @@ -99,9 +101,9 @@ "pg-mem": "^1.9.9", "pgmock2": "^2.0.6", "pluralize": "^8.0.0", - "postgraphile": "^4.8.0", + "postgraphile": "^4.14.0", "postgraphile-plugin-upload-field": "^1.0.0-alpha.10", - "prosemirror-model": "^1.13.1", + "prosemirror-model": "^1.19.3", "prosemirror-schema-list": "^1.1.4", "puppeteer": "^19.7.2", "rate-limiter-flexible": "^2.4.1", @@ -113,7 +115,7 @@ "sqlite": "^4.1.1", "sqlite3": "^5.0.8", "tmp-promise": "^3.0.3", - "typescript": "^4.9.5", + "typescript": "^5.2", "unsplash-js": "^7.0.15", "uuid": "^8.3.0" }, @@ -132,7 +134,6 @@ "@types/ms": "^0.7.31", "@types/node-rsa": "^1.0.0", "@types/pg": "^7.14.4", - "@types/prosemirror-model": "^1.11.2", "@types/shortid": "0.0.29", "@types/slonik": "^22.1.4", "aws-sdk": "^2.713.0", diff --git a/packages/api/schema.sql b/packages/api/schema.sql index 72ec219ae..5e9f3d415 100644 --- a/packages/api/schema.sql +++ b/packages/api/schema.sql @@ -145,7 +145,7 @@ CREATE TYPE public.arcgis_import_item AS ( is_folder boolean, title text, source_id integer, - parent_id integer, + parent_id text, sublayer_id integer, stable_id text ); @@ -444,7 +444,8 @@ CREATE TYPE public.interactivity_type AS ENUM ( 'TOOLTIP', 'POPUP', 'FIXED_BLOCK', - 'NONE' + 'NONE', + 'ALL_PROPERTIES_POPUP' ); @@ -2628,7 +2629,8 @@ CREATE TABLE public.basemaps ( surveys_only boolean DEFAULT false NOT NULL, use_default_offline_tile_settings boolean DEFAULT true NOT NULL, translated_props jsonb DEFAULT '{}'::jsonb NOT NULL, - is_arcgis_tiled_mapservice boolean DEFAULT false NOT NULL + is_arcgis_tiled_mapservice boolean DEFAULT false NOT NULL, + maxzoom integer ); @@ -3045,7 +3047,7 @@ CREATE FUNCTION public.before_insert_or_update_data_sources_trigger() RETURNS tr declare bucket_id text; begin - if new.minzoom is not null and (new.type != 'vector' and new.type != 'raster' and new.type != 'raster-dem' and new.type != 'seasketch-mvt' and new.type != 'seasketch-raster' ) then + if new.minzoom is not null and (new.type != 'vector' and new.type != 'raster' and new.type != 'raster-dem' and new.type != 'seasketch-mvt' and new.type != 'seasketch-raster' and new.type != 'arcgis-raster-tiles' ) then raise 'minzoom may only be set for tiled sources (vector, raster, raster-dem)'; end if; if new.coordinates is null and (new.type = 'video' or new.type = 'image') then @@ -8054,6 +8056,66 @@ CREATE FUNCTION public.has_session() RETURNS boolean COMMENT ON FUNCTION public.has_session() IS '@omit'; +-- +-- Name: id_lookup_get_key(integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.id_lookup_get_key(key integer) RETURNS integer + LANGUAGE plpgsql + AS $$ + begin + if lookup is null then + raise exception 'lookup is null'; + else + return (lookup->key)::int; + end if; + end; + $$; + + +-- +-- Name: id_lookup_get_key(jsonb, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.id_lookup_get_key(lookup jsonb, key integer) RETURNS integer + LANGUAGE plpgsql + AS $$ + begin + if lookup is null then + raise exception 'lookup is null'; + else + if (lookup->key::text)::int is null then + raise exception 'key % not found in lookup. %', key::text, lookup; + else + return (lookup->key::text)::int; + end if; + end if; + end; + $$; + + +-- +-- Name: id_lookup_set_key(jsonb, integer, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.id_lookup_set_key(lookup jsonb, key integer, value integer) RETURNS jsonb + LANGUAGE plpgsql + AS $$ + begin + if lookup is null then + raise exception 'lookup is null'; + else + raise warning 'key: %, value: %', key, value; + return jsonb_set( + lookup, + array[key::text], + value::text::jsonb + ); + end if; + end; + $$; + + -- -- Name: table_of_contents_items; Type: TABLE; Schema: public; Owner: - -- @@ -8213,10 +8275,11 @@ CREATE FUNCTION public.import_arcgis_services("projectId" integer, items public. source_id_map jsonb; layer_id_map jsonb; item_id_map jsonb; + interactive_layers int[]; begin - source_id_map = '[]'::jsonb; - layer_id_map = '[]'::jsonb; - item_id_map = '[]'::jsonb; + source_id_map = '{}'::jsonb; + layer_id_map = '{}'::jsonb; + item_id_map = '{}'::jsonb; if session_is_admin("projectId") then for i in array_lower(sources, 1)..array_upper(sources, 1) loop source = sources[i]; @@ -8233,49 +8296,106 @@ CREATE FUNCTION public.import_arcgis_services("projectId" integer, items public. source.fetch_strategy ) returning id into source_id; select - jsonb_set( - source_id_map, - array[source.id::text], - source_id::text::jsonb - ) + id_lookup_set_key(source_id_map, source.id, source_id) into source_id_map; + insert into data_layers ( project_id, data_source_id ) values ( "projectId", - (source_id_map->source.id)::int + source_id ) returning id into layer_id; select - jsonb_set( - layer_id_map, - array[source.id::text], - layer_id::text::jsonb - ) + id_lookup_set_key(layer_id_map, source.id, layer_id) into layer_id_map; + interactive_layers = array_append(interactive_layers, layer_id); + elsif source.type = 'arcgis-raster-tiles' then + insert into data_sources ( + project_id, + type, + url, + use_device_pixel_ratio + ) values ( + "projectId", + 'arcgis-raster-tiles', + source.url, + true + ) returning id into source_id; + insert into data_layers ( + project_id, + data_source_id + ) values ( + "projectId", + source_id + ) returning id into layer_id; + elsif source.type = 'arcgis-dynamic-mapserver' then + insert into data_sources ( + project_id, + type, + url, + use_device_pixel_ratio + ) values ( + "projectId", + 'arcgis-dynamic-mapserver', + source.url, + true + ) returning id into source_id; + -- create data layers for each sublayer + for i in array_lower(items, 1)..array_upper(items, 1) loop + if items[i].sublayer_id is not null then + insert into data_layers ( + project_id, + data_source_id, + sublayer + ) values ( + "projectId", + source_id, + items[i].sublayer_id + ) returning id into layer_id; + interactive_layers = array_append(interactive_layers, layer_id); + select + id_lookup_set_key(layer_id_map, items[i].id, layer_id) + into layer_id_map; + end if; + end loop; else raise exception 'Source type % not supported', source.type; end if; end loop; - for i in array_lower(items, 1)..array_upper(items, 1) loop + if array_length(items, 1) > 1 then + perform import_subtree("projectId", items, layer_id_map, null); + else insert into table_of_contents_items ( project_id, is_draft, is_folder, data_layer_id, title, - stable_id - -- path + stable_id, + path ) values ( "projectId", true, - items[i].is_folder, - (layer_id_map->items[i].source_id)::int, - items[i].title, - items[i].stable_id - -- items[i].stable_id::ltree - ) returning id into item_id; - end loop; + false, + layer_id, + items[1].title, + items[1].stable_id, + items[1].stable_id::ltree + ); + end if; + update interactivity_settings + set type = 'ALL_PROPERTIES_POPUP' + where id = any( + ( + select + interactivity_settings_id + from + data_layers + where + id = any(interactive_layers) + ) + ); else raise exception 'Only admins can import ArcGIS services'; end if; @@ -8283,6 +8403,63 @@ CREATE FUNCTION public.import_arcgis_services("projectId" integer, items public. $$; +-- +-- Name: import_subtree(integer, public.arcgis_import_item[], jsonb, public.ltree); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.import_subtree("projectId" integer, items public.arcgis_import_item[], layer_id_lookup jsonb, path public.ltree) RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + AS $$ + declare + item_path ltree; + layer_id int; + parent_sid text; + begin + for i in array_lower(items, 1)..array_upper(items, 1) loop + if (path is null and items[i].parent_id is null) or items[i].parent_id = subpath(path, -1, 1)::text then + if path is null then + item_path = items[i].stable_id::ltree; + parent_sid = null; + else + item_path = path || items[i].stable_id::ltree; + parent_sid = subpath(path, -1, 1)::text; + end if; + if items[i].is_folder = true then + layer_id = null; + elsif items[i].sublayer_id is not null then + layer_id = id_lookup_get_key(layer_id_lookup, items[i].id); + else + layer_id = id_lookup_get_key(layer_id_lookup, items[i].source_id); + end if; + insert into table_of_contents_items ( + project_id, + is_draft, + is_folder, + data_layer_id, + title, + stable_id, + path, + parent_stable_id + ) values ( + "projectId", + true, + items[i].is_folder, + layer_id, + items[i].title, + items[i].stable_id, + item_path, + parent_sid + ); + if items[i].is_folder = true then + perform import_subtree("projectId", items, layer_id_lookup, item_path); + end if; + end if; + end loop; + return 0; + end; + $$; + + -- -- Name: initialize_blank_sketch_class_form(integer); Type: FUNCTION; Schema: public; Owner: - -- @@ -9812,40 +9989,36 @@ then use the `publishTableOfContents` mutation when it is ready for end-users. CREATE FUNCTION public.projects_imported_arcgis_services(p public.projects) RETURNS text[] LANGUAGE plpgsql STABLE SECURITY DEFINER - AS $$ + AS $_$ declare - services text[]; + data_sources_services text[]; + basemap_services text[]; begin - if session_is_admin(p.id) then - select - array_agg(coalesce(url, original_source_url)) as url - into - services + if true or session_is_admin(p.id) then + select + array_agg(distinct(REGEXP_REPLACE(url, '/[0-9]+$', ''))) + into + basemap_services from - ( - select - basemaps.url, data_sources.original_source_url - from basemaps, data_sources - where - ( - basemaps.project_id = p.id and - basemaps.is_arcgis_tiled_mapservice = true - ) or - ( - data_sources.project_id = p.id and - ( - data_sources.type = 'arcgis-raster-tiles' or - data_sources.type = 'arcgis-vector' or - data_sources.type = 'arcgis-dynamic-mapserver' - ) - ) - ) q; - return services; + basemaps + where + is_arcgis_tiled_mapservice = true and + project_id = p.id; + select + array_agg(distinct(REGEXP_REPLACE(url, '/[0-9]+$', ''))) + from + data_sources + into + data_sources_services + where + project_id = p.id and + type in ('arcgis-raster-tiles', 'arcgis-vector', 'arcgis-dynamic-mapserver'); + return array_cat(data_sources_services, basemap_services); else raise exception 'Permission denied'; end if; end; - $$; + $_$; -- @@ -10736,7 +10909,8 @@ CREATE FUNCTION public.publish_table_of_contents("projectId" integer) RETURNS SE normalized_source_bytes, geostats, upload_task_id, - translated_props + translated_props, + arcgis_fetch_strategy ) select "projectId", @@ -10774,7 +10948,8 @@ CREATE FUNCTION public.publish_table_of_contents("projectId" integer) RETURNS SE normalized_source_bytes, geostats, upload_task_id, - translated_props + translated_props, + arcgis_fetch_strategy from data_sources where @@ -12455,6 +12630,25 @@ CREATE FUNCTION public.table_of_contents_items_has_metadata(toc public.table_of_ $$; +-- +-- Name: table_of_contents_items_is_custom_gl_source(public.table_of_contents_items); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.table_of_contents_items_is_custom_gl_source(t public.table_of_contents_items) RETURNS boolean + LANGUAGE plpgsql STABLE SECURITY DEFINER + AS $$ + declare + source_type text; + begin + if t.data_layer_id is null then + return null; + end if; + select type into source_type from data_sources where id = (select data_source_id from data_layers where id = t.data_layer_id); + return source_type = 'arcgis-dynamic-mapserver' or source_type = 'arcgis-vector' or source_type = 'arcgis-raster-tiles'; + end; + $$; + + -- -- Name: table_of_contents_items_primary_download_url(public.table_of_contents_items); Type: FUNCTION; Schema: public; Owner: - -- @@ -12509,6 +12703,28 @@ CREATE FUNCTION public.table_of_contents_items_project_update() RETURNS trigger $$; +-- +-- Name: table_of_contents_items_uses_dynamic_metadata(public.table_of_contents_items); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.table_of_contents_items_uses_dynamic_metadata(t public.table_of_contents_items) RETURNS boolean + LANGUAGE plpgsql STABLE SECURITY DEFINER + AS $$ + declare + uses_dynamic_metadata boolean; + begin + if t.metadata is not null then + return false; + end if; + if t.data_layer_id is null then + return false; + end if; + select type = 'arcgis-dynamic-mapserver' or type = 'arcgis-vector' or type = 'arcgis-raster-tiles' into uses_dynamic_metadata from data_sources where id = (select data_source_id from data_layers where id = t.data_layer_id); + return uses_dynamic_metadata; + end; + $$; + + -- -- Name: template_forms(); Type: FUNCTION; Schema: public; Owner: - -- @@ -17553,7 +17769,7 @@ CREATE POLICY data_sources_select ON public.data_sources FOR SELECT USING ((publ CREATE POLICY data_sources_update ON public.data_sources FOR UPDATE USING ((public.session_is_admin(project_id) AND ( SELECT (( SELECT 1 FROM public.table_of_contents_items - WHERE ((table_of_contents_items.data_layer_id = ( SELECT data_layers.id + WHERE ((table_of_contents_items.data_layer_id IN ( SELECT data_layers.id FROM public.data_layers WHERE (data_layers.data_source_id = data_sources.id))) AND (table_of_contents_items.is_draft = false))) IS NULL)))) WITH CHECK (public.session_is_admin(project_id)); @@ -21287,6 +21503,13 @@ GRANT SELECT(translated_props) ON TABLE public.data_sources TO anon; GRANT UPDATE(translated_props) ON TABLE public.data_sources TO seasketch_user; +-- +-- Name: COLUMN data_sources.arcgis_fetch_strategy; Type: ACL; Schema: public; Owner: - +-- + +GRANT UPDATE(arcgis_fetch_strategy) ON TABLE public.data_sources TO seasketch_user; + + -- -- Name: FUNCTION data_sources_uploaded_by(data_source public.data_sources); Type: ACL; Schema: public; Owner: - -- @@ -22662,6 +22885,27 @@ REVOKE ALL ON FUNCTION public.hmac(bytea, bytea, text) FROM PUBLIC; REVOKE ALL ON FUNCTION public.hmac(text, text, text) FROM PUBLIC; +-- +-- Name: FUNCTION id_lookup_get_key(key integer); Type: ACL; Schema: public; Owner: - +-- + +REVOKE ALL ON FUNCTION public.id_lookup_get_key(key integer) FROM PUBLIC; + + +-- +-- Name: FUNCTION id_lookup_get_key(lookup jsonb, key integer); Type: ACL; Schema: public; Owner: - +-- + +REVOKE ALL ON FUNCTION public.id_lookup_get_key(lookup jsonb, key integer) FROM PUBLIC; + + +-- +-- Name: FUNCTION id_lookup_set_key(lookup jsonb, key integer, value integer); Type: ACL; Schema: public; Owner: - +-- + +REVOKE ALL ON FUNCTION public.id_lookup_set_key(lookup jsonb, key integer, value integer) FROM PUBLIC; + + -- -- Name: TABLE table_of_contents_items; Type: ACL; Schema: public; Owner: - -- @@ -22816,6 +23060,13 @@ REVOKE ALL ON FUNCTION public.import_arcgis_services("projectId" integer, items GRANT ALL ON FUNCTION public.import_arcgis_services("projectId" integer, items public.arcgis_import_item[], sources public.arcgis_import_source[]) TO seasketch_user; +-- +-- Name: FUNCTION import_subtree("projectId" integer, items public.arcgis_import_item[], layer_id_lookup jsonb, path public.ltree); Type: ACL; Schema: public; Owner: - +-- + +REVOKE ALL ON FUNCTION public.import_subtree("projectId" integer, items public.arcgis_import_item[], layer_id_lookup jsonb, path public.ltree) FROM PUBLIC; + + -- -- Name: FUNCTION index(public.ltree, public.ltree); Type: ACL; Schema: public; Owner: - -- @@ -27914,6 +28165,14 @@ REVOKE ALL ON FUNCTION public.table_of_contents_items_has_metadata(toc public.ta GRANT ALL ON FUNCTION public.table_of_contents_items_has_metadata(toc public.table_of_contents_items) TO anon; +-- +-- Name: FUNCTION table_of_contents_items_is_custom_gl_source(t public.table_of_contents_items); Type: ACL; Schema: public; Owner: - +-- + +REVOKE ALL ON FUNCTION public.table_of_contents_items_is_custom_gl_source(t public.table_of_contents_items) FROM PUBLIC; +GRANT ALL ON FUNCTION public.table_of_contents_items_is_custom_gl_source(t public.table_of_contents_items) TO anon; + + -- -- Name: FUNCTION table_of_contents_items_primary_download_url(item public.table_of_contents_items); Type: ACL; Schema: public; Owner: - -- @@ -27937,6 +28196,14 @@ GRANT ALL ON FUNCTION public.table_of_contents_items_project(t public.table_of_c REVOKE ALL ON FUNCTION public.table_of_contents_items_project_update() FROM PUBLIC; +-- +-- Name: FUNCTION table_of_contents_items_uses_dynamic_metadata(t public.table_of_contents_items); Type: ACL; Schema: public; Owner: - +-- + +REVOKE ALL ON FUNCTION public.table_of_contents_items_uses_dynamic_metadata(t public.table_of_contents_items) FROM PUBLIC; +GRANT ALL ON FUNCTION public.table_of_contents_items_uses_dynamic_metadata(t public.table_of_contents_items) TO anon; + + -- -- Name: FUNCTION template_forms(); Type: ACL; Schema: public; Owner: - -- diff --git a/packages/api/src/graphileOptions.ts b/packages/api/src/graphileOptions.ts index e0b52afe7..89d551450 100644 --- a/packages/api/src/graphileOptions.ts +++ b/packages/api/src/graphileOptions.ts @@ -39,6 +39,7 @@ import { import SketchClassStylePlugin from "./plugins/sketchClassStylePlugin"; import DataUploadTasksSubscriptionPlugin from "./plugins/dataUploadTasksSubscriptionPlugin"; import DraftTocStatusPlugin from "./plugins/projectDraftTableOfContentsStatusSubscription"; +import ComputedMetadataPlugin from "./plugins/computedMetadataPlugin"; const pluginHook = makePluginHook([{ ...PgPubsub, ...SentryPlugin }]); @@ -89,6 +90,7 @@ export default function graphileOptions(): PostGraphileOptions { SketchClassStylePlugin, DataUploadTasksSubscriptionPlugin, DraftTocStatusPlugin, + ComputedMetadataPlugin, // reorderSchemaFields(graphqlSchemaModifiers.fieldOrder), // extraDocumentationPlugin(graphqlSchemaModifiers.documentation), ], diff --git a/packages/api/src/plugins/computedMetadataPlugin.ts b/packages/api/src/plugins/computedMetadataPlugin.ts new file mode 100644 index 000000000..458cd3294 --- /dev/null +++ b/packages/api/src/plugins/computedMetadataPlugin.ts @@ -0,0 +1,201 @@ +import { makeExtendSchemaPlugin, gql } from "graphile-utils"; + +const ComputedMetadataPlugin = makeExtendSchemaPlugin((build) => { + return { + typeDefs: gql` + extend type TableOfContentsItem { + """ + Metadata will be returned as directly stored in the SeaSketch + database or computed by fetching from a 3rd party service, + depending on the data source type. + """ + computedMetadata: JSON @requires(columns: ["metadata", "data_layer_id"]) + } + `, + resolvers: { + TableOfContentsItem: { + computedMetadata: async (item, args, context, info) => { + if (item.metadata) { + return item.metadata; + } else if (item.dataLayerId) { + const q = await context.pgClient.query( + `select data_source_id, sublayer from data_layers where id = $1`, + [item.dataLayerId] + ); + if (q.rows.length === 0) { + return null; + } + const { data_source_id, sublayer } = q.rows[0]; + let { rows } = await context.pgClient.query( + `select type, url from data_sources where id = $1`, + [data_source_id] + ); + if (rows.length === 0) { + return null; + } + const { type, url } = rows[0]; + switch (type) { + case "arcgis-vector": + case "arcgis-dynamic-mapserver": + case "arcgis-raster-tiles": + const serviceMetadataResponse = await fetch( + `${url.replace(/\d+[\/]*$/, "")}?f=json` + ); + const serviceMetadata = await serviceMetadataResponse.json(); + + const response = await fetch( + `${url}${sublayer !== null ? `/${sublayer}` : ""}?f=json` + ); + const layerMetadata = await response.json(); + return generateMetadataForLayer( + url, + serviceMetadata, + layerMetadata + ); + default: + return null; + } + } else { + return null; + } + }, + }, + }, + }; +}); + +// TODO: Replace these metadata generation functions with those exported +// from @seasketch/mapbox-gl-esri-sources. +// Right now I'm having trouble getting the server to build when importing +// that package. + +export function contentOrFalse(str?: string) { + if (str && str.length > 0) { + return str; + } else { + return false; + } +} + +function pickDescription(info: any, layer?: any) { + return ( + contentOrFalse(layer?.description) || + contentOrFalse(info.description) || + contentOrFalse(info.documentInfo?.Subject) || + contentOrFalse(info.documentInfo?.Comments) + ); +} + +export function generateMetadataForLayer( + url: string, + mapServerInfo: any, + layer: any +) { + const attribution = + contentOrFalse(layer.copyrightText) || + contentOrFalse(mapServerInfo.copyrightText) || + contentOrFalse(mapServerInfo.documentInfo?.Author); + const description = pickDescription(mapServerInfo, layer); + let keywords = + mapServerInfo.documentInfo?.Keywords && + mapServerInfo.documentInfo?.Keywords.length + ? mapServerInfo.documentInfo?.Keywords.split(",") + : []; + return { + type: "doc", + content: [ + { + type: "heading", + attrs: { level: 1 }, + content: [ + { + type: "text", + text: layer.name || mapServerInfo.documentInfo?.Title || "", + }, + ], + }, + ...(description + ? [ + { + type: "paragraph", + content: [ + { + type: "text", + text: description || "", + }, + ], + }, + ] + : []), + ...(attribution + ? [ + { type: "paragraph" }, + { + type: "heading", + attrs: { level: 3 }, + content: [{ type: "text", text: "Attribution" }], + }, + { + type: "paragraph", + content: [ + { + type: "text", + text: attribution, + }, + ], + }, + ] + : []), + ...(keywords && keywords.length + ? [ + { type: "paragraph" }, + { + type: "heading", + attrs: { level: 3 }, + content: [ + { + type: "text", + text: "Keywords", + }, + ], + }, + { + type: "bullet_list", + marks: [], + attrs: {}, + content: keywords.map((word: any) => ({ + type: "list_item", + content: [ + { + type: "paragraph", + content: [{ type: "text", text: word || "" }], + }, + ], + })), + }, + ] + : []), + { type: "paragraph" }, + { + type: "paragraph", + content: [ + { + type: "text", + marks: [ + { + type: "link", + attrs: { + href: url, + title: "ArcGIS Server", + }, + }, + ], + text: url, + }, + ], + }, + ], + }; +} + +export default ComputedMetadataPlugin; diff --git a/packages/api/src/plugins/sentryPlugin.ts b/packages/api/src/plugins/sentryPlugin.ts index a96551973..56e0b8415 100644 --- a/packages/api/src/plugins/sentryPlugin.ts +++ b/packages/api/src/plugins/sentryPlugin.ts @@ -23,7 +23,7 @@ const SentryPlugin: PostGraphilePlugin = { if (operations.length) { const graphqlDetails = { operationType: operations[0].operation, - name: operations[0].name.value, + name: operations[0].name?.value || "Un-named operation", role: pgRole, errorCount, resultStatusCode: resultStatusCode || 200, diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json index dba73659a..86e19725b 100644 --- a/packages/api/tsconfig.json +++ b/packages/api/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ - "target": "es2019" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, + "target": "ES2019" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, // "lib": [], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ @@ -39,7 +39,7 @@ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ @@ -55,7 +55,7 @@ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - "resolveJsonModule": true, + // "resolveJsonModule": true, /* Experimental Options */ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ @@ -63,7 +63,8 @@ /* Advanced Options */ "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, "allowJs": true, - "checkJs": false + "checkJs": false, + "skipLibCheck": true }, "include": [ "**/*", diff --git a/packages/client/.storybook/main.js b/packages/client/.storybook/main.js index 32ce43328..57d1f6f45 100644 --- a/packages/client/.storybook/main.js +++ b/packages/client/.storybook/main.js @@ -22,6 +22,16 @@ module.exports = { ], include: path.resolve(__dirname, "../"), }); + config.module.rules.push({ + test: /\.cjs/, + include: /node_modules/, + type: "javascript/auto", + }); + config.module.rules.push({ + test: /\.mjs/, + include: /node_modules/, + type: "javascript/auto", + }); return config; }, }; diff --git a/packages/client/craco.config.js b/packages/client/craco.config.js index 1ce3256a4..991f2f4e3 100644 --- a/packages/client/craco.config.js +++ b/packages/client/craco.config.js @@ -48,6 +48,11 @@ module.exports = { include: /node_modules/, type: "javascript/auto", }, + { + test: /\.cjs/, + include: /node_modules/, + type: "javascript/auto", + }, ], }, }, @@ -95,4 +100,12 @@ module.exports = { return babelLoaderOptions; }, }, + jest: { + configure: { + moduleNameMapper: { + "mapbox-gl/dist/style-spec/index.es.js": + "mapbox-gl/dist/style-spec/index.cjs", + }, + }, + }, }; diff --git a/packages/client/package-lock.json b/packages/client/package-lock.json index 67536c048..22376f4b9 100644 --- a/packages/client/package-lock.json +++ b/packages/client/package-lock.json @@ -28,9 +28,11 @@ "@percy/cli": "^1.15.0", "@percy/cypress": "^3.1.2", "@popperjs/core": "^2.11.6", + "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-dropdown-menu": "^2.0.5", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-menubar": "^1.0.3", + "@radix-ui/react-scroll-area": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.6", "@sentry/react": "^6.17.1", "@sentry/tracing": "^6.17.1", @@ -64,6 +66,7 @@ "@types/loadable__component": "^5.13.0", "@types/lodash.clonedeep": "^4.5.6", "@types/lodash.debounce": "^4.0.6", + "@types/lodash.isequal": "^4.5.6", "@types/lodash.set": "^4.3.6", "@types/lodash.setwith": "^4.3.6", "@types/lodash.sortby": "^4.7.6", @@ -150,11 +153,13 @@ "localforage": "^1.10.0", "lodash.clonedeep": "^4.5.0", "lodash.debounce": "^4.0.8", + "lodash.isequal": "^4.5.0", "lodash.set": "^4.3.2", "lodash.setwith": "^4.3.2", "lodash.sortby": "^4.7.0", "lru-cache": "^6.0.0", - "mapbox-gl": "2.15.0", + "mapbox-expression": "^0.0.3", + "mapbox-gl": "2.15", "mapbox-gl-draw-rectangle-mode": "^1.0.4", "md5": "^2.3.0", "mnemonist": "^0.39.2", @@ -209,7 +214,7 @@ "strind": "^0.3.1", "subscriptions-transport-ws": "^0.9.18", "tailwindcss-dir": "^4.0.0", - "typescript": "^4.1.3", + "typescript": "^5.2", "undefined": "https://github.com/arthurgeron/eslint-plugin-idiomatic-jsx.git", "union-subdivided-polygons": "^0.9.1", "use-prosemirror": "^1.2.1", @@ -247,8 +252,8 @@ "@storybook/preset-create-react-app": "^3.1.4", "@storybook/react": "^6.0.21", "@types/pg": "^8.6.1", - "@typescript-eslint/eslint-plugin": "^4.15.0", - "@typescript-eslint/parser": "^4.15.0", + "@typescript-eslint/eslint-plugin": "^6.0", + "@typescript-eslint/parser": "^6.0", "autoprefixer": "^9.8.7", "babel-eslint": "^10.1.0", "chokidar-cli": "^2.1.0", @@ -272,6 +277,43 @@ "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.16" } }, + "../../../../mapbox-gl-arcgis-featureserver": { + "version": "0.0.8", + "extraneous": true, + "license": "Apache-2.0", + "dependencies": { + "@mapbox/tilebelt": "^1.0.2", + "arcgis-pbf-parser": "0.0.1" + }, + "devDependencies": { + "@ant-design/icons-vue": "^5.1.9", + "@rollup/plugin-commonjs": "^19.0.0", + "@rollup/plugin-node-resolve": "^13.0.0", + "ant-design-vue": "^1.7.3", + "ava": "^1.0.1", + "benchmark": "^2.1.4", + "cross-env": "^5.2.0", + "css-loader": "^2.1.0", + "eslint": "^7.26.0", + "eslint-config-mourner": "^3.0.0", + "esm": "^3.0.84", + "file-loader": "^6.0.0", + "json-loader": "^0.5.7", + "load-json-file": "^5.1.0", + "maplibre-gl": "^1.13.0-rc.5", + "rollup": "^2.48.0", + "rollup-plugin-terser": "^7.0.2", + "style-loader": "^2.0.0", + "vue": "^2.5.22", + "vue-loader": "^15.6.2", + "vue-range-component": "^1.0.3", + "vue-router": "^3.5.1", + "vue-template-compiler": "^2.5.22", + "webpack": "^4.29.0", + "webpack-cli": "^3.2.1", + "webpack-dev-server": "^3.1.14" + } + }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", @@ -3160,6 +3202,42 @@ "node": ">=12" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -6201,6 +6279,14 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@radix-ui/number": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", + "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, "node_modules/@radix-ui/primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", @@ -6209,6 +6295,37 @@ "@babel/runtime": "^7.13.10" } }, + "node_modules/@radix-ui/react-accordion": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.1.2.tgz", + "integrity": "sha512-fDG7jcoNKVjSK6yfmuAs0EnPDro0WMXIhMtXdTBWqEioVW206ku+4Lw07e+13lUkFkpoEQ2PdeMIAGpdqEAmDg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collapsible": "1.0.3", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-arrow": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", @@ -6232,6 +6349,36 @@ } } }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.0.3.tgz", + "integrity": "sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collection": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", @@ -6650,6 +6797,37 @@ } } }, + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.0.5.tgz", + "integrity": "sha512-b6PAgH4GQf9QEn8zbT2XUHpW5z8BzqEc7Kl11TwDrvuTrxlkcjTD5qa/bxgKr+nmuXKu4L/W5UZ4mlP/VG/5Gw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/number": "1.0.1", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", @@ -10534,9 +10712,9 @@ "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==" }, "node_modules/@types/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-YIQtIg4PKr7ZyqNPZObpxfHsHEmuB8dXCxd6qVcGuQVDK2bpsF7bYNnBJ4Nn7giuACZg+WewExgrtAJ3XnA4Xw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.3.tgz", + "integrity": "sha512-Wny3a2UXn5FEA1l7gc6BbpoV5mD1XijZqgkp4TRgDCDL5r3B5ieOFGUX5h3n78Tr1MEG7BfvoM8qeztdvNU0fw==", "dependencies": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -10598,9 +10776,9 @@ } }, "node_modules/@types/js-yaml": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.6.tgz", - "integrity": "sha512-ACTuifTSIIbyksx2HTon3aFtCKWcID7/h3XEmRpDYdMCXxPbl+m9GteOJeaAkiAta/NJaSFuA7ahZ0NkwajDSw==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.7.tgz", + "integrity": "sha512-RJZP9WAMMr1514KbdSXkLRrKvYQacjr1+HWnY8pui/uBTBoSgD9ZGR17u/d4nb9NpERp0FkdLBe7hq8NIPBgkg==", "dev": true }, "node_modules/@types/json-schema": { @@ -10662,6 +10840,14 @@ "@types/lodash": "*" } }, + "node_modules/@types/lodash.isequal": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.6.tgz", + "integrity": "sha512-Ww4UGSe3DmtvLLJm2F16hDwEQSv7U0Rr8SujLUA2wHI2D2dm8kPu6Et+/y303LfjTIwSBKXB/YTUcAKpem/XEg==", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/lodash.set": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/@types/lodash.set/-/lodash.set-4.3.7.tgz", @@ -10822,9 +11008,9 @@ "integrity": "sha512-hw6bDMjvm+QTvEC+pRLpnTknQXoPu8Fnf+A+zX9HB7j/7RfYajFSbdukabo3adPwvvEHhIMafQl0R0Tpej7clQ==" }, "node_modules/@types/pg": { - "version": "8.10.3", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.10.3.tgz", - "integrity": "sha512-BACzsw64lCZesclRpZGu55tnqgFAYcrCBP92xLh1KLypZLCOsvJTSTgaoFVTy3lCys/aZTQzfeDxtjwrvdzL2g==", + "version": "8.10.5", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.10.5.tgz", + "integrity": "sha512-GS3ebGcSJQqKSnq4/WnSH1XQvx0vTDLEmqLENk7onKvTnry9BWPsZiZeUMJlEPw+5bCQDzfxZFhxlUztpNCKgQ==", "dev": true, "dependencies": { "@types/node": "*", @@ -10945,9 +11131,9 @@ "integrity": "sha512-W3ue/GYWXBOpkRm0VSoifrP3HV0Ni47aVJWvXyWMcbtpBy/l/K/smBRiJ+fI8f7shXRjZBiux+iJzYbh7VmcZg==" }, "node_modules/@types/react": { - "version": "16.14.48", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.48.tgz", - "integrity": "sha512-7HP7K9IyuP6CpxEHmfRPEl21pwra+nSgZHXhyq7WOkxhIGYtSpIHJBijh4zuScgelrPxsUXVPDRkSKHhT+6nkg==", + "version": "16.14.49", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.49.tgz", + "integrity": "sha512-WHKMS4fIlDpeLVKCGDs5k1MTCyqh1tyFhGqouSFgpPsCsWNDTtiMpTYUcJnHg66kp03ubqb4BFjd5+7gS3MyHw==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -11136,6 +11322,12 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.4.tgz", "integrity": "sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ==" }, + "node_modules/@types/semver": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.5.tgz", + "integrity": "sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==", + "dev": true + }, "node_modules/@types/shallowequal": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@types/shallowequal/-/shallowequal-1.1.2.tgz", @@ -11283,29 +11475,33 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", - "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", - "dependencies": { - "@typescript-eslint/experimental-utils": "4.33.0", - "@typescript-eslint/scope-manager": "4.33.0", - "debug": "^4.3.1", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.1.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.12.0.tgz", + "integrity": "sha512-XOpZ3IyJUIV1b15M7HVOpgQxPPF7lGXgsfcEIu3yDxFPaf/xZKt7s9QO/pbk7vpWQyVulpJbu4E5LwpZiQo4kA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.12.0", + "@typescript-eslint/type-utils": "6.12.0", + "@typescript-eslint/utils": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^4.0.0", - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -11313,10 +11509,70 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.12.0.tgz", + "integrity": "sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.12.0.tgz", + "integrity": "sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.12.0.tgz", + "integrity": "sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.12.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -11351,24 +11607,26 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", - "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.12.0.tgz", + "integrity": "sha512-s8/jNFPKPNRmXEnNXfuo1gemBdVmpQsK1pcu+QIvuNJuhFzGrpD7WjOcvDc/+uEdfzSYpNu7U/+MmbScjoQ6vg==", + "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "debug": "^4.3.1" + "@typescript-eslint/scope-manager": "6.12.0", + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/typescript-estree": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0", + "debug": "^4.3.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -11376,6 +11634,136 @@ } } }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.12.0.tgz", + "integrity": "sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.12.0.tgz", + "integrity": "sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.12.0.tgz", + "integrity": "sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.12.0.tgz", + "integrity": "sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.12.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@typescript-eslint/scope-manager": { "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", @@ -11392,6 +11780,146 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.12.0.tgz", + "integrity": "sha512-WWmRXxhm1X8Wlquj+MhsAG4dU/Blvf1xDgGaYCzfvStP2NwPQh6KBvCDbiOEvaE0filhranjIlK/2fSTVwtBng==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.12.0", + "@typescript-eslint/utils": "6.12.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.12.0.tgz", + "integrity": "sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.12.0.tgz", + "integrity": "sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.12.0.tgz", + "integrity": "sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.12.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@typescript-eslint/types": { "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", @@ -11444,6 +11972,161 @@ "node": ">=10" } }, + "node_modules/@typescript-eslint/utils": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.12.0.tgz", + "integrity": "sha512-LywPm8h3tGEbgfyjYnu3dauZ0U7R60m+miXgKcZS8c7QALO9uWJdvNoP+duKTk2XMWc7/Q3d/QiCuLN9X6SWyQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.12.0", + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/typescript-estree": "6.12.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.12.0.tgz", + "integrity": "sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.12.0.tgz", + "integrity": "sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.12.0.tgz", + "integrity": "sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.12.0", + "@typescript-eslint/visitor-keys": "6.12.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.12.0.tgz", + "integrity": "sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.12.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@typescript-eslint/visitor-keys": { "version": "4.33.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", @@ -14117,9 +14800,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001546", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001546.tgz", - "integrity": "sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw==", + "version": "1.0.30001547", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001547.tgz", + "integrity": "sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==", "funding": [ { "type": "opencollective", @@ -18061,9 +18744,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.544", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.544.tgz", - "integrity": "sha512-54z7squS1FyFRSUqq/knOFSptjjogLZXbKcYk3B0qkE1KZzvqASwRZnY2KzZQJqIYLVD38XZeoiMRflYSwyO4w==" + "version": "1.4.549", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.549.tgz", + "integrity": "sha512-gpXfJslSi4hYDkA0mTLEpYKRv9siAgSUgZ+UWyk+J5Cttpd1ThCVwdclzIwQSclz3hYn049+M2fgrP1WpvF8xg==" }, "node_modules/elegant-spinner": { "version": "1.0.1", @@ -21783,6 +22466,12 @@ "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "node_modules/graphlib": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", @@ -28180,6 +28869,14 @@ "node": ">=0.10.0" } }, + "node_modules/mapbox-expression": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mapbox-expression/-/mapbox-expression-0.0.3.tgz", + "integrity": "sha512-P2hiPXPxXzmOTZUP2zLa+kjN42QbvAM125tH/akt+hdOOu4QgxAvXGcffiKMLVxOFE28vceD8Qc11gWUbnkc/A==", + "peerDependencies": { + "mapbox-gl": "^1.0" + } + }, "node_modules/mapbox-gl": { "version": "2.15.0", "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-2.15.0.tgz", @@ -34283,6 +34980,93 @@ "@types/node": "*" } }, + "node_modules/react-scripts/node_modules/@typescript-eslint/eslint-plugin": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", + "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", + "dependencies": { + "@typescript-eslint/experimental-utils": "4.33.0", + "@typescript-eslint/scope-manager": "4.33.0", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.1.8", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^4.0.0", + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/react-scripts/node_modules/@typescript-eslint/parser": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", + "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", + "dependencies": { + "@typescript-eslint/scope-manager": "4.33.0", + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/typescript-estree": "4.33.0", + "debug": "^4.3.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/react-scripts/node_modules/acorn": { "version": "6.4.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", @@ -40535,6 +41319,18 @@ "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-dedent": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", @@ -40843,15 +41639,15 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", + "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/typeson": { diff --git a/packages/client/package.json b/packages/client/package.json index 3bb78ca43..afeae3adf 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -23,11 +23,14 @@ "@percy/cli": "^1.15.0", "@percy/cypress": "^3.1.2", "@popperjs/core": "^2.11.6", + "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-dropdown-menu": "^2.0.5", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-menubar": "^1.0.3", + "@radix-ui/react-scroll-area": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.6", "@seasketch/map-tile-cache-calculator": "^1.0.0", + "@seasketch/mapbox-gl-esri-sources": "0.9.0", "@seasketch/vector-data-source": "^1.0.0", "@sentry/react": "^6.17.1", "@sentry/tracing": "^6.17.1", @@ -61,6 +64,7 @@ "@types/loadable__component": "^5.13.0", "@types/lodash.clonedeep": "^4.5.6", "@types/lodash.debounce": "^4.0.6", + "@types/lodash.isequal": "^4.5.6", "@types/lodash.set": "^4.3.6", "@types/lodash.setwith": "^4.3.6", "@types/lodash.sortby": "^4.7.6", @@ -147,13 +151,14 @@ "localforage": "^1.10.0", "lodash.clonedeep": "^4.5.0", "lodash.debounce": "^4.0.8", + "lodash.isequal": "^4.5.0", "lodash.set": "^4.3.2", "lodash.setwith": "^4.3.2", "lodash.sortby": "^4.7.0", "lru-cache": "^6.0.0", - "mapbox-gl": "2.15.0", + "mapbox-expression": "^0.0.3", + "mapbox-gl": "2.15", "mapbox-gl-draw-rectangle-mode": "^1.0.4", - "mapbox-gl-esri-feature-layers": "^1.0.0", "md5": "^2.3.0", "mnemonist": "^0.39.2", "mustache": "^4.1.0", @@ -207,7 +212,7 @@ "strind": "^0.3.1", "subscriptions-transport-ws": "^0.9.18", "tailwindcss-dir": "^4.0.0", - "typescript": "^4.1.3", + "typescript": "^5.2", "undefined": "https://github.com/arthurgeron/eslint-plugin-idiomatic-jsx.git", "union-subdivided-polygons": "^0.9.1", "use-prosemirror": "^1.2.1", @@ -281,8 +286,8 @@ "@storybook/react": "^6.0.21", "@types/node": "^17.0.17", "@types/pg": "^8.6.1", - "@typescript-eslint/eslint-plugin": "^4.15.0", - "@typescript-eslint/parser": "^4.15.0", + "@typescript-eslint/eslint-plugin": "^6.0", + "@typescript-eslint/parser": "^6.0", "autoprefixer": "^9.8.7", "babel-eslint": "^10.1.0", "chokidar-cli": "^2.1.0", diff --git a/packages/client/src/MeasureControl.tsx b/packages/client/src/MeasureControl.tsx index fc0010175..ed948ad5c 100644 --- a/packages/client/src/MeasureControl.tsx +++ b/packages/client/src/MeasureControl.tsx @@ -398,7 +398,6 @@ class MeasureControl extends EventEmitter { } ); } else { - // console.log("lock request denied"); } } } diff --git a/packages/client/src/admin/MetadataEditor.tsx b/packages/client/src/admin/MetadataEditor.tsx index 154eef1d8..cdf7410cb 100644 --- a/packages/client/src/admin/MetadataEditor.tsx +++ b/packages/client/src/admin/MetadataEditor.tsx @@ -13,6 +13,7 @@ import { MutationResult } from "@apollo/client"; import { Trans, useTranslation } from "react-i18next"; import useDialog from "../components/useDialog"; import Modal from "../components/Modal"; +import { Pencil1Icon } from "@radix-ui/react-icons"; const { schema, plugins } = editorConfig; interface MetadataEditorProps { @@ -27,6 +28,9 @@ interface MetadataEditorProps { error?: Error; /* metadata document as json */ startingDocument?: any; + /* whether the metadata document is using dynamic metadata from a CustomGLSource */ + usingDynamicMetadata?: boolean; + dynamicMetadataAvailable?: boolean; } export default function MetadataEditor({ @@ -36,6 +40,8 @@ export default function MetadataEditor({ loading, error, startingDocument, + usingDynamicMetadata, + dynamicMetadataAvailable, }: MetadataEditorProps) { const [state, setState] = useProseMirror({ schema }); const [changes, setChanges] = useState(false); @@ -94,6 +100,21 @@ export default function MetadataEditor({ Discard Changes )} + {usingDynamicMetadata && ( + - )} - - - )}{" "} - + ); + })} + {showMoreColumns === false && + source.geostats.attributes.length > 4 && ( + + )} + + } + /> + )} + )} {source?.type === DataSourceTypes.ArcgisVector && ( <> -
-
-
- Source Type -
-
- - Vector data hosted on ArcGIS Server - -
-
-
-
- Source Server -
-
+ + + -
-
-
+ } + /> + { + updateFetchStrategy({ + variables: { + sourceId: source.id, + fetchStrategy: e.target + .value as ArcgisFeatureLayerFetchStrategy, + }, + }); + }} + > + + + + + } + > + + SeaSketch determines an appropriate strategy for + retrieving vector data at import time.
+ GeoJSON requests offer best performance when + datasets can be downloaded in a single request.{" "} + Tiled requests can be used for datasets that + are too large or contain > 2000 features.{" "} + Auto is not recommended other than for + debugging. +
+ +
+
+ + {perfModalOpen && ( + setPerfModalOpen(false)} + /> + )} )} {source?.type === DataSourceTypes.ArcgisDynamicMapserver && ( - <> -
-
-
- Source Type -
-
+ + + + {source + .url!.replace("https://", "") + .replace("http://", "")} + + } + /> + - ArcGIS Dynamic Map Service + Supported. Users can control layer visibility, + opacity, and ordering. -
-
-
-
- Source Server -
-
- - {source - .url!.replace("https://", "") - .replace("http://", "")} - -
-
-
-
- Dynamic Layers -
-
- {source.supportsDynamicLayers ? ( - - Supported. Users can control layer visibility, - opacity, and ordering. - - ) : ( - - Unsupported. Users will not be able to - control the visibility and ordering of individual - layers. - - )} -
-
-
- - )} - - {source?.type === DataSourceTypes.ArcgisVector && ( - { - const queryParameters = { - ...source.queryParameters, - geometryPrecision: e.target.value, - }; - client.writeFragment({ - id: `DataSource:${source.id}`, - fragment: gql` - fragment NewQueryParameters on DataSource { - queryParameters - } - `, - data: { - queryParameters, - }, - }); - updateQueryParameters({ - variables: { - sourceId: source.id, - queryParameters, - }, - }); - }} + ) : ( + + Unsupported. Users will not be able to + control the visibility and ordering of individual + layers. + + ) + } + /> + { + updateEnableHighDpiRequests({ + variables: { + sourceId: source.id, + useDevicePixelRatio: value, + }, + }); + }} + /> + } > - - - - } - > - {t("Using a lower level of precision reduces download size")} - - )} - {source?.type === DataSourceTypes.ArcgisDynamicMapserver && ( - <> - { - client.writeFragment({ - id: `DataSource:${source.id}`, - fragment: gql` - fragment UpdateHighDPI on DataSource { - useDevicePixelRatio - } - `, - data: { - useDevicePixelRatio: value, - }, - }); - updateEnableHighDpiRequests({ - variables: { - sourceId: source.id, - useDevicePixelRatio: value, - }, - }); - }} - /> - } - > - - Request higher resolution images when the user has a - "Retina" or 4k display. Maps will be much more detailed, - but it demands more of the data server. - - - { - client.writeFragment({ - id: `DataSource:${source.id}`, - fragment: gql` - fragment UpdateFormat on DataSource { - queryParameters - } - `, - data: { - queryParameters: { - ...source.queryParameters, - format: e.target.value, + + Request higher resolution images when the user has a + "Retina" or 4k display. Maps will be much more detailed, + but it demands more of the data server. + + + { + client.writeFragment({ + id: `DataSource:${source.id}`, + fragment: gql` + fragment UpdateFormat on DataSource { + queryParameters + } + `, + data: { + queryParameters: { + ...source.queryParameters, + format: e.target.value, + }, }, - }, - }); - updateQueryParameters({ - variables: { - sourceId: source.id, - queryParameters: { - ...source.queryParameters, - format: e.target.value, + }); + updateQueryParameters({ + variables: { + sourceId: source.id, + queryParameters: { + ...source.queryParameters, + format: e.target.value, + }, }, - }, - }); - }} - > - {["PNG", "PNG8", "PNG24", "PNG32", "GIF", "JPG"].map( - (f) => ( - - ) - )} - - } - > - - Imagery data looks best using jpg, for others{" "} - png is a good choice. - - - - )} + }); + }} + > + {["PNG", "PNG8", "PNG24", "PNG32", "GIF", "JPG"].map( + (f) => ( + + ) + )} + + } + > + + Imagery data looks best using jpg, for all + others png is usually the right choice. + + + + )} + {source?.type === DataSourceTypes.ArcgisRasterTiles && ( + + )} + @@ -680,13 +776,28 @@ export default function LayerTableOfContentsItemEditor( {item && selectedTab === "interactivity" && (
- {source && layer && ( - - )} + {source && + layer && + source.type !== DataSourceTypes.ArcgisRasterTiles && ( + + )} + {source && + layer && + source.type === DataSourceTypes.ArcgisRasterTiles && ( +
+ +
+ + Popups and other interactivity options are not supported + for tiled ArcGIS sources. + +
+
+ )}
)} + {isArcGISCustomSource && ( +
+ +
+ + Styling is not available for ArcGIS sources. SeaSketch + respects cartographic styling as it is defined in the service + and will change automatically when the service is updated. + +
+
+ )}
)} diff --git a/packages/client/src/admin/data/TableOfContentsEditor.tsx b/packages/client/src/admin/data/TableOfContentsEditor.tsx index a711f2e6a..cc49e1aeb 100644 --- a/packages/client/src/admin/data/TableOfContentsEditor.tsx +++ b/packages/client/src/admin/data/TableOfContentsEditor.tsx @@ -2,9 +2,13 @@ import { useCallback, useContext, useEffect, useState } from "react"; import { useParams } from "react-router-dom"; import { Trans, useTranslation } from "react-i18next"; import Spinner from "../../components/Spinner"; -import { MapContext } from "../../dataLayers/MapContextManager"; +import { + MapContext, + sourceTypeIsCustomGLSource, +} from "../../dataLayers/MapContextManager"; import TableOfContentsMetadataModal from "../../dataLayers/TableOfContentsMetadataModal"; import { + DataSourceTypes, useDeleteBranchMutation, useDraftStatusSubscription, useDraftTableOfContentsQuery, @@ -30,6 +34,7 @@ import * as Menubar from "@radix-ui/react-menubar"; import { MenuBarContent, MenuBarItem, + MenuBarLabel, MenuBarSeparator, MenuBarSubmenu, MenubarRadioItem, @@ -40,6 +45,15 @@ import { DataUploadDropzoneContext } from "../uploads/DataUploadDropzone"; import { Feature } from "geojson"; import { Map } from "mapbox-gl"; import * as Tooltip from "@radix-ui/react-tooltip"; +import React from "react"; +import { CustomGLSource } from "@seasketch/mapbox-gl-esri-sources"; + +const LazyArcGISCartModal = React.lazy( + () => + import( + /* webpackChunkName: "AdminArcGISBrowser" */ "./arcgis/ArcGISCartModal" + ) +); export default function TableOfContentsEditor() { const [selectedView, setSelectedView] = useState("tree"); @@ -56,11 +70,19 @@ export default function TableOfContentsEditor() { const [updateChildrenMutation] = useUpdateTableOfContentsItemChildrenMutation(); const [folderId, setFolderId] = useState(); + const [openMetadataViewerState, setOpenMetadataViewerState] = useState< + | undefined + | { + itemId: number; + sublayerId?: string; + customGLSource?: CustomGLSource; + } + >(); const [openMetadataItemId, setOpenMetadataItemId] = useState(); - const [openMetadataViewerId, setOpenMetadataViewerId] = useState(); const [publishOpen, setPublishOpen] = useState(false); const [deleteItem] = useDeleteBranchMutation(); const mapContext = useContext(MapContext); + const [arcgisCartOpen, setArcgisCartOpen] = useState(false); useDraftStatusSubscription({ variables: { slug, @@ -121,11 +143,17 @@ export default function TableOfContentsEditor() { const item = items.find((item) => item.stableId === treeItem.id); if (item) { const sidebar = currentSidebarState(); - const contextMenuOptions: (DropdownOption | DropdownDividerProps)[] = [ - { + const contextMenuOptions: (DropdownOption | DropdownDividerProps)[] = + []; + if ( + !item.isFolder || + items.find((i) => i.parentStableId === item.stableId && i.bounds) + ) { + contextMenuOptions.push({ id: "zoom-to", + disabled: !item.bounds && !checkedItems.includes(item.stableId), label: t("Zoom to bounds"), - onClick: () => { + onClick: async () => { let bounds: [number, number, number, number] | undefined; if (item.isFolder) { bounds = createBoundsRecursive(item, items); @@ -134,6 +162,26 @@ export default function TableOfContentsEditor() { bounds = item.bounds.map((coord: string) => parseFloat(coord) ) as [number, number, number, number]; + } else { + const layer = + layersAndSources.data?.projectBySlug?.dataLayersForItems?.find( + (l) => l.id === item.dataLayerId + ); + if (layer && layer.dataSourceId) { + const source = + layersAndSources.data?.projectBySlug?.dataSourcesForItems?.find( + (s) => s.id === layer.dataSourceId + ); + if (source && sourceTypeIsCustomGLSource(source.type)) { + const customSource = + mapContext.manager?.getCustomGLSource(source.id); + const metadata = + await customSource?.getComputedMetadata(); + if (metadata?.bounds) { + bounds = metadata.bounds; + } + } + } } } if ( @@ -151,30 +199,33 @@ export default function TableOfContentsEditor() { }); } }, - }, - { - id: "edit", - label: t("Edit"), - onClick: () => { - if (item?.isFolder) { - setFolderId(item.id); - } else { - if (item.dataLayerId) { - manager?.showTocItems([item.stableId]); - } - setOpenLayerItemId(item.id); + }); + } + contextMenuOptions.push({ + id: "edit", + label: t("Edit"), + onClick: () => { + if (item?.isFolder) { + setFolderId(item.id); + } else { + if (item.dataLayerId) { + manager?.showTocItems([item.stableId]); } - }, + setOpenLayerItemId(item.id); + } }, - ]; + }); if (!item.isFolder || item.hideChildren) { contextMenuOptions.push({ id: "metadata", label: t("Metadata"), onClick: () => { - setOpenMetadataViewerId(item.id); + setOpenMetadataViewerState({ + itemId: item.id, + }); }, }); + // if (item.isFolder) { contextMenuOptions.push({ id: "edit-metadata", label: t("Edit metadata"), @@ -182,6 +233,7 @@ export default function TableOfContentsEditor() { setOpenMetadataItemId(item.id); }, }); + // } } contextMenuOptions.push({ id: "delete", @@ -211,7 +263,16 @@ export default function TableOfContentsEditor() { return []; } }, - [confirmDelete, deleteItem, manager, mapContext.manager?.map, t, tocQuery] + [ + confirmDelete, + deleteItem, + manager, + mapContext.manager, + t, + tocQuery, + layersAndSources.data, + checkedItems, + ] ); const onSortEnd: ( @@ -381,6 +442,9 @@ export default function TableOfContentsEditor() { /> )}
{ + setArcgisCartOpen(true); + }} onRequestOpenFolder={() => { setCreateNewFolderModalOpen(true); }} @@ -449,10 +513,21 @@ export default function TableOfContentsEditor() { onRequestClose={() => setOpenMetadataItemId(undefined)} /> )} - {openMetadataViewerId && ( + {openMetadataViewerState && ( setOpenMetadataViewerId(undefined)} + id={openMetadataViewerState.itemId} + onRequestClose={() => setOpenMetadataViewerState(undefined)} + /> + )} + {arcgisCartOpen && ( + setArcgisCartOpen(false)} + importedArcGISServices={ + (tocQuery.data?.projectBySlug?.importedArcgisServices || + []) as string[] + } /> )} @@ -468,6 +543,7 @@ function Header({ onRequestPublish, publishDisabled, lastPublished, + openArcGISCart, }: { selectedView: string; setSelectedView: (view: string) => void; @@ -477,6 +553,7 @@ function Header({ onRequestPublish: () => void; publishDisabled?: boolean; lastPublished?: Date; + openArcGISCart: () => void; }) { const uploadContext = useContext(DataUploadDropzoneContext); const { t } = useTranslation("admin:data"); @@ -531,6 +608,7 @@ function Header({ Add Folder + {t("Host data on SeaSketch")} { const fileInput = document.createElement("input"); @@ -549,6 +627,15 @@ function Header({ > {t("Upload spatial data files")} + + {t("Connect to data services")} + { + openArcGISCart(); + }} + > + {t("Esri ArcGIS Service...")} + diff --git a/packages/client/src/admin/data/TableOfContentsMetadataEditor.tsx b/packages/client/src/admin/data/TableOfContentsMetadataEditor.tsx index 34e6447ee..8c4902164 100644 --- a/packages/client/src/admin/data/TableOfContentsMetadataEditor.tsx +++ b/packages/client/src/admin/data/TableOfContentsMetadataEditor.tsx @@ -23,6 +23,12 @@ export default function TableOfContentsMetadataEditor({ return ( mutation({ variables: { @@ -34,7 +40,7 @@ export default function TableOfContentsMetadataEditor({ mutationState={mutationState} loading={loading} error={error} - startingDocument={data?.tableOfContentsItem?.metadata} + startingDocument={data?.tableOfContentsItem?.computedMetadata} /> ); } diff --git a/packages/client/src/admin/data/arcgis/Accordion.css b/packages/client/src/admin/data/arcgis/Accordion.css new file mode 100644 index 000000000..c7644ec31 --- /dev/null +++ b/packages/client/src/admin/data/arcgis/Accordion.css @@ -0,0 +1,3 @@ +.AccordionTrigger[data-state="open"] > .AccordionChevron { + transform: rotate(180deg); +} diff --git a/packages/client/src/admin/data/arcgis/ArcGISBrowserColumn.tsx b/packages/client/src/admin/data/arcgis/ArcGISBrowserColumn.tsx new file mode 100644 index 000000000..3e2e14939 --- /dev/null +++ b/packages/client/src/admin/data/arcgis/ArcGISBrowserColumn.tsx @@ -0,0 +1,177 @@ +import React, { useState } from "react"; +import Spinner from "../../../components/Spinner"; +import { useCatalogItems, CatalogItem } from "./arcgis"; +import { ArcGISRESTServiceRequestManager } from "@seasketch/mapbox-gl-esri-sources"; + +export interface ArcGISBrowserColumnProps { + url: string; + name: string; + type: + | "Root" + | "Folder" + | "GPServer" + | "MapServer" + | "FeatureServer" + | "GeometryServer" + | "GeocodeServer"; + onSelection?: (item: CatalogItem) => void; + leading?: boolean; + requestManager: ArcGISRESTServiceRequestManager; +} + +export function ArcGISBrowserColumn(props: ArcGISBrowserColumnProps) { + const { catalogInfo, error, loading } = useCatalogItems( + props.url, + props.requestManager + ); + const [selectedItem, setSelectedItem] = useState(); + const updateSelection = (item: CatalogItem) => { + setSelectedItem(item); + if (props.onSelection) { + props.onSelection(item); + } + }; + if (loading) { + return ( +
+ +
+ ); + } + return ( +
+
    + {catalogInfo?.map((item) => ( + updateSelection(item)} + url={item.url} + selected={item.url === selectedItem?.url} + type={item.type} + leading={props.leading} + /> + ))} +
+
+ ); +} + +function ServiceItem(props: { + type: + | "Folder" + | "MapServer" + | "FeatureServer" + | "GPServer" + | "GeometryServer" + | "GeocodeServer"; + url: string; + title: string; + key: string; + selected: boolean; + leading?: boolean; + onClick?: (e: { id: string; url: string }) => void; +}) { + const disabled = + props.type !== "MapServer" && + props.type !== "Folder" && + props.type !== "FeatureServer"; + /* eslint-disable */ + const svgClassName = `mr-1.5 w-4 -mt-0.5 h-4 inline ${ + props.selected + ? !!props.leading + ? "text-white" + : "text-black" + : disabled + ? "text-gray-500" + : "text-gray-800" + }`; + /* eslint-enable */ + return ( +
  • + props.onClick && props.onClick({ id: props.url, url: props.url }) + } + > + {props.type === "MapServer" && ( + + + + )} + {props.type === "FeatureServer" && ( + + + + )} + {props.type === "GPServer" && ( + + + + )} + {props.type === "GeocodeServer" && ( + + + + )} + {/* {props.type === "Folder" && } */} + {props.title} +
  • + ); +} diff --git a/packages/client/src/admin/data/arcgis/ArcGISCartLegend.tsx b/packages/client/src/admin/data/arcgis/ArcGISCartLegend.tsx new file mode 100644 index 000000000..85e25d36d --- /dev/null +++ b/packages/client/src/admin/data/arcgis/ArcGISCartLegend.tsx @@ -0,0 +1,322 @@ +/* eslint-disable i18next/no-literal-string */ +import { + CaretDownIcon, + EyeClosedIcon, + EyeOpenIcon, +} from "@radix-ui/react-icons"; +import { + DataTableOfContentsItem, + FolderTableOfContentsItem, + ImageList, + LegendItem, +} from "@seasketch/mapbox-gl-esri-sources"; +import * as Accordion from "@radix-ui/react-accordion"; +import Spinner from "../../../components/Spinner"; +import { Layer, Map } from "mapbox-gl"; +import { + SeaSketchGlLayer, + compileLegendFromGLStyleLayers, +} from "../../../dataLayers/legends/compileLegend"; +import { memo } from "react"; +import SimpleSymbol from "../../../dataLayers/legends/SimpleSymbol"; +import { LegendForGLLayers } from "../../../dataLayers/legends/LegendDataModel"; +import { + hasGetExpression, + isExpression, +} from "../../../dataLayers/legends/utils"; +import Legend from "../../../dataLayers/Legend"; +require("./Accordion.css"); + +export default function ArcGISCartLegend({ + items, + className, + loading, + visibleLayerIds, + toggleLayer, + map, +}: { + className?: string; + items: (FolderTableOfContentsItem | DataTableOfContentsItem)[]; + loading?: boolean; + visibleLayerIds?: string[]; + toggleLayer?: (id: string) => void; + map: Map; +}) { + function onChangeVisibility(id: string) { + if (toggleLayer) { + return () => { + toggleLayer(id); + }; + } else { + return undefined; + } + } + if (items.length === 0) { + return null; + } else { + return ( + <> +
    + + + + +

    + Legend + {loading && } +

    + + +
    +
    + + {/*
    */} +
      + {items.map((item) => { + const visible = visibleLayerIds + ? visibleLayerIds.includes(item.id) + : item.defaultVisibility; + if (item.type === "folder") { + return ( +
    • + {item.label} +
    • + ); + } else { + if (!item.legend) { + if (item.glStyle) { + if (styleHasDataExpression(item.glStyle.layers)) { + return ( +
    • +
      + + {item.label} + + +
      +
        +
      • +
      +
    • + ); + } else { + return ( +
    • + + + {item.label} + + +
    • + ); + } + } else { + return ( +
    • + + {item.label} + + +
    • + ); + } + } else if (item.legend && item.legend.length === 1) { + const legendItem = item.legend[0]; + return ( +
    • + + + {item.label} + + +
    • + ); + } else if (item.legend && item.legend.length > 1) { + return ( +
    • +
      + + {item.label} + + +
      +
        + {item.legend.map((legendItem) => { + return ( +
      • + + + {legendItem.label} + +
      • + ); + })} +
      +
    • + ); + } else { + return null; + } + } + })} +
    + + + +
    + + ); + } +} + +function LegendImage({ + item, + className, +}: { + item: LegendItem; + className?: string; +}) { + return ( + {item.label} 1 ? window.devicePixelRatio / 1.5 : 1) + } + height={ + (item.imageHeight || 20) / + (window.devicePixelRatio > 1 ? window.devicePixelRatio / 1.5 : 1) + } + /> + ); +} + +function Toggle({ + visible, + onChange, + className, +}: { + visible: boolean; + onChange?: () => void; + className?: string; +}) { + return ( + + ); +} + +export function styleHasDataExpression(style: SeaSketchGlLayer[]) { + for (const layer of style) { + if ( + style.length > 1 && + layer.filter && + isExpression(layer.filter) && + hasGetExpression(layer.filter, true) + ) { + return true; + } else if (layer.paint) { + for (const key in layer.paint) { + if (isExpression((layer.paint as any)[key])) { + return hasGetExpression((layer.paint as any)[key], key === "filter"); + } + } + } else if (layer.layout) { + for (const key in layer.layout) { + if (isExpression((layer.layout as any)[key])) { + return hasGetExpression((layer.layout as any)[key], key === "filter"); + } + } + } + } + return false; +} + +type Expression = [string, Expression | string | number | boolean | null]; + +const SimpleLegendIconFromStyle = memo( + function _SimpleLegendIconFromStyle(props: { + layers: SeaSketchGlLayer[]; + map: Map; + }) { + let data: LegendForGLLayers | undefined; + try { + data = compileLegendFromGLStyleLayers(props.layers, "vector"); + } catch (e) { + // Do nothing + } + const simpleSymbol = data?.type === "SimpleGLLegend" ? data.symbol : null; + return ( +
    + {simpleSymbol ? ( + + ) : null} +
    + ); + } +); diff --git a/packages/client/src/admin/data/arcgis/ArcGISCartModal.tsx b/packages/client/src/admin/data/arcgis/ArcGISCartModal.tsx new file mode 100644 index 000000000..cfc53fc61 --- /dev/null +++ b/packages/client/src/admin/data/arcgis/ArcGISCartModal.tsx @@ -0,0 +1,979 @@ +import { useCallback, useEffect, useMemo, useState } from "react"; +import { createPortal } from "react-dom"; +import ArcGISSearchPage from "./ArcGISSearchPage"; +import { + CatalogItem, + NormalizedArcGISServerLocation, + generateStableId, + useCatalogItemDetails, + useCatalogItems, +} from "./arcgis"; +import { AnyLayer, LngLatBounds, LngLatBoundsLike, Map } from "mapbox-gl"; +import { SearchIcon } from "@heroicons/react/outline"; +import Skeleton from "../../../components/Skeleton"; +import { + ActivityLogIcon, + ArrowLeftIcon, + CheckCircledIcon, + ExternalLinkIcon, +} from "@radix-ui/react-icons"; +import { FolderIcon } from "@heroicons/react/solid"; +import Spinner from "../../../components/Spinner"; +import Button from "../../../components/Button"; +import { + ArcGISDynamicMapService, + ArcGISRESTServiceRequestManager, + CustomGLSource, + ArcGISTiledMapService, + DataTableOfContentsItem, + FolderTableOfContentsItem, + ArcGISFeatureLayerSource, +} from "@seasketch/mapbox-gl-esri-sources"; +import { Feature } from "geojson"; +import bbox from "@turf/bbox"; +import Legend, { LegendItem } from "../../../dataLayers/Legend"; +import { compileLegendFromGLStyleLayers } from "../../../dataLayers/legends/compileLegend"; +import Switch from "../../../components/Switch"; +import { Trans, useTranslation } from "react-i18next"; +import useDialog from "../../../components/useDialog"; +import { + ArcgisFeatureLayerFetchStrategy, + ArcgisImportItemInput, + ArcgisImportSourceInput, + ArcgisSourceType, + BasemapType, + DraftTableOfContentsDocument, + GetBasemapsDocument, + useCreateBasemapMutation, + useImportArcGisServiceMutation, +} from "../../../generated/graphql"; + +const requestManager = new ArcGISRESTServiceRequestManager(); + +const FEATURE_LAYERS_DEFAULT_VISIBLE_LAYERS_LIMIT = 3; + +export default function ArcGISCartModal({ + onRequestClose, + region, + importedArcGISServices, + projectId, +}: { + onRequestClose: () => void; + region?: Feature; + importedArcGISServices: string[]; + projectId: number; +}) { + const { t } = useTranslation("admin:data"); + let mapBounds: LngLatBoundsLike | undefined = undefined; + if (region) { + const box = region + ? (bbox(region) as [number, number, number, number]) + : undefined; + if (box) { + mapBounds = new LngLatBounds([box[0], box[1]], [box[2], box[3]]); + } + } + const [location, setLocation] = + useState(null); + const [mapDiv, setMapDiv] = useState(null); + const [selection, setSelection] = useState(null); + const [selectedFolder, setSelectedFolder] = useState( + null + ); + const [map, setMap] = useState(null); + const { catalogInfo, error, loading } = useCatalogItems( + location?.servicesRoot + ? selectedFolder + ? location.servicesRoot + "/" + selectedFolder.name + : location.servicesRoot + : "", + requestManager + ); + + const [importMutation, importState] = useImportArcGisServiceMutation(); + + const [sourceLoading, setSourceLoading] = useState(false); + + const [tableOfContentsItems, setTableOfContentsItems] = useState< + (FolderTableOfContentsItem | DataTableOfContentsItem)[] + >([]); + + const legendItems = useMemo(() => { + const items: LegendItem[] = []; + for (const item of tableOfContentsItems) { + if (item.type === "folder") { + continue; + } + if (item.type === "data" && item.glStyle) { + items.push({ + type: "GLStyleLegendItem", + id: item.id.toString(), + label: item.label, + legend: compileLegendFromGLStyleLayers(item.glStyle.layers, "vector"), + }); + } else if (item.type === "data" && item.legend) { + items.push({ + type: "CustomGLSourceSymbolLegend", + label: item.label, + supportsDynamicRendering: { + layerOpacity: true, + layerOrder: true, + layerVisibility: true, + }, + symbols: item.legend, + id: item.id.toString(), + }); + } + } + return items; + }, [tableOfContentsItems]); + + const [hiddenLayers, setHiddenLayers] = useState([]); + + useEffect(() => { + if (mapDiv) { + // initialize mapbox map + const m = new Map({ + container: mapDiv, + style: "mapbox://styles/mapbox/streets-v11", + // bounds: mapBounds, + center: [-74.5, 40], + zoom: 9, + }); + // const legend = new LegendControl(); + // m.addControl(legend, "bottom-left"); + m.fitBounds(mapBounds as LngLatBounds, { padding: 10, animate: false }); + + m.on("load", () => { + // m.fitBounds(mapBounds as LngLatBounds, { padding: 10, animate: false }); + m.resize(); + setMap(m); + }); + } + }, [mapDiv]); + + const [customSources, setCustomSources] = useState[]>([]); + + useEffect(() => { + if (map && customSources.length) { + let timeout: any = null; + const updateIsLoading = () => { + let isLoading = false; + for (const source of customSources) { + isLoading = source.loading || isLoading; + } + setSourceLoading(isLoading); + if (isLoading) { + if (timeout) { + clearTimeout(timeout); + } + timeout = setTimeout(updateIsLoading, 2000); + } + }; + map.on("data", updateIsLoading); + map.on("dataloading", updateIsLoading); + + return () => { + map.off("data", updateIsLoading); + map.off("dataloading", updateIsLoading); + if (timeout) { + clearTimeout(timeout); + } + }; + } + }, [customSources, map, setSourceLoading, hiddenLayers]); + + useEffect(() => { + if (map) { + const dataLoadingHandler = () => { + let anyLoading = false; + if (customSources.length > 0) { + for (const source of customSources) { + if (source.loading) { + anyLoading = true; + break; + } + } + } + if (anyLoading) { + setSourceLoading(true); + setTimeout(() => { + dataLoadingHandler(); + }, 200); + } else { + setSourceLoading(false); + } + }; + map.on("dataloading", dataLoadingHandler); + map.on("data", dataLoadingHandler); + map.on("moveend", dataLoadingHandler); + + return () => { + map.off("dataloading", dataLoadingHandler); + map.off("data", dataLoadingHandler); + map.off("moveend", dataLoadingHandler); + }; + } + }, [map, setSourceLoading, customSources]); + + const catalogItemDetailsQuery = useCatalogItemDetails( + requestManager, + selection?.type === "FeatureServer" || selection?.type === "MapServer" + ? selection.url + : undefined + ); + + const [search, setSearch] = useState(""); + + const [useFeatureLayers, setUseFeatureLayers] = useState(false); + + const items = (catalogInfo || []).filter( + (i) => + (i.type === "MapServer" || + i.type === "FeatureServer" || + i.type === "Folder") && + (search.length === 0 || + i.name.toLowerCase().includes(search.toLowerCase())) + ); + + useEffect(() => { + setSourceLoading(false); + setTableOfContentsItems([]); + setHiddenLayers([]); + if (catalogItemDetailsQuery.data && map && selection) { + const { type } = catalogItemDetailsQuery.data; + setSourceLoading(true); + if (type === "MapServer" && catalogItemDetailsQuery.data.tiled) { + const tileSource = new ArcGISTiledMapService(requestManager, { + url: selection.url, + supportHighDpiDisplays: true, + }); + tileSource + .getComputedMetadata() + .then(({ bounds, tableOfContentsItems }) => { + setTableOfContentsItems(tableOfContentsItems); + setHiddenLayers( + tableOfContentsItems + .filter( + (item) => + item.type === "data" && item.defaultVisibility === false + ) + .map((item) => item.id) + ); + if (bounds && bounds[0]) { + map.fitBounds(bounds, { padding: 10, animate: false }); + } + }); + setCustomSources([tileSource]); + tileSource.addToMap(map).then(() => { + tileSource.getGLStyleLayers().then(({ layers }) => { + for (const layer of layers) { + map.addLayer(layer); + } + }); + }); + return () => { + setCustomSources([]); + tileSource.destroy(); + }; + } else if (type === "MapServer" || type === "FeatureServer") { + const featureLayers = + type === "FeatureServer" + ? catalogItemDetailsQuery.data.metadata?.layers + : catalogItemDetailsQuery.data.layers.layers.filter((lyr: any) => { + return ( + lyr.type === "Feature Layer" && + /geojson/i.test(lyr.supportedQueryFormats) + ); + }); + if (type === "FeatureServer" || useFeatureLayers) { + const sources: ArcGISFeatureLayerSource[] = []; + const layerStaticIdsToHide: string[] = []; + let defaultVisibleLayers = featureLayers + .filter((item) => item.defaultVisibility === true) + .map((item) => item.id); + if ( + defaultVisibleLayers.length > + FEATURE_LAYERS_DEFAULT_VISIBLE_LAYERS_LIMIT + ) { + defaultVisibleLayers = defaultVisibleLayers + .reverse() + .slice(0, FEATURE_LAYERS_DEFAULT_VISIBLE_LAYERS_LIMIT); + } + for (const layer of featureLayers.reverse()) { + const featureLayerSource = new ArcGISFeatureLayerSource( + requestManager, + { + url: selection.url + "/" + layer.id, + fetchStrategy: "auto", + } + ); + sources.push(featureLayerSource); + featureLayerSource.getComputedMetadata().then((data) => { + const { bounds, tableOfContentsItems } = data; + if (!defaultVisibleLayers.includes(layer.id)) { + for (const item of tableOfContentsItems) { + layerStaticIdsToHide.push(item.id); + } + featureLayerSource.updateLayers([]); + } else { + featureLayerSource.updateLayers([ + { + id: layer.id.toString(), + opacity: 1, + }, + ]); + } + setTableOfContentsItems((prev) => [ + ...prev, + ...tableOfContentsItems, + ]); + if (bounds && bounds[0]) { + map.fitBounds(bounds, { padding: 10, animate: false }); + } + }); + featureLayerSource.addToMap(map).then(() => { + featureLayerSource + .getGLStyleLayers() + .then(({ layers, imageList }) => { + if (imageList) { + imageList.addToMap(map); + } + for (const layer of layers) { + map.addLayer(layer as AnyLayer); + } + }); + }); + } + setHiddenLayers(layerStaticIdsToHide); + + setCustomSources(sources); + return () => { + setCustomSources([]); + for (const source of sources) { + source.destroy(); + } + }; + } else { + const dynamicSource = new ArcGISDynamicMapService(requestManager, { + url: selection.url, + supportHighDpiDisplays: true, + }); + dynamicSource.getComputedMetadata().then((data) => { + const { bounds, tableOfContentsItems } = data; + setTableOfContentsItems(tableOfContentsItems); + setHiddenLayers( + tableOfContentsItems + .filter( + (item) => + item.type === "data" && item.defaultVisibility === false + ) + .map((item) => item.id) + ); + if (bounds && bounds[0]) { + map.fitBounds(bounds, { + padding: 10, + animate: true, + duration: 100, + }); + } + }); + setCustomSources([dynamicSource]); + dynamicSource.addToMap(map).then(() => { + dynamicSource.getGLStyleLayers().then(({ layers }) => { + for (const layer of layers) { + map.addLayer(layer); + } + }); + }); + return () => { + setCustomSources([]); + dynamicSource.destroy(); + }; + } + } + return; + } + }, [catalogItemDetailsQuery.data, map, useFeatureLayers]); + + useEffect(() => { + for (const source of customSources) { + const visibleLayers: string[] = []; + for (const item of tableOfContentsItems) { + if (item.type === "data" && !hiddenLayers.includes(item.id)) { + visibleLayers.push(item.id); + } + } + source.updateLayers( + visibleLayers.map((id) => ({ + id: id.toString(), + opacity: 1, + })) + ); + } + }, [hiddenLayers, customSources, tableOfContentsItems]); + + const { loadingMessage, confirm, alert, makeChoice } = useDialog(); + + const [createBasemap, createBaseMapState] = useCreateBasemapMutation(); + + const selectionAlreadyAdded = + selection && importedArcGISServices.includes(selection.url); + + const onAddService = useCallback(async () => { + if (importedArcGISServices.includes(selection?.url || "")) { + const answer = await confirm( + t( + "This service has already been added to your project's table of contents. Are you sure you want to import it again?" + ) + ); + if (!answer) { + return; + } + } + const { hideLoadingMessage, updateLoadingMessage } = loadingMessage( + t(`Evaluating services (1/${customSources.length})...`) + ); + const layers = catalogItemDetailsQuery.data?.metadata.layers || []; + if (layers.length && customSources.length) { + const items: ArcgisImportItemInput[] = []; + const sources: ArcgisImportSourceInput[] = []; + if (customSources[0].type === "ArcGISTiledMapService") { + let thumbnail: string = ""; + try { + thumbnail = await ( + customSources[0] as ArcGISTiledMapService + ).getThumbnailForCurrentExtent(); + } catch (e) { + alert( + t("Error importing service. Could not generate thumbnail image"), + { + description: e.message, + } + ); + return; + } + const choice = await makeChoice({ + title: t("Import this tiled service as a..."), + choices: [ +
    + Basemap thumbnail +
    +

    {t("Basemap")}

    +

    + {t( + "Map tiles will be displayed below all overlay layers, and the basemap is accessible from the Maps tab." + )} +

    +
    +
    , +
    +
    + +
    +
    +

    {t("Overlay Layer")}

    +

    + {t( + "Use this option if you would like to display these map tiles over an existing basemap. Best for tiled layers with alpha transparency." + )} +

    +
    +
    , + ], + }); + if (choice === false) { + return; + } else if (choice === 0) { + try { + updateLoadingMessage(t("Creating thumbnail...")); + const response = await fetch(thumbnail); + const blob = await response.blob(); + updateLoadingMessage(t("Creating basemap...")); + const basemapResponse = await createBasemap({ + variables: { + projectId, + name: (selection?.name || "Imported ArcGIS Service").replace( + /_/g, + " " + ), + // eslint-disable-next-line i18next/no-literal-string + url: customSources[0].url + `/tile/{z}/{y}/{x}`, + type: BasemapType.RasterUrlTemplate, + isArcgisTiledMapservice: true, + tileSize: 256, + surveysOnly: false, + thumbnail: blob, + }, + refetchQueries: [ + DraftTableOfContentsDocument, + GetBasemapsDocument, + ], + }); + // load thumbnail as soon as saved to prevent flicker + const url = basemapResponse.data?.createBasemap?.basemap?.thumbnail; + if (url) { + fetch(url); + } + hideLoadingMessage(); + return; + } catch (e) { + hideLoadingMessage(); + alert(t("Error importing service"), { + description: e.message, + }); + } + } else { + items.push({ + title: selection?.name, + isFolder: false, + id: 1, + sourceId: 1, + stableId: generateStableId(), + }); + sources.push({ + id: 1, + type: ArcgisSourceType.ArcgisRasterTiles, + url: customSources[0].url, + }); + } + } else { + let importedCount = 0; + for (const layer of layers) { + if ( + Array.isArray(layer.subLayerIds) && + layer.subLayerIds.length > 0 + ) { + // FOLDER + items.push({ + id: layer.id, + isFolder: true, + title: layer.name, + ...("parentLayerId" in layer && layer.parentLayerId > -1 + ? { parentId: layer.parentLayerId.toString() } + : {}), + stableId: generateStableId(), + }); + } else if ( + catalogItemDetailsQuery.data?.type === "FeatureServer" || + useFeatureLayers + ) { + // add layers as individual feature layers + const source = customSources.find( + (s) => s.url === selection?.url + "/" + layer.id + ); + if (!source) { + console.warn("cant find source", selection?.url + "/" + layer.id); + continue; + // throw new Error("Source not found"); + } + updateLoadingMessage( + t( + `Evaluating services (${importedCount++}/${ + customSources.length + })...` + ) + ); + + items.push({ + id: layer.id, + isFolder: false, + title: + catalogItemDetailsQuery.data?.type === "FeatureServer" && + layers.length === 1 + ? selection?.name || layer.name + : layer.name, + sourceId: layer.id, + ...("parentLayerId" in layer && layer.parentLayerId > -1 + ? { parentId: layer.parentLayerId.toString() } + : {}), + stableId: generateStableId(), + }); + + const fetchStrategy = await ( + source as ArcGISFeatureLayerSource + ).getFetchStrategy(); + + sources.push({ + id: layer.id, + type: ArcgisSourceType.ArcgisVector, + url: selection?.url + "/" + layer.id, + fetchStrategy: + fetchStrategy === "auto" + ? ArcgisFeatureLayerFetchStrategy.Auto + : fetchStrategy === "tiled" + ? ArcgisFeatureLayerFetchStrategy.Tiled + : ArcgisFeatureLayerFetchStrategy.Raw, + }); + } else { + // Dynamic Map Service + items.push({ + id: layer.id, + isFolder: false, + title: layer.name, + sourceId: 1, + ...("parentLayerId" in layer && layer.parentLayerId > -1 + ? { parentId: layer.parentLayerId.toString() } + : {}), + sublayerId: layer.id, + stableId: generateStableId(), + }); + } + } + } + if ( + catalogItemDetailsQuery.data?.type === "MapServer" && + !useFeatureLayers && + !catalogItemDetailsQuery.data?.tiled + ) { + sources.push({ + id: 1, + type: ArcgisSourceType.ArcgisDynamicMapserver, + url: selection?.url, + }); + } + + if (items.length > 1) { + // Stick everything in an enclosing folder + const enclosingFolder: ArcgisImportItemInput = { + id: 9999999, + isFolder: true, + stableId: generateStableId(), + title: selection?.name || "Imported ArcGIS Service", + }; + for (const item of items) { + if (item.parentId === undefined) { + // @ts-ignore + item.parentId = enclosingFolder.id; + } + } + items.push(enclosingFolder); + } + + // replace parentIds with stableIds + for (const item of items) { + if (item.parentId) { + const parentId = items.find( + (i) => i.id?.toString() === item.parentId?.toString() + )?.stableId; + item.parentId = parentId; + } + } + + // eslint-disable-next-line i18next/no-literal-string + updateLoadingMessage(`Adding ${items.length} items to project...`); + try { + await importMutation({ + variables: { + projectId, + items, + sources, + }, + refetchQueries: [DraftTableOfContentsDocument], + }); + hideLoadingMessage(); + } catch (e) { + hideLoadingMessage(); + alert(t("Error importing service"), { + description: e.message, + }); + } + } + }, [ + customSources, + useFeatureLayers, + selection, + catalogItemDetailsQuery.data, + t, + loadingMessage, + projectId, + importMutation, + importedArcGISServices, + alert, + confirm, + createBasemap, + makeChoice, + ]); + + return createPortal( + <> +
    +
    + {!location && ( +
    + { + setLocation(result.location); + if (result.selectedFolder) { + setSelectedFolder(result.selectedFolder); + } + if (result.catalogItem) { + setSelection(result.catalogItem); + } + }} + /> +
    + )} + {location && ( +
    +
    + {location.servicesRoot} +
    +
    +
    +
    +
    + +
    + setSearch(e.target.value)} + type="text" + className="rounded-md border-gray-200 input w-full focus:ring-gray-600 pl-10" + /> +
    + {selectedFolder && ( +
    + +
    + )} +
    + {items.length === 0 && loading && ( + <> + + + + + + + + + + + + + + + + + + )} + {items.length === 0 && !loading && selectedFolder && ( +
    + {t("No map services in this folder")} +
    + )} + {items.length === 0 && !loading && !selectedFolder && ( +
    + {t("No map services found")} +
    + )} + {items.map((item) => ( + + ))} +
    +
    + {map && legendItems.length > 0 && ( + { + setHiddenLayers((prev) => { + if (visible) { + return [...prev, id]; + } else { + return prev.filter((i) => i !== id); + } + }); + }} + opacity={{}} + zOrder={{}} + map={map} + maxHeight={600} + /> + )} + {useFeatureLayers && + legendItems.length === 0 && + sourceLoading === false && ( +
    +

    + {t("Legend")} +

    +

    + {t( + "No vector layers found. Disable vector request preference to see raster layers." + )} +

    +
    + )} +
    +
    +
    +
    +
    +
    + + Display vectors instead of requesting images from + MapService + +
    +

    + + Best option for smaller services, can cause problems for + larger ones. + +

    +
    + { + setUseFeatureLayers(val); + }} + /> +
    +
    +
    +
    +
    + )} +
    + , + document.body + ); +} + +function LoadingSkeleton() { + return ( +
    +
    + +
    + +
    + +
    + ); +} diff --git a/packages/client/src/admin/data/arcgis/ArcGISSearchPage.tsx b/packages/client/src/admin/data/arcgis/ArcGISSearchPage.tsx new file mode 100644 index 000000000..89dcb7ce9 --- /dev/null +++ b/packages/client/src/admin/data/arcgis/ArcGISSearchPage.tsx @@ -0,0 +1,184 @@ +import React, { useRef, useState } from "react"; +import { + CatalogItem, + normalizeArcGISServerUrl, + NormalizedArcGISServerLocation, +} from "./arcgis"; +import useRecentDataServers from "./useRecentServers"; +import { Trans } from "react-i18next"; +import Spinner from "../../../components/Spinner"; +import { ArcGISRESTServiceRequestManager } from "@seasketch/mapbox-gl-esri-sources"; + +export default function ArcGISSearchPage({ + onResult, + requestManager, +}: { + onResult?: (e: { + location: NormalizedArcGISServerLocation; + version: string; + catalogItem?: CatalogItem; + selectedFolder?: CatalogItem; + }) => void; + requestManager: ArcGISRESTServiceRequestManager; +}) { + const [inputUrl, setInputUrl] = useState(""); + const [inputError, setInputError] = useState(null); + const [recentServers, addServer] = useRecentDataServers(); + const [loading, setLoading] = useState(false); + + const inputRef = useRef(null); + const onSubmitServiceUrl = async () => { + const l = normalizeArcGISServerUrl(inputUrl); + loadServerUrl(l); + }; + + const loadServerUrl = async ( + location: NormalizedArcGISServerLocation & { updateInput?: boolean } + ) => { + if (location.updateInput && inputRef.current) { + inputRef.current.value = location.baseUrl; + } + setInputError(null); + if (/http:/.test(location.baseUrl)) { + setInputError("Service protocol https:// is required."); + } else { + try { + setLoading(true); + const serviceResponse = await requestManager.getCatalogItems( + location.servicesRoot + ); + setLoading(false); + if (serviceResponse.currentVersion) { + addServer({ location: location.baseUrl, type: "arcgis" }); + if (onResult) { + let catalogItem: CatalogItem | undefined; + let folder: CatalogItem | undefined; + if (location.location.split("/").length > 2) { + const folderName = location.location.split("/")[1]; + folder = { + name: folderName, + type: "Folder", + url: location.servicesRoot + "/" + folderName, + }; + } + if (/MapServer/.test(location.location)) { + catalogItem = { + name: location.location.split("/")[1], + type: "MapServer", + url: location.servicesRoot + location.location, + }; + } else if (/FeatureServer/.test(location.location)) { + catalogItem = { + name: location.location.split("/")[1], + type: "FeatureServer", + url: location.servicesRoot + location.location, + }; + } + onResult({ + location, + version: serviceResponse.currentVersion.toString(), + catalogItem, + selectedFolder: folder, + }); + } + // setVersion(serviceResponse.currentVersion); + // setLocation(location); + } else { + setInputError("Unrecognized server response"); + } + } catch (e) { + setLoading(false); + console.error(e); + setInputError(e.toString()); + } + } + }; + + return ( +
    + +
    +
    + { + if (e.key === "Enter") { + onSubmitServiceUrl(); + } + }} + id="arcgis" + value={inputUrl} + onChange={(e) => setInputUrl(e.target.value)} + className={`p-2 ${ + loading ? "bg-gray-100" : "bg-white" + } block w-full border-gray-300 rounded-md focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50 sm:text-sm sm:leading-5`} + placeholder="https://example.com/argis/rest/services" + /> +
    + +
    + {inputError &&

    {inputError}

    } + {recentServers && recentServers.length > 0 && ( +
    +

    + Recently Used Servers +

    +
      + {recentServers + .filter((server) => server.type === "arcgis") + .map((server) => ( +
    • + loadServerUrl({ + baseUrl: server.location, + location: "/", + servicesRoot: server.location + "/arcgis/rest/services", + updateInput: true, + }) + } + > + {server.location} +
    • + ))} +
    +
    + )} +
    + ); +} diff --git a/packages/client/src/admin/data/arcgis/ArcGISServiceMetadata.tsx b/packages/client/src/admin/data/arcgis/ArcGISServiceMetadata.tsx new file mode 100644 index 000000000..dd308815c --- /dev/null +++ b/packages/client/src/admin/data/arcgis/ArcGISServiceMetadata.tsx @@ -0,0 +1,69 @@ +import React, { ReactNode } from "react"; +import DefinitionList from "../../../components/DefinitionList"; +import { LayerInfo, MapServerCatalogInfo } from "./arcgis"; + +export interface ArcGISServiceMetadataProps { + serviceInfo: MapServerCatalogInfo; + layer?: LayerInfo; +} + +export default function ArcGISServiceMetadata( + props: ArcGISServiceMetadataProps +) { + const mapServerInfo = props.serviceInfo; + let definitionListItems: [string, string | ReactNode][] = []; + if (props.layer) { + definitionListItems.push( + ["Description", props.layer?.description], + ["Author", mapServerInfo.documentInfo?.Author], + ["Copyright", props.layer?.copyrightText || mapServerInfo.copyrightText], + [ + "Projection", + + + {mapServerInfo.spatialReference.latestWkid || + mapServerInfo.spatialReference.wkid} + , + ] + ); + } else { + definitionListItems.push( + [ + "Description", + mapServerInfo.serviceDescription || mapServerInfo.description, + ], + ["Author", mapServerInfo.documentInfo?.Author], + ["Subject", mapServerInfo.documentInfo?.Subject], + ["Comments", mapServerInfo.documentInfo?.Comments], + ["Copyright", mapServerInfo.copyrightText], + ["Keywords", mapServerInfo.documentInfo?.Keywords], + [ + "Projection", + + + {mapServerInfo.spatialReference.latestWkid || + mapServerInfo.spatialReference.wkid} + , + ] + ); + } + return ( +
    + +
    + ); +} diff --git a/packages/client/src/admin/data/arcgis/DynamicMapServerSettingsForm.tsx b/packages/client/src/admin/data/arcgis/DynamicMapServerSettingsForm.tsx new file mode 100644 index 000000000..591d7657b --- /dev/null +++ b/packages/client/src/admin/data/arcgis/DynamicMapServerSettingsForm.tsx @@ -0,0 +1,172 @@ +import { useHistory, useParams } from "react-router-dom"; +import Button from "../../../components/Button"; +import InputBlock from "../../../components/InputBlock"; +import ProgressBar from "../../../components/ProgressBar"; +import Switch from "../../../components/Switch"; +import { useTranslation, Trans } from "react-i18next"; +import useProjectId from "../../../useProjectId"; +import { + ArcGISServiceSettings, + LayerInfo, + MapServerCatalogInfo, + MapServerImageFormat, + useImportArcGISService, +} from "./arcgis"; +import Modal from "../../../components/Modal"; + +export default function DynamicMapServerSettingsForm(props: { + settings: ArcGISServiceSettings; + serviceRoot: string; + updateSettings: (settings: ArcGISServiceSettings) => void; + layerInfo: LayerInfo[]; + mapServerInfo: MapServerCatalogInfo; +}) { + const { settings, updateSettings } = props; + const [importService, importServiceState] = useImportArcGISService( + props.serviceRoot + ); + const { slug } = useParams<{ slug: string }>(); + const projectId = useProjectId(); + const history = useHistory(); + const { t } = useTranslation("admin"); + const acceptableImageFormats = [ + "PNG", + "PNG8", + "PNG24", + "PNG32", + "GIF", + "JPG", + ]; + return ( +
    + + updateSettings({ + ...settings, + enableHighDpi: !settings.enableHighDpi, + }) + } + /> + } + > + + Request higher resolution images when the user has a "Retina" or 4k + display. Maps will be much more detailed, but it demands more of the + data server. + + + { + updateSettings({ + ...settings, + imageFormat: e.target.value as MapServerImageFormat, + }); + }} + > + {acceptableImageFormats.map((f) => ( + + ))} + + } + > + + Imagery data looks best using jpg, for others{" "} + png is a good choice. + + + {/* { + updateSettings({ + ...settings, + renderUnder: e.target.value as RenderUnderType, + }); + }} + > + + + + } + > + If your basemaps are configured to identify these special layers, you + can render this service underneath labels or land. + */} +
    +

    {t("Import Service")}

    +

    + + Please review the layer list and exclude any that you do not wish to + display, and check that the above settings render well. + +

    +
    + {(!!importServiceState.inProgress || !!importServiceState.error) && ( + {}} + > +
    + {importServiceState.error && ( + <> +
    +

    {importServiceState.error.name}

    + {importServiceState.error.message} +
    +
    +
    + )} +
    + ); +} diff --git a/packages/client/src/admin/data/arcgis/ExcludeLayerToggle.tsx b/packages/client/src/admin/data/arcgis/ExcludeLayerToggle.tsx new file mode 100644 index 000000000..e351c4220 --- /dev/null +++ b/packages/client/src/admin/data/arcgis/ExcludeLayerToggle.tsx @@ -0,0 +1,58 @@ +import React from "react"; + +export default function ExcludeLayerToggle({ + excluded, + onClick, +}: { + excluded: boolean; + onClick: () => void; +}) { + const className = "w-5 h-5 text-primary-600"; + return ( + + ); +} + +export const ExcludeAddIcon = ({ className }: { className?: string }) => ( + + + +); + +export const ExcludeIcon = ({ className }: { className?: string }) => ( + + + +); diff --git a/packages/client/src/admin/data/arcgis/LayerLoadingStateContext.ts b/packages/client/src/admin/data/arcgis/LayerLoadingStateContext.ts new file mode 100644 index 000000000..4a17e98a7 --- /dev/null +++ b/packages/client/src/admin/data/arcgis/LayerLoadingStateContext.ts @@ -0,0 +1,70 @@ +import { MapDataEvent } from "mapbox-gl"; +import React from "react"; + +interface LayerState { + loading: boolean; + error?: Error; +} + +export interface LayerLoadingStates { + [sourceId: string]: LayerState; +} + +const LayerLoadingStateContext = React.createContext({}); + +export function updateLoadingState( + event: (MapDataEvent | (ErrorEvent & MapDataEvent)) & { + sourceId?: string; + error?: Error; + isSourceLoaded?: boolean; + }, + loadingStates: LayerLoadingStates +): LayerLoadingStates { + if (event.sourceId && event.sourceId !== "composite") { + const layerState: LayerState = loadingStates[event.sourceId] || { + loading: true, + }; + // @ts-ignore + const sourceType: string | undefined = event.source?.type; + if (event.type === "error") { + layerState.loading = false; + layerState.error = event.error || new Error("Unknown source error"); + } else if (event.type === "dataloading") { + delete layerState.error; + // @ts-ignore + if (event.source.type === "image") { + // layerState.loading = true; + // setTimeout(() => { + layerState.loading = !event.target.isSourceLoaded(event.sourceId!); + // }, 50); + } else { + layerState.loading = true; + } + } else if (event.type === "data") { + delete layerState.error; + // @ts-ignore + if (event.source.type === "image") { + layerState.loading = event.target.isSourceLoaded(event.sourceId); + } else { + if (event.isSourceLoaded) { + layerState.loading = false; + } else { + layerState.loading = true; + } + } + } else { + layerState.loading = false; + layerState.error = new Error( + "Unknown map event handled by LayerLoadingStateContext" + ); + } + return { + ...loadingStates, + [event.sourceId!]: layerState, + }; + } else { + return loadingStates; + } +} + +export default LayerLoadingStateContext; diff --git a/packages/client/src/admin/data/arcgis/arcgis.ts b/packages/client/src/admin/data/arcgis/arcgis.ts index 300cc2a5e..cc384aee2 100644 --- a/packages/client/src/admin/data/arcgis/arcgis.ts +++ b/packages/client/src/admin/data/arcgis/arcgis.ts @@ -1,56 +1,43 @@ -/* eslint-disable i18next/no-literal-string */ -// /* eslint-disable */ -// import { Layer } from "mapbox-gl"; -// import { -// Dispatch, -// SetStateAction, -// useCallback, -// useContext, -// useEffect, -// useState, -// } from "react"; -// import { Symbol } from "arcgis-rest-api"; -// import { -// ImageList, -// styleForFeatureLayer, -// fetchFeatureLayerData, -// } from "mapbox-gl-esri-feature-layers"; -// import { v4 as uuid } from "uuid"; -// import bboxPolygon from "@turf/bbox-polygon"; -// import area from "@turf/area"; -// import bbox from "@turf/bbox"; -// import geobuf from "geobuf"; -// import Pbf from "pbf"; -// // import { ClientTableOfContentsItem } from "../../../dataLayers/tableOfContents/TableOfContents"; -// import { FeatureCollection } from "geojson"; -// import Worker from "../../../workers/index"; -// import { -// useCreateTableOfContentsItemMutation, -// useCreateArcGisDynamicDataSourceMutation, -// useCreateSeaSketchVectorSourceMutation, -// DataSourceImportTypes, -// useCreateDataLayerMutation, -// DraftTableOfContentsDocument, -// RenderUnderType, -// useCreateArcGisImageSourceMutation, -// useGetOrCreateSpriteMutation, -// useAddImageToSpriteMutation, -// GetOrCreateSpriteMutation, -// Exact, -// AddImageToSpriteMutation, -// DataSourceTypes, -// useUpdateInteractivitySettingsMutation, -// } from "../../../generated/graphql"; -// // import nanoid from "nanoid"; +/* eslint-disable */ +import { Layer } from "mapbox-gl"; +import { useContext, useEffect, useState } from "react"; +import { Symbol } from "arcgis-rest-api"; +import { + ArcGISRESTServiceRequestManager, + ImageList, + styleForFeatureLayer, + MapServiceMetadata, + FeatureServerMetadata, + LayersMetadata, +} from "@seasketch/mapbox-gl-esri-sources"; +import { v4 as uuid } from "uuid"; +import bboxPolygon from "@turf/bbox-polygon"; +import area from "@turf/area"; +import bbox from "@turf/bbox"; +import { FeatureCollection } from "geojson"; +import { + useCreateTableOfContentsItemMutation, + useCreateArcGisDynamicDataSourceMutation, + useCreateSeaSketchVectorSourceMutation, + DataSourceImportTypes, + useCreateDataLayerMutation, + DraftTableOfContentsDocument, + RenderUnderType, + useCreateArcGisImageSourceMutation, + useGetOrCreateSpriteMutation, + useAddImageToSpriteMutation, + GetOrCreateSpriteMutation, + Exact, + AddImageToSpriteMutation, + DataSourceTypes, + useUpdateInteractivitySettingsMutation, + DataSourceDetailsFragment, +} from "../../../generated/graphql"; +// import nanoid from "nanoid"; import { customAlphabet } from "nanoid"; -// import { default as axios } from "axios"; -// import { -// ClientDataLayer, -// ClientDataSource, -// ClientSprite, -// MapContext, -// } from "../../../dataLayers/MapContextManager"; -// import { MutationFunctionOptions } from "@apollo/client"; +import { default as axios } from "axios"; +import { MapContext } from "../../../dataLayers/MapContextManager"; +import { MutationFunctionOptions } from "@apollo/client"; // import { ArcGISVectorSourceCacheEvent } from "../../../dataLayers/ArcGISVectorSourceCache"; const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"; @@ -61,914 +48,712 @@ export function generateStableId() { return nanoId(); } -// let worker: any; -// if (process.env.NODE_ENV === "test") { -// worker = { gzippedSize: () => 0 }; -// } else { -// import("../../../workers/index").then((mod) => { -// worker = new mod.default(); -// }); -// } - -// export interface NormalizedArcGISServerLocation { -// baseUrl: string; -// servicesRoot: string; -// location: string; -// } - -// export function normalizeArcGISServerUrl( -// url: string -// ): NormalizedArcGISServerLocation { -// if (!/http[s]?:\/\//.test(url)) { -// url = "https://" + url; -// } -// if (/arcgis\/rest\/services/.test(url)) { -// const parts = url.split("/arcgis/rest/services"); -// const baseUrl = parts[0]; -// const servicesRoot = baseUrl + "/arcgis/rest/services"; -// const location = parts[1].replace(/\/$/, "") || "/"; -// return { baseUrl, servicesRoot, location }; -// } else if (/arcgis\/rest/.test(url)) { -// const parts = url.split("/arcgis/rest"); -// const baseUrl = parts[0]; -// const servicesRoot = baseUrl + "/arcgis/rest/services"; -// const location = "/"; -// return { baseUrl, servicesRoot, location }; -// } else if (/arcgis/.test(url)) { -// const parts = url.split("/arcgis"); -// const baseUrl = parts[0]; -// const servicesRoot = baseUrl + "/arcgis/rest/services"; -// const location = "/"; -// return { baseUrl, servicesRoot, location }; -// } else if (/arcgis/.test(url)) { -// return { -// baseUrl: "", -// servicesRoot: "", -// location: "", -// }; -// } else { -// const baseUrl = url.replace(/\/$/, ""); -// const servicesRoot = baseUrl + "/arcgis/rest/services"; -// return { -// baseUrl, -// location: "/", -// servicesRoot, -// }; -// } -// } - -// export interface SpatialReference { -// /** Code for projection at the time of publishing. */ -// wkid: number | string; -// /** References the most up-to-date code for this projection in the current version of arcgis. For example, wkid might be 102100, but wkid would be 3857. https://support.esri.com/en/technical-article/000013950#:~:text=At%20ArcGIS%20version%2010%2C%20the,changed%20from%20102100%20to%203857.&text=For%20ArcGIS%20version%2010.1%20and,with%20the%20same%20spatial%20reference. */ -// latestWkid?: number | string; -// } +let worker: any; +if (process.env.NODE_ENV === "test") { + worker = { gzippedSize: () => 0 }; +} else { + import("../../../workers/index").then((mod) => { + worker = new mod.default(); + }); +} -// export function normalizeSpatialReference(sr: SpatialReference) { -// const wkid = sr.latestWkid || sr.wkid; -// if (typeof wkid === "string") { -// if (/WGS\s*84/.test(wkid)) { -// return 4326; -// } else { -// return -1; -// } -// } else { -// return wkid || -1; -// } -// } +export interface NormalizedArcGISServerLocation { + baseUrl: string; + servicesRoot: string; + location: string; +} -// export interface MapServerCatalogInfo { -// url: string; -// currentVersion: number; -// serviceDescription: string; -// description: string; -// mapName: string; -// copyrightText: string; -// supportsDynamicLayers: boolean; -// layers: SummaryLayerInfo[]; -// spatialReference: SpatialReference; -// singleFusedMapCache: false; -// initialExtent: Extent; -// fullExtent: Extent; -// minScale: number; -// maxScale: number; -// /** comma-delimited */ -// supportedImageFormatTypes: string; -// documentInfo?: { -// Title: string; -// Author: string; -// Comments: string; -// Subject: string; -// Category: string; -// AntialiasingMode: string; -// TextAntialiasingMode: string; -// /** comma-delimited */ -// Keywords: string; -// }; -// /** comma-delimited */ -// capabilities: string; -// /** comma-delimited */ -// supportedQueryFormats: string; -// supportsDatumTransformation: boolean; -// maxRecordCount: number; -// maxImageHeight: number; -// maxImageWidth: number; -// generatedId: string; -// } +export function normalizeArcGISServerUrl( + url: string +): NormalizedArcGISServerLocation { + if (!/http[s]?:\/\//.test(url)) { + url = "https://" + url; + } + if (/arcgis\/rest\/services/i.test(url)) { + const parts = url.split(/\/arcgis\/rest\/services/i); + const baseUrl = parts[0]; + const servicesRoot = baseUrl + "/arcgis/rest/services"; + const location = parts[1].replace(/\/$/, "") || "/"; + return { baseUrl, servicesRoot, location }; + } else if (/arcgis\/rest/i.test(url)) { + const parts = url.split("/arcgis/rest"); + const baseUrl = parts[0]; + const servicesRoot = baseUrl + "/arcgis/rest/services"; + const location = "/"; + return { baseUrl, servicesRoot, location }; + } else if (/arcgis/i.test(url)) { + const parts = url.split("/arcgis"); + const baseUrl = parts[0]; + const servicesRoot = baseUrl + "/arcgis/rest/services"; + const location = "/"; + return { baseUrl, servicesRoot, location }; + } else if (/arcgis/i.test(url)) { + return { + baseUrl: "", + servicesRoot: "", + location: "", + }; + } else { + const baseUrl = url.replace(/\/$/, ""); + const servicesRoot = baseUrl + "/arcgis/rest/services"; + return { + baseUrl, + location: "/", + servicesRoot, + }; + } +} -// export interface Extent { -// xmin: number; -// ymin: number; -// xmax: number; -// ymax: number; -// spatialReference: SpatialReference; -// } +export interface SpatialReference { + /** Code for projection at the time of publishing. */ + wkid: number | string; + /** References the most up-to-date code for this projection in the current version of arcgis. For example, wkid might be 102100, but wkid would be 3857. https://support.esri.com/en/technical-article/000013950#:~:text=At%20ArcGIS%20version%2010%2C%20the,changed%20from%20102100%20to%203857.&text=For%20ArcGIS%20version%2010.1%20and,with%20the%20same%20spatial%20reference. */ + latestWkid?: number | string; +} -// export interface SummaryLayerInfo { -// id: number; -// name: string; -// parentLayerId: number; -// defaultVisibility: number; -// subLayerIds: number; -// minScale: number; -// maxScale: number; -// } +export function normalizeSpatialReference(sr: SpatialReference) { + const wkid = sr.latestWkid || sr.wkid; + if (typeof wkid === "string") { + if (/WGS\s*84/.test(wkid)) { + return 4326; + } else { + return -1; + } + } else { + return wkid || -1; + } +} -// export interface LayerInfo { -// id: number; -// url: string; -// type: "Feature Layer" | "Raster Layer" | "Group Layer"; -// geometryType: -// | "esriGeometryPoint" -// | "esriGeometryLine" -// | "esriGeometryPolyline" -// | "esriGeometryPolygon" -// | "esriGeometryMultipoint"; -// defaultVisibility: boolean; -// parentLayer?: { id: number }; -// extent: Extent; -// description?: string; -// copyrightText?: string; -// drawingInfo: { -// renderer: Symbol; -// scaleSymbols: boolean; -// transparency: number; -// labelingInfo: any; -// }; -// name: string; -// advancedQueryCapabilities: { -// supportsPagination: boolean; -// }; -// mapboxLayers: Layer[]; -// imageList: ImageList; -// generatedId: string; -// fields: { -// name: string; -// type: -// | "esriFieldTypeOID" -// | "esriFieldTypeDouble" -// | "esriFieldTypeSmallInteger" -// | "esriFieldTypeString" -// | "esriFieldTypeDate" -// | "esriFieldTypeGeometry" -// | "esriFieldTypeInteger"; -// alias: string; -// }[]; -// } +export interface MapServerCatalogInfo { + url: string; + currentVersion: number; + serviceDescription: string; + description: string; + mapName: string; + copyrightText: string; + supportsDynamicLayers: boolean; + layers: SummaryLayerInfo[]; + spatialReference: SpatialReference; + singleFusedMapCache: false; + initialExtent: Extent; + fullExtent: Extent; + minScale: number; + maxScale: number; + /** comma-delimited */ + supportedImageFormatTypes: string; + documentInfo?: { + Title: string; + Author: string; + Comments: string; + Subject: string; + Category: string; + AntialiasingMode: string; + TextAntialiasingMode: string; + /** comma-delimited */ + Keywords: string; + }; + /** comma-delimited */ + capabilities: string; + /** comma-delimited */ + supportedQueryFormats: string; + supportsDatumTransformation: boolean; + maxRecordCount: number; + maxImageHeight: number; + maxImageWidth: number; + generatedId: string; + tileInfo?: { + rows: number; + dpi: number; + cols: number; + format: string; + lods: { + level: number; + resolution: number; + scale: number; + }[]; + }; +} -// export const esriFieldTypesToTileJSONTypes = { -// esriFieldTypeOID: "Number", -// esriFieldTypeDouble: "Number", -// esriFieldTypeSmallInteger: "Number", -// esriFieldTypeString: "String", -// esriFieldTypeDate: "String", -// esriFieldTypeGeometry: "String", -// esriFieldTypeInteger: "Number", -// }; - -// const mapServerInfoCache: { -// [url: string]: { -// mapServerInfo: MapServerCatalogInfo; -// layerInfo: LayerInfo[]; -// }; -// } = {}; - -// export function useMapServerInfo(location: string | undefined) { -// const [loading, setLoading] = useState(true); -// const [error, setError] = useState(); -// const [data, setData] = useState<{ -// mapServerInfo: MapServerCatalogInfo; -// layerInfo: LayerInfo[]; -// }>(); - -// useEffect(() => { -// setLoading(false); -// setError(undefined); -// setData(undefined); -// if (!location) { -// return; -// } -// const cache = mapServerInfoCache[location]; -// if (mapServerInfoCache[location]) { -// setData({ -// mapServerInfo: cache.mapServerInfo, -// layerInfo: cache.layerInfo, -// }); -// setError(undefined); -// setLoading(false); -// } else { -// let abortController = new AbortController(); -// setLoading(true); -// fetch(location + "?f=json", { -// signal: abortController.signal, -// // cache: "force-cache", -// }) -// .then(async (r) => { -// const mapServerData = await r.json(); -// mapServerData.url = location; -// mapServerData.generatedId = uuid(); -// if (!abortController.signal.aborted) { -// fetch(`${location}/layers?f=json`, { -// signal: abortController.signal, -// // cache: "force-cache", -// }).then(async (r) => { -// const layerData = await r.json(); -// setError(undefined); -// setLoading(false); -// const layerInfo: LayerInfo[] = await Promise.all( -// layerData.layers.map(async (lyr: LayerInfo) => { -// const generatedId = uuid(); -// if (lyr.type === "Feature Layer") { -// let { layers, imageList } = await styleForFeatureLayer( -// location + "/" + lyr.id, -// generatedId -// ); -// imageList = await imageList; -// return { -// ...lyr, -// generatedId, -// mapboxLayers: layers, -// imageList, -// url: `${location}/${lyr.id}`, -// }; -// } else { -// return { -// ...lyr, -// generatedId, -// url: `${location}/${lyr.id}`, -// }; -// } -// }) -// ); -// const d = { -// mapServerInfo: mapServerData, -// layerInfo, -// }; -// setData(d); -// mapServerInfoCache[location] = d; -// }); -// } -// }) -// .catch((e) => setError(e)); -// return () => abortController.abort(); -// } -// }, [location]); +export interface Extent { + xmin: number; + ymin: number; + xmax: number; + ymax: number; + spatialReference: SpatialReference; +} -// return { -// loading, -// error, -// data, -// }; -// } +export interface SummaryLayerInfo { + id: number; + name: string; + parentLayerId: number; + defaultVisibility: number; + subLayerIds: number; + minScale: number; + maxScale: number; +} -// export function metersToDegrees(x: number, y: number): [number, number] { -// var lon = (x * 180) / 20037508.34; -// var lat = -// (Math.atan(Math.exp((y * Math.PI) / 20037508.34)) * 360) / Math.PI - 90; -// return [lon, lat]; -// } +export interface LayerInfo { + id: number; + url: string; + type: "Feature Layer" | "Raster Layer" | "Group Layer"; + geometryType: + | "esriGeometryPoint" + | "esriGeometryLine" + | "esriGeometryPolyline" + | "esriGeometryPolygon" + | "esriGeometryMultipoint"; + defaultVisibility: boolean; + parentLayer?: { id: number }; + extent: Extent; + description?: string; + copyrightText?: string; + drawingInfo: { + renderer: Symbol; + scaleSymbols: boolean; + transparency: number; + labelingInfo: any; + }; + name: string; + advancedQueryCapabilities: { + supportsPagination: boolean; + }; + mapboxLayers: Layer[]; + imageList: ImageList; + generatedId: string; + fields: { + name: string; + type: + | "esriFieldTypeOID" + | "esriFieldTypeDouble" + | "esriFieldTypeSmallInteger" + | "esriFieldTypeString" + | "esriFieldTypeDate" + | "esriFieldTypeGeometry" + | "esriFieldTypeInteger"; + alias: string; + }[]; +} -// export function extentToLatLngBounds( -// extent: Extent -// ): [[number, number], [number, number]] | void { -// if (extent) { -// const wkid = normalizeSpatialReference(extent.spatialReference); -// if (wkid === 4326) { -// return [ -// [extent.xmin, extent.ymin], -// [extent.xmax, extent.ymax], -// ]; -// } else if (wkid === 3857) { -// return [ -// metersToDegrees(extent.xmin, extent.ymin), -// metersToDegrees(extent.xmax, extent.ymax), -// ]; -// } -// } -// } +export const esriFieldTypesToTileJSONTypes = { + esriFieldTypeOID: "Number", + esriFieldTypeDouble: "Number", + esriFieldTypeSmallInteger: "Number", + esriFieldTypeString: "String", + esriFieldTypeDate: "String", + esriFieldTypeGeometry: "String", + esriFieldTypeInteger: "Number", +}; + +const mapServerInfoCache: { + [url: string]: { + mapServerInfo: MapServerCatalogInfo; + layerInfo: LayerInfo[]; + }; +} = {}; + +export function useMapServerInfo(location: string | undefined) { + const [loading, setLoading] = useState(true); + const [error, setError] = useState(); + const [data, setData] = useState<{ + mapServerInfo: MapServerCatalogInfo; + layerInfo: LayerInfo[]; + }>(); + + useEffect(() => { + setLoading(false); + setError(undefined); + setData(undefined); + if (!location) { + return; + } + const cache = mapServerInfoCache[location]; + if (mapServerInfoCache[location]) { + setData({ + mapServerInfo: cache.mapServerInfo, + layerInfo: cache.layerInfo, + }); + setError(undefined); + setLoading(false); + } else { + let abortController = new AbortController(); + setLoading(true); + fetch(location + "?f=json", { + signal: abortController.signal, + // cache: "force-cache", + }) + .then(async (r) => { + const mapServerData = await r.json(); + mapServerData.url = location; + mapServerData.generatedId = uuid(); + if (!abortController.signal.aborted) { + fetch(`${location}/layers?f=json`, { + signal: abortController.signal, + // cache: "force-cache", + }).then(async (r) => { + const layerData = await r.json(); + setError(undefined); + const layerInfo: LayerInfo[] = await Promise.all( + layerData.layers.map(async (lyr: LayerInfo) => { + const generatedId = uuid(); + if (lyr.type === "Feature Layer" && lyr.drawingInfo) { + let { layers, imageList } = await styleForFeatureLayer( + location, + lyr.id, + generatedId, + lyr + ); + imageList = await imageList; + return { + ...lyr, + generatedId, + mapboxLayers: layers, + imageList, + url: `${location}/${lyr.id}`, + }; + } else { + return { + ...lyr, + generatedId, + url: `${location}/${lyr.id}`, + }; + } + }) + ); + const d = { + mapServerInfo: mapServerData, + layerInfo, + }; + // if ( + // mapServerData.type === "FeatureServer" || + // mapServerData.type === "MapServer" + // ) { + // for (const info of d.layerInfo) { + // if (info.type === "Feature Layer") { + // // add style information + // info.mapboxGlStyleLayers = await styleForFeatureLayer(); + // } + // } + // } + setData(d); + setLoading(false); + mapServerInfoCache[location] = d; + }); + } + }) + .catch((e) => setError(e)); + return () => abortController.abort(); + } + }, [location]); + + return { + loading, + error, + data, + }; +} -// export type MapServerImageFormat = -// | "PNG" -// | "PNG8" -// | "PNG32" -// | "GIF" -// | "JPG" -// | "PNG24"; - -// export interface CatalogItem { -// name: string; -// type: -// | "Folder" -// | "GPServer" -// | "MapServer" -// | "FeatureServer" -// | "GeometryServer" -// | "GeocodeServer"; -// url: string; -// } +export interface MapServerCatalogItemDetails { + type: "MapServer"; + tiled: boolean; + metadata: MapServiceMetadata; + layers: LayersMetadata; +} -// export function useCatalogItems(location: string) { -// const [loading, setLoading] = useState(true); -// const [error, setError] = useState(); -// const [catalogInfo, setCatalogInfo] = useState(); - -// useEffect(() => { -// let abortController = new AbortController(); -// fetch(location + "?f=json", { -// signal: abortController.signal, -// // cache: "force-cache", -// }).then(async (r) => { -// const data = await r.json(); -// if (!abortController.signal.aborted) { -// setError(undefined); -// setLoading(false); -// const info = [ -// ...(data.folders || []).map((name: string) => ({ -// name, -// type: "Folder", -// })), -// ...data.services, -// ].map((item) => { -// const baseLocation = -// location.split("rest/services")[0] + "rest/services"; -// let url = baseLocation + "/" + item.name; -// if (item.type !== "Folder") { -// url += "/" + item.type; -// } -// let name = item.name; -// if (/\//.test(item.name)) { -// name = item.name.split("/").slice(-1); -// } -// return { -// name, -// url, -// type: item.type, -// }; -// }); -// setCatalogInfo(info); -// } -// }); -// return () => abortController.abort(); -// }, [location]); - -// return { -// loading: loading, -// error: error, -// catalogInfo: catalogInfo, -// }; -// } +export interface FeatureServerCatalogItemDetails { + type: "FeatureServer"; + metadata: FeatureServerMetadata; +} -// export interface ArcGISServiceSettings { -// sourceType: "arcgis-dynamic-mapservice" | "arcgis-vector-source"; -// enableHighDpi: boolean; -// imageFormat: MapServerImageFormat; -// renderUnder: RenderUnderType; -// excludedSublayers: string[]; -// vectorSublayerSettings: VectorSublayerSettings[]; -// // preferInstantLayers: boolean; -// } +export function useCatalogItemDetails( + requestManager: ArcGISRESTServiceRequestManager, + url?: string +) { + const [loading, setLoading] = useState(true); + const [error, setError] = useState(); + const [data, setData] = useState< + MapServerCatalogItemDetails | FeatureServerCatalogItemDetails | null + >(); + + useEffect(() => { + setLoading(true); + setError(undefined); + setData(null); + const AC = new AbortController(); + if (url && /MapServer/.test(url)) { + requestManager + .getMapServiceMetadata(url, { signal: AC.signal }) + .then((data) => { + if (!AC.signal.aborted) { + setError(undefined); + setLoading(false); + setData({ + type: "MapServer", + tiled: !!data.serviceMetadata.singleFusedMapCache, + metadata: data.serviceMetadata, + layers: data.layers, + }); + } + }) + .catch((e) => { + if (e.cancelled) { + // do nothing + } else { + setError(e.message); + } + }); + return () => AC.abort(); + } else if (url && /FeatureServer/.test(url)) { + requestManager + .getFeatureServerMetadata(url, { signal: AC.signal }) + .then((data) => { + if (!AC.signal.aborted) { + setError(undefined); + setLoading(false); + setData({ + type: "FeatureServer", + metadata: data.serviceMetadata, + }); + } + }) + .catch((e) => { + if (e.cancelled) { + // do nothing + } else { + setError(e.message); + } + }); + return () => AC.abort(); + } + }, [url]); + + return { data, loading, error }; +} -// export type VectorImportType = "geojson" | "dynamic"; - -// export interface VectorSublayerSettings { -// sublayer: number; -// renderUnder: RenderUnderType; -// outFields: string; -// importType: VectorImportType; -// geometryPrecision: 4 | 5 | 6; -// ignoreByteLimit: boolean; -// mapboxLayers?: any[]; -// uploadedGeoJSON?: FeatureCollection; -// // tileSource: string; -// // tileJobId: string; -// } +export function metersToDegrees(x: number, y: number): [number, number] { + var lon = (x * 180) / 20037508.34; + var lat = + (Math.atan(Math.exp((y * Math.PI) / 20037508.34)) * 360) / Math.PI - 90; + return [lon, lat]; +} -// const defaultServiceSettings: ArcGISServiceSettings = { -// sourceType: "arcgis-dynamic-mapservice", -// enableHighDpi: true, -// imageFormat: "PNG", -// renderUnder: RenderUnderType.Labels, -// excludedSublayers: [], -// vectorSublayerSettings: [], -// // preferInstantLayers: true, -// }; - -// const settingsCache: { [location: string]: ArcGISServiceSettings } = {}; - -// export function useArcGISServiceSettings( -// location: string | undefined -// ): [ -// ArcGISServiceSettings | undefined, -// (settings: ArcGISServiceSettings) => void -// ] { -// // eventually, init from localstorage too -// const [settings, setSettings] = useState(); -// useEffect(() => { -// let s: ArcGISServiceSettings | undefined; -// if (location) { -// s = settingsCache[location] || defaultServiceSettings; -// } else { -// s = undefined; -// } -// if (s && location && /FeatureServer/.test(location)) { -// s.sourceType = "arcgis-vector-source"; -// } -// setSettings(s); -// }, [location]); -// function updateSettings(settings: ArcGISServiceSettings) { -// setSettings(settings); -// if (location) { -// settingsCache[location] = settings; -// } -// } -// return [settings, updateSettings]; -// } +export function extentToLatLngBounds( + extent: Extent +): [[number, number], [number, number]] | void { + if (extent) { + const wkid = normalizeSpatialReference(extent.spatialReference); + if (wkid === 4326) { + return [ + [extent.xmin, extent.ymin], + [extent.xmax, extent.ymax], + ]; + } else if (wkid === 3857 || wkid === 102100) { + return [ + metersToDegrees(extent.xmin, extent.ymin), + metersToDegrees(extent.xmax, extent.ymax), + ]; + } + } +} -// const visibleLayersCache: { [location: string]: number[] } = {}; - -// export function useVisibleLayersSettings( -// location?: string -// ): [number[] | undefined, (layers: number[]) => void] { -// const [visibleLayers, setVisibleLayers] = useState(); -// useEffect(() => { -// if (location) { -// setVisibleLayers(visibleLayersCache[location] || undefined); -// } else { -// setVisibleLayers(undefined); -// } -// }, [location]); -// return [ -// visibleLayers, -// (layers: number[]) => { -// setVisibleLayers(layers); -// if (location) { -// visibleLayersCache[location] = layers; -// } -// }, -// ]; -// } +export type MapServerImageFormat = + | "PNG" + | "PNG8" + | "PNG32" + | "GIF" + | "JPG" + | "PNG24"; + +export interface CatalogItem { + name: string; + type: + | "Folder" + | "GPServer" + | "MapServer" + | "FeatureServer" + | "GeometryServer" + | "GeocodeServer"; + url: string; +} -// const featureLayerSizeDataCache: { -// [key: string]: FeatureLayerSizeInfo; -// } = {}; - -// const makeFeatureLayerSizeDataCacheKey = ( -// url: string, -// settings?: VectorSublayerSettings -// ) => -// url + -// "-" + -// (settings?.geometryPrecision || 6) + -// "-" + -// (settings?.outFields || "*"); - -// interface FeatureLayerSizeInfo { -// geoJsonBytes: number; -// gzipBytes: number; -// areaKm: number; -// objects: number; -// attributes: number; -// warnings: VectorImportWarning[]; -// } +export function useCatalogItems( + location: string, + requestManager: ArcGISRESTServiceRequestManager +) { + const [loading, setLoading] = useState(true); + const [error, setError] = useState(); + const [catalogInfo, setCatalogInfo] = useState(); + + useEffect(() => { + setCatalogInfo([]); + setLoading(true); + setError(undefined); + if (!location) { + return; + } + let abortController = new AbortController(); + requestManager + .getCatalogItems(location, { signal: abortController.signal }) + .then((data) => { + if (!abortController.signal.aborted) { + setError(undefined); + setLoading(false); + } + const info = [ + ...(data.folders || []).map((name: string) => ({ + name, + type: "Folder", + })), + ...data.services, + ].map((item) => { + const baseLocation = + location.split("rest/services")[0] + "rest/services"; + let url = baseLocation + "/" + item.name; + if (item.type !== "Folder") { + url += "/" + item.type; + } + let name = item.name; + if (/\//.test(item.name)) { + name = item.name.split("/").slice(-1)[0]; + } + return { + name, + url, + type: item.type as + | "Folder" + | "GPServer" + | "MapServer" + | "FeatureServer" + | "GeometryServer" + | "GeocodeServer", + }; + }); + setCatalogInfo(info); + }) + .catch((e) => { + if ("cancelled" in e) { + // do nothing + } else { + setError(e.message); + } + }); + return () => abortController.abort(); + }, [location]); + + return { + loading: loading, + error: error, + catalogInfo: catalogInfo, + }; +} -// export function useFeatureLayerSizeData( -// id: string, -// url: string, -// settings?: VectorSublayerSettings -// ) { -// const [data, setData] = useState(); -// const [loading, setLoading] = useState(); -// const [error, setError] = useState(); -// const mapContext = useContext(MapContext); -// const cache = mapContext.manager?.arcgisVectorSourceCache; - -// async function updateStats() { -// if (url && cache) { -// const key = makeFeatureLayerSizeDataCacheKey(url, settings); -// if (featureLayerSizeDataCache[key]) { -// setData(featureLayerSizeDataCache[key]); -// } else { -// const item = cache.get({ -// url, -// id, -// type: DataSourceTypes.ArcgisVector, -// queryParameters: { -// geometryPrecision: settings?.geometryPrecision, -// outFields: settings?.outFields, -// }, -// }); -// if (item.value) { -// const data = await layerData(item.value); -// featureLayerSizeDataCache[key] = data; -// setData(data); -// } else if (item.error) { -// setLoading(false); -// setError(item.error); -// } else { -// setLoading(true); -// item.promise -// .then(async (featureCollection) => { -// const data = await layerData(featureCollection); -// featureLayerSizeDataCache[key] = data; -// setData(data); -// }) -// .catch((e) => { -// // do nothing -// setError(e); -// setLoading(false); -// }); -// } -// } -// } else { -// setLoading(false); -// } -// } +export interface ArcGISServiceSettings { + sourceType: "arcgis-dynamic-mapservice" | "arcgis-vector-source"; + enableHighDpi: boolean; + imageFormat: MapServerImageFormat; + renderUnder: RenderUnderType; + excludedSublayers: string[]; + vectorSublayerSettings: VectorSublayerSettings[]; + // preferInstantLayers: boolean; +} -// useEffect(() => { -// if (cache) { -// setData(undefined); -// setError(undefined); -// updateStats(); -// } -// }, [settings?.geometryPrecision, settings?.outFields, cache, url, id]); -// return { -// data, -// loading, -// error, -// }; -// } +export type VectorImportType = "geojson" | "dynamic"; + +export interface VectorSublayerSettings { + sublayer: number; + renderUnder: RenderUnderType; + outFields: string; + importType: VectorImportType; + geometryPrecision: 4 | 5 | 6; + ignoreByteLimit: boolean; + mapboxLayers?: any[]; + uploadedGeoJSON?: FeatureCollection; + // tileSource: string; + // tileJobId: string; +} -// async function layerData( -// featureCollection: any, -// layerSettings?: VectorSublayerSettings -// ) { -// const str = JSON.stringify(featureCollection); -// const geoJsonBytes = byteLength(str); -// const box = bboxPolygon(bbox(featureCollection)); -// const sqMeters = area(box); -// const areaKm = sqMeters / 1000000; -// const gSize = await worker.gzippedSize(str); - -// const warnings = calculateWarnings( -// featureCollection.features.length, -// geoJsonBytes, -// layerSettings -// ); - -// return { -// geoJsonBytes, -// areaKm, -// gzipBytes: gSize, -// objects: featureCollection.features.length, -// attributes: Object.keys( -// (featureCollection.features[0] || { properties: {} }).properties -// ).length, -// warnings, -// }; -// } +const defaultServiceSettings: ArcGISServiceSettings = { + sourceType: "arcgis-dynamic-mapservice", + enableHighDpi: true, + imageFormat: "PNG", + renderUnder: RenderUnderType.Labels, + excludedSublayers: [], + vectorSublayerSettings: [], + // preferInstantLayers: true, +}; + +const settingsCache: { [location: string]: ArcGISServiceSettings } = {}; + +export function useArcGISServiceSettings( + location: string | undefined +): [ + ArcGISServiceSettings | undefined, + (settings: ArcGISServiceSettings) => void +] { + // eventually, init from localstorage too + const [settings, setSettings] = useState(); + useEffect(() => { + let s: ArcGISServiceSettings | undefined; + if (location) { + s = settingsCache[location] || defaultServiceSettings; + } else { + s = undefined; + } + if (s && location && /FeatureServer/.test(location)) { + s.sourceType = "arcgis-vector-source"; + } + setSettings(s); + }, [location]); + function updateSettings(settings: ArcGISServiceSettings) { + setSettings(settings); + if (location) { + settingsCache[location] = settings; + } + } + return [settings, updateSettings]; +} -// function syncLayerData( -// featureCollection: any, -// layerSettings?: VectorSublayerSettings -// ) { -// const str = JSON.stringify(featureCollection); -// const geoJsonBytes = byteLength(str); -// const box = bboxPolygon(bbox(featureCollection)); -// const sqMeters = area(box); -// const areaKm = sqMeters / 1000000; - -// const warnings = calculateWarnings( -// featureCollection.features.length, -// geoJsonBytes, -// layerSettings -// ); - -// return { -// geoJsonBytes, -// areaKm, -// objects: featureCollection.features.length, -// attributes: Object.keys( -// (featureCollection.features[0] || { properties: {} }).properties -// ).length, -// warnings, -// }; -// } +const visibleLayersCache: { [location: string]: number[] } = {}; + +export function useVisibleLayersSettings( + location?: string +): [number[] | undefined, (layers: number[]) => void] { + const [visibleLayers, setVisibleLayers] = useState(); + useEffect(() => { + if (location) { + setVisibleLayers(visibleLayersCache[location] || undefined); + } else { + setVisibleLayers(undefined); + } + }, [location]); + return [ + visibleLayers, + (layers: number[]) => { + setVisibleLayers(layers); + if (location) { + visibleLayersCache[location] = layers; + } + }, + ]; +} -// function calculateWarnings( -// numFeatures: number, -// geoJsonBytes: number, -// layerSettings?: VectorSublayerSettings -// ) { -// const warnings: VectorImportWarning[] = []; -// const numRequests = Math.ceil(numFeatures / 1000); -// if (numRequests > 1) { -// warnings.push({ -// type: "arcgis", -// level: "warning", -// /* eslint-disable-next-line */ -// message: `This dataset will require ${numRequests} requests to load due to the number of features.`, -// }); -// } -// if (geoJsonBytes > 5_000_000) { -// warnings.push({ -// type: "arcgis", -// level: "warning", -// /* eslint-disable-next-line */ -// message: `Dynamic vector sources are not recommended for large layers. Consider importing a copy into SeaSketch or using an Image Source.`, -// }); -// } -// if (geoJsonBytes > 30_000_000) { -// warnings.push({ -// type: "geojson", -// level: "warning", -// /* eslint-disable-next-line */ -// message: -// "Consider importing very large datasets individually to avoid a long import process.", -// }); -// } else if (geoJsonBytes > 5_000_000) { -// warnings.push({ -// type: "geojson", -// level: geoJsonBytes > 10_000_000 ? "warning" : "info", -// /* eslint-disable-next-line */ -// message: -// "This dataset could take a long time to download depending on connection speed. You should consider adding it to a tileset after import.", -// }); -// } -// return warnings; -// } +const featureLayerSizeDataCache: { + [key: string]: FeatureLayerSizeInfo; +} = {}; + +const makeFeatureLayerSizeDataCacheKey = ( + url: string, + settings?: VectorSublayerSettings +) => + url + + "-" + + (settings?.geometryPrecision || 6) + + "-" + + (settings?.outFields || "*"); + +interface FeatureLayerSizeInfo { + geoJsonBytes: number; + gzipBytes: number; + areaKm: number; + objects: number; + attributes: number; + warnings: VectorImportWarning[]; +} -// interface VectorImportWarning { -// type: "arcgis" | "geojson"; -// message: string; -// level: "error" | "warning" | "info"; -// } +function syncLayerData( + featureCollection: any, + layerSettings?: VectorSublayerSettings +) { + const str = JSON.stringify(featureCollection); + const geoJsonBytes = byteLength(str); + const box = bboxPolygon(bbox(featureCollection)); + const sqMeters = area(box); + const areaKm = sqMeters / 1000000; + + const warnings = calculateWarnings( + featureCollection.features.length, + geoJsonBytes, + layerSettings + ); + + return { + geoJsonBytes, + areaKm, + objects: featureCollection.features.length, + attributes: Object.keys( + (featureCollection.features[0] || { properties: {} }).properties + ).length, + warnings, + }; +} -// export function useVectorSublayerStatus( -// begin: boolean, -// layers?: LayerInfo[], -// settings?: VectorSublayerSettings[] -// ) { -// const mapContext = useContext(MapContext); -// const [state, setState] = useState<{ -// [id: string]: { -// error?: Error; -// loading?: boolean; -// loadedBytes?: number; -// loadedFeatures?: number; -// estimatedFeatures?: number; -// data?: Pick< -// FeatureLayerSizeInfo, -// "areaKm" | "attributes" | "geoJsonBytes" | "objects" | "warnings" -// >; -// }; -// }>({}); -// const [controller, setController] = useState(new AbortController()); -// const onError = (event: ArcGISVectorSourceCacheEvent) => { -// const layer = layers?.find((lyr) => lyr.generatedId === event.key); -// if (layer) { -// setState((prev) => ({ -// ...prev, -// [layer.generatedId]: { -// loading: false, -// error: event.item.error, -// }, -// })); -// } -// }; -// const onData = (event: ArcGISVectorSourceCacheEvent) => { -// const layer = layers?.find((lyr) => lyr.generatedId === event.key); -// if (layer) { -// setState((prev) => ({ -// ...prev, -// [layer.generatedId]: { -// data: syncLayerData(event.item.value), -// loading: false, -// error: undefined, -// loadedBytes: event.item.bytes, -// loadedFeatures: event.item.loadedFeatures, -// estimatedFeatures: event.item.estimatedFeatures, -// }, -// })); -// } -// }; -// const onProgress = (event: ArcGISVectorSourceCacheEvent) => { -// const layer = layers?.find((lyr) => lyr.generatedId === event.key); -// if (layer) { -// setState((prev) => ({ -// ...prev, -// [layer.generatedId]: { -// loading: true, -// loadedBytes: event.item.bytes, -// loadedFeatures: event.item.loadedFeatures, -// estimatedFeatures: event.item.estimatedFeatures, -// }, -// })); -// } -// }; +function calculateWarnings( + numFeatures: number, + geoJsonBytes: number, + layerSettings?: VectorSublayerSettings +) { + const warnings: VectorImportWarning[] = []; + const numRequests = Math.ceil(numFeatures / 1000); + if (numRequests > 1) { + warnings.push({ + type: "arcgis", + level: "warning", + /* eslint-disable-next-line */ + message: `This dataset will require ${numRequests} requests to load due to the number of features.`, + }); + } + if (geoJsonBytes > 5_000_000) { + warnings.push({ + type: "arcgis", + level: "warning", + /* eslint-disable-next-line */ + message: `Dynamic vector sources are not recommended for large layers. Consider importing a copy into SeaSketch or using an Image Source.`, + }); + } + if (geoJsonBytes > 30_000_000) { + warnings.push({ + type: "geojson", + level: "warning", + /* eslint-disable-next-line */ + message: + "Consider importing very large datasets individually to avoid a long import process.", + }); + } else if (geoJsonBytes > 5_000_000) { + warnings.push({ + type: "geojson", + level: geoJsonBytes > 10_000_000 ? "warning" : "info", + /* eslint-disable-next-line */ + message: + "This dataset could take a long time to download depending on connection speed. You should consider adding it to a tileset after import.", + }); + } + return warnings; +} -// useEffect(() => { -// if (mapContext.manager?.arcgisVectorSourceCache) { -// mapContext.manager.arcgisVectorSourceCache.on("error", onError); -// mapContext.manager.arcgisVectorSourceCache.on("data", onData); -// mapContext.manager.arcgisVectorSourceCache.on("progress", onProgress); -// return () => { -// mapContext.manager?.arcgisVectorSourceCache.off("error", onError); -// mapContext.manager?.arcgisVectorSourceCache.off("data", onData); -// mapContext.manager?.arcgisVectorSourceCache.off("progress", onProgress); -// }; -// } -// }, [mapContext.manager, onError, onData, onProgress]); - -// useEffect(() => { -// setState({}); -// if (begin && mapContext.manager) { -// (async () => { -// for (const layer of layers || []) { -// if (layer.type === "Feature Layer") { -// const layerSettings = settings?.find( -// (s) => s.sublayer === layer.id -// ); -// const item = mapContext.manager?.arcgisVectorSourceCache.get({ -// id: layer.generatedId, -// url: layer.url, -// type: DataSourceTypes.ArcgisVector, -// queryParameters: { -// outFields: layerSettings?.outFields, -// geometryPrecision: layerSettings?.geometryPrecision, -// }, -// }); -// if (item?.value) { -// setState((prev) => ({ -// ...prev, -// [layer.generatedId]: { -// data: syncLayerData(item.value), -// loading: false, -// error: undefined, -// loadedBytes: item.bytes, -// loadedFeatures: item.loadedFeatures, -// estimatedFeatures: item.estimatedFeatures, -// }, -// })); -// } else if (item) { -// setState((prev) => ({ -// ...prev, -// [layer.generatedId]: { -// loading: true, -// error: undefined, -// loadedBytes: 0, -// loadedFeatures: 0, -// }, -// })); -// try { -// await item.promise; -// } catch (e) { -// setState((prev) => ({ -// ...prev, -// [layer.generatedId]: { -// loading: false, -// error: e, -// }, -// })); -// } -// } else { -// // do nothing -// } -// } -// } -// })(); -// } -// }, [layers, settings, begin, mapContext.manager]); - -// // (async () => { -// // for (const layer of layers || []) { -// // if (layer.type === "Feature Layer") { -// // const layerSettings = settings?.find( -// // (s) => s.sublayer === layer.id -// // ); -// // const key = makeFeatureLayerSizeDataCacheKey( -// // layer.url, -// // layerSettings -// // ); -// // if (!controller.signal.aborted) { -// // if (featureLayerSizeDataCache[key]) { -// // // recalculate warnings in case settings changed -// // const data = featureLayerSizeDataCache[key]; -// // data.warnings = calculateWarnings( -// // data.objects, -// // data.geoJsonBytes, -// // layerSettings -// // ); - -// // setState((prev) => ({ -// // ...prev, -// // [layer.generatedId]: { -// // data, -// // loading: false, -// // error: undefined, -// // }, -// // })); -// // } else { -// // setState((prev) => ({ -// // ...prev, -// // [layer.generatedId]: { -// // loading: true, -// // }, -// // })); -// // try { -// // const data = await fetchFeatureSizeDetails( -// // layer.url, -// // controller, -// // layerSettings, -// // (bytes, loadedFeatures, estimatedFeatures) => { -// // setState((prev) => ({ -// // ...prev, -// // [layer.generatedId]: { -// // loading: true, -// // loadedBytes: bytes, -// // loadedFeatures, -// // estimatedFeatures, -// // }, -// // })); -// // } -// // ); - -// // setState((prev) => { -// // return { -// // ...prev, -// // [layer.generatedId]: { -// // loading: false, -// // data, -// // }, -// // }; -// // }); -// // featureLayerSizeDataCache[key] = data; -// // } catch (e) { -// // setState((prev) => { -// // return { -// // ...prev, -// // [layer.generatedId]: { -// // error: e, -// // }, -// // }; -// // }); -// // } -// // } -// // } else { -// // // setState({}); -// // } -// // } -// // } -// // })(); -// // } -// // return () => { -// // controller.abort(); -// // setController(new AbortController()); -// // }; -// // }, [layers, settings, begin, mapContext.manager]); - -// return { layerStatus: state, abortController: controller }; -// } +interface VectorImportWarning { + type: "arcgis" | "geojson"; + message: string; + level: "error" | "warning" | "info"; +} -// // https://stackoverflow.com/a/23329386/299467 -// export function byteLength(str: string) { -// // returns the byte length of an utf8 string -// var s = str.length; -// for (var i = str.length - 1; i >= 0; i--) { -// var code = str.charCodeAt(i); -// if (code > 0x7f && code <= 0x7ff) s++; -// else if (code > 0x7ff && code <= 0xffff) s += 2; -// if (code >= 0xdc00 && code <= 0xdfff) i--; //trail surrogate -// } -// return s; -// } +// https://stackoverflow.com/a/23329386/299467 +export function byteLength(str: string) { + // returns the byte length of an utf8 string + var s = str.length; + for (var i = str.length - 1; i >= 0; i--) { + var code = str.charCodeAt(i); + if (code > 0x7f && code <= 0x7ff) s++; + else if (code > 0x7ff && code <= 0xffff) s += 2; + if (code >= 0xdc00 && code <= 0xdfff) i--; //trail surrogate + } + return s; +} // export function treeDataFromLayerList(layers: LayerInfo[]) { -// let data: ClientTableOfContentsItem[] = []; -// let nodesBySublayer: { [id: string]: ClientTableOfContentsItem } = { +// let data: OverlayFragment[] = []; +// let nodesBySublayer: { [id: string]: OverlayFragment } = { // root: { -// id: "root", +// id: 0, // title: "Layers", -// expanded: true, // isFolder: true, // children: [], // isClickOffOnly: false, @@ -1015,568 +800,466 @@ export function generateStableId() { // return data; // } -// interface ImportArcGISServiceState { -// inProgress: boolean; -// statusMessage?: string; -// error?: Error; -// abortController?: AbortController; -// parentStableIds?: { [sublayer: number]: string }; -// /* 0.0 - 1.0. Used to render a progress bar */ -// progress?: number; -// } - -// export function useImportArcGISService( -// serviceRoot?: string -// ): [ -// ( -// layerInfo: LayerInfo[], -// mapServerInfo: MapServerCatalogInfo, -// projectId: number, -// settings: ArcGISServiceSettings, -// importType: "vector" | "image", -// totalBytesForUpload?: number -// ) => Promise, -// ImportArcGISServiceState -// ] { -// // const [mutate, mutationState] = useCreateFolderMutation(); -// const [ -// createTableOfContentsItem, -// createTableOfContentsItemState, -// ] = useCreateTableOfContentsItemMutation({ -// refetchQueries: [DraftTableOfContentsDocument.loc!.source.body], -// }); -// const [ -// createDynamicSource, -// dynamicSourceState, -// ] = useCreateArcGisDynamicDataSourceMutation(); -// const [ -// createSeaSketchVectorSource, -// seasketchVectorSourceState, -// ] = useCreateSeaSketchVectorSourceMutation(); -// const [createDataLayer, createDataLayerState] = useCreateDataLayerMutation(); -// const [ -// createArcGISImageSource, -// createArcGISImageSourceState, -// ] = useCreateArcGisImageSourceMutation(); -// const [ -// updateInteractivitySettings, -// updateInteractivitySettingsState, -// ] = useUpdateInteractivitySettingsMutation(); -// const [createSprite, createSpriteState] = useGetOrCreateSpriteMutation(); -// const [addImageToSprite] = useAddImageToSpriteMutation(); -// const mapContext = useContext(MapContext); -// // importService: ( -// // layerInfo: LayerInfo[], -// // mapServerInfo: MapServerCatalogInfo, -// // projectId: number, -// // settings: ArcGISServiceSettings, -// // importType: "vector" | "image", -// // totalBytesForUpload?: number -// // ) => Promise; -// const [state, setState] = useState({ -// inProgress: false, -// }); -// useEffect(() => { -// setState((prev) => { -// return { -// ...prev, -// inProgress: false, -// error: undefined, -// parentStableIds: undefined, -// }; -// }); -// }, [serviceRoot]); -// const importService = async ( -// layerInfo: LayerInfo[], -// mapServerInfo: MapServerCatalogInfo, -// projectId: number, -// settings: ArcGISServiceSettings, -// importType: "vector" | "image", -// totalBytesForUpload?: number -// ) => { -// try { -// let totalBytesUploaded = 0; -// let imageSourceId: number | undefined; -// let progress = 0.0; -// let totalProgressCredits = -// (totalBytesForUpload || 0) + -// layerInfo.filter((l) => { -// return ( -// l.type != "Group Layer" && -// !!settings.vectorSublayerSettings.find( -// (s) => s.sublayer === l.id -// ) && -// settings.excludedSublayers.indexOf(l.generatedId) === -1 -// ); -// }).length * -// 1000; -// if (importType === "image") { -// totalProgressCredits = -// layerInfo.filter((l) => { -// return ( -// l.type != "Group Layer" && -// settings.excludedSublayers.indexOf(l.generatedId) === -1 -// ); -// }).length * 1000; -// } - -// const stableIds: { [sublayer: number]: string } = {}; -// const saved: { [sublayer: number]: boolean } = {}; -// const saveItem = async ( -// layer: LayerInfo, -// mapServerInfo: MapServerCatalogInfo, -// containerFolderId?: string -// ) => { -// try { -// if (state.abortController && state.abortController.signal.aborted) { -// return; -// } -// if (saved[layer.id]) { -// return stableIds[layer.id]; -// } else { -// let parentStableId: string | undefined = undefined; -// if (layer.parentLayer && layer.parentLayer.id !== -1) { -// parentStableId = await saveItem( -// layerInfo.find((l) => l.id === layer.parentLayer!.id)!, -// mapServerInfo -// ); -// } else { -// parentStableId = containerFolderId; -// } -// const id = nanoId(); -// let bounds: number[] | undefined; -// if (layer.type === "Group Layer") { -// setState((prev) => { -// return { -// ...prev, -// statusMessage: `Saving folder "${layer.name}"`, -// }; -// }); -// await createTableOfContentsItem({ -// variables: { -// projectId, -// title: layer.name, -// stableId: id, -// parentStableId: parentStableId || undefined, -// isFolder: true, -// }, -// }); -// } else { -// const layerSettings = settings.vectorSublayerSettings.find( -// (s) => s.sublayer === layer.id -// ); -// let sourceId: number; -// let dataLayerId: number; -// if (importType === "vector") { -// const isDynamic = -// layerSettings && layerSettings.importType == "dynamic"; -// if (!isDynamic) { -// // need to upload the geojson and create a new source -// setState((prev) => { -// return { -// ...prev, -// statusMessage: `Downloading source data for "${layer.name}"`, -// }; -// }); - -// const cacheItem = mapContext.manager!.arcgisVectorSourceCache.get( -// { -// type: DataSourceTypes.ArcgisVector, -// id: layer.generatedId, -// url: layer.url, -// queryParameters: { -// outFields: layerSettings?.outFields, -// geometryPrecision: layerSettings?.geometryPrecision, -// }, -// } -// ); -// if (!cacheItem.value) { -// await cacheItem.promise; -// } -// const featureCollection = cacheItem.value; - -// bounds = bbox(featureCollection); - -// setState((prev) => { -// return { -// ...prev, -// statusMessage: `Creating source record for "${layer.name}"`, -// }; -// }); -// const stringifiedJSON = JSON.stringify(featureCollection); -// const { data, errors } = await createSeaSketchVectorSource({ -// variables: { -// projectId, -// attribution: -// contentOrFalse(layer.copyrightText) || -// contentOrFalse(mapServerInfo.copyrightText) || -// contentOrFalse(mapServerInfo.documentInfo?.Author) || -// null, -// bounds: bounds, -// byteLength: byteLength(stringifiedJSON), -// enhancedSecurity: false, -// importType: DataSourceImportTypes.Arcgis, -// originalSourceUrl: layer.url, -// }, -// }); -// if (errors && errors.length) { -// throw new Error(errors[0].message); -// } -// const source = data!.createDataSource!.dataSource!; -// sourceId = data!.createDataSource!.dataSource!.id; -// setState((prev) => { -// return { -// ...prev, -// statusMessage: `Uploading "${layer.name}"`, -// }; -// }); - -// try { -// const response = await axios({ -// method: "PUT", -// url: source.presignedUploadUrl!, -// data: stringifiedJSON, -// headers: { -// "content-type": "application/json", -// "x-amz-tagging": source.enhancedSecurity -// ? "enhancedSecurity=YES" -// : "", -// "cache-control": "max-age=31536000, immutable", -// }, -// onUploadProgress: (event) => { -// const layerProgress = Math.round( -// (event.loaded * 100) / event.total -// ); -// // progress += event.loaded; -// setState((prev) => { -// return { -// ...prev, -// statusMessage: `Uploading "${layer.name}" ${layerProgress}%`, -// progress: -// (progress + event.loaded) / totalProgressCredits, -// }; -// }); -// }, -// }); -// } catch (e) { -// const error = new Error(e.message); -// error.name = "S3UploadError"; -// throw error; -// } -// progress += byteLength(stringifiedJSON); -// } else { -// // source is simpler. just provide options -// setState((prev) => { -// return { -// ...prev, -// statusMessage: `Creating source for "${layer.name}"`, -// }; -// }); -// let queryParameters = { -// geometryPrecision: layerSettings?.geometryPrecision || 6, -// outFields: layerSettings?.outFields || "*", -// }; -// const latLngBounds = extentToLatLngBounds(layer.extent); -// bounds = latLngBounds -// ? [ -// latLngBounds[0][0], -// latLngBounds[0][1], -// latLngBounds[1][0], -// latLngBounds[1][1], -// ] -// : undefined; - -// const sourceResponse = await createDynamicSource({ -// variables: { -// projectId, -// url: layer.url, -// attribution: -// contentOrFalse(layer.copyrightText) || -// contentOrFalse(mapServerInfo.copyrightText) || -// contentOrFalse(mapServerInfo.documentInfo?.Author) || -// null, -// bounds: bounds, -// queryParameters: queryParameters, -// }, -// }); - -// sourceId = sourceResponse.data!.createDataSource!.dataSource! -// .id; -// progress += 1000; -// } - -// // remove source and id from gl-style layers (generated at runtime) -// let glStyles = -// layerSettings?.mapboxLayers || layer.mapboxLayers || []; -// for (const style of glStyles) { -// delete style.id; -// delete style.source; -// } - -// let idReplacements: { [oldId: string]: string } = {}; -// if ( -// !isDynamic && -// layer.imageList && -// layer.imageList.toJSON().length > 0 -// ) { -// // upload any sprites if needed -// setState((prev) => { -// return { -// ...prev, -// statusMessage: `Creating sprites for "${layer.name}".`, -// }; -// }); -// idReplacements = await getOrCreateSpritesFromImageSet( -// layer.imageList, -// projectId, -// createSprite, -// addImageToSprite -// ); -// } - -// glStyles = replaceSpriteIds(glStyles, idReplacements); -// // Create the layer -// const dataLayerData = await createDataLayer({ -// variables: { -// projectId, -// dataSourceId: sourceId!, -// mapboxGlStyles: !isDynamic ? glStyles : null, -// // @ts-ignore -// renderUnder: layerSettings?.renderUnder?.toUpperCase(), -// // sublayer: isDynamic ? layer.id.toString() : undefined, -// }, -// }); -// dataLayerId = dataLayerData.data!.createDataLayer!.dataLayer! -// .id; -// const interactivitySettingsId = -// dataLayerData.data?.createDataLayer?.dataLayer -// ?.interactivitySettings?.id; -// if (interactivitySettingsId) { -// await updateInteractivitySettings({ -// variables: { -// id: interactivitySettingsId, -// shortTemplate: `${layer.name}`, -// longTemplate: ` -// ${layer.name} -// -// ${layer.fields -// .map( -// (field) => ` -// -// -// ` -// ) -// .join("\n")} -//
    ${field.name}{{${field.name}}}
    -// `, -// }, -// }); -// } -// } else { -// // create dataLayers for the current sublayer -// const dataLayerData = await createDataLayer({ -// variables: { -// projectId, -// dataSourceId: imageSourceId!, -// sublayer: layer.id.toString(), -// // @ts-ignore -// renderUnder: layerSettings?.renderUnder?.toUpperCase(), -// // sublayer: isDynamic ? layer.id.toString() : undefined, -// }, -// }); -// dataLayerId = dataLayerData.data!.createDataLayer!.dataLayer! -// .id; -// const interactivitySettingsId = -// dataLayerData.data?.createDataLayer?.dataLayer -// ?.interactivitySettings?.id; -// if (interactivitySettingsId) { -// await updateInteractivitySettings({ -// variables: { -// id: interactivitySettingsId, -// shortTemplate: `${layer.name}`, -// longTemplate: ` -// ${layer.name} -// -// ${layer.fields -// .map( -// (field) => ` -// -// -// ` -// ) -// .join("\n")} -//
    ${field.name}{{${field.name}}}
    -// `, -// }, -// }); -// } -// } - -// // Create the table of contents item -// await createTableOfContentsItem({ -// variables: { -// title: layer.name, -// stableId: id, -// projectId, -// parentStableId: -// layer.parentLayer && layer.parentLayer.id !== -1 -// ? stableIds[layer.parentLayer.id] -// : parentStableId, -// isFolder: false, -// dataLayerId: dataLayerId, -// bounds, -// metadata: generateMetadataForLayer( -// mapServerInfo, -// layer, -// !layerSettings || layerSettings.importType === "geojson" -// ), -// }, -// }); -// } -// stableIds[layer.id] = id; -// saved[layer.id] = true; -// return id; -// } -// } catch (e) { -// console.error(e); -// throw e; -// } -// }; - -// setState((prev) => { -// return { -// ...prev, -// inProgress: true, -// abortController: new AbortController(), -// progress: progress / totalProgressCredits, -// }; -// }); - -// // Loop through each layer, adding each and any parent folders - -// const dataLayers = layerInfo.filter((l) => l.type !== "Group Layer"); -// let error: Error; - -// if (importType === "image") { -// setState((prev) => { -// return { -// ...prev, -// statusMessage: `Creating source record ${ -// mapServerInfo.mapName ? `"${mapServerInfo.mapName}"` : "" -// }`, -// }; -// }); -// const latLngBounds = extentToLatLngBounds(mapServerInfo.fullExtent); -// try { -// const sourceResponse = await createArcGISImageSource({ -// variables: { -// projectId, -// url: mapServerInfo.url, -// attribution: -// contentOrFalse(mapServerInfo.copyrightText) || -// contentOrFalse(mapServerInfo.documentInfo?.Author) || -// null, -// bounds: latLngBounds -// ? [ -// latLngBounds[0][0], -// latLngBounds[0][1], -// latLngBounds[1][0], -// latLngBounds[1][1], -// ] -// : null, -// enableHighDPI: settings.enableHighDpi, -// supportsDynamicLayers: mapServerInfo.supportsDynamicLayers, -// }, -// }); -// imageSourceId = sourceResponse.data!.createDataSource!.dataSource!.id; -// } catch (e) { -// throw new Error(`Problem saving source. ${e.message}`); -// } -// } - -// const layers = dataLayers.filter( -// (l) => settings.excludedSublayers.indexOf(l.generatedId) === -1 -// ); -// let folderStableId: string | undefined; -// // If vector import, create containing folder first -// if (layers.length > 1) { -// const folderName = -// mapServerInfo.documentInfo?.Title || -// mapServerInfo.documentInfo?.Subject || -// mapServerInfo.serviceDescription || -// "New Import"; -// setState((prev) => { -// return { -// ...prev, -// progress: 0, -// statusMessage: `Creating folder "${folderName}"`, -// }; -// }); -// folderStableId = nanoId(); -// await createTableOfContentsItem({ -// variables: { -// title: folderName, -// stableId: folderStableId, -// projectId, -// isFolder: true, -// }, -// }); -// } +interface ImportArcGISServiceState { + inProgress: boolean; + statusMessage?: string; + error?: Error; + abortController?: AbortController; + parentStableIds?: { [sublayer: number]: string }; + /* 0.0 - 1.0. Used to render a progress bar */ + progress?: number; +} -// for (const layer of layers) { -// // check first if item has any children -// if (state.abortController && state.abortController.signal.aborted) { -// return; -// } -// progress += 1000; -// setState((prev) => { -// return { -// ...prev, -// progress: progress / totalProgressCredits, -// statusMessage: `Saving layer ${layer.name}`, -// }; -// }); -// try { -// await saveItem(layer, mapServerInfo, folderStableId); -// } catch (e) { -// error = e; -// break; -// } -// } -// if (error!) { -// setState((prev) => { -// return { -// ...prev, -// error, -// inProgress: false, -// // parentStableIds: stableIds, -// }; -// }); -// throw error; -// } else { -// setState((prev) => { -// return { -// ...prev, -// inProgress: false, -// parentStableIds: stableIds, -// }; -// }); -// return state; -// } -// } catch (e) { -// setState((prev) => { -// return { -// ...prev, -// error: e, -// inProgress: false, -// parentStableIds: undefined, -// }; -// }); -// throw e; -// } -// }; -// return [importService, state]; -// } +export function useImportArcGISService( + serviceRoot?: string +): [ + ( + layerInfo: LayerInfo[], + mapServerInfo: MapServerCatalogInfo, + projectId: number, + settings: ArcGISServiceSettings, + importType: "vector" | "image", + totalBytesForUpload?: number + ) => Promise, + ImportArcGISServiceState +] { + // const [mutate, mutationState] = useCreateFolderMutation(); + const [createTableOfContentsItem, createTableOfContentsItemState] = + useCreateTableOfContentsItemMutation({ + refetchQueries: [DraftTableOfContentsDocument.loc!.source.body], + }); + const [createDynamicSource, dynamicSourceState] = + useCreateArcGisDynamicDataSourceMutation(); + const [createSeaSketchVectorSource, seasketchVectorSourceState] = + useCreateSeaSketchVectorSourceMutation(); + const [createDataLayer, createDataLayerState] = useCreateDataLayerMutation(); + const [createArcGISImageSource, createArcGISImageSourceState] = + useCreateArcGisImageSourceMutation(); + const [updateInteractivitySettings, updateInteractivitySettingsState] = + useUpdateInteractivitySettingsMutation(); + const [createSprite, createSpriteState] = useGetOrCreateSpriteMutation(); + const [addImageToSprite] = useAddImageToSpriteMutation(); + const mapContext = useContext(MapContext); + // importService: ( + // layerInfo: LayerInfo[], + // mapServerInfo: MapServerCatalogInfo, + // projectId: number, + // settings: ArcGISServiceSettings, + // importType: "vector" | "image", + // totalBytesForUpload?: number + // ) => Promise; + const [state, setState] = useState({ + inProgress: false, + }); + useEffect(() => { + setState((prev) => { + return { + ...prev, + inProgress: false, + error: undefined, + parentStableIds: undefined, + }; + }); + }, [serviceRoot]); + const importService = async ( + layerInfo: LayerInfo[], + mapServerInfo: MapServerCatalogInfo, + projectId: number, + settings: ArcGISServiceSettings, + importType: "vector" | "image", + totalBytesForUpload?: number + ) => { + try { + let totalBytesUploaded = 0; + let imageSourceId: number | undefined; + let progress = 0.0; + let totalProgressCredits = + (totalBytesForUpload || 0) + + layerInfo.filter((l) => { + return ( + l.type != "Group Layer" && + !!settings.vectorSublayerSettings.find( + (s) => s.sublayer === l.id + ) && + settings.excludedSublayers.indexOf(l.generatedId) === -1 + ); + }).length * + 1000; + if (importType === "image") { + totalProgressCredits = + layerInfo.filter((l) => { + return ( + l.type != "Group Layer" && + settings.excludedSublayers.indexOf(l.generatedId) === -1 + ); + }).length * 1000; + } + + const stableIds: { [sublayer: number]: string } = {}; + const saved: { [sublayer: number]: boolean } = {}; + const saveItem = async ( + layer: LayerInfo, + mapServerInfo: MapServerCatalogInfo, + containerFolderId?: string + ) => { + try { + if (state.abortController && state.abortController.signal.aborted) { + return; + } + if (saved[layer.id]) { + return stableIds[layer.id]; + } else { + let parentStableId: string | undefined = undefined; + if (layer.parentLayer && layer.parentLayer.id !== -1) { + parentStableId = await saveItem( + layerInfo.find((l) => l.id === layer.parentLayer!.id)!, + mapServerInfo + ); + } else { + parentStableId = containerFolderId; + } + const id = nanoId(); + let bounds: number[] | undefined; + if (layer.type === "Group Layer") { + setState((prev) => { + return { + ...prev, + statusMessage: `Saving folder "${layer.name}"`, + }; + }); + await createTableOfContentsItem({ + variables: { + projectId, + title: layer.name, + stableId: id, + parentStableId: parentStableId || undefined, + isFolder: true, + }, + }); + } else { + const layerSettings = settings.vectorSublayerSettings.find( + (s) => s.sublayer === layer.id + ); + let sourceId: number; + let dataLayerId: number; + if (importType === "vector") { + const isDynamic = + layerSettings && layerSettings.importType == "dynamic"; + if (!isDynamic) { + throw new Error("Only dynamic layers are supported"); + } else { + // source is simpler. just provide options + setState((prev) => { + return { + ...prev, + statusMessage: `Creating source for "${layer.name}"`, + }; + }); + let queryParameters = { + geometryPrecision: layerSettings?.geometryPrecision || 6, + outFields: layerSettings?.outFields || "*", + }; + const latLngBounds = extentToLatLngBounds(layer.extent); + bounds = latLngBounds + ? [ + latLngBounds[0][0], + latLngBounds[0][1], + latLngBounds[1][0], + latLngBounds[1][1], + ] + : undefined; + + const sourceResponse = await createDynamicSource({ + variables: { + projectId, + url: layer.url, + attribution: + contentOrFalse(layer.copyrightText) || + contentOrFalse(mapServerInfo.copyrightText) || + contentOrFalse(mapServerInfo.documentInfo?.Author) || + null, + bounds: bounds, + queryParameters: queryParameters, + }, + }); + + sourceId = + sourceResponse.data!.createDataSource!.dataSource!.id; + progress += 1000; + } + + // remove source and id from gl-style layers (generated at runtime) + let glStyles = + layerSettings?.mapboxLayers || layer.mapboxLayers || []; + for (const style of glStyles) { + delete style.id; + delete style.source; + } + + let idReplacements: { [oldId: string]: string } = {}; + if ( + !isDynamic && + layer.imageList && + layer.imageList.toJSON().length > 0 + ) { + // upload any sprites if needed + setState((prev) => { + return { + ...prev, + statusMessage: `Creating sprites for "${layer.name}".`, + }; + }); + idReplacements = await getOrCreateSpritesFromImageSet( + layer.imageList, + projectId, + createSprite, + addImageToSprite + ); + } + + glStyles = replaceSpriteIds(glStyles, idReplacements); + // Create the layer + const dataLayerData = await createDataLayer({ + variables: { + projectId, + dataSourceId: sourceId!, + mapboxGlStyles: !isDynamic ? glStyles : null, + // @ts-ignore + renderUnder: layerSettings?.renderUnder?.toUpperCase(), + // sublayer: isDynamic ? layer.id.toString() : undefined, + }, + }); + dataLayerId = + dataLayerData.data!.createDataLayer!.dataLayer!.id; + const interactivitySettingsId = + dataLayerData.data?.createDataLayer?.dataLayer + ?.interactivitySettings?.id; + if (interactivitySettingsId) { + await updateInteractivitySettings({ + variables: { + id: interactivitySettingsId, + shortTemplate: `${layer.name}`, + longTemplate: ` +${layer.name} + +${layer.fields + .map( + (field) => ` + + +` + ) + .join("\n")} +
    ${field.name}{{${field.name}}}
    + `, + }, + }); + } + } else { + // create dataLayers for the current sublayer + const dataLayerData = await createDataLayer({ + variables: { + projectId, + dataSourceId: imageSourceId!, + sublayer: layer.id.toString(), + // @ts-ignore + renderUnder: layerSettings?.renderUnder?.toUpperCase(), + // sublayer: isDynamic ? layer.id.toString() : undefined, + }, + }); + dataLayerId = + dataLayerData.data!.createDataLayer!.dataLayer!.id; + const interactivitySettingsId = + dataLayerData.data?.createDataLayer?.dataLayer + ?.interactivitySettings?.id; + if (interactivitySettingsId) { + await updateInteractivitySettings({ + variables: { + id: interactivitySettingsId, + shortTemplate: `${layer.name}`, + longTemplate: ` +${layer.name} + +${layer.fields + .map( + (field) => ` + + +` + ) + .join("\n")} +
    ${field.name}{{${field.name}}}
    + `, + }, + }); + } + } + + // Create the table of contents item + await createTableOfContentsItem({ + variables: { + title: layer.name, + stableId: id, + projectId, + parentStableId: + layer.parentLayer && layer.parentLayer.id !== -1 + ? stableIds[layer.parentLayer.id] + : parentStableId, + isFolder: false, + dataLayerId: dataLayerId, + bounds, + metadata: generateMetadataForLayer( + mapServerInfo, + layer, + !layerSettings || layerSettings.importType === "geojson" + ), + }, + }); + } + stableIds[layer.id] = id; + saved[layer.id] = true; + return id; + } + } catch (e) { + console.error(e); + throw e; + } + }; + + setState((prev) => { + return { + ...prev, + inProgress: true, + abortController: new AbortController(), + progress: progress / totalProgressCredits, + }; + }); + + // Loop through each layer, adding each and any parent folders + + const dataLayers = layerInfo.filter((l) => l.type !== "Group Layer"); + let error: Error; + + if (importType === "image") { + setState((prev) => { + return { + ...prev, + statusMessage: `Creating source record ${ + mapServerInfo.mapName ? `"${mapServerInfo.mapName}"` : "" + }`, + }; + }); + const latLngBounds = extentToLatLngBounds(mapServerInfo.fullExtent); + try { + const sourceResponse = await createArcGISImageSource({ + variables: { + projectId, + url: mapServerInfo.url, + attribution: + contentOrFalse(mapServerInfo.copyrightText) || + contentOrFalse(mapServerInfo.documentInfo?.Author) || + null, + bounds: latLngBounds + ? [ + latLngBounds[0][0], + latLngBounds[0][1], + latLngBounds[1][0], + latLngBounds[1][1], + ] + : null, + enableHighDPI: settings.enableHighDpi, + supportsDynamicLayers: mapServerInfo.supportsDynamicLayers, + }, + }); + imageSourceId = sourceResponse.data!.createDataSource!.dataSource!.id; + } catch (e) { + throw new Error(`Problem saving source. ${e.message}`); + } + } + + const layers = dataLayers.filter( + (l) => settings.excludedSublayers.indexOf(l.generatedId) === -1 + ); + let folderStableId: string | undefined; + // If vector import, create containing folder first + if (layers.length > 1) { + const folderName = + mapServerInfo.documentInfo?.Title || + mapServerInfo.documentInfo?.Subject || + mapServerInfo.serviceDescription || + "New Import"; + setState((prev) => { + return { + ...prev, + progress: 0, + statusMessage: `Creating folder "${folderName}"`, + }; + }); + folderStableId = nanoId(); + await createTableOfContentsItem({ + variables: { + title: folderName, + stableId: folderStableId, + projectId, + isFolder: true, + }, + }); + } + + for (const layer of layers) { + // check first if item has any children + if (state.abortController && state.abortController.signal.aborted) { + return; + } + progress += 1000; + setState((prev) => { + return { + ...prev, + progress: progress / totalProgressCredits, + statusMessage: `Saving layer ${layer.name}`, + }; + }); + try { + await saveItem(layer, mapServerInfo, folderStableId); + } catch (e) { + error = e; + break; + } + } + if (error!) { + setState((prev) => { + return { + ...prev, + error, + inProgress: false, + // parentStableIds: stableIds, + }; + }); + throw error; + } else { + setState((prev) => { + return { + ...prev, + inProgress: false, + parentStableIds: stableIds, + }; + }); + return state; + } + } catch (e) { + setState((prev) => { + return { + ...prev, + error: e, + inProgress: false, + parentStableIds: undefined, + }; + }); + throw e; + } + }; + return [importService, state]; +} export async function createImageBlobFromDataURI( width: number, @@ -1604,307 +1287,310 @@ export function replaceSpriteIds( return JSON.parse(stringified); } -// export async function getOrCreateSpritesFromImageSet( -// imageList: ImageList, -// projectId: number, -// createSprite: ( -// options?: -// | MutationFunctionOptions< -// GetOrCreateSpriteMutation, -// Exact<{ -// height: number; -// width: number; -// pixelRatio: number; -// projectId: number; -// smallestImage: any; -// }> -// > -// | undefined -// ) => Promise, -// addImageToSprite: ( -// options?: -// | MutationFunctionOptions< -// AddImageToSpriteMutation, -// Exact<{ -// spriteId: number; -// width: number; -// height: number; -// pixelRatio: number; -// image: any; -// }> -// > -// | undefined -// ) => Promise -// ): Promise<{ [oldId: string]: string }> { -// const replacementIds: { [oldId: string]: string } = {}; -// const imageSetJSON = imageList.toJSON(); -// if (imageSetJSON.length) { -// for (const imageSet of imageSetJSON) { -// const dpi1Image = imageSet.images.find((i) => i.pixelRatio === 1); -// if (!dpi1Image) { -// throw new Error("Sprite does not contain any images with dpi=1"); -// } -// const blob = await createImageBlobFromDataURI( -// dpi1Image.width, -// dpi1Image.height, -// dpi1Image.dataURI -// ); -// const result = await createSprite({ -// variables: { -// height: dpi1Image.height, -// width: dpi1Image.width, -// pixelRatio: dpi1Image.pixelRatio, -// projectId: projectId, -// smallestImage: blob, -// }, -// }); -// if (!result.data?.getOrCreateSprite) { -// throw new Error("Failed to create initial sprite"); -// } -// replacementIds[ -// imageSet.id.toString() -// ] = result.data.getOrCreateSprite.id.toString(); -// let sprite = result.data.getOrCreateSprite; -// await Promise.all( -// imageSet.images -// .filter((i) => i.pixelRatio !== 1) -// .map(async (image) => { -// if ( -// !sprite.spriteImages.find( -// (i: { pixelRatio: number }) => i.pixelRatio === image.pixelRatio -// ) -// ) { -// const blob = await createImageBlobFromDataURI( -// image.width, -// image.height, -// image.dataURI -// ); -// try { -// let r = await addImageToSprite({ -// variables: { -// spriteId: sprite.id, -// width: image.width, -// height: image.height, -// pixelRatio: image.pixelRatio, -// image: blob, -// }, -// }); -// if (!r.data?.addImageToSprite) { -// throw new Error("Failed to add image to sprite"); -// } -// sprite = r.data.addImageToSprite; -// } catch (e) { -// throw new Error("Problem creating sprite. " + e.message); -// } -// } -// }) -// ); -// } -// } -// return replacementIds; -// } +export async function getOrCreateSpritesFromImageSet( + imageList: ImageList, + projectId: number, + createSprite: ( + options?: + | MutationFunctionOptions< + GetOrCreateSpriteMutation, + Exact<{ + height: number; + width: number; + pixelRatio: number; + projectId: number; + smallestImage: any; + }> + > + | undefined + ) => Promise, + addImageToSprite: ( + options?: + | MutationFunctionOptions< + AddImageToSpriteMutation, + Exact<{ + spriteId: number; + width: number; + height: number; + pixelRatio: number; + image: any; + }> + > + | undefined + ) => Promise +): Promise<{ [oldId: string]: string }> { + const replacementIds: { [oldId: string]: string } = {}; + const imageSetJSON = imageList.toJSON(); + if (imageSetJSON.length) { + for (const imageSet of imageSetJSON) { + const dpi1Image = imageSet.images.find((i) => i.pixelRatio === 1); + if (!dpi1Image) { + throw new Error("Sprite does not contain any images with dpi=1"); + } + const blob = await createImageBlobFromDataURI( + dpi1Image.width, + dpi1Image.height, + dpi1Image.dataURI + ); + const result = await createSprite({ + variables: { + height: dpi1Image.height, + width: dpi1Image.width, + pixelRatio: dpi1Image.pixelRatio, + projectId: projectId, + smallestImage: blob, + }, + }); + if (!result.data?.getOrCreateSprite) { + throw new Error("Failed to create initial sprite"); + } + replacementIds[imageSet.id.toString()] = + result.data.getOrCreateSprite.id.toString(); + let sprite = result.data.getOrCreateSprite; + await Promise.all( + imageSet.images + .filter((i) => i.pixelRatio !== 1) + .map(async (image) => { + if ( + !sprite.spriteImages.find( + (i: { pixelRatio: number }) => i.pixelRatio === image.pixelRatio + ) + ) { + const blob = await createImageBlobFromDataURI( + image.width, + image.height, + image.dataURI + ); + try { + let r = await addImageToSprite({ + variables: { + spriteId: sprite.id, + width: image.width, + height: image.height, + pixelRatio: image.pixelRatio, + image: blob, + }, + }); + if (!r.data?.addImageToSprite) { + throw new Error("Failed to add image to sprite"); + } + sprite = r.data.addImageToSprite; + } catch (e) { + throw new Error("Problem creating sprite. " + e.message); + } + } + }) + ); + } + } + return replacementIds; +} -// export async function identifyLayers( -// position: [number, number], -// source: ClientDataSource, -// sublayers: string[], -// mapBounds: [number, number, number, number], -// width: number, -// height: number, -// dpi: number, -// abortController?: AbortController -// ): Promise< -// { sourceId: number; sublayer: string; attributes: { [key: string]: any } }[] -// > { -// if (source.type === DataSourceTypes.ArcgisDynamicMapserver) { -// const response = await fetch( -// `${source.url}/identify?f=json&tolerance=${4}&mapExtent=${mapBounds.join( -// "," -// )}&imageDisplay=${width},${height},${dpi}&geometryType=esriGeometryPoint&geometry={x:${ -// position[0] -// }, y: ${ -// position[1] -// }}&sr=4326&returnGeometry=false&layers=all:${sublayers.join(",")}`, -// { -// signal: abortController?.signal, -// } -// ); -// const data: any = await response.json(); -// if (data.results) { -// return data.results.map((record: any) => { -// return { -// sourceId: source.id, -// sublayer: record.layerId, -// attributes: record.attributes, -// }; -// }); -// } else { -// console.warn("Unrecognized response from identify"); -// return []; -// } -// } else { -// throw new Error("Not supported"); -// } -// } +export async function identifyLayers( + position: [number, number], + source: DataSourceDetailsFragment, + sublayers: string[], + mapBounds: [number, number, number, number], + width: number, + height: number, + dpi: number, + abortController?: AbortController +): Promise< + { sourceId: number; sublayer: string; attributes: { [key: string]: any } }[] +> { + if (source.type === DataSourceTypes.ArcgisDynamicMapserver) { + const response = await fetch( + `${source.url}/identify?f=json&tolerance=${4}&mapExtent=${mapBounds.join( + "," + )}&imageDisplay=${width},${height},${dpi}&geometryType=esriGeometryPoint&geometry={x:${ + position[0] + }, y: ${ + position[1] + }}&sr=4326&returnGeometry=false&layers=all:${sublayers.join(",")}`, + { + signal: abortController?.signal, + } + ); + const data: any = await response.json(); + if (data.results) { + return data.results.map((record: any) => { + return { + sourceId: source.id, + sublayer: record.layerId, + attributes: record.attributes, + }; + }); + } else { + console.warn("Unrecognized response from identify"); + return []; + } + } else { + throw new Error("Not supported"); + } +} -// export function generateMetadataForLayer( -// mapServerInfo: MapServerCatalogInfo, -// layer: LayerInfo, -// hostedOnSeaSketch: boolean -// ) { -// const attribution = -// contentOrFalse(layer.copyrightText) || -// contentOrFalse(mapServerInfo.copyrightText) || -// contentOrFalse(mapServerInfo.documentInfo?.Author); -// const description = pickDescription(mapServerInfo, layer); -// let keywords = -// mapServerInfo.documentInfo?.Keywords && -// mapServerInfo.documentInfo?.Keywords.length -// ? mapServerInfo.documentInfo?.Keywords.split(",") -// : []; -// return { -// type: "doc", -// content: [ -// { -// type: "heading", -// attrs: { level: 1 }, -// content: [{ type: "text", text: layer.name }], -// }, -// ...(description -// ? [ -// { -// type: "paragraph", -// content: [ -// { -// type: "text", -// text: description, -// }, -// ], -// }, -// ] -// : []), -// ...(attribution -// ? [ -// { type: "paragraph" }, -// { -// type: "heading", -// attrs: { level: 3 }, -// content: [{ type: "text", text: "Attribution" }], -// }, -// { -// type: "paragraph", -// content: [ -// { -// type: "text", -// text: attribution, -// }, -// ], -// }, -// ] -// : []), -// ...(keywords && keywords.length -// ? [ -// { type: "paragraph" }, -// { -// type: "heading", -// attrs: { level: 3 }, -// content: [ -// { -// type: "text", -// text: "Keywords", -// }, -// ], -// }, -// { -// type: "bullet_list", -// marks: [], -// attrs: {}, -// content: keywords.map((word) => ({ -// type: "list_item", -// content: [ -// { -// type: "paragraph", -// content: [{ type: "text", text: word }], -// }, -// ], -// })), -// }, -// ] -// : []), -// { type: "paragraph" }, -// { -// type: "heading", -// attrs: { level: 3 }, -// content: [ -// { -// type: "text", -// text: hostedOnSeaSketch ? "Original Source" : "Source Server", -// }, -// ], -// }, -// { -// type: "paragraph", -// content: [ -// { -// type: "text", -// marks: [ -// { -// type: "link", -// attrs: { -// href: layer.url, -// title: "ArcGIS Server", -// }, -// }, -// ], -// text: layer.url, -// }, -// ], -// }, -// ], -// }; -// } +export function generateMetadataForLayer( + mapServerInfo: MapServerCatalogInfo, + layer: LayerInfo, + hostedOnSeaSketch: boolean +) { + const attribution = + contentOrFalse(layer.copyrightText) || + contentOrFalse(mapServerInfo.copyrightText) || + contentOrFalse(mapServerInfo.documentInfo?.Author); + const description = pickDescription(mapServerInfo, layer); + let keywords = + mapServerInfo.documentInfo?.Keywords && + mapServerInfo.documentInfo?.Keywords.length + ? mapServerInfo.documentInfo?.Keywords.split(",") + : []; + return { + type: "doc", + content: [ + { + type: "heading", + attrs: { level: 1 }, + content: [{ type: "text", text: layer.name }], + }, + ...(description + ? [ + { + type: "paragraph", + content: [ + { + type: "text", + text: description, + }, + ], + }, + ] + : []), + ...(attribution + ? [ + { type: "paragraph" }, + { + type: "heading", + attrs: { level: 3 }, + content: [{ type: "text", text: "Attribution" }], + }, + { + type: "paragraph", + content: [ + { + type: "text", + text: attribution, + }, + ], + }, + ] + : []), + ...(keywords && keywords.length + ? [ + { type: "paragraph" }, + { + type: "heading", + attrs: { level: 3 }, + content: [ + { + type: "text", + text: "Keywords", + }, + ], + }, + { + type: "bullet_list", + marks: [], + attrs: {}, + content: keywords.map((word) => ({ + type: "list_item", + content: [ + { + type: "paragraph", + content: [{ type: "text", text: word }], + }, + ], + })), + }, + ] + : []), + { type: "paragraph" }, + { + type: "heading", + attrs: { level: 3 }, + content: [ + { + type: "text", + text: hostedOnSeaSketch ? "Original Source" : "Source Server", + }, + ], + }, + { + type: "paragraph", + content: [ + { + type: "text", + marks: [ + { + type: "link", + attrs: { + href: layer.url, + title: "ArcGIS Server", + }, + }, + ], + text: layer.url, + }, + ], + }, + ], + }; +} -// function pickDescription(info: MapServerCatalogInfo, layer?: LayerInfo) { -// return ( -// contentOrFalse(layer?.description) || -// contentOrFalse(info.description) || -// contentOrFalse(info.documentInfo?.Subject) || -// contentOrFalse(info.documentInfo?.Comments) -// ); -// } +function pickDescription(info: MapServerCatalogInfo, layer?: LayerInfo) { + return ( + contentOrFalse(layer?.description) || + contentOrFalse(info.description) || + contentOrFalse(info.documentInfo?.Subject) || + contentOrFalse(info.documentInfo?.Comments) + ); +} -// function contentOrFalse(str?: string) { -// if (str && str.length > 0) { -// return str; -// } else { -// return false; -// } -// } +function contentOrFalse(str?: string) { + if (str && str.length > 0) { + return str; + } else { + return false; + } +} -// const dynamicArcGISStyles: { -// [sourceId: string]: Promise<{ -// imageList: ImageList; -// layers: mapboxgl.Layer[]; -// }>; -// } = {}; -// /** -// * Returns a promise that resolves to gl style information from mapbox-gl-esri-sources. -// * Will first reference an internal cache unless skipCache is true. -// * @param url URL to a feature layer. Should end in MapServer/\d+ -// * @param sourceId Valid gl styles must reference a data source. Provide the ID of the geojson source that will be used -// */ -// export async function getDynamicArcGISStyle( -// url: string, -// sourceId: string, -// skipCache = false -// ) { -// const layers: Layer[] = []; -// // @ts-ignore -// if (dynamicArcGISStyles[sourceId] && !skipCache) { -// // already working -// return dynamicArcGISStyles[sourceId]; -// } else { -// dynamicArcGISStyles[sourceId] = styleForFeatureLayer(url, sourceId); -// } -// return dynamicArcGISStyles[sourceId]; -// } +const dynamicArcGISStyles: { + [sourceId: string]: Promise<{ + imageList: ImageList; + layers: mapboxgl.Layer[]; + }>; +} = {}; +/** + * Returns a promise that resolves to gl style information from mapbox-gl-esri-sources. + * Will first reference an internal cache unless skipCache is true. + * @param url URL to a feature layer. Should end in MapServer/\d+ + * @param sourceId Valid gl styles must reference a data source. Provide the ID of the geojson source that will be used + */ +export async function getDynamicArcGISStyle( + url: string, + sourceId: string, + skipCache = false +) { + const layers: Layer[] = []; + // @ts-ignore + if (dynamicArcGISStyles[sourceId] && !skipCache) { + // already working + return dynamicArcGISStyles[sourceId]; + } else { + dynamicArcGISStyles[sourceId] = styleForFeatureLayer( + url.split(/\/\d+$/)[0], + parseInt(url.split(/\/\d+$/)[1]), + sourceId + ); + } + return dynamicArcGISStyles[sourceId]; +} diff --git a/packages/client/src/admin/data/arcgis/mapbox-gl-esri-sources.d.ts b/packages/client/src/admin/data/arcgis/mapbox-gl-esri-sources.d.ts new file mode 100644 index 000000000..7260968df --- /dev/null +++ b/packages/client/src/admin/data/arcgis/mapbox-gl-esri-sources.d.ts @@ -0,0 +1,53 @@ +declare module "mapbox-gl-esri-sources" { + import { Map, RasterSource } from "mapbox-gl"; + export interface EsriServiceOptions { + url: string; + fetchOptions?: RequestInit; + layers?: string[]; + format?: string; + transparent?: boolean; + layerDefs?: { [id: number]: string }; + from?: Date; + to?: Date; + getAttributionFromService?: boolean; + } + export interface TiledMapServiceEsriServiceOptions { + url: string; + fetchOptions?: RequestInit; + } + + export class DynamicMapService { + constructor( + id: string, + map: Map, + esriServiceOptions: EsriServiceOptions, + rasterSourceOptions?: Partial + ); + /* Returns a promise which when resolved returns the service metadata as a json object. */ + getMetadata(): Promise; + /* Returns a promise which when resolved returns the features as an esri-json object. By default the geometry is not returned. */ + identify( + latLon: { lng: number; lat: number }, + returnGeometry: boolean + ): Promise; + /* Redraws the layer with the new layer definitions. Corresponds to the option above on the Esri Service Options. */ + setLayerDefs(layerDefs: { [id: number]: string }): void; + /* Redraws the layer to show the passed array of layer ids. */ + setLayers(layers: string[]): void; + /* Redraws the layer with he passed time range. */ + setDates(from: Date, to: Date): void; + /** + * Sets the attribution on the map from the service metadata. This happens automatically if the getAttributionFromService option is true. + */ + setAttributionFromService(getAttributionFromService: boolean): void; + } + + export class TiledMapService { + constructor( + id: string, + map: Map, + esriServiceOptions: TiledMapServiceEsriServiceOptions, + rasterSourceOptions?: Partial + ); + } +} diff --git a/packages/client/src/components/ContextMenuDropdown.tsx b/packages/client/src/components/ContextMenuDropdown.tsx index 4089cac22..a14cab3fb 100644 --- a/packages/client/src/components/ContextMenuDropdown.tsx +++ b/packages/client/src/components/ContextMenuDropdown.tsx @@ -94,7 +94,11 @@ export default function ContextMenuDropdown({ onClick={onOptionClick(props)} className={classNames( "group", - "group-hover:bg-gray-100 group-hover:text-gray-900 text-gray-700", + `${ + props.disabled + ? "" + : "group-hover:bg-gray-100 group-hover:text-gray-900" + } text-gray-700`, "block px-4 py-2 text-sm w-full text-left", disabled ? "pointer-events-none opacity-50" : "" )} diff --git a/packages/client/src/components/Menubar.tsx b/packages/client/src/components/Menubar.tsx index f7b69c9f4..fbf61b907 100644 --- a/packages/client/src/components/Menubar.tsx +++ b/packages/client/src/components/Menubar.tsx @@ -30,6 +30,14 @@ export function MenubarRadioItem({ ); } +export function MenuBarLabel({ children }: { children?: ReactNode }) { + return ( + + {children} + + ); +} + export function MenuBarSeparator() { return ( void; disableBackdropClick?: boolean; footer?: FooterButtonProps[]; @@ -92,94 +93,109 @@ export default function Modal(props: ModalProps) { }} >
    - -
    + + +
    + )} + {!props.loading && ( + - {props.icon && props.icon === "delete" && ( -
    -
    - )} - {props.icon && props.icon === "alert" && ( -
    -
    - )}
    - {hasTitle && ( - - {props.title} - - {props.tabs && props.tabs.length > 0 && ( - <> - - - )} - + {props.icon && props.icon === "delete" && ( +
    +
    + )} + {props.icon && props.icon === "alert" && ( +
    +
    )} -
    - {props.children} -
    - {((props.footer && props.footer.length > 0) || - !props.zeroPadding) && ( + {hasTitle && ( + + {props.title} + + {props.tabs && props.tabs.length > 0 && ( + <> + + + )} + + )} +
    - {(props.footer || []).map((footerProps) => ( - - ))} + {props.children}
    - )} + {((props.footer && props.footer.length > 0) || + !props.zeroPadding) && ( +
    + {(props.footer || []).map((footerProps) => ( + + ))} +
    + )} +
    -
    - + + )} @@ -225,6 +241,7 @@ function Panel({ zeroPadding, initialFocus, dark, + className, }: { children?: ReactNode; autoWidth?: boolean; @@ -232,6 +249,7 @@ function Panel({ zeroPadding: boolean; initialFocus?: React.RefObject; dark?: boolean; + className?: string; }) { const myRef = useRef(null); return ( @@ -267,7 +285,7 @@ function Panel({ zeroPadding ? "" : "px-4" } pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:max-w-xl lg:max-w-2xl ${ autoWidth ? "w-auto" : "w-full" - } sm:p-0`} + } sm:p-0 ${className}`} onAnimationComplete={() => { if (initialFocus && initialFocus.current) { initialFocus.current.focus(); diff --git a/packages/client/src/components/useDialog.tsx b/packages/client/src/components/useDialog.tsx index eb686b9bb..22e6cb31b 100644 --- a/packages/client/src/components/useDialog.tsx +++ b/packages/client/src/components/useDialog.tsx @@ -1,13 +1,59 @@ -import { useCallback, useEffect, useMemo, useState } from "react"; +import { + Dispatch, + SetStateAction, + useCallback, + useEffect, + useMemo, + useState, +} from "react"; import { createContext, ReactNode, useContext } from "react"; import { useTranslation } from "react-i18next"; import Modal from "./Modal"; import TextInput from "./TextInput"; +import Spinner from "./Spinner"; export default function useDialog() { const context = useContext(UseDialogContext); return useMemo( () => ({ + makeChoice: (options: { + title: string; + choices: ReactNode[]; + }): Promise => { + return new Promise((resolve, reject) => { + context.setState({ + type: "choice", + message: options.title, + open: true, + choices: options.choices, + onSubmit: (value) => resolve(parseInt(value)), + onCancel: () => resolve(false), + submitting: false, + }); + }); + }, + loadingMessage: (message: string) => { + context.setState({ + type: "loading", + open: true, + message: null, + submitting: false, + description: message, + }); + return { + hideLoadingMessage: () => { + context.setState(ResetState); + }, + updateLoadingMessage: (message: string) => { + context.setState((prev) => { + return { + ...prev, + description: message, + }; + }); + }, + }; + }, prompt: (options: { message: string; defaultValue?: string; @@ -90,7 +136,7 @@ export default function useDialog() { } type DialogContextState = { - type: "prompt" | "alert" | "confirm"; + type: "prompt" | "alert" | "confirm" | "loading" | "choice"; open: boolean; message: string | ReactNode; description?: string; @@ -102,6 +148,7 @@ type DialogContextState = { primaryButtonVariant?: "primary" | "danger"; primaryButtonText?: string; disableBackdropClick?: boolean; + choices?: ReactNode[]; }; const ResetState: DialogContextState = { @@ -113,7 +160,7 @@ const ResetState: DialogContextState = { const UseDialogContext = createContext<{ state: DialogContextState; - setState: (state: DialogContextState) => void; + setState: Dispatch>; }>({ state: ResetState, setState: () => {}, @@ -160,6 +207,7 @@ export function DialogProvider({ children }: { children?: ReactNode }) { {state.open && ( { @@ -170,7 +218,9 @@ export function DialogProvider({ children }: { children?: ReactNode }) { disableBackdropClick={state.disableBackdropClick} autoWidth footer={ - state.type === "alert" + state.type === "loading" + ? [] + : state.type === "alert" ? [ { disabled: false, @@ -202,6 +252,19 @@ export function DialogProvider({ children }: { children?: ReactNode }) { }, }, ] + : state.type === "choice" + ? [ + { + disabled: state.submitting, + label: t("Cancel"), + onClick: () => { + if (state.onCancel) { + state.onCancel(); + } + reset(); + }, + }, + ] : [ { disabled: state.submitting, @@ -225,9 +288,35 @@ export function DialogProvider({ children }: { children?: ReactNode }) { } >
    - {state.description && ( + {state.type === "loading" && ( +
    + +
    {state.description}
    +
    + )} + {state.type !== "loading" && state.description && (

    {state.description}

    )} + {state.choices && state.type === "choice" && ( +
    + {state.choices.map((node, i) => { + return ( +
    { + if (state.onSubmit) { + state.onSubmit(i.toString()); + } + reset(); + }} + > + {node} +
    + ); + })} +
    + )} {state.type === "prompt" && ( { - // if (selectedBasemap !== b.id) { mapContext.manager?.setSelectedBasemap(b.id.toString()); - // setSelectedBasemap(b.id); - // managerContext.manager?.changeBasemap(b.url); - // } }} /> ))} diff --git a/packages/client/src/dataLayers/LayerInteractivityManager.ts b/packages/client/src/dataLayers/LayerInteractivityManager.ts index a8f315daa..69e6b13ba 100644 --- a/packages/client/src/dataLayers/LayerInteractivityManager.ts +++ b/packages/client/src/dataLayers/LayerInteractivityManager.ts @@ -25,6 +25,12 @@ import // getDynamicArcGISStyle, // identifyLayers, "../admin/data/arcgis/arcgis"; import { EventEmitter } from "eventemitter3"; +import { CustomGLSource } from "@seasketch/mapbox-gl-esri-sources"; +import { identifyLayers } from "../admin/data/arcgis/arcgis"; + +const PopupNumberFormatter = Intl.NumberFormat(undefined, { + maximumFractionDigits: 2, +}); /** * LayerInteractivityManager works in tandem with the MapContextManager to react @@ -53,6 +59,8 @@ export default class LayerInteractivityManager extends EventEmitter { private basemap: BasemapDetailsFragment | undefined; private sketchLayerIds: string[] = []; private focusedSketchId?: number; + private customSources: { [sourceId: string]: CustomGLSource } = {}; + private tocItemLabels: { [stableId: string]: string } = {}; /** * @@ -82,6 +90,10 @@ export default class LayerInteractivityManager extends EventEmitter { this.registerEventListeners(map); } + setCustomSources(sources: { [sourceId: string]: CustomGLSource }) { + this.customSources = sources; + } + setSketchLayerIds(ids: string[]) { this.sketchLayerIds = ids; this.map.off("mousemove", this.debouncedMouseMoveListener); @@ -105,8 +117,10 @@ export default class LayerInteractivityManager extends EventEmitter { async setVisibleLayers( dataLayers: DataLayerDetailsFragment[], dataSources: { [dataSourceId: string]: DataSourceDetailsFragment }, - basemap: BasemapDetailsFragment + basemap: BasemapDetailsFragment, + tocItemLabels: { [stableId: string]: string } ) { + this.tocItemLabels = tocItemLabels; const newActiveLayers: { [layerId: string]: DataLayerDetailsFragment } = {}; const newActiveImageSources: { [sourceId: string]: DataSourceDetailsFragment; @@ -129,26 +143,25 @@ export default class LayerInteractivityManager extends EventEmitter { let GLStyles: Layer[]; if (layer.mapboxGlStyles && Array.isArray(layer.mapboxGlStyles)) { GLStyles = layer.mapboxGlStyles; - } else { - if (source.type === DataSourceTypes.ArcgisVector) { - throw new Error("Not Implemented"); - // const { layers } = await getDynamicArcGISStyle( - // source.url!, - // source.id.toString() - // ); - // GLStyles = layers; - } else { - throw new Error( - /* eslint-disable-next-line */ - `Could not find mapbox layer ids for client layer id=${layer.id}` - ); + newInteractiveVectorLayerIds = [ + ...newInteractiveVectorLayerIds, + ...GLStyles.map((s, i) => idForLayer(layer, i)), + ]; + newActiveLayers[layer.id] = layer; + } else if (source.type === DataSourceTypes.ArcgisVector) { + const customSource = this.customSources[source.id]; + if (customSource) { + const { layers } = await customSource.getGLStyleLayers(); + newInteractiveVectorLayerIds = [ + ...newInteractiveVectorLayerIds, + ...layers.map((l) => l.id), + ]; + for (const lyr of layers) { + newActiveLayers[lyr.id] = layer; + } + // newActiveLayers[layer.id] = layer; } } - newInteractiveVectorLayerIds = [ - ...newInteractiveVectorLayerIds, - ...GLStyles.map((s, i) => idForLayer(layer, i)), - ]; - newActiveLayers[layer.id] = layer; } } } @@ -334,7 +347,7 @@ export default class LayerInteractivityManager extends EventEmitter { interactivitySetting && interactivitySetting.type === InteractivityType.Popup ) { - new Popup({ closeOnClick: true, closeButton: false }) + new Popup({ closeOnClick: true, closeButton: true }) .setLngLat([e.lngLat.lng, e.lngLat.lat]) .setHTML( Mustache.render(interactivitySetting.longTemplate || "", { @@ -344,6 +357,53 @@ export default class LayerInteractivityManager extends EventEmitter { ) .addTo(this.map!); vectorPopupOpened = true; + } else if ( + interactivitySetting && + interactivitySetting.type === InteractivityType.AllPropertiesPopup + ) { + const lyr = this.layers[top.layer.id]; + // @ts-ignore + const layerLabel = (this.tocItemLabels || {})[lyr?.tocId]; + new Popup({ closeOnClick: true, closeButton: true }) + .setLngLat([e.lngLat.lng, e.lngLat.lat]) + .setHTML( + Mustache.render( + ` +
    + ${ + layerLabel + ? `

    ${layerLabel}

    ` + : "" + } +
    + ${Object.keys(top.properties || {}) + .map((key) => { + const value = (top.properties || {})[key]; + const isNumber = typeof value === "number"; + const strValue = isNumber + ? PopupNumberFormatter.format(value) + : value.toString(); + return ` +
    +
    ${key}
    +
    ${strValue} +
    +
    `; + }) + .join("\n")} +
    +
    + `, + { + ...mustacheHelpers, + properties: top.properties, + } + ) + ) + .addTo(this.map!); + vectorPopupOpened = true; } } if (!vectorPopupOpened) { @@ -353,91 +413,154 @@ export default class LayerInteractivityManager extends EventEmitter { ); interactiveImageLayers.sort((a, b) => a.zIndex - b.zIndex); if (interactiveImageLayers.length) { - throw new Error("Not implemented"); - // this.openImageServicePopups( - // [e.lngLat.lng, e.lngLat.lat], - // interactiveImageLayers - // ); + // throw new Error("Not implemented"); + this.openImageServicePopups( + [e.lngLat.lng, e.lngLat.lat], + interactiveImageLayers + ); } } }; - // private async openImageServicePopups( - // position: [number, number], - // layers: DataLayerDetailsFragment[] - // ) { - // if (this.popupAbortController) { - // this.popupAbortController.abort(); - // delete this.popupAbortController; - // } - // this.popupAbortController = new AbortController(); - // const requests: { sublayers: string[]; source: DataSourceDetailsFragment }[] = []; - // for (const layer of layers) { - // let existingRequest = requests.find( - // (r) => r.source.id === layer.dataSourceId - // ); - // if (!existingRequest) { - // const source = this.imageSources[layer.dataSourceId]; - // if (!source) { - // /* eslint-disable-next-line */ - // throw new Error(`Could not find source id=${layer.dataSourceId}`); - // } - // existingRequest = { - // sublayers: [], - // source: source, - // }; - // requests.push(existingRequest); - // } - // existingRequest.sublayers.push(layer.sublayer!); - // } - // const bounds = this.map!.getBounds(); - // const extent = [ - // bounds.getWest(), - // bounds.getSouth(), - // bounds.getEast(), - // bounds.getNorth(), - // ] as [number, number, number, number]; - // const width = this.map!.getCanvas().width; - // const height = this.map!.getCanvas().height; - // const dpi = window.devicePixelRatio * 96; - // this.map!.getCanvas().style.cursor = "progress"; - // const data = await Promise.all( - // requests.map((request) => { - // return identifyLayers( - // position, - // request.source, - // request.sublayers, - // extent, - // width, - // height, - // dpi, - // this.popupAbortController - // ); - // }) - // ); - // this.map!.getCanvas().style.cursor = ""; - // if (!this.popupAbortController.signal.aborted) { - // for (const sublayerData of data) { - // if (sublayerData.length) { - // const interactivitySetting = layers.find( - // (l) => - // l.sublayer?.toString() === sublayerData[0].sublayer.toString() && - // l.dataSourceId === sublayerData[0].sourceId - // )?.interactivitySettings; - // new Popup({ closeOnClick: true, closeButton: false }) - // .setLngLat(position) - // .setHTML( - // Mustache.render(interactivitySetting!.longTemplate || "", { - // ...mustacheHelpers, - // ...sublayerData[0].attributes, - // }) - // ) - // .addTo(this.map!); - // break; - // } - // } - // } - // } + // Note, this will only work with ArcGIS Server + // TODO: refactor this at some point to support CustomGLSources + // more generally + private async openImageServicePopups( + position: [number, number], + layers: DataLayerDetailsFragment[] + ) { + if (this.popupAbortController) { + this.popupAbortController.abort(); + delete this.popupAbortController; + } + this.popupAbortController = new AbortController(); + const requests: { + sublayers: string[]; + source: DataSourceDetailsFragment; + }[] = []; + for (const layer of layers) { + let existingRequest = requests.find( + (r) => r.source.id === layer.dataSourceId + ); + if (!existingRequest) { + const source = this.imageSources[layer.dataSourceId]; + if (!source) { + /* eslint-disable-next-line */ + throw new Error(`Could not find source id=${layer.dataSourceId}`); + } + existingRequest = { + sublayers: [], + source: source, + }; + requests.push(existingRequest); + } + existingRequest.sublayers.push(layer.sublayer!); + } + const bounds = this.map!.getBounds(); + const extent = [ + bounds.getWest(), + bounds.getSouth(), + bounds.getEast(), + bounds.getNorth(), + ] as [number, number, number, number]; + const width = this.map!.getCanvas().width; + const height = this.map!.getCanvas().height; + const dpi = window.devicePixelRatio * 96; + this.map!.getCanvas().style.cursor = "progress"; + const data = await Promise.all( + requests.map((request) => { + return identifyLayers( + position, + request.source, + request.sublayers, + extent, + width, + height, + dpi, + this.popupAbortController + ); + }) + ); + this.map!.getCanvas().style.cursor = ""; + if (!this.popupAbortController.signal.aborted) { + for (const sublayerData of data) { + if (sublayerData.length) { + const interactivitySetting = layers.find( + (l) => + l.sublayer?.toString() === sublayerData[0].sublayer.toString() && + l.dataSourceId === sublayerData[0].sourceId + )?.interactivitySettings; + if ( + interactivitySetting?.type === InteractivityType.AllPropertiesPopup + ) { + const data = sublayerData[0]; + let layerLabel: undefined | string; + if (data) { + const lyr = layers.find( + (l) => + l.sublayer?.toString() === data.sublayer.toString() && + l.dataSourceId === data.sourceId + ); + // @ts-ignore + layerLabel = (this.tocItemLabels || {})[lyr?.tocId]; + } + + const properties = sublayerData[0]?.attributes || {}; + new Popup({ closeOnClick: true, closeButton: true }) + .setLngLat(position) + .setHTML( + Mustache.render( + ` + ${ + layerLabel + ? `

    ${layerLabel}

    ` + : "" + } +
    +
    + ${Object.keys(properties) + .map((key) => { + const value = properties[key]; + const isNumber = typeof value === "number"; + const strValue = isNumber + ? PopupNumberFormatter.format(value) + : value.toString(); + return ` +
    +
    ${key}
    +
    ${strValue} +
    +
    `; + }) + .join("\n")} +
    +
    + `, + { + ...mustacheHelpers, + properties, + } + ) + ) + .addTo(this.map!); + } else { + new Popup({ closeOnClick: true, closeButton: true }) + .setLngLat(position) + .setHTML( + Mustache.render(interactivitySetting!.longTemplate || "", { + ...mustacheHelpers, + ...sublayerData[0].attributes, + }) + ) + .addTo(this.map!); + } + break; + } + } + } + } private debouncedMouseMoveListener = (e: MapMouseEvent, backoff = 4) => { if (this.paused) { @@ -524,6 +647,7 @@ export default class LayerInteractivityManager extends EventEmitter { }; break; case InteractivityType.Popup: + case InteractivityType.AllPropertiesPopup: cursor = "pointer"; break; case InteractivityType.FixedBlock: @@ -547,7 +671,8 @@ export default class LayerInteractivityManager extends EventEmitter { this.previousInteractionTarget === currentInteractionTarget && (interactivitySetting.type === InteractivityType.Banner || interactivitySetting.type === InteractivityType.FixedBlock || - interactivitySetting.type === InteractivityType.Popup) + interactivitySetting.type === InteractivityType.Popup || + interactivitySetting.type === InteractivityType.AllPropertiesPopup) ) { // Don't waste cycles on a state update } else { @@ -576,6 +701,12 @@ export default class LayerInteractivityManager extends EventEmitter { return; } return dataLayer.interactivitySettings; + } else if (this.layers[feature.layer.id]) { + const dataLayer = this.layers[feature.layer.id]; + if (!dataLayer) { + return; + } + return dataLayer.interactivitySettings; } else { if ( this.basemap && diff --git a/packages/client/src/dataLayers/Legend.tsx b/packages/client/src/dataLayers/Legend.tsx new file mode 100644 index 000000000..d9b08c4ff --- /dev/null +++ b/packages/client/src/dataLayers/Legend.tsx @@ -0,0 +1,386 @@ +import { + DynamicRenderingSupportOptions, + LegendItem as LegendSymbolItem, +} from "@seasketch/mapbox-gl-esri-sources"; +import { GLLegendPanel, LegendForGLLayers } from "./legends/LegendDataModel"; +import * as Accordion from "@radix-ui/react-accordion"; +import { + CaretDownIcon, + EyeClosedIcon, + EyeOpenIcon, +} from "@radix-ui/react-icons"; +import { useTranslation } from "react-i18next"; +import Spinner from "../components/Spinner"; +import SimpleSymbol from "./legends/SimpleSymbol"; +import { Map } from "mapbox-gl"; +import LegendBubblePanel from "./legends/LegendBubblePanel"; +import LegendGradientPanel from "./legends/LegendGradientPanel"; +import LegendHeatmapPanel from "./legends/LegendHeatmapPanel"; +import LegendListPanel from "./legends/LegendListPanel"; +import LegendMarkerSizePanel from "./legends/LegendMarkerSizePanel"; +import LegendStepPanel from "./legends/LegendStepPanel"; +import LegendSimpleSymbolPanel from "./legends/LegendSimpleSymbolPanel"; +import { useLocalForage } from "../useLocalForage"; +require("../admin/data/arcgis/Accordion.css"); + +interface SingleImageLegendItem { + type: "SingleImageLegendItem"; + label: string; + /** TableOfContentsItem ids */ + ids: string[]; + /** Image URL */ + url: string; + supportsDynamicRendering: DynamicRenderingSupportOptions; + id: string; + zOrder?: number; +} + +interface CustomGLSourceSymbolLegend { + label: string; + type: "CustomGLSourceSymbolLegend"; + supportsDynamicRendering: DynamicRenderingSupportOptions; + symbols: LegendSymbolItem[]; + id: string; + zOrder?: number; +} + +interface GLStyleLegendItem { + label: string; + type: "GLStyleLegendItem"; + /** Table of contents item id */ + id: string; + legend?: LegendForGLLayers; + zOrder?: number; +} + +export type LegendItem = + | GLStyleLegendItem + | CustomGLSourceSymbolLegend + | SingleImageLegendItem; + +const PANEL_WIDTH = 180; + +export default function Legend({ + className, + items, + loading, + hiddenItems, + onHiddenItemsChange, + map, + maxHeight, + backdropBlur: blur, + persistedStateKey, +}: { + backdropBlur?: boolean; + items: LegendItem[]; + zOrder: { [id: string]: number }; + opacity: { [id: string]: number }; + hiddenItems: string[]; + onZOrderChange?: (id: string, zOrder: number) => void; + onOpacityChange?: (id: string, opacity: number) => void; + onHiddenItemsChange?: (id: string, hidden: boolean) => void; + className?: string; + loading?: boolean; + map?: Map; + maxHeight?: number; + persistedStateKey?: string; +}) { + const { t } = useTranslation("homepage"); + maxHeight = maxHeight || undefined; + const [hidden, setHidden] = useLocalForage( + persistedStateKey || "legend", + true + ); + + return ( + + ); +} + +function PanelFactory({ panel, map }: { panel: GLLegendPanel; map?: Map }) { + return ( +
    + {(() => { + switch (panel.type) { + case "GLLegendHeatmapPanel": + return ; + case "GLLegendGradientPanel": + return ; + case "GLLegendBubblePanel": + return ; + case "GLLegendListPanel": + return ; + case "GLMarkerSizePanel": + return ; + case "GLLegendStepPanel": + return ; + case "GLLegendSimpleSymbolPanel": + return ; + case "GLLegendFilterPanel": + return ( +
    +

    {panel.label}

    +
      + {panel.children.map((child) => ( + + ))} +
    +
    + ); + default: + // eslint-disable-next-line i18next/no-literal-string + return
    not implemented
    ; + } + })()} +
    + ); +} + +function Toggle({ + visible, + onChange, + className, +}: { + visible: boolean; + onChange?: () => void; + className?: string; +}) { + return ( + + ); +} + +function LegendImage({ + item, + className, +}: { + item: LegendSymbolItem; + className?: string; +}) { + return ( + {item.label} 1 ? window.devicePixelRatio / 1.5 : 1) + } + height={ + (item.imageHeight || 20) / + (window.devicePixelRatio > 1 ? window.devicePixelRatio / 1.5 : 1) + } + /> + ); +} diff --git a/packages/client/src/dataLayers/MapContextManager.ts b/packages/client/src/dataLayers/MapContextManager.ts index 3123e7f3e..17f1c58ef 100644 --- a/packages/client/src/dataLayers/MapContextManager.ts +++ b/packages/client/src/dataLayers/MapContextManager.ts @@ -11,7 +11,6 @@ import mapboxgl, { AnySourceData, AnyLayer, Sources, - GeoJSONSource, } from "mapbox-gl"; import { createContext, @@ -22,7 +21,9 @@ import { } from "react"; import { BBox, Feature, Polygon } from "geojson"; import { + ArcgisFeatureLayerFetchStrategy, BasemapDetailsFragment, + BasemapType, DataLayerDetailsFragment, DataSourceDetailsFragment, DataSourceTypes, @@ -48,12 +49,21 @@ import LRU from "lru-cache"; import debounce from "lodash.debounce"; import { currentSidebarState } from "../projects/ProjectAppSidebar"; import { ApolloClient, NormalizedCacheObject } from "@apollo/client"; +import { compileLegendFromGLStyleLayers } from "./legends/compileLegend"; +import { LegendItem } from "./Legend"; import { EventEmitter } from "eventemitter3"; -import MeasureControl, { - MeasureControlState, - measureLayers, -} from "../MeasureControl"; +import { MeasureControlState } from "../MeasureControl"; import cloneDeep from "lodash.clonedeep"; +import { + ArcGISDynamicMapService, + ArcGISFeatureLayerSource, + ArcGISRESTServiceRequestManager, + ArcGISTiledMapService, + CustomGLSource, +} from "@seasketch/mapbox-gl-esri-sources"; +import { OrderedLayerSettings } from "@seasketch/mapbox-gl-esri-sources/dist/src/CustomGLSource"; +import { isArcGISDynamicMapService } from "@seasketch/mapbox-gl-esri-sources/dist/src/ArcGISDynamicMapService"; +import { isArcgisFeatureLayerSource } from "@seasketch/mapbox-gl-esri-sources/dist/src/ArcGISFeatureLayerSource"; export const MeasureEventTypes = { Started: "measure_started", @@ -80,6 +90,8 @@ const graphqlURL = new URL( process.env.REACT_APP_GRAPHQL_ENDPOINT || "http://localhost:3857/graphql" ); +const STALE_CUSTOM_SOURCE_SIZE = 3; + export const BASE_SERVER_ENDPOINT = `${graphqlURL.protocol}//${graphqlURL.host}`; const LocalSketchGeometryCache = new LRU< @@ -127,6 +139,7 @@ mapboxgl.accessToken = process.env.REACT_APP_MAPBOX_ACCESS_TOKEN!; export interface LayerState { visible: true; + opacity?: number; loading: boolean; error?: Error; } @@ -163,6 +176,8 @@ class MapContextManager extends EventEmitter { private selectedSketches?: number[]; private sketchTimestamps = new global.Map(); private hideEditableSketchId?: number; + private arcgisRequestManager: ArcGISRESTServiceRequestManager = + new ArcGISRESTServiceRequestManager(); // Used to track previous map state before the application of a map bookmark // so that the map state change may be undone. private previousMapState: @@ -182,6 +197,15 @@ class MapContextManager extends EventEmitter { * changed. */ private geoprocessingReferenceIds: { [referenceId: string]: string } = {}; + private customSources: { + [sourceId: number]: { + customSource: CustomGLSource; + visible: boolean; + lastUsedTimestamp: number; + listenersAdded: boolean; + sublayers?: OrderedLayerSettings; + }; + } = {}; constructor( initialState: MapContextInterface, @@ -203,6 +227,10 @@ class MapContextManager extends EventEmitter { this.visibleLayers = initialState.layerStatesByTocStaticId; } + getCustomGLSource(sourceId: number) { + return this.customSources[sourceId]?.customSource; + } + private setState = (action: SetStateAction) => { if (typeof action === "function") { this.internalState = action(this.internalState); @@ -298,7 +326,12 @@ class MapContextManager extends EventEmitter { /** * Call whenever the context will be replaced or no longer used */ - destroy() {} + destroy() { + for (const key in this.customSources) { + this.customSources[key].customSource.destroy(); + delete this.customSources[key]; + } + } /** * Create a Mapbox GL JS instance. Should be called by component. @@ -327,6 +360,10 @@ class MapContextManager extends EventEmitter { this.map.off("moveend", this.onMapMove); this.map.remove(); } + for (const key in this.customSources) { + this.customSources[key].customSource.destroy(); + delete this.customSources[key]; + } } if (!this.internalState.ready) { throw new Error( @@ -381,7 +418,8 @@ class MapContextManager extends EventEmitter { .filter((id) => this.visibleLayers[id]?.visible && this.layers[id]) .map((id) => this.layers[id]), this.clientDataSources, - this.getSelectedBasemap()! + this.getSelectedBasemap()!, + this.tocItemLabels ); if (this.internalState.showScale) { @@ -441,7 +479,7 @@ class MapContextManager extends EventEmitter { // eslint-disable-next-line i18next/no-literal-string headers: { authorization: `Bearer ${this.userAccessToken}` }, }; - } else { + } else if (!/^data:/.test(url)) { Url.searchParams.set("ssn-tr", "true"); url = Url.toString(); } @@ -753,33 +791,124 @@ class MapContextManager extends EventEmitter { }, backoff); } - private updateStyleInfinitLoopDetector = 0; + private updateStyleInfiniteLoopDetector = 0; async updateStyle() { if (this.map && this.internalState.ready) { - this.updateStyleInfinitLoopDetector = 0; - const { style, sprites } = await this.getComputedStyle(); + this.updateStyleInfiniteLoopDetector++; + this.updateStyleInfiniteLoopDetector = 0; + if (this.updateStyleInfiniteLoopDetector > 10) { + this.updateStyleInfiniteLoopDetector = 0; + throw new Error("Infinite loop"); + } + const { style, sprites } = await this.getComputedStyle(() => { + this.debouncedUpdateStyle(); + }); const styleHash = md5(JSON.stringify(style)); this.addSprites(sprites, this.map); - if (!this.mapIsLoaded) { - setTimeout(() => { - this.map!.setStyle(style); - this.setState((prev) => ({ ...prev, styleHash })); - }, 20); - } else { + const update = () => { + if (!this.map) { + return; + } + // add any custom sources event listeners this.map.setStyle(style); + for (const id in this.customSources) { + const { visible, listenersAdded, customSource, sublayers } = + this.customSources[id]; + // Make sure event listeners are added + if (visible && !listenersAdded) { + customSource.addEventListeners(this.map); + this.customSources[id].listenersAdded = true; + this.customSources[id].lastUsedTimestamp = new Date().getTime(); + } else if (!visible && listenersAdded) { + customSource.removeEventListeners(this.map); + this.customSources[id].listenersAdded = false; + } + } + this.pruneInactiveCustomSources(); + for (const id in this.customSources) { + const sourceConfig = this.clientDataSources[id]; + const { customSource, visible } = this.customSources[id]; + if (visible) { + if (isArcGISDynamicMapService(customSource)) { + customSource.updateUseDevicePixelRatio( + sourceConfig.useDevicePixelRatio || false + ); + customSource.updateQueryParameters( + sourceConfig.queryParameters || {} + ); + } else if (isArcgisFeatureLayerSource(customSource)) { + customSource.updateFetchStrategy( + sourceConfig.arcgisFetchStrategy === + ArcgisFeatureLayerFetchStrategy.Raw + ? "raw" + : sourceConfig.arcgisFetchStrategy === + ArcgisFeatureLayerFetchStrategy.Tiled + ? "tiled" + : "auto" + ); + } + } + } + const sources: { [id: string]: CustomGLSource } = {}; + for (const id in this.customSources) { + const { customSource, visible } = this.customSources[id]; + if (visible) { + sources[id] = customSource; + } + } + this.interactivityManager?.setCustomSources(sources); this.setState((prev) => ({ ...prev, styleHash })); + }; + if (!this.mapIsLoaded) { + setTimeout(update, 20); + } else { + update(); } } else { - this.updateStyleInfinitLoopDetector++; - if (this.updateStyleInfinitLoopDetector > 10) { - this.updateStyleInfinitLoopDetector = 0; + this.updateStyleInfiniteLoopDetector++; + if (this.updateStyleInfiniteLoopDetector > 10) { + this.updateStyleInfiniteLoopDetector = 0; } else { this.debouncedUpdateStyle(); } } } + private pruneInactiveCustomSources() { + // prune customSources, removing non-active sources that haven't been used + // in a while + let inactiveSources: { + id: string; + customSource: CustomGLSource; + timestamp: number; + }[] = []; + // collect inactive sources + for (const id in this.customSources) { + const { visible, lastUsedTimestamp, customSource } = + this.customSources[id]; + if (!visible) { + inactiveSources.push({ + id, + customSource, + timestamp: lastUsedTimestamp, + }); + } + } + + if (inactiveSources.length > STALE_CUSTOM_SOURCE_SIZE) { + // sort inactiveSources by lastUsedTimestamp, in descending order + inactiveSources.sort((a, b) => b.timestamp - a.timestamp); + inactiveSources = inactiveSources.slice(0, STALE_CUSTOM_SOURCE_SIZE); + for (const id in this.customSources) { + if (inactiveSources.find((s) => s.id === id)) { + this.customSources[id].customSource.destroy(); + delete this.customSources[id]; + } + } + } + } + /** * Set the visible overlays associated with TableOfContentsItems * @param ids List of ids. Can be the TableOfContents.stableId or geoprocessingReferenceId @@ -803,6 +932,7 @@ class MapContextManager extends EventEmitter { } this.debouncedUpdateLayerState(); this.debouncedUpdateStyle(); + this.updateLegends(); } /** @@ -815,6 +945,7 @@ class MapContextManager extends EventEmitter { ? this.geoprocessingReferenceIds[id] : id; delete this.visibleLayers[stableId]; + this.updateLegends(); } /** @@ -834,14 +965,17 @@ class MapContextManager extends EventEmitter { if (!state.visible) { state.visible = true; state.loading = true; + state.opacity = 1; } } } else { this.visibleLayers[stableId] = { loading: true, visible: true, + opacity: 1, }; } + this.updateLegends(); } /** @@ -933,7 +1067,7 @@ class MapContextManager extends EventEmitter { this.debouncedUpdateStyle(); } - async getComputedStyle(): Promise<{ + async getComputedStyle(unfinishedCustomSourceCallback?: () => void): Promise<{ style: Style; sprites: SpriteDetailsFragment[]; }> { @@ -947,23 +1081,64 @@ class MapContextManager extends EventEmitter { basemap?.url || "mapbox://styles/underbluewaters/cklb3vusx2dvs17pay6jp5q7e"; let baseStyle: Style; - try { - baseStyle = await fetchGlStyle(url); - if (this.internalState.basemapError) { + if (basemap?.type === BasemapType.RasterUrlTemplate) { + let url = basemap.url; + if ( + url.indexOf("services.arcgisonline.com") > -1 && + process.env.REACT_APP_ARCGIS_DEVELOPER_API_KEY + ) { + // eslint-disable-next-line i18next/no-literal-string + url += `?token=${process.env.REACT_APP_ARCGIS_DEVELOPER_API_KEY}`; + } + baseStyle = { + version: 8, + // TODO: choose a ip un-encumbered alternative for these + glyphs: "mapbox://fonts/mapbox/{fontstack}/{range}.pbf", + sprite: "mapbox://sprites/mapbox/streets-v11", + sources: { + raster: { + type: "raster", + tiles: [url], + tileSize: 256, + ...(basemap.maxzoom ? { maxzoom: basemap.maxzoom } : {}), + }, + }, + layers: [ + { + id: "bg", + type: "background", + paint: { + "background-color": "#efefef", + }, + }, + { + id: "raster-tiles", + type: "raster", + source: "raster", + minzoom: 0, + maxzoom: 22, + }, + ], + }; + } else { + try { + baseStyle = await fetchGlStyle(url); + if (this.internalState.basemapError) { + this.setState((prev) => ({ + ...prev, + basemapError: undefined, + })); + } + } catch (e) { this.setState((prev) => ({ ...prev, - basemapError: undefined, + basemapError: e, })); + console.warn(e); + baseStyle = await fetchGlStyle( + "mapbox://styles/underbluewaters/cklb5eho20sb817qhmzltsrpf" + ); } - } catch (e) { - this.setState((prev) => ({ - ...prev, - basemapError: e, - })); - console.warn(e); - baseStyle = await fetchGlStyle( - "mapbox://styles/underbluewaters/cklb5eho20sb817qhmzltsrpf" - ); } baseStyle = { ...baseStyle, @@ -1065,6 +1240,8 @@ class MapContextManager extends EventEmitter { let overLabels: any[] = baseStyle.layers.slice(labelsLayerIndex); let isUnderLabels = true; let i = this.layersByZIndex.length; + const insertedCustomSourceIds: number[] = []; + // reset sublayer settings before proceeding while (i--) { const layerId = this.layersByZIndex[i]; if (layerId === "LABELS") { @@ -1075,7 +1252,6 @@ class MapContextManager extends EventEmitter { // If layer or source are not set yet, they will be ignored if (layer) { const source = this.clientDataSources[layer.dataSourceId]; - let sourceWasAdded = false; if (source) { // Add the source if (!baseStyle.sources[source.id.toString()]) { @@ -1086,7 +1262,6 @@ class MapContextManager extends EventEmitter { attribution: source.attribution || "", tiles: source.tiles as string[], }; - sourceWasAdded = true; break; case DataSourceTypes.SeasketchMvt: baseStyle.sources[source.id.toString()] = { @@ -1094,7 +1269,6 @@ class MapContextManager extends EventEmitter { url: source.url! + ".json", attribution: source.attribution || "", }; - sourceWasAdded = true; break; case DataSourceTypes.SeasketchVector: case DataSourceTypes.Geojson: @@ -1103,7 +1277,6 @@ class MapContextManager extends EventEmitter { data: source.url!, attribution: source.attribution || "", }; - sourceWasAdded = true; break; case DataSourceTypes.SeasketchRaster: if (source.url) { @@ -1112,33 +1285,123 @@ class MapContextManager extends EventEmitter { url: source.url, attribution: source.attribution || "", }; - sourceWasAdded = true; } else { throw new Error("Not implemented"); } break; case DataSourceTypes.ArcgisVector: - throw new Error("not supported"); - // const request = this.arcgisVectorSourceCache.get(source); - // if (request.value) { - // baseStyle.sources[source.id.toString()] = { - // type: "geojson", - // data: request.value, - // attribution: source.attribution || "", - // }; - // sourceWasAdded = true; - // } else if (request.error) { - // // User will need to toggle the layer off - // // to clear the error and try again. - // } else { - // request.promise - // .then((data) => { - // this.debouncedUpdateStyle(); - // }) - // .catch((e) => { - // // do nothing, this will be handled elsewhere - // }); - // } + case DataSourceTypes.ArcgisRasterTiles: + case DataSourceTypes.ArcgisDynamicMapserver: + // Sublayers can be represented multiple times, so don't + // add the source if it's already there + if (!insertedCustomSourceIds.includes(source.id)) { + insertedCustomSourceIds.push(source.id); + if (!this.customSources[source.id]) { + switch (source.type) { + case DataSourceTypes.ArcgisVector: + const fetchStrategy = + source.arcgisFetchStrategy === + ArcgisFeatureLayerFetchStrategy.Raw + ? "raw" + : source.arcgisFetchStrategy === + ArcgisFeatureLayerFetchStrategy.Tiled + ? "tiled" + : "auto"; + this.customSources[source.id] = { + listenersAdded: false, + visible: true, + lastUsedTimestamp: new Date().getTime(), + customSource: new ArcGISFeatureLayerSource( + this.arcgisRequestManager, + { + url: source.url!, + fetchStrategy, + sourceId: source.id.toString(), + attributionOverride: + (source.attribution || "").trim().length > 0 + ? source.attribution! + : undefined, + } + ), + }; + break; + case DataSourceTypes.ArcgisRasterTiles: + this.customSources[source.id] = { + listenersAdded: false, + visible: true, + lastUsedTimestamp: new Date().getTime(), + customSource: new ArcGISTiledMapService( + this.arcgisRequestManager, + { + url: source.url!, + sourceId: source.id.toString(), + attributionOverride: + (source.attribution || "").trim().length > 0 + ? source.attribution! + : undefined, + maxZoom: source.maxzoom + ? source.maxzoom + : undefined, + developerApiKey: + process.env + .REACT_APP_ARCGIS_DEVELOPER_API_KEY, + } + ), + }; + break; + case DataSourceTypes.ArcgisDynamicMapserver: + this.customSources[source.id] = { + listenersAdded: false, + visible: true, + lastUsedTimestamp: new Date().getTime(), + customSource: new ArcGISDynamicMapService( + this.arcgisRequestManager, + { + url: source.url!, + sourceId: source.id.toString(), + supportHighDpiDisplays: + source.useDevicePixelRatio || false, + queryParameters: source.queryParameters || {}, + attributionOverride: + (source.attribution || "").trim().length > 0 + ? source.attribution! + : undefined, + } + ), + }; + break; + default: + throw new Error( + `CustomGLSource not yet supported for ${source.type}` + ); + } + // Initialize the source + const { customSource } = this.customSources[source.id]; + customSource.prepare().then(async () => { + if (unfinishedCustomSourceCallback) { + unfinishedCustomSourceCallback(); + } + }); + } else { + delete this.customSources[source.id].sublayers; + } + this.customSources[source.id].visible = true; + // add style if ready + const { customSource } = this.customSources[source.id]; + // Adding the source is skipped until later when sublayers are setup + if (customSource.ready) { + const styleData = await customSource.getGLStyleLayers(); + if (styleData.imageList && this.map) { + styleData.imageList.addToMap(this.map); + } + const layers = isUnderLabels ? underLabels : overLabels; + layers.push(...styleData.layers); + } else { + setTimeout(() => { + this.debouncedUpdateStyle(); + }, 50); + } + } break; default: break; @@ -1148,92 +1411,76 @@ class MapContextManager extends EventEmitter { if (layer.sprites?.length) { sprites = [...sprites, ...layer.sprites]; } - // Add the layer(s) - if (sourceWasAdded) { - if ( - (source.type === DataSourceTypes.SeasketchVector || - source.type === DataSourceTypes.Geojson || + + // Add the layer(s) for static sources (non-CustomGLSource's) + if ( + (source.type === DataSourceTypes.SeasketchVector || + source.type === DataSourceTypes.Geojson || + source.type === DataSourceTypes.Vector || + source.type === DataSourceTypes.SeasketchRaster || + // source.type === DataSourceTypes.ArcgisVector || + source.type === DataSourceTypes.SeasketchMvt) && + layer.mapboxGlStyles?.length + ) { + for (let i = 0; i < layer.mapboxGlStyles.length; i++) { + const layers = isUnderLabels ? underLabels : overLabels; + if ( + source.type === DataSourceTypes.SeasketchMvt || source.type === DataSourceTypes.Vector || - source.type === DataSourceTypes.SeasketchRaster || - // source.type === DataSourceTypes.ArcgisVector || - source.type === DataSourceTypes.SeasketchMvt) && - layer.mapboxGlStyles?.length - ) { - for (let i = 0; i < layer.mapboxGlStyles.length; i++) { - const layers = isUnderLabels ? underLabels : overLabels; - if ( - source.type === DataSourceTypes.SeasketchMvt || - source.type === DataSourceTypes.Vector || - source.type === DataSourceTypes.SeasketchRaster - ) { - layers.push({ - ...layer.mapboxGlStyles[i], - source: source.id.toString(), - id: idForLayer(layer, i), - "source-layer": layer.sourceLayer, - }); - } else { - layers.push({ - ...layer.mapboxGlStyles[i], - source: source.id.toString(), - id: idForLayer(layer, i), - }); - } + source.type === DataSourceTypes.SeasketchRaster + ) { + layers.push({ + ...layer.mapboxGlStyles[i], + source: source.id.toString(), + id: idForLayer(layer, i), + "source-layer": layer.sourceLayer, + }); + } else { + layers.push({ + ...layer.mapboxGlStyles[i], + source: source.id.toString(), + id: idForLayer(layer, i), + }); } } + } else if (isCustomSourceType(source.type) && layer.sublayer) { + // Add sublayer info if needed + if (!Array.isArray(this.customSources[source.id].sublayers)) { + this.customSources[source.id].sublayers = []; + } + const settings = this.visibleLayers[layerId]; + if (!settings) { + throw new Error("Visible layer settings missing"); + } + this.customSources[source.id].sublayers!.push({ + id: layer.sublayer, + opacity: + "opacity" in settings && settings.opacity !== undefined + ? settings.opacity + : 1, + }); } } } - } else { - // Handle image sources with multiple sublayers baked in - // TODO: Bring back arcgis server dynamic mapservice support with sublayers - // if (/seasketch\/[\w\d-]+\/image/.test(layerId)) { - // const sourceId = layerId.match(/seasketch\/([\w\d-]+)\/image/)![1]; - // if (sourceId) { - // const source = this.clientDataSources[sourceId]; - // if ( - // source && - // source.type === DataSourceTypes.ArcgisDynamicMapserver - // ) { - // let visibleSublayers: ClientDataLayer[] = []; - // for (const layerId in this.visibleLayers) { - // if ( - // this.visibleLayers[layerId].visible && - // this.layers[layerId]?.dataSourceId.toString() === sourceId - // ) { - // visibleSublayers.push(this.layers[layerId]); - // } - // } - // if (visibleSublayers.length) { - // visibleSublayers = visibleSublayers.sort( - // (a, b) => a.zIndex - b.zIndex - // ); - // const { url, tileSize } = urlTemplateForArcGISDynamicSource( - // source, - // visibleSublayers.map((l) => ({ sublayer: l.sublayer! })) - // ); - // baseStyle.sources[source.id.toString()] = { - // type: "raster", - // tiles: [url], - // tileSize: tileSize, - // attribution: source.attribution || "", - // // Doesn't like these... - // // maxzoom: source.maxzoom || undefined, - // // minzoom: source.minzoom || undefined, - // // bounds: source.bounds || undefined, - // }; - // const styleLayer = { - // id: layerId, - // type: "raster", - // source: source.id.toString(), - // } as Layer; - // (isUnderLabels ? underLabels : overLabels).push(styleLayer); - // // sourceWasAdded = true; - // // break; - // } - // } - // } - // } + } + } + } + + // mark customSources that are not visible as inactive, and remove their + // event listeners + for (const id in this.customSources) { + if (!insertedCustomSourceIds.includes(parseInt(id))) { + this.customSources[id].visible = false; + } else { + const { customSource, sublayers } = this.customSources[id]; + if (customSource.ready) { + // Update sublayers first so that sources that rely on a dynamically + // updated raster url can be initialized with proper data. + if (sublayers) { + customSource.updateLayers(sublayers); + } + const glSource = await customSource.getGLSource(this.map!); + baseStyle.sources[id] = glSource; } } } @@ -1554,11 +1801,14 @@ class MapContextManager extends EventEmitter { this.interactivityManager.setVisibleLayers( visibleLayers, this.clientDataSources, - this.getSelectedBasemap()! + this.getSelectedBasemap()!, + this.tocItemLabels ); } } + _updateSourceStatesLoopDetector = 0; + private updateSourceStates() { let anyChanges = false; let anyLoading = false; @@ -1582,6 +1832,13 @@ class MapContextManager extends EventEmitter { } for (const sourceId in sources) { let loading = !this.map!.isSourceLoaded(sourceId); + if (sourceId in this.customSources) { + const customSource = + this.customSources[parseInt(sourceId)].customSource; + if (customSource) { + loading = customSource.loading; + } + } if (loading) { anyLoading = true; } @@ -1620,9 +1877,15 @@ class MapContextManager extends EventEmitter { } // This is needed for geojson sources if (anyLoading) { - setTimeout(() => { - this.debouncedUpdateSourceStates(); - }, 100); + this._updateSourceStatesLoopDetector++; + setTimeout( + () => { + this.debouncedUpdateSourceStates(); + }, + this._updateSourceStatesLoopDetector > 100 ? 1000 : 100 + ); + } else { + this._updateSourceStatesLoopDetector = 0; } } @@ -1634,12 +1897,14 @@ class MapContextManager extends EventEmitter { highlightLayer(layerId: string) {} + private tocItemLabels: { [id: string]: string } = {}; + reset( sources: DataSourceDetailsFragment[], layers: DataLayerDetailsFragment[], tocItems: Pick< OverlayFragment, - "id" | "stableId" | "dataLayerId" | "geoprocessingReferenceId" + "id" | "stableId" | "dataLayerId" | "geoprocessingReferenceId" | "title" >[] ) { this.clientDataSources = {}; @@ -1652,6 +1917,7 @@ class MapContextManager extends EventEmitter { // this.layers[layer.id] = layer; } for (const item of tocItems) { + this.tocItemLabels[item.stableId] = item.title; if (item.dataLayerId) { const layer = layersById[item.dataLayerId]; if (layer) { @@ -1671,7 +1937,7 @@ class MapContextManager extends EventEmitter { if (Object.keys(layers).length) { // Cleanup entries in visibleLayers that no longer exist for (const key in this.visibleLayers) { - if (!this.layers[key]) { + if (!tocItems.find((i) => i.stableId === key)) { delete this.visibleLayers[key]; } } @@ -1679,6 +1945,7 @@ class MapContextManager extends EventEmitter { } this.debouncedUpdateStyle(); this.updateInteractivitySettings(); + this.updateLegends(); return; } @@ -1712,14 +1979,14 @@ class MapContextManager extends EventEmitter { labelsLayerInserted = true; layerIds.push("LABELS"); } - if (layer.sublayer) { - const specialId = idForSublayer(layer); - if (layerIds.indexOf(specialId) === -1) { - layerIds.push(specialId); - } - } else { - layerIds.push(layer.tocId); - } + // if (layer.sublayer) { + // const specialId = idForSublayer(layer); + // if (layerIds.indexOf(specialId) === -1) { + // layerIds.push(specialId); + // } + // } else { + layerIds.push(layer.tocId); + // } } this.layersByZIndex = layerIds; } @@ -2404,6 +2671,139 @@ class MapContextManager extends EventEmitter { getMissingSketches() {} isBasemapMissing() {} + + private async _updateLegends(clearCache = false) { + const newLegendState: { [layerId: string]: LegendItem | null } = {}; + let changes = false; + for (const id of this.layersByZIndex) { + if (this.visibleLayers[id]?.visible) { + if (clearCache === true && id in this.internalState.legends) { + newLegendState[id] = this.internalState.legends[id]; + } else { + const layer = this.layers[id]; + const source = this.clientDataSources[layer?.dataSourceId]; + if (layer && source && layer.mapboxGlStyles) { + let sourceType: + | undefined + | "vector" + | "raster" + | "image" + | "video" + | "raster-dem" + | "geojson" = undefined; + switch (source.type) { + case DataSourceTypes.Geojson: + case DataSourceTypes.SeasketchVector: + sourceType = "geojson"; + break; + case DataSourceTypes.Raster: + case DataSourceTypes.SeasketchRaster: + sourceType = "raster"; + break; + case DataSourceTypes.Vector: + case DataSourceTypes.SeasketchMvt: + sourceType = "vector"; + break; + case DataSourceTypes.RasterDem: + sourceType = "raster-dem"; + break; + case DataSourceTypes.ArcgisDynamicMapserver: + case DataSourceTypes.Image: + sourceType = "image"; + break; + case DataSourceTypes.Video: + sourceType = "video"; + break; + } + + if (sourceType) { + try { + const legend = compileLegendFromGLStyleLayers( + layer.mapboxGlStyles, + sourceType + ); + if (legend) { + newLegendState[id] = { + id, + type: "GLStyleLegendItem", + legend: legend, + zOrder: this.layersByZIndex.indexOf(id), + label: this.tocItemLabels[layer.tocId] || "", + }; + changes = true; + } else { + newLegendState[id] = null; + changes = true; + } + } catch (e) { + console.error(e); + newLegendState[id] = { + id, + type: "GLStyleLegendItem", + zOrder: this.layersByZIndex.indexOf(id), + label: this.tocItemLabels[layer.tocId] || "", + }; + changes = true; + } + } + } else if (source && isCustomSourceType(source.type)) { + const { customSource, visible } = this.customSources[source.id]; + if (visible && customSource) { + const { tableOfContentsItems } = + await customSource.getComputedMetadata(); + const item = + tableOfContentsItems.length === 1 + ? tableOfContentsItems[0] + : tableOfContentsItems.find( + (i) => i.id.toString() === layer?.sublayer?.toString() + ); + if (item && item.type === "data") { + if (item.glStyle) { + newLegendState[id] = { + id, + zOrder: this.layersByZIndex.indexOf(id), + type: "GLStyleLegendItem", + legend: compileLegendFromGLStyleLayers( + item.glStyle.layers, + "vector" + ), + label: this.tocItemLabels[layer.tocId] || "", + }; + } else if (item.legend) { + newLegendState[id] = { + type: "CustomGLSourceSymbolLegend", + label: item.label, + zOrder: this.layersByZIndex.indexOf(id), + supportsDynamicRendering: { + layerOpacity: true, + layerOrder: true, + layerVisibility: true, + }, + symbols: item.legend, + id: item.id.toString(), + }; + } + changes = true; + } + } + } + } + } else { + if (id in this.internalState.legends) { + delete newLegendState[id]; + changes = true; + } + } + } + if (changes) { + this.setState((prev) => ({ + ...prev, + legends: newLegendState, + })); + } + } + + updateLegends = debounce(this._updateLegends, 20); } export default MapContextManager; @@ -2446,6 +2846,9 @@ export interface MapContextInterface { supportsUndo: boolean; }; languageCode?: string; + legends: { + [layerId: string]: LegendItem | null; + }; measureControlState?: MeasureControlState; digitizingLockState: DigitizingLockState; digitizingLockedBy?: string; @@ -2483,6 +2886,7 @@ export function useMapContext(options?: MapContextOptions) { basemapOptionalLayerStates: {}, styleHash: "", containerPortal: containerPortal || null, + legends: {}, digitizingLockState: DigitizingLockState.Free, }; const token = useAccessToken(); @@ -2570,6 +2974,7 @@ export const MapContext = createContext({ basemapOptionalLayerStates: {}, styleHash: "", containerPortal: null, + legends: {}, digitizingLockState: DigitizingLockState.Free, }, (state) => {} @@ -2580,6 +2985,7 @@ export const MapContext = createContext({ terrainEnabled: false, basemapOptionalLayerStates: {}, containerPortal: null, + legends: {}, digitizingLockState: DigitizingLockState.Free, }); @@ -2705,3 +3111,25 @@ function sketchGeoJSONUrl(id: number, timestamp?: string | number) { }` }`; } + +export function sourceTypeIsCustomGLSource(type: DataSourceTypes) { + return ( + type === DataSourceTypes.ArcgisVector || + type === DataSourceTypes.ArcgisRasterTiles || + type === DataSourceTypes.ArcgisDynamicMapserver + ); +} + +type CustomSourceType = + | DataSourceTypes.ArcgisVector + | DataSourceTypes.ArcgisRasterTiles + | DataSourceTypes.ArcgisDynamicMapserver; + +function isCustomSourceType(type: DataSourceTypes): type is CustomSourceType { + return ( + sourceTypeIsCustomGLSource(type) || + type === DataSourceTypes.ArcgisDynamicMapserver || + type === DataSourceTypes.ArcgisRasterTiles || + type === DataSourceTypes.ArcgisVector + ); +} diff --git a/packages/client/src/dataLayers/MetadataModal.tsx b/packages/client/src/dataLayers/MetadataModal.tsx index 16595d2d3..23eda26ac 100644 --- a/packages/client/src/dataLayers/MetadataModal.tsx +++ b/packages/client/src/dataLayers/MetadataModal.tsx @@ -34,7 +34,7 @@ export default function MetadataModal({ }, [document]); return ( - + <>
    diff --git a/packages/client/src/dataLayers/TableOfContentsMetadataModal.tsx b/packages/client/src/dataLayers/TableOfContentsMetadataModal.tsx index 87fa8fe26..dd4b2f59b 100644 --- a/packages/client/src/dataLayers/TableOfContentsMetadataModal.tsx +++ b/packages/client/src/dataLayers/TableOfContentsMetadataModal.tsx @@ -12,10 +12,12 @@ export default function TableOfContentsMetadataModal({ variables: { itemId: id, }, + skip: !id, }); + return (
    + ); +} diff --git a/packages/client/src/dataLayers/legends/ExpressionEvaluator.ts b/packages/client/src/dataLayers/legends/ExpressionEvaluator.ts new file mode 100644 index 000000000..ca7a7c7bc --- /dev/null +++ b/packages/client/src/dataLayers/legends/ExpressionEvaluator.ts @@ -0,0 +1,69 @@ +import { expression } from "mapbox-gl/dist/style-spec/index.es.js"; +import { Expression as MapboxExpression, StyleFunction } from "mapbox-gl"; +import { Feature } from "geojson"; + +const expressionGlobals = { + zoom: 14, +}; + +/** A color as returned by a Mapbox style expression. All values are in [0, 1] */ +export interface RGBA { + r: number; + g: number; + b: number; + a: number; +} + +interface TypeMap { + string: string; + number: number; + color: RGBA; + boolean: boolean; + [other: string]: any; +} + +// Copied from https://gist.github.com/danvk/4378b6936f9cd634fc8c9f69c4f18b81 +/** + * Class for working with Mapbox style expressions. + * + * See https://docs.mapbox.com/mapbox-gl-js/style-spec/#expressions + */ +export class ExpressionEvaluator { + /** + * Parse a Mapbox style expression. + * + * Pass an expected type to get tigher error checking and more precise types. + */ + static parse( + expr: + | number + | string + | Readonly + | Readonly + | undefined, + expectedType?: T + ): ExpressionEvaluator { + // For details on use of this private API and plans to publicize it, see + // https://github.com/mapbox/mapbox-gl-js/issues/7670 + let parseResult: expression.ParseResult; + if (expectedType) { + parseResult = expression.createExpression(expr, { type: expectedType }); + if (parseResult.result === "success") { + return new ExpressionEvaluator(parseResult.value); + } + } else { + parseResult = expression.createExpression(expr); + if (parseResult.result === "success") { + return new ExpressionEvaluator(parseResult.value); + } + } + + throw parseResult.value[0]; + } + + constructor(public parsedExpression: expression.StyleExpression) {} + + evaluate(feature: Feature): T { + return this.parsedExpression.evaluate(expressionGlobals, feature); + } +} diff --git a/packages/client/src/dataLayers/legends/FillSymbol.tsx b/packages/client/src/dataLayers/legends/FillSymbol.tsx new file mode 100644 index 000000000..82753a6a4 --- /dev/null +++ b/packages/client/src/dataLayers/legends/FillSymbol.tsx @@ -0,0 +1,76 @@ +import { useEffect, useState } from "react"; +import { GLLegendFillSymbol } from "./LegendDataModel"; +import { LegendResolvedImage, getImage } from "./MarkerSymbol"; +import { Map } from "mapbox-gl"; +import { colord, extend } from "colord"; +import namesPlugin from "colord/plugins/names"; +extend([namesPlugin]); + +export default function FillSymbol({ + data, + map, +}: { + data: GLLegendFillSymbol; + map?: Map; +}) { + const [imageData, setImageData] = useState(); + useEffect(() => { + if (map && !imageData && data.patternImageId) { + const resolvedImage = getImage(data.patternImageId, map); + if (resolvedImage) { + setImageData(resolvedImage); + } else { + const handler = () => { + const resolvedImage = getImage(data.patternImageId!, map); + if (resolvedImage) { + setImageData(resolvedImage); + map.off("styledata", handler); + } + }; + map.on("styledata", handler); + return () => { + map.off("styledata", handler); + }; + } + } + }, [setImageData, map, data.patternImageId, imageData]); + const simpleSymbol = data; + let bg = colord(simpleSymbol.color); + if ( + data.fillOpacity !== undefined && + data.fillOpacity < 1 && + bg.alpha() === 1 + ) { + bg = bg.alpha(data.fillOpacity); + } + let strokeColor = colord(simpleSymbol.strokeColor || "#000"); + if ( + data.strokeOpacity !== undefined && + data.strokeOpacity < 1 && + strokeColor.alpha() === 1 + ) { + strokeColor = strokeColor.alpha(data.strokeOpacity); + } + + return ( +
    + ); +} diff --git a/packages/client/src/dataLayers/legends/LegendBubblePanel.tsx b/packages/client/src/dataLayers/legends/LegendBubblePanel.tsx new file mode 100644 index 000000000..babcd138f --- /dev/null +++ b/packages/client/src/dataLayers/legends/LegendBubblePanel.tsx @@ -0,0 +1,62 @@ +import { GLLegendBubblePanel } from "./LegendDataModel"; + +export default function LegendBubblePanel({ + panel, + panelWidth, +}: { + panel: GLLegendBubblePanel; + panelWidth: number; +}) { + const maxRadius = panel.stops[panel.stops.length - 1]?.radius || 5; + const scaling = panelWidth < maxRadius ? panelWidth / maxRadius : 1; + return ( + // eslint-disable-next-line i18next/no-literal-string +
  • + {panel.label && ( +

    {panel.label}

    + )} + {[...panel.stops].reverse().map((stop, i) => { + return ( +
    +
    0 + ? `${stop.strokeWidth}px solid ${stop.stroke}` + : undefined, + }} + >
    + + + {stop.value.toLocaleString()} + + +
    + ); + })} +
  • + ); +} diff --git a/packages/client/src/dataLayers/legends/LegendDataModel.ts b/packages/client/src/dataLayers/legends/LegendDataModel.ts new file mode 100644 index 000000000..867810bce --- /dev/null +++ b/packages/client/src/dataLayers/legends/LegendDataModel.ts @@ -0,0 +1,176 @@ +export interface GLLegendFillSymbol { + type: "fill"; + color: string; + extruded?: boolean; + patternImageId?: string; + /** 0-1 */ + fillOpacity: number; + strokeWidth: number; + strokeOpacity?: number; + strokeColor?: string; + dashed?: boolean; +} + +export interface GLLegendLineSymbol { + type: "line"; + color: string; + strokeWidth: number; + patternImageId?: string; + dashed?: boolean; + opacity?: number; +} + +export interface GLLegendCircleSymbol { + type: "circle"; + color: string; + strokeWidth: number; + strokeColor?: string; + /** 0-1 */ + fillOpacity: number; + strokeOpacity: number; + radius: number; +} + +// TODO: icon-color +export interface GLLegendMarkerSymbol { + type: "marker"; + imageId: string; + haloColor?: string; + haloWidth?: number; + rotation?: number; + /** multiple of width & height to display */ + iconSize: number; +} + +export interface GLLegendRasterSymbol { + type: "raster"; +} + +export interface GLLegendVideoSymbol { + type: "video"; +} + +export interface GLLegendTextSymbol { + type: "text"; + color: string; + fontFamily: string; + fontWeight: "normal" | "bold"; + fontStyle: "normal" | "italic"; + haloColor?: string; + haloWidth?: number; +} + +export type GLLegendSymbol = + | GLLegendFillSymbol + | GLLegendCircleSymbol + | GLLegendMarkerSymbol + | GLLegendTextSymbol + | GLLegendRasterSymbol + | GLLegendLineSymbol + | GLLegendVideoSymbol; + +export type GLLegendListPanel = { + id: string; + type: "GLLegendListPanel"; + label?: string; + items: { id: string; label: string; symbol: GLLegendSymbol }[]; +}; + +export type GLLegendFilterPanel = { + id: string; + type: "GLLegendFilterPanel"; + label: string; + children: GLLegendPanel[]; +}; + +/** + * Display should be stacked if bubbles are big and can nest together, otherwise + * display as a list. + * + * Note that a BubblePanel may be paired with a ListPanel for a common case of + * a bubble chart with a categorical variable controlling the color of the bubbles. + */ +export type GLLegendBubblePanel = { + id: string; + type: "GLLegendBubblePanel"; + label?: string; + stops: { + value: number; + radius: number; + fill: string; + fillOpacity: number; + stroke: string; + strokeWidth: number; + }[]; +}; + +export type GLMarkerSizePanel = { + id: string; + type: "GLMarkerSizePanel"; + label?: string; + stops: { + id: string; + imageId: string; + value: number; + iconSize: number; + color?: string; + haloColor?: string; + haloWidth?: number; + rotation?: number; + }[]; +}; + +export type GLLegendStepPanel = { + id: string; + type: "GLLegendStepPanel"; + label?: string; + steps: { id: string; label: string; symbol: GLLegendSymbol }[]; +}; + +export type GLLegendHeatmapPanel = { + id: string; + type: "GLLegendHeatmapPanel"; + stops: { value: number; color: string }[]; +}; + +export type GLLegendGradientPanel = { + id: string; + type: "GLLegendGradientPanel"; + label?: string; + stops: { value: number; label: string; color: string }[]; +}; + +export type GLLegendSimpleSymbolPanel = { + id: string; + type: "GLLegendSimpleSymbolPanel"; + label?: string; + items: { + id: string; + label?: string; + symbol: GLLegendSymbol; + }[]; +}; + +export type GLLegendPanel = + | GLLegendListPanel + | GLLegendBubblePanel + | GLLegendHeatmapPanel + | GLLegendGradientPanel + | GLMarkerSizePanel + | GLLegendStepPanel + | GLLegendSimpleSymbolPanel + | GLLegendFilterPanel; + +export type SimpleLegendForGLLayers = { + type: "SimpleGLLegend"; + symbol: GLLegendSymbol; +}; + +export type MultipleSymbolLegendForGLLayers = { + type: "MultipleSymbolGLLegend"; + panels: GLLegendPanel[]; +}; + +export type LegendForGLLayers = + | SimpleLegendForGLLayers + | MultipleSymbolLegendForGLLayers; diff --git a/packages/client/src/dataLayers/legends/LegendGradientPanel.tsx b/packages/client/src/dataLayers/legends/LegendGradientPanel.tsx new file mode 100644 index 000000000..dbae51d6b --- /dev/null +++ b/packages/client/src/dataLayers/legends/LegendGradientPanel.tsx @@ -0,0 +1,26 @@ +import { GLLegendGradientPanel } from "./LegendDataModel"; +import { stopsToLinearGradient } from "./utils"; + +export default function LegendGradientPanel({ + panel, +}: { + panel: GLLegendGradientPanel; +}) { + const vals = panel.stops.map((stop) => stop.value); + const maxVal = Math.max(...vals); + return ( +
  • + {panel.label &&

    {panel.label}

    } +
    +
    + {vals[0].toLocaleString()} + {maxVal.toLocaleString()} +
    +
  • + ); +} diff --git a/packages/client/src/dataLayers/legends/LegendHeatmapPanel.tsx b/packages/client/src/dataLayers/legends/LegendHeatmapPanel.tsx new file mode 100644 index 000000000..56fdcadd5 --- /dev/null +++ b/packages/client/src/dataLayers/legends/LegendHeatmapPanel.tsx @@ -0,0 +1,25 @@ +import { GLLegendHeatmapPanel } from "./LegendDataModel"; +import { stopsToLinearGradient } from "./utils"; + +export default function LegendHeatmapPanel({ + panel, +}: { + panel: GLLegendHeatmapPanel; +}) { + return ( +
  • +
    +
    +
  • + ); +} diff --git a/packages/client/src/dataLayers/legends/LegendListPanel.tsx b/packages/client/src/dataLayers/legends/LegendListPanel.tsx new file mode 100644 index 000000000..5e591b8d4 --- /dev/null +++ b/packages/client/src/dataLayers/legends/LegendListPanel.tsx @@ -0,0 +1,37 @@ +import SimpleSymbol from "./SimpleSymbol"; +import { GLLegendListPanel } from "./LegendDataModel"; +import { Map } from "mapbox-gl"; + +export default function LegendListPanel({ + panel, + map, +}: { + panel: GLLegendListPanel; + map?: Map; +}) { + return ( +
  • + {panel.label && ( +

    {panel.label}

    + )} +
      + {panel.items.map((item) => { + return ( +
    • +
      + {item.symbol ? ( + + ) : null} +
      + + {item.label} +
    • + ); + })} +
    +
  • + ); +} diff --git a/packages/client/src/dataLayers/legends/LegendMarkerSizePanel.tsx b/packages/client/src/dataLayers/legends/LegendMarkerSizePanel.tsx new file mode 100644 index 000000000..11561a9fe --- /dev/null +++ b/packages/client/src/dataLayers/legends/LegendMarkerSizePanel.tsx @@ -0,0 +1,45 @@ +import MarkerSymbol from "./MarkerSymbol"; +import SimpleSymbol from "./SimpleSymbol"; +import { GLLegendListPanel, GLMarkerSizePanel } from "./LegendDataModel"; +import { Map } from "mapbox-gl"; + +export default function LegendMarkerSizePanel({ + panel, + map, +}: { + panel: GLMarkerSizePanel; + map?: Map; +}) { + return ( +
  • + {panel.label && ( +

    {panel.label}

    + )} +
      + {panel.stops.map((stop) => { + return ( +
    • +
      + {map && stop.imageId ? ( + + ) : null} +
      + + + {stop.value.toLocaleString()} + +
    • + ); + })} +
    +
  • + ); +} diff --git a/packages/client/src/dataLayers/legends/LegendSimpleSymbolPanel.tsx b/packages/client/src/dataLayers/legends/LegendSimpleSymbolPanel.tsx new file mode 100644 index 000000000..e2f80ce45 --- /dev/null +++ b/packages/client/src/dataLayers/legends/LegendSimpleSymbolPanel.tsx @@ -0,0 +1,39 @@ +import SimpleSymbol from "./SimpleSymbol"; +import { GLLegendSimpleSymbolPanel } from "./LegendDataModel"; +import { Map } from "mapbox-gl"; + +export default function LegendSimpleSymbolPanel({ + panel, + map, +}: { + panel: GLLegendSimpleSymbolPanel; + map?: Map; +}) { + return ( +
  • + {panel.label && ( + // eslint-disable-next-line i18next/no-literal-string +

    simple - {panel.label}

    + )} +
      + {panel.items.map((item) => { + return ( +
    • +
      + {item.symbol ? ( + + ) : null} +
      + {item.label && ( + {item.label} + )} +
    • + ); + })} +
    +
  • + ); +} diff --git a/packages/client/src/dataLayers/legends/LegendStepPanel.tsx b/packages/client/src/dataLayers/legends/LegendStepPanel.tsx new file mode 100644 index 000000000..28a13a3ae --- /dev/null +++ b/packages/client/src/dataLayers/legends/LegendStepPanel.tsx @@ -0,0 +1,34 @@ +import { Map } from "mapbox-gl"; +import { GLLegendStepPanel } from "./LegendDataModel"; +import SimpleSymbol from "./SimpleSymbol"; + +export default function LegendStepPanel({ + panel, + map, +}: { + panel: GLLegendStepPanel; + map?: Map; +}) { + return ( +
  • + {panel.label && ( +

    {panel.label}

    + )} +
      + {panel.steps.map((step) => { + return ( +
    • +
      + {map && } +
      + {step.label} +
    • + ); + })} +
    +
  • + ); +} diff --git a/packages/client/src/dataLayers/legends/LineSymbol.tsx b/packages/client/src/dataLayers/legends/LineSymbol.tsx new file mode 100644 index 000000000..4708d8ada --- /dev/null +++ b/packages/client/src/dataLayers/legends/LineSymbol.tsx @@ -0,0 +1,23 @@ +import { GLLegendLineSymbol } from "./LegendDataModel"; + +// TODO: support line patterns +// but how?? +export default function LineSymbol(props: { data: GLLegendLineSymbol }) { + const simpleSymbol = props.data; + return ( +
    + ); +} diff --git a/packages/client/src/dataLayers/legends/MarkerSymbol.tsx b/packages/client/src/dataLayers/legends/MarkerSymbol.tsx new file mode 100644 index 000000000..9108677fd --- /dev/null +++ b/packages/client/src/dataLayers/legends/MarkerSymbol.tsx @@ -0,0 +1,132 @@ +import { Map } from "mapbox-gl"; +import { useEffect, useState } from "react"; +import { blankDataUri } from "@seasketch/mapbox-gl-esri-sources/dist/src/ArcGISDynamicMapService"; + +// TODO: icon-color +export default function MarkerSymbol({ + map, + imageId, + fullSize, + iconSize, +}: { + imageId: string; + map: Map; + fullSize?: boolean; + iconSize?: number; +}) { + const [imageData, setImageData] = useState(); + + useEffect(() => { + if (!imageData && imageId) { + const resolvedImage = getImage(imageId, map); + if (resolvedImage) { + setImageData(resolvedImage); + } else { + const handler = () => { + const resolvedImage = getImage(imageId, map); + if (resolvedImage) { + setImageData(resolvedImage); + map.off("styledata", handler); + } + }; + map.on("styledata", handler); + return () => { + map.off("styledata", handler); + }; + } + } + }, [setImageData, map, imageId, imageData]); + + if (imageData) { + return ( +
    + ); + // return ( + // + // ); + } else { + // eslint-disable-next-line i18next/no-literal-string + return null; + } +} + +const toImageData = (d: { width: number; height: number; data: any }) => { + const { width, height, data } = d; + const size = Math.max(width, height); + const canvas = document.createElement("canvas"); + canvas.setAttribute("width", size.toString()); + canvas.setAttribute("height", size.toString()); + const ctx = canvas.getContext("2d"); + if (ctx) { + const imageData = new ImageData( + Uint8ClampedArray.from(data), + width, + height + ); + ctx.putImageData(imageData, (size - width) / 4, (size - height) / 4); + } + return canvas.toDataURL(); +}; + +export interface LegendResolvedImage { + url: string; + width: number; + height: number; + pixelRatio: number; +} + +export function getImage( + id: string, + map: Map +): LegendResolvedImage | undefined { + // @ts-ignore + const d = map.style.getImage(id); + if (d) { + if (d.sdf) { + // TODO: somehow support sdf? + return { + url: blankDataUri, + width: d.width, + height: d.height, + pixelRatio: d.pixelRatio, + }; + } else { + const url = toImageData(d.data); + if (url) { + return { + url, + width: d.data.width, + height: d.data.height, + pixelRatio: d.pixelRatio, + }; + } else { + // If it fails somehow, return a blank data uri + return { + url: blankDataUri, + width: d.width, + height: d.height, + pixelRatio: d.pixelRatio, + }; + } + } + } else { + return undefined; + } +} diff --git a/packages/client/src/dataLayers/legends/SimpleSymbol.tsx b/packages/client/src/dataLayers/legends/SimpleSymbol.tsx new file mode 100644 index 000000000..6d34e3b00 --- /dev/null +++ b/packages/client/src/dataLayers/legends/SimpleSymbol.tsx @@ -0,0 +1,67 @@ +import { VideoCameraIcon } from "@heroicons/react/solid"; +import { GLLegendSymbol } from "./LegendDataModel"; +import FillSymbol from "./FillSymbol"; +import LineSymbol from "./LineSymbol"; +import MarkerSymbol from "./MarkerSymbol"; +import { Map } from "mapbox-gl"; +import CircleSymbol from "./CircleSymbol"; + +export default function SimpleSymbol(props: { + data: GLLegendSymbol; + map?: Map; +}) { + switch (props.data.type) { + case "line": + return ; + case "fill": + return ; + case "marker": + return props.map ? ( + + ) : null; + case "text": + return ( + + + + ); + case "raster": + return ( + + + + ); + case "video": + return ; + case "circle": + return ; + default: + throw new Error(`Unknown symbol type: ${props.data}`); + } +} diff --git a/packages/client/src/dataLayers/legends/compileLegend.ts b/packages/client/src/dataLayers/legends/compileLegend.ts new file mode 100644 index 000000000..f41a618fd --- /dev/null +++ b/packages/client/src/dataLayers/legends/compileLegend.ts @@ -0,0 +1,3297 @@ +import { Feature } from "geojson"; +import { + CircleLayer, + Expression, + FillExtrusionLayer, + FillLayer, + Layer, + LineLayer, +} from "mapbox-gl"; +import styleSpec from "mapbox-gl/src/style-spec/reference/v8.json"; +import { ExpressionEvaluator, RGBA } from "./ExpressionEvaluator"; +import { + GLLegendBubblePanel, + GLLegendCircleSymbol, + GLLegendFillSymbol, + GLLegendFilterPanel, + GLLegendGradientPanel, + GLLegendHeatmapPanel, + GLLegendLineSymbol, + GLLegendListPanel, + GLLegendMarkerSymbol, + GLLegendPanel, + GLLegendSimpleSymbolPanel, + GLLegendStepPanel, + GLLegendSymbol, + GLLegendTextSymbol, + GLMarkerSizePanel, + LegendForGLLayers, +} from "./LegendDataModel"; +import cloneDeep from "lodash.clonedeep"; +import { + pluckGetExpressionsOfType, + isExpression, + hasGetExpression, + findGetExpression, + hasGetExpressionForProperty, + NULLIFIED_EXPRESSION_OUTPUT_NUMBER, + NULLIFIED_EXPRESSION_OUTPUT_STRING, +} from "./utils"; + +// ordered by rank of importance +const SIGNIFICANT_PAINT_PROPS = [ + "fill-color", + "fill-extrusion-color", + "fill-pattern", + "fill-opacity", + "fill-outline-color", + "fill-extrusion-height", + "circle-color", + "line-color", + "line-pattern", + "text-color", + "circle-radius", + "icon-color", + "circle-stroke-color", + "line-width", + "circle-stroke-width", + "line-dasharray", + "line-opacity", + "circle-opacity", +]; + +const SIGNIFICANT_LAYOUT_PROPS = ["icon-image", "icon-size"]; + +const SIGNIFICANT_STYLE_PROPS = [ + ...(SIGNIFICANT_PAINT_PROPS.map((p) => ({ + type: "paint", + prop: p, + })) as StyleProp[]), + ...(SIGNIFICANT_LAYOUT_PROPS.map((p) => ({ + type: "layout", + prop: p, + })) as StyleProp[]), +]; + +export function compileLegendFromGLStyleLayers( + layers: SeaSketchGlLayer[], + sourceType: "vector" | "raster" | "geojson" | "image" | "video" | "raster-dem" +): LegendForGLLayers { + if ( + sourceType === "raster" || + sourceType === "raster-dem" || + sourceType === "image" + ) { + return { + type: "SimpleGLLegend", + symbol: { + type: "raster", + }, + }; + } else if (sourceType === "video") { + return { + type: "SimpleGLLegend", + symbol: { + type: "video", + }, + }; + } + let legendItems: { panel: GLLegendPanel; filters: Expression[] }[] = []; + + try { + // Iterate through each of the possible complex panel types, creating them + // as found. When creating these panels, layers and style properties are + // "plucked" from the list so that they aren't repeated. + // If panels require additional data conditions to be met, those are added + // to the filters array. + const clonedLayers = cloneDeep(layers); + const context = { layers: clonedLayers }; + try { + legendItems.push(...pluckHeatmapPanels(context)); + } catch (e) { + console.warn(e); + } + try { + legendItems.push(...pluckBubblePanels(context)); + } catch (e) { + console.warn(e); + } + try { + legendItems.push(...pluckMarkerSizePanels(context)); + } catch (e) { + console.warn(e); + } + try { + legendItems.push(...pluckGradientPanels(context)); + } catch (e) { + console.warn(e); + } + try { + legendItems.push(...pluckStepPanels(context)); + } catch (e) { + console.warn(e); + } + try { + legendItems.push(...pluckListPanelsFromMatchExpressions(context)); + } catch (e) { + console.warn(e); + } + try { + legendItems.push(...pluckListPanelsForCaseAndFilterExpressions(context)); + } catch (e) { + // do nothing + console.warn(e); + } + + try { + legendItems.push(...pluckFilterPanels(context)); + } catch (e) { + console.warn(e); + } + + // render any remaining layers as simple symbols + if (legendItems.length > 0) { + for (const layer of context.layers) { + if ( + layer.type !== "symbol" || + (layer.paint || ({} as any))["icon-image"] + ) { + legendItems.push({ + filters: [], + panel: { + // eslint-disable-next-line i18next/no-literal-string + id: `remaining-layer-${context.layers.indexOf(layer)}`, + type: "GLLegendSimpleSymbolPanel", + items: [ + { + id: "remaining-layer-single-child", + symbol: getSingleSymbolForVectorLayers([layer]), + }, + ], + }, + }); + } + } + } + + let panels = consolidatePanels(legendItems); + + const culled: GLLegendPanel[] = []; + for (const panel of panels) { + switch (panel.type) { + case "GLLegendListPanel": + case "GLLegendSimpleSymbolPanel": + if (panel.items.length === 0) { + culled.push(panel); + } + break; + case "GLLegendStepPanel": + if (panel.steps.length === 0) { + culled.push(panel); + } + break; + } + } + panels = panels.filter((p) => !culled.includes(p)); + + // Where fill layers exist, convert all line layers to fill layers with a + // transparent fill + if (layers.find((l) => l.type === "fill")) { + transformLineSymbolsToFill(panels); + } + + eliminateDuplicatesAndRenameDefaults(panels); + + // sort panels. filter panels should go last + panels.sort((a, b) => { + if (a.type === "GLLegendFilterPanel") { + return 1; + } else if (b.type === "GLLegendFilterPanel") { + return -1; + } else { + return 0; + } + }); + if (legendItems.length === 0) { + return { + type: "SimpleGLLegend", + symbol: getSingleSymbolForVectorLayers(layers), + }; + } else { + return { + type: "MultipleSymbolGLLegend", + panels, + }; + } + } catch (e) { + console.warn(e); + return { + type: "SimpleGLLegend", + symbol: getSingleSymbolForVectorLayers(layers), + }; + } +} + +function eliminateDuplicatesAndRenameDefaults(panels: GLLegendPanel[]) { + for (const panel of panels) { + if (panel.type === "GLLegendFilterPanel") { + eliminateDuplicatesAndRenameDefaults(panel.children); + } else if (panel.type === "GLLegendListPanel") { + const existingKeys = new Set(); + const plucked: any = []; + for (const item of panel.items) { + if ( + (item.label === "" || item.label === " ") && + panel.items.length > 1 + ) { + item.label = "default"; + } + const key = `${item.label}-${item.symbol.type}`; + if (existingKeys.has(key)) { + plucked.push(item); + } else { + existingKeys.add(key); + } + } + panel.items = panel.items.filter((item) => !plucked.includes(item)); + // find any item labelled "default", and put it at the buttom of the list + const defaultItem = panel.items.find((item) => item.label === "default"); + if (defaultItem) { + panel.items = panel.items.filter((item) => item !== defaultItem); + panel.items.push(defaultItem); + } + } + } +} + +/** + * For a given facet, reads the filter expressions and produces + * a GeoJSON properties object that would pass all those filters + * @param facet + */ +export function propsForFilterExpressions(filters: Expression[]) { + const featureProps: { [prop: string]: any } = {}; + // Read filters and create props that would pass them all + for (const filter of filters) { + if (filter[0] === "any") { + const filterProps = propsForSimpleFilter(filter[1][0]); + for (const prop in filterProps) { + featureProps[prop] = filterProps[prop]; + } + } else if (filter[0] === "all") { + for (const arg of filter.slice(1)) { + const filterProps = propsForSimpleFilter(arg); + for (const prop in filterProps) { + featureProps[prop] = filterProps[prop]; + } + } + } else { + const filterProps = propsForSimpleFilter(filter); + for (const prop in filterProps) { + featureProps[prop] = filterProps[prop]; + } + } + } + return featureProps; +} + +function propsForSimpleFilter(filter: Expression) { + if (filter[0] === "all" || filter[0] === "any") { + throw new Error( + "propsForSimpleFilter should not be called with all or any expressions" + ); + } + const featureProps: { [prop: string]: any } = {}; + let getInfo = { + position: 0, + prop: "", + }; + if (filter[0] === "has") { + featureProps[filter[1]] = "__has"; + return featureProps; + } + if (isExpression(filter[1]) && filter[1][0] === "get") { + getInfo.position = 0; + getInfo.prop = filter[1][1]; + } else if (isExpression(filter[2]) && filter[2][0] === "get") { + getInfo.position = 1; + getInfo.prop = filter[2][1]; + } else { + // throw new Error("Could not find get expression in filter"); + } + let propType = typeof filter[getInfo.position === 0 ? 2 : 1]; + switch (filter[0]) { + case "==": + featureProps[getInfo.prop] = filter[getInfo.position === 0 ? 2 : 1]; + break; + case "!=": + featureProps[getInfo.prop] = + propType === "number" ? -38574326900913 : "___ne"; + break; + // @ts-ignore + case "!has": + delete featureProps[getInfo.prop]; + break; + case ">": + case ">=": + featureProps[getInfo.prop] = + filter[getInfo.position === 0 ? 2 : 1] + 0.00001; + break; + case "<": + case "<=": + featureProps[getInfo.prop] = + filter[getInfo.position === 0 ? 2 : 1] - 0.00001; + break; + case "!": + featureProps[getInfo.prop] = false; + break; + case "in": + featureProps[getInfo.prop] = filter[getInfo.position === 0 ? 2 : 1]; + break; + default: + throw new Error(`Unsupported filter operator "${filter[0]}"`); + } + return featureProps; +} + +export function pluckBubblePanels(context: { layers: SeaSketchGlLayer[] }) { + const panels: { panel: GLLegendBubblePanel; filters: Expression[] }[] = []; + // Look for circle layers with a circle-radius expression + const circleLayers = context.layers.filter( + (l) => l.type === "circle" && l.layout?.visibility !== "none" + ) as CircleLayer[]; + for (const layer of circleLayers) { + if (layer.paint) { + const radius = layer.paint["circle-radius"]; + if (radius) { + const exprData = pluckGetExpressionsOfType( + radius, + "interpolate", + "number" + ); + if (exprData.facets.length) { + const representedProperties = new RepresentedProperties(); + // the "plucking" part, removes bubble chart related values from layer + layer.paint["circle-radius"] = exprData.remainingValues; + representedProperties.add("circle-radius"); + representedProperties.commit(); + for (const facet of exprData.facets) { + const featureProps = propsForFilterExpressions(facet.filters); + const facetFilters = isExpression(layer.filter) + ? [layer.filter, ...facet.filters] + : facet.filters; + const interpolate = facet.expression; + let stops = stopsFromInterpolation(interpolate); + // limit stops to 3 in bubble charts + if (stops.length > 3) { + stops = [ + stops[0], + stops[Math.floor(stops.length / 2)], + stops[stops.length - 1], + ]; + } else if (stops.length === 2) { + // add a midpoint stop + stops = [ + stops[0], + { + // @ts-ignore + input: (stops[0].input + stops[1].input) / 2, + // @ts-ignore + output: (stops[0].output + stops[1].output) / 2, + }, + stops[1], + ]; + } + // construct a bubble panel + panels.push({ + panel: { + // eslint-disable-next-line i18next/no-literal-string + id: `bubble-${circleLayers.indexOf( + layer + )}-${exprData.facets.indexOf(facet)}`, + type: "GLLegendBubblePanel", + label: interpolate[2][1], + stops: stops + .filter((s) => "input" in s) + .map((s) => { + if (!("input" in s)) { + throw new Error("Stop lacks input"); + } + const featureData = { + ...featureProps, + [interpolate[2][1]]: s.input, + }; + return { + fill: evaluateLayerProperty( + layer, + "paint", + "circle-color", + featureData, + "Point", + representedProperties + ), + radius: s.output as number, + stroke: evaluateLayerProperty( + layer, + "paint", + "circle-stroke-color", + featureData, + "Point", + representedProperties + ), + strokeWidth: evaluateLayerProperty( + layer, + "paint", + "circle-stroke-width", + featureData, + "Point", + representedProperties + ), + value: s.input, + fillOpacity: evaluateLayerProperty( + layer, + "paint", + "circle-opacity", + featureData, + "Point", + representedProperties + ), + }; + }), + }, + filters: facetFilters, + }); + } + representedProperties.commit(); + // remove the layer if it's been fully represented by the bubble chart + let hasRemainingGetExpressions = false; + const paint: any = layer.paint || {}; + for (const prop of [ + "circle-color", + "circle-stroke-width", + "circle-stroke-color", + "circle-opacity", + ]) { + if ( + prop in paint && + hasGetExpression(paint[prop]) && + !representedProperties.has(prop) + ) { + hasRemainingGetExpressions = true; + } + } + if (!hasRemainingGetExpressions) { + context.layers = context.layers.filter((l) => l !== layer); + } + } + } + } + } + return panels; +} + +export function pluckMarkerSizePanels(context: { layers: SeaSketchGlLayer[] }) { + const panels: { panel: GLMarkerSizePanel; filters: Expression[] }[] = []; + // Look for symbol layers with an icon-size expression + const symbolLayers = context.layers.filter( + (l) => l.type === "symbol" && l.layout?.visibility !== "none" + ) as CircleLayer[]; + for (const layer of symbolLayers) { + const layout = layer.layout || ({} as any); + if (layout["icon-size"]) { + const size = layout["icon-size"]; + if (size) { + const exprData = pluckGetExpressionsOfType( + size, + "interpolate", + "number" + ); + if (exprData.facets.length) { + const representedProperties = new RepresentedProperties(); + // the "plucking" part, removes bubble chart related values from layer + // @ts-ignore + layer.layout["icon-size"] = exprData.remainingValues; + representedProperties.add("circle-radius"); + representedProperties.commit(); + for (const facet of exprData.facets) { + const featureProps = propsForFilterExpressions(facet.filters); + const facetFilters = isExpression(layer.filter) + ? [layer.filter, ...facet.filters] + : facet.filters; + const interpolate = facet.expression; + let stops = stopsFromInterpolation(interpolate); + // limit stops to 3 in bubble charts + if (stops.length > 3) { + stops = [ + stops[0], + stops[Math.floor(stops.length / 2)], + stops[stops.length - 1], + ]; + } else if (stops.length === 2) { + // add a midpoint stop + stops = [ + stops[0], + { + // @ts-ignore + input: (stops[0].input + stops[1].input) / 2, + // @ts-ignore + output: (stops[0].output + stops[1].output) / 2, + }, + stops[1], + ]; + } + // construct a bubble panel + panels.push({ + panel: { + // eslint-disable-next-line i18next/no-literal-string + id: `marker-size-${symbolLayers.indexOf( + layer + )}-${exprData.facets.indexOf(facet)}`, + type: "GLMarkerSizePanel", + label: interpolate[2][1], + stops: stops + .filter((s) => "input" in s) + .map((s) => { + if (!("input" in s)) { + throw new Error("Stop lacks input"); + } + const featureData = { + ...featureProps, + [interpolate[2][1]]: s.input, + }; + return { + // eslint-disable-next-line i18next/no-literal-string + id: `stop-${stops.indexOf(s)}`, + imageId: evaluateLayerProperty( + layer, + "layout", + "icon-image", + featureData, + "Point", + representedProperties + ), + value: s.input, + iconSize: s.output as number, + color: evaluateLayerProperty( + layer, + "paint", + "icon-color", + featureData, + "Point", + representedProperties + ), + haloColor: evaluateLayerProperty( + layer, + "paint", + "icon-halo-color", + featureData, + "Point", + representedProperties + ), + haloWidth: evaluateLayerProperty( + layer, + "paint", + "icon-halo-width", + featureData, + "Point", + representedProperties + ), + rotation: evaluateLayerProperty( + layer, + "layout", + "icon-rotate", + featureData, + "Point", + representedProperties + ), + }; + }), + }, + filters: facetFilters, + }); + } + representedProperties.commit(); + } + } + } + } + return panels; +} + +export function pluckHeatmapPanels(context: { layers: SeaSketchGlLayer[] }) { + const panels: { panel: GLLegendHeatmapPanel; filters: Expression[] }[] = []; + let newLayers = [...context.layers]; + for (const layer of context.layers) { + if (layer.type === "heatmap") { + newLayers = newLayers.filter((l) => l !== layer); + const paint = layer.paint || ({} as any); + const exprData = pluckGetExpressionsOfType( + paint["heatmap-color"], + "interpolate", + // @ts-ignore + ["interpolate"], + { targetExpressionMustIncludeGet: false } + ); + if (exprData.facets.length === 0) { + const filters = + layer.filter && isExpression(layer.filter) ? [layer.filter] : []; + panels.push({ + panel: { + id: "heatmap", + type: "GLLegendHeatmapPanel", + stops: interpolationExpressionToStops(paint["heatmap-color"]), + }, + filters, + }); + } else { + for (const facet of exprData.facets) { + const facetFilters = isExpression(layer.filter) + ? [layer.filter, ...facet.filters] + : facet.filters; + panels.push({ + panel: { + id: "heatmap", + type: "GLLegendHeatmapPanel", + stops: interpolationExpressionToStops(facet.expression), + }, + filters: facetFilters, + }); + } + } + } + } + context.layers = newLayers; + return panels; +} + +export function pluckGradientPanels(context: { layers: SeaSketchGlLayer[] }) { + const panels: { panel: GLLegendGradientPanel; filters: Expression[] }[] = []; + let remainingLayers = [...context.layers]; + for (const layer of context.layers) { + if ( + ["fill", "line", "fill-extrusion", "circle", "symbol"].includes( + layer.type + ) + ) { + // look for gradient expressions, ordered by priority + const paint = layer.paint || ({} as any); + for (const paintProp of [ + "fill-color", + "fill-extrusion-color", + "circle-color", + "line-color", + "icon-color", + // fill outline color is so faint I can't see anyone using it + // as the primary means of communicating data, so I'm ommiting it + // "fill-outline-color", + ]) { + if (paintProp in paint && isExpression(paint[paintProp])) { + const exprData = pluckGetExpressionsOfType( + paint[paintProp], + /interpolate/, + "color" + ); + if (exprData.facets.length) { + for (const facet of exprData.facets) { + // @ts-ignore + layer.paint[paintProp] = exprData.remainingValues; + panels.push({ + panel: { + // eslint-disable-next-line i18next/no-literal-string + id: `${context.layers.indexOf( + layer + )}-${paintProp}-${exprData.facets.indexOf(facet)}-gradient`, + type: "GLLegendGradientPanel", + label: paintProp, + stops: interpolationExpressionToStops(facet.expression), + }, + filters: + layer.filter && isExpression(layer.filter) + ? [layer.filter, ...facet.filters] + : facet.filters, + }); + } + // Look for unrelated get expressions in the remaining style props + // if none are present, remove the layer + let hasRemainingGetExpressions = false; + for (const styleProp of SIGNIFICANT_STYLE_PROPS) { + const paint = layer.paint || ({} as any); + const layout = layer.layout || ({} as any); + const styleValue = + styleProp.type === "paint" + ? paint[styleProp.prop] + : layout[styleProp.prop]; + if ( + styleValue && + hasGetExpression(styleValue) && + styleProp.prop !== paintProp + ) { + hasRemainingGetExpressions = true; + } + } + if (!hasRemainingGetExpressions) { + remainingLayers = remainingLayers.filter((l) => l !== layer); + } + break; + } + } + } + } + } + context.layers = remainingLayers; + return panels; +} + +interface StyleProp { + type: "paint" | "layout"; + prop: string; +} + +function pluckLayersWithExpression( + context: { layers: SeaSketchGlLayer[] }, + expressionFnName: "step" | "match" | "case", + fn: ( + prop: StyleProp, + expression: Expression, + layer: SeaSketchGlLayer, + representedProperties: RepresentedProperties, + id: string, + featureProps: { + [prop: string]: any; + }, + sortedLayers: SeaSketchGlLayer[] + ) => null | T +) { + const panels: { panel: T; filters: Expression[] }[] = []; + const pluckedLayers: SeaSketchGlLayer[] = []; + const representedProperties = new RepresentedProperties(); + // order layers so that fills come before lines + const layers = [...context.layers].sort((a, b) => { + if (a.type === "fill" && b.type === "line") { + return -1; + } else if (a.type === "line" && b.type === "fill") { + return 1; + } else { + return 0; + } + }); + for (const layer of layers) { + // Line layers related to fills could be "plucked" before this point + if (!pluckedLayers.includes(layer)) { + representedProperties.reset(); + const paint = layer.paint || ({} as any); + const layout = layer.layout || ({} as any); + for (const styleProp of SIGNIFICANT_STYLE_PROPS) { + const styleValue = + styleProp.type === "paint" + ? paint[styleProp.prop] + : layout[styleProp.prop]; + if ( + styleValue !== undefined && + isExpression(styleValue) && + !representedProperties.has(styleProp.prop) + ) { + const exprData = pluckGetExpressionsOfType( + styleValue, + expressionFnName, + // @ts-ignore + styleSpec[ + (styleProp.type === "paint" ? "paint_" : "layout_") + layer.type + ][styleProp.prop].type + ); + if (exprData.facets.length) { + for (const facet of exprData.facets) { + const prop = facet.expression[1][1]; + representedProperties.addUsedFeatureProperty(prop); + for (const expression of facet.filters) { + findGetExpressionProperties(expression, (prop) => { + representedProperties.addUsedFeatureProperty(prop); + }); + } + const id = `${layers.indexOf(layer)}-${ + styleProp.prop + }-${exprData.facets.indexOf(facet)}-${expressionFnName}`; + // TODO: need to set featureProps to match the facet filters + const featureProps = propsForFilterExpressions(facet.filters); + const result = fn( + styleProp, + facet.expression, + layer, + representedProperties, + id, + featureProps, + layers + ); + if (result) { + panels.push({ + panel: result, + filters: + layer.filter && isExpression(layer.filter) + ? [layer.filter, ...facet.filters] + : facet.filters, + }); + } + } + if (exprData.remainingValues === null) { + pluckedLayers.push(layer); + } + representedProperties.commit(); + // pluck line layers with matching expressions that have been used + if (layer.type === "line" || layer.type === "fill") { + const relatedLayers = layers.filter((l) => + layer.type === "fill" ? l.type === "line" : l.type === "fill" + ); + for (const layer of relatedLayers) { + if (!pluckedLayers.includes(layer)) { + const paint = layer.paint || ({} as any); + for (const styleProp of layer.type === "line" + ? [ + "line-color", + "line-width", + "line-opacity", + "line-dasharray", + ] + : ["fill-color", "fill-opacity"]) { + for (const getProp of representedProperties.featurePropertiesUsed) { + if ( + hasGetExpressionForProperty(paint[styleProp], getProp) + ) { + pluckedLayers.push(layer); + break; + } + } + } + } + } + } + } + if (styleProp.type === "paint") { + // @ts-ignore + layer.paint[styleProp.prop] = exprData.remainingValues; + } else { + // @ts-ignore + layer.layout[styleProp.prop] = exprData.remainingValues; + } + } + } + } + } + // Look for unrelated get expressions in the plucked layers + // if none are present, remove the layer + for (const layer of pluckedLayers) { + let hasRemainingGetExpressions = false; + const paint = layer.paint || ({} as any); + for (const prop of SIGNIFICANT_PAINT_PROPS) { + if (prop in paint) { + const get = findGetExpression(paint[prop]); + if ( + get && + !representedProperties.featurePropertyWasUsed(get.property) + ) { + hasRemainingGetExpressions = true; + } + } + } + if (!hasRemainingGetExpressions) { + context.layers = context.layers.filter((l) => l !== layer); + } + } + return panels; +} + +export function pluckStepPanels(context: { layers: SeaSketchGlLayer[] }) { + return pluckLayersWithExpression( + context, + "step", + ( + paintProp, + expression, + layer, + representedProperties, + id, + featureProps, + sortedLayers + ) => { + const inputOutputPairs = expression.slice(3); + const prop = expression[1][1]; + const panel: GLLegendStepPanel = { + id, + type: "GLLegendStepPanel", + label: prop, + steps: [ + { + id: id + "-first", + label: "< " + inputOutputPairs[0], + symbol: createSymbol( + sortedLayers.indexOf(layer), + sortedLayers, + { + ...featureProps, + [prop]: inputOutputPairs[0] - 1, + }, + representedProperties + ), + }, + ...inputOutputPairs.reduce((steps, current, i) => { + if (i % 2 === 0) { + const input = current; + const output = inputOutputPairs[i + 1]; + if ( + output !== NULLIFIED_EXPRESSION_OUTPUT_NUMBER && + output !== NULLIFIED_EXPRESSION_OUTPUT_STRING + ) { + steps.push({ + id: id + "-" + i, + label: `${input.toLocaleString()}`, + symbol: createSymbol( + sortedLayers.indexOf(layer), + sortedLayers, + { + ...featureProps, + [prop]: input, + }, + representedProperties + ), + }); + } + } + return steps; + }, [] as { id: string; label: string; symbol: GLLegendSymbol }[]), + ], + }; + return panel; + } + ); +} + +export function pluckFilterPanels(context: { layers: SeaSketchGlLayer[] }) { + const panels: { panel: GLLegendSimpleSymbolPanel; filters: Expression[] }[] = + []; + const pluckedLayers: SeaSketchGlLayer[] = []; + const layers = [...context.layers].sort((a, b) => { + if (a.type === "fill" && b.type === "line") { + return -1; + } else if (a.type === "line" && b.type === "fill") { + return 1; + } else { + return 0; + } + }); + + for (const layer of layers) { + if (!pluckedLayers.includes(layer)) { + if (layer.filter && isExpression(layer.filter)) { + const labels = new Set(); + findGetExpressionProperties( + normalizeLegacyFilterExpression(layer.filter), + (prop) => { + labels.add(prop); + } + ); + const label = labels.size === 1 ? [...labels][0] : undefined; + // Special case to consider here when plucking line layers. If there are one + // or more filtered fill layers remaining, there is a state where none of + // those filters pass and the line will be rendered without fill. We need + // to detect those situations and include the default + if ( + context.layers.find((l) => l.type === "fill") && + context.layers.find( + (l) => l.type === "line" && l.filter === undefined + ) + ) { + // add the line as a default + panels.push({ + filters: [], + panel: { + type: "GLLegendSimpleSymbolPanel", + id: "default-line", + label, + items: [ + { + id: "default-line", + symbol: getSingleSymbolForVectorLayers( + context.layers.filter((l) => l.type === "line") + ), + label: "default", + }, + ], + }, + }); + } + + const panel: GLLegendSimpleSymbolPanel = { + // eslint-disable-next-line i18next/no-literal-string + id: `${layers.indexOf(layer)}-filter`, + type: "GLLegendSimpleSymbolPanel", + items: [], + label, + }; + let relatedLayers: SeaSketchGlLayer[] = []; + const featureProps = propsForFilterExpressions([ + normalizeLegacyFilterExpression(layer.filter), + ]); + if ((layer.type === "fill" || layer.type === "line") && layer.filter) { + for (const lyr of layers) { + if (layer !== lyr && (lyr.type === "line" || lyr.type === "fill")) { + if (lyr.filter) { + const filter = normalizeLegacyFilterExpression(lyr.filter); + // next see if the values which would satisfy fill layer's filters + // would also satisfy the line layer's filters + const featureProps = propsForFilterExpressions([ + normalizeLegacyFilterExpression(layer.filter), + ]); + if (evaluateFilter(filter, featureProps)) { + relatedLayers.push(lyr); + } + } else { + // if the line layer has no filter, it will be rendered + // when the fill layer is rendered, so we can include it + // in the legend + relatedLayers.push(lyr); + } + } + } + } + const symbol = getSingleSymbolForVectorLayers( + [layer, ...relatedLayers], + featureProps + ); + if (symbol) { + panel.items.push({ + id: panel.id + "-only", + label: + layer.metadata?.label || + labelForExpression( + normalizeLegacyFilterExpression(layer.filter), + false + ), + symbol, + }); + panels.push({ + panel, + // Can clear filters here, since this is the last step and there + // should be no sub-expressions + filters: [layer.filter], + }); + pluckedLayers.push(layer); + pluckedLayers.push(...relatedLayers); + } + } + } + } + context.layers = context.layers.filter((l) => !pluckedLayers.includes(l)); + return panels; +} + +export function pluckListPanelsFromMatchExpressions(context: { + layers: SeaSketchGlLayer[]; +}) { + return pluckLayersWithExpression( + context, + "match", + ( + paintProp, + expression, + layer, + representedProperties, + id, + featureProps, + sortedLayers + ) => { + const prop = expression[1][1]; + const defaultValue = expression[expression.length - 1]; + const inputOutputPairs = expression.slice(2, -1); + + const panel: GLLegendListPanel = { + id, + type: "GLLegendListPanel", + label: expression[1][1], + items: [], + }; + const valuesUsed: any = []; + for (var i = 0; i < inputOutputPairs.length; i += 2) { + const input = inputOutputPairs[i]; + const output = inputOutputPairs[i + 1]; + if ( + output !== NULLIFIED_EXPRESSION_OUTPUT_NUMBER && + output !== NULLIFIED_EXPRESSION_OUTPUT_STRING + ) { + valuesUsed.push(input); + panel.items.push({ + id: id + "-" + i, + label: `${input}`, + symbol: createSymbol( + sortedLayers.indexOf(layer), + sortedLayers, + { + ...featureProps, + [prop]: input, + }, + representedProperties + ), + }); + } + } + if (layer.type === "fill" || layer.type === "line") { + const valuesForFeatureProperty = collectValuesForFeatureProperty( + prop, + context.layers.filter( + (l) => (l !== layer && l.type === "fill") || l.type === "line" + ) + ); + for (const value of valuesForFeatureProperty) { + if (!valuesUsed.includes(value)) { + panel.items.push({ + id: id + "-" + value, + label: `${value}`, + symbol: createSymbol( + sortedLayers.indexOf(layer), + sortedLayers, + { + ...featureProps, + [prop]: value, + }, + representedProperties + ), + }); + valuesUsed.push(value); + } + } + } + if ( + defaultValue !== NULLIFIED_EXPRESSION_OUTPUT_NUMBER && + defaultValue !== NULLIFIED_EXPRESSION_OUTPUT_STRING + ) { + panel.items.push({ + id: id + "-default", + label: "default", + symbol: createSymbol( + sortedLayers.indexOf(layer), + sortedLayers, + { + ...featureProps, + }, + representedProperties + ), + }); + } + return panel; + } + ); +} + +export function pluckListPanelsForCaseAndFilterExpressions(context: { + layers: SeaSketchGlLayer[]; +}) { + // then handle case expressions, since these can be used to achieve the same + // type of results + const caseResults: { panel: GLLegendListPanel; filters: Expression[] }[] = []; + const representedProperties = new RepresentedProperties(); + const pluckedLayers: SeaSketchGlLayer[] = []; + const layers = [...context.layers].sort((a, b) => { + if (a.type === "fill" && b.type === "line") { + return -1; + } else if (a.type === "line" && b.type === "fill") { + return 1; + } else { + return 0; + } + }); + for (const layer of layers) { + if (!pluckedLayers.includes(layer)) { + representedProperties.reset(); + const paint = layer.paint || ({} as any); + const layout = layer.layout || ({} as any); + for (const styleProp of SIGNIFICANT_STYLE_PROPS) { + const styleValue = + styleProp.type === "paint" + ? paint[styleProp.prop] + : layout[styleProp.prop]; + if ( + styleValue && + isExpression(styleValue) && + !representedProperties.has(styleProp.prop) && + hasGetExpression(styleValue) && + styleValue[0] === "case" + ) { + const expression = styleValue; + + findGetExpressionProperties(expression, (prop) => { + representedProperties.addUsedFeatureProperty(prop); + }); + const fallback = expression[expression.length - 1]; + const inputOutputPairs = expression.slice(1, -1); + let isSimple = + [...representedProperties.featurePropertiesUsed].length === 1; + if (isSimple) { + for (var i = 0; i < inputOutputPairs.length; i += 2) { + const output = inputOutputPairs[i + 1]; + if (isExpression(output)) { + isSimple = false; + break; + } + } + } + if ([...representedProperties.featurePropertiesUsed].length === 0) { + // probably zoom-based case expression. Let the layer fall + // through to individual symbol handling + } else { + if (isSimple) { + const prop = [...representedProperties.featurePropertiesUsed][0]; + const panel: GLLegendListPanel = { + // eslint-disable-next-line i18next/no-literal-string + id: `${layers.indexOf(layer)}-${styleProp.prop}-case`, + type: "GLLegendListPanel", + label: prop, + items: [], + }; + const featureProps = propsForFilterExpressions( + layer.filter && isExpression(layer.filter) ? [layer.filter] : [] + ); + const valuesUsed: any = []; + for (var i = 0; i < inputOutputPairs.length; i += 2) { + const input = inputOutputPairs[i]; + const output = inputOutputPairs[i + 1]; + if ( + output !== NULLIFIED_EXPRESSION_OUTPUT_NUMBER && + output !== NULLIFIED_EXPRESSION_OUTPUT_STRING + ) { + const featureData = { + ...featureProps, + ...propsForFilterExpressions([input]), + }; + valuesUsed.push(featureData[prop]); + panel.items.push({ + id: panel.id + "-" + i, + label: labelForExpression(input), + symbol: createSymbol( + layers.indexOf(layer), + layers, + featureData, + representedProperties + ), + }); + } + } + + if (layer.type === "fill" || layer.type === "line") { + const valuesForFeatureProperty = + collectValuesForFeatureProperty( + prop, + context.layers.filter( + (l) => + (l !== layer && l.type === "fill") || l.type === "line" + ) + ); + for (const value of valuesForFeatureProperty) { + if (!valuesUsed.includes(value)) { + panel.items.push({ + id: panel.id + "-" + panel.items.length, + label: `${value}`, + symbol: createSymbol( + layers.indexOf(layer), + layers, + { + ...featureProps, + [prop]: value, + }, + representedProperties + ), + }); + valuesUsed.push(value); + } + } + } + + if ( + fallback !== NULLIFIED_EXPRESSION_OUTPUT_NUMBER && + fallback !== NULLIFIED_EXPRESSION_OUTPUT_STRING + ) { + panel.items.push({ + id: panel.id + "-default", + label: "default", + symbol: createSymbol( + layers.indexOf(layer), + layers, + { + ...featureProps, + [prop]: fallback, + }, + representedProperties + ), + }); + } + caseResults.push({ + panel, + filters: + layer.filter && isExpression(layer.filter) + ? [layer.filter] + : [], + }); + // Remove related line or fill layers + if (layer.type === "line" || layer.type === "fill") { + const relatedLayers = layers.filter((l) => + layer.type === "fill" ? l.type === "line" : l.type === "fill" + ); + for (const layer of relatedLayers) { + if (!pluckedLayers.includes(layer)) { + // pluckedLayers.push(layer); + const paint = layer.paint || ({} as any); + for (const styleProp of layer.type === "line" + ? [ + "line-color", + "line-width", + "line-opacity", + "line-dasharray", + ] + : ["fill-color", "fill-opacity"]) { + for (const getProp of representedProperties.featurePropertiesUsed) { + if ( + hasGetExpressionForProperty(paint[styleProp], getProp) + ) { + pluckedLayers.push(layer); + break; + } + } + } + } + } + } + } else { + // TODO: do we want/need to support this? + // console.warn("not simple", expression); + } + } + context.layers = context.layers.filter((l) => l !== layer); + } + } + } + } + context.layers = context.layers.filter((l) => !pluckedLayers.includes(l)); + return caseResults; +} + +function labelForExpression( + expression: Expression, + includePropertyName = false, + forFilterPanel = false +) { + const fnName = expression[0]; + if (fnName === "any" || fnName === "all") { + let isSimplish = true; + for (const e of expression[1] as Expression[]) { + if (e[0] === "any" || e[0] === "all") { + isSimplish = false; + break; + } + } + if (!isSimplish) { + return JSON.stringify(expression); + } + + const conditions = expression.slice(1); + const conditionFnNames = conditions.map((e: Expression) => e[0]); + + if ( + fnName === "all" && + conditionFnNames.length === 2 && + (conditionFnNames.includes("<") || conditionFnNames.includes("<=")) && + (conditionFnNames.includes(">") || conditionFnNames.includes(">=")) + ) { + // format as min - max + const minConditionIdx = conditionFnNames.findIndex( + (c) => c === ">" || c === ">=" + ); + const maxConditionIdx = conditionFnNames.findIndex( + (c) => c === "<" || c === "<=" + ); + const min = extractComparisonFromExpression(conditions[minConditionIdx]); + const max = extractComparisonFromExpression(conditions[maxConditionIdx]); + return `${min.toLocaleString()} - ${max.toLocaleString()}`; + } + + return conditions + .map((e: Expression) => stringifySimpleExpression(e, includePropertyName)) + .join(fnName === "all" ? ", " : " or "); + } else { + return stringifySimpleExpression(expression, includePropertyName); + } +} + +function extractComparisonFromExpression(expression: Expression) { + const [fnName, ...args] = expression; + if (fnName === "all" || fnName === "any") { + throw new Error( + `Can't extractComparisonFromExpression on all or any expressions` + ); + } + for (const arg of args) { + if (!isExpression(arg)) { + return arg; + } + } + throw new Error(`No comparison found in expression ${expression}`); +} + +function stringifySimpleExpression( + expression: Expression, + includePropertyName = false +) { + const getInfo = findGetExpression(expression); + let comparison: string | number = ""; + const fnName = expression[0]; + if (fnName === "all" || fnName === "any") { + throw new Error( + `all and any expressions are not supported by stringifySimpleExpression` + ); + } + if (isExpression(expression[1])) { + comparison = expression[2]; + } else { + comparison = expression[1]; + } + if (typeof comparison === "number") { + comparison = comparison.toLocaleString(); + } + const propLabel = includePropertyName ? getInfo?.property + " " || "" : ""; + switch (fnName) { + case "==": + return propLabel + comparison; + case "!=": + return `${propLabel}!= ${comparison}`; + case "!": + return `!${comparison}`; + default: + return `${propLabel}${fnName} ${comparison}`; + } +} + +/** + * While building the legend, it is important to keep track of what style props + * are already represented in symbols. A single dataset with complex styles + * may for example have one panel which represents circle color as a gradient of + * interpolated color, but then needs another panel to show how a categorical + * variable controls circle stroke color. In this case, the categorical panel + * should have an empty fill, since it is already represented in the gradient. + * + * Tracking this is difficult since we need to keep track of what props have + * been represented and then add to the list when new symbols are created. + * Instances of this class can be passed down to symbol creation functions so + * that they can add to the list of represented properties. + * + * The pending/commit system ensures that properties are only added to the + * list of represented properties after a full "panel" has been created. + */ +class RepresentedProperties { + private committed = new Set(); + private pending = new Set(); + private usedFeatureProperties = new Set(); + + has(prop: string) { + return this.committed.has(prop); + } + + add(prop: string, commit?: boolean) { + this.pending.add(prop); + if (commit) { + this.commit(); + } + } + + commit() { + this.pending.forEach((p) => this.committed.add(p)); + this.pending.clear(); + } + + reset() { + this.pending.clear(); + this.committed.clear(); + this.usedFeatureProperties.clear(); + } + + clearPending() { + this.pending.clear(); + } + + /** Always "committed" immediately */ + addUsedFeatureProperty(featurePropertyName: string) { + this.usedFeatureProperties.add(featurePropertyName); + } + + featurePropertyWasUsed(featurePropertyName: string) { + return this.usedFeatureProperties.has(featurePropertyName); + } + + get featurePropertiesUsed() { + return this.usedFeatureProperties.values(); + } +} + +// TODO: update to handle multiple source layers in the same tableOfContentsItem +// some day. + +function evaluateLayerProperty( + layer: SeaSketchGlLayer, + type: "layout" | "paint", + propName: string, + featureProps: any, + geometryType?: "Point" | "Polygon" | "LineString", + representedProperties?: RepresentedProperties +) { + const expression = + layer[type] && propName in layer[type]! + ? // @ts-ignore + (layer[type][propName] as number | Expression | string) + : undefined; + const defaultForProp = + // @ts-ignore + styleSpec[`${type}_${layer.type}`][propName]["default"]; + if (isExpression(expression)) { + geometryType = geometryType || "Point"; + const feature = { + type: "Feature", + properties: featureProps, + geometry: { + type: geometryType, + coordinates: [0, 0], + }, + } as Feature; + const value = ExpressionEvaluator.parse(expression).evaluate(feature); + if (representedProperties) { + const getProperties = findGetProperties(expression); + const anyMissing = getProperties.some((prop) => !(prop in featureProps)); + if (!anyMissing) { + representedProperties.add(propName); + } + } + if (isRGBA(value)) { + // eslint-disable-next-line i18next/no-literal-string + return `rgba(${value.r * 255},${value.g * 255},${value.b * 255},${ + value.a + })`; + } else { + return value; + } + } else { + representedProperties?.add(propName); + return expression || defaultForProp; + } +} + +function findGetProperties(expression: any, properties: string[] = []) { + if (isExpression(expression)) { + if (expression[0] === "get") { + const prop = expression[1]; + if (!properties.includes(prop)) { + properties.push(prop); + } + } else { + for (const arg of expression.slice(1)) { + findGetProperties(arg, properties); + } + } + } + return properties; +} + +function isRGBA(value: any): value is RGBA { + return typeof value === "object" && "r" in value; +} + +function hasMatchingStops(a: Stop[], b: Stop[]) { + if (a.length !== b.length) { + return false; + } + for (let i = 0; i < a.length; i++) { + // @ts-ignore + if (a[i].output !== b[i].output || a[i].input !== b[i].input) { + return false; + } + } + return true; +} + +/** + * Converts a mapbox-gl-style interpolation expression to a list of stops that can be used to create a legend. + * @param expression Mapbox gl style interpolation expression + * @returns Array<{value: number, color: string}> + */ +function interpolationExpressionToStops(expression?: any) { + expression = + expression && isExpression(expression) + ? expression + : [ + "interpolate", + ["linear"], + ["heatmap-density"], + 0, + "rgba(0, 0, 255, 0)", + 0.1, + "royalblue", + 0.3, + "cyan", + 0.5, + "lime", + 0.7, + "yellow", + 1, + "red", + ]; + const stops: { value: number; color: string; label: string }[] = []; + if (expression[0] === "interpolate") { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [interpolationType, property, ...args] = expression; + if (interpolationType === "interpolate") { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [input, ...stopPairs] = args; + for (let i = 0; i < stopPairs.length; i += 2) { + stops.push({ + value: stopPairs[i], + color: stopPairs[i + 1], + label: stopPairs[i].toLocaleString(), + }); + } + } + } + return stops; +} + +function getPaintProp( + paint: any, + propName: string, + featureData: { [propName: string]: any }, + representedProperties?: RepresentedProperties, + defaultIfAlreadyRepresented?: number | string | boolean +) { + if (representedProperties && representedProperties.has(propName)) { + return defaultIfAlreadyRepresented; + } + let type = propName.split("-")[0]; + if (type === "text" || type === "icon") { + type = "symbol"; + } + if (propName in paint) { + const value = paint[propName]; + if (isExpression(value)) { + if ( + representedProperties && + hasGetExpression(value, propName === "filter") + ) { + representedProperties.add(propName); + } + const fnName = value[0]; + featureData = featureData || {}; + // ExpressionEvaluator will complain over console-log if a number is + // not provided to step functions + if (fnName === "step") { + const prop = value[1][1]; + if (typeof prop === "string" && !(prop in featureData)) { + featureData[prop] = 0; + } + } + // evaluate expression using featureData + return ExpressionEvaluator.parse(value).evaluate({ + type: "Feature", + properties: featureData, + geometry: { + type: "Point", + coordinates: [1, 2], + }, + }); + } else { + return value; + } + } + // @ts-ignore + const defaultForProp = styleSpec["paint_" + type][propName]["default"]; + return defaultForProp || undefined; +} + +function getLayoutProp( + layout: any, + type: string, + propName: string, + featureData: { [propName: string]: any }, + representedProperties?: RepresentedProperties, + defaultIfAlreadyRepresented?: number | string +) { + if (representedProperties && isRepresented(propName, representedProperties)) { + return defaultIfAlreadyRepresented; + } + if (propName in layout) { + const value = layout[propName]; + if (isExpression(value)) { + // evaluate expression using featureData + return ExpressionEvaluator.parse(value).evaluate({ + type: "Feature", + properties: featureData || {}, + geometry: { + type: "Point", + coordinates: [1, 2], + }, + }); + } else { + return value; + } + } + // @ts-ignore + const defaultForProp = styleSpec["layout_" + type][propName]["default"]; + return defaultForProp || undefined; +} + +interface SignificantLiteralExpression { + layerIndex: number; + styleProp: string; + getProp: string; + usageType: "literal"; + rank: number; +} + +type Operator = "<" | "<=" | ">" | ">=" | "==" | "!=" | "!"; + +interface SignificantExpressionSimpleDomain { + type: "simple"; + operator: Operator; + getProp: string; + comparisonValue: number | boolean | string; + isFallback?: boolean; +} + +interface SignificantExpressionComplexDomain { + type: "complex"; + operator: "all" | "any"; + domains: SignificantExpressionSimpleDomain[]; +} + +type SignificantExpressionDomain = + | SignificantExpressionSimpleDomain + | SignificantExpressionComplexDomain; + +// @deprecated +// interface SignificantDecisionExpression { +// layerIndex: number; +// styleProp: string; +// getProp: string; +// usageType: "decision"; +// fnName: "case" | "match"; +// domains: SignificantExpressionSimpleDomain[]; +// rank: number; +// } + +interface SignificantMatchExpression { + layerIndex: number; + styleProp: string; + getProp: string; + usageType: "decision"; + fnName: "match"; + matches: { value: number | boolean | string; isFallback: boolean }[]; + rank: number; +} + +interface SignificantCaseExpression { + layerIndex: number; + styleProp: string; + usageType: "decision"; + fnName: "case"; + domains: SignificantExpressionDomain[]; + rank: number; +} + +type Stop = + | { input: number; output: string | number } + | { output: string | number; isDefault: true }; + +interface SignificantRampScaleOrCurveExpression { + layerIndex: number; + styleProp: string; + getProp: string; + usageType: "rampScaleOrCurve"; + fnName: "interpolate" | "interpolate-hcl" | "interpolate-lab" | "step"; + stops: Stop[]; + rank: number; + interpolationType?: "linear" | "exponential" | "log"; +} + +interface SignificantFilterExpression { + layerIndex: number; + styleProps: string[]; + getProp: string; + usageType: "filter"; + domains: SignificantExpressionDomain; + rank: number; + /* + * Assigned if the related layer has a `label` property in its metadata + * property. Used by @seasketch/mapbox-gl-esri-sources + */ + metadataDerivedLabel?: string; +} + +type SignificantExpression = + | SignificantLiteralExpression + | SignificantMatchExpression + | SignificantCaseExpression + | SignificantRampScaleOrCurveExpression + | SignificantFilterExpression; + +export type SeaSketchGlLayer = Omit; + +interface LegendContext { + globalContext: { + zoomRanges: { layerIndex: number; zoomRange: [number, number] }[]; + }; + significantExpressions: SignificantExpression[]; + includesHeatmap: boolean; +} + +interface ParentExpressionContext { + fnName: string; + expression: Expression; + parent?: ParentExpressionContext; +} + +function getUsageContextFromContext(parent?: ParentExpressionContext): + | { + usageType: "decision" | "rampScaleOrCurve" | "filter"; + parentExpression: Expression; + } + | { usageType: "literal" } { + if (!parent) { + return { usageType: "literal" }; + } else { + switch (parent.fnName) { + case "case": + case "match": + return { usageType: "decision", parentExpression: parent.expression }; + case "interpolate": + case "interpolate-hcl": + case "interpolate-lab": + case "step": + return { + usageType: "rampScaleOrCurve", + parentExpression: parent.expression, + }; + default: + return getUsageContextFromContext(parent.parent); + } + } +} + +function stopsFromInterpolation(expression: Expression) { + if (/interpolate/.test(expression[0])) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [fnName, interpolationType, input, ...inputOutputPairs] = expression; + const stops: Stop[] = []; + for (let i = 0; i < inputOutputPairs.length; i++) { + if (i % 2 === 0) { + stops.push({ + input: inputOutputPairs[i], + output: inputOutputPairs[i + 1], + }); + } + } + return stops; + } else { + throw new Error(`Expected interpolation expression. Got ${expression[0]}`); + } +} + +function stopsFromStepExpression(expression: Expression) { + if (expression[0] === "step") { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [fnName, input, ...inputOutputPairs] = expression; + const stops: Stop[] = []; + stops.push({ output: inputOutputPairs[0], isDefault: true }); + for (var i = 1; i < inputOutputPairs.length; i += 2) { + stops.push({ + input: inputOutputPairs[i].toLocaleString(), + output: inputOutputPairs[i + 1], + }); + } + return stops; + } else { + throw new Error(`Expected step expression. Got ${expression[0]}`); + } +} + +function parseExpression( + styleProp: string, + propertyValue: any, + layerIndex: number, + parent?: ParentExpressionContext +): SignificantExpression | null { + if (!isExpression(propertyValue)) { + return null; + } else if (propertyValue[0] === "get") { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [_get, getProp, defaultFallbackValue] = propertyValue; + // found get expression! + // first find the usage type + const context = getUsageContextFromContext(parent); + const rank = + SIGNIFICANT_PAINT_PROPS.indexOf(styleProp) > -1 + ? SIGNIFICANT_PAINT_PROPS.indexOf(styleProp) + : SIGNIFICANT_LAYOUT_PROPS.indexOf(styleProp); + const knownProps = { styleProp, rank, layerIndex }; + switch (context.usageType) { + case "literal": + return { + ...knownProps, + getProp, + usageType: context.usageType, + }; + case "rampScaleOrCurve": + const { usageType, parentExpression } = context; + const [fnName, ...args] = parentExpression; + switch (fnName) { + case "interpolate": + case "interpolate-hcl": + case "interpolate-lab": { + return { + ...knownProps, + usageType, + getProp, + fnName, + stops: stopsFromInterpolation(parentExpression), + interpolationType: + args.length > 0 && args[0].length > 0 ? args[0][0] : undefined, + }; + } + case "step": { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [input, firstOutput, ...inputOutputPairs] = args; + return { + ...knownProps, + usageType, + getProp, + fnName, + stops: stopsFromStepExpression(parentExpression), + }; + } + default: + throw new Error( + `Unknown ramp, scale or curve expression "${fnName}"` + ); + } + case "decision": + if (context.parentExpression[0] === "match") { + // Simpler of the two, enumerates entire domain + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [_match, input, ...inputOutputPairsAndFallback] = + context.parentExpression; + const inputOutputPairs = inputOutputPairsAndFallback.slice( + 0, + inputOutputPairsAndFallback.length - 1 + ); + const matches: { + value: string | boolean | number; + isFallback: boolean; + }[] = []; + for (let i = 0; i < inputOutputPairs.length; i++) { + if (i % 2 === 0) { + matches.push({ value: inputOutputPairs[i], isFallback: false }); + } + } + // Add default fallback value + const lastValueInDomain = matches[matches.length - 1]; + matches.push({ + value: + typeof lastValueInDomain.value === "number" + ? lastValueInDomain.value + 99999 + : "__glLegendDefaultValue", + isFallback: true, + }); + return { + ...knownProps, + usageType: context.usageType, + getProp, + fnName: "match", + matches, + }; + } else if (context.parentExpression[0] === "case") { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [_case, ...args] = context.parentExpression; + const conditionsAndOutputs = args.slice(0, args.length - 1); + const domains: SignificantExpressionSimpleDomain[] = []; + for (let i = 0; i < conditionsAndOutputs.length; i++) { + if (i % 2 === 0) { + const condition = conditionsAndOutputs[i]; + if (isExpression(condition)) { + const { isSimple, comparatorIndex } = isSimpleCondition( + condition, + getProp + ); + const [conditionFnName, ...conditionArgs] = condition; + if (isSimple) { + domains.push({ + type: "simple", + getProp, + operator: conditionFnName as Operator, + comparisonValue: + comparatorIndex !== undefined && comparatorIndex > -1 + ? conditionArgs[comparatorIndex!] + : null, + }); + } else { + // TODO: complex expression could be any or all. Support these. + // Note, all or any expressions could refer to more than one + // data property... + } + } + } + } + + const fallback = findFallbackForDomain(domains); + if (fallback) { + domains.push(fallback); + } + return { + ...knownProps, + usageType: context.usageType, + fnName: "case", + domains, + }; + } + } + } else { + // it's an expression, but we don't know what kind. Drill down into args + // looking for a get expression + const currentContext: ParentExpressionContext = { + fnName: propertyValue[0], + expression: propertyValue, + parent, + }; + for (const arg of propertyValue.slice(1)) { + const details = parseExpression( + styleProp, + arg, + layerIndex, + currentContext + ); + if (details !== null) { + return details; + } + } + } + return null; +} + +function findFallbackForDomain( + domains: SignificantExpressionSimpleDomain[] +): SignificantExpressionSimpleDomain | null { + if (domains.length === 0) { + return null; + } + const isNumeric = typeof domains[0].comparisonValue === "number"; + const isBoolean = typeof domains[0].comparisonValue === "boolean"; + if (!isNumeric && !isBoolean) { + // String value, easy + return { + operator: "==", + comparisonValue: "__glLegendDefaultValue", + isFallback: true, + getProp: domains[0].getProp, + type: "simple", + }; + } + + const possibleFallbacks: { + domain: SignificantExpressionSimpleDomain; + value: number; + }[] = []; + const excludedValues: (string | number | boolean)[] = []; + const includedValues: (string | number | boolean)[] = []; + for (const domain of domains) { + // This shouldn't be encountered, but just in case + if (domain.isFallback) { + return domain; + } else { + if (isNumeric) { + // numeric domain + if (domain.operator === "!=") { + excludedValues.push(domain.comparisonValue); + } else if (domain.operator === "==") { + includedValues.push(domain.comparisonValue); + } else if (domain.operator === "<") { + possibleFallbacks.push({ + domain, + value: domain.comparisonValue as number, + }); + } else if (domain.operator === "<=") { + possibleFallbacks.push({ + domain, + value: (domain.comparisonValue as number) + 0.001, + }); + } else if (domain.operator === ">") { + possibleFallbacks.push({ + domain, + value: domain.comparisonValue as number, + }); + } else if (domain.operator === ">=") { + possibleFallbacks.push({ + domain, + value: (domain.comparisonValue as number) - 0.001, + }); + } + } else if (isBoolean) { + // boolean domain + if (domain.operator === "!=") { + excludedValues.push(domain.comparisonValue); + } else if (domain.operator === "==") { + includedValues.push(domain.comparisonValue); + } else if (domain.operator === "!") { + includedValues.push(!domain.comparisonValue); + } + } + } + } + if (isBoolean) { + const allValues = [...excludedValues, ...includedValues]; + if (!allValues.includes(false)) { + return { + type: "simple", + operator: "==", + comparisonValue: false, + isFallback: true, + getProp: domains[0].getProp, + }; + } else if (!allValues.includes(true)) { + return { + type: "simple", + operator: "==", + comparisonValue: true, + isFallback: true, + getProp: domains[0].getProp, + }; + } else { + return null; + } + } else if (isNumeric) { + // First, see if any of the possible fallbacks are not in the included in + // any of the existing domains. + if (possibleFallbacks.length === 1 && domains.length === 1) { + return { + type: "simple", + operator: "==", + comparisonValue: possibleFallbacks[0].value, + isFallback: true, + getProp: domains[0].getProp, + }; + } + for (const fallback of possibleFallbacks) { + if (includedValues.includes(fallback.value)) { + break; + } + for (const domain of domains) { + if (domain === fallback.domain) { + // skip + continue; + } else { + if ( + domain.operator === "==" && + fallback.value === domain.comparisonValue + ) { + continue; + } else if ( + domain.operator === "<" && + fallback.value < (domain.comparisonValue as number) + ) { + continue; + } else if ( + domain.operator === "<=" && + fallback.value <= (domain.comparisonValue as number) + ) { + continue; + } else if ( + domain.operator === ">" && + fallback.value > (domain.comparisonValue as number) + ) { + continue; + } else if ( + domain.operator === ">=" && + fallback.value >= (domain.comparisonValue as number) + ) { + continue; + } else if ( + domain.operator === "!=" && + fallback.value !== (domain.comparisonValue as number) + ) { + continue; + } + } + // if you get to this point, the fallback is a good choice + return { + type: "simple", + operator: "==", + comparisonValue: fallback.value, + isFallback: true, + getProp: domains[0].getProp, + }; + } + } + // If none of those work, find the max of included and excluded values + // and add 1 + const allValues = [...includedValues, ...excludedValues] as number[]; + if (allValues.length) { + const max = Math.max(...allValues); + return { + type: "simple", + operator: "==", + comparisonValue: max + 1, + isFallback: true, + getProp: domains[0].getProp, + }; + } + } + return null; +} + +/** + * Returns whether the given condition is a simple condition that directly + * compares the named feature property to a literal value. Simple type + * conversion like to-number can wrap property access but nothing else. + * @param condition + * @param featurePropertyName + */ +function isSimpleCondition(condition: Expression, featurePropertyName: string) { + const [fnName, ...args] = condition; + if (["==", "!=", "<", "<=", ">", ">=", "!"].includes(fnName)) { + const getArgIndex = args.findIndex((arg) => isSimpleGetArg(arg)); + if (getArgIndex === -1) { + return { isSimple: false }; + } else { + // Special case with no comparator + if (fnName === "!") { + return { isSimple: true, comparatorIndex: -1 }; + } + const comparatorIndex = getArgIndex === 0 ? 1 : 0; + if (isExpression(args[comparatorIndex])) { + // Unexpected situation. + return { isSimple: false }; + } else { + return { isSimple: true, comparatorIndex }; + } + } + } else { + return { isSimple: false }; + } +} + +function isSimpleGetArg(arg: any): boolean { + if (isExpression(arg)) { + const [fnName, ...args] = arg; + if (fnName === "get") { + return true; + } else if (/to-/.test(fnName)) { + return isSimpleGetArg(args[0]); + } + } + return false; +} + +function getSingleSymbolForVectorLayers( + layers: SeaSketchGlLayer[], + featureProps = {} as { [propName: string]: any } +): GLLegendSymbol { + // Last layer in the array is the top-most layer, so it should be the primary + layers = [...layers].reverse(); + // determine primary symbol type + const fillLayer = layers.find( + (layer) => + (layer.type === "fill" || layer.type === "fill-extrusion") && + layerIsVisible(layer, featureProps) + ) as FillLayer | FillExtrusionLayer | undefined; + if (fillLayer && layerIsVisible(fillLayer)) { + return createFillSymbol( + fillLayer, + layers.filter((l) => l !== fillLayer), + featureProps + ); + } + const lineLayer = layers.find((layer) => layer.type === "line") as LineLayer; + if (lineLayer && layerIsVisible(lineLayer)) { + return createLineSymbol( + lineLayer, + layers.filter((l) => l !== lineLayer), + featureProps + ); + } + const circleLayer = layers.find((layer) => layer.type === "circle"); + if (circleLayer && layerIsVisible(circleLayer)) { + return createCircleSymbol( + circleLayer, + layers.filter((l) => l !== circleLayer), + featureProps + ); + } + const symbolLayer = layers.find((layer) => layer.type === "symbol"); + if (symbolLayer && layerIsVisible(symbolLayer)) { + const layout = (symbolLayer.layout || {}) as any; + const iconImage = layout["icon-image"]; + if (iconImage) { + return createMarkerSymbol( + symbolLayer, + layers.filter((l) => l !== symbolLayer), + featureProps + ); + } else if (layout["text-field"]) { + return createTextSymbol( + symbolLayer, + layers.filter((l) => l !== symbolLayer), + featureProps + ); + } + } + throw new Error("Not implemented"); +} + +function layerIsVisible( + layer: SeaSketchGlLayer, + featureData?: { [propName: string]: any } +) { + const isVisible = + !layer.layout?.visibility || layer.layout.visibility !== "none"; + const filterPasses = + !layer.filter || + featureData === undefined || + passesFilter(layer.filter, featureData); + return isVisible && filterPasses; +} + +function createMarkerSymbol( + symbolLayer: SeaSketchGlLayer, + otherLayers: SeaSketchGlLayer[], + featureData: { [propName: string]: any }, + representedProperties?: RepresentedProperties +): GLLegendMarkerSymbol { + const paint = (symbolLayer.paint || {}) as any; + const layout = (symbolLayer.layout || {}) as any; + const iconImage = getLayoutProp( + layout, + "symbol", + "icon-image", + featureData, + representedProperties + ); + const iconSize = getLayoutProp( + layout, + "symbol", + "icon-size", + featureData, + representedProperties, + 1 + ); + const rotation = getLayoutProp( + layout, + "symbol", + "icon-rotate", + featureData, + representedProperties, + 0 + ); + const haloColor = getPaintProp( + paint, + "text-halo-color", + featureData, + representedProperties, + "transparent" + ); + const haloWidth = getPaintProp( + paint, + "text-halo-width", + featureData, + representedProperties, + 0 + ); + + return { + type: "marker", + imageId: iconImage, + haloColor, + haloWidth, + rotation, + iconSize, + }; +} + +function createTextSymbol( + symbolLayer: SeaSketchGlLayer, + otherLayers: SeaSketchGlLayer[], + featureData: { [propName: string]: any }, + representedProperties?: RepresentedProperties +): GLLegendTextSymbol { + const paint = (symbolLayer.paint || {}) as any; + const layout = (symbolLayer.layout || {}) as any; + const color = getPaintProp( + paint, + "text-color", + featureData, + representedProperties, + "#000" + ); + const fontFamily = getLayoutProp( + layout, + "symbol", + "text-font", + featureData, + representedProperties, + "Open Sans Regular" + ); + const haloColor = getPaintProp( + paint, + "text-halo-color", + featureData, + representedProperties, + "transparent" + ); + const haloWidth = getPaintProp( + paint, + "text-halo-width", + featureData, + representedProperties, + 0 + ); + return { + type: "text", + color, + fontFamily: Array.isArray(fontFamily) ? fontFamily[0] : fontFamily, + fontWeight: /bold/.test(fontFamily) ? "bold" : "normal", + fontStyle: /italic/.test(fontFamily) ? "italic" : "normal", + haloColor, + haloWidth, + }; +} +function createLineSymbol( + lineLayer: SeaSketchGlLayer, + otherLayers: SeaSketchGlLayer[], + featureData: { [propName: string]: any }, + representedProperties?: RepresentedProperties +): GLLegendLineSymbol { + const paint = (lineLayer.paint || {}) as any; + const color = getPaintProp( + paint, + "line-color", + featureData, + representedProperties, + "#000" + ); + const opacity = getPaintProp( + paint, + "line-opacity", + featureData, + representedProperties, + 1 + ); + const width = getPaintProp( + paint, + "line-width", + featureData, + representedProperties, + 1 + ); + const dasharray = getPaintProp( + paint, + "line-dasharray", + featureData, + representedProperties, + false + ); + return { + type: "line", + color, + opacity, + strokeWidth: width, + dashed: dasharray, + }; +} + +function createCircleSymbol( + circleLayer: SeaSketchGlLayer, + otherLayers: SeaSketchGlLayer[], + featureData: { [propName: string]: any }, + representedProperties?: RepresentedProperties +): GLLegendCircleSymbol { + const paint = (circleLayer.paint || {}) as any; + const fillOpacity = getPaintProp( + paint, + "circle-opacity", + featureData, + representedProperties, + 1 + ); + const strokeWidth = getPaintProp( + paint, + "circle-stroke-width", + featureData, + representedProperties, + 1 + ); + const strokeColor = getPaintProp( + paint, + "circle-stroke-color", + featureData, + representedProperties, + "#000" + ); + const strokeOpacity = getPaintProp( + paint, + "circle-stroke-opacity", + featureData, + representedProperties, + 1 + ); + const color = getPaintProp( + paint, + "circle-color", + featureData, + representedProperties, + "transparent" + ); + + const radius = getPaintProp( + paint, + "circle-radius", + featureData, + representedProperties, + 5 + ); + return { + radius, + type: "circle", + color, + fillOpacity, + strokeWidth, + strokeColor, + strokeOpacity, + }; +} + +function isRepresented( + styleProp: string, + representedProperties?: RepresentedProperties +) { + if (!representedProperties) { + return false; + } else { + return representedProperties.has(styleProp); + } +} + +function idForPanel(expression: SignificantExpression) { + return `${expression.layerIndex}-${ + "styleProp" in expression + ? expression.styleProp + : expression.styleProps.join("-") + }`; +} + +function createSymbol( + layerIndex: number, + layers: SeaSketchGlLayer[], + featureProps: { [key: string]: any }, + representedProperties: RepresentedProperties +) { + const primaryLayer = layers[layerIndex]; + if (!primaryLayer) { + throw new Error("No primary layer: " + layerIndex); + } + const otherLayers = layers.filter((l) => l !== primaryLayer); + switch (primaryLayer.type) { + case "fill": + case "fill-extrusion": + return createFillSymbol( + primaryLayer, + otherLayers, + featureProps, + representedProperties + ); + case "line": + const visibleFillLayer = otherLayers.find( + (l) => l.type === "fill" && layerIsVisible(l, featureProps) + ); + if (visibleFillLayer) { + return createFillSymbol( + visibleFillLayer, + layers.filter((l) => l !== visibleFillLayer), + featureProps, + representedProperties, + false + ); + } else { + return createLineSymbol( + primaryLayer, + otherLayers, + featureProps, + representedProperties + ); + } + case "circle": + return createCircleSymbol( + primaryLayer, + otherLayers, + featureProps, + representedProperties + ); + case "symbol": + const layout = (primaryLayer.layout || {}) as any; + const iconImage = layout["icon-image"]; + if (iconImage) { + return createMarkerSymbol(primaryLayer, otherLayers, featureProps); + } + const textField = layout["text-field"]; + if (textField) { + return createTextSymbol( + primaryLayer, + otherLayers, + featureProps, + representedProperties + ); + } + throw new Error("Not implemented"); + default: + throw new Error("Not implemented"); + } +} + +function createFillSymbol( + fillLayer: SeaSketchGlLayer, + otherLayers: SeaSketchGlLayer[], + featureData: { [propName: string]: any }, + representedProperties?: RepresentedProperties, + skipFill = false +): GLLegendFillSymbol { + const paint = (fillLayer.paint || {}) as any; + const extruded = fillLayer.type === "fill-extrusion"; + const opacity = extruded + ? getPaintProp( + paint, + "fill-extrusion-opacity", + featureData, + representedProperties, + 1 + ) + : getPaintProp( + paint, + "fill-opacity", + featureData, + representedProperties, + 1 + ); + let strokeWidth = paint["fill-outline-color"] ? 1 : 0; + let strokeColor = getPaintProp( + paint, + "fill-outline-color", + featureData, + representedProperties, + "transparent" + ); + const lineLayers = otherLayers.filter( + (layer) => layer.type === "line" && passesFilter(layer.filter, featureData) + ) as Layer[]; + let dashedLine = false; + let strokeOpacity: number | undefined; + for (const layer of lineLayers) { + const linePaint = (layer.paint || {}) as any; + strokeWidth = getPaintProp( + linePaint, + "line-width", + featureData, + representedProperties, + 1 + ); + strokeColor = getPaintProp( + linePaint, + "line-color", + featureData, + representedProperties, + "#000" + ); + dashedLine = getPaintProp( + linePaint, + "line-dasharray", + featureData, + representedProperties, + false + ); + strokeOpacity = getPaintProp( + linePaint, + "line-opacity", + featureData, + representedProperties, + 1 + ); + } + const color = extruded + ? getPaintProp( + paint, + "fill-extrusion-color", + featureData, + representedProperties, + "rgba(0,0,0,0.2)" + ) + : getPaintProp( + paint, + "fill-color", + featureData, + representedProperties, + "rgba(0,0,0,0.2)" + ); + return { + type: "fill", + color: skipFill ? "transparent" : color, + extruded: fillLayer.type === "fill-extrusion" ? true : false, + fillOpacity: opacity, + strokeWidth, + dashed: dashedLine, + strokeColor, + strokeOpacity, + patternImageId: extruded + ? getPaintProp( + paint, + "fill-extrusion-pattern", + featureData, + representedProperties, + undefined + ) + : getPaintProp( + paint, + "fill-pattern", + featureData, + representedProperties, + undefined + ), + }; +} + +function passesFilter(filter: any, featureData: { [propName: string]: any }) { + if (!filter) { + return true; + } else { + return evaluateFilter(filter, featureData); + } +} + +function evaluateFilter( + filter: Expression, + featureData: { [propName: string]: any } +) { + filter = normalizeLegacyFilterExpression(filter); + return ( + ExpressionEvaluator.parse(filter).evaluate({ + type: "Feature", + properties: featureData, + geometry: { + type: "Point", + coordinates: [1, 2], + }, + }) === true + ); +} + +function findGetExpressionProperties( + expression: any, + callback: (propName: string) => void +) { + if (isExpression(expression)) { + const [fnName, ...args] = expression; + if (fnName === "get") { + callback(args[0]); + } else { + for (const arg of args) { + findGetExpressionProperties(arg, callback); + } + } + } +} + +function normalizeLegacyFilterExpression(expression: any) { + if (isExpression(expression) && !hasGetExpression(expression)) { + if (typeof expression[1] === "string" && expression[1][0] !== "$") { + const newExpression = [...expression]; + newExpression[1] = ["get", expression[1]]; + return newExpression; + } else if (expression[0] === "all" || expression[0] === "any") { + const newExpression = [...expression]; + var i = 1; + while (expression[i]) { + newExpression[i] = normalizeLegacyFilterExpression(expression[i]); + i++; + } + return newExpression; + } + } + return expression; +} + +function collectValuesForFeatureProperty( + propName: string, + layers: SeaSketchGlLayer[] +) { + const values: (string | number | boolean)[] = []; + for (const layer of layers) { + const paint = layer.paint || ({} as any); + const layout = layer.layout || ({} as any); + for (const styleProp of SIGNIFICANT_STYLE_PROPS) { + const styleValue = ( + styleProp.type === "paint" + ? paint[styleProp.prop] + : layout[styleProp.prop] + ) as any; + if (hasGetExpressionForProperty(styleValue, propName)) { + forEachMatch(styleValue as Expression, (match) => { + if (match[1][1] === propName) { + const inputOutputPairs = match.slice(2, match.length - 1); + for (var i = 0; i < inputOutputPairs.length; i += 2) { + values.push(inputOutputPairs[0]); + } + } + }); + forEachStep(styleValue as Expression, (step) => { + if (step[1][1] === propName) { + const inputOutputPairs = step.slice(3); + for (var i = 0; i < inputOutputPairs.length; i += 2) { + values.push(inputOutputPairs[0]); + } + } + }); + forEachTerminatingCaseBranch(styleValue as Expression, (condition) => { + // find the get prop in the condition + if (hasGetExpressionForProperty(condition, propName)) { + const value = propsForFilterExpressions([condition])[propName]; + if (values.indexOf(value) === -1) { + values.push(value); + } + } + }); + } + } + } + return values; +} + +function forEachMatch( + expression: Expression, + fn: (match: Expression, filters: Expression[]) => void, + filters = [] as Expression[] +) { + if (expression[0] === "match") { + fn(expression, filters); + } else if (expression[0] === "case") { + const fallback = expression[expression.length - 1]; + const conditionsAndOutputs = expression.slice(1, expression.length - 1); + for (var i = 0; i < conditionsAndOutputs.length; i += 2) { + const condition = conditionsAndOutputs[i]; + const output = conditionsAndOutputs[i + 1]; + forEachMatch(output, fn, [...filters, condition]); + } + forEachMatch(fallback, fn, filters); + } +} + +function forEachStep( + expression: Expression, + fn: (step: Expression, filters: Expression[]) => void, + filters = [] as Expression[] +) { + if (expression[0] === "step") { + fn(expression, filters); + } else if (expression[0] === "case") { + const fallback = expression[expression.length - 1]; + const conditionsAndOutputs = expression.slice(1, expression.length - 1); + for (var i = 0; i < conditionsAndOutputs.length; i += 2) { + const condition = conditionsAndOutputs[i]; + const output = conditionsAndOutputs[i + 1]; + forEachStep(output, fn, [...filters, condition]); + } + forEachStep(fallback, fn, filters); + } +} + +function forEachTerminatingCaseBranch( + expression: Expression, + fn: (condition: Expression, output: any, filters: Expression[]) => void, + filters = [] as Expression[] +) { + if (expression[0] === "case") { + const conditionsAndOutputs = expression.slice(1, expression.length - 1); + for (var i = 0; i < conditionsAndOutputs.length; i += 2) { + const condition = conditionsAndOutputs[i]; + const output = conditionsAndOutputs[i + 1]; + if (isExpression(output) && !/^to-/.test(output[0])) { + if (output[0] === "case") { + forEachTerminatingCaseBranch(output, fn, [...filters, condition]); + } + } else { + fn(condition, output, filters); + } + } + } +} + +function transformLineSymbolsToFill(panels: GLLegendPanel[]) { + for (const panel of panels) { + switch (panel.type) { + case "GLLegendListPanel": + case "GLLegendStepPanel": + case "GLLegendSimpleSymbolPanel": + const items = + panel.type === "GLLegendStepPanel" ? panel.steps : panel.items; + const replacements = items.map((item) => { + let original = item.symbol; + return { + ...item, + symbol: + original.type === "line" + ? { + type: "fill", + color: "transparent", + fillOpacity: 0, + strokeWidth: original.strokeWidth, + strokeColor: original.color, + dashed: original.dashed, + extruded: false, + strokeOpacity: original.opacity, + } + : original, + }; + }); + if ( + panel.type === "GLLegendListPanel" || + panel.type === "GLLegendSimpleSymbolPanel" + ) { + panel.items = replacements as any; + } else { + panel.steps = replacements as any; + } + break; + } + } +} + +export interface GroupByFilterNode { + filters: Expression[]; + children: (GroupByFilterNode | GLLegendPanel)[]; +} + +export function isGroupByFilterNode( + node: GroupByFilterNode | GLLegendPanel +): node is GroupByFilterNode { + return "filters" in node && "children" in node; +} + +export interface PanelItem { + panel: GLLegendPanel; + filters: Expression[]; +} + +/** + * Groups the given panels by the filters that are applied to them. + * Common filters are grouped together, and panels that have no filters + * are attached to the root node. + * + */ +export function groupByFilters(items: PanelItem[]): GroupByFilterNode { + const root: GroupByFilterNode = { + filters: [], + children: [], + }; + // explode ALL expressions + items = [...items].map((item) => { + const filters: Expression[] = []; + for (const filter of item.filters) { + if (filter[0] === "all") { + filters.push(...filter.slice(1)); + } else { + filters.push(filter); + } + } + return { + ...item, + filters, + }; + }); + + populateNode(root, items); + // some branches might be something like + // filterNode(filter=a) -> filterNode(filter=b) -> panels + // consolidate into filterNode(filter=a,b) -> panels + consolidateFilterNodesWithNoPanels(root); + return root; +} + +function consolidateFilterNodesWithNoPanels(node: GroupByFilterNode) { + const newChildren: (GLLegendPanel | GroupByFilterNode)[] = []; + for (const child of node.children) { + if (isGroupByFilterNode(child)) { + if ( + child.children.length === 1 && + isGroupByFilterNode(child.children[0]) + ) { + // promote the child's children to this node + newChildren.push(child.children[0]); + child.children[0].filters = [ + ...child.filters, + ...child.children[0].filters, + ]; + consolidateFilterNodesWithNoPanels(child.children[0]); + } else { + newChildren.push(child); + consolidateFilterNodesWithNoPanels(child); + } + } else { + newChildren.push(child); + } + node.children = newChildren; + } +} + +function populateNode(node: GroupByFilterNode, items: PanelItem[]) { + const groupByExpression: { [expression: string]: PanelItem[] } = {}; + // add items that match the filter of the above node (exactly) + for (const item of items) { + let matches = true; + for (const filter of item.filters) { + if ( + !node.filters.find((f) => JSON.stringify(f) === JSON.stringify(filter)) + ) { + matches = false; + break; + } + } + if (matches) { + node.children.push(item.panel); + } + } + items = items.filter((i) => !node.children.includes(i.panel)); + if (items.length === 0) { + return; + } + // cull "represented" filters from remaining items since they are nested under + // a filtered node + for (const item of items) { + item.filters = item.filters.filter( + (f) => + !node.filters.find((f2) => JSON.stringify(f2) === JSON.stringify(f)) + ); + } + let iterationLimit = 999; + for (const item of items) { + for (const filter of item.filters) { + const stringified = JSON.stringify(filter); + if (!groupByExpression[stringified]) { + groupByExpression[stringified] = []; + } + groupByExpression[stringified].push(item); + } + } + while (Object.keys(groupByExpression).length) { + iterationLimit--; + if (iterationLimit < 0) { + throw new Error("Iteration limit exceeded"); + } + // find the most common expression + let mostCommonExpression: string | undefined; + for (const key in groupByExpression) { + if ( + !mostCommonExpression || + groupByExpression[key].length > + groupByExpression[mostCommonExpression].length + ) { + mostCommonExpression = key; + } + } + if (!mostCommonExpression) { + throw new Error("No most common expression"); + } + const child: GroupByFilterNode = { + filters: [JSON.parse(mostCommonExpression)], + children: [], + }; + node.children.push(child); + populateNode(child, groupByExpression[mostCommonExpression]); + for (const item of groupByExpression[mostCommonExpression]) { + for (const key in groupByExpression) { + groupByExpression[key] = groupByExpression[key].filter( + (i) => i !== item + ); + if (groupByExpression[key].length === 0) { + delete groupByExpression[key]; + } + } + } + delete groupByExpression[mostCommonExpression]; + } +} + +function consolidatePanels( + items: { + panel: GLLegendPanel; + filters: Expression[]; + }[] +): GLLegendPanel[] { + // first, ensure all legacy filters are converted to get expressions so that + // related filters match + for (const item of items) { + item.filters = item.filters.map((filter) => + normalizeLegacyFilterExpression(filter) + ); + } + // Merge list panels with matching filters and labels + const grouped = groupByFilters(items); + consolidateNode(grouped); + const rootFilterPanel = consolidateFilterPanel(grouped); + + // At each "level", perform a consolidation of panels + return rootFilterPanel.children; +} + +function consolidateNode(node: GroupByFilterNode) { + // repeat recursively for all sub-nodes + // promote subNodes with single simple symbol panels which have matching + // get expression target properties into a single list panel + const listPanels: { [key: string]: GLLegendListPanel } = {}; + for (const subNode of node.children.filter(isGroupByFilterNode)) { + if ( + subNode.children.length === 1 && + !isGroupByFilterNode(subNode.children[0]) && + subNode.children[0].type === "GLLegendSimpleSymbolPanel" + ) { + const getProps = new Set(); + subNode.filters.forEach((f) => { + findGetExpressionProperties(f, (propName) => { + getProps.add(propName); + }); + }); + if (getProps.size === 1) { + const propName = getProps.values().next().value; + const panel = subNode.children[0] as GLLegendSimpleSymbolPanel; + const key = `${propName}%%${panel.items[0].symbol.type}`; + if (!listPanels[key]) { + listPanels[key] = { + id: key, + type: "GLLegendListPanel", + label: propName, + items: [], + }; + } + const list = listPanels[key]; + let filterPropNames = new Set(); + for (const filter of subNode.filters) { + findGetExpressionProperties(filter, (propName) => { + filterPropNames.add(propName); + }); + } + const filterPropRepresentedInPanelLabel = + filterPropNames.size === 1 && filterPropNames.has(panel.label || ""); + list.items.push({ + id: panel.items[0].id, + label: + panel.items[0].label || + subNode.filters + .map((f) => + labelForExpression(f, !filterPropRepresentedInPanelLabel) + ) + .join(" && "), + symbol: panel.items[0].symbol, + }); + node.children = node.children.filter((c) => c !== subNode); + } + } + } + node.children.push(...Object.values(listPanels)); + + // Do another pass, looking for nodes with a single get filter prop that + // matches their child panel labels. If they match, delete the filter and + // promote the node + for (const child of node.children.filter(isGroupByFilterNode)) { + const filterProps = new Set(); + child.filters.forEach((f) => { + findGetExpressionProperties(f, (propName) => { + filterProps.add(propName); + }); + }); + if (filterProps.size === 1) { + const propName = filterProps.values().next().value; + for (const panel of child.children) { + if (!isGroupByFilterNode(panel)) { + if ( + (panel.type === "GLLegendListPanel" || + panel.type === "GLLegendSimpleSymbolPanel") && + panel.label === propName + ) { + // promote to node + node.children.push(panel); + child.children = child.children.filter((c) => c !== panel); + } + } + } + if (child.children.length === 0) { + node.children = node.children.filter((c) => c !== child); + } + } + } + + // Finally, merge all list and simple panels with matching labels and symbol + // types + const merged: { [key: string]: GLLegendListPanel } = {}; + const plucked: GLLegendPanel[] = []; + for (const panel of node.children) { + if (!isGroupByFilterNode(panel)) { + if ( + (panel.type === "GLLegendListPanel" || + panel.type === "GLLegendSimpleSymbolPanel") && + panel.items.length > 0 + ) { + const key = `${panel.label}`; + if (!merged[key]) { + merged[key] = { + id: panel.id, + type: "GLLegendListPanel", + label: panel.label, + items: [], + }; + } + const list = merged[key]; + list.items.push( + ...panel.items.map((item) => ({ + ...item, + label: item.label || panel.label || "", + })) + ); + plucked.push(panel); + } + } + } + + node.children = node.children.filter( + (n) => isGroupByFilterNode(n) || !plucked.includes(n) + ); + for (const item of Object.values(merged)) { + node.children.push(item); + } + + for (const subNode of node.children.filter(isGroupByFilterNode)) { + consolidateNode(subNode); + } +} + +function consolidateFilterPanel(node: GroupByFilterNode) { + const filterPanel: GLLegendFilterPanel = { + id: node.filters.length === 0 ? "root" : JSON.stringify(node.filters), + type: "GLLegendFilterPanel", + label: node.filters + .map((f) => labelForExpression(f, true, true)) + .join(", "), + children: [], + }; + const panels: GLLegendPanel[] = []; + for (const nodeOrPanel of node.children) { + if (isGroupByFilterNode(nodeOrPanel)) { + filterPanel.children.push(consolidateFilterPanel(nodeOrPanel)); + } else { + panels.push(nodeOrPanel); + } + } + + filterPanel.children.push(...panels); + return filterPanel; +} diff --git a/packages/client/src/dataLayers/legends/glLegendPanels.test.ts b/packages/client/src/dataLayers/legends/glLegendPanels.test.ts new file mode 100644 index 000000000..f05976422 --- /dev/null +++ b/packages/client/src/dataLayers/legends/glLegendPanels.test.ts @@ -0,0 +1,2149 @@ +import { Expression } from "mapbox-gl"; +import { + GLLegendCircleSymbol, + GLLegendFillSymbol, + GLLegendListPanel, + MultipleSymbolLegendForGLLayers, +} from "./LegendDataModel"; +import { + compileLegendFromGLStyleLayers, + GroupByFilterNode, + groupByFilters, + isGroupByFilterNode, + PanelItem, + pluckBubblePanels, + pluckFilterPanels, + pluckGradientPanels, + pluckHeatmapPanels, + pluckListPanelsForCaseAndFilterExpressions, + pluckListPanelsFromMatchExpressions, + pluckStepPanels, + SeaSketchGlLayer, +} from "./compileLegend"; +import { testCases } from "./legendKitchenSinkTestCases"; + +beforeEach(() => { + // supress console.warn from expression evaluator + jest.spyOn(console, "warn").mockImplementation(() => {}); +}); + +describe("bubble charts", () => { + test("Simple bubble chart", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "circle", + paint: { + "circle-radius": [ + "interpolate", + ["linear"], + ["get", "population"], + 100000, + 5, + 10000000, + 50, + ], + }, + }, + ], + }; + const output = pluckBubblePanels(context); + + expect(output.length).toBe(1); + const { filters, panel } = output[0]; + const stops = panel.stops; + expect(stops[0].radius).toBe(5); + expect(stops[1].radius).toBe(55 / 2); + expect(stops[2].radius).toBe(50); + expect(filters.length).toBe(0); + expect(panel.label).toBe("population"); + const firstStop = stops[0]; + expect(firstStop.value).toBe(100000); + expect(firstStop.radius).toBe(5); + expect(firstStop.strokeWidth).toBe(0); + expect(firstStop.fill).toBe("#000000"); + expect(firstStop.stroke).toBe("#000000"); + expect(firstStop.fillOpacity).toBe(1); + + // layer is removed since it's been fully represented by the bubble chart + expect(context.layers.length).toBe(0); + }); + + test("Stops are limited to 3", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "circle", + paint: { + "circle-radius": [ + "interpolate", + ["linear"], + ["get", "population"], + 100000, + 5, + 10000000, + 50, + 20000000, + 100, + 30000000, + 150, + 40000000, + 200, + ], + }, + }, + ], + }; + const output = pluckBubblePanels(context); + expect(output.length).toBe(1); + const { panel } = output[0]; + const stopValues = panel.stops.map((s) => s.radius); + expect(stopValues).toEqual([5, 100, 200]); + }); + + test("Bubble chart with static stroke style", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "circle", + paint: { + "circle-radius": [ + "interpolate", + ["linear"], + ["get", "population"], + 100000, + 5, + 10000000, + 50, + ], + "circle-stroke-color": "red", + "circle-stroke-width": 1, + }, + }, + ], + }; + const output = pluckBubblePanels(context); + + expect(output.length).toBe(1); + const { filters, panel } = output[0]; + const stops = panel.stops; + expect(filters.length).toBe(0); + expect(panel.label).toBe("population"); + const firstStop = stops[0]; + expect(firstStop.value).toBe(100000); + expect(firstStop.radius).toBe(5); + expect(firstStop.strokeWidth).toBe(1); + expect(firstStop.fill).toBe("#000000"); + expect(firstStop.stroke).toBe("red"); + expect(firstStop.fillOpacity).toBe(1); + + // layer is removed since it's been fully represented by the bubble chart + expect(context.layers.length).toBe(0); + }); + + test("Bubble chart with matching interpolated stroke style", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "circle", + paint: { + "circle-radius": [ + "interpolate", + ["linear"], + ["get", "population"], + 100000, + 5, + 10000000, + 50, + ], + "circle-color": [ + "interpolate-hcl", + ["linear"], + ["get", "population"], + 100000, + "rgb(0,0,255)", + 10000000, + "rgb(255,0,0)", + ], + "circle-stroke-width": 1, + }, + }, + ], + }; + const output = pluckBubblePanels(context); + + expect(output.length).toBe(1); + const { filters, panel } = output[0]; + const stops = panel.stops; + expect(filters.length).toBe(0); + expect(panel.label).toBe("population"); + const firstStop = stops[0]; + expect(firstStop.value).toBe(100000); + expect(firstStop.radius).toBe(5); + expect(firstStop.strokeWidth).toBe(1); + expect(firstStop.fill).toBe("rgba(0,0,255,1)"); + expect(firstStop.fillOpacity).toBe(1); + expect(stops[2].fill).toBe("rgba(255,0,0,1)"); + + // layer is removed since it's been fully represented by the bubble chart + expect(context.layers.length).toBe(0); + }); + + test("Bubble chart with non-matching expression-based stroke style", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "circle", + paint: { + "circle-radius": [ + "interpolate", + ["linear"], + ["get", "population"], + 100000, + 5, + 10000000, + 50, + ], + "circle-color": [ + "match", + ["get", "type"], + "foo", + "purple", + "bar", + "green", + "black", + ], + "circle-stroke-width": 1, + }, + }, + ], + }; + const output = pluckBubblePanels(context); + + expect(output.length).toBe(1); + const { filters, panel } = output[0]; + const stops = panel.stops; + expect(filters.length).toBe(0); + expect(panel.label).toBe("population"); + const firstStop = stops[0]; + expect(firstStop.value).toBe(100000); + expect(firstStop.fill).toBe("black"); + // Layer is not removed because it does not represent the match expression + // which changes the fill color based on "type" + expect(context.layers.length).toBe(1); + const remainingLayer = context.layers[0]; + expect(remainingLayer.paint).toEqual({ + "circle-radius": null, + "circle-stroke-width": 1, + "circle-color": [ + "match", + ["get", "type"], + "foo", + "purple", + "bar", + "green", + "black", + ], + }); + }); + + test("Multiple bubble charts for different layers with filters", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + filter: ["==", ["get", "type"], "foo"], + type: "circle", + paint: { + "circle-radius": [ + "interpolate", + ["linear"], + ["get", "population"], + 100000, + 5, + 10000000, + 50, + ], + "circle-stroke-width": 1, + "circle-stroke-color": "red", + }, + }, + { + filter: ["==", ["get", "type"], "bar"], + type: "circle", + paint: { + "circle-radius": [ + "interpolate", + ["linear"], + ["get", "users"], + 100000, + 5, + 10000000, + 50, + ], + "circle-stroke-width": 1, + "circle-stroke-color": "blue", + }, + }, + ], + }; + const output = pluckBubblePanels(context); + expect(output.length).toBe(2); + expect(output[0].filters.length).toBe(1); + expect(output[1].filters.length).toBe(1); + expect(output[0].panel.label).toBe("population"); + expect(output[1].panel.label).toBe("users"); + const population = output[0].panel; + expect(population.stops[0].stroke).toBe("red"); + const users = output[1].panel; + expect(users.stops[0].stroke).toBe("blue"); + }); + + test("Multiple bubble charts for different facets (case)", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "circle", + paint: { + "circle-radius": [ + "case", + ["has", "population"], + [ + "interpolate", + ["linear"], + ["get", "population"], + 100000, + 5, + 10000000, + 50, + ], + [ + "interpolate", + ["linear"], + ["get", "users"], + 100000, + 5, + 10000000, + 50, + ], + ], + "circle-stroke-width": 1, + }, + }, + ], + }; + const output = pluckBubblePanels(context); + expect(output.length).toBe(2); + expect(output[0].filters.length).toBe(1); + expect(output[1].filters.length).toBe(0); + expect(output[0].panel.label).toBe("population"); + expect(output[1].panel.label).toBe("users"); + expect(context.layers.length).toBe(0); + }); + + test("Different facets with matching stroke facets", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "circle", + paint: { + "circle-radius": [ + "case", + ["has", "population"], + [ + "interpolate", + ["linear"], + ["get", "population"], + 100000, + 5, + 10000000, + 50, + ], + [ + "interpolate", + ["linear"], + ["get", "users"], + 100000, + 5, + 10000000, + 50, + ], + ], + "circle-stroke-color": [ + "case", + ["has", "population"], + "red", + "black", + ], + "circle-stroke-width": 1, + }, + }, + ], + }; + const output = pluckBubblePanels(context); + expect(output.length).toBe(2); + expect(output[0].filters.length).toBe(1); + expect(output[1].filters.length).toBe(0); + const population = output[0].panel; + expect(population.label).toBe("population"); + expect(population.stops[0].stroke).toBe("red"); + const users = output[1].panel; + expect(users.label).toBe("users"); + expect(users.stops[0].stroke).toBe("black"); + expect(context.layers.length).toBe(0); + }); + + test("Different facets with non-matching stroke facets", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "circle", + paint: { + "circle-radius": [ + "case", + ["has", "population"], + [ + "interpolate", + ["linear"], + ["get", "population"], + 100000, + 5, + 10000000, + 50, + ], + [ + "interpolate", + ["linear"], + ["get", "users"], + 100000, + 5, + 10000000, + 50, + ], + ], + "circle-stroke-color": [ + "case", + ["==", ["get", "type"], "foo"], + "red", + "black", + ], + "circle-stroke-width": 1, + }, + }, + ], + }; + const output = pluckBubblePanels(context); + expect(output.length).toBe(2); + expect(output[0].filters.length).toBe(1); + expect(output[1].filters.length).toBe(0); + const population = output[0].panel; + expect(population.label).toBe("population"); + expect(population.stops[0].stroke).toBe("black"); + const users = output[1].panel; + expect(users.label).toBe("users"); + expect(users.stops[0].stroke).toBe("black"); + expect(context.layers.length).toBe(1); + const remainingLayer = context.layers[0]; + expect(remainingLayer.paint).toEqual({ + "circle-stroke-width": 1, + "circle-stroke-color": [ + "case", + ["==", ["get", "type"], "foo"], + "red", + "black", + ], + "circle-radius": null, + }); + }); +}); + +describe("heatmaps", () => { + test("Simple heatmap", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "heatmap", + paint: {}, + }, + ], + }; + const output = pluckHeatmapPanels(context); + expect(output.length).toBe(1); + const { filters, panel } = output[0]; + }); + + test("2 Heatmaps filtered by type", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + filter: ["==", ["get", "type"], "foo"], + type: "heatmap", + paint: {}, + }, + { + filter: ["==", ["get", "type"], "bar"], + type: "heatmap", + paint: { + "heatmap-color": [ + "interpolate", + ["linear"], + ["heatmap-density"], + 0, + "rgba(33,102,172,0)", + 0.2, + "rgb(103,169,207)", + 0.4, + "rgb(209,229,240)", + 0.6, + "rgb(253,219,199)", + 0.8, + "rgb(239,138,98)", + 1, + "rgb(178,24,43)", + ], + }, + }, + ], + }; + const output = pluckHeatmapPanels(context); + expect(output.length).toBe(2); + expect(output[0].filters.length).toBe(1); + expect(output[1].filters.length).toBe(1); + const foo = output[0]; + const bar = output[1]; + expect(foo.filters[0]).toEqual(["==", ["get", "type"], "foo"]); + expect(foo.panel.stops[0].color).toBe("rgba(0, 0, 255, 0)"); + expect(bar.panel.stops[0].color).toBe("rgba(33,102,172,0)"); + }); + + test("2 different heatmaps controlled by case statement", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "heatmap", + paint: { + "heatmap-color": [ + "case", + ["==", ["get", "type"], "foo"], + [ + "interpolate", + ["linear"], + ["heatmap-density"], + 0, + "rgba(33,102,172,0)", + 0.2, + "rgb(103,169,207)", + 0.4, + "rgb(209,229,240)", + 0.6, + "rgb(253,219,199)", + 0.8, + "rgb(239,138,98)", + 1, + "rgb(178,24,43)", + ], + [ + "interpolate", + ["linear"], + ["heatmap-density"], + 0, + "rgba(0, 0, 255, 0)", + 0.1, + "royalblue", + 0.3, + "cyan", + 0.5, + "lime", + 0.7, + "yellow", + 1, + "red", + ], + ], + }, + }, + ], + }; + const output = pluckHeatmapPanels(context); + expect(output.length).toBe(2); + expect(output[0].filters.length).toBe(1); + expect(output[1].filters.length).toBe(0); + const foo = output[0]; + const bar = output[1]; + expect(foo.filters[0]).toEqual(["==", ["get", "type"], "foo"]); + expect(foo.panel.stops[0].color).toBe("rgba(33,102,172,0)"); + expect(bar.panel.stops[0].color).toBe("rgba(0, 0, 255, 0)"); + }); +}); + +describe("Gradient panels", () => { + test("Simple gradient panel", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "circle", + paint: { + "circle-color": [ + "interpolate", + ["linear"], + ["get", "pop_max"], + 10000, + "red", + 100000, + "purple", + 3500000, + "yellow", + ], + "circle-radius": 5, + "circle-stroke-width": 2, + "circle-stroke-opacity": 0.5, + }, + }, + { + type: "symbol", + paint: { + "text-halo-color": "white", + "text-halo-width": 1, + }, + layout: { + "text-size": 12, + "text-field": ["get", "name"], + "text-anchor": "left", + "text-offset": [0.5, 0.5], + "symbol-placement": "point", + }, + }, + ], + }; + const output = pluckGradientPanels(context); + expect(output.length).toBe(1); + const stops = output[0].panel.stops; + expect(stops[0].color).toBe("red"); + expect(stops[2].color).toBe("yellow"); + expect(output[0].filters.length).toBe(0); + // gradient-related layer should be removed since it's been fully + // represented + expect(context.layers.length).toBe(1); + }); + + test("Gradient panel with unrelated expression", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "circle", + paint: { + "circle-color": [ + "interpolate", + ["linear"], + ["get", "pop_max"], + 10000, + "red", + 100000, + "purple", + 3500000, + "yellow", + ], + "circle-radius": 5, + "circle-stroke-color": [ + "match", + ["get", "featurecla"], + "Admin-0 capital", + "red", + "Scientific station", + "#bab0ab", + "black", + ], + "circle-stroke-width": 2, + "circle-stroke-opacity": 0.5, + }, + }, + { + type: "symbol", + paint: { + "text-halo-color": "white", + "text-halo-width": 1, + }, + layout: { + "text-size": 12, + "text-field": ["get", "name"], + "text-anchor": "left", + "text-offset": [0.5, 0.5], + "symbol-placement": "point", + }, + }, + ], + }; + const output = pluckGradientPanels(context); + expect(output.length).toBe(1); + const stops = output[0].panel.stops; + expect(stops[0].color).toBe("red"); + expect(stops[2].color).toBe("yellow"); + expect(output[0].filters.length).toBe(0); + // layer should not be removed since there are unrelated expressions present + expect(context.layers.length).toBe(2); + }); + + test("Multiple gradients depending on filter", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + filter: ["==", ["get", "type"], "foo"], + type: "circle", + paint: { + "circle-color": [ + "interpolate", + ["linear"], + ["get", "pop_max"], + 10000, + "red", + 100000, + "purple", + 3500000, + "yellow", + ], + "circle-radius": 5, + "circle-stroke-width": 2, + "circle-stroke-opacity": 0.5, + }, + }, + { + filter: ["==", ["get", "type"], "bar"], + type: "circle", + paint: { + "circle-color": [ + "interpolate", + ["linear"], + ["get", "users"], + 10000, + "yellow", + 100000, + "purple", + ], + "circle-radius": 5, + "circle-stroke-width": 2, + "circle-stroke-opacity": 0.5, + }, + }, + ], + }; + const output = pluckGradientPanels(context); + expect(output.length).toBe(2); + const [foo, bar] = output; + expect(foo.panel.stops.map((s) => s.color)).toEqual([ + "red", + "purple", + "yellow", + ]); + expect(foo.filters.length).toBe(1); + expect(bar.filters.length).toBe(1); + expect(bar.panel.stops.map((s) => s.color)).toEqual(["yellow", "purple"]); + // gradient-related layer should be removed since it's been fully + // represented + expect(context.layers.length).toBe(0); + }); + + test("Multiple gradients depending on case", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "circle", + paint: { + "circle-color": [ + "case", + ["==", ["get", "type"], "foo"], + [ + "interpolate", + ["linear"], + ["get", "pop_max"], + 10000, + "red", + 100000, + "purple", + 3500000, + "yellow", + ], + [ + "interpolate", + ["linear"], + ["get", "users"], + 10000, + "yellow", + 100000, + "purple", + ], + ], + "circle-radius": 5, + "circle-stroke-width": 2, + "circle-stroke-opacity": 0.5, + }, + }, + ], + }; + const output = pluckGradientPanels(context); + expect(output.length).toBe(2); + const [foo, bar] = output; + expect(foo.panel.stops.map((s) => s.color)).toEqual([ + "red", + "purple", + "yellow", + ]); + expect(foo.filters.length).toBe(1); + expect(bar.panel.stops.map((s) => s.color)).toEqual(["yellow", "purple"]); + expect(bar.filters.length).toBe(0); + // gradient-related layer should be removed since it's been fully + // represented + expect(context.layers.length).toBe(0); + }); + + test("Multiple gradients (case) with unrelated stroke expression", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "circle", + paint: { + "circle-color": [ + "case", + ["==", ["get", "type"], "foo"], + [ + "interpolate", + ["linear"], + ["get", "pop_max"], + 10000, + "red", + 100000, + "purple", + 3500000, + "yellow", + ], + [ + "interpolate", + ["linear"], + ["get", "users"], + 10000, + "yellow", + 100000, + "purple", + ], + ], + "circle-radius": 5, + "circle-stroke-width": [ + "case", + [">", ["get", "pop_max"], 100000], + 5, + 2, + ], + "circle-stroke-opacity": 0.5, + }, + }, + ], + }; + const output = pluckGradientPanels(context); + expect(output.length).toBe(2); + // gradient-related layer should be removed since it's been fully + // represented + expect(context.layers.length).toBe(1); + // @ts-ignore + expect(context.layers[0].paint["circle-color"]).toBe(null); + // @ts-ignore + expect(context.layers[0].paint["circle-stroke-width"]).toEqual([ + "case", + [">", ["get", "pop_max"], 100000], + 5, + 2, + ]); + }); +}); + +describe("Step panels", () => { + test("Simple step panel", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "line", + paint: { + "line-color": [ + "step", + ["get", "pop_max"], + "red", + 100000, + "purple", + 3500000, + "yellow", + ], + "line-width": 2, + }, + }, + { + type: "fill", + paint: { + "fill-color": [ + "step", + ["get", "pop_max"], + "red", + 100000, + "purple", + 3500000, + "yellow", + ], + }, + }, + ], + }; + const output = pluckStepPanels(context); + expect(output.length).toBe(1); + const steps = output[0].panel.steps; + expect(steps[0].symbol.type).toBe("fill"); + const firstSymbol = steps[0].symbol as GLLegendFillSymbol; + const symbols = steps.map((s) => s.symbol as GLLegendFillSymbol); + expect(symbols[0].color).toBe("red"); + expect(symbols[0].strokeColor).toBe("red"); + expect(symbols[1].color).toBe("purple"); + expect(symbols[1].strokeColor).toBe("purple"); + expect(symbols[2].color).toBe("yellow"); + expect(symbols[2].strokeColor).toBe("yellow"); + expect(symbols[0].strokeWidth).toBe(2); + // step layer should be removed since it's been fully + // represented + expect(context.layers.length).toBe(0); + }); + + test("Two step panels with different filters", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + filter: ["==", ["get", "type"], "foo"], + type: "line", + paint: { + "line-color": [ + "step", + ["get", "pop_max"], + "red", + 100000, + "purple", + 3500000, + "yellow", + ], + "line-width": 2, + }, + }, + { + filter: ["==", ["get", "type"], "foo"], + type: "fill", + paint: { + "fill-color": [ + "step", + ["get", "pop_max"], + "red", + 100000, + "purple", + 3500000, + "yellow", + ], + }, + }, + { + filter: ["==", ["get", "type"], "bar"], + type: "line", + paint: { + "line-color": ["step", ["get", "pop_max"], "pink", 3500000, "blue"], + "line-width": 2, + }, + }, + { + filter: ["==", ["get", "type"], "bar"], + type: "fill", + paint: { + "fill-color": ["step", ["get", "pop_max"], "pink", 3500000, "blue"], + }, + }, + ], + }; + const output = pluckStepPanels(context); + expect(output.length).toBe(2); + const [foo, bar] = output; + expect(foo.filters.length).toBe(1); + expect(bar.filters.length).toBe(1); + expect(bar.filters[0][2]).toBe("bar"); + const fooSymbols = foo.panel.steps.map( + (s) => s.symbol as GLLegendFillSymbol + ); + expect(fooSymbols[0].color).toBe("red"); + expect(fooSymbols[1].color).toBe("purple"); + expect(fooSymbols[2].color).toBe("yellow"); + const barSymbols = bar.panel.steps.map( + (s) => s.symbol as GLLegendFillSymbol + ); + expect(barSymbols[0].color).toBe("pink"); + expect(barSymbols[1].color).toBe("blue"); + // step layers should be removed since it's been fully represented + expect(context.layers.length).toBe(0); + }); + + test("Two step panels with different case statements", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "line", + paint: { + "line-color": [ + "case", + ["==", ["get", "type"], "foo"], + [ + "step", + ["get", "pop_max"], + "red", + 100000, + "purple", + 3500000, + "yellow", + ], + ["step", ["get", "pop_max"], "pink", 3500000, "blue"], + ], + "line-width": 2, + }, + }, + { + type: "fill", + paint: { + "fill-color": [ + "case", + ["==", ["get", "type"], "foo"], + [ + "step", + ["get", "pop_max"], + "red", + 100000, + "purple", + 3500000, + "yellow", + ], + ["step", ["get", "pop_max"], "pink", 3500000, "blue"], + ], + }, + }, + ], + }; + const output = pluckStepPanels(context); + expect(output.length).toBe(2); + const [foo, bar] = output; + expect(foo.filters.length).toBe(1); + expect(bar.filters.length).toBe(0); + expect(foo.filters[0][2]).toBe("foo"); + const fooSymbols = foo.panel.steps.map( + (s) => s.symbol as GLLegendFillSymbol + ); + expect(fooSymbols[0].color).toBe("red"); + expect(fooSymbols[1].color).toBe("purple"); + expect(fooSymbols[2].color).toBe("yellow"); + const barSymbols = bar.panel.steps.map( + (s) => s.symbol as GLLegendFillSymbol + ); + expect(barSymbols[0].color).toBe("pink"); + expect(barSymbols[1].color).toBe("blue"); + // step layers should be removed since it's been fully represented + expect(context.layers.length).toBe(0); + }); + + test("Two step properties in one layer", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "line", + paint: { + "line-color": [ + "step", + ["get", "users"], + "red", + 100000, + "purple", + 3500000, + "yellow", + ], + "line-width": 2, + }, + }, + { + type: "fill", + paint: { + "fill-color": [ + "step", + ["get", "pop_max"], + "red", + 100000, + "purple", + 3500000, + "yellow", + ], + }, + }, + ], + }; + const output = pluckStepPanels(context); + expect(output.length).toBe(2); + const [fillStep, lineStep] = output; + const fillSymbols = fillStep.panel.steps.map( + (step) => step.symbol as GLLegendFillSymbol + ); + expect(fillSymbols[0].color).toBe("red"); + expect(fillSymbols[1].color).toBe("purple"); + expect(fillSymbols[0].strokeColor).toBe("red"); + expect(context.layers.length).toBe(0); + }); +}); + +describe("List panels", () => { + test("Simple list panel based on match", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "circle", + paint: { + "circle-color": [ + "match", + ["get", "type"], + "foo", + "purple", + "bar", + "green", + "black", + ], + "circle-radius": 6, + "circle-stroke-width": 2, + "circle-stroke-opacity": 0.5, + }, + }, + ], + }; + const output = pluckListPanelsFromMatchExpressions(context); + expect(output.length).toBe(1); + const list = output[0]; + expect(list.filters.length).toBe(0); + expect(list.panel.label).toBe("type"); + const symbols = list.panel.items.map( + (i: any) => i.symbol as GLLegendCircleSymbol + ); + expect(symbols[0].radius).toBe(6); + expect(symbols[0].color).toBe("purple"); + expect(symbols[1].color).toBe("green"); + expect(symbols[2].color).toBe("black"); + expect(list.panel.items[0].label).toBe("foo"); + expect(list.panel.items[1].label).toBe("bar"); + expect(list.panel.items[2].label).toBe("default"); + expect(context.layers.length).toBe(0); + }); + + test("Simple list panel based on case", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "circle", + paint: { + "circle-color": [ + "case", + ["==", ["get", "type"], "foo"], + "purple", + ["==", ["get", "type"], "bar"], + "green", + "black", + ], + "circle-radius": 6, + "circle-stroke-width": 2, + "circle-stroke-opacity": 0.5, + }, + }, + ], + }; + const output = pluckListPanelsForCaseAndFilterExpressions(context); + expect(output.length).toBe(1); + const list = output[0]; + expect(list.filters.length).toBe(0); + expect(list.panel.label).toBe("type"); + const symbols = list.panel.items.map( + (i: any) => i.symbol as GLLegendCircleSymbol + ); + expect(symbols[0].radius).toBe(6); + expect(symbols[0].color).toBe("purple"); + expect(symbols[1].color).toBe("green"); + expect(symbols[2].color).toBe("black"); + expect(list.panel.items[0].label).toBe("foo"); + expect(list.panel.items[1].label).toBe("bar"); + expect(list.panel.items[2].label).toBe("default"); + expect(context.layers.length).toBe(0); + }); + + test("Combined match and case expressions to form list of fill symbols", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "circle", + paint: { + "circle-color": [ + "case", + ["!=", ["get", "state"], "California"], + [ + "match", + ["get", "type"], + "foo", + "purple", + "bar", + "green", + "black", + ], + "red", + ], + "circle-radius": 6, + "circle-stroke-width": 2, + "circle-stroke-opacity": 0.5, + }, + }, + ], + }; + const output = pluckListPanelsFromMatchExpressions(context); + expect(output.length).toBe(1); + const list = output[0]; + expect(list.filters.length).toBe(1); + expect(list.panel.label).toBe("type"); + const symbols = list.panel.items.map( + (i: any) => i.symbol as GLLegendCircleSymbol + ); + expect(symbols[0].radius).toBe(6); + expect(symbols[0].color).toBe("purple"); + expect(symbols[1].color).toBe("green"); + expect(symbols[2].color).toBe("black"); + expect(list.panel.items[0].label).toBe("foo"); + expect(list.panel.items[1].label).toBe("bar"); + expect(list.panel.items[2].label).toBe("default"); + // == California style should fall through + expect(context.layers.length).toBe(1); + }); + + test("Previous state filter with reversed order of expressions", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "circle", + paint: { + "circle-color": [ + "case", + ["==", ["get", "state"], "California"], + "red", + [ + "match", + ["get", "type"], + "foo", + "purple", + "bar", + "green", + "black", + ], + ], + "circle-radius": 6, + "circle-stroke-width": 2, + "circle-stroke-opacity": 0.5, + }, + }, + ], + }; + const output = [ + ...pluckListPanelsFromMatchExpressions(context), + ...pluckListPanelsForCaseAndFilterExpressions(context), + ]; + expect(output.length).toBe(2); + const caPanel = output.find((p: any) => p.panel.label === "state")!; + const typePanel = output.find((o: any) => o !== caPanel)!; + // TODO: this might change? + expect(caPanel.filters.length).toBe(0); + expect(typePanel.filters.length).toBe(0); + expect(typePanel.panel.label).toBe("type"); + const symbols = typePanel.panel.items.map( + (i: any) => i.symbol as GLLegendCircleSymbol + ); + expect(symbols[0].radius).toBe(6); + expect(symbols[0].color).toBe("purple"); + expect(symbols[1].color).toBe("green"); + expect(symbols[2].color).toBe("black"); + expect(typePanel.panel.items[0].label).toBe("foo"); + expect(typePanel.panel.items[1].label).toBe("bar"); + expect(typePanel.panel.items[2].label).toBe("default"); + expect(context.layers.length).toBe(0); + }); + + test.todo("Mis-matched case and fill expressions"); + + test("Nested under filter", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "circle", + filter: ["==", ["get", "state"], "California"], + paint: { + "circle-color": [ + "match", + ["get", "type"], + "foo", + "purple", + "bar", + "green", + "black", + ], + "circle-radius": 6, + "circle-stroke-width": 2, + "circle-stroke-opacity": 0.5, + }, + }, + ], + }; + const output = pluckListPanelsFromMatchExpressions(context); + expect(output.length).toBe(1); + const list = output[0]; + expect(list.filters.length).toBe(1); + expect(list.panel.label).toBe("type"); + const symbols = list.panel.items.map( + (i: any) => i.symbol as GLLegendCircleSymbol + ); + expect(symbols[0].radius).toBe(6); + expect(symbols[0].color).toBe("purple"); + expect(symbols[1].color).toBe("green"); + expect(symbols[2].color).toBe("black"); + expect(list.panel.items[0].label).toBe("foo"); + expect(list.panel.items[1].label).toBe("bar"); + expect(list.panel.items[2].label).toBe("default"); + // == California style should fall through + expect(context.layers.length).toBe(0); + }); + + test.todo("Mis-matched case+match and unrelated expression"); + test.todo("Case expression with mixed get expressions"); + test.todo("Case expression with 'all' expression"); + test.todo("Case expression with 'any' expression"); +}); + +describe("filter panels", () => { + test("Numeric filters creating a choropleth", () => { + const context: { layers: SeaSketchGlLayer[] } = { + layers: [ + { + type: "fill", + paint: { + "fill-color": "rgba(49,163,84,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 145993.6237613576], + ["<=", "EmpArts", 290160], + ], + metadata: { + label: "145,995 to 290,160", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(49,163,84,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 73652.57252191499], + ["<=", "EmpArts", 145993.6237613576], + ], + metadata: { + label: "37,354 to 145,994", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(116,196,118,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 37352.65411538615], + ["<=", "EmpArts", 73652.57252191499], + ], + metadata: { + label: "37,354 to 73,653", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(186,228,179,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 19137.767080371515], + ["<=", "EmpArts", 37352.65411538615], + ], + metadata: { + label: "19,138 to 37,353", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(186,228,179,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 9997.743513853795], + ["<=", "EmpArts", 19137.767080371515], + ], + metadata: { + label: "9,999 to 19,138", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(237,248,233,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 5411.383431800605], + ["<=", "EmpArts", 9997.743513853795], + ], + metadata: { + label: "5,412 to 9,998", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(237,248,233,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: ["all", ["<=", "EmpArts", 5411.383431800605]], + metadata: { + label: "3,110 to 5,411", + }, + }, + ], + }; + const output = pluckFilterPanels(context); + expect(output.length).toBe(7); + expect(output[0].filters.length).toBe(1); + expect(output[0].filters[0]).toEqual([ + "all", + [">", "EmpArts", 145993.6237613576], + ["<=", "EmpArts", 290160], + ]); + expect(output[0].panel.items.length).toBe(1); + expect(output[0].panel.items[0].label).toBe("145,995 to 290,160"); + expect(output[0].panel.label).toBe("EmpArts"); + }); +}); + +describe("Complex test cases", () => { + test("Choropleth with california singled-out", () => { + const context: { layers: SeaSketchGlLayer[]; sourceType: "vector" } = { + sourceType: "vector", + layers: [ + { + type: "fill", + paint: { + "fill-color": "rgba(49,163,84,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 145993.6237613576], + ["<=", "EmpArts", 290160], + ["!=", "NAME", "California"], + ], + metadata: { + label: "145,995 to 290,160", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(49,163,84,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 73652.57252191499], + ["<=", "EmpArts", 145993.6237613576], + ["!=", "NAME", "California"], + ], + metadata: { + label: "37,354 to 145,994", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(116,196,118,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 37352.65411538615], + ["<=", "EmpArts", 73652.57252191499], + ["!=", "NAME", "California"], + ], + metadata: { + label: "37,354 to 73,653", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(186,228,179,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 19137.767080371515], + ["<=", "EmpArts", 37352.65411538615], + ["!=", "NAME", "California"], + ], + metadata: { + label: "19,138 to 37,353", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(186,228,179,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 9997.743513853795], + ["<=", "EmpArts", 19137.767080371515], + ["!=", "NAME", "California"], + ], + metadata: { + label: "9,999 to 19,138", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(237,248,233,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 5411.383431800605], + ["<=", "EmpArts", 9997.743513853795], + ["!=", "NAME", "California"], + ], + metadata: { + label: "5,412 to 9,998", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(237,248,233,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + ["<=", "EmpArts", 5411.383431800605], + ["!=", "NAME", "California"], + ], + metadata: { + label: "3,110 to 5,411", + }, + }, + { + type: "line", + paint: { + "line-color": [ + "case", + ["==", ["get", "SUB_REGION"], "Pacific"], + "blue", + "rgba(0,0,0,0.2)", + ], + }, + }, + { + type: "line", + paint: { + "line-color": "red", + }, + filter: ["==", ["get", "NAME"], "California"], + }, + { + type: "fill", + paint: { + "fill-color": "#9c755f", + "fill-opacity": 0.5, + }, + filter: ["==", "NAME", "California"], + }, + { + type: "symbol", + layout: { + "icon-image": [ + "match", + ["get", "NAME"], + "California", + "seasketch://sprites/1", + "seasketch://sprites/2", + ], + }, + }, + ], + }; + + const legend = compileLegendFromGLStyleLayers(context.layers, "vector"); + expect(legend.type).toBe("MultipleSymbolGLLegend"); + if (legend.type === "MultipleSymbolGLLegend") { + expect(legend.panels.length).toBe(3); + } + }); + + test("EEZ with complex expressions", () => { + const legend = compileLegendFromGLStyleLayers( + [ + { + type: "fill", + paint: { + "fill-color": "#FF0000", + "fill-opacity": [ + "case", + ["==", ["get", "ISO_SOV1"], "MEX"], + 0.15, + 0, + ], + }, + layout: {}, + }, + { + type: "line", + paint: { + "line-color": [ + "case", + ["==", ["get", "ISO_SOV1"], "USA"], + "#FF0000", + "blue", + ], + "line-width": ["case", ["==", ["get", "ISO_SOV1"], "MEX"], 4, 1], + "line-opacity": 0.75, + }, + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + ], + "vector" + ) as MultipleSymbolLegendForGLLayers; + expect(legend.type).toBe("MultipleSymbolGLLegend"); + expect(legend.panels.length).toBe(1); + const list = legend.panels[0] as GLLegendListPanel; + expect(list.type).toBe("GLLegendListPanel"); + expect(list.items.find((l) => l.label === "USA")).toBeDefined(); + expect(list.items.find((l) => l.label === "MEX")).toBeDefined(); + expect(list.items.find((l) => l.label === "default")).toBeDefined(); + }); + + test("EEZ with complex case+match expressions", () => { + const legend = compileLegendFromGLStyleLayers( + [ + { + type: "fill", + paint: { + "fill-color": "#FF0000", + "fill-opacity": [ + "case", + ["==", ["get", "ISO_SOV1"], "MEX"], + 0.15, + 0, + ], + }, + layout: {}, + }, + { + type: "line", + paint: { + "line-color": [ + "case", + ["==", ["get", "ISO_SOV1"], "USA"], + "#FF0000", + "blue", + ], + "line-width": ["match", ["get", "ISO_SOV1"], "MEX", 4, 1], + "line-opacity": 0.75, + }, + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + ], + "vector" + ) as MultipleSymbolLegendForGLLayers; + expect(legend.type).toBe("MultipleSymbolGLLegend"); + expect(legend.panels.length).toBe(1); + const list = legend.panels[0] as GLLegendListPanel; + expect(list.type).toBe("GLLegendListPanel"); + expect(list.items.find((l) => l.label === "USA")).toBeDefined(); + expect(list.items.find((l) => l.label === "MEX")).toBeDefined(); + expect(list.items.find((l) => l.label === "default")).toBeDefined(); + }); + + test("EEZ with complex case+filter expressions", () => { + const legend = compileLegendFromGLStyleLayers( + [ + { + type: "fill", + paint: { + "fill-color": "#FF0000", + "fill-opacity": [ + "case", + ["==", ["get", "ISO_SOV1"], "MEX"], + 0.15, + 0, + ], + }, + layout: {}, + }, + { + type: "line", + paint: { + "line-color": "#FF0000", + "line-width": 1, + "line-opacity": 0.75, + }, + filter: ["==", "ISO_SOV1", "USA"], + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + { + type: "line", + paint: { + "line-color": "blue", + "line-width": ["match", ["get", "ISO_SOV1"], "MEX", 4, 1], + "line-opacity": 0.75, + }, + filter: ["!=", "ISO_SOV1", "USA"], + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + ], + "vector" + ) as MultipleSymbolLegendForGLLayers; + expect(legend.type).toBe("MultipleSymbolGLLegend"); + expect(legend.panels.length).toBe(1); + const list = legend.panels[0] as GLLegendListPanel; + expect(list.type).toBe("GLLegendListPanel"); + expect(list.items.find((l) => l.label === "USA")).toBeDefined(); + expect(list.items.find((l) => l.label === "MEX")).toBeDefined(); + expect(list.items.find((l) => l.label === "default")).toBeDefined(); + }); + + test("EEZ (complex w/only filters)", () => { + const legend = compileLegendFromGLStyleLayers( + [ + { + type: "fill", + paint: { + "fill-color": "#FF0000", + "fill-opacity": 0.15, + }, + filter: ["==", "ISO_SOV1", "MEX"], + layout: {}, + }, + { + type: "line", + paint: { + "line-color": "red", + "line-width": 1, + "line-opacity": 0.75, + }, + filter: ["==", "ISO_SOV1", "USA"], + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + { + type: "line", + paint: { + "line-color": "blue", + "line-width": 1, + "line-opacity": 0.75, + }, + filter: ["all", ["!=", "ISO_SOV1", "USA"], ["!=", "ISO_SOV1", "MEX"]], + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + { + type: "line", + paint: { + "line-color": "blue", + "line-width": 4, + "line-opacity": 0.75, + }, + filter: ["==", "ISO_SOV1", "MEX"], + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + ], + "vector" + ) as MultipleSymbolLegendForGLLayers; + expect(legend.type).toBe("MultipleSymbolGLLegend"); + expect(legend.panels.length).toBe(1); + const list = legend.panels[0] as GLLegendListPanel; + expect(list.type).toBe("GLLegendListPanel"); + expect(list.items.find((l) => l.label === "USA")).toBeDefined(); + expect(list.items.find((l) => l.label === "MEX")).toBeDefined(); + expect(list.items.find((l) => l.label === "!= USA, != MEX")).toBeDefined(); + }); + + test("EEZ w/filter by country and default style", () => { + const legend = compileLegendFromGLStyleLayers( + [ + { + type: "line", + paint: { + "line-color": "rgb(110,110,110)", + "line-width": 1, + "line-opacity": 1, + }, + layout: {}, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(252,215,215,1)", + "fill-outline-color": "rgba(110,110,110,1)", + }, + filter: ["==", "ISO_SOV1", "USA"], + }, + ], + "vector" + ); + expect(legend.type).toBe("MultipleSymbolGLLegend"); + if (legend.type === "MultipleSymbolGLLegend") { + expect(legend.panels.length).toBe(1); + expect(legend.panels[0].type).toBe("GLLegendListPanel"); + const list = legend.panels[0] as GLLegendListPanel; + expect(list.items.length).toBe(2); + const labels = list.items.map((i) => i.label); + expect(labels).toContain("default"); + expect(labels).toContain("USA"); + expect(list.items[0].symbol.type).toBe("fill"); + } + }); +}); + +describe("groupByFilters", () => { + test("simple output", () => { + const items = [ + { + filters: [], + panel: { type: "GLLegendListPanel", label: "foo" } as GLLegendListPanel, + }, + { + filters: [], + panel: { type: "GLLegendListPanel", label: "bar" } as GLLegendListPanel, + }, + ]; + const output = groupByFilters(items); + expect(output.children.length).toBe(2); + }); + + test("a few filters", () => { + const items = [ + { + filters: [], + panel: { type: "GLLegendListPanel", label: "foo" } as GLLegendListPanel, + }, + { + filters: [], + panel: { type: "GLLegendListPanel", label: "bar" } as GLLegendListPanel, + }, + { + filters: [["==", "state", "CA"] as Expression], + panel: { type: "GLLegendListPanel", label: "baz" } as GLLegendListPanel, + }, + { + filters: [["==", "state", "CA"] as Expression], + panel: { + type: "GLLegendListPanel", + label: "bang", + } as GLLegendListPanel, + }, + ]; + const output = groupByFilters(items); + expect(output.children.length).toBe(3); + const filterNode = output.children.find((c) => + isGroupByFilterNode(c) + ) as GroupByFilterNode; + expect(filterNode).not.toBeUndefined(); + expect(filterNode.children.length).toBe(2); + }); + + test("deeply nested", () => { + const items = [ + { + filters: [], + panel: { type: "GLLegendListPanel", label: "foo" } as GLLegendListPanel, + }, + { + filters: [], + panel: { type: "GLLegendListPanel", label: "bar" } as GLLegendListPanel, + }, + { + filters: [["==", "state", "CA"] as Expression], + panel: { type: "GLLegendListPanel", label: "baz" } as GLLegendListPanel, + }, + { + filters: [["==", "state", "CA"] as Expression], + panel: { + type: "GLLegendListPanel", + label: "bang", + } as GLLegendListPanel, + }, + { + filters: [ + ["==", ["get", "county"], "santa barbara"], + ["==", "state", "CA"] as Expression, + ], + panel: { + type: "GLLegendListPanel", + label: "sb", + } as GLLegendListPanel, + }, + ]; + const output = groupByFilters(items as PanelItem[]); + expect(output.children.length).toBe(3); + const lists = output.children.filter( + (c) => !isGroupByFilterNode(c) + ) as GLLegendListPanel[]; + expect(lists.length).toBe(2); + expect(lists.map((l) => l.label).sort()).toEqual(["bar", "foo"].sort()); + const filterNode = output.children.find((c) => + isGroupByFilterNode(c) + ) as GroupByFilterNode; + const subLists = filterNode.children.filter( + (c) => !isGroupByFilterNode(c) + ) as GLLegendListPanel[]; + expect(subLists.length).toBe(2); + expect(subLists.map((l) => l.label).sort()).toEqual(["baz", "bang"].sort()); + expect(filterNode).not.toBeUndefined(); + expect(filterNode.children.length).toBe(3); + const sbNode = filterNode.children.find((c) => + isGroupByFilterNode(c) + ) as GroupByFilterNode; + expect(sbNode).not.toBeUndefined(); + expect(sbNode.children.length).toBe(1); + expect((sbNode.children[0] as GLLegendListPanel).label).toBe("sb"); + }); + + test("more branches and deeply nested", () => { + const items = [ + { + filters: [], + panel: { type: "GLLegendListPanel", label: "foo" } as GLLegendListPanel, + }, + { + filters: [], + panel: { type: "GLLegendListPanel", label: "bar" } as GLLegendListPanel, + }, + { + filters: [["==", "state", "CA"] as Expression], + panel: { type: "GLLegendListPanel", label: "baz" } as GLLegendListPanel, + }, + { + filters: [["==", "state", "CA"] as Expression], + panel: { + type: "GLLegendListPanel", + label: "bang", + } as GLLegendListPanel, + }, + { + filters: [ + ["==", ["get", "county"], "santa barbara"], + ["==", "state", "CA"] as Expression, + ], + panel: { + type: "GLLegendListPanel", + label: "sb", + } as GLLegendListPanel, + }, + { + filters: [["==", "state", "AZ"] as Expression], + panel: { type: "GLLegendListPanel", label: "AZ" } as GLLegendListPanel, + }, + { + filters: [ + ["==", "state", "AZ"], + ["==", "county", "Maricopa County"], + ] as Expression[], + panel: { + type: "GLLegendListPanel", + label: "Maricopa County", + } as GLLegendListPanel, + }, + ]; + const output = groupByFilters(items as PanelItem[]); + expect(output.children.length).toBe(4); + const lists = output.children.filter( + (c) => !isGroupByFilterNode(c) + ) as GLLegendListPanel[]; + expect(lists.length).toBe(2); + expect(lists.map((l) => l.label).sort()).toEqual(["bar", "foo"].sort()); + const filterNodes = output.children.filter((c) => + isGroupByFilterNode(c) + ) as GroupByFilterNode[]; + const CA = filterNodes.find((n) => n.filters[0][2] === "CA")!; + expect(CA).not.toBeUndefined(); + const subLists = CA.children.filter( + (c) => !isGroupByFilterNode(c) + ) as GLLegendListPanel[]; + expect(subLists.length).toBe(2); + expect(subLists.map((l) => l.label).sort()).toEqual(["baz", "bang"].sort()); + expect(CA.children.length).toBe(3); + const sbNode = CA.children.find((c) => + isGroupByFilterNode(c) + ) as GroupByFilterNode; + expect(sbNode).not.toBeUndefined(); + expect(sbNode.children.length).toBe(1); + expect((sbNode.children[0] as GLLegendListPanel).label).toBe("sb"); + const AZ = filterNodes.find((n) => n.filters[0][2] === "AZ")!; + expect(AZ).not.toBeUndefined(); + expect(AZ.children.length).toBe(2); + const maricopa = AZ.children.find((c) => + isGroupByFilterNode(c) + ) as GroupByFilterNode; + expect(maricopa).not.toBeUndefined(); + expect(maricopa.children.length).toBe(1); + expect((maricopa.children[0] as GLLegendListPanel).label).toBe( + "Maricopa County" + ); + }); + + test("'all' expressions are 'exploded'", () => { + const items = [ + { + filters: [], + panel: { type: "GLLegendListPanel", label: "foo" } as GLLegendListPanel, + }, + { + filters: [], + panel: { type: "GLLegendListPanel", label: "bar" } as GLLegendListPanel, + }, + { + filters: [["==", "state", "CA"] as Expression], + panel: { type: "GLLegendListPanel", label: "baz" } as GLLegendListPanel, + }, + { + filters: [["==", "state", "CA"] as Expression], + panel: { + type: "GLLegendListPanel", + label: "bang", + } as GLLegendListPanel, + }, + { + filters: [ + [ + "all", + ["==", ["get", "county"], "santa barbara"], + ["==", "state", "CA"], + ], + ] as Expression[], + panel: { + type: "GLLegendListPanel", + label: "sb", + } as GLLegendListPanel, + }, + ]; + const output = groupByFilters(items as PanelItem[]); + expect(output.children.length).toBe(3); + const lists = output.children.filter( + (c) => !isGroupByFilterNode(c) + ) as GLLegendListPanel[]; + expect(lists.length).toBe(2); + expect(lists.map((l) => l.label).sort()).toEqual(["bar", "foo"].sort()); + const filterNode = output.children.find((c) => + isGroupByFilterNode(c) + ) as GroupByFilterNode; + const subLists = filterNode.children.filter( + (c) => !isGroupByFilterNode(c) + ) as GLLegendListPanel[]; + expect(subLists.length).toBe(2); + expect(subLists.map((l) => l.label).sort()).toEqual(["baz", "bang"].sort()); + expect(filterNode).not.toBeUndefined(); + expect(filterNode.children.length).toBe(3); + const sbNode = filterNode.children.find((c) => + isGroupByFilterNode(c) + ) as GroupByFilterNode; + expect(sbNode).not.toBeUndefined(); + expect(sbNode.children.length).toBe(1); + expect((sbNode.children[0] as GLLegendListPanel).label).toBe("sb"); + }); + + test("related filters are grouped into the same node", () => { + const items = [ + { + filters: [], + panel: { type: "GLLegendListPanel", label: "foo" } as GLLegendListPanel, + }, + { + filters: [], + panel: { type: "GLLegendListPanel", label: "bar" } as GLLegendListPanel, + }, + { + filters: [ + ["==", "state", "CA"], + [">", ["get", "population"], 50000], + ] as Expression[], + panel: { type: "GLLegendListPanel", label: "baz" } as GLLegendListPanel, + }, + { + filters: [ + ["==", "state", "CA"], + [">", ["get", "population"], 50000], + ] as Expression[], + panel: { + type: "GLLegendListPanel", + label: "bang", + } as GLLegendListPanel, + }, + ]; + const output = groupByFilters(items); + expect(output.children.length).toBe(3); + const filterNode = output.children.find((c) => + isGroupByFilterNode(c) + ) as GroupByFilterNode; + expect(filterNode).not.toBeUndefined(); + expect(filterNode.filters.length).toBe(2); + expect(filterNode.children.length).toBe(2); + }); +}); + +describe("Kitchen sink", () => { + test.each(Object.keys(testCases))("Kitchen sink: %s", (name: string) => { + // @ts-ignore + const { input, output } = testCases[name]; + const legendData = compileLegendFromGLStyleLayers(input, "vector"); + expect(legendData).toMatchObject(output); + }); +}); diff --git a/packages/client/src/dataLayers/legends/legendKitchenSinkTestCases.ts b/packages/client/src/dataLayers/legends/legendKitchenSinkTestCases.ts new file mode 100644 index 000000000..66c972d6c --- /dev/null +++ b/packages/client/src/dataLayers/legends/legendKitchenSinkTestCases.ts @@ -0,0 +1,4138 @@ +export const testCases = { + "Place names": { + input: [ + { + type: "symbol", + paint: { + "text-color": "black", + "text-halo-color": "white", + "text-halo-width": 2, + }, + layout: { + "text-size": 12, + "text-field": ["get", "name"], + "symbol-placement": "point", + }, + }, + ], + output: { + type: "SimpleGLLegend", + symbol: { + type: "text", + color: "black", + fontFamily: "Open Sans Regular", + fontWeight: "normal", + fontStyle: "normal", + haloColor: "white", + haloWidth: 2, + }, + }, + }, + "Populated places (heatmap)": { + input: [ + { + type: "heatmap", + paint: { + "heatmap-radius": ["interpolate", ["linear"], ["zoom"], 0, 2, 9, 20], + "heatmap-opacity": [ + "interpolate", + ["linear"], + ["zoom"], + 10, + 1, + 12, + 0, + ], + "heatmap-intensity": [ + "interpolate", + ["linear"], + ["zoom"], + 0, + 1, + 9, + 3, + ], + }, + layout: {}, + maxzoom: 8.2, + }, + { + type: "circle", + paint: { + "circle-color": "#4e79a7", + "circle-radius": 5, + "circle-stroke-color": "white", + "circle-stroke-width": 2, + }, + minzoom: 8, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "heatmap", + type: "GLLegendHeatmapPanel", + stops: [ + { + value: 0, + color: "rgba(0, 0, 255, 0)", + label: "0", + }, + { + value: 0.1, + color: "royalblue", + label: "0.1", + }, + { + value: 0.3, + color: "cyan", + label: "0.3", + }, + { + value: 0.5, + color: "lime", + label: "0.5", + }, + { + value: 0.7, + color: "yellow", + label: "0.7", + }, + { + value: 1, + color: "red", + label: "1", + }, + ], + }, + { + id: "remaining-layer-0", + type: "GLLegendListPanel", + items: [ + { + id: "remaining-layer-single-child", + symbol: { + radius: 5, + type: "circle", + color: "#4e79a7", + fillOpacity: 1, + strokeWidth: 2, + strokeColor: "white", + strokeOpacity: 1, + }, + label: "", + }, + ], + }, + ], + }, + }, + "Populated places (gradient)": { + input: [ + { + type: "circle", + paint: { + "circle-color": [ + "interpolate", + ["linear"], + ["get", "pop_max"], + 10000, + "red", + 100000, + "purple", + 3500000, + "yellow", + ], + "circle-radius": 5, + "circle-stroke-color": [ + "match", + ["get", "featurecla"], + "Admin-0 capital", + "red", + "Scientific station", + "#bab0ab", + "black", + ], + "circle-stroke-width": 2, + "circle-stroke-opacity": 0.5, + }, + }, + { + type: "symbol", + paint: { + "text-halo-color": "white", + "text-halo-width": 1, + }, + layout: { + "text-size": 12, + "text-field": ["get", "name"], + "text-anchor": "left", + "text-offset": [0.5, 0.5], + "symbol-placement": "point", + }, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "0-circle-color-0-gradient", + type: "GLLegendGradientPanel", + label: "circle-color", + stops: [ + { + value: 10000, + color: "red", + label: "10,000", + }, + { + value: 100000, + color: "purple", + label: "100,000", + }, + { + value: 3500000, + color: "yellow", + label: "3,500,000", + }, + ], + }, + { + id: "0-circle-stroke-color-0-match", + type: "GLLegendListPanel", + label: "featurecla", + items: [ + { + id: "0-circle-stroke-color-0-match-0", + label: "Admin-0 capital", + symbol: { + radius: 5, + type: "circle", + color: null, + fillOpacity: 1, + strokeWidth: 2, + strokeColor: "red", + strokeOpacity: 0.5, + }, + }, + { + id: "0-circle-stroke-color-0-match-2", + label: "Scientific station", + symbol: { + radius: 5, + type: "circle", + color: null, + fillOpacity: 1, + strokeWidth: 2, + strokeColor: "#bab0ab", + strokeOpacity: 0.5, + }, + }, + { + id: "0-circle-stroke-color-0-match-default", + label: "default", + symbol: { + radius: 5, + type: "circle", + color: null, + fillOpacity: 1, + strokeWidth: 2, + strokeColor: "black", + strokeOpacity: 0.5, + }, + }, + ], + }, + ], + }, + }, + "Populated places (circle)": { + input: [ + { + type: "circle", + paint: { + "circle-color": "#CC66FF", + "circle-radius": ["step", ["zoom"], 0, 1, 5, 5, 10], + "circle-stroke-color": "#000000", + "circle-stroke-width": 1, + "circle-stroke-opacity": 0.5, + }, + }, + { + type: "symbol", + paint: { + "text-halo-color": "white", + "text-halo-width": 1, + }, + layout: { + "text-size": 12, + "text-field": ["get", "FEATURE_NA"], + "text-anchor": "left", + "text-offset": [0.5, 0.5], + "symbol-placement": "point", + }, + }, + ], + output: { + type: "SimpleGLLegend", + symbol: { + radius: 10, + type: "circle", + color: "#CC66FF", + fillOpacity: 1, + strokeWidth: 1, + strokeColor: "#000000", + strokeOpacity: 0.5, + }, + }, + }, + "Populated places (bubble chart)": { + input: [ + { + type: "circle", + paint: { + "circle-color": "#af7aa1", + "circle-radius": [ + "interpolate", + ["linear"], + ["get", "pop_max"], + 10000, + 5, + 35676000, + 50, + ], + "circle-stroke-color": [ + "match", + ["get", "featurecla"], + "Admin-0 capital", + "red", + "Meteorological Station", + "#edc949", + "black", + ], + "circle-stroke-width": 2, + }, + layout: {}, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "bubble-0-0", + type: "GLLegendBubblePanel", + label: "pop_max", + stops: [ + { + fill: "#af7aa1", + radius: 5, + stroke: "black", + strokeWidth: 2, + value: 10000, + fillOpacity: 1, + }, + { + fill: "#af7aa1", + radius: 27.5, + stroke: "black", + strokeWidth: 2, + value: 17843000, + fillOpacity: 1, + }, + { + fill: "#af7aa1", + radius: 50, + stroke: "black", + strokeWidth: 2, + value: 35676000, + fillOpacity: 1, + }, + ], + }, + { + id: "0-circle-stroke-color-0-match", + type: "GLLegendListPanel", + label: "featurecla", + items: [ + { + id: "0-circle-stroke-color-0-match-0", + label: "Admin-0 capital", + symbol: { + radius: null, + type: "circle", + color: "#af7aa1", + fillOpacity: 1, + strokeWidth: 2, + strokeColor: "red", + strokeOpacity: 1, + }, + }, + { + id: "0-circle-stroke-color-0-match-2", + label: "Meteorological Station", + symbol: { + radius: null, + type: "circle", + color: "#af7aa1", + fillOpacity: 1, + strokeWidth: 2, + strokeColor: "#edc949", + strokeOpacity: 1, + }, + }, + { + id: "0-circle-stroke-color-0-match-default", + label: "default", + symbol: { + radius: null, + type: "circle", + color: "#af7aa1", + fillOpacity: 1, + strokeWidth: 2, + strokeColor: "black", + strokeOpacity: 1, + }, + }, + ], + }, + ], + }, + }, + "Marker bubble chart": { + input: [ + { + type: "symbol", + paint: { + "text-halo-color": "white", + "text-halo-width": 1, + }, + layout: { + "icon-size": [ + "interpolate", + ["linear"], + ["get", "pop_max"], + -99, + 1, + 35676000, + 3, + ], + "text-size": 12, + "icon-image": "seasketch://sprites/1", + "text-field": ["get", "FEATURE_NA"], + "text-anchor": "left", + "text-offset": [0.5, 0.5], + "symbol-placement": "point", + }, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "marker-size-0-0", + type: "GLMarkerSizePanel", + label: "pop_max", + stops: [ + { + id: "stop-0", + imageId: "seasketch://sprites/1", + value: -99, + iconSize: 1, + color: "#000000", + haloColor: "rgba(0, 0, 0, 0)", + haloWidth: 0, + rotation: 0, + }, + { + id: "stop-1", + imageId: "seasketch://sprites/1", + value: 17837950.5, + iconSize: 2, + color: "#000000", + haloColor: "rgba(0, 0, 0, 0)", + haloWidth: 0, + rotation: 0, + }, + { + id: "stop-2", + imageId: "seasketch://sprites/1", + value: 35676000, + iconSize: 3, + color: "#000000", + haloColor: "rgba(0, 0, 0, 0)", + haloWidth: 0, + rotation: 0, + }, + ], + }, + ], + }, + }, + "Places (by feature class)": { + input: [ + { + type: "circle", + paint: { + "circle-color": [ + "match", + ["get", "featurecla"], + "Admin-0 capital", + "#4e79a7", + "Admin-0 capital alt", + "#f28e2c", + "Admin-0 region capital", + "#e15759", + "Admin-1 capital", + "#76b7b2", + "Admin-1 region capital", + "#59a14f", + "Historic place", + "#edc949", + "Meteorological Station", + "#af7aa1", + "Populated Place", + "#ff9da7", + "Populated place", + "#9c755f", + "Scientific station", + "#bab0ab", + "black", + ], + "circle-radius": [ + "case", + ["==", ["get", "featurecla"], "Scientific station"], + 15, + 5, + ], + "circle-opacity": 0.7, + "circle-stroke-color": [ + "match", + ["get", "featurecla"], + "Admin-0 capital", + "#4e79a7", + "Admin-0 capital alt", + "#f28e2c", + "Admin-0 region capital", + "#e15759", + "Admin-1 capital", + "#76b7b2", + "Admin-1 region capital", + "#59a14f", + "Historic place", + "#edc949", + "Meteorological Station", + "#af7aa1", + "Populated Place", + "#ff9da7", + "Populated place", + "#9c755f", + "Scientific station", + "#eb0000", + "black", + ], + "circle-stroke-width": [ + "match", + ["get", "featurecla"], + "Scientific station", + 5, + 1, + ], + }, + layout: {}, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "0-circle-color-0-match", + type: "GLLegendListPanel", + label: "featurecla", + items: [ + { + id: "0-circle-color-0-match-0", + label: "Admin-0 capital", + symbol: { + radius: 5, + type: "circle", + color: "#4e79a7", + fillOpacity: 0.7, + strokeWidth: 1, + strokeColor: "#4e79a7", + strokeOpacity: 1, + }, + }, + { + id: "0-circle-color-0-match-2", + label: "Admin-0 capital alt", + symbol: { + radius: 5, + type: "circle", + color: "#f28e2c", + fillOpacity: 0.7, + strokeWidth: 1, + strokeColor: "#f28e2c", + strokeOpacity: 1, + }, + }, + { + id: "0-circle-color-0-match-4", + label: "Admin-0 region capital", + symbol: { + radius: 5, + type: "circle", + color: "#e15759", + fillOpacity: 0.7, + strokeWidth: 1, + strokeColor: "#e15759", + strokeOpacity: 1, + }, + }, + { + id: "0-circle-color-0-match-6", + label: "Admin-1 capital", + symbol: { + radius: 5, + type: "circle", + color: "#76b7b2", + fillOpacity: 0.7, + strokeWidth: 1, + strokeColor: "#76b7b2", + strokeOpacity: 1, + }, + }, + { + id: "0-circle-color-0-match-8", + label: "Admin-1 region capital", + symbol: { + radius: 5, + type: "circle", + color: "#59a14f", + fillOpacity: 0.7, + strokeWidth: 1, + strokeColor: "#59a14f", + strokeOpacity: 1, + }, + }, + { + id: "0-circle-color-0-match-10", + label: "Historic place", + symbol: { + radius: 5, + type: "circle", + color: "#edc949", + fillOpacity: 0.7, + strokeWidth: 1, + strokeColor: "#edc949", + strokeOpacity: 1, + }, + }, + { + id: "0-circle-color-0-match-12", + label: "Meteorological Station", + symbol: { + radius: 5, + type: "circle", + color: "#af7aa1", + fillOpacity: 0.7, + strokeWidth: 1, + strokeColor: "#af7aa1", + strokeOpacity: 1, + }, + }, + { + id: "0-circle-color-0-match-14", + label: "Populated Place", + symbol: { + radius: 5, + type: "circle", + color: "#ff9da7", + fillOpacity: 0.7, + strokeWidth: 1, + strokeColor: "#ff9da7", + strokeOpacity: 1, + }, + }, + { + id: "0-circle-color-0-match-16", + label: "Populated place", + symbol: { + radius: 5, + type: "circle", + color: "#9c755f", + fillOpacity: 0.7, + strokeWidth: 1, + strokeColor: "#9c755f", + strokeOpacity: 1, + }, + }, + { + id: "0-circle-color-0-match-18", + label: "Scientific station", + symbol: { + radius: 15, + type: "circle", + color: "#bab0ab", + fillOpacity: 0.7, + strokeWidth: 5, + strokeColor: "#eb0000", + strokeOpacity: 1, + }, + }, + { + id: "0-circle-color-0-match-default", + label: "default", + symbol: { + radius: 5, + type: "circle", + color: "black", + fillOpacity: 0.7, + strokeWidth: 1, + strokeColor: "black", + strokeOpacity: 1, + }, + }, + ], + }, + ], + }, + }, + "EEZ (complex case+match)": { + input: [ + { + type: "fill", + paint: { + "fill-color": "#FF0000", + "fill-opacity": ["case", ["==", ["get", "ISO_SOV1"], "MEX"], 0.15, 0], + }, + layout: {}, + }, + { + type: "line", + paint: { + "line-color": [ + "case", + ["==", ["get", "ISO_SOV1"], "USA"], + "#FF0000", + "blue", + ], + "line-width": ["match", ["get", "ISO_SOV1"], "MEX", 4, 1], + "line-opacity": 0.75, + }, + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "1-line-width-0-match", + type: "GLLegendListPanel", + label: "ISO_SOV1", + items: [ + { + id: "1-line-width-0-match-0", + label: "MEX", + symbol: { + type: "fill", + color: "#FF0000", + extruded: false, + fillOpacity: 0.15, + strokeWidth: 4, + strokeColor: "blue", + strokeOpacity: 0.75, + }, + }, + { + id: "1-line-width-0-match-USA", + label: "USA", + symbol: { + type: "fill", + color: "#FF0000", + extruded: false, + fillOpacity: 0, + strokeWidth: 1, + strokeColor: "#FF0000", + strokeOpacity: 0.75, + }, + }, + { + id: "1-line-width-0-match-default", + label: "default", + symbol: { + type: "fill", + color: "#FF0000", + extruded: false, + fillOpacity: 0, + strokeWidth: 1, + strokeColor: "blue", + strokeOpacity: 0.75, + }, + }, + ], + }, + ], + }, + }, + "EEZ (by country code)": { + input: [ + { + type: "line", + paint: { + "line-color": "#FC49A3", + "line-width": 1, + "line-opacity": 0.75, + }, + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + { + type: "fill", + paint: { + "fill-color": [ + "match", + ["get", "ISO_SOV1"], + "AFG", + "#4e79a7", + "AGO", + "#f28e2c", + "ALB", + "#e15759", + "AND", + "#76b7b2", + "ARE", + "#59a14f", + "ARG", + "#edc949", + "ARM", + "#af7aa1", + "ATA", + "#ff9da7", + "ATG", + "#9c755f", + "AUS", + "#bab0ab", + "AUT", + "#4e79a7", + "AZE", + "#f28e2c", + "BDI", + "#e15759", + "BEL", + "#76b7b2", + "BEN", + "#59a14f", + "BFA", + "#edc949", + "BGD", + "#af7aa1", + "BGR", + "#ff9da7", + "BHR", + "#9c755f", + "BHS", + "#bab0ab", + "BIH", + "#4e79a7", + "BLR", + "#f28e2c", + "BLZ", + "#e15759", + "BOL", + "#76b7b2", + "BRA", + "#59a14f", + "BRB", + "#edc949", + "BRN", + "#af7aa1", + "BTN", + "#ff9da7", + "BWA", + "#9c755f", + "CAF", + "#bab0ab", + "CAN", + "#4e79a7", + "CHE", + "#f28e2c", + "CHL", + "#e15759", + "CHN", + "#76b7b2", + "CIV", + "#59a14f", + "CMR", + "#edc949", + "COD", + "#af7aa1", + "COG", + "#ff9da7", + "COL", + "#9c755f", + "COM", + "#bab0ab", + "CPV", + "#4e79a7", + "CRI", + "#f28e2c", + "CUB", + "#e15759", + "CYP", + "#76b7b2", + "CZE", + "#59a14f", + "DEU", + "#edc949", + "DJI", + "#af7aa1", + "DMA", + "#ff9da7", + "DNK", + "#9c755f", + "DOM", + "#bab0ab", + "DZA", + "#4e79a7", + "ECU", + "#f28e2c", + "EGY", + "#e15759", + "ERI", + "#76b7b2", + "ESH", + "#59a14f", + "ESP", + "#edc949", + "EST", + "#af7aa1", + "ETH", + "#ff9da7", + "FIN", + "#9c755f", + "FJI", + "#bab0ab", + "FRA", + "#4e79a7", + "FSM", + "#f28e2c", + "GAB", + "#e15759", + "GBR", + "#76b7b2", + "GEO", + "#59a14f", + "GHA", + "#edc949", + "GIB", + "#af7aa1", + "GIN", + "#ff9da7", + "GMB", + "#9c755f", + "GNB", + "#bab0ab", + "GNQ", + "#4e79a7", + "GRC", + "#f28e2c", + "GRD", + "#e15759", + "GTM", + "#76b7b2", + "GUY", + "#59a14f", + "HND", + "#edc949", + "HRV", + "#af7aa1", + "HTI", + "#ff9da7", + "HUN", + "#9c755f", + "IDN", + "#bab0ab", + "IND", + "#4e79a7", + "IRL", + "#f28e2c", + "IRN", + "#e15759", + "IRQ", + "#76b7b2", + "ISL", + "#59a14f", + "ISR", + "#edc949", + "ITA", + "#af7aa1", + "JAM", + "#ff9da7", + "JOR", + "#9c755f", + "JPN", + "#bab0ab", + "KAZ", + "#4e79a7", + "KEN", + "#f28e2c", + "KGZ", + "#e15759", + "KHM", + "#76b7b2", + "KIR", + "#59a14f", + "KNA", + "#edc949", + "KOR", + "#af7aa1", + "KWT", + "#ff9da7", + "LAO", + "#9c755f", + "LBN", + "#bab0ab", + "black", + ], + "fill-opacity": 0.8, + }, + layout: {}, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "0-fill-color-0-match", + type: "GLLegendListPanel", + label: "ISO_SOV1", + items: [ + { + id: "0-fill-color-0-match-0", + label: "AFG", + symbol: { + type: "fill", + color: "#4e79a7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-2", + label: "AGO", + symbol: { + type: "fill", + color: "#f28e2c", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-4", + label: "ALB", + symbol: { + type: "fill", + color: "#e15759", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-6", + label: "AND", + symbol: { + type: "fill", + color: "#76b7b2", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-8", + label: "ARE", + symbol: { + type: "fill", + color: "#59a14f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-10", + label: "ARG", + symbol: { + type: "fill", + color: "#edc949", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-12", + label: "ARM", + symbol: { + type: "fill", + color: "#af7aa1", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-14", + label: "ATA", + symbol: { + type: "fill", + color: "#ff9da7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-16", + label: "ATG", + symbol: { + type: "fill", + color: "#9c755f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-18", + label: "AUS", + symbol: { + type: "fill", + color: "#bab0ab", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-20", + label: "AUT", + symbol: { + type: "fill", + color: "#4e79a7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-22", + label: "AZE", + symbol: { + type: "fill", + color: "#f28e2c", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-24", + label: "BDI", + symbol: { + type: "fill", + color: "#e15759", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-26", + label: "BEL", + symbol: { + type: "fill", + color: "#76b7b2", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-28", + label: "BEN", + symbol: { + type: "fill", + color: "#59a14f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-30", + label: "BFA", + symbol: { + type: "fill", + color: "#edc949", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-32", + label: "BGD", + symbol: { + type: "fill", + color: "#af7aa1", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-34", + label: "BGR", + symbol: { + type: "fill", + color: "#ff9da7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-36", + label: "BHR", + symbol: { + type: "fill", + color: "#9c755f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-38", + label: "BHS", + symbol: { + type: "fill", + color: "#bab0ab", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-40", + label: "BIH", + symbol: { + type: "fill", + color: "#4e79a7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-42", + label: "BLR", + symbol: { + type: "fill", + color: "#f28e2c", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-44", + label: "BLZ", + symbol: { + type: "fill", + color: "#e15759", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-46", + label: "BOL", + symbol: { + type: "fill", + color: "#76b7b2", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-48", + label: "BRA", + symbol: { + type: "fill", + color: "#59a14f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-50", + label: "BRB", + symbol: { + type: "fill", + color: "#edc949", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-52", + label: "BRN", + symbol: { + type: "fill", + color: "#af7aa1", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-54", + label: "BTN", + symbol: { + type: "fill", + color: "#ff9da7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-56", + label: "BWA", + symbol: { + type: "fill", + color: "#9c755f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-58", + label: "CAF", + symbol: { + type: "fill", + color: "#bab0ab", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-60", + label: "CAN", + symbol: { + type: "fill", + color: "#4e79a7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-62", + label: "CHE", + symbol: { + type: "fill", + color: "#f28e2c", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-64", + label: "CHL", + symbol: { + type: "fill", + color: "#e15759", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-66", + label: "CHN", + symbol: { + type: "fill", + color: "#76b7b2", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-68", + label: "CIV", + symbol: { + type: "fill", + color: "#59a14f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-70", + label: "CMR", + symbol: { + type: "fill", + color: "#edc949", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-72", + label: "COD", + symbol: { + type: "fill", + color: "#af7aa1", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-74", + label: "COG", + symbol: { + type: "fill", + color: "#ff9da7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-76", + label: "COL", + symbol: { + type: "fill", + color: "#9c755f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-78", + label: "COM", + symbol: { + type: "fill", + color: "#bab0ab", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-80", + label: "CPV", + symbol: { + type: "fill", + color: "#4e79a7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-82", + label: "CRI", + symbol: { + type: "fill", + color: "#f28e2c", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-84", + label: "CUB", + symbol: { + type: "fill", + color: "#e15759", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-86", + label: "CYP", + symbol: { + type: "fill", + color: "#76b7b2", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-88", + label: "CZE", + symbol: { + type: "fill", + color: "#59a14f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-90", + label: "DEU", + symbol: { + type: "fill", + color: "#edc949", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-92", + label: "DJI", + symbol: { + type: "fill", + color: "#af7aa1", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-94", + label: "DMA", + symbol: { + type: "fill", + color: "#ff9da7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-96", + label: "DNK", + symbol: { + type: "fill", + color: "#9c755f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-98", + label: "DOM", + symbol: { + type: "fill", + color: "#bab0ab", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-100", + label: "DZA", + symbol: { + type: "fill", + color: "#4e79a7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-102", + label: "ECU", + symbol: { + type: "fill", + color: "#f28e2c", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-104", + label: "EGY", + symbol: { + type: "fill", + color: "#e15759", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-106", + label: "ERI", + symbol: { + type: "fill", + color: "#76b7b2", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-108", + label: "ESH", + symbol: { + type: "fill", + color: "#59a14f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-110", + label: "ESP", + symbol: { + type: "fill", + color: "#edc949", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-112", + label: "EST", + symbol: { + type: "fill", + color: "#af7aa1", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-114", + label: "ETH", + symbol: { + type: "fill", + color: "#ff9da7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-116", + label: "FIN", + symbol: { + type: "fill", + color: "#9c755f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-118", + label: "FJI", + symbol: { + type: "fill", + color: "#bab0ab", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-120", + label: "FRA", + symbol: { + type: "fill", + color: "#4e79a7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-122", + label: "FSM", + symbol: { + type: "fill", + color: "#f28e2c", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-124", + label: "GAB", + symbol: { + type: "fill", + color: "#e15759", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-126", + label: "GBR", + symbol: { + type: "fill", + color: "#76b7b2", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-128", + label: "GEO", + symbol: { + type: "fill", + color: "#59a14f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-130", + label: "GHA", + symbol: { + type: "fill", + color: "#edc949", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-132", + label: "GIB", + symbol: { + type: "fill", + color: "#af7aa1", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-134", + label: "GIN", + symbol: { + type: "fill", + color: "#ff9da7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-136", + label: "GMB", + symbol: { + type: "fill", + color: "#9c755f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-138", + label: "GNB", + symbol: { + type: "fill", + color: "#bab0ab", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-140", + label: "GNQ", + symbol: { + type: "fill", + color: "#4e79a7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-142", + label: "GRC", + symbol: { + type: "fill", + color: "#f28e2c", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-144", + label: "GRD", + symbol: { + type: "fill", + color: "#e15759", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-146", + label: "GTM", + symbol: { + type: "fill", + color: "#76b7b2", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-148", + label: "GUY", + symbol: { + type: "fill", + color: "#59a14f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-150", + label: "HND", + symbol: { + type: "fill", + color: "#edc949", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-152", + label: "HRV", + symbol: { + type: "fill", + color: "#af7aa1", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-154", + label: "HTI", + symbol: { + type: "fill", + color: "#ff9da7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-156", + label: "HUN", + symbol: { + type: "fill", + color: "#9c755f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-158", + label: "IDN", + symbol: { + type: "fill", + color: "#bab0ab", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-160", + label: "IND", + symbol: { + type: "fill", + color: "#4e79a7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-162", + label: "IRL", + symbol: { + type: "fill", + color: "#f28e2c", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-164", + label: "IRN", + symbol: { + type: "fill", + color: "#e15759", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-166", + label: "IRQ", + symbol: { + type: "fill", + color: "#76b7b2", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-168", + label: "ISL", + symbol: { + type: "fill", + color: "#59a14f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-170", + label: "ISR", + symbol: { + type: "fill", + color: "#edc949", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-172", + label: "ITA", + symbol: { + type: "fill", + color: "#af7aa1", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-174", + label: "JAM", + symbol: { + type: "fill", + color: "#ff9da7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-176", + label: "JOR", + symbol: { + type: "fill", + color: "#9c755f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-178", + label: "JPN", + symbol: { + type: "fill", + color: "#bab0ab", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-180", + label: "KAZ", + symbol: { + type: "fill", + color: "#4e79a7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-182", + label: "KEN", + symbol: { + type: "fill", + color: "#f28e2c", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-184", + label: "KGZ", + symbol: { + type: "fill", + color: "#e15759", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-186", + label: "KHM", + symbol: { + type: "fill", + color: "#76b7b2", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-188", + label: "KIR", + symbol: { + type: "fill", + color: "#59a14f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-190", + label: "KNA", + symbol: { + type: "fill", + color: "#edc949", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-192", + label: "KOR", + symbol: { + type: "fill", + color: "#af7aa1", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-194", + label: "KWT", + symbol: { + type: "fill", + color: "#ff9da7", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-196", + label: "LAO", + symbol: { + type: "fill", + color: "#9c755f", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-198", + label: "LBN", + symbol: { + type: "fill", + color: "#bab0ab", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-color-0-match-default", + label: "default", + symbol: { + type: "fill", + color: "black", + extruded: false, + fillOpacity: 0.8, + strokeWidth: 1, + strokeColor: "#FC49A3", + strokeOpacity: 0.75, + }, + }, + ], + }, + { + id: "remaining-layer-0", + type: "GLLegendListPanel", + items: [ + { + id: "remaining-layer-single-child", + symbol: { + type: "fill", + color: "transparent", + fillOpacity: 0, + strokeWidth: 1, + strokeColor: "#FC49A3", + extruded: false, + strokeOpacity: 0.75, + }, + label: "", + }, + ], + }, + ], + }, + }, + "EEZ (complex, case only)": { + input: [ + { + type: "fill", + paint: { + "fill-color": "#FF0000", + "fill-opacity": ["case", ["==", ["get", "ISO_SOV1"], "MEX"], 0.15, 0], + }, + layout: {}, + }, + { + type: "line", + paint: { + "line-color": [ + "case", + ["==", ["get", "ISO_SOV1"], "USA"], + "#FF0000", + "blue", + ], + "line-width": ["case", ["==", ["get", "ISO_SOV1"], "MEX"], 4, 1], + "line-opacity": 0.75, + }, + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "0-fill-opacity-case", + type: "GLLegendListPanel", + label: "ISO_SOV1", + items: [ + { + id: "0-fill-opacity-case-0", + label: "MEX", + symbol: { + type: "fill", + color: "#FF0000", + extruded: false, + fillOpacity: 0.15, + strokeWidth: 4, + strokeColor: "blue", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-opacity-case-1", + label: "USA", + symbol: { + type: "fill", + color: "#FF0000", + extruded: false, + fillOpacity: 0, + strokeWidth: 1, + strokeColor: "#FF0000", + strokeOpacity: 0.75, + }, + }, + { + id: "0-fill-opacity-case-default", + label: "default", + symbol: { + type: "fill", + color: "#FF0000", + extruded: false, + fillOpacity: 0, + strokeWidth: 1, + strokeColor: "blue", + strokeOpacity: 0.75, + }, + }, + ], + }, + ], + }, + }, + "EEZ (filter by country)": { + input: [ + { + type: "line", + paint: { + "line-color": "rgb(110,110,110)", + "line-width": 1, + "line-opacity": 1, + }, + layout: {}, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(252,215,215,1)", + "fill-outline-color": "rgba(110,110,110,1)", + }, + filter: ["==", "ISO_SOV1", "USA"], + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "default-line", + type: "GLLegendListPanel", + label: "ISO_SOV1", + items: [ + { + id: "0-filter-only", + label: "USA", + symbol: { + type: "fill", + color: "rgba(252,215,215,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + strokeColor: "rgb(110,110,110)", + strokeOpacity: 1, + }, + }, + { + id: "default-line", + symbol: { + type: "fill", + color: "transparent", + fillOpacity: 0, + strokeWidth: 1, + strokeColor: "rgb(110,110,110)", + extruded: false, + strokeOpacity: 1, + }, + label: "default", + }, + ], + }, + ], + }, + }, + "EEZ (complex w/case+filters)": { + input: [ + { + type: "fill", + paint: { + "fill-color": "#FF0000", + "fill-opacity": ["case", ["==", ["get", "ISO_SOV1"], "MEX"], 0.15, 0], + }, + layout: {}, + }, + { + type: "line", + paint: { + "line-color": "#FF0000", + "line-width": 1, + "line-opacity": 0.75, + }, + filter: ["==", "ISO_SOV1", "USA"], + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + { + type: "line", + paint: { + "line-color": "blue", + "line-width": ["match", ["get", "ISO_SOV1"], "MEX", 4, 1], + "line-opacity": 0.75, + }, + filter: ["!=", "ISO_SOV1", "USA"], + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "ISO_SOV1%%line", + type: "GLLegendListPanel", + label: "ISO_SOV1", + items: [ + { + id: "0-filter-only", + label: "USA", + symbol: { + type: "fill", + color: "transparent", + fillOpacity: 0, + strokeWidth: 1, + strokeColor: "#FF0000", + extruded: false, + strokeOpacity: 0.75, + }, + }, + { + id: "2-line-width-0-match-0", + label: "MEX", + symbol: { + type: "fill", + color: "#FF0000", + extruded: false, + fillOpacity: 0.15, + strokeWidth: 4, + strokeColor: "blue", + strokeOpacity: 0.75, + }, + }, + { + id: "2-line-width-0-match-default", + label: "default", + symbol: { + type: "fill", + color: "#FF0000", + extruded: false, + fillOpacity: 0, + strokeWidth: 1, + strokeColor: "blue", + strokeOpacity: 0.75, + }, + }, + ], + }, + ], + }, + }, + "EEZ (complex, only filters)": { + input: [ + { + type: "fill", + paint: { + "fill-color": "#FF0000", + "fill-opacity": 0.15, + }, + filter: ["==", "ISO_SOV1", "MEX"], + layout: {}, + }, + { + type: "line", + paint: { + "line-color": "red", + "line-width": 1, + "line-opacity": 0.75, + }, + filter: ["==", "ISO_SOV1", "USA"], + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + { + type: "line", + paint: { + "line-color": "blue", + "line-width": 1, + "line-opacity": 0.75, + }, + filter: ["all", ["!=", "ISO_SOV1", "USA"], ["!=", "ISO_SOV1", "MEX"]], + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + { + type: "line", + paint: { + "line-color": "blue", + "line-width": 4, + "line-opacity": 0.75, + }, + filter: ["==", "ISO_SOV1", "MEX"], + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "ISO_SOV1%%fill", + type: "GLLegendListPanel", + label: "ISO_SOV1", + items: [ + { + id: "0-filter-only", + label: "MEX", + symbol: { + type: "fill", + color: "#FF0000", + extruded: false, + fillOpacity: 0.15, + strokeWidth: 4, + strokeColor: "blue", + strokeOpacity: 0.75, + }, + }, + { + id: "1-filter-only", + label: "USA", + symbol: { + type: "fill", + color: "transparent", + fillOpacity: 0, + strokeWidth: 1, + strokeColor: "red", + extruded: false, + strokeOpacity: 0.75, + }, + }, + { + id: "2-filter-only", + label: "!= USA, != MEX", + symbol: { + type: "fill", + color: "transparent", + fillOpacity: 0, + strokeWidth: 1, + strokeColor: "blue", + extruded: false, + strokeOpacity: 0.75, + }, + }, + ], + }, + ], + }, + }, + "EEZ (filtered lines w/shared fill)": { + input: [ + { + type: "fill", + paint: { + "fill-color": "#FF0000", + "fill-opacity": 0.15, + }, + layout: {}, + }, + { + type: "line", + paint: { + "line-color": "red", + "line-width": 1, + "line-opacity": 0.75, + }, + filter: ["==", "ISO_SOV1", "USA"], + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + { + type: "line", + paint: { + "line-color": "blue", + "line-width": 1, + "line-opacity": 0.75, + }, + filter: ["all", ["!=", "ISO_SOV1", "USA"], ["!=", "ISO_SOV1", "MEX"]], + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + { + type: "line", + paint: { + "line-color": "blue", + "line-width": 4, + "line-opacity": 0.75, + }, + filter: ["==", "ISO_SOV1", "MEX"], + layout: { + "line-cap": "round", + "line-join": "round", + visibility: "visible", + }, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "ISO_SOV1%%fill", + type: "GLLegendListPanel", + label: "ISO_SOV1", + items: [ + { + id: "1-filter-only", + label: "USA", + symbol: { + type: "fill", + color: "#FF0000", + extruded: false, + fillOpacity: 0.15, + strokeWidth: 1, + strokeColor: "red", + strokeOpacity: 0.75, + }, + }, + { + id: "2-filter-only", + label: "!= USA, != MEX", + symbol: { + type: "fill", + color: "#FF0000", + extruded: false, + fillOpacity: 0.15, + strokeWidth: 1, + strokeColor: "blue", + strokeOpacity: 0.75, + }, + }, + { + id: "3-filter-only", + label: "MEX", + symbol: { + type: "fill", + color: "#FF0000", + extruded: false, + fillOpacity: 0.15, + strokeWidth: 4, + strokeColor: "blue", + strokeOpacity: 0.75, + }, + }, + ], + }, + ], + }, + }, + "Populated places text label": { + input: [ + { + type: "symbol", + paint: { + "text-halo-color": "white", + "text-halo-width": 1, + }, + layout: { + "text-size": [ + "match", + ["get", "featurecla"], + "Admin-0 capital", + 20, + "Admin-0 capital alt", + 20, + "Admin-0 region capital", + 20, + "Admin-1 capital", + 20, + "Admin-1 region capital", + 18, + "Historic place", + 12, + "Meteorological Station", + 12, + "Populated Place", + 12, + "Populated place", + 12, + "Scientific station", + 12, + 12, + ], + "text-field": ["get", "name"], + "text-anchor": "left", + "text-offset": [0.5, 0.5], + "symbol-sort-key": ["*", -1, ["get", "labelrank"]], + "symbol-placement": "point", + "icon-allow-overlap": true, + }, + }, + ], + output: { + type: "SimpleGLLegend", + symbol: { + type: "text", + color: "#000000", + fontFamily: "Open Sans Regular", + fontWeight: "normal", + fontStyle: "normal", + haloColor: "white", + haloWidth: 1, + }, + }, + }, + "Places (circle-color step)": { + input: [ + { + type: "circle", + paint: { + "circle-color": [ + "step", + ["get", "pop_max"], + "#0d0887", + 679, + "#0d0887", + 19542, + "#6a00a8", + 56032, + "#b12a90", + 142564, + "#e16462", + 441094, + "#fca636", + ], + "circle-radius": 5, + "circle-stroke-color": "black", + "circle-stroke-width": 1, + "circle-stroke-opacity": 0.5, + }, + }, + { + type: "symbol", + paint: { + "text-halo-color": "white", + "text-halo-width": 1, + }, + layout: { + "text-size": 12, + "text-field": ["get", "FEATURE_NA"], + "text-anchor": "left", + "text-offset": [0.5, 0.5], + "symbol-placement": "point", + }, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "0-circle-color-0-step", + type: "GLLegendStepPanel", + label: "pop_max", + steps: [ + { + id: "0-circle-color-0-step-first", + label: "< 679", + symbol: { + radius: 5, + type: "circle", + color: "#0d0887", + fillOpacity: 1, + strokeWidth: 1, + strokeColor: "black", + strokeOpacity: 0.5, + }, + }, + { + id: "0-circle-color-0-step-0", + label: "679", + symbol: { + radius: 5, + type: "circle", + color: "#0d0887", + fillOpacity: 1, + strokeWidth: 1, + strokeColor: "black", + strokeOpacity: 0.5, + }, + }, + { + id: "0-circle-color-0-step-2", + label: "19,542", + symbol: { + radius: 5, + type: "circle", + color: "#6a00a8", + fillOpacity: 1, + strokeWidth: 1, + strokeColor: "black", + strokeOpacity: 0.5, + }, + }, + { + id: "0-circle-color-0-step-4", + label: "56,032", + symbol: { + radius: 5, + type: "circle", + color: "#b12a90", + fillOpacity: 1, + strokeWidth: 1, + strokeColor: "black", + strokeOpacity: 0.5, + }, + }, + { + id: "0-circle-color-0-step-6", + label: "142,564", + symbol: { + radius: 5, + type: "circle", + color: "#e16462", + fillOpacity: 1, + strokeWidth: 1, + strokeColor: "black", + strokeOpacity: 0.5, + }, + }, + { + id: "0-circle-color-0-step-8", + label: "441,094", + symbol: { + radius: 5, + type: "circle", + color: "#fca636", + fillOpacity: 1, + strokeWidth: 1, + strokeColor: "black", + strokeOpacity: 0.5, + }, + }, + ], + }, + ], + }, + }, + "Places (case range w/no logical fallback)": { + input: [ + { + type: "symbol", + paint: { + "text-halo-color": "white", + "text-halo-width": 1, + }, + layout: { + "icon-size": [ + "case", + [">=", ["get", "pop_max"], 50000], + 2, + ["<=", ["get", "pop_max"], 50000], + 1, + 0.5, + ], + "text-size": 12, + "icon-image": "seasketch://sprites/1", + "text-field": ["get", "FEATURE_NA"], + "text-anchor": "left", + "text-offset": [0.5, 0.5], + "symbol-placement": "point", + }, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "0-icon-size-case", + type: "GLLegendListPanel", + label: "pop_max", + items: [ + { + id: "0-icon-size-case-0", + label: ">= 50,000", + symbol: { + type: "marker", + imageId: "seasketch://sprites/1", + haloColor: "white", + haloWidth: 1, + iconSize: 2, + }, + }, + { + id: "0-icon-size-case-2", + label: "<= 50,000", + symbol: { + type: "marker", + imageId: "seasketch://sprites/1", + haloColor: "white", + haloWidth: 1, + iconSize: 1, + }, + }, + { + id: "0-icon-size-case-default", + label: "default", + symbol: { + type: "marker", + imageId: "seasketch://sprites/1", + haloColor: "white", + haloWidth: 1, + iconSize: 1, + }, + }, + ], + }, + ], + }, + }, + "Places (case w/logical fallback)": { + input: [ + { + type: "symbol", + paint: { + "text-halo-color": "white", + "text-halo-width": 1, + }, + layout: { + "icon-size": [ + "case", + [">=", ["get", "pop_max"], 50000], + 2, + ["<=", ["get", "pop_max"], 20000], + 1, + 0.5, + ], + "text-size": 12, + "icon-image": "seasketch://sprites/1", + "text-field": ["get", "FEATURE_NA"], + "text-anchor": "left", + "text-offset": [0.5, 0.5], + "symbol-placement": "point", + }, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "0-icon-size-case", + type: "GLLegendListPanel", + label: "pop_max", + items: [ + { + id: "0-icon-size-case-0", + label: ">= 50,000", + symbol: { + type: "marker", + imageId: "seasketch://sprites/1", + haloColor: "white", + haloWidth: 1, + iconSize: 2, + }, + }, + { + id: "0-icon-size-case-2", + label: "<= 20,000", + symbol: { + type: "marker", + imageId: "seasketch://sprites/1", + haloColor: "white", + haloWidth: 1, + iconSize: 1, + }, + }, + { + id: "0-icon-size-case-default", + label: "default", + symbol: { + type: "marker", + imageId: "seasketch://sprites/1", + haloColor: "white", + haloWidth: 1, + iconSize: 1, + }, + }, + ], + }, + ], + }, + }, + "Places (text color by population)": { + input: [ + { + type: "symbol", + paint: { + "text-color": [ + "case", + [">=", ["get", "pop_max"], 2000000], + "red", + [">=", ["get", "pop_max"], 500000], + "orange", + "blue", + ], + "text-halo-color": "white", + "text-halo-width": 2, + }, + layout: { + "text-size": 11, + "text-field": ["get", "name"], + "text-anchor": "left", + "text-offset": [0.5, 0.5], + "symbol-placement": "point", + }, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "0-text-color-case", + type: "GLLegendListPanel", + label: "pop_max", + items: [ + { + id: "0-text-color-case-0", + label: ">= 2,000,000", + symbol: { + type: "text", + color: "red", + fontFamily: "Open Sans Regular", + fontWeight: "normal", + fontStyle: "normal", + haloColor: "white", + haloWidth: 2, + }, + }, + { + id: "0-text-color-case-2", + label: ">= 500,000", + symbol: { + type: "text", + color: "orange", + fontFamily: "Open Sans Regular", + fontWeight: "normal", + fontStyle: "normal", + haloColor: "white", + haloWidth: 2, + }, + }, + { + id: "0-text-color-case-default", + label: "default", + symbol: { + type: "text", + color: null, + fontFamily: "Open Sans Regular", + fontWeight: "normal", + fontStyle: "normal", + haloColor: "white", + haloWidth: 2, + }, + }, + ], + }, + ], + }, + }, + "state-data (choropleth, layer.metadata.label present)": { + input: [ + { + type: "fill", + paint: { + "fill-color": "rgba(49,163,84,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 145993.6237613576], + ["<=", "EmpArts", 290160], + ], + metadata: { + label: "145,995 to 290,160", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(49,163,84,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 73652.57252191499], + ["<=", "EmpArts", 145993.6237613576], + ], + metadata: { + label: "37,354 to 145,994", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(116,196,118,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 37352.65411538615], + ["<=", "EmpArts", 73652.57252191499], + ], + metadata: { + label: "37,354 to 73,653", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(186,228,179,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 19137.767080371515], + ["<=", "EmpArts", 37352.65411538615], + ], + metadata: { + label: "19,138 to 37,353", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(186,228,179,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 9997.743513853795], + ["<=", "EmpArts", 19137.767080371515], + ], + metadata: { + label: "9,999 to 19,138", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(237,248,233,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 5411.383431800605], + ["<=", "EmpArts", 9997.743513853795], + ], + metadata: { + label: "5,412 to 9,998", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(237,248,233,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: ["all", ["<=", "EmpArts", 5411.383431800605]], + metadata: { + label: "3,110 to 5,411", + }, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "EmpArts%%fill", + type: "GLLegendListPanel", + label: "EmpArts", + items: [ + { + id: "0-filter-only", + label: "145,995 to 290,160", + symbol: { + type: "fill", + color: "rgba(49,163,84,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "1-filter-only", + label: "37,354 to 145,994", + symbol: { + type: "fill", + color: "rgba(49,163,84,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "2-filter-only", + label: "37,354 to 73,653", + symbol: { + type: "fill", + color: "rgba(116,196,118,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "3-filter-only", + label: "19,138 to 37,353", + symbol: { + type: "fill", + color: "rgba(186,228,179,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "4-filter-only", + label: "9,999 to 19,138", + symbol: { + type: "fill", + color: "rgba(186,228,179,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "5-filter-only", + label: "5,412 to 9,998", + symbol: { + type: "fill", + color: "rgba(237,248,233,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "6-filter-only", + label: "3,110 to 5,411", + symbol: { + type: "fill", + color: "rgba(237,248,233,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + ], + }, + ], + }, + }, + "state-data (very complex)": { + input: [ + { + type: "fill", + paint: { + "fill-color": "rgba(49,163,84,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 145993.6237613576], + ["<=", "EmpArts", 290160], + ["!=", "NAME", "California"], + ], + metadata: { + label: "145,995 to 290,160", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(49,163,84,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 73652.57252191499], + ["<=", "EmpArts", 145993.6237613576], + ["!=", "NAME", "California"], + ], + metadata: { + label: "37,354 to 145,994", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(116,196,118,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 37352.65411538615], + ["<=", "EmpArts", 73652.57252191499], + ["!=", "NAME", "California"], + ], + metadata: { + label: "37,354 to 73,653", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(186,228,179,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 19137.767080371515], + ["<=", "EmpArts", 37352.65411538615], + ["!=", "NAME", "California"], + ], + metadata: { + label: "19,138 to 37,353", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(186,228,179,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 9997.743513853795], + ["<=", "EmpArts", 19137.767080371515], + ["!=", "NAME", "California"], + ], + metadata: { + label: "9,999 to 19,138", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(237,248,233,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 5411.383431800605], + ["<=", "EmpArts", 9997.743513853795], + ["!=", "NAME", "California"], + ], + metadata: { + label: "5,412 to 9,998", + }, + }, + { + type: "fill", + paint: { + "fill-color": "rgba(237,248,233,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + ["<=", "EmpArts", 5411.383431800605], + ["!=", "NAME", "California"], + ], + metadata: { + label: "3,110 to 5,411", + }, + }, + { + type: "line", + paint: { + "line-color": [ + "case", + ["==", ["get", "SUB_REGION"], "Pacific"], + "blue", + "rgba(0,0,0,0.2)", + ], + }, + }, + { + type: "line", + paint: { + "line-color": "red", + }, + filter: ["==", ["get", "NAME"], "California"], + }, + { + type: "fill", + paint: { + "fill-color": "#9c755f", + "fill-opacity": 0.5, + }, + filter: ["==", "NAME", "California"], + }, + { + type: "symbol", + layout: { + "icon-image": [ + "match", + ["get", "NAME"], + "California", + "seasketch://sprites/1", + "seasketch://sprites/2", + ], + }, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "10-icon-image-0-match", + type: "GLLegendListPanel", + label: "NAME", + items: [ + { + id: "10-icon-image-0-match-0", + label: "California", + symbol: { + type: "marker", + imageId: "seasketch://sprites/1", + haloColor: "rgba(0, 0, 0, 0)", + iconSize: 1, + }, + }, + { + id: "7-filter-only", + label: "California", + symbol: { + type: "fill", + color: "#9c755f", + extruded: false, + fillOpacity: 0.5, + strokeWidth: 1, + strokeColor: "red", + strokeOpacity: 1, + }, + }, + { + id: "10-icon-image-0-match-default", + label: "default", + symbol: { + type: "marker", + imageId: "seasketch://sprites/2", + haloColor: "rgba(0, 0, 0, 0)", + iconSize: 1, + }, + }, + ], + }, + { + id: "8-line-color-case", + type: "GLLegendListPanel", + label: "SUB_REGION", + items: [ + { + id: "8-line-color-case-0", + label: "Pacific", + symbol: { + type: "fill", + color: "transparent", + fillOpacity: 0, + strokeWidth: 1, + strokeColor: "blue", + extruded: false, + strokeOpacity: 1, + }, + }, + { + id: "8-line-color-case-default", + label: "default", + symbol: { + type: "fill", + color: "transparent", + fillOpacity: 0, + strokeWidth: 1, + strokeColor: "rgba(0,0,0,0.2)", + extruded: false, + strokeOpacity: 1, + }, + }, + ], + }, + { + id: '[["!=",["get","NAME"],"California"]]', + type: "GLLegendFilterPanel", + label: "NAME != California", + children: [ + { + id: "EmpArts%%fill", + type: "GLLegendListPanel", + label: "EmpArts", + items: [ + { + id: "0-filter-only", + label: "145,995 to 290,160", + symbol: { + type: "fill", + color: "rgba(49,163,84,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "1-filter-only", + label: "37,354 to 145,994", + symbol: { + type: "fill", + color: "rgba(49,163,84,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "2-filter-only", + label: "37,354 to 73,653", + symbol: { + type: "fill", + color: "rgba(116,196,118,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "3-filter-only", + label: "19,138 to 37,353", + symbol: { + type: "fill", + color: "rgba(186,228,179,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "4-filter-only", + label: "9,999 to 19,138", + symbol: { + type: "fill", + color: "rgba(186,228,179,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "5-filter-only", + label: "5,412 to 9,998", + symbol: { + type: "fill", + color: "rgba(237,248,233,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "6-filter-only", + label: "3,110 to 5,411", + symbol: { + type: "fill", + color: "rgba(237,248,233,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + ], + }, + ], + }, + ], + }, + }, + "Places (single icon image)": { + input: [ + { + type: "symbol", + layout: { + "icon-image": "seasketch://sprites/1", + }, + }, + ], + output: { + type: "SimpleGLLegend", + symbol: { + type: "marker", + imageId: "seasketch://sprites/1", + haloColor: "rgba(0, 0, 0, 0)", + iconSize: 1, + }, + }, + }, + "state-data (choropleth, no metadata labels)": { + input: [ + { + type: "fill", + paint: { + "fill-color": "rgba(49,163,84,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 145993.6237613576], + ["<=", "EmpArts", 290160], + ], + }, + { + type: "fill", + paint: { + "fill-color": "rgba(49,163,84,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 73652.57252191499], + ["<=", "EmpArts", 145993.6237613576], + ], + }, + { + type: "fill", + paint: { + "fill-color": "rgba(116,196,118,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 37352.65411538615], + ["<=", "EmpArts", 73652.57252191499], + ], + }, + { + type: "fill", + paint: { + "fill-color": "rgba(186,228,179,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 19137.767080371515], + ["<=", "EmpArts", 37352.65411538615], + ], + }, + { + type: "fill", + paint: { + "fill-color": "rgba(186,228,179,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 9997.743513853795], + ["<=", "EmpArts", 19137.767080371515], + ], + }, + { + type: "fill", + paint: { + "fill-color": "rgba(237,248,233,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: [ + "all", + [">", "EmpArts", 5411.383431800605], + ["<=", "EmpArts", 9997.743513853795], + ], + }, + { + type: "fill", + paint: { + "fill-color": "rgba(237,248,233,1)", + "fill-outline-color": "rgba(0,0,0,1)", + }, + filter: ["all", ["<=", "EmpArts", 5411.383431800605]], + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "EmpArts%%fill", + type: "GLLegendListPanel", + label: "EmpArts", + items: [ + { + id: "0-filter-only", + label: "145,993.624 - 290,160", + symbol: { + type: "fill", + color: "rgba(49,163,84,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "1-filter-only", + label: "73,652.573 - 145,993.624", + symbol: { + type: "fill", + color: "rgba(49,163,84,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "2-filter-only", + label: "37,352.654 - 73,652.573", + symbol: { + type: "fill", + color: "rgba(116,196,118,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "3-filter-only", + label: "19,137.767 - 37,352.654", + symbol: { + type: "fill", + color: "rgba(186,228,179,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "4-filter-only", + label: "9,997.744 - 19,137.767", + symbol: { + type: "fill", + color: "rgba(186,228,179,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "5-filter-only", + label: "5,411.383 - 9,997.744", + symbol: { + type: "fill", + color: "rgba(237,248,233,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + { + id: "6-filter-only", + label: "<= 5,411.383", + symbol: { + type: "fill", + color: "rgba(237,248,233,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(0,0,0,1)", + }, + }, + ], + }, + ], + }, + }, + "Geology (filter-based arcgis server layer)": { + input: [ + { + id: "e4564597-7f97-4cf5-90e7-06dea3a3c27d", + type: "fill", + source: "7fa8c2ef-5c23-4ace-993e-acd3b3fcf5ad", + paint: { + "fill-color": "rgba(202,252,192,1)", + "fill-outline-color": "rgba(110,110,110,1)", + }, + metadata: { + label: "Alluvium", + }, + filter: ["==", "Name", "Alluvium"], + }, + { + id: "4b4e8dcc-f75e-45c8-a84e-5261c6f7b0d8", + type: "fill", + source: "7fa8c2ef-5c23-4ace-993e-acd3b3fcf5ad", + paint: { + "fill-color": "rgba(230,179,252,1)", + "fill-outline-color": "rgba(110,110,110,1)", + }, + metadata: { + label: "Fanglomerate", + }, + filter: ["==", "Name", "Fanglomerate"], + }, + { + id: "860f809e-6391-49e9-a4a7-a5536bd282a9", + type: "fill", + source: "7fa8c2ef-5c23-4ace-993e-acd3b3fcf5ad", + paint: { + "fill-color": "rgba(252,193,184,1)", + "fill-outline-color": "rgba(110,110,110,1)", + }, + metadata: { + label: "Rincon Shale", + }, + filter: ["==", "Name", "Rincon Shale"], + }, + { + id: "9419460d-dba4-4427-a355-c8ae08275f2b", + type: "fill", + source: "7fa8c2ef-5c23-4ace-993e-acd3b3fcf5ad", + paint: { + "fill-color": "rgba(179,230,252,1)", + "fill-outline-color": "rgba(110,110,110,1)", + }, + metadata: { + label: "Sespe Formation", + }, + filter: ["==", "Name", "Sespe Formation"], + }, + { + id: "3d546844-3c43-4d5e-8dad-3b270c852c77", + type: "fill", + source: "7fa8c2ef-5c23-4ace-993e-acd3b3fcf5ad", + paint: { + "fill-color": "rgba(252,243,212,1)", + "fill-outline-color": "rgba(110,110,110,1)", + }, + metadata: { + label: "Vaqueros Sandstone", + }, + filter: ["==", "Name", "Vaqueros Sandstone"], + }, + { + id: "42c50aaa-9757-4d49-b01d-05f583f117c9", + type: "fill", + source: "7fa8c2ef-5c23-4ace-993e-acd3b3fcf5ad", + paint: { + "fill-color": "rgba(130,130,130,1)", + "fill-outline-color": "rgba(110,110,110,1)", + }, + filter: [ + "!", + [ + "any", + ["==", ["get", "Name"], "Alluvium"], + ["==", ["get", "Name"], "Fanglomerate"], + ["==", ["get", "Name"], "Rincon Shale"], + ["==", ["get", "Name"], "Sespe Formation"], + ["==", ["get", "Name"], "Vaqueros Sandstone"], + ], + ], + metadata: { + label: "Default", + }, + }, + ], + output: { + type: "MultipleSymbolGLLegend", + panels: [ + { + id: "Name%%fill", + type: "GLLegendListPanel", + label: "Name", + items: [ + { + id: "0-filter-only", + label: "Alluvium", + symbol: { + type: "fill", + color: "rgba(202,252,192,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(110,110,110,1)", + }, + }, + { + id: "1-filter-only", + label: "Fanglomerate", + symbol: { + type: "fill", + color: "rgba(230,179,252,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(110,110,110,1)", + }, + }, + { + id: "2-filter-only", + label: "Rincon Shale", + symbol: { + type: "fill", + color: "rgba(252,193,184,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(110,110,110,1)", + }, + }, + { + id: "3-filter-only", + label: "Sespe Formation", + symbol: { + type: "fill", + color: "rgba(179,230,252,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(110,110,110,1)", + }, + }, + { + id: "4-filter-only", + label: "Vaqueros Sandstone", + symbol: { + type: "fill", + color: "rgba(252,243,212,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(110,110,110,1)", + }, + }, + { + id: "5-filter-only", + label: "Default", + symbol: { + type: "fill", + color: "rgba(130,130,130,1)", + extruded: false, + fillOpacity: 1, + strokeWidth: 1, + dashed: false, + strokeColor: "rgba(110,110,110,1)", + }, + }, + ], + }, + ], + }, + }, +}; diff --git a/packages/client/src/dataLayers/legends/legends.stories.tsx b/packages/client/src/dataLayers/legends/legends.stories.tsx new file mode 100644 index 000000000..eb892e7d3 --- /dev/null +++ b/packages/client/src/dataLayers/legends/legends.stories.tsx @@ -0,0 +1,57 @@ +import { Story } from "@storybook/react/types-6-0"; +import Legend from "../Legend"; +import { testCases } from "./legendKitchenSinkTestCases"; +import { + SeaSketchGlLayer, + compileLegendFromGLStyleLayers, +} from "./compileLegend"; + +const defaultArgs = { + zOrder: {}, + opacity: {}, + hiddenItems: [], +}; + +export default { + title: "Legends", + component: Legend, + args: { + zOrder: {}, + opacity: {}, + hiddenItems: [], + }, +}; + +export const Legends = () => { + return ( +
    +
      + {Object.keys(testCases).map((testCaseName) => { + const data = compileLegendFromGLStyleLayers( + // @ts-ignore + testCases[testCaseName].input as SeaSketchGlLayer[], + "vector" + ); + return ( +
      + {/*

      {testCaseName}

      */} + {/* @ts-ignore */} + +
      + ); + })} +
    +
    + ); +}; diff --git a/packages/client/src/dataLayers/legends/pluckGetExpressionsOfType.test.ts b/packages/client/src/dataLayers/legends/pluckGetExpressionsOfType.test.ts new file mode 100644 index 000000000..f78d13381 --- /dev/null +++ b/packages/client/src/dataLayers/legends/pluckGetExpressionsOfType.test.ts @@ -0,0 +1,207 @@ +import { pluckGetExpressionsOfType } from "./utils"; + +test("no expression present", () => { + // looking for interpolation of circle-radius + const output = pluckGetExpressionsOfType(5, "interpolate", "color"); + expect(output.facets.length).toBe(0); + expect(output.remainingValues).toBe(5); +}); + +test("single get expression present", () => { + // looking for interpolation of circle-radius + const output = pluckGetExpressionsOfType( + ["interpolate", ["linear"], ["get", "population"], 0, 200, 0, 20], + "interpolate", + "number" + ); + expect(output.facets.length).toBe(1); + const facet = output.facets[0]; + expect(facet.expression[0]).toBe("interpolate"); + expect(facet.filters.length).toBe(0); + expect(output.remainingValues).toBe(null); +}); + +test("single non-get expression present", () => { + // looking for interpolation of circle-radius + const output = pluckGetExpressionsOfType( + ["interpolate", ["linear"], ["zoom"], 0, 5, 16, 10], + "interpolate", + "number" + ); + expect(output.facets.length).toBe(0); + expect(output.remainingValues).toEqual([ + "interpolate", + ["linear"], + ["zoom"], + 0, + 5, + 16, + 10, + ]); +}); + +test("multiple get expressions conditional to a case statement", () => { + const expression = [ + "case", + ["==", ["get", "foo"], "bar"], + ["interpolate", ["linear"], ["get", "population"], 1, 0, 5, 200_000], + ["interpolate", ["linear"], ["get", "population"], 10, 0, 20, 200_000], + ]; + const output = pluckGetExpressionsOfType(expression, "interpolate", "number"); + expect(output.facets.length).toBe(2); + expect(output.facets[0].filters.length).toBe(1); + // fallback does not get a filter, but this behavior may change in the future + expect(output.facets[1].filters.length).toBe(0); + expect(output.remainingValues).toBe(null); +}); + +test("multiple get expressions conditional to a case statement with a static fallback", () => { + const expression = [ + "case", + ["==", ["get", "foo"], "bar"], + ["interpolate", ["linear"], ["get", "population"], 1, 0, 5, 200_000], + ["==", ["get", "foo"], "baz"], + ["interpolate", ["linear"], ["get", "population"], 10, 0, 20, 200_000], + 5, + ]; + const output = pluckGetExpressionsOfType(expression, "interpolate", "number"); + expect(output.facets.length).toBe(2); + expect(output.facets[0].filters.length).toBe(1); + const facet1 = output.facets[0]; + expect(facet1.expression[0]).toBe("interpolate"); + expect(facet1.filters[0][0]).toBe("=="); + expect(facet1.filters[0][1]).toEqual(["get", "foo"]); + expect(facet1.filters[0][2]).toBe("bar"); + expect(output.facets[1].filters.length).toBe(1); + expect(output.remainingValues).toBe(5); +}); + +test("conditional static value falling back to the interpolation of interest", () => { + const expression = [ + "case", + ["==", ["get", "foo"], "bar"], + 12, + ["interpolate", ["linear"], ["get", "population"], 1, 0, 5, 200_000], + ]; + const output = pluckGetExpressionsOfType(expression, "interpolate", "number"); + expect(output.facets.length).toBe(1); + expect(output.facets[0].filters.length).toBe(0); + expect(output.remainingValues).toEqual([ + "case", + ["==", ["get", "foo"], "bar"], + 12, + // Notice the fallback is set to null, indicating a fallback value should + // not be represented in subsequent list panels + null, + ]); +}); + +test("match expression with multiple interpolations", () => { + const expression = [ + "match", + ["get", "foo"], + "bar", + ["interpolate", ["linear"], ["get", "population"], 1, 0, 5, 200_000], + "baz", + ["interpolate", ["linear"], ["get", "population"], 10, 0, 20, 200_000], + ["interpolate", ["linear"], ["get", "population"], 20, 0, 50, 500_000], + ]; + const output = pluckGetExpressionsOfType(expression, "interpolate", "number"); + expect(output.facets.length).toBe(3); + expect(output.facets[0].filters.length).toBe(1); + expect(output.facets[1].filters.length).toBe(1); + expect(output.facets[2].filters.length).toBe(0); +}); + +test("match expression with multiple interpolations and a static fallback", () => { + const expression = [ + "match", + ["get", "foo"], + "bar", + ["interpolate", ["linear"], ["get", "population"], 1, 0, 5, 200_000], + "baz", + ["interpolate", ["linear"], ["get", "population"], 10, 0, 20, 200_000], + 5, + ]; + const output = pluckGetExpressionsOfType(expression, "interpolate", "number"); + expect(output.facets.length).toBe(2); + expect(output.facets[0].filters.length).toBe(1); + expect(output.facets[1].filters.length).toBe(1); + const facet1 = output.facets[0]; + expect(facet1.filters[0][0]).toBe("=="); + expect(facet1.filters[0][1]).toEqual(["get", "foo"]); + expect(facet1.filters[0][2]).toEqual("bar"); + expect(output.remainingValues).toBe(5); +}); + +test("match expression with fallback set to null", () => { + const expression = [ + "match", + ["get", "foo"], + "bar", + ["interpolate", ["linear"], ["get", "population"], 1, 0, 5, 200_000], + "baz", + 5, + ["interpolate", ["linear"], ["get", "population"], 20, 0, 50, 500_000], + ]; + const output = pluckGetExpressionsOfType(expression, "interpolate", "number"); + expect(output.facets.length).toBe(2); + expect(output.facets[0].filters.length).toBe(1); + const facet1 = output.facets[0]; + expect(facet1.filters[0][0]).toBe("=="); + expect(facet1.filters[0][1]).toEqual(["get", "foo"]); + expect(facet1.filters[0][2]).toBe("bar"); + expect(output.facets[1].filters.length).toBe(0); + expect(output.remainingValues).toEqual([ + "match", + ["get", "foo"], + "baz", + 5, + null, + ]); +}); + +test.only("targetExpressionMustIncludeGet option", () => { + const expression = [ + "case", + ["==", ["get", "type"], "foo"], + [ + "interpolate", + ["linear"], + ["heatmap-density"], + 0, + "rgba(33,102,172,0)", + 0.2, + "rgb(103,169,207)", + 0.4, + "rgb(209,229,240)", + 0.6, + "rgb(253,219,199)", + 0.8, + "rgb(239,138,98)", + 1, + "rgb(178,24,43)", + ], + [ + "interpolate", + ["linear"], + ["heatmap-density"], + 0, + "rgba(0, 0, 255, 0)", + 0.1, + "royalblue", + 0.3, + "cyan", + 0.5, + "lime", + 0.7, + "yellow", + 1, + "red", + ], + ]; + const output = pluckGetExpressionsOfType(expression, "interpolate", "color", { + targetExpressionMustIncludeGet: false, + }); + expect(output.facets.length).toBe(2); +}); diff --git a/packages/client/src/dataLayers/legends/style-spec.d.ts b/packages/client/src/dataLayers/legends/style-spec.d.ts new file mode 100644 index 000000000..41d50acff --- /dev/null +++ b/packages/client/src/dataLayers/legends/style-spec.d.ts @@ -0,0 +1,62 @@ +declare module "mapbox-gl/dist/style-spec/index.es.js" { + import { Feature } from "geojson"; + + export namespace expression { + export type FeatureState = { [key: string]: any }; + + export type GlobalProperties = Readonly<{ + zoom: number; + heatmapDensity?: number; + lineProgress?: number; + isSupportedScript?: (script: string) => boolean; + accumulated?: any; + }>; + + interface StyleExpression { + expression: any; + + evaluate( + globals: GlobalProperties, + feature?: Feature, + featureState?: FeatureState + ): any; + evaluateWithoutErrorHandling( + globals: GlobalProperties, + feature?: Feature, + featureState?: FeatureState + ): any; + } + + export interface ParseResultSuccess { + result: "success"; + value: StyleExpression; + } + export interface ParsingError extends Error { + key: string; + message: string; + } + export interface ParseResultError { + result: "error"; + value: ParsingError[]; + } + export type ParseResult = ParseResultSuccess | ParseResultError; + + export type StylePropertyType = + | "color" + | "string" + | "number" + | "enum" + | "boolean" + | "formatted" + | "image"; + + export interface StylePropertySpecification { + type: StylePropertyType; + } + + export function createExpression( + expr: any, + propertySpec?: StylePropertySpecification + ): ParseResult; + } +} diff --git a/packages/client/src/dataLayers/legends/utils.ts b/packages/client/src/dataLayers/legends/utils.ts new file mode 100644 index 000000000..42ff4169c --- /dev/null +++ b/packages/client/src/dataLayers/legends/utils.ts @@ -0,0 +1,223 @@ +import { Expression } from "mapbox-gl"; + +export const NULLIFIED_EXPRESSION_OUTPUT_NUMBER = -999999999999999; +export const NULLIFIED_EXPRESSION_OUTPUT_STRING = "__ssn__nullified"; + +export function isExpression(e: any): e is Expression { + return Array.isArray(e) && typeof e[0] === "string"; +} + +/** + * Converts a set of stops to a css linear-gradient + * @param stops { color: string; value: number} + */ +export function stopsToLinearGradient( + stops: { color: string; value: number }[] +): string { + const sortedStops = [...stops].sort((a, b) => a.value - b.value); + const colors = sortedStops.map((stop) => stop.color); + const values = sortedStops.map((stop) => stop.value); + const max = Math.max(...values); + const min = Math.min(...values); + const percentages = values.map((val) => (val - min) / (max - min)); + const stopStrings = colors.map( + (color, i) => `${color} ${percentages[i] * 100}%` + ); + // eslint-disable-next-line i18next/no-literal-string + return `linear-gradient(90deg, ${stopStrings.join(", ")})`; +} + +export function pluckGetExpressionsOfType( + expression: any, + type: string | RegExp, + styleValueType: "color" | "number" | "enum", + options?: { + targetExpressionMustIncludeGet?: boolean; + } +) { + let remainingValues: any = expression; + const expressions: { expression: Expression; filters: Expression[] }[] = []; + function matchesType(fnName: string) { + return typeof type === "string" ? fnName === type : type.test(fnName); + } + if (isExpression(expression)) { + const fnName = expression[0]; + if (matchesType(fnName) && hasGetExpression(expression)) { + expressions.push({ expression, filters: [] }); + remainingValues = null; + } else { + // If a case or match expression, add any matching expressions but append + // a filter. In theory these expressions could be deeply nested but I'm + // not going to support such pathological cases unless an important + // use-case is identified. + if (fnName === "case") { + const newExpression: Expression = ["case"]; + const args = expression.slice(1); + const conditionsAndOutputs = args.slice(0, args.length - 1); + for (var i = 0; i < conditionsAndOutputs.length; i += 2) { + const condition = conditionsAndOutputs[i]; + const output = conditionsAndOutputs[i + 1]; + if ( + isExpression(output) && + matchesType(output[0]) && + (hasGetExpression(output) || + options?.targetExpressionMustIncludeGet === false) + ) { + expressions.push({ + expression: output, + filters: [condition], + }); + } else { + newExpression.push(condition, output); + } + } + let fallback = args[args.length - 1]; + const originalFallback = fallback; + if ( + isExpression(fallback) && + matchesType(fallback[0]) && + (hasGetExpression(fallback) || + options?.targetExpressionMustIncludeGet === false) + ) { + // fallback is added without any filters. I'm not sure if that's right + // TODO: it may be better to add the "inverse" of previous conditions + expressions.push({ expression: fallback, filters: [] }); + fallback = null; + if (newExpression.length > 1) { + // nullify fallback so that fallback isn't represented in later + // panels + newExpression.push( + styleValueType === "color" || styleValueType === "enum" + ? NULLIFIED_EXPRESSION_OUTPUT_STRING + : NULLIFIED_EXPRESSION_OUTPUT_NUMBER + ); + } + } else { + if (newExpression.length > 1) { + newExpression.push(fallback); + fallback = null; + } + } + if (newExpression.length > 2) { + remainingValues = newExpression; + } else { + if (fallback) { + remainingValues = fallback; + } else { + remainingValues = null; + } + } + } else if (fnName === "match") { + const newExpression: Expression = ["match"]; + const args = expression.slice(1); + const input = args[0]; + const inputIsGetExpression = isExpression(input) && input[0] === "get"; + newExpression.push(input); + const fallback = args[args.length - 1]; + const originalFallback = fallback; + const matchAndOutputs = args.slice(1, args.length - 1); + for (var i = 0; i < matchAndOutputs.length; i += 2) { + const match = matchAndOutputs[i]; + const output = matchAndOutputs[i + 1]; + if ( + isExpression(output) && + matchesType(output[0]) && + hasGetExpression(output) + ) { + expressions.push({ + expression: output, + filters: inputIsGetExpression ? [["==", input, match]] : [], + }); + } else { + newExpression.push(match, output); + } + } + let newExpressionIsEmpty = newExpression.length === 2; + if ( + isExpression(fallback) && + matchesType(fallback[0]) && + hasGetExpression(fallback) + ) { + // fallback is added without any filters. I'm not sure if that's right + expressions.push({ expression: fallback, filters: [] }); + if (!newExpressionIsEmpty) { + // nullify fallback so that fallback isn't represented in later + // panels + newExpression.push( + styleValueType === "color" || styleValueType === "enum" + ? NULLIFIED_EXPRESSION_OUTPUT_STRING + : NULLIFIED_EXPRESSION_OUTPUT_NUMBER + ); + remainingValues = newExpression; + } else { + remainingValues = null; + } + } else { + if (newExpressionIsEmpty) { + remainingValues = fallback; + } else { + newExpression.push(fallback); + remainingValues = newExpression; + } + } + } + } + } + return { facets: expressions, remainingValues }; +} + +export function hasGetExpression(expression: any, isFilter?: boolean): boolean { + const get = findGetExpression(expression, isFilter); + return get ? true : false; +} + +export function findGetExpression( + expression: any, + isFilter?: boolean, + parent?: Expression +): null | { type: "legacy" | "get"; property: string } { + if (!isExpression(expression)) { + return null; + } + if (isFilter && !parent) { + // check for legacy filter type + if ( + typeof expression[1] === "string" && + !/\$/.test(expression[1]) && + expression[1] !== "zoom" + ) { + return { + type: "legacy", + property: expression[1], + }; + } + } + if (expression[0] === "get") { + return { type: "get", property: expression[1] }; + } else { + for (const arg of expression.slice(1)) { + if (isExpression(arg)) { + const found = findGetExpression(arg, isFilter, expression); + if (found !== null) { + return found; + } + } + } + } + return null; +} + +export function hasGetExpressionForProperty(expression: any, property: string) { + if (isExpression(expression)) { + if (expression[0] === "get" && expression[1] === property) { + return true; + } else { + for (const arg of expression.slice(1)) { + if (hasGetExpressionForProperty(arg, property)) { + return true; + } + } + } + } + return false; +} diff --git a/packages/client/src/draw/useMapboxGLDraw.ts b/packages/client/src/draw/useMapboxGLDraw.ts index ea3757358..d89985689 100644 --- a/packages/client/src/draw/useMapboxGLDraw.ts +++ b/packages/client/src/draw/useMapboxGLDraw.ts @@ -210,7 +210,6 @@ export default function useMapboxGLDraw( } } else { // draw.changeMode(drawMode); - // console.log("no initial value"); } setState(DigitizingState.NO_SELECTION); @@ -689,22 +688,6 @@ export default function useMapboxGLDraw( } }, [mapContext.manager, draw, state, mapContext]); - // useEffect(() => { - // console.log(DigitizingState[state]); - // if ( - // state === DigitizingState.CREATE || - // state === DigitizingState.NO_SELECTION || - // state === DigitizingState.UNFINISHED || - // state === DigitizingState.PAUSED_FOR_MEASUREMENT - // ) { - // console.log("call enableMeasurementTools"); - // mapContextManager?.resumeMeasurementTools(); - // } else { - // console.log("call disableMeasurementTools"); - // mapContextManager?.pauseMeasurementTools(); - // } - // }, [state, mapContextManager]); - return { digitizingState: state, selection, diff --git a/packages/client/src/editor/EditorMenuBar.tsx b/packages/client/src/editor/EditorMenuBar.tsx index b3a6e966e..d3e6a4a65 100644 --- a/packages/client/src/editor/EditorMenuBar.tsx +++ b/packages/client/src/editor/EditorMenuBar.tsx @@ -45,6 +45,7 @@ import { useGlobalErrorHandler } from "../components/GlobalErrorHandler"; import useIsSuperuser from "../useIsSuperuser"; import { useAuth0 } from "@auth0/auth0-react"; import axios from "axios"; +import { Link1Icon, Link2Icon } from "@radix-ui/react-icons"; interface EditorMenuBarProps { state?: EditorState; @@ -58,6 +59,7 @@ interface EditorMenuBarProps { sizeBytes: number, contentType: string ) => Promise; + onUseServiceMetadata?: () => void; } export default function EditorMenuBar(props: EditorMenuBarProps) { @@ -590,6 +592,11 @@ export default function EditorMenuBar(props: EditorMenuBarProps) { )} + {props.onUseServiceMetadata && ( + + )} {chooseSketchesOpen && ( { // if (props.value?.collection.features?.length) { - // console.log("onSubmit"); // props.onSubmit(); // } }} diff --git a/packages/client/src/formElements/ParticipantCount.stories.tsx b/packages/client/src/formElements/ParticipantCount.stories.tsx index a69fea97b..1cd21d940 100644 --- a/packages/client/src/formElements/ParticipantCount.stories.tsx +++ b/packages/client/src/formElements/ParticipantCount.stories.tsx @@ -48,7 +48,6 @@ export default { const Template: Story = (args: any) => { const [value, setValue] = useState(args.value); - console.log("top level value", value); return ( { // if (props.value?.collection.features?.length) { - // console.log("onSubmit"); // props.onSubmit(); // } }} diff --git a/packages/client/src/generated/graphql.ts b/packages/client/src/generated/graphql.ts index 42e47c53c..192046506 100644 --- a/packages/client/src/generated/graphql.ts +++ b/packages/client/src/generated/graphql.ts @@ -243,7 +243,7 @@ export enum ArcgisFeatureLayerFetchStrategy { export type ArcgisImportItemInput = { id?: Maybe; isFolder?: Maybe; - parentId?: Maybe; + parentId?: Maybe; sourceId?: Maybe; stableId?: Maybe; sublayerId?: Maybe; @@ -313,6 +313,7 @@ export type Basemap = Node & { labelsLayerId?: Maybe; /** Reads and enables pagination through a set of `MapBookmark`. */ mapBookmarksBySelectedBasemapConnection: MapBookmarksConnection; + maxzoom?: Maybe; /** Label shown in the basemap picker interface */ name: Scalars['String']; /** A globally unique identifier. Can be used in various places throughout the system to identify this single value. */ @@ -434,6 +435,7 @@ export type BasemapInput = { isDisabled?: Maybe; /** Identify the labels layer lowest in the stack so that overlay layers may be placed underneath. */ labelsLayerId?: Maybe; + maxzoom?: Maybe; /** Label shown in the basemap picker interface */ name: Scalars['String']; /** @@ -488,6 +490,7 @@ export type BasemapPatch = { isDisabled?: Maybe; /** Identify the labels layer lowest in the stack so that overlay layers may be placed underneath. */ labelsLayerId?: Maybe; + maxzoom?: Maybe; /** Label shown in the basemap picker interface */ name?: Maybe; /** @@ -2358,6 +2361,7 @@ export type DataSourceInput = { /** Represents an update to a `DataSource`. Fields that are set will be updated. */ export type DataSourcePatch = { + arcgisFetchStrategy?: Maybe; /** Contains an attribution to be displayed when the map is shown to a user. */ attribution?: Maybe; /** @@ -5632,6 +5636,7 @@ export type InteractivitySettingPatch = { }; export enum InteractivityType { + AllPropertiesPopup = 'ALL_PROPERTIES_POPUP', Banner = 'BANNER', FixedBlock = 'FIXED_BLOCK', None = 'NONE', @@ -8377,6 +8382,13 @@ export enum OptionalBasemapLayersOrderBy { PrimaryKeyDesc = 'PRIMARY_KEY_DESC' } +export type OutstandingSurveyInvites = { + __typename?: 'OutstandingSurveyInvites'; + projectId: Scalars['Int']; + surveyId: Scalars['Int']; + token: Scalars['String']; +}; + /** Information about pagination in a connection. */ export type PageInfo = { __typename?: 'PageInfo'; @@ -11985,6 +11997,12 @@ export type TableOfContentsItem = Node & { acl?: Maybe; /** If set, users will be able to zoom to the bounds of this item. [minx, miny, maxx, maxy] */ bounds?: Maybe>>; + /** + * Metadata will be returned as directly stored in the SeaSketch + * database or computed by fetching from a 3rd party service, + * depending on the data source type. + */ + computedMetadata?: Maybe; /** Reads a single `DataLayer` that is related to this `TableOfContentsItem`. */ dataLayer?: Maybe; /** If is_folder=false, a DataLayers visibility will be controlled by this item */ @@ -11999,6 +12017,7 @@ export type TableOfContentsItem = Node & { * their children. Toggles can only be used to toggle children off */ isClickOffOnly: Scalars['Boolean']; + isCustomGlSource?: Maybe; /** * Identifies whether this item is part of the draft table of contents edited by * admin or the static public version. This property cannot be changed. Rather, @@ -12035,6 +12054,7 @@ export type TableOfContentsItem = Node & { /** Name used in the table of contents rendering */ title: Scalars['String']; translatedProps: Scalars['JSON']; + usesDynamicMetadata?: Maybe; }; /** @@ -12179,6 +12199,12 @@ export enum TileScheme { Xyz = 'XYZ' } +export type TocItemDetails = { + __typename?: 'TocItemDetails'; + id: Scalars['Int']; + type: SketchChildType; +}; + /** All input for the `toggleAdminAccess` mutation. */ export type ToggleAdminAccessInput = { /** @@ -14391,16 +14417,6 @@ export type UpdateProjectStorageBucketMutation = ( )> } ); -export type NewQueryParametersFragment = ( - { __typename?: 'DataSource' } - & Pick -); - -export type UpdateHighDpiFragment = ( - { __typename?: 'DataSource' } - & Pick -); - export type UpdateFormatFragment = ( { __typename?: 'DataSource' } & Pick @@ -14872,7 +14888,7 @@ export type RequestInviteOnlyProjectAccessMutation = ( export type BasemapDetailsFragment = ( { __typename?: 'Basemap' } - & Pick + & Pick & { interactivitySettings?: Maybe<( { __typename?: 'InteractivitySetting' } & Pick @@ -14910,6 +14926,7 @@ export type CreateBasemapMutationVariables = Exact<{ type: BasemapType; url: Scalars['String']; surveysOnly?: Maybe; + isArcgisTiledMapservice?: Maybe; }>; @@ -14944,7 +14961,7 @@ export type UploadBasemapMutation = ( export type BasemapAdminDetailsFragment = ( { __typename?: 'Basemap' } - & Pick + & Pick & { interactivitySettings?: Maybe<( { __typename?: 'InteractivitySetting' } & Pick @@ -15284,6 +15301,23 @@ export type MapboxKeysQuery = ( )> } ); +export type SetBasemapMaxZoomMutationVariables = Exact<{ + id: Scalars['Int']; + maxzoom?: Maybe; +}>; + + +export type SetBasemapMaxZoomMutation = ( + { __typename?: 'Mutation' } + & { updateBasemap?: Maybe<( + { __typename?: 'UpdateBasemapPayload' } + & { basemap?: Maybe<( + { __typename?: 'Basemap' } + & Pick + )> } + )> } +); + export type CreateProjectMutationVariables = Exact<{ name: Scalars['String']; slug: Scalars['String']; @@ -15535,7 +15569,7 @@ export type DraftTableOfContentsQuery = ( { __typename?: 'Query' } & { projectBySlug?: Maybe<( { __typename?: 'Project' } - & Pick + & Pick & { region: ( { __typename?: 'GeometryPolygon' } & Pick @@ -15559,7 +15593,7 @@ export type LayersAndSourcesForItemsQuery = ( & Pick & { dataSourcesForItems?: Maybe + & Pick )>>, dataLayersForItems?: Maybe @@ -15697,7 +15731,7 @@ export type GetLayerItemQuery = ( )> } )>>, dataSource?: Maybe<( { __typename?: 'DataSource' } - & Pick + & Pick )> } )> } )> } @@ -15885,6 +15919,23 @@ export type UpdateQueryParametersMutation = ( )> } ); +export type UpdateFetchStrategyMutationVariables = Exact<{ + sourceId: Scalars['Int']; + fetchStrategy: ArcgisFeatureLayerFetchStrategy; +}>; + + +export type UpdateFetchStrategyMutation = ( + { __typename?: 'Mutation' } + & { updateDataSource?: Maybe<( + { __typename?: 'UpdateDataSourcePayload' } + & { dataSource?: Maybe<( + { __typename?: 'DataSource' } + & Pick + )> } + )> } +); + export type UpdateEnableHighDpiRequestsMutationVariables = Exact<{ sourceId: Scalars['Int']; useDevicePixelRatio: Scalars['Boolean']; @@ -15911,13 +15962,13 @@ export type GetMetadataQuery = ( { __typename?: 'Query' } & { tableOfContentsItem?: Maybe<( { __typename?: 'TableOfContentsItem' } - & Pick + & Pick )> } ); export type UpdateMetadataMutationVariables = Exact<{ itemId: Scalars['Int']; - metadata: Scalars['JSON']; + metadata?: Maybe; }>; @@ -15927,7 +15978,7 @@ export type UpdateMetadataMutation = ( { __typename?: 'UpdateTableOfContentsItemPayload' } & { tableOfContentsItem?: Maybe<( { __typename?: 'TableOfContentsItem' } - & Pick + & Pick )> } )> } ); @@ -15991,6 +16042,41 @@ export type DraftStatusSubscription = ( )> } ); +export type ImportArcGisServiceMutationVariables = Exact<{ + items: Array | ArcgisImportItemInput; + sources: Array | ArcgisImportSourceInput; + projectId: Scalars['Int']; +}>; + + +export type ImportArcGisServiceMutation = ( + { __typename?: 'Mutation' } + & { importArcgisServices?: Maybe<( + { __typename?: 'ImportArcgisServicesPayload' } + & { tableOfContentsItems?: Maybe + )>> } + )> } +); + +export type SetMaxZoomMutationVariables = Exact<{ + sourceId: Scalars['Int']; + maxzoom?: Maybe; +}>; + + +export type SetMaxZoomMutation = ( + { __typename?: 'Mutation' } + & { updateDataSource?: Maybe<( + { __typename?: 'UpdateDataSourcePayload' } + & { dataSource?: Maybe<( + { __typename?: 'DataSource' } + & Pick + )> } + )> } +); + export type ForumListDetailsFragment = ( { __typename?: 'Forum' } & Pick @@ -17036,7 +17122,7 @@ export type PublishedTableOfContentsQuery = ( export type DataSourceDetailsFragment = ( { __typename?: 'DataSource' } - & Pick + & Pick ); export type ClientSpriteFragment = ( @@ -19278,16 +19364,6 @@ export const NewBasemapFragmentDoc = gql` surveysOnly } `; -export const NewQueryParametersFragmentDoc = gql` - fragment NewQueryParameters on DataSource { - queryParameters -} - `; -export const UpdateHighDpiFragmentDoc = gql` - fragment UpdateHighDPI on DataSource { - useDevicePixelRatio -} - `; export const UpdateFormatFragmentDoc = gql` fragment UpdateFormat on DataSource { queryParameters @@ -19524,6 +19600,8 @@ export const BasemapDetailsFragmentDoc = gql` url surveysOnly translatedProps + isArcgisTiledMapservice + maxzoom } `; export const BasemapAdminDetailsFragmentDoc = gql` @@ -19565,6 +19643,7 @@ export const BasemapAdminDetailsFragmentDoc = gql` type url surveysOnly + isArcgisTiledMapservice } ${BasemapDetailsFragmentDoc}`; export const DataUploadDetailsFragmentDoc = gql` @@ -19969,6 +20048,7 @@ export const DataSourceDetailsFragmentDoc = gql` useDevicePixelRatio supportsDynamicLayers translatedProps + arcgisFetchStrategy } `; export const ClientSpriteFragmentDoc = gql` @@ -21459,9 +21539,9 @@ export type GetBasemapsQueryHookResult = ReturnType; export type GetBasemapsLazyQueryHookResult = ReturnType; export type GetBasemapsQueryResult = Apollo.QueryResult; export const CreateBasemapDocument = gql` - mutation CreateBasemap($projectId: Int, $name: String!, $thumbnail: Upload!, $tileSize: Int, $type: BasemapType!, $url: String!, $surveysOnly: Boolean) { + mutation CreateBasemap($projectId: Int, $name: String!, $thumbnail: Upload!, $tileSize: Int, $type: BasemapType!, $url: String!, $surveysOnly: Boolean, $isArcgisTiledMapservice: Boolean) { createBasemap( - input: {basemap: {projectId: $projectId, name: $name, thumbnail: $thumbnail, tileSize: $tileSize, type: $type, url: $url, surveysOnly: $surveysOnly}} + input: {basemap: {projectId: $projectId, name: $name, thumbnail: $thumbnail, tileSize: $tileSize, type: $type, url: $url, surveysOnly: $surveysOnly, isArcgisTiledMapservice: $isArcgisTiledMapservice}} ) { basemap { ...BasemapDetails @@ -21491,6 +21571,7 @@ export type CreateBasemapMutationFn = Apollo.MutationFunction; export type MapboxKeysLazyQueryHookResult = ReturnType; export type MapboxKeysQueryResult = Apollo.QueryResult; +export const SetBasemapMaxZoomDocument = gql` + mutation SetBasemapMaxZoom($id: Int!, $maxzoom: Int) { + updateBasemap(input: {id: $id, patch: {maxzoom: $maxzoom}}) { + basemap { + id + maxzoom + } + } +} + `; +export type SetBasemapMaxZoomMutationFn = Apollo.MutationFunction; + +/** + * __useSetBasemapMaxZoomMutation__ + * + * To run a mutation, you first call `useSetBasemapMaxZoomMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useSetBasemapMaxZoomMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [setBasemapMaxZoomMutation, { data, loading, error }] = useSetBasemapMaxZoomMutation({ + * variables: { + * id: // value for 'id' + * maxzoom: // value for 'maxzoom' + * }, + * }); + */ +export function useSetBasemapMaxZoomMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(SetBasemapMaxZoomDocument, options); + } +export type SetBasemapMaxZoomMutationHookResult = ReturnType; +export type SetBasemapMaxZoomMutationResult = Apollo.MutationResult; +export type SetBasemapMaxZoomMutationOptions = Apollo.BaseMutationOptions; export const CreateProjectDocument = gql` mutation CreateProject($name: String!, $slug: String!) { createProject(input: {name: $name, slug: $slug}) { @@ -22847,6 +22965,7 @@ export const DraftTableOfContentsDocument = gql` draftTableOfContentsItems { ...Overlay } + importedArcgisServices } } ${OverlayFragmentDoc}`; @@ -22913,6 +23032,7 @@ export const LayersAndSourcesForItemsDocument = gql` supportsDynamicLayers uploadedSourceFilename translatedProps + arcgisFetchStrategy } dataLayersForItems(tableOfContentsItemIds: $tableOfContentsItemIds) { interactivitySettings { @@ -23268,6 +23388,7 @@ export const GetLayerItemDocument = gql` uploadedBy geostats translatedProps + arcgisFetchStrategy } } } @@ -23747,6 +23868,45 @@ export function useUpdateQueryParametersMutation(baseOptions?: Apollo.MutationHo export type UpdateQueryParametersMutationHookResult = ReturnType; export type UpdateQueryParametersMutationResult = Apollo.MutationResult; export type UpdateQueryParametersMutationOptions = Apollo.BaseMutationOptions; +export const UpdateFetchStrategyDocument = gql` + mutation UpdateFetchStrategy($sourceId: Int!, $fetchStrategy: ArcgisFeatureLayerFetchStrategy!) { + updateDataSource( + input: {id: $sourceId, patch: {arcgisFetchStrategy: $fetchStrategy}} + ) { + dataSource { + id + arcgisFetchStrategy + } + } +} + `; +export type UpdateFetchStrategyMutationFn = Apollo.MutationFunction; + +/** + * __useUpdateFetchStrategyMutation__ + * + * To run a mutation, you first call `useUpdateFetchStrategyMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUpdateFetchStrategyMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [updateFetchStrategyMutation, { data, loading, error }] = useUpdateFetchStrategyMutation({ + * variables: { + * sourceId: // value for 'sourceId' + * fetchStrategy: // value for 'fetchStrategy' + * }, + * }); + */ +export function useUpdateFetchStrategyMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(UpdateFetchStrategyDocument, options); + } +export type UpdateFetchStrategyMutationHookResult = ReturnType; +export type UpdateFetchStrategyMutationResult = Apollo.MutationResult; +export type UpdateFetchStrategyMutationOptions = Apollo.BaseMutationOptions; export const UpdateEnableHighDpiRequestsDocument = gql` mutation UpdateEnableHighDPIRequests($sourceId: Int!, $useDevicePixelRatio: Boolean!) { updateDataSource( @@ -23790,7 +23950,9 @@ export const GetMetadataDocument = gql` query GetMetadata($itemId: Int!) { tableOfContentsItem(id: $itemId) { id - metadata + computedMetadata + usesDynamicMetadata + isCustomGlSource } } `; @@ -23823,11 +23985,13 @@ export type GetMetadataQueryHookResult = ReturnType; export type GetMetadataLazyQueryHookResult = ReturnType; export type GetMetadataQueryResult = Apollo.QueryResult; export const UpdateMetadataDocument = gql` - mutation UpdateMetadata($itemId: Int!, $metadata: JSON!) { + mutation UpdateMetadata($itemId: Int!, $metadata: JSON) { updateTableOfContentsItem(input: {id: $itemId, patch: {metadata: $metadata}}) { tableOfContentsItem { id metadata + usesDynamicMetadata + computedMetadata } } } @@ -24007,6 +24171,83 @@ export function useDraftStatusSubscription(baseOptions: Apollo.SubscriptionHookO } export type DraftStatusSubscriptionHookResult = ReturnType; export type DraftStatusSubscriptionResult = Apollo.SubscriptionResult; +export const ImportArcGisServiceDocument = gql` + mutation ImportArcGISService($items: [ArcgisImportItemInput!]!, $sources: [ArcgisImportSourceInput!]!, $projectId: Int!) { + importArcgisServices( + input: {items: $items, sources: $sources, projectId: $projectId} + ) { + tableOfContentsItems { + id + title + } + } +} + `; +export type ImportArcGisServiceMutationFn = Apollo.MutationFunction; + +/** + * __useImportArcGisServiceMutation__ + * + * To run a mutation, you first call `useImportArcGisServiceMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useImportArcGisServiceMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [importArcGisServiceMutation, { data, loading, error }] = useImportArcGisServiceMutation({ + * variables: { + * items: // value for 'items' + * sources: // value for 'sources' + * projectId: // value for 'projectId' + * }, + * }); + */ +export function useImportArcGisServiceMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(ImportArcGisServiceDocument, options); + } +export type ImportArcGisServiceMutationHookResult = ReturnType; +export type ImportArcGisServiceMutationResult = Apollo.MutationResult; +export type ImportArcGisServiceMutationOptions = Apollo.BaseMutationOptions; +export const SetMaxZoomDocument = gql` + mutation SetMaxZoom($sourceId: Int!, $maxzoom: Int) { + updateDataSource(input: {id: $sourceId, patch: {maxzoom: $maxzoom}}) { + dataSource { + id + maxzoom + } + } +} + `; +export type SetMaxZoomMutationFn = Apollo.MutationFunction; + +/** + * __useSetMaxZoomMutation__ + * + * To run a mutation, you first call `useSetMaxZoomMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useSetMaxZoomMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [setMaxZoomMutation, { data, loading, error }] = useSetMaxZoomMutation({ + * variables: { + * sourceId: // value for 'sourceId' + * maxzoom: // value for 'maxzoom' + * }, + * }); + */ +export function useSetMaxZoomMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(SetMaxZoomDocument, options); + } +export type SetMaxZoomMutationHookResult = ReturnType; +export type SetMaxZoomMutationResult = Apollo.MutationResult; +export type SetMaxZoomMutationOptions = Apollo.BaseMutationOptions; export const ForumAdminListDocument = gql` query ForumAdminList($slug: String!) { projectBySlug(slug: $slug) { @@ -29881,6 +30122,7 @@ export const namedOperations = { UpdateOptionalBasemapLayerOptions: 'UpdateOptionalBasemapLayerOptions', UpdateOptionalBasemapLayerMetadata: 'UpdateOptionalBasemapLayerMetadata', UpdateInteractivitySettingsLayers: 'UpdateInteractivitySettingsLayers', + SetBasemapMaxZoom: 'SetBasemapMaxZoom', CreateProject: 'CreateProject', VerifyEmail: 'VerifyEmail', createDataUpload: 'createDataUpload', @@ -29901,9 +30143,12 @@ export const namedOperations = { UpdateZIndexes: 'UpdateZIndexes', UpdateRenderUnderType: 'UpdateRenderUnderType', UpdateQueryParameters: 'UpdateQueryParameters', + UpdateFetchStrategy: 'UpdateFetchStrategy', UpdateEnableHighDPIRequests: 'UpdateEnableHighDPIRequests', UpdateMetadata: 'UpdateMetadata', PublishTableOfContents: 'PublishTableOfContents', + ImportArcGISService: 'ImportArcGISService', + SetMaxZoom: 'SetMaxZoom', CreateForum: 'CreateForum', UpdateForum: 'UpdateForum', DeleteForum: 'DeleteForum', @@ -30000,8 +30245,6 @@ export const namedOperations = { NewLabelsLayer: 'NewLabelsLayer', NewTerrain: 'NewTerrain', NewBasemap: 'NewBasemap', - NewQueryParameters: 'NewQueryParameters', - UpdateHighDPI: 'UpdateHighDPI', UpdateFormat: 'UpdateFormat', NewGLStyle: 'NewGLStyle', NewRenderUnder: 'NewRenderUnder', diff --git a/packages/client/src/generated/queries.ts b/packages/client/src/generated/queries.ts index 0eddbf4a4..55116fe17 100644 --- a/packages/client/src/generated/queries.ts +++ b/packages/client/src/generated/queries.ts @@ -241,7 +241,7 @@ export enum ArcgisFeatureLayerFetchStrategy { export type ArcgisImportItemInput = { id?: Maybe; isFolder?: Maybe; - parentId?: Maybe; + parentId?: Maybe; sourceId?: Maybe; stableId?: Maybe; sublayerId?: Maybe; @@ -311,6 +311,7 @@ export type Basemap = Node & { labelsLayerId?: Maybe; /** Reads and enables pagination through a set of `MapBookmark`. */ mapBookmarksBySelectedBasemapConnection: MapBookmarksConnection; + maxzoom?: Maybe; /** Label shown in the basemap picker interface */ name: Scalars['String']; /** A globally unique identifier. Can be used in various places throughout the system to identify this single value. */ @@ -432,6 +433,7 @@ export type BasemapInput = { isDisabled?: Maybe; /** Identify the labels layer lowest in the stack so that overlay layers may be placed underneath. */ labelsLayerId?: Maybe; + maxzoom?: Maybe; /** Label shown in the basemap picker interface */ name: Scalars['String']; /** @@ -486,6 +488,7 @@ export type BasemapPatch = { isDisabled?: Maybe; /** Identify the labels layer lowest in the stack so that overlay layers may be placed underneath. */ labelsLayerId?: Maybe; + maxzoom?: Maybe; /** Label shown in the basemap picker interface */ name?: Maybe; /** @@ -2356,6 +2359,7 @@ export type DataSourceInput = { /** Represents an update to a `DataSource`. Fields that are set will be updated. */ export type DataSourcePatch = { + arcgisFetchStrategy?: Maybe; /** Contains an attribution to be displayed when the map is shown to a user. */ attribution?: Maybe; /** @@ -5630,6 +5634,7 @@ export type InteractivitySettingPatch = { }; export enum InteractivityType { + AllPropertiesPopup = 'ALL_PROPERTIES_POPUP', Banner = 'BANNER', FixedBlock = 'FIXED_BLOCK', None = 'NONE', @@ -8375,6 +8380,13 @@ export enum OptionalBasemapLayersOrderBy { PrimaryKeyDesc = 'PRIMARY_KEY_DESC' } +export type OutstandingSurveyInvites = { + __typename?: 'OutstandingSurveyInvites'; + projectId: Scalars['Int']; + surveyId: Scalars['Int']; + token: Scalars['String']; +}; + /** Information about pagination in a connection. */ export type PageInfo = { __typename?: 'PageInfo'; @@ -11983,6 +11995,12 @@ export type TableOfContentsItem = Node & { acl?: Maybe; /** If set, users will be able to zoom to the bounds of this item. [minx, miny, maxx, maxy] */ bounds?: Maybe>>; + /** + * Metadata will be returned as directly stored in the SeaSketch + * database or computed by fetching from a 3rd party service, + * depending on the data source type. + */ + computedMetadata?: Maybe; /** Reads a single `DataLayer` that is related to this `TableOfContentsItem`. */ dataLayer?: Maybe; /** If is_folder=false, a DataLayers visibility will be controlled by this item */ @@ -11997,6 +12015,7 @@ export type TableOfContentsItem = Node & { * their children. Toggles can only be used to toggle children off */ isClickOffOnly: Scalars['Boolean']; + isCustomGlSource?: Maybe; /** * Identifies whether this item is part of the draft table of contents edited by * admin or the static public version. This property cannot be changed. Rather, @@ -12033,6 +12052,7 @@ export type TableOfContentsItem = Node & { /** Name used in the table of contents rendering */ title: Scalars['String']; translatedProps: Scalars['JSON']; + usesDynamicMetadata?: Maybe; }; /** @@ -12177,6 +12197,12 @@ export enum TileScheme { Xyz = 'XYZ' } +export type TocItemDetails = { + __typename?: 'TocItemDetails'; + id: Scalars['Int']; + type: SketchChildType; +}; + /** All input for the `toggleAdminAccess` mutation. */ export type ToggleAdminAccessInput = { /** @@ -14389,16 +14415,6 @@ export type UpdateProjectStorageBucketMutation = ( )> } ); -export type NewQueryParametersFragment = ( - { __typename?: 'DataSource' } - & Pick -); - -export type UpdateHighDpiFragment = ( - { __typename?: 'DataSource' } - & Pick -); - export type UpdateFormatFragment = ( { __typename?: 'DataSource' } & Pick @@ -14870,7 +14886,7 @@ export type RequestInviteOnlyProjectAccessMutation = ( export type BasemapDetailsFragment = ( { __typename?: 'Basemap' } - & Pick + & Pick & { interactivitySettings?: Maybe<( { __typename?: 'InteractivitySetting' } & Pick @@ -14908,6 +14924,7 @@ export type CreateBasemapMutationVariables = Exact<{ type: BasemapType; url: Scalars['String']; surveysOnly?: Maybe; + isArcgisTiledMapservice?: Maybe; }>; @@ -14942,7 +14959,7 @@ export type UploadBasemapMutation = ( export type BasemapAdminDetailsFragment = ( { __typename?: 'Basemap' } - & Pick + & Pick & { interactivitySettings?: Maybe<( { __typename?: 'InteractivitySetting' } & Pick @@ -15282,6 +15299,23 @@ export type MapboxKeysQuery = ( )> } ); +export type SetBasemapMaxZoomMutationVariables = Exact<{ + id: Scalars['Int']; + maxzoom?: Maybe; +}>; + + +export type SetBasemapMaxZoomMutation = ( + { __typename?: 'Mutation' } + & { updateBasemap?: Maybe<( + { __typename?: 'UpdateBasemapPayload' } + & { basemap?: Maybe<( + { __typename?: 'Basemap' } + & Pick + )> } + )> } +); + export type CreateProjectMutationVariables = Exact<{ name: Scalars['String']; slug: Scalars['String']; @@ -15533,7 +15567,7 @@ export type DraftTableOfContentsQuery = ( { __typename?: 'Query' } & { projectBySlug?: Maybe<( { __typename?: 'Project' } - & Pick + & Pick & { region: ( { __typename?: 'GeometryPolygon' } & Pick @@ -15557,7 +15591,7 @@ export type LayersAndSourcesForItemsQuery = ( & Pick & { dataSourcesForItems?: Maybe + & Pick )>>, dataLayersForItems?: Maybe @@ -15695,7 +15729,7 @@ export type GetLayerItemQuery = ( )> } )>>, dataSource?: Maybe<( { __typename?: 'DataSource' } - & Pick + & Pick )> } )> } )> } @@ -15883,6 +15917,23 @@ export type UpdateQueryParametersMutation = ( )> } ); +export type UpdateFetchStrategyMutationVariables = Exact<{ + sourceId: Scalars['Int']; + fetchStrategy: ArcgisFeatureLayerFetchStrategy; +}>; + + +export type UpdateFetchStrategyMutation = ( + { __typename?: 'Mutation' } + & { updateDataSource?: Maybe<( + { __typename?: 'UpdateDataSourcePayload' } + & { dataSource?: Maybe<( + { __typename?: 'DataSource' } + & Pick + )> } + )> } +); + export type UpdateEnableHighDpiRequestsMutationVariables = Exact<{ sourceId: Scalars['Int']; useDevicePixelRatio: Scalars['Boolean']; @@ -15909,13 +15960,13 @@ export type GetMetadataQuery = ( { __typename?: 'Query' } & { tableOfContentsItem?: Maybe<( { __typename?: 'TableOfContentsItem' } - & Pick + & Pick )> } ); export type UpdateMetadataMutationVariables = Exact<{ itemId: Scalars['Int']; - metadata: Scalars['JSON']; + metadata?: Maybe; }>; @@ -15925,7 +15976,7 @@ export type UpdateMetadataMutation = ( { __typename?: 'UpdateTableOfContentsItemPayload' } & { tableOfContentsItem?: Maybe<( { __typename?: 'TableOfContentsItem' } - & Pick + & Pick )> } )> } ); @@ -15989,6 +16040,41 @@ export type DraftStatusSubscription = ( )> } ); +export type ImportArcGisServiceMutationVariables = Exact<{ + items: Array | ArcgisImportItemInput; + sources: Array | ArcgisImportSourceInput; + projectId: Scalars['Int']; +}>; + + +export type ImportArcGisServiceMutation = ( + { __typename?: 'Mutation' } + & { importArcgisServices?: Maybe<( + { __typename?: 'ImportArcgisServicesPayload' } + & { tableOfContentsItems?: Maybe + )>> } + )> } +); + +export type SetMaxZoomMutationVariables = Exact<{ + sourceId: Scalars['Int']; + maxzoom?: Maybe; +}>; + + +export type SetMaxZoomMutation = ( + { __typename?: 'Mutation' } + & { updateDataSource?: Maybe<( + { __typename?: 'UpdateDataSourcePayload' } + & { dataSource?: Maybe<( + { __typename?: 'DataSource' } + & Pick + )> } + )> } +); + export type ForumListDetailsFragment = ( { __typename?: 'Forum' } & Pick @@ -17034,7 +17120,7 @@ export type PublishedTableOfContentsQuery = ( export type DataSourceDetailsFragment = ( { __typename?: 'DataSource' } - & Pick + & Pick ); export type ClientSpriteFragment = ( @@ -19276,16 +19362,6 @@ export const NewBasemapFragmentDoc = /*#__PURE__*/ gql` surveysOnly } `; -export const NewQueryParametersFragmentDoc = /*#__PURE__*/ gql` - fragment NewQueryParameters on DataSource { - queryParameters -} - `; -export const UpdateHighDpiFragmentDoc = /*#__PURE__*/ gql` - fragment UpdateHighDPI on DataSource { - useDevicePixelRatio -} - `; export const UpdateFormatFragmentDoc = /*#__PURE__*/ gql` fragment UpdateFormat on DataSource { queryParameters @@ -19522,6 +19598,8 @@ export const BasemapDetailsFragmentDoc = /*#__PURE__*/ gql` url surveysOnly translatedProps + isArcgisTiledMapservice + maxzoom } `; export const BasemapAdminDetailsFragmentDoc = /*#__PURE__*/ gql` @@ -19563,6 +19641,7 @@ export const BasemapAdminDetailsFragmentDoc = /*#__PURE__*/ gql` type url surveysOnly + isArcgisTiledMapservice } ${BasemapDetailsFragmentDoc}`; export const DataUploadDetailsFragmentDoc = /*#__PURE__*/ gql` @@ -19967,6 +20046,7 @@ export const DataSourceDetailsFragmentDoc = /*#__PURE__*/ gql` useDevicePixelRatio supportsDynamicLayers translatedProps + arcgisFetchStrategy } `; export const ClientSpriteFragmentDoc = /*#__PURE__*/ gql` @@ -20833,9 +20913,9 @@ export const GetBasemapsDocument = /*#__PURE__*/ gql` } ${BasemapDetailsFragmentDoc}`; export const CreateBasemapDocument = /*#__PURE__*/ gql` - mutation CreateBasemap($projectId: Int, $name: String!, $thumbnail: Upload!, $tileSize: Int, $type: BasemapType!, $url: String!, $surveysOnly: Boolean) { + mutation CreateBasemap($projectId: Int, $name: String!, $thumbnail: Upload!, $tileSize: Int, $type: BasemapType!, $url: String!, $surveysOnly: Boolean, $isArcgisTiledMapservice: Boolean) { createBasemap( - input: {basemap: {projectId: $projectId, name: $name, thumbnail: $thumbnail, tileSize: $tileSize, type: $type, url: $url, surveysOnly: $surveysOnly}} + input: {basemap: {projectId: $projectId, name: $name, thumbnail: $thumbnail, tileSize: $tileSize, type: $type, url: $url, surveysOnly: $surveysOnly, isArcgisTiledMapservice: $isArcgisTiledMapservice}} ) { basemap { ...BasemapDetails @@ -21082,6 +21162,16 @@ export const MapboxKeysDocument = /*#__PURE__*/ gql` } } `; +export const SetBasemapMaxZoomDocument = /*#__PURE__*/ gql` + mutation SetBasemapMaxZoom($id: Int!, $maxzoom: Int) { + updateBasemap(input: {id: $id, patch: {maxzoom: $maxzoom}}) { + basemap { + id + maxzoom + } + } +} + `; export const CreateProjectDocument = /*#__PURE__*/ gql` mutation CreateProject($name: String!, $slug: String!) { createProject(input: {name: $name, slug: $slug}) { @@ -21231,6 +21321,7 @@ export const DraftTableOfContentsDocument = /*#__PURE__*/ gql` draftTableOfContentsItems { ...Overlay } + importedArcgisServices } } ${OverlayFragmentDoc}`; @@ -21269,6 +21360,7 @@ export const LayersAndSourcesForItemsDocument = /*#__PURE__*/ gql` supportsDynamicLayers uploadedSourceFilename translatedProps + arcgisFetchStrategy } dataLayersForItems(tableOfContentsItemIds: $tableOfContentsItemIds) { interactivitySettings { @@ -21451,6 +21543,7 @@ export const GetLayerItemDocument = /*#__PURE__*/ gql` uploadedBy geostats translatedProps + arcgisFetchStrategy } } } @@ -21622,6 +21715,18 @@ export const UpdateQueryParametersDocument = /*#__PURE__*/ gql` } } `; +export const UpdateFetchStrategyDocument = /*#__PURE__*/ gql` + mutation UpdateFetchStrategy($sourceId: Int!, $fetchStrategy: ArcgisFeatureLayerFetchStrategy!) { + updateDataSource( + input: {id: $sourceId, patch: {arcgisFetchStrategy: $fetchStrategy}} + ) { + dataSource { + id + arcgisFetchStrategy + } + } +} + `; export const UpdateEnableHighDpiRequestsDocument = /*#__PURE__*/ gql` mutation UpdateEnableHighDPIRequests($sourceId: Int!, $useDevicePixelRatio: Boolean!) { updateDataSource( @@ -21638,16 +21743,20 @@ export const GetMetadataDocument = /*#__PURE__*/ gql` query GetMetadata($itemId: Int!) { tableOfContentsItem(id: $itemId) { id - metadata + computedMetadata + usesDynamicMetadata + isCustomGlSource } } `; export const UpdateMetadataDocument = /*#__PURE__*/ gql` - mutation UpdateMetadata($itemId: Int!, $metadata: JSON!) { + mutation UpdateMetadata($itemId: Int!, $metadata: JSON) { updateTableOfContentsItem(input: {id: $itemId, patch: {metadata: $metadata}}) { tableOfContentsItem { id metadata + usesDynamicMetadata + computedMetadata } } } @@ -21695,6 +21804,28 @@ export const DraftStatusDocument = /*#__PURE__*/ gql` } } `; +export const ImportArcGisServiceDocument = /*#__PURE__*/ gql` + mutation ImportArcGISService($items: [ArcgisImportItemInput!]!, $sources: [ArcgisImportSourceInput!]!, $projectId: Int!) { + importArcgisServices( + input: {items: $items, sources: $sources, projectId: $projectId} + ) { + tableOfContentsItems { + id + title + } + } +} + `; +export const SetMaxZoomDocument = /*#__PURE__*/ gql` + mutation SetMaxZoom($sourceId: Int!, $maxzoom: Int) { + updateDataSource(input: {id: $sourceId, patch: {maxzoom: $maxzoom}}) { + dataSource { + id + maxzoom + } + } +} + `; export const ForumAdminListDocument = /*#__PURE__*/ gql` query ForumAdminList($slug: String!) { projectBySlug(slug: $slug) { @@ -23776,6 +23907,7 @@ export const namedOperations = { UpdateOptionalBasemapLayerOptions: 'UpdateOptionalBasemapLayerOptions', UpdateOptionalBasemapLayerMetadata: 'UpdateOptionalBasemapLayerMetadata', UpdateInteractivitySettingsLayers: 'UpdateInteractivitySettingsLayers', + SetBasemapMaxZoom: 'SetBasemapMaxZoom', CreateProject: 'CreateProject', VerifyEmail: 'VerifyEmail', createDataUpload: 'createDataUpload', @@ -23796,9 +23928,12 @@ export const namedOperations = { UpdateZIndexes: 'UpdateZIndexes', UpdateRenderUnderType: 'UpdateRenderUnderType', UpdateQueryParameters: 'UpdateQueryParameters', + UpdateFetchStrategy: 'UpdateFetchStrategy', UpdateEnableHighDPIRequests: 'UpdateEnableHighDPIRequests', UpdateMetadata: 'UpdateMetadata', PublishTableOfContents: 'PublishTableOfContents', + ImportArcGISService: 'ImportArcGISService', + SetMaxZoom: 'SetMaxZoom', CreateForum: 'CreateForum', UpdateForum: 'UpdateForum', DeleteForum: 'DeleteForum', @@ -23895,8 +24030,6 @@ export const namedOperations = { NewLabelsLayer: 'NewLabelsLayer', NewTerrain: 'NewTerrain', NewBasemap: 'NewBasemap', - NewQueryParameters: 'NewQueryParameters', - UpdateHighDPI: 'UpdateHighDPI', UpdateFormat: 'UpdateFormat', NewGLStyle: 'NewGLStyle', NewRenderUnder: 'NewRenderUnder', diff --git a/packages/client/src/index.css b/packages/client/src/index.css index b2a6fadd8..2cdea8c93 100644 --- a/packages/client/src/index.css +++ b/packages/client/src/index.css @@ -278,3 +278,12 @@ input[type="checkbox"][indeterminate="true"]:checked { background-image: linear-gradient(to left, rgb(249, 250, 251), transparent); z-index: 10; } */ + +.mapboxgl-popup-close-button { + @apply px-0.5 bg-white z-10 flex items-center; + border-radius: 100%; +} + +.mapboxgl-popup-content { + @apply px-3 pt-4 !important; +} diff --git a/packages/client/src/projects/Forums/InlineAuthorDetails.tsx b/packages/client/src/projects/Forums/InlineAuthorDetails.tsx index ed7c81f5c..1433b8e6c 100644 --- a/packages/client/src/projects/Forums/InlineAuthorDetails.tsx +++ b/packages/client/src/projects/Forums/InlineAuthorDetails.tsx @@ -148,7 +148,6 @@ function fallbackCopyTextToClipboard(text: string) { try { var successful = document.execCommand("copy"); var msg = successful ? "successful" : "unsuccessful"; - // console.log("Fallback: Copying text command was " + msg); } catch (err) { console.error("Fallback: Oops, unable to copy", err); } diff --git a/packages/client/src/projects/OverlayLayers.tsx b/packages/client/src/projects/OverlayLayers.tsx index 1f151b281..014840b8c 100644 --- a/packages/client/src/projects/OverlayLayers.tsx +++ b/packages/client/src/projects/OverlayLayers.tsx @@ -1,7 +1,15 @@ import { useState, useContext, useCallback } from "react"; import TableOfContentsMetadataModal from "../dataLayers/TableOfContentsMetadataModal"; -import { OverlayFragment, TableOfContentsItem } from "../generated/graphql"; -import { MapContext } from "../dataLayers/MapContextManager"; +import { + DataLayerDetailsFragment, + DataSourceDetailsFragment, + OverlayFragment, + TableOfContentsItem, +} from "../generated/graphql"; +import { + MapContext, + sourceTypeIsCustomGLSource, +} from "../dataLayers/MapContextManager"; import TreeView, { TreeItem, useOverlayState } from "../components/TreeView"; import { DropdownDividerProps } from "../components/ContextMenuDropdown"; import { DropdownOption } from "../components/DropdownButton"; @@ -10,12 +18,18 @@ import { currentSidebarState } from "./ProjectAppSidebar"; export default function OverlayLayers({ items, + layers, + sources, }: { items: TableOfContentsItem[]; + layers: DataLayerDetailsFragment[]; + sources: DataSourceDetailsFragment[]; }) { const { t } = useTranslation("homepage"); const mapContext = useContext(MapContext); - const [openMetadataViewerId, setOpenMetadataViewerId] = useState(); + const [openMetadataViewerState, setOpenMetadataViewerState] = useState< + undefined | number + >(); const { expandedIds, @@ -36,7 +50,8 @@ export default function OverlayLayers({ { id: "zoom-to", label: t("Zoom to bounds"), - onClick: () => { + disabled: !item.bounds && !checkedItems.includes(item.stableId), + onClick: async () => { let bounds: [number, number, number, number] | undefined; if (item.isFolder) { bounds = createBoundsRecursive(item, items); @@ -45,6 +60,22 @@ export default function OverlayLayers({ bounds = item.bounds.map((coord: string) => parseFloat(coord) ) as [number, number, number, number]; + } else { + const layer = layers?.find((l) => l.id === item.dataLayerId); + if (layer && layer.dataSourceId) { + const source = sources?.find( + (s) => s.id === layer.dataSourceId + ); + if (source && sourceTypeIsCustomGLSource(source.type)) { + const customSource = + mapContext.manager?.getCustomGLSource(source.id); + const metadata = + await customSource?.getComputedMetadata(); + if (metadata?.bounds) { + bounds = metadata.bounds; + } + } + } } } if ( @@ -69,7 +100,7 @@ export default function OverlayLayers({ id: "metadata", label: t("Metadata"), onClick: () => { - setOpenMetadataViewerId(item.id); + setOpenMetadataViewerState(item.id); }, }); } @@ -78,15 +109,23 @@ export default function OverlayLayers({ return []; } }, - [items, mapContext.manager?.map, t] + [ + items, + mapContext.manager?.map, + t, + mapContext.manager, + layers, + sources, + checkedItems, + ] ); return (
    - {openMetadataViewerId && ( + {openMetadataViewerState && ( setOpenMetadataViewerId(undefined)} + id={openMetadataViewerState} + onRequestClose={() => setOpenMetadataViewerState(undefined)} /> )} import(/* webpackChunkName: "Overlays" */ "./OverlayLayers") @@ -78,7 +79,8 @@ export default function ProjectApp() { forums: t("Discussion Forums"), settings: t("Cache Settings"), }; - const { basemaps, tableOfContentsItems } = useMapData(mapContext); + const { basemaps, tableOfContentsItems, dataLayers, dataSources } = + useMapData(mapContext); // Disabling until I can see some Divehi translations -cb 3/29/23 // Might need to just enable this for forum content and attribute forms // const { selectedLang } = getSelectedLanguage(i18n); @@ -96,6 +98,7 @@ export default function ProjectApp() { {/* */}
    + diff --git a/packages/client/src/projects/ProjectMapLegend.tsx b/packages/client/src/projects/ProjectMapLegend.tsx new file mode 100644 index 000000000..935c6bbce --- /dev/null +++ b/packages/client/src/projects/ProjectMapLegend.tsx @@ -0,0 +1,40 @@ +import { useContext, useMemo } from "react"; +import { MapContext } from "../dataLayers/MapContextManager"; +import Legend, { LegendItem } from "../dataLayers/Legend"; + +export default function ProjectMapLegend() { + const mapContext = useContext(MapContext); + const legendItems = useMemo( + () => + Object.values(mapContext?.legends || {}).filter( + (l) => !!l + ) as LegendItem[], + [mapContext.legends] + ); + const loading = useMemo(() => { + for (const key in mapContext.layerStatesByTocStaticId) { + if (mapContext.layerStatesByTocStaticId[key].loading) { + return true; + } + } + return false; + }, [mapContext.layerStatesByTocStaticId]); + if (legendItems.length > 0) { + return ( + + ); + } else { + return null; + } +} diff --git a/packages/client/src/projects/Sketches/SketchEditorModal.tsx b/packages/client/src/projects/Sketches/SketchEditorModal.tsx index 10ef62a62..0dd8c80d9 100644 --- a/packages/client/src/projects/Sketches/SketchEditorModal.tsx +++ b/packages/client/src/projects/Sketches/SketchEditorModal.tsx @@ -74,7 +74,6 @@ export default function SketchEditorModal({ // return extractRelevantPropsFromStyle(sketchClass.mapboxGlStyle || []); // }, [sketchClass]); // // - // console.log(styleRelevantProps, properties); // useEffect(() => { // const elements = sketchClass.form?.formElements || []; diff --git a/packages/client/src/queries/BasemapAdmin.graphql b/packages/client/src/queries/BasemapAdmin.graphql index acdc64cb6..8c110f31a 100644 --- a/packages/client/src/queries/BasemapAdmin.graphql +++ b/packages/client/src/queries/BasemapAdmin.graphql @@ -36,6 +36,8 @@ fragment BasemapDetails on Basemap { url surveysOnly translatedProps + isArcgisTiledMapservice + maxzoom } query GetBasemaps($slug: String!) { @@ -58,6 +60,7 @@ mutation CreateBasemap( $type: BasemapType! $url: String! $surveysOnly: Boolean + $isArcgisTiledMapservice: Boolean ) { createBasemap( input: { @@ -69,6 +72,7 @@ mutation CreateBasemap( type: $type url: $url surveysOnly: $surveysOnly + isArcgisTiledMapservice: $isArcgisTiledMapservice } } ) { @@ -136,6 +140,7 @@ fragment BasemapAdminDetails on Basemap { type url surveysOnly + isArcgisTiledMapservice } query GetBasemap($id: Int!) { @@ -382,3 +387,12 @@ query MapboxKeys($slug: String!) { mapboxSecretKey } } + +mutation SetBasemapMaxZoom($id: Int!, $maxzoom: Int) { + updateBasemap(input: { id: $id, patch: { maxzoom: $maxzoom } }) { + basemap { + id + maxzoom + } + } +} diff --git a/packages/client/src/queries/DraftTableOfContents.graphql b/packages/client/src/queries/DraftTableOfContents.graphql index dbff747e6..e355f773c 100644 --- a/packages/client/src/queries/DraftTableOfContents.graphql +++ b/packages/client/src/queries/DraftTableOfContents.graphql @@ -9,6 +9,7 @@ query DraftTableOfContents($slug: String!) { draftTableOfContentsItems { ...Overlay } + importedArcgisServices } } @@ -49,6 +50,7 @@ query layersAndSourcesForItems( supportsDynamicLayers uploadedSourceFilename translatedProps + arcgisFetchStrategy } dataLayersForItems(tableOfContentsItemIds: $tableOfContentsItemIds) { interactivitySettings { @@ -262,6 +264,7 @@ query GetLayerItem($id: Int!) { uploadedBy geostats translatedProps + arcgisFetchStrategy } } } @@ -467,6 +470,20 @@ mutation UpdateQueryParameters($sourceId: Int!, $queryParameters: JSON!) { } } +mutation UpdateFetchStrategy( + $sourceId: Int! + $fetchStrategy: ArcgisFeatureLayerFetchStrategy! +) { + updateDataSource( + input: { id: $sourceId, patch: { arcgisFetchStrategy: $fetchStrategy } } + ) { + dataSource { + id + arcgisFetchStrategy + } + } +} + mutation UpdateEnableHighDPIRequests( $sourceId: Int! $useDevicePixelRatio: Boolean! @@ -487,17 +504,21 @@ mutation UpdateEnableHighDPIRequests( query GetMetadata($itemId: Int!) { tableOfContentsItem(id: $itemId) { id - metadata + computedMetadata + usesDynamicMetadata + isCustomGlSource } } -mutation UpdateMetadata($itemId: Int!, $metadata: JSON!) { +mutation UpdateMetadata($itemId: Int!, $metadata: JSON) { updateTableOfContentsItem( input: { id: $itemId, patch: { metadata: $metadata } } ) { tableOfContentsItem { id metadata + usesDynamicMetadata + computedMetadata } } } @@ -540,3 +561,27 @@ subscription DraftStatus($slug: String!) { } } } + +mutation ImportArcGISService( + $items: [ArcgisImportItemInput!]! + $sources: [ArcgisImportSourceInput!]! + $projectId: Int! +) { + importArcgisServices( + input: { items: $items, sources: $sources, projectId: $projectId } + ) { + tableOfContentsItems { + id + title + } + } +} + +mutation SetMaxZoom($sourceId: Int!, $maxzoom: Int) { + updateDataSource(input: { id: $sourceId, patch: { maxzoom: $maxzoom } }) { + dataSource { + id + maxzoom + } + } +} diff --git a/packages/client/src/queries/PublishedTableOfContents.graphql b/packages/client/src/queries/PublishedTableOfContents.graphql index df022d3ca..796976e29 100644 --- a/packages/client/src/queries/PublishedTableOfContents.graphql +++ b/packages/client/src/queries/PublishedTableOfContents.graphql @@ -58,6 +58,7 @@ fragment DataSourceDetails on DataSource { useDevicePixelRatio supportsDynamicLayers translatedProps + arcgisFetchStrategy } fragment ClientSprite on Sprite { diff --git a/packages/client/src/react-app-env.d.ts b/packages/client/src/react-app-env.d.ts index e64c98792..bf39164b9 100644 --- a/packages/client/src/react-app-env.d.ts +++ b/packages/client/src/react-app-env.d.ts @@ -14,5 +14,6 @@ declare namespace NodeJS { REACT_APP_ENABLE_GRAPHQL_QUERY_CACHE_BY_DEFAULT?: string; REACT_APP_SENTRY_ENV?: string; REACT_APP_CLOUDFLARE_IMAGES_ENDPOINT: string; + REACT_APP_ARCGIS_DEVELOPER_API_KEY: string; } } diff --git a/packages/infra/sessionmanager-bundle.zip b/packages/infra/sessionmanager-bundle.zip new file mode 100644 index 000000000..356c020c1 Binary files /dev/null and b/packages/infra/sessionmanager-bundle.zip differ diff --git a/packages/infra/sessionmanager-bundle/LICENSE b/packages/infra/sessionmanager-bundle/LICENSE new file mode 100644 index 000000000..56ee3c8c4 --- /dev/null +++ b/packages/infra/sessionmanager-bundle/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/packages/infra/sessionmanager-bundle/NOTICE b/packages/infra/sessionmanager-bundle/NOTICE new file mode 100644 index 000000000..872fc8f72 --- /dev/null +++ b/packages/infra/sessionmanager-bundle/NOTICE @@ -0,0 +1,2 @@ +Session Manager Plugin +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. \ No newline at end of file diff --git a/packages/infra/sessionmanager-bundle/README.md b/packages/infra/sessionmanager-bundle/README.md new file mode 100644 index 000000000..89cebfa3d --- /dev/null +++ b/packages/infra/sessionmanager-bundle/README.md @@ -0,0 +1,82 @@ + +# Session Manager Plugin + +This plugin helps you to use the AWS Command Line Interface (AWS CLI) to start and end sessions to your managed instances. Session Manager is a capability of AWS Systems Manager. + +## Overview + +Session Manager is a fully managed AWS Systems Manager capability that lets you manage your Amazon Elastic Compute Cloud (Amazon EC2) instances, on-premises instances and virtual machines. Session Manager provides secure and auditable instance management without the need to open inbound ports. When you use the Session Manager plugin with the AWS CLI to start a session, the plugin builds the websocket connection to your managed instances. + +### Prerequisites + +Before using Session Manager, make sure your environment meets the following requirements. [Complete Session Manager prerequisites](http://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-prerequisites.html). + +### Starting a session + +For information about starting a session using the AWS CLI, see [Starting a session (AWS CLI)](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-sessions-start.html#sessions-start-cli). + +### Troubleshooting + +For information about troubleshooting, see [Troubleshooting Session Manager](http://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-troubleshooting.html). + + +### Working with Docker + +To build the Session Manager plugin in a `Docker` container, complete the following steps: + +1. Install [`docker`](https://docs.docker.com/engine/install/centos/) + +2. Build the `docker` image +``` +docker build -t session-manager-plugin-image . +``` +3. Build the plugin +``` +docker run -it --rm --name session-manager-plugin -v `pwd`:/session-manager-plugin session-manager-plugin-image make release +``` + +### Working with Linux + +To build the binaries required to install the Session Manager plugin, complete the following steps. + +1. Install `golang` + +2. Install `rpm-build` and `rpmdevtools` + +3. Install `gcc 8.3+` and `glibc 2.27+` + +4. Run `make release` to build the plugin for Linux, Debian, macOS and Windows. + +5. Change to the directory of your local machine's operating system architecture and open the `session-manager-plugin` directory. Then follow the installation procedure that applies to your local machine. For more information, see [Install the Session Manager plugin for the AWS CLI](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html). If the machine you're building the plugin on differs from the machine you plan to install the plugin on you will need to copy the `session-manager-plugin` binary to the appropriate directory for that operating system. + +``` +Linux - /usr/local/sessionmanagerplugin/bin/session-manager-plugin + +macOS - /usr/local/sessionmanagerplugin/bin/session-manager-plugin + +Windows - C:\Program Files\Amazon\SessionManagerPlugin\bin\session-manager-plugin.exe +``` + +The `ssmcli` binary is available for some operating systems for testing purposes only. The following is an example command using this binary. + +``` +./ssmcli start-session --instance-id i-1234567890abcdef0 --region us-east-2 +``` + +### Directory structure + +Source code + +* `sessionmanagerplugin/session` contains the source code for core functionalities +* `communicator/` contains the source code for websocket related operations +* `vendor/src` contains the vendor package source code +* `packaging/` contains rpm and dpkg artifacts +* `Tools/src` contains build scripts + +## Feedback + +Thank you for helping us to improve the Session Manager plugin. Please send your questions or comments to the [Systems Manager Forum](https://forums.aws.amazon.com/forum.jspa?forumID=185&start=0) + +## License + +The session-manager-plugin is licensed under the Apache 2.0 License. diff --git a/packages/infra/sessionmanager-bundle/RELEASENOTES.md b/packages/infra/sessionmanager-bundle/RELEASENOTES.md new file mode 100644 index 000000000..d2aa8f13b --- /dev/null +++ b/packages/infra/sessionmanager-bundle/RELEASENOTES.md @@ -0,0 +1,112 @@ +Latest +================ +- Upgrade Go SDK to v1.44.302 + +1.2.463.0 +================ +- Support ARM64 for Apple Mac M1 +- Remove unused start/stop steps in packaging scripts + +1.2.398.0 +================ +- Support golang version 1.17 +- Update default session-manager-plugin runner for macOS to be python3 +- Update import path from SSMCLI to session-manager-plugin + +1.2.339.0 +================ +- Fix idle session timeout for port sessions + +1.2.331.0 +================ +- Fix port session premature close when local server is not connected before timeout + +1.2.323.0 +================ +- Disable smux keep alive to use idle session timeout feature + +1.2.312.0 +================ +- Support more output message payload type. + +1.2.295.0 +================ +- Fix hung sessions caused by client resending stream data when agent becomes inactive. +- Fix incorrect logs for start_publication and pause_publication messages. + +1.2.279.0 +================ +- Fix single memory reference access for parameters +- Upgrade gorilla package to 1.4.2 + +1.2.245.0 +================ +- Enhancement: Upgrade aws-sdk-go to latest version (v1.40.17) to support SSO +- Enhancement: Improve error message for legacy CLI version + +1.2.234.0 +================ +- Change data streaming related logs from debug to trace level. +- Fix typo for log config file. +- Fix interactive command session abruptly terminated issue. + +1.2.205.0 +================ +- Introduce client timeout for session start request. +- Add support for signed session-manager-plugin.pkg file for macOS. + +1.2.54.0 +================ +- Enhancement: Added support for running session in NonInteractiveCommands execution mode. + +1.2.30.0 +================ +- Bug Fix: (Port forwarding sessions only) Using system tmp folder for unix socket path. + +1.2.7.0 +================ +- Enhancement: (Port forwarding sessions only) Reduced latency and improved overall performance. + +1.1.61.0 +================ +- Enhancement: Added ARM support for Linux and Ubuntu. + +1.1.54.0 +================ +- Bug Fix: Handle race condition scenario of packets being dropped when plugin is not ready. + +1.1.50.0 +================ +- Enhancement: Add support for forwarding port session to local unix socket. + +1.1.35.0 +================ +- Enhancement: For port forwarding session, send terminateSession flag to SSM agent on receiving Control-C signal. + +1.1.33.0 +================ +- Enhancement: For port forwarding session, send disconnect flag to server when client drops tcp connection. + +1.1.31.0 +================ +- Enhancement: Change to keep port forwarding session open until remote server closes the connection. + +1.1.26.0 +================ +- Enhancement: Limit the rate of data transfer in port session. + +1.1.23.0 +================ +- Enhancement: Add support for running SSH sessions using Session Manager. + +1.1.17.0 +================ +- Enhancement: Add support for further encryption of session data using AWS KMS. + +1.0.37.0 +================ +- Fix bug for Windows SessionManagerPlugin + +1.0.0.0 +================ +- Initial SessionManagerPlugin release diff --git a/packages/infra/sessionmanager-bundle/THIRD-PARTY b/packages/infra/sessionmanager-bundle/THIRD-PARTY new file mode 100644 index 000000000..bbe87ffac --- /dev/null +++ b/packages/infra/sessionmanager-bundle/THIRD-PARTY @@ -0,0 +1,90 @@ +The Amazon Session Manager Plugin constitutes AWS Content as defined in the AWS Customer Agreement +or your relevant customer agreement with AWS, and is licensed to you under that agreement. + +The Amazon Session Manager Plugin includes the following third-party software/licensing: + +** cihub/seelog - https://github.com/cihub/seelog +Copyright (c) 2012, Cloud Instruments Co., Ltd. . All rights reserved. +** gorilla/websocket - https://github.com/gorilla/websocket +Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. +** fsnotigy/fsnotify - https://github.com/fsnotify/fsnotify +Copyright (c) 2012 The Go Authors. All rights reserved. +Copyright (c) 2012 fsnotify Authors. All rights reserved. + +BSD License + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Cloud Instruments Co., Ltd. nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------- + +** twinj/uuid - https://github.com/twinj/uuid +Copyright (C) 2011 by Krzysztof Kowalik +Copyright (C) 2016 by Daniel Kemp Derivative work +** stretchr/testify - https://github.com/stretchr/testify +Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell +** stretchr/objx - https://github.com/stretchr/objx +Copyright (c) 2014 Stretchr, Inc. +Copyright (c) 2017-2018 objx contributors +** eiannone/keyboard - https://github.com/eiannone/keyboard +Copyright (C) 2012 termbox-go authors +Copyright (c) 2015 Emanuele Iannone +**xtaci/smux - https://github.com/xtaci/smux +Copyright (c) 2016-2017 Daniel Fu + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +---------------- + +jmespath/go-jmespath - https://github.com/jmespath/go-jmespath + +Copyright 2015 James Saryerwinnie + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/packages/infra/sessionmanager-bundle/VERSION b/packages/infra/sessionmanager-bundle/VERSION new file mode 100644 index 000000000..9507d53c2 --- /dev/null +++ b/packages/infra/sessionmanager-bundle/VERSION @@ -0,0 +1 @@ +1.2.497.0 \ No newline at end of file diff --git a/packages/infra/sessionmanager-bundle/bin/session-manager-plugin b/packages/infra/sessionmanager-bundle/bin/session-manager-plugin new file mode 100755 index 000000000..ecadc4ccb Binary files /dev/null and b/packages/infra/sessionmanager-bundle/bin/session-manager-plugin differ diff --git a/packages/infra/sessionmanager-bundle/install b/packages/infra/sessionmanager-bundle/install new file mode 100755 index 000000000..60c35dcef --- /dev/null +++ b/packages/infra/sessionmanager-bundle/install @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +import optparse +import os +import shutil +import sys + +""" +This script installs the session-manager-plugin for macOS. +The executable is installed to /usr/local/sessionmanagerplugin (default) or to an install directory provided by the user. +It also creates a symlink session-manager-plugin in the /usr/local/bin directory +""" + +PLUGIN_FILE = "session-manager-plugin" +VERSION_FILE = "VERSION" +LICENSE_FILE = "LICENSE" +SEELOG_FILE = "seelog.xml.template" + +INSTALL_DIR = "/usr/local/sessionmanagerplugin" +SYMLINK_NAME = "/usr/local/bin/{}".format(PLUGIN_FILE) + +def create_symlink(real_location, symlink_name): + """ + Removes a duplicate symlink if it exists and + creates symlink from real_location to symlink_name + """ + if os.path.isfile(symlink_name): + print("Symlink already exists. Removing symlink from {}".format(symlink_name)) + os.remove(symlink_name) + + print("Creating Symlink from {} to {}".format(real_location, symlink_name)) + os.symlink(real_location, symlink_name) + +def main(): + parser = optparse.OptionParser() + parser.add_option("-i", "--install-dir", help="The location to install the Session Manager Plugin." + " The default value is {}".format(INSTALL_DIR), default=INSTALL_DIR) + parser.add_option("-b", "--bin-location", help="If this argument is " + "provided, then a symlink will be created at this " + "location that points to the session-manager-plugin executable. " + "The default symlink location is {}\n" + "Note: The session-manager-plugin executable must be in your $PATH " + "to use Session Manager Plugin with AWS CLI.".format(SYMLINK_NAME), default=SYMLINK_NAME) + options = parser.parse_args()[0] + + try: + current_working_directory = os.path.dirname(os.path.abspath(__file__)) + + current_bin_folder = os.path.join(current_working_directory, 'bin') + install_bin_folder = os.path.join(options.install_dir, 'bin') + + if not os.path.isdir(install_bin_folder): + print("Creating install directories: {}".format(install_bin_folder)) + os.makedirs(install_bin_folder) + + # Copy executable. Overwrites file if it exists. The basename of the file is copied + current_bin_location = os.path.join(current_working_directory, 'bin', PLUGIN_FILE) + shutil.copy2(current_bin_location, install_bin_folder) + current_bin_folder = install_bin_folder + + # Copy see_log file + seelog_location = os.path.join(current_working_directory, SEELOG_FILE) + shutil.copy2(seelog_location, options.install_dir) + + # Copy Version File + version_file_location = os.path.join(current_working_directory, VERSION_FILE) + shutil.copy2(version_file_location, options.install_dir) + + # Copy License File + license_file_location = os.path.join(current_working_directory, LICENSE_FILE) + shutil.copy2(license_file_location, options.install_dir) + + install_bin_location = os.path.join(options.install_dir,'bin', PLUGIN_FILE) + create_symlink(install_bin_location, options.bin_location) + print("Installation successful!") + except: + print("Failed to create symlink.\nPlease add {} to your $PATH to use Session Manager Plugin.".format(current_bin_folder)) + +if __name__ == '__main__': + main() diff --git a/packages/infra/sessionmanager-bundle/seelog.xml.template b/packages/infra/sessionmanager-bundle/seelog.xml.template new file mode 100644 index 000000000..068a9af26 --- /dev/null +++ b/packages/infra/sessionmanager-bundle/seelog.xml.template @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/packages/mapbox-gl-esri-feature-layers/README.md b/packages/mapbox-gl-esri-feature-layers/README.md deleted file mode 100644 index 50619c8cd..000000000 --- a/packages/mapbox-gl-esri-feature-layers/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# `mapbox-gl-esri-feature-layers` - -Load esri feature layers in mapbox-gl. This is a pared down version of [@seasketch/mapbox-gl-esri-sources](https://github.com/seasketch/mapbox-gl-esri-sources) and is intended to be a replacement. The older version included classes like ArcGISVectorSource & ArcGISDynamicMapService which could be used to load data and keep the map up to date. This turned out not to be useful in SeaSketch Next and I don't want to publish something we're not using since there won't be much incentive to maintain it. - -When it comes time to release this as open source, the library will have two main functions. #1 is a function to fetch feature data from the service as GeoJSON. #2 is a function which will extract style data from the service and translate it into a mapbox-gl style. It may be useful to include examples of how to load dynamic map services but [no library is really needed to do that](https://github.com/seasketch/next/blob/917bf4e2576aa07b91e6e8b1c9dc309058ebbb2c/packages/client/src/dataLayers/sourceTypes/ArcGISDynamicMapServiceSource.ts). diff --git a/packages/mapbox-gl-esri-feature-layers/dist/index.js b/packages/mapbox-gl-esri-feature-layers/dist/index.js deleted file mode 100644 index ddee7b163..000000000 --- a/packages/mapbox-gl-esri-feature-layers/dist/index.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ImageList = exports.styleForFeatureLayer = exports.fetchFeatureCollection = exports.fetchFeatureLayerData = void 0; -var fetchData_1 = require("./lib/fetchData"); -Object.defineProperty(exports, "fetchFeatureLayerData", { enumerable: true, get: function () { return fetchData_1.fetchFeatureLayerData; } }); -Object.defineProperty(exports, "fetchFeatureCollection", { enumerable: true, get: function () { return fetchData_1.fetchFeatureCollection; } }); -var styleForFeatureLayer_1 = require("./lib/styleForFeatureLayer"); -Object.defineProperty(exports, "styleForFeatureLayer", { enumerable: true, get: function () { return __importDefault(styleForFeatureLayer_1).default; } }); -var ImageList_1 = require("./lib/ImageList"); -Object.defineProperty(exports, "ImageList", { enumerable: true, get: function () { return ImageList_1.ImageList; } }); diff --git a/packages/mapbox-gl-esri-feature-layers/dist/lib/ImageList.js b/packages/mapbox-gl-esri-feature-layers/dist/lib/ImageList.js deleted file mode 100644 index 769868648..000000000 --- a/packages/mapbox-gl-esri-feature-layers/dist/lib/ImageList.js +++ /dev/null @@ -1,257 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ImageList = void 0; -const uuid_1 = require("uuid"); -const drawSMS_1 = __importDefault(require("./symbols/drawSMS")); -const fillPatterns_1 = __importDefault(require("./symbols/fillPatterns")); -const utils_1 = require("./symbols/utils"); -class ImageList { - constructor(arcGISVersion, isFeatureServer, imageSets) { - this.imageSets = []; - this.supportsHighDPILegends = false; - if (!isFeatureServer && arcGISVersion && arcGISVersion >= 10.6) { - this.supportsHighDPILegends = true; - } - if (imageSets) { - this.imageSets = imageSets; - } - } - toJSON() { - return this.imageSets; - } - /** - * Add a fill image for a PictureFillSymbol to the image set. - * - * PictureFillSymbol images cannot be requested at high-dpi from the legend - * endpoint because they would include an outline and not the full pattern. If - * there is a way to request a high-dpi image I do not know it. Instead, - * serialized image data is just pulled from the symbol itself. - * - * @hidden - * @param {PictureFillSymbol} symbol - * @returns {string} imageid - */ - addEsriPFS(symbol) { - const imageid = uuid_1.v4(); - this.imageSets.push({ - id: imageid, - images: [ - { - pixelRatio: 1, - dataURI: `data:${symbol.contentType};base64,${symbol.imageData}`, - width: utils_1.ptToPx(symbol.width), - height: utils_1.ptToPx(symbol.height), - }, - ], - }); - return imageid; - } - /** - * Add a PictureMarkerSymbol image to the set. If the server supports high-dpi - * legends (10.6+), this function will fetch high resolution markers from the - * origin server. Otherwise it will just use serialized image data from the - * symbol definition. - * - * @param {PictureMarkerSymbol} symbol - * @param {string} serviceBaseUrl - * @param {number} sublayer - * @param {number} legendIndex - * @returns {string} imageid - * @hidden - */ - addEsriPMS(symbol, serviceBaseUrl, sublayer, legendIndex) { - const imageid = uuid_1.v4(); - if (this.supportsHighDPILegends) { - this.imageSets.push(new Promise(async (resolve) => { - const imageSet = { - id: imageid, - images: [ - { - pixelRatio: 1, - dataURI: `data:${symbol.contentType};base64,${symbol.imageData}`, - width: utils_1.ptToPx(symbol.width), - height: utils_1.ptToPx(symbol.height), - }, - ], - }; - const legend2x = await fetchLegendImage(serviceBaseUrl, sublayer, legendIndex, 2); - const legend3x = await fetchLegendImage(serviceBaseUrl, sublayer, legendIndex, 3); - imageSet.images.push(legend2x, legend3x); - resolve(imageSet); - })); - } - else { - this.imageSets.push({ - id: imageid, - images: [ - { - pixelRatio: 1, - dataURI: `data:${symbol.contentType};base64,${symbol.imageData}`, - width: utils_1.ptToPx(symbol.width), - height: utils_1.ptToPx(symbol.height), - }, - ], - }); - } - return imageid; - } - /** - * Adds a SimpleMarkerSymbol to the ImageSet. These markers will be generated - * in multiple resolutions using html canvas to support multiple device pixel - * ratios (1, 2 and 3) - * - * @param {SimpleMarkerSymbol} symbol - * @returns {string} imageid - * @hidden - */ - addEsriSMS(symbol) { - const imageid = uuid_1.v4(); - let width = 0; - const images = [1, 2, 3].map((pixelRatio) => { - const marker = drawSMS_1.default(symbol, pixelRatio); - if (pixelRatio === 1) - width = marker.width; - return { - dataURI: marker.data, - pixelRatio, - width: marker.width, - height: marker.height, - }; - }); - this.imageSets.push({ - id: imageid, - images: images, - }); - return imageid; - } - /** - * @hidden - * @param {SimpleFillSymbol} symbol - * @returns - * @memberof ImageList - */ - addEsriSFS(symbol) { - const imageId = uuid_1.v4(); - const pattern = fillPatterns_1.default[symbol.style](utils_1.rgba(symbol.color)); - this.imageSets.push({ - id: imageId, - images: [ - createFillImage(pattern, 1), - createFillImage(pattern, 2), - createFillImage(pattern, 3), - ], - }); - return imageId; - } - /** - * Add all images to a MapBox GL JS map instance so that they may be used in - * style layers. Call before adding layers created by {@link styleForFeatureLayer}. - * - * The ImageList may contain multiple copies of images at different dpi. Since - * MapBox GL does not currently support adding images at multiple resolutions - * this function will pick those that best match the current [devicePixelRatio](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio). - * If the devicePixelRatio changes (e.g. switching monitors), the images - * *will not* be updated and may be at a less than ideal resolution, though - * mapbox gl will still show them at the correct size. - * - * @param {Map} map - * @returns - * @memberof ImageList - */ - addToMap(map) { - return Promise.all(this.imageSets.map(async (imageSet) => { - if (imageSet instanceof Promise) { - imageSet = await imageSet; - } - let imageData = imageSet.images[0]; - // MapBox GL does not allow adding images with multiple copies for each - // pixelRatio. So we have to pick the one that matches the current - // devicePixelRatio. This may change during the user session and result - // than a less than ideal display, but updating the image is a lot of - // extra complexity to manage. - if (imageSet.images.length > 1) { - imageData = - imageSet.images.find((i) => i.pixelRatio === Math.round(window.devicePixelRatio)) || imageData; - } - const image = await createImage(imageData.width, imageData.height, imageData.dataURI); - map.addImage(imageSet.id, image, { - pixelRatio: imageData.pixelRatio, - }); - })); - } - /** - * Remove a previously added ImageList from the map - * - * @param {Map} map - * @memberof ImageList - */ - removeFromMap(map) { - return Promise.all(this.imageSets.map(async (imageSet) => { - if (imageSet instanceof Promise) { - imageSet = await imageSet; - } - map.removeImage(imageSet.id); - })); - } -} -exports.ImageList = ImageList; -/** @hidden */ -async function createImage(width, height, dataURI) { - return new Promise((resolve) => { - const image = new Image(width, height); - image.src = dataURI; - image.onload = () => { - resolve(image); - }; - }); -} -/** @hidden */ -function createFillImage(pattern, pixelRatio) { - const size = 4 * 2 ** pixelRatio; - const canvas = document.createElement("canvas"); - canvas.setAttribute("width", size.toString()); - canvas.setAttribute("height", size.toString()); - const ctx = canvas.getContext("2d"); - ctx.fillStyle = pattern; - ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.lineTo(0, size); - ctx.lineTo(size, size); - ctx.lineTo(size, 0); - ctx.closePath(); - ctx.fill(); - return { - pixelRatio, - dataURI: canvas.toDataURL(), - width: size, - height: size, - }; -} -/** @hidden */ -const cache = {}; -/** @hidden */ -async function fetchLegendImage(serviceRoot, sublayer, legendIndex, pixelRatio) { - const legendData = await fetchLegendData(serviceRoot, pixelRatio); - const sublayerData = legendData.layers.find((lyr) => lyr.layerId === sublayer); - const legendItem = sublayerData.legend[legendIndex]; - return { - dataURI: `data:${legendItem.contentType};base64,${legendItem.imageData}`, - pixelRatio, - width: legendItem.width, - height: legendItem.height, - }; -} -/** @hidden */ -async function fetchLegendData(serviceRoot, pixelRatio) { - const dpi = pixelRatio === 2 ? 192 : 384; - if (!cache[serviceRoot]) { - cache[serviceRoot] = {}; - } - if (!cache[serviceRoot][pixelRatio]) { - cache[serviceRoot][pixelRatio] = fetch(`${serviceRoot}/legend?f=json&dpi=${dpi}`).then((r) => r.json()); - } - return cache[serviceRoot][pixelRatio]; -} diff --git a/packages/mapbox-gl-esri-feature-layers/dist/lib/fetchData.js b/packages/mapbox-gl-esri-feature-layers/dist/lib/fetchData.js deleted file mode 100644 index 387937340..000000000 --- a/packages/mapbox-gl-esri-feature-layers/dist/lib/fetchData.js +++ /dev/null @@ -1,110 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.fetchFeatureLayerData = exports.fetchFeatureCollection = void 0; -const bytes_1 = __importDefault(require("bytes")); -function fetchFeatureCollection(url, geometryPrecision = 6, outFields = "*", bytesLimit = 1000000 * 100) { - return new Promise((resolve, reject) => { - fetchFeatureLayerData(url, outFields, reject, geometryPrecision, null, null, undefined, undefined, bytesLimit) - .then((data) => resolve(data)) - .catch((e) => reject(e)); - }); -} -exports.fetchFeatureCollection = fetchFeatureCollection; -async function fetchFeatureLayerData(url, outFields, onError, geometryPrecision = 6, abortController = null, onPageReceived = null, disablePagination = false, pageSize = 1000, bytesLimit) { - const featureCollection = { - type: "FeatureCollection", - features: [], - }; - const params = new URLSearchParams({ - inSR: "4326", - outSR: "4326", - where: "1>0", - outFields, - returnGeometry: "true", - geometryPrecision: geometryPrecision.toString(), - returnIdsOnly: "false", - f: "geojson", - }); - await fetchData(url, params, featureCollection, onError, abortController, onPageReceived, disablePagination, pageSize, bytesLimit); - return featureCollection; -} -exports.fetchFeatureLayerData = fetchFeatureLayerData; -async function fetchData(baseUrl, params, featureCollection, onError, abortController, onPageReceived, disablePagination = false, pageSize = 1000, bytesLimit, bytesReceived, objectIdFieldName, expectedFeatureCount) { - var _a; - bytesReceived = bytesReceived || 0; - const decoder = new TextDecoder("utf-8"); - params.set("returnIdsOnly", "false"); - if (featureCollection.features.length > 0) { - // fetch next page using objectIds - let featureIds; - params.delete("where"); - params.delete("resultOffset"); - params.delete("resultRecordCount"); - params.set("orderByFields", objectIdFieldName); - const lastFeature = featureCollection.features[featureCollection.features.length - 1]; - params.set("where", `${objectIdFieldName}>${lastFeature.id}`); - } - const response = await fetch(`${baseUrl}/query?${params.toString()}`, { - // mode: "cors", - ...(abortController ? { signal: abortController.signal } : {}), - }); - const str = await response.text(); - bytesReceived += byteLength(str); - if (bytesLimit && bytesReceived > bytesLimit) { - const e = new Error(`Exceeded bytesLimit. ${bytes_1.default(bytesReceived)} > ${bytes_1.default(bytesLimit)}`); - return onError(e); - } - const fc = JSON.parse(str); - if (fc.error) { - return onError(new Error(fc.error.message)); - } - else { - featureCollection.features.push(...fc.features); - if (fc.exceededTransferLimit || ((_a = fc.properties) === null || _a === void 0 ? void 0 : _a.exceededTransferLimit)) { - if (!objectIdFieldName) { - // Fetch objectIds to do manual paging - params.set("returnIdsOnly", "true"); - try { - const r = await fetch(`${baseUrl}/query?${params.toString()}`, { - // mode: "cors", - ...(abortController ? { signal: abortController.signal } : {}), - }); - const featureIds = featureCollection.features.map((f) => f.id); - let objectIdParameters = await r.json(); - // FeatureServers (at least on ArcGIS Online) behave differently - if (objectIdParameters.properties) { - objectIdParameters = objectIdParameters.properties; - } - expectedFeatureCount = objectIdParameters.objectIds.length; - objectIdFieldName = objectIdParameters.objectIdFieldName; - } - catch (e) { - return onError(e); - } - } - if (onPageReceived) { - onPageReceived(bytesReceived, featureCollection.features.length, expectedFeatureCount); - } - await fetchData(baseUrl, params, featureCollection, onError, abortController, onPageReceived, disablePagination, pageSize, bytesLimit, bytesReceived, objectIdFieldName, expectedFeatureCount); - } - } - return bytesReceived; -} -// https://stackoverflow.com/a/23329386/299467 -function byteLength(str) { - // returns the byte length of an utf8 string - var s = str.length; - for (var i = str.length - 1; i >= 0; i--) { - var code = str.charCodeAt(i); - if (code > 0x7f && code <= 0x7ff) - s++; - else if (code > 0x7ff && code <= 0xffff) - s += 2; - if (code >= 0xdc00 && code <= 0xdfff) - i--; //trail surrogate - } - return s; -} diff --git a/packages/mapbox-gl-esri-feature-layers/dist/lib/styleForFeatureLayer.js b/packages/mapbox-gl-esri-feature-layers/dist/lib/styleForFeatureLayer.js deleted file mode 100644 index 9bea698b4..000000000 --- a/packages/mapbox-gl-esri-feature-layers/dist/lib/styleForFeatureLayer.js +++ /dev/null @@ -1,185 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const index_1 = require("./symbols/index"); -const ImageList_1 = require("./ImageList"); -const esriTS_1 = __importDefault(require("./symbols/esriTS")); -const utils_1 = require("./symbols/utils"); -/** - * This function retrieves rendering and style information from the ArcGIS REST - * API for a given [Feature Layer](https://developers.arcgis.com/rest/services-reference/layer-table.htm) - * and produces images and style layers that can be used to faithfully represent - * these services as vectors in MapBox GL. It can be used in conjunction with - * {@link ArcGISVectorSource}. - * - * Style generation is seperated from source handling so that you could even - * use tippecanoe or other tools to generate vector tiles from a service and - * style them using the generated layers. With this seperation of concerns it's - * also possible to cache style information so that it does not need to - * always be generated dynamically. - * - * ### Usage - * - * ```typescript - * import { ArcGISVectorSource, styleForFeatureLayer } from "mapbox-gl-esri-sources"; - * - * // setup map... - * // add source... - * - * const { imageList, layers } = styleForFeatureLayer( - * "https://sampleserver6.arcgisonline.com/arcgis/rest/services/SampleWorldCities/MapServer/0", - * "cities-source-id" - * ); - * - * imageList.addToMap(map); - * - * for (const layer of layers) { - * map.addLayer(layer); - * } - * - * ``` - * - * @param {string} url Feature layer endpoint. Should terminate in _/MapServer/0..n_ - * @param {string} sourceId ID for the [source](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/) of vector data to be used in rendering. - * @returns The {@link ImageList.addToMap} function should be called before adding the generated layers to the map. - */ -async function styleForFeatureLayer(url, sourceId) { - const rootUrl = url.replace(/\/\d+[\/]*$/, ""); - const sublayer = parseInt(url.match(/\/(\d+)[\/]*$/)[1]); - const response = await fetch(url + "?f=json").then((r) => r.json()); - const renderer = response.drawingInfo.renderer; - let layers = []; - const imageList = new ImageList_1.ImageList(response.currentVersion, /FeatureServer/.test(rootUrl)); - let legendItemIndex = 0; - switch (renderer.type) { - case "uniqueValue": { - const fields = [renderer.field1]; - if (renderer.field2) { - fields.push(renderer.field2); - if (renderer.field3) { - fields.push(renderer.field3); - } - } - const filters = []; - const field = renderer.field1; - legendItemIndex = renderer.defaultSymbol ? 1 : 0; - const fieldTypes = fields.map((f) => { - const fieldRecord = response.fields.find((r) => r.name === f); - return FIELD_TYPES[fieldRecord === null || fieldRecord === void 0 ? void 0 : fieldRecord.type] || "string"; - }); - for (const info of renderer.uniqueValueInfos) { - const values = normalizeValuesForFieldTypes(info.value, renderer.fieldDelimiter, fieldTypes); - layers.push(...index_1.symbolToLayers(info.symbol, sourceId, imageList, rootUrl, sublayer, legendItemIndex++).map((lyr) => { - if (fields.length === 1) { - lyr.filter = ["==", field, values[0]]; - filters.push(lyr.filter); - } - else { - lyr.filter = [ - "all", - ...fields.map((field) => [ - "==", - field, - values[fields.indexOf(field)], - ]), - ]; - filters.push(lyr.filter); - } - return lyr; - })); - } - if (renderer.defaultSymbol && renderer.defaultSymbol.type) { - layers.push(...index_1.symbolToLayers(renderer.defaultSymbol, sourceId, imageList, rootUrl, sublayer, 0).map((lyr) => { - lyr.filter = ["none", ...filters]; - return lyr; - })); - } - break; - } - case "classBreaks": - // TODO: look for test dataset for backgroundFillSymbol - if (renderer.backgroundFillSymbol) { - layers.push(...index_1.symbolToLayers(renderer.backgroundFillSymbol, sourceId, imageList, rootUrl, sublayer, 0)); - } - const field = renderer.field; - const filters = []; - legendItemIndex = renderer.classBreakInfos.length - 1; - let minValue = 0; - const minMaxValues = renderer.classBreakInfos.map((b) => { - const values = [b.classMinValue || minValue, b.classMaxValue]; - minValue = values[1]; - return values; - }); - for (const info of [...renderer.classBreakInfos].reverse()) { - layers.push(...index_1.symbolToLayers(info.symbol, sourceId, imageList, rootUrl, sublayer, legendItemIndex--).map((lyr) => { - const [min, max] = minMaxValues[renderer.classBreakInfos.indexOf(info)]; - if (renderer.classBreakInfos.indexOf(info) === 0) { - lyr.filter = ["all", ["<=", field, max]]; - } - else { - lyr.filter = ["all", [">", field, min], ["<=", field, max]]; - } - filters.push(lyr.filter); - return lyr; - })); - } - if (renderer.defaultSymbol && renderer.defaultSymbol.type) { - const defaultLayers = await index_1.symbolToLayers(renderer.defaultSymbol, sourceId, imageList, rootUrl, sublayer, 0); - for (const index in defaultLayers) { - defaultLayers[index].filter = ["none", filters]; - } - layers.push(...defaultLayers); - } - break; - default: - // simple - layers = index_1.symbolToLayers(renderer.symbol, sourceId, imageList, rootUrl, sublayer, 0); - break; - } - if (response.drawingInfo.labelingInfo) { - for (const info of response.drawingInfo.labelingInfo) { - const layer = esriTS_1.default(info, response.geometryType, response.fields.map((f) => f.name)); - layer.source = sourceId; - layer.id = utils_1.generateId(); - layers.push(layer); - } - } - return { - imageList, - layers, - }; -} -exports.default = styleForFeatureLayer; -/** @hidden */ -function normalizeValuesForFieldTypes(value, delimiter, fieldTypes) { - const values = value.split(delimiter); - return values.map((v, i) => { - if (fieldTypes[i] === "string") { - return v; - } - else if (fieldTypes[i] === "integer") { - return parseInt(v); - } - else if (fieldTypes[i] === "float") { - return parseFloat(v); - } - }); -} -/** @hidden */ -const FIELD_TYPES = { - esriFieldTypeSmallInteger: "integer", - esriFieldTypeInteger: "integer", - esriFieldTypeSingle: "float", - esriFieldTypeDouble: "float", - esriFieldTypeString: "string", - esriFieldTypeDate: "string", - esriFieldTypeOID: "integer", - esriFieldTypeGeometry: "string", - esriFieldTypeBlob: "string", - esriFieldTypeRaster: "string", - esriFieldTypeGUID: "string", - esriFieldTypeGlobalID: "string", - esriFieldTypeXML: "string", -}; diff --git a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/drawSMS.js b/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/drawSMS.js deleted file mode 100644 index 17fd13a7a..000000000 --- a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/drawSMS.js +++ /dev/null @@ -1,99 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const utils_1 = require("./utils"); -/** @hidden */ -function default_1(symbol, pixelRatio) { - var _a, _b; - const size = utils_1.ptToPx(symbol.size || 13); - const scale = 2 ** (pixelRatio - 1); - const padding = 1; - const width = (size + 1 * 2 + (((_a = symbol.outline) === null || _a === void 0 ? void 0 : _a.width) || 0) * 2) * scale; - const height = width; - let canvas = utils_1.createCanvas(width, height); - var ctx = canvas.getContext("2d"); - ctx.lineWidth = - utils_1.ptToPx(!!symbol.outline ? symbol.outline.width || 1 : 1) * scale; - ctx.strokeStyle = !!symbol.outline - ? utils_1.rgba((_b = symbol.outline) === null || _b === void 0 ? void 0 : _b.color) - : utils_1.rgba(symbol.color); - ctx.fillStyle = utils_1.rgba(symbol.color); - switch (symbol.style) { - case "esriSMSCircle": - // canvas.style = "image-rendering: pixelated;"; - // ctx.imageSmoothingEnabled = false; - ctx.beginPath(); - var x = width / 2; - var y = height / 2; - var diameter = size * scale; - // I have no idea why, but adding a bit here helps match arcgis server output a bit better - var radius = Math.round((diameter + ctx.lineWidth) / 2); - ctx.arc(x, y, radius, 0, Math.PI * 2, true); - ctx.fill(); - ctx.stroke(); - break; - case "esriSMSCross": - var w = size * scale; - ctx.lineWidth = Math.round(w / 4); - ctx.strokeStyle = utils_1.rgba(symbol.color); - ctx.moveTo(width / 2, (height - w) / 2); - ctx.lineTo(width / 2, height - (height - w) / 2); - ctx.moveTo((width - w) / 2, height / 2); - ctx.lineTo(width - (width - w) / 2, height / 2); - ctx.stroke(); - ctx.fill(); - break; - case "esriSMSX": - var w = size * scale; - ctx.translate(width / 2, height / 2); - ctx.rotate((45 * Math.PI) / 180); - ctx.translate(-width / 2, -height / 2); - ctx.moveTo(width / 2, (height - w) / 2); - ctx.lineTo(width / 2, height - (height - w) / 2); - ctx.moveTo((width - w) / 2, height / 2); - ctx.lineTo(width - (width - w) / 2, height / 2); - ctx.stroke(); - ctx.fill(); - ctx.setTransform(1, 0, 0, 1, 0, 0); - break; - case "esriSMSDiamond": - var w = size * scale; - var h = w; - var x = width / 2 - w / 2; - var y = height / 2 - h / 2; - ctx.translate(x + w / 2, y + h / 2); - ctx.rotate((45 * Math.PI) / 180); - ctx.fillRect(-w / 2, -h / 2, w, h); - ctx.strokeRect(-w / 2, -h / 2, w, h); - break; - case "esriSMSSquare": - var w = size * scale; - var h = w; - var x = width / 2 - w / 2; - var y = height / 2 - h / 2; - ctx.fillRect(x, y, w, h); - ctx.strokeRect(x, y, w, h); - break; - case "esriSMSTriangle": - ctx.beginPath(); - var w = size * scale; - var h = w; - var midpoint = width / 2; - var x1 = midpoint; - var y1 = (height - width) / 2; - var x2 = width - (width - width) / 2; - var y2 = height - (height - width) / 2; - var x3 = (width - width) / 2; - var y3 = height - (height - width) / 2; - ctx.moveTo(x1, y1); - ctx.lineTo(x2, y2); - ctx.lineTo(x3, y3); - ctx.lineTo(x1, y1); - ctx.fill(); - ctx.stroke(); - break; - default: - throw new Error(`Unknown symbol type ${symbol.style}`); - } - return { width, height, data: canvas.toDataURL() }; -} -exports.default = default_1; diff --git a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriPFS.js b/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriPFS.js deleted file mode 100644 index c6be6e267..000000000 --- a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriPFS.js +++ /dev/null @@ -1,35 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const esriSLS_1 = __importDefault(require("./esriSLS")); -const utils_1 = require("./utils"); -// TODO: Add support for lesser-used options -// height -// width -// angle -// xoffset -// yoffset -// xscale -// yscale -/** @hidden */ -exports.default = (symbol, sourceId, imageList) => { - const imageId = imageList.addEsriPFS(symbol); - const layers = [ - { - id: utils_1.generateId(), - source: sourceId, - type: "fill", - paint: { - "fill-pattern": imageId, - }, - layout: {}, - }, - ]; - if ("outline" in symbol) { - let outline = esriSLS_1.default(symbol.outline, sourceId); - layers.push(...outline); - } - return layers; -}; diff --git a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriPMS.js b/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriPMS.js deleted file mode 100644 index b90d2bf20..000000000 --- a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriPMS.js +++ /dev/null @@ -1,21 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const utils_1 = require("./utils"); -/** @hidden */ -exports.default = (symbol, sourceId, imageList, serviceBaseUrl, sublayer, legendIndex) => { - const imageId = imageList.addEsriPMS(symbol, serviceBaseUrl, sublayer, legendIndex); - return [ - { - id: utils_1.generateId(), - source: sourceId, - type: "symbol", - paint: {}, - layout: { - "icon-allow-overlap": true, - "icon-rotate": symbol.angle, - "icon-offset": [symbol.xoffset || 0, symbol.yoffset || 0], - "icon-image": imageId, - }, - }, - ]; -}; diff --git a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriSFS.js b/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriSFS.js deleted file mode 100644 index 1182bc315..000000000 --- a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriSFS.js +++ /dev/null @@ -1,64 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const utils_1 = require("./utils"); -const esriSLS_1 = __importDefault(require("./esriSLS")); -const utils_2 = require("./utils"); -/** @hidden */ -exports.default = (symbol, sourceId, imageList) => { - const layers = []; - let useFillOutlineColor = symbol.outline && - utils_1.ptToPx(symbol.outline.width || 1) === 1 && - symbol.outline.style === "esriSLSSolid"; - switch (symbol.style) { - case "esriSFSSolid": - if (symbol.color && symbol.color[3] === 0) { - useFillOutlineColor = false; - } - else { - layers.push({ - id: utils_2.generateId(), - type: "fill", - source: sourceId, - paint: { - "fill-color": utils_1.rgba(symbol.color), - ...(useFillOutlineColor - ? { "fill-outline-color": utils_1.rgba(symbol.outline.color) } - : {}), - }, - }); - } - break; - case "esriSFSNull": - // leave empty - break; - case "esriSFSBackwardDiagonal": - case "esriSFSCross": - case "esriSFSDiagonalCross": - case "esriSFSForwardDiagonal": - case "esriSFSHorizontal": - case "esriSFSVertical": - const imageId = imageList.addEsriSFS(symbol); - layers.push({ - id: utils_2.generateId(), - source: sourceId, - type: "fill", - paint: { - "fill-pattern": imageId, - ...(useFillOutlineColor - ? { "fill-outline-color": utils_1.rgba(symbol.outline.color) } - : {}), - }, - }); - break; - default: - throw new Error(`Unknown fill style ${symbol.style}`); - } - if (symbol.outline && !useFillOutlineColor) { - let outline = esriSLS_1.default(symbol.outline, sourceId); - layers.push(...outline); - } - return layers; -}; diff --git a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriSLS.js b/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriSLS.js deleted file mode 100644 index f7c6e37ad..000000000 --- a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriSLS.js +++ /dev/null @@ -1,33 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const utils_1 = require("./utils"); -const linePatterns_1 = __importDefault(require("./linePatterns")); -const utils_2 = require("./utils"); -/** @hidden */ -exports.default = (symbol, sourceId) => { - const { color, opacity } = utils_1.colorAndOpacity(symbol.color); - let strokeWidth = utils_1.ptToPx(symbol.width || 1); - // No idea why... but this matches map service image output - if (strokeWidth === -1) { - strokeWidth = 1; - } - const style = symbol.style || "esriSLSSolid"; - const layer = { - id: utils_2.generateId(), - type: "line", - paint: { - "line-color": color, - "line-opacity": opacity, - "line-width": strokeWidth, - }, - layout: {}, - source: sourceId, - }; - if (style !== "esriSLSSolid") { - layer.paint["line-dasharray"] = linePatterns_1.default[style](strokeWidth); - } - return [layer]; -}; diff --git a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriSMS.js b/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriSMS.js deleted file mode 100644 index f27496134..000000000 --- a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriSMS.js +++ /dev/null @@ -1,22 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const utils_1 = require("./utils"); -/** @hidden */ -exports.default = (symbol, sourceId, imageList) => { - const imageId = imageList.addEsriSMS(symbol); - return [ - { - id: utils_1.generateId(), - type: "symbol", - source: sourceId, - paint: {}, - layout: { - "icon-allow-overlap": true, - "icon-rotate": symbol.angle, - "icon-offset": [symbol.xoffset || 0, symbol.yoffset || 0], - "icon-image": imageId, - "icon-size": 1, - }, - }, - ]; -}; diff --git a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriTS.js b/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriTS.js deleted file mode 100644 index f8e4274e2..000000000 --- a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/esriTS.js +++ /dev/null @@ -1,47 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const utils_1 = require("./utils"); -/** @hidden */ -exports.default = (labelingInfo, geometryType, fieldNames) => { - // TODO: Support scale-dependant rendering. Right now just taking first label - // TODO: labelExpressions (full Arcade!?) - // TODO: where expressions - // TODO: xoffset, yoffset, kerning, angle, rightToLeft, horizontalAlignment, etc - // See https://developers.arcgis.com/documentation/common-data-types/labeling-objects.htm - return { - id: utils_1.generateId(), - type: "symbol", - layout: { - // TODO: properly support labeling functions like UCASE(), CONCAT(), etc - // https://developers.arcgis.com/documentation/common-data-types/labeling-objects.htm - "text-field": toExpression(labelingInfo.labelExpression, fieldNames), - // Only supports points right now - "text-anchor": utils_1.toTextAnchor(labelingInfo.labelPlacement), - "text-size": utils_1.ptToPx(labelingInfo.symbol.font.size || 13), - "symbol-placement": geometryType === "line" ? "line" : "point", - "text-max-angle": 20, - }, - paint: { - "text-color": utils_1.rgba(labelingInfo.symbol.color), - "text-halo-width": utils_1.ptToPx(labelingInfo.symbol.haloSize || 0), - "text-halo-color": utils_1.rgba(labelingInfo.symbol.haloColor || [255, 255, 255, 255]), - "text-halo-blur": utils_1.ptToPx(labelingInfo.symbol.haloSize || 0) * 0.5, - }, - }; -}; -/** @hidden */ -function toExpression(labelExpression, fieldNames) { - const fields = (labelExpression.match(/\[\w+\]/g) || []) - .map((val) => val.replace(/[\[\]]/g, "")) - .map((val) => fieldNames.find((name) => name.toLowerCase() === val.toLowerCase())); - const strings = labelExpression.split(/\[\w+\]/g); - const expression = ["format"]; - while (strings.length) { - expression.push(strings.shift()); - const field = fields.shift(); - if (field) { - expression.push(["get", field]); - } - } - return expression; -} diff --git a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/fillPatterns.js b/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/fillPatterns.js deleted file mode 100644 index 67dce2faf..000000000 --- a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/fillPatterns.js +++ /dev/null @@ -1,96 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const utils_1 = require("./utils"); -/** @hidden */ -exports.default = { - esriSFSVertical: (strokeStyle = "#000000") => { - var canvas = utils_1.createCanvas(16, 16); - var ctx = canvas.getContext("2d"); - ctx.strokeStyle = strokeStyle || "#000000"; - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.moveTo(8, 0); - ctx.lineTo(8, 16); - ctx.stroke(); - return ctx.createPattern(canvas, "repeat"); - }, - esriSFSHorizontal: (strokeStyle = "#000000") => { - var canvas = utils_1.createCanvas(16, 16); - var ctx = canvas.getContext("2d"); - ctx.strokeStyle = strokeStyle || "#000000"; - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.moveTo(0, 8); - ctx.lineTo(16, 8); - ctx.stroke(); - return ctx.createPattern(canvas, "repeat"); - }, - esriSFSBackwardDiagonal: (strokeStyle = "#000000") => { - var canvas = utils_1.createCanvas(16, 16); - var ctx = canvas.getContext("2d"); - ctx.strokeStyle = strokeStyle; - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.moveTo(0, 8); - ctx.lineTo(8, 0); - ctx.stroke(); - ctx.beginPath(); - ctx.moveTo(0, 24); - ctx.lineTo(24, 0); - ctx.stroke(); - return ctx.createPattern(canvas, "repeat"); - }, - esriSFSForwardDiagonal: (strokeStyle = "#000000") => { - var canvas = utils_1.createCanvas(16, 16); - var ctx = canvas.getContext("2d"); - ctx.strokeStyle = strokeStyle; - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.moveTo(0, 8); - ctx.lineTo(8, 16); - ctx.stroke(); - ctx.beginPath(); - ctx.moveTo(8, 0); - ctx.lineTo(16, 8); - ctx.stroke(); - return ctx.createPattern(canvas, "repeat"); - }, - esriSFSCross: (strokeStyle = "#000000") => { - var canvas = utils_1.createCanvas(16, 16); - var ctx = canvas.getContext("2d"); - ctx.strokeStyle = strokeStyle; - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.moveTo(0, 8); - ctx.lineTo(16, 8); - ctx.stroke(); - ctx.beginPath(); - ctx.moveTo(8, 0); - ctx.lineTo(8, 16); - ctx.stroke(); - return ctx.createPattern(canvas, "repeat"); - }, - esriSFSDiagonalCross: (strokeStyle = "#000000") => { - var canvas = utils_1.createCanvas(16, 16); - var ctx = canvas.getContext("2d"); - ctx.strokeStyle = strokeStyle; - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.moveTo(0, 8); - ctx.lineTo(8, 16); - ctx.stroke(); - ctx.beginPath(); - ctx.moveTo(8, 0); - ctx.lineTo(16, 8); - ctx.stroke(); - ctx.beginPath(); - ctx.moveTo(0, 8); - ctx.lineTo(8, 0); - ctx.stroke(); - ctx.beginPath(); - ctx.moveTo(0, 24); - ctx.lineTo(24, 0); - ctx.stroke(); - return ctx.createPattern(canvas, "repeat"); - }, -}; diff --git a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/index.js b/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/index.js deleted file mode 100644 index 9caf9d0b9..000000000 --- a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/index.js +++ /dev/null @@ -1,36 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.symbolToLayers = void 0; -const esriSLS_1 = __importDefault(require("./esriSLS")); -const esriSFS_1 = __importDefault(require("./esriSFS")); -const esriPMS_1 = __importDefault(require("./esriPMS")); -const esriSMS_1 = __importDefault(require("./esriSMS")); -const esriPFS_1 = __importDefault(require("./esriPFS")); -/** @hidden */ -function symbolToLayers(symbol, sourceId, imageList, serviceBaseUrl, sublayer, legendIndex) { - var layers; - switch (symbol.type) { - case "esriSFS": - layers = esriSFS_1.default(symbol, sourceId, imageList); - break; - case "esriPFS": - layers = esriPFS_1.default(symbol, sourceId, imageList); - break; - case "esriSLS": - layers = esriSLS_1.default(symbol, sourceId); - break; - case "esriPMS": - layers = esriPMS_1.default(symbol, sourceId, imageList, serviceBaseUrl, sublayer, legendIndex); - break; - case "esriSMS": - layers = esriSMS_1.default(symbol, sourceId, imageList); - break; - default: - throw new Error(`Unknown symbol type ${symbol.type}`); - } - return layers; -} -exports.symbolToLayers = symbolToLayers; diff --git a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/linePatterns.js b/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/linePatterns.js deleted file mode 100644 index 5bd371457..000000000 --- a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/linePatterns.js +++ /dev/null @@ -1,11 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -/** @hidden */ -const patterns = { - esriSLSDash: (strokeWidth) => [2, 0.5], - esriSLSDashDot: (strokeWidth) => [3, 1, 1, 1], - esriSLSDashDotDot: (strokeWidth) => [3, 1, 1, 1, 1, 1], - esriSLSNull: () => [0, 10], - esriSLSDot: (strokeWidth) => [1, 1], -}; -exports.default = patterns; diff --git a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/utils.js b/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/utils.js deleted file mode 100644 index 5a92f28b2..000000000 --- a/packages/mapbox-gl-esri-feature-layers/dist/lib/symbols/utils.js +++ /dev/null @@ -1,72 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.toTextAnchor = exports.ptToPx = exports.colorAndOpacity = exports.rgba = exports.createCanvas = exports.generateId = void 0; -const uuid_1 = require("uuid"); -/** @hidden */ -function generateId() { - return uuid_1.v4(); -} -exports.generateId = generateId; -/** @hidden */ -function createCanvas(w, h) { - const canvas = document.createElement("canvas"); - canvas.setAttribute("width", w.toString()); - canvas.setAttribute("height", h.toString()); - return canvas; -} -exports.createCanvas = createCanvas; -/** @hidden */ -const rgba = (color) => { - color = color || [0, 0, 0, 0]; - return `rgba(${color[0]},${color[1]},${color[2]},${color[3] / 255})`; -}; -exports.rgba = rgba; -/** @hidden */ -const colorAndOpacity = (color) => { - color = color || [0, 0, 0, 0]; - return { - color: `rgb(${color[0]},${color[1]},${color[2]})`, - opacity: color[3] / 255, - }; -}; -exports.colorAndOpacity = colorAndOpacity; -/** @hidden */ -const ptToPx = (pt) => Math.round(pt * 1.33); -exports.ptToPx = ptToPx; -/** @hidden */ -const ANCHORS = { - // Note that these are essentially backwards from what you'd expect - // details: http://resources.arcgis.com/en/help/rest/apiref/index.html?renderer.html - // https://www.mapbox.com/mapbox-gl-js/style-spec/#layout-symbol-text-anchor - // Label Placement Values For Point Features - esriServerPointLabelPlacementAboveCenter: "bottom", - esriServerPointLabelPlacementAboveLeft: "bottom-right", - esriServerPointLabelPlacementAboveRight: "bottom-left", - esriServerPointLabelPlacementBelowCenter: "top", - esriServerPointLabelPlacementBelowLeft: "top-right", - esriServerPointLabelPlacementBelowRight: "top-left", - esriServerPointLabelPlacementCenterCenter: "center", - esriServerPointLabelPlacementCenterLeft: "right", - esriServerPointLabelPlacementCenterRight: "left", - // Label Placement Values For Line Features - // esriServerLinePlacementAboveAfter - esriServerLinePlacementAboveAlong: "bottom", - esriServerLinePlacementAboveBefore: "bottom-left", - esriServerLinePlacementAboveStart: "bottom-left", - esriServerLinePlacementAboveEnd: "bottom-right", - esriServerLinePlacementBelowAfter: "top-right", - esriServerLinePlacementBelowAlong: "top", - esriServerLinePlacementBelowBefore: "top-left", - esriServerLinePlacementBelowStart: "top-left", - esriServerLinePlacementBelowEnd: "top-right", - esriServerLinePlacementCenterAfter: "right", - esriServerLinePlacementCenterAlong: "center", - esriServerLinePlacementCenterBefore: "center-left", - esriServerLinePlacementCenterStart: "center-left", - esriServerLinePlacementCenterEnd: "center-right", - // // Label Placement Values For Polygon Features - esriServerPolygonPlacementAlwaysHorizontal: "center", -}; -/** @hidden */ -const toTextAnchor = (labelPlacement) => ANCHORS[labelPlacement] || "center"; -exports.toTextAnchor = toTextAnchor; diff --git a/packages/mapbox-gl-esri-feature-layers/index.ts b/packages/mapbox-gl-esri-feature-layers/index.ts deleted file mode 100644 index da836a017..000000000 --- a/packages/mapbox-gl-esri-feature-layers/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { fetchFeatureLayerData, fetchFeatureCollection } from "./lib/fetchData"; -import { ImageList } from "./lib/ImageList"; -export { default as styleForFeatureLayer } from "./lib/styleForFeatureLayer"; -export { ImageList } from "./lib/ImageList"; diff --git a/packages/mapbox-gl-esri-feature-layers/package-lock.json b/packages/mapbox-gl-esri-feature-layers/package-lock.json deleted file mode 100644 index e0c374a3a..000000000 --- a/packages/mapbox-gl-esri-feature-layers/package-lock.json +++ /dev/null @@ -1,9671 +0,0 @@ -{ - "name": "mapbox-gl-esri-feature-layers", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "mapbox-gl-esri-feature-layers", - "version": "1.0.0", - "license": "BSD-3-Clause", - "dependencies": { - "@types/arcgis-rest-api": "^10.4.4", - "@types/geojson": "^7946.0.7", - "@types/mapbox-gl": "^2.1.1", - "bytes": "^3.1.0", - "esbuild": "^0.9.6", - "geojson": "^0.5.0", - "jest": "^26.6.3", - "typescript": "^4.2.3" - }, - "devDependencies": { - "@types/bytes": "^3.1.0", - "@types/uuid": "^8.3.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.0.tgz", - "integrity": "sha512-d5RysTlJ7hmw5Tw4UxgxcY3lkMe92n8sXCcuLPAyIAHK6j8DefDwtGnVVDgOnv+RnEosulDJ9NPKQL27bDId0g==", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "dependencies": { - "@babel/highlight": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.0.tgz", - "integrity": "sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.2.tgz", - "integrity": "sha512-R3VH5G42VSDolRHyUO4V2cfag8WHcZyxdq5Z/m8Xyb92lW/Erm/6kM+XtRFGf3Mulre3mveni2NHfEUws8wSvw==", - "dependencies": { - "@ampproject/remapping": "^2.0.0", - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.0", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.17.2", - "@babel/parser": "^7.17.0", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.0", - "@babel/types": "^7.17.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.0.tgz", - "integrity": "sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw==", - "dependencies": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/generator/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", - "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", - "dependencies": { - "@babel/compat-data": "^7.16.4", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", - "dependencies": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", - "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", - "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", - "dependencies": { - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.2.tgz", - "integrity": "sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==", - "dependencies": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.0", - "@babel/types": "^7.17.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.0.tgz", - "integrity": "sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw==", - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.0.tgz", - "integrity": "sha512-fpFIXvqD6kC7c7PUNnZ0Z8cQXlarCLtCUpt2S1Dx7PjoRtCFffvOkHHSom+m5HIxMZn5bIBVb71lhabcmjEsqg==", - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.0", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.0", - "@babel/types": "^7.17.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" - }, - "node_modules/@cnakazawa/watch": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", - "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", - "dependencies": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - }, - "bin": { - "watch": "cli.js" - }, - "engines": { - "node": ">=0.1.95" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/@jest/core": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", - "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", - "dependencies": { - "@jest/console": "^26.6.2", - "@jest/reporters": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-changed-files": "^26.6.2", - "jest-config": "^26.6.3", - "jest-haste-map": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.6.2", - "jest-resolve-dependencies": "^26.6.3", - "jest-runner": "^26.6.3", - "jest-runtime": "^26.6.3", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "jest-watcher": "^26.6.2", - "micromatch": "^4.0.2", - "p-each-series": "^2.1.0", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "dependencies": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/@jest/fake-timers": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", - "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", - "dependencies": { - "@jest/types": "^26.6.2", - "@sinonjs/fake-timers": "^6.0.1", - "@types/node": "*", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/@jest/globals": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", - "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", - "dependencies": { - "@jest/environment": "^26.6.2", - "@jest/types": "^26.6.2", - "expect": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/@jest/reporters": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", - "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.4", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.3", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "jest-haste-map": "^26.6.2", - "jest-resolve": "^26.6.2", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^7.0.0" - }, - "engines": { - "node": ">= 10.14.2" - }, - "optionalDependencies": { - "node-notifier": "^8.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", - "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", - "dependencies": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "dependencies": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", - "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", - "dependencies": { - "@jest/test-result": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.6.2", - "jest-runner": "^26.6.3", - "jest-runtime": "^26.6.3" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/@jest/transform": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", - "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", - "dependencies": { - "@babel/core": "^7.1.0", - "@jest/types": "^26.6.2", - "babel-plugin-istanbul": "^6.0.0", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-util": "^26.6.2", - "micromatch": "^4.0.2", - "pirates": "^4.0.1", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.4.tgz", - "integrity": "sha512-cz8HFjOFfUBtvN+NXYSFMHYRdxZMaEl0XypVrhzxBgadKIXhIkRd8aMeHhmF56Sl7SuS8OnUpQ73/k9LE4VnLg==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.10.tgz", - "integrity": "sha512-Ht8wIW5v165atIX1p+JvKR5ONzUyF4Ac8DZIQ5kZs9zrb6M8SJNXpx1zn04rn65VjBMygRoMXcyYwNK0fT7bEg==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", - "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/@types/arcgis-rest-api": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/@types/arcgis-rest-api/-/arcgis-rest-api-10.4.5.tgz", - "integrity": "sha512-MhpTj3aaURIFhK6pBRF50yCHW5TpbeuC1E81juovfNesSOshsQnCzludhpBhIr2pNzplTBlfzrT7RKiLZ5F3Tw==" - }, - "node_modules/@types/babel__core": { - "version": "7.1.18", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz", - "integrity": "sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ==", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", - "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", - "dependencies": { - "@babel/types": "^7.3.0" - } - }, - "node_modules/@types/bytes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-lOGyCnw+2JVPKU3wIV0srU0NyALwTBJlVSx5DfMQOFuuohA8y9S8orImpuIQikZ0uIQ8gehrRjxgQC1rLRi11w==", - "dev": true - }, - "node_modules/@types/geojson": { - "version": "7946.0.8", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz", - "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==" - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/mapbox-gl": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-2.6.1.tgz", - "integrity": "sha512-ErM4hS1cAH9eJZ+b0bgrF9JuSN1P5i9jhfiJTZ6uOb2Bib7a8tkUivLz/fA69h51JN2CE3G5RE8EZIslgBzIIA==", - "dependencies": { - "@types/geojson": "*" - } - }, - "node_modules/@types/node": { - "version": "17.0.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.17.tgz", - "integrity": "sha512-e8PUNQy1HgJGV3iU/Bp2+D/DXh3PYeyli8LgIwsQcs1Ar1LoaWHSIT6Rw+H2rNJmiq6SNWiDytfx8+gYj7wDHw==" - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" - }, - "node_modules/@types/prettier": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz", - "integrity": "sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA==" - }, - "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" - }, - "node_modules/@types/uuid": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", - "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "15.0.14", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", - "integrity": "sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "20.2.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", - "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" - }, - "node_modules/abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==" - }, - "node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, - "node_modules/acorn-globals/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/babel-jest": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", - "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", - "dependencies": { - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/babel__core": "^7.1.7", - "babel-plugin-istanbul": "^6.0.0", - "babel-preset-jest": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", - "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", - "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", - "dependencies": { - "babel-plugin-jest-hoist": "^26.6.2", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": ">= 10.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dependencies": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" - }, - "node_modules/browserslist": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", - "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", - "dependencies": { - "caniuse-lite": "^1.0.30001286", - "electron-to-chromium": "^1.4.17", - "escalade": "^3.1.1", - "node-releases": "^2.0.1", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dependencies": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001311", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001311.tgz", - "integrity": "sha512-mleTFtFKfykEeW34EyfhGIFjGCqzhh38Y0LhdQ9aWF+HorZTtdgKV/1hEE0NlFkG2ubvisPV6l400tlbPys98A==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } - }, - "node_modules/capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dependencies": { - "rsvp": "^4.8.4" - }, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - }, - "node_modules/cjs-module-lexer": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", - "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==" - }, - "node_modules/class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dependencies": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" - }, - "node_modules/collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dependencies": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dependencies": { - "safe-buffer": "~5.1.1" - } - }, - "node_modules/copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" - }, - "node_modules/data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dependencies": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" - }, - "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" - }, - "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dependencies": { - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/domexception/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.68", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.68.tgz", - "integrity": "sha512-cId+QwWrV8R1UawO6b9BR1hnkJ4EJPCPAr4h315vliHUtVUJDk39Sg1PMNnaWKfj5x+93ssjeJ9LKL6r8LaMiA==" - }, - "node_modules/emittery": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", - "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/esbuild": { - "version": "0.9.7", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.9.7.tgz", - "integrity": "sha512-VtUf6aQ89VTmMLKrWHYG50uByMF4JQlVysb8dmg6cOgW8JnFCipmz7p+HNBl+RR3LLCuBxFGVauAe2wfnF9bLg==", - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/exec-sh": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", - "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==" - }, - "node_modules/execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/expand-brackets/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" - }, - "node_modules/fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dependencies": { - "map-cache": "^0.2.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/geojson": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/geojson/-/geojson-0.5.0.tgz", - "integrity": "sha1-PNbJY5m+ZbVu5VWWEW/pGRznAcA=", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" - }, - "node_modules/growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "optional": true - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" - }, - "node_modules/html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dependencies": { - "whatwg-encoding": "^1.0.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" - }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "engines": { - "node": ">=8.12.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" - }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dependencies": { - "ci-info": "^2.0.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "optional": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "optional": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dependencies": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", - "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", - "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", - "dependencies": { - "@jest/core": "^26.6.3", - "import-local": "^3.0.2", - "jest-cli": "^26.6.3" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-changed-files": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", - "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", - "dependencies": { - "@jest/types": "^26.6.2", - "execa": "^4.0.0", - "throat": "^5.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-cli": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", - "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", - "dependencies": { - "@jest/core": "^26.6.3", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "import-local": "^3.0.2", - "is-ci": "^2.0.0", - "jest-config": "^26.6.3", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "prompts": "^2.0.1", - "yargs": "^15.4.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-config": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", - "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", - "dependencies": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^26.6.3", - "@jest/types": "^26.6.2", - "babel-jest": "^26.6.3", - "chalk": "^4.0.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.4", - "jest-environment-jsdom": "^26.6.2", - "jest-environment-node": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-jasmine2": "^26.6.3", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.6.2", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - }, - "peerDependencies": { - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-docblock": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", - "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-each": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", - "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", - "dependencies": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-util": "^26.6.2", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-environment-jsdom": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", - "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", - "dependencies": { - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2", - "jsdom": "^16.4.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-environment-node": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", - "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", - "dependencies": { - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-haste-map": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", - "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", - "dependencies": { - "@jest/types": "^26.6.2", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-regex-util": "^26.0.0", - "jest-serializer": "^26.6.2", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "micromatch": "^4.0.2", - "sane": "^4.0.3", - "walker": "^1.0.7" - }, - "engines": { - "node": ">= 10.14.2" - }, - "optionalDependencies": { - "fsevents": "^2.1.2" - } - }, - "node_modules/jest-jasmine2": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", - "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", - "dependencies": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^26.6.2", - "@jest/source-map": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^26.6.2", - "is-generator-fn": "^2.0.0", - "jest-each": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-runtime": "^26.6.3", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "pretty-format": "^26.6.2", - "throat": "^5.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", - "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", - "dependencies": { - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", - "dependencies": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", - "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", - "dependencies": { - "@jest/types": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-snapshot": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runner": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", - "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", - "dependencies": { - "@jest/console": "^26.6.2", - "@jest/environment": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.7.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-config": "^26.6.3", - "jest-docblock": "^26.0.0", - "jest-haste-map": "^26.6.2", - "jest-leak-detector": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-resolve": "^26.6.2", - "jest-runtime": "^26.6.3", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "source-map-support": "^0.5.6", - "throat": "^5.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", - "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", - "dependencies": { - "@jest/console": "^26.6.2", - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/globals": "^26.6.2", - "@jest/source-map": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0", - "cjs-module-lexer": "^0.6.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.4", - "jest-config": "^26.6.3", - "jest-haste-map": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.6.2", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "slash": "^3.0.0", - "strip-bom": "^4.0.0", - "yargs": "^15.4.1" - }, - "bin": { - "jest-runtime": "bin/jest-runtime.js" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-serializer": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", - "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", - "dependencies": { - "@types/node": "*", - "graceful-fs": "^4.2.4" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-snapshot": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", - "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", - "dependencies": { - "@babel/types": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.0.0", - "chalk": "^4.0.0", - "expect": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-haste-map": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-resolve": "^26.6.2", - "natural-compare": "^1.4.0", - "pretty-format": "^26.6.2", - "semver": "^7.3.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-validate": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", - "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", - "dependencies": { - "@jest/types": "^26.6.2", - "camelcase": "^6.0.0", - "chalk": "^4.0.0", - "jest-get-type": "^26.3.0", - "leven": "^3.1.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", - "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", - "dependencies": { - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^26.6.2", - "string-length": "^4.0.1" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dependencies": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "node_modules/json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dependencies": { - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "dependencies": { - "mime-db": "1.51.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=" - }, - "node_modules/node-notifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz", - "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==", - "optional": true, - "dependencies": { - "growly": "^1.3.0", - "is-wsl": "^2.2.0", - "semver": "^7.3.2", - "shellwords": "^0.1.1", - "uuid": "^8.3.0", - "which": "^2.0.2" - } - }, - "node_modules/node-notifier/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "optional": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==" - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nwsapi": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==" - }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dependencies": { - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-each-series": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", - "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" - }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - }, - "node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" - }, - "node_modules/repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, - "node_modules/resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dependencies": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "deprecated": "https://github.com/lydell/resolve-url#deprecated" - }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "engines": { - "node": ">=0.12" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rsvp": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", - "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", - "engines": { - "node": "6.* || >= 7.*" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dependencies": { - "ret": "~0.1.10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "deprecated": "some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added", - "dependencies": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - }, - "bin": { - "sane": "src/cli.js" - }, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/sane/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "node_modules/sane/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/sane/node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/sane/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/sane/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/sane/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "engines": { - "node": ">=4" - } - }, - "node_modules/sane/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/sane/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "node_modules/set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dependencies": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/set-value/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/set-value/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "optional": true - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dependencies": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dependencies": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dependencies": { - "kind-of": "^3.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-util/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/snapdragon/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/snapdragon/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "deprecated": "See https://github.com/lydell/source-map-url#deprecated" - }, - "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==" - }, - "node_modules/split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dependencies": { - "extend-shallow": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "node_modules/stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dependencies": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" - }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/throat": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==" - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "engines": { - "node": ">=4" - } - }, - "node_modules/to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-object-path/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dependencies": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tough-cookie": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.1.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/typescript": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", - "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dependencies": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/union-value/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dependencies": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dependencies": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dependencies": { - "isarray": "1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "deprecated": "Please see https://github.com/lydell/urix#deprecated" - }, - "node_modules/use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "optional": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-to-istanbul": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz", - "integrity": "sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/v8-to-istanbul/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dependencies": { - "browser-process-hrtime": "^1.0.0" - } - }, - "node_modules/w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dependencies": { - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "engines": { - "node": ">=10.4" - } - }, - "node_modules/whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dependencies": { - "iconv-lite": "0.4.24" - } - }, - "node_modules/whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" - }, - "node_modules/whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dependencies": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" - }, - "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - } - }, - "dependencies": { - "@ampproject/remapping": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.0.tgz", - "integrity": "sha512-d5RysTlJ7hmw5Tw4UxgxcY3lkMe92n8sXCcuLPAyIAHK6j8DefDwtGnVVDgOnv+RnEosulDJ9NPKQL27bDId0g==", - "requires": { - "@jridgewell/trace-mapping": "^0.3.0" - } - }, - "@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", - "requires": { - "@babel/highlight": "^7.16.7" - } - }, - "@babel/compat-data": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.0.tgz", - "integrity": "sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng==" - }, - "@babel/core": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.2.tgz", - "integrity": "sha512-R3VH5G42VSDolRHyUO4V2cfag8WHcZyxdq5Z/m8Xyb92lW/Erm/6kM+XtRFGf3Mulre3mveni2NHfEUws8wSvw==", - "requires": { - "@ampproject/remapping": "^2.0.0", - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.0", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.17.2", - "@babel/parser": "^7.17.0", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.0", - "@babel/types": "^7.17.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.1.2", - "semver": "^6.3.0" - } - }, - "@babel/generator": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.0.tgz", - "integrity": "sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw==", - "requires": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, - "@babel/helper-compilation-targets": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", - "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", - "requires": { - "@babel/compat-data": "^7.16.4", - "@babel/helper-validator-option": "^7.16.7", - "browserslist": "^4.17.5", - "semver": "^6.3.0" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", - "requires": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-module-imports": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", - "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-module-transforms": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", - "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", - "requires": { - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/helper-validator-identifier": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", - "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==" - }, - "@babel/helper-simple-access": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", - "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", - "requires": { - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==" - }, - "@babel/helper-validator-option": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", - "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==" - }, - "@babel/helpers": { - "version": "7.17.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.2.tgz", - "integrity": "sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==", - "requires": { - "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.0", - "@babel/types": "^7.17.0" - } - }, - "@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.0.tgz", - "integrity": "sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw==" - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/traverse": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.0.tgz", - "integrity": "sha512-fpFIXvqD6kC7c7PUNnZ0Z8cQXlarCLtCUpt2S1Dx7PjoRtCFffvOkHHSom+m5HIxMZn5bIBVb71lhabcmjEsqg==", - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.0", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.0", - "@babel/types": "^7.17.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", - "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" - }, - "@cnakazawa/watch": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", - "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - } - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==" - }, - "@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", - "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", - "requires": { - "@jest/console": "^26.6.2", - "@jest/reporters": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-changed-files": "^26.6.2", - "jest-config": "^26.6.3", - "jest-haste-map": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.6.2", - "jest-resolve-dependencies": "^26.6.3", - "jest-runner": "^26.6.3", - "jest-runtime": "^26.6.3", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "jest-watcher": "^26.6.2", - "micromatch": "^4.0.2", - "p-each-series": "^2.1.0", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "requires": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" - } - }, - "@jest/fake-timers": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", - "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", - "requires": { - "@jest/types": "^26.6.2", - "@sinonjs/fake-timers": "^6.0.1", - "@types/node": "*", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - } - }, - "@jest/globals": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", - "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", - "requires": { - "@jest/environment": "^26.6.2", - "@jest/types": "^26.6.2", - "expect": "^26.6.2" - } - }, - "@jest/reporters": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", - "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.4", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.3", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "jest-haste-map": "^26.6.2", - "jest-resolve": "^26.6.2", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "node-notifier": "^8.0.0", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^7.0.0" - } - }, - "@jest/source-map": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", - "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" - } - }, - "@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "requires": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", - "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", - "requires": { - "@jest/test-result": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.6.2", - "jest-runner": "^26.6.3", - "jest-runtime": "^26.6.3" - } - }, - "@jest/transform": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", - "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^26.6.2", - "babel-plugin-istanbul": "^6.0.0", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-util": "^26.6.2", - "micromatch": "^4.0.2", - "pirates": "^4.0.1", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - } - }, - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.4.tgz", - "integrity": "sha512-cz8HFjOFfUBtvN+NXYSFMHYRdxZMaEl0XypVrhzxBgadKIXhIkRd8aMeHhmF56Sl7SuS8OnUpQ73/k9LE4VnLg==" - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.10.tgz", - "integrity": "sha512-Ht8wIW5v165atIX1p+JvKR5ONzUyF4Ac8DZIQ5kZs9zrb6M8SJNXpx1zn04rn65VjBMygRoMXcyYwNK0fT7bEg==" - }, - "@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", - "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" - }, - "@types/arcgis-rest-api": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/@types/arcgis-rest-api/-/arcgis-rest-api-10.4.5.tgz", - "integrity": "sha512-MhpTj3aaURIFhK6pBRF50yCHW5TpbeuC1E81juovfNesSOshsQnCzludhpBhIr2pNzplTBlfzrT7RKiLZ5F3Tw==" - }, - "@types/babel__core": { - "version": "7.1.18", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz", - "integrity": "sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ==", - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz", - "integrity": "sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==", - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/bytes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-lOGyCnw+2JVPKU3wIV0srU0NyALwTBJlVSx5DfMQOFuuohA8y9S8orImpuIQikZ0uIQ8gehrRjxgQC1rLRi11w==", - "dev": true - }, - "@types/geojson": { - "version": "7946.0.8", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz", - "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==" - }, - "@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/mapbox-gl": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-2.6.1.tgz", - "integrity": "sha512-ErM4hS1cAH9eJZ+b0bgrF9JuSN1P5i9jhfiJTZ6uOb2Bib7a8tkUivLz/fA69h51JN2CE3G5RE8EZIslgBzIIA==", - "requires": { - "@types/geojson": "*" - } - }, - "@types/node": { - "version": "17.0.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.17.tgz", - "integrity": "sha512-e8PUNQy1HgJGV3iU/Bp2+D/DXh3PYeyli8LgIwsQcs1Ar1LoaWHSIT6Rw+H2rNJmiq6SNWiDytfx8+gYj7wDHw==" - }, - "@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" - }, - "@types/prettier": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz", - "integrity": "sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA==" - }, - "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" - }, - "@types/uuid": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", - "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", - "dev": true - }, - "@types/yargs": { - "version": "15.0.14", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", - "integrity": "sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "20.2.1", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz", - "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==" - }, - "abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==" - }, - "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==" - }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - }, - "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" - } - } - }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "requires": { - "debug": "4" - } - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" - }, - "babel-jest": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", - "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", - "requires": { - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/babel__core": "^7.1.7", - "babel-plugin-istanbul": "^6.0.0", - "babel-preset-jest": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "slash": "^3.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "dependencies": { - "istanbul-lib-instrument": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz", - "integrity": "sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q==", - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - } - } - } - }, - "babel-plugin-jest-hoist": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", - "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", - "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", - "requires": { - "babel-plugin-jest-hoist": "^26.6.2", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - } - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" - }, - "browserslist": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", - "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", - "requires": { - "caniuse-lite": "^1.0.30001286", - "electron-to-chromium": "^1.4.17", - "escalade": "^3.1.1", - "node-releases": "^2.0.1", - "picocolors": "^1.0.0" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, - "caniuse-lite": { - "version": "1.0.30001311", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001311.tgz", - "integrity": "sha512-mleTFtFKfykEeW34EyfhGIFjGCqzhh38Y0LhdQ9aWF+HorZTtdgKV/1hEE0NlFkG2ubvisPV6l400tlbPys98A==" - }, - "capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "requires": { - "rsvp": "^4.8.4" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==" - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - }, - "cjs-module-lexer": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", - "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==" - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" - }, - "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" - }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" - } - } - }, - "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - } - }, - "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "decimal.js": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", - "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==" - }, - "diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==" - }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==" - } - } - }, - "electron-to-chromium": { - "version": "1.4.68", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.68.tgz", - "integrity": "sha512-cId+QwWrV8R1UawO6b9BR1hnkJ4EJPCPAr4h315vliHUtVUJDk39Sg1PMNnaWKfj5x+93ssjeJ9LKL6r8LaMiA==" - }, - "emittery": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", - "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "esbuild": { - "version": "0.9.7", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.9.7.tgz", - "integrity": "sha512-VtUf6aQ89VTmMLKrWHYG50uByMF4JQlVysb8dmg6cOgW8JnFCipmz7p+HNBl+RR3LLCuBxFGVauAe2wfnF9bLg==" - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" - }, - "escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" - }, - "exec-sh": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", - "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==" - }, - "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "requires": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" - }, - "fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", - "requires": { - "bser": "2.1.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" - }, - "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "requires": { - "map-cache": "^0.2.2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" - }, - "geojson": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/geojson/-/geojson-0.5.0.tgz", - "integrity": "sha1-PNbJY5m+ZbVu5VWWEW/pGRznAcA=" - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==" - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "requires": { - "pump": "^3.0.0" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" - }, - "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" - }, - "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" - }, - "growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "optional": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" - }, - "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "requires": { - "whatwg-encoding": "^1.0.5" - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==" - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "requires": { - "has": "^1.0.3" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "optional": true - }, - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==" - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "requires": { - "isobject": "^3.0.1" - } - }, - "is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "optional": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==" - }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", - "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jest": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", - "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", - "requires": { - "@jest/core": "^26.6.3", - "import-local": "^3.0.2", - "jest-cli": "^26.6.3" - } - }, - "jest-changed-files": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", - "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", - "requires": { - "@jest/types": "^26.6.2", - "execa": "^4.0.0", - "throat": "^5.0.0" - } - }, - "jest-cli": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", - "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", - "requires": { - "@jest/core": "^26.6.3", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "import-local": "^3.0.2", - "is-ci": "^2.0.0", - "jest-config": "^26.6.3", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "prompts": "^2.0.1", - "yargs": "^15.4.1" - } - }, - "jest-config": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", - "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^26.6.3", - "@jest/types": "^26.6.2", - "babel-jest": "^26.6.3", - "chalk": "^4.0.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.4", - "jest-environment-jsdom": "^26.6.2", - "jest-environment-node": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-jasmine2": "^26.6.3", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.6.2", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2" - } - }, - "jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-docblock": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", - "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", - "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", - "requires": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-util": "^26.6.2", - "pretty-format": "^26.6.2" - } - }, - "jest-environment-jsdom": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", - "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", - "requires": { - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2", - "jsdom": "^16.4.0" - } - }, - "jest-environment-node": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", - "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", - "requires": { - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==" - }, - "jest-haste-map": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", - "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", - "requires": { - "@jest/types": "^26.6.2", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.1.2", - "graceful-fs": "^4.2.4", - "jest-regex-util": "^26.0.0", - "jest-serializer": "^26.6.2", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "micromatch": "^4.0.2", - "sane": "^4.0.3", - "walker": "^1.0.7" - } - }, - "jest-jasmine2": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", - "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^26.6.2", - "@jest/source-map": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^26.6.2", - "is-generator-fn": "^2.0.0", - "jest-each": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-runtime": "^26.6.3", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "pretty-format": "^26.6.2", - "throat": "^5.0.0" - } - }, - "jest-leak-detector": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", - "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", - "requires": { - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*" - } - }, - "jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "requires": {} - }, - "jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==" - }, - "jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", - "requires": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", - "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", - "requires": { - "@jest/types": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-snapshot": "^26.6.2" - } - }, - "jest-runner": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", - "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", - "requires": { - "@jest/console": "^26.6.2", - "@jest/environment": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.7.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-config": "^26.6.3", - "jest-docblock": "^26.0.0", - "jest-haste-map": "^26.6.2", - "jest-leak-detector": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-resolve": "^26.6.2", - "jest-runtime": "^26.6.3", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "source-map-support": "^0.5.6", - "throat": "^5.0.0" - } - }, - "jest-runtime": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", - "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", - "requires": { - "@jest/console": "^26.6.2", - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/globals": "^26.6.2", - "@jest/source-map": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0", - "cjs-module-lexer": "^0.6.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.4", - "jest-config": "^26.6.3", - "jest-haste-map": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.6.2", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "slash": "^3.0.0", - "strip-bom": "^4.0.0", - "yargs": "^15.4.1" - } - }, - "jest-serializer": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", - "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", - "requires": { - "@types/node": "*", - "graceful-fs": "^4.2.4" - } - }, - "jest-snapshot": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", - "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.0.0", - "chalk": "^4.0.0", - "expect": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-haste-map": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-resolve": "^26.6.2", - "natural-compare": "^1.4.0", - "pretty-format": "^26.6.2", - "semver": "^7.3.2" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - }, - "jest-validate": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", - "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", - "requires": { - "@jest/types": "^26.6.2", - "camelcase": "^6.0.0", - "chalk": "^4.0.0", - "jest-get-type": "^26.3.0", - "leven": "^3.1.0", - "pretty-format": "^26.6.2" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" - } - } - }, - "jest-watcher": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", - "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", - "requires": { - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^26.6.2", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "requires": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "requires": { - "minimist": "^1.2.5" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "requires": { - "semver": "^6.0.0" - } - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "requires": { - "tmpl": "1.0.5" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "requires": { - "object-visit": "^1.0.0" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" - }, - "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", - "requires": { - "mime-db": "1.51.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=" - }, - "node-notifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz", - "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==", - "optional": true, - "requires": { - "growly": "^1.3.0", - "is-wsl": "^2.2.0", - "semver": "^7.3.2", - "shellwords": "^0.1.1", - "uuid": "^8.3.0", - "which": "^2.0.2" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "optional": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==" - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "requires": { - "path-key": "^3.0.0" - } - }, - "nwsapi": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "requires": { - "isobject": "^3.0.0" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "requires": { - "isobject": "^3.0.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "p-each-series": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", - "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==" - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" - }, - "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==" - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "requires": { - "find-up": "^4.0.0" - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==" - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "dependencies": { - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" - } - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" - }, - "repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==" - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, - "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "requires": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "requires": { - "resolve-from": "^5.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "rsvp": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", - "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==" - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "requires": { - "path-key": "^2.0.0" - } - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "requires": { - "xmlchars": "^2.2.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - } - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "optional": true - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==" - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==" - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", - "requires": { - "escape-string-regexp": "^2.0.0" - } - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" - }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" - }, - "terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "requires": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "throat": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==" - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { - "is-number": "^7.0.0" - } - }, - "tough-cookie": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.1.2" - } - }, - "tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "requires": { - "punycode": "^2.1.1" - } - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" - }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typescript": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", - "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==" - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - } - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" - } - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "optional": true - }, - "v8-to-istanbul": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz", - "integrity": "sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" - } - } - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "requires": { - "browser-process-hrtime": "^1.0.0" - } - }, - "w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "requires": { - "xml-name-validator": "^3.0.0" - } - }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "requires": { - "makeerror": "1.0.12" - } - }, - "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" - }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "requires": { - "iconv-lite": "0.4.24" - } - }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" - }, - "whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "requires": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", - "requires": {} - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" - }, - "xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } -} diff --git a/packages/mapbox-gl-esri-feature-layers/package.json b/packages/mapbox-gl-esri-feature-layers/package.json deleted file mode 100644 index 67dceb2c8..000000000 --- a/packages/mapbox-gl-esri-feature-layers/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "mapbox-gl-esri-feature-layers", - "version": "1.0.0", - "private": true, - "description": "Fetch data from Esri feature layers and convert styles to mapbox-gl-style for use in mapbox-gl-js", - "keywords": [ - "mapbox-gl-js", - "esri", - "seasketch" - ], - "author": "Chad Burt ", - "homepage": "https://github.com/seasketch/next/tree/master/packages/mapbox-gl-esri-feature-layers", - "license": "BSD-3-Clause", - "main": "dist/index.js", - "directories": { - "lib": "lib", - "test": "__tests__" - }, - "files": [ - "lib" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/seasketch/next.git" - }, - "scripts": { - "test": "echo \"Error: run tests from root\" && exit 1", - "build": "tsc" - }, - "bugs": { - "url": "https://github.com/seasketch/next/issues" - }, - "dependencies": { - "@types/arcgis-rest-api": "^10.4.4", - "@types/geojson": "^7946.0.7", - "@types/mapbox-gl": "^2.1.1", - "bytes": "^3.1.0", - "esbuild": "^0.9.6", - "geojson": "^0.5.0", - "jest": "^26.6.3", - "typescript": "^4.2.3" - }, - "devDependencies": { - "@types/bytes": "^3.1.0", - "@types/uuid": "^8.3.0" - } -} diff --git a/packages/mapbox-gl-esri-feature-layers/tsconfig.json b/packages/mapbox-gl-esri-feature-layers/tsconfig.json deleted file mode 100644 index 861b9b21d..000000000 --- a/packages/mapbox-gl-esri-feature-layers/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "target": "es2018" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, - "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, - "outDir": "./dist", - "strict": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true - } -} diff --git a/packages/mapbox-gl-esri-sources/.gitignore b/packages/mapbox-gl-esri-sources/.gitignore new file mode 100644 index 000000000..d3f9e9e33 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +node_modules +package-lock.json \ No newline at end of file diff --git a/packages/mapbox-gl-esri-sources/.nojekyll b/packages/mapbox-gl-esri-sources/.nojekyll new file mode 100644 index 000000000..e69de29bb diff --git a/packages/mapbox-gl-esri-sources/.npmignore b/packages/mapbox-gl-esri-sources/.npmignore new file mode 100644 index 000000000..f6fc57035 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/.npmignore @@ -0,0 +1,6 @@ +src/ +examples/ +docs/ +.vscode/ +index.ts +tsconfig.json \ No newline at end of file diff --git a/packages/mapbox-gl-esri-sources/.nvmrc b/packages/mapbox-gl-esri-sources/.nvmrc new file mode 100644 index 000000000..2b9e31b9e --- /dev/null +++ b/packages/mapbox-gl-esri-sources/.nvmrc @@ -0,0 +1 @@ +v14.7.0 diff --git a/packages/mapbox-gl-esri-sources/.vscode/settings.json b/packages/mapbox-gl-esri-sources/.vscode/settings.json new file mode 100644 index 000000000..f8f52db2b --- /dev/null +++ b/packages/mapbox-gl-esri-sources/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.exclude": { + "dist/**/*": true + }, + "editor.formatOnSave": true +} diff --git a/packages/mapbox-gl-esri-sources/LICENSE b/packages/mapbox-gl-esri-sources/LICENSE new file mode 100644 index 000000000..d5d38ea9c --- /dev/null +++ b/packages/mapbox-gl-esri-sources/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2020, Regents of the University of California +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/packages/mapbox-gl-esri-sources/README.md b/packages/mapbox-gl-esri-sources/README.md new file mode 100644 index 000000000..de7f0d6c4 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/README.md @@ -0,0 +1,139 @@ +Easily add Esri map services or vector layers to MapBox GL JS, maintaining a faithful reproduction of cartographic style. In many cases services can render with greater graphical fidelity and performance than in the Esri JS API or ArcGIS Online. + +vector layers demonstration in mapbox + +## Examples + +- [Dynamic MapService](https://seasketch.github.io/mapbox-gl-esri-sources/examples/arcgisDynamic.html) +- [Vector Layers](https://seasketch.github.io/mapbox-gl-esri-sources/examples/arcgisVectorLayers.html) + +## ArcGIS Tiled Map Services + +Tiled services host pre-seeded sets of image tiles referenced using z/y/z urls similar to WMTS or TMS layers. You actually don't need this module to load tiled services, but since you are here I'll show you how to do it! + +[Anacapa Island Tiled Service Example](https://seasketch.github.io/mapbox-gl-esri-sources/examples/arcgisTiled.html) + +```javascript +map.on("load", () => { + // add the image source + map.addSource("tile-layer", { + type: "raster", + tiles: [ + // Be sure to reference the /tile/ endpoint with appropriate template vars + "https://tiles.arcgis.com/tiles/4TXrdeWh0RyCqPgB/arcgis/rest/services/Anacapa_Island/MapServer/tile/{z}/{y}/{x}", + ], + // Most services have tile sizes of 256. You may use this division trick to + // support higher resolution screens, though watch out for missing tile + // images at higher zoom levels. + tileSize: 256 / window.devicePixelRatio, + // min/max zoom can be based on service metadata + minZoom: 0, + maxZoom: 23, + // MapService metadata also has a "Full Extent", which you can convert to + // degrees and avoid 404 errors from out of bounds requests + bounds: [ + -119.45627340923632, + 33.9923034787097, + -119.33632759039419, + 34.028212713477615, + ], + // More options are detailed in: + // https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/#raster-tiles + }); + // now add a layer that references this source + map.addLayer({ + id: "esri-tiles", + source: "tile-layer", + type: "raster", + // see https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#raster + // you can control opacity, saturation, and other rendering aspects + }); +}); +``` + +## ArcGIS Dynamic Map Services + +Dynamic Map Services are more difficult to support without this module. These services work similar to older WMS where it's expected that images representing the entire viewport are requested. `mapbox-gl-esri-sources` provides an [ArcGISDynamicService](https://seasketch.github.io/mapbox-gl-esri-sources/docs/classes/arcgisdynamicmapservice.html) class which will listen to map viewport change events and update this image. It also provides [methods](https://seasketch.github.io/mapbox-gl-esri-sources/docs/classes/arcgisdynamicmapservice.html#updatelayers) to update sublayer visibility, order and opacity. + +By default images will be requested in a resolution that matches the client's [devicePixelRatio](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) for higher quality maps on high-dpi devices. + +```javascript +import { ArcGISDynamicMapService } from "mapbox-gl-esri-sources"; + + // ... setup your map + map.on("load", () => { + const populatedPlaces = new ArcGISDynamicMapService( + map, + "populated-places-source", + "https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer", { + supportsDynamicLayers: true, + sublayers: [ + { sublayer: 0, opacity: 1 }, + { sublayer: 1, opacity: 1 }, + { sublayer: 2, opacity: 0.5 }, + ], + queryParameters: { + format: 'png32' + } + } + }); + // Don't forget to add a layer to reference your source + map.addLayer({ + id: "ags-layer", + type: "raster", + source: populatedPlaces.id, + paint: { + // fading looks weird on non-tiled images + "raster-fade-duration": 0, + }, + }); + // turn off the third sublayer and update opacity + populatedPlaces.updateLayers([ + { sublayer: 0, opacity: 0.5 }, + { sublayer: 1, opacity: 1 }, + ]); +}); +``` + +[API Documentation](https://seasketch.github.io/mapbox-gl-esri-sources/docs/classes/arcgisdynamicmapservice.html) + +[Dynamic Map Service Examples](https://seasketch.github.io/mapbox-gl-esri-sources/examples/arcgisDynamic.html) + +## ArcGIS Feature Layers + +Both Esri map and feature services typically support querying vector data through a "feature layer" endpoint and most often this is the best way to display data from ArcGIS in MapBox GL. This module provides both an [ArcGISVectorSource](https://seasketch.github.io/mapbox-gl-esri-sources/docs/classes/arcgisvectorsource.html) class for loading the entire dataset as a GeoJSON source, and the [styleForFeatureLayer](https://seasketch.github.io/mapbox-gl-esri-sources/docs/globals.html#styleforfeaturelayer) function which will faithfully translate renderer information from the ArcGIS REST API into GL Style. + +```javascript +import { + ArcGISVectorSource, + styleForFeatureLayer, +} from "mapbox-gl-esri-sources"; + +// setup map... + +map.on("load", () => { + + const { imageList, layers } = styleForFeatureLayer( + "https://sampleserver6.arcgisonline.com/arcgis/rest/services/SampleWorldCities/MapServer/0", + "cities-source-id" + ); + + const esriSource = new ArcGISVectorSource( + map, + 'cities-source-id', + "https://sampleserver6.arcgisonline.com/arcgis/rest/services/SampleWorldCities/MapServer/0"), + ); + + imageList.addToMap(map); + + for (const layer of layers) { + map.addLayer(layer); + } +}); +``` + +The process of generating images and styles is seperate from loading the vector source to provide more flexibility. Rather than just dynamically loading style and data together, you might generate and cache style information so that it doesn't have to be created on each map load. You could also adjust layers as needed, or even generate vector tiles from complex services using tippecanoe and use the same layers with this new derivative source. + +[Vector Layer Examples](https://seasketch.github.io/mapbox-gl-esri-sources/examples/arcgisVectorLayers.html) + +[ArcGISVectorSource API](https://seasketch.github.io/mapbox-gl-esri-sources/docs/classes/arcgisvectorsource.html) | [styleForFeatureLayer API](https://seasketch.github.io/mapbox-gl-esri-sources/docs/globals.html#styleforfeaturelayer) diff --git a/packages/mapbox-gl-esri-sources/docs/assets/css/main.css b/packages/mapbox-gl-esri-sources/docs/assets/css/main.css new file mode 100644 index 000000000..b3cdb66dd --- /dev/null +++ b/packages/mapbox-gl-esri-sources/docs/assets/css/main.css @@ -0,0 +1,2321 @@ +/*! normalize.css v1.1.3 | MIT License | git.io/normalize */ +/* ========================================================================== + * HTML5 display definitions + * ========================================================================== */ +/** + * Correct `block` display not defined in IE 6/7/8/9 and Firefox 3. */ +article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { + display: block; } + +/** + * Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. */ +audio, canvas, video { + display: inline-block; + *display: inline; + *zoom: 1; } + +/** + * Prevent modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. */ +audio:not([controls]) { + display: none; + height: 0; } + +/** + * Address styling not present in IE 7/8/9, Firefox 3, and Safari 4. + * Known issue: no IE 6 support. */ +[hidden] { + display: none; } + +/* ========================================================================== + * Base + * ========================================================================== */ +/** + * 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using + * `em` units. + * 2. Prevent iOS text size adjust after orientation change, without disabling + * user zoom. */ +html { + font-size: 100%; + /* 1 */ + -ms-text-size-adjust: 100%; + /* 2 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + font-family: sans-serif; } + +/** + * Address `font-family` inconsistency between `textarea` and other form + * elements. */ +button, input, select, textarea { + font-family: sans-serif; } + +/** + * Address margins handled incorrectly in IE 6/7. */ +body { + margin: 0; } + +/* ========================================================================== + * Links + * ========================================================================== */ +/** + * Address `outline` inconsistency between Chrome and other browsers. */ +a:focus { + outline: thin dotted; } + +a:active, a:hover { + outline: 0; } + +/** + * Improve readability when focused and also mouse hovered in all browsers. */ +/* ========================================================================== + * Typography + * ========================================================================== */ +/** + * Address font sizes and margins set differently in IE 6/7. + * Address font sizes within `section` and `article` in Firefox 4+, Safari 5, + * and Chrome. */ +h1 { + font-size: 2em; + margin: 0.67em 0; } + +h2 { + font-size: 1.5em; + margin: 0.83em 0; } + +h3 { + font-size: 1.17em; + margin: 1em 0; } + +h4, .tsd-index-panel h3 { + font-size: 1em; + margin: 1.33em 0; } + +h5 { + font-size: 0.83em; + margin: 1.67em 0; } + +h6 { + font-size: 0.67em; + margin: 2.33em 0; } + +/** + * Address styling not present in IE 7/8/9, Safari 5, and Chrome. */ +abbr[title] { + border-bottom: 1px dotted; } + +/** + * Address style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome. */ +b, strong { + font-weight: bold; } + +blockquote { + margin: 1em 40px; } + +/** + * Address styling not present in Safari 5 and Chrome. */ +dfn { + font-style: italic; } + +/** + * Address differences between Firefox and other browsers. + * Known issue: no IE 6/7 normalization. */ +hr { + box-sizing: content-box; + height: 0; } + +/** + * Address styling not present in IE 6/7/8/9. */ +mark { + background: #ff0; + color: #000; } + +/** + * Address margins set differently in IE 6/7. */ +p, pre { + margin: 1em 0; } + +/** + * Correct font family set oddly in IE 6, Safari 4/5, and Chrome. */ +code, kbd, pre, samp { + font-family: monospace, serif; + _font-family: 'courier new', monospace; + font-size: 1em; } + +/** + * Improve readability of pre-formatted text in all browsers. */ +pre { + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; } + +/** + * Address CSS quotes not supported in IE 6/7. */ +q { + quotes: none; } + q:before, q:after { + content: ''; + content: none; } + +/** + * Address `quotes` property not supported in Safari 4. */ +/** + * Address inconsistent and variable font size in all browsers. */ +small { + font-size: 80%; } + +/** + * Prevent `sub` and `sup` affecting `line-height` in all browsers. */ +sub { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; } + +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; + top: -0.5em; } + +sub { + bottom: -0.25em; } + +/* ========================================================================== + * Lists + * ========================================================================== */ +/** + * Address margins set differently in IE 6/7. */ +dl, menu, ol, ul { + margin: 1em 0; } + +dd { + margin: 0 0 0 40px; } + +/** + * Address paddings set differently in IE 6/7. */ +menu, ol, ul { + padding: 0 0 0 40px; } + +/** + * Correct list images handled incorrectly in IE 7. */ +nav ul, nav ol { + list-style: none; + list-style-image: none; } + +/* ========================================================================== + * Embedded content + * ========================================================================== */ +/** + * 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3. + * 2. Improve image quality when scaled in IE 7. */ +img { + border: 0; + /* 1 */ + -ms-interpolation-mode: bicubic; } + +/* 2 */ +/** + * Correct overflow displayed oddly in IE 9. */ +svg:not(:root) { + overflow: hidden; } + +/* ========================================================================== + * Figures + * ========================================================================== */ +/** + * Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11. */ +figure, form { + margin: 0; } + +/* ========================================================================== + * Forms + * ========================================================================== */ +/** + * Correct margin displayed oddly in IE 6/7. */ +/** + * Define consistent border, margin, and padding. */ +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; } + +/** + * 1. Correct color not being inherited in IE 6/7/8/9. + * 2. Correct text not wrapping in Firefox 3. + * 3. Correct alignment displayed oddly in IE 6/7. */ +legend { + border: 0; + /* 1 */ + padding: 0; + white-space: normal; + /* 2 */ + *margin-left: -7px; } + +/* 3 */ +/** + * 1. Correct font size not being inherited in all browsers. + * 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5, + * and Chrome. + * 3. Improve appearance and consistency in all browsers. */ +button, input, select, textarea { + font-size: 100%; + /* 1 */ + margin: 0; + /* 2 */ + vertical-align: baseline; + /* 3 */ + *vertical-align: middle; } + +/* 3 */ +/** + * Address Firefox 3+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. */ +button, input { + line-height: normal; } + +/** + * Address inconsistent `text-transform` inheritance for `button` and `select`. + * All other form control elements do not inherit `text-transform` values. + * Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+. + * Correct `select` style inheritance in Firefox 4+ and Opera. */ +button, select { + text-transform: none; } + +/** + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Correct inability to style clickable `input` types in iOS. + * 3. Improve usability and consistency of cursor style between image-type + * `input` and others. + * 4. Remove inner spacing in IE 7 without affecting normal text inputs. + * Known issue: inner spacing remains in IE 6. */ +button, html input[type="button"] { + -webkit-appearance: button; + /* 2 */ + cursor: pointer; + /* 3 */ + *overflow: visible; } + +/* 4 */ +input[type="reset"], input[type="submit"] { + -webkit-appearance: button; + /* 2 */ + cursor: pointer; + /* 3 */ + *overflow: visible; } + +/* 4 */ +/** + * Re-set default cursor for disabled elements. */ +button[disabled], html input[disabled] { + cursor: default; } + +/** + * 1. Address box sizing set to content-box in IE 8/9. + * 2. Remove excess padding in IE 8/9. + * 3. Remove excess padding in IE 7. + * Known issue: excess padding remains in IE 6. */ +input { + /* 3 */ } + input[type="checkbox"], input[type="radio"] { + box-sizing: border-box; + /* 1 */ + padding: 0; + /* 2 */ + *height: 13px; + /* 3 */ + *width: 13px; } + input[type="search"] { + -webkit-appearance: textfield; + /* 1 */ + /* 2 */ + box-sizing: content-box; } + input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; } + +/** + * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome + * (include `-moz` to future-proof). */ +/** + * Remove inner padding and search cancel button in Safari 5 and Chrome + * on OS X. */ +/** + * Remove inner padding and border in Firefox 3+. */ +button::-moz-focus-inner, input::-moz-focus-inner { + border: 0; + padding: 0; } + +/** + * 1. Remove default vertical scrollbar in IE 6/7/8/9. + * 2. Improve readability and alignment in all browsers. */ +textarea { + overflow: auto; + /* 1 */ + vertical-align: top; } + +/* 2 */ +/* ========================================================================== + * Tables + * ========================================================================== */ +/** + * Remove most spacing between table cells. */ +table { + border-collapse: collapse; + border-spacing: 0; } + +/* + * + *Visual Studio-like style based on original C# coloring by Jason Diamond */ +.hljs { + display: inline-block; + padding: 0.5em; + background: white; + color: black; } + +.hljs-comment, .hljs-annotation, .hljs-template_comment, .diff .hljs-header, .hljs-chunk, .apache .hljs-cbracket { + color: #008000; } + +.hljs-keyword, .hljs-id, .hljs-built_in, .css .smalltalk .hljs-class, .hljs-winutils, .bash .hljs-variable, .tex .hljs-command, .hljs-request, .hljs-status, .nginx .hljs-title { + color: #00f; } + +.xml .hljs-tag { + color: #00f; } + .xml .hljs-tag .hljs-value { + color: #00f; } + +.hljs-string, .hljs-title, .hljs-parent, .hljs-tag .hljs-value, .hljs-rules .hljs-value { + color: #a31515; } + +.ruby .hljs-symbol { + color: #a31515; } + .ruby .hljs-symbol .hljs-string { + color: #a31515; } + +.hljs-template_tag, .django .hljs-variable, .hljs-addition, .hljs-flow, .hljs-stream, .apache .hljs-tag, .hljs-date, .tex .hljs-formula, .coffeescript .hljs-attribute { + color: #a31515; } + +.ruby .hljs-string, .hljs-decorator, .hljs-filter .hljs-argument, .hljs-localvars, .hljs-array, .hljs-attr_selector, .hljs-pseudo, .hljs-pi, .hljs-doctype, .hljs-deletion, .hljs-envvar, .hljs-shebang, .hljs-preprocessor, .hljs-pragma, .userType, .apache .hljs-sqbracket, .nginx .hljs-built_in, .tex .hljs-special, .hljs-prompt { + color: #2b91af; } + +.hljs-phpdoc, .hljs-javadoc, .hljs-xmlDocTag { + color: #808080; } + +.vhdl .hljs-typename { + font-weight: bold; } + +.vhdl .hljs-string { + color: #666666; } + +.vhdl .hljs-literal { + color: #a31515; } + +.vhdl .hljs-attribute { + color: #00b0e8; } + +.xml .hljs-attribute { + color: #f00; } + +.col > :first-child, .col-1 > :first-child, .col-2 > :first-child, .col-3 > :first-child, .col-4 > :first-child, .col-5 > :first-child, .col-6 > :first-child, .col-7 > :first-child, .col-8 > :first-child, .col-9 > :first-child, .col-10 > :first-child, .col-11 > :first-child, .tsd-panel > :first-child, ul.tsd-descriptions > li > :first-child, +.col > :first-child > :first-child, +.col-1 > :first-child > :first-child, +.col-2 > :first-child > :first-child, +.col-3 > :first-child > :first-child, +.col-4 > :first-child > :first-child, +.col-5 > :first-child > :first-child, +.col-6 > :first-child > :first-child, +.col-7 > :first-child > :first-child, +.col-8 > :first-child > :first-child, +.col-9 > :first-child > :first-child, +.col-10 > :first-child > :first-child, +.col-11 > :first-child > :first-child, +.tsd-panel > :first-child > :first-child, +ul.tsd-descriptions > li > :first-child > :first-child, +.col > :first-child > :first-child > :first-child, +.col-1 > :first-child > :first-child > :first-child, +.col-2 > :first-child > :first-child > :first-child, +.col-3 > :first-child > :first-child > :first-child, +.col-4 > :first-child > :first-child > :first-child, +.col-5 > :first-child > :first-child > :first-child, +.col-6 > :first-child > :first-child > :first-child, +.col-7 > :first-child > :first-child > :first-child, +.col-8 > :first-child > :first-child > :first-child, +.col-9 > :first-child > :first-child > :first-child, +.col-10 > :first-child > :first-child > :first-child, +.col-11 > :first-child > :first-child > :first-child, +.tsd-panel > :first-child > :first-child > :first-child, +ul.tsd-descriptions > li > :first-child > :first-child > :first-child { + margin-top: 0; } + +.col > :last-child, .col-1 > :last-child, .col-2 > :last-child, .col-3 > :last-child, .col-4 > :last-child, .col-5 > :last-child, .col-6 > :last-child, .col-7 > :last-child, .col-8 > :last-child, .col-9 > :last-child, .col-10 > :last-child, .col-11 > :last-child, .tsd-panel > :last-child, ul.tsd-descriptions > li > :last-child, +.col > :last-child > :last-child, +.col-1 > :last-child > :last-child, +.col-2 > :last-child > :last-child, +.col-3 > :last-child > :last-child, +.col-4 > :last-child > :last-child, +.col-5 > :last-child > :last-child, +.col-6 > :last-child > :last-child, +.col-7 > :last-child > :last-child, +.col-8 > :last-child > :last-child, +.col-9 > :last-child > :last-child, +.col-10 > :last-child > :last-child, +.col-11 > :last-child > :last-child, +.tsd-panel > :last-child > :last-child, +ul.tsd-descriptions > li > :last-child > :last-child, +.col > :last-child > :last-child > :last-child, +.col-1 > :last-child > :last-child > :last-child, +.col-2 > :last-child > :last-child > :last-child, +.col-3 > :last-child > :last-child > :last-child, +.col-4 > :last-child > :last-child > :last-child, +.col-5 > :last-child > :last-child > :last-child, +.col-6 > :last-child > :last-child > :last-child, +.col-7 > :last-child > :last-child > :last-child, +.col-8 > :last-child > :last-child > :last-child, +.col-9 > :last-child > :last-child > :last-child, +.col-10 > :last-child > :last-child > :last-child, +.col-11 > :last-child > :last-child > :last-child, +.tsd-panel > :last-child > :last-child > :last-child, +ul.tsd-descriptions > li > :last-child > :last-child > :last-child { + margin-bottom: 0; } + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 40px; } + @media (max-width: 640px) { + .container { + padding: 0 20px; } } + +.container-main { + padding-bottom: 200px; } + +.row { + display: -ms-flexbox; + display: flex; + position: relative; + margin: 0 -10px; } + .row:after { + visibility: hidden; + display: block; + content: ""; + clear: both; + height: 0; } + +.col, .col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11 { + box-sizing: border-box; + float: left; + padding: 0 10px; } + +.col-1 { + width: 8.3333333333%; } + +.offset-1 { + margin-left: 8.3333333333%; } + +.col-2 { + width: 16.6666666667%; } + +.offset-2 { + margin-left: 16.6666666667%; } + +.col-3 { + width: 25%; } + +.offset-3 { + margin-left: 25%; } + +.col-4 { + width: 33.3333333333%; } + +.offset-4 { + margin-left: 33.3333333333%; } + +.col-5 { + width: 41.6666666667%; } + +.offset-5 { + margin-left: 41.6666666667%; } + +.col-6 { + width: 50%; } + +.offset-6 { + margin-left: 50%; } + +.col-7 { + width: 58.3333333333%; } + +.offset-7 { + margin-left: 58.3333333333%; } + +.col-8 { + width: 66.6666666667%; } + +.offset-8 { + margin-left: 66.6666666667%; } + +.col-9 { + width: 75%; } + +.offset-9 { + margin-left: 75%; } + +.col-10 { + width: 83.3333333333%; } + +.offset-10 { + margin-left: 83.3333333333%; } + +.col-11 { + width: 91.6666666667%; } + +.offset-11 { + margin-left: 91.6666666667%; } + +.tsd-kind-icon { + display: block; + position: relative; + padding-left: 20px; + text-indent: -20px; } + .tsd-kind-icon:before { + content: ''; + display: inline-block; + vertical-align: middle; + width: 17px; + height: 17px; + margin: 0 3px 2px 0; + background-image: url(../images/icons.png); } + @media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { + .tsd-kind-icon:before { + background-image: url(../images/icons@2x.png); + background-size: 238px 204px; } } + +.tsd-signature.tsd-kind-icon:before { + background-position: 0 -153px; } + +.tsd-kind-object-literal > .tsd-kind-icon:before { + background-position: 0px -17px; } + +.tsd-kind-object-literal.tsd-is-protected > .tsd-kind-icon:before { + background-position: -17px -17px; } + +.tsd-kind-object-literal.tsd-is-private > .tsd-kind-icon:before { + background-position: -34px -17px; } + +.tsd-kind-class > .tsd-kind-icon:before { + background-position: 0px -34px; } + +.tsd-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -17px -34px; } + +.tsd-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -34px -34px; } + +.tsd-kind-class.tsd-has-type-parameter > .tsd-kind-icon:before { + background-position: 0px -51px; } + +.tsd-kind-class.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { + background-position: -17px -51px; } + +.tsd-kind-class.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { + background-position: -34px -51px; } + +.tsd-kind-interface > .tsd-kind-icon:before { + background-position: 0px -68px; } + +.tsd-kind-interface.tsd-is-protected > .tsd-kind-icon:before { + background-position: -17px -68px; } + +.tsd-kind-interface.tsd-is-private > .tsd-kind-icon:before { + background-position: -34px -68px; } + +.tsd-kind-interface.tsd-has-type-parameter > .tsd-kind-icon:before { + background-position: 0px -85px; } + +.tsd-kind-interface.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { + background-position: -17px -85px; } + +.tsd-kind-interface.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { + background-position: -34px -85px; } + +.tsd-kind-namespace > .tsd-kind-icon:before { + background-position: 0px -102px; } + +.tsd-kind-namespace.tsd-is-protected > .tsd-kind-icon:before { + background-position: -17px -102px; } + +.tsd-kind-namespace.tsd-is-private > .tsd-kind-icon:before { + background-position: -34px -102px; } + +.tsd-kind-module > .tsd-kind-icon:before { + background-position: 0px -102px; } + +.tsd-kind-module.tsd-is-protected > .tsd-kind-icon:before { + background-position: -17px -102px; } + +.tsd-kind-module.tsd-is-private > .tsd-kind-icon:before { + background-position: -34px -102px; } + +.tsd-kind-enum > .tsd-kind-icon:before { + background-position: 0px -119px; } + +.tsd-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -17px -119px; } + +.tsd-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -34px -119px; } + +.tsd-kind-enum-member > .tsd-kind-icon:before { + background-position: 0px -136px; } + +.tsd-kind-enum-member.tsd-is-protected > .tsd-kind-icon:before { + background-position: -17px -136px; } + +.tsd-kind-enum-member.tsd-is-private > .tsd-kind-icon:before { + background-position: -34px -136px; } + +.tsd-kind-signature > .tsd-kind-icon:before { + background-position: 0px -153px; } + +.tsd-kind-signature.tsd-is-protected > .tsd-kind-icon:before { + background-position: -17px -153px; } + +.tsd-kind-signature.tsd-is-private > .tsd-kind-icon:before { + background-position: -34px -153px; } + +.tsd-kind-type-alias > .tsd-kind-icon:before { + background-position: 0px -170px; } + +.tsd-kind-type-alias.tsd-is-protected > .tsd-kind-icon:before { + background-position: -17px -170px; } + +.tsd-kind-type-alias.tsd-is-private > .tsd-kind-icon:before { + background-position: -34px -170px; } + +.tsd-kind-type-alias.tsd-has-type-parameter > .tsd-kind-icon:before { + background-position: 0px -187px; } + +.tsd-kind-type-alias.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { + background-position: -17px -187px; } + +.tsd-kind-type-alias.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { + background-position: -34px -187px; } + +.tsd-kind-variable > .tsd-kind-icon:before { + background-position: -136px -0px; } + +.tsd-kind-variable.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -0px; } + +.tsd-kind-variable.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -0px; } + +.tsd-kind-variable.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -0px; } + +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -0px; } + +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -0px; } + +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -0px; } + +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -0px; } + +.tsd-kind-variable.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -0px; } + +.tsd-kind-variable.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -0px; } + +.tsd-kind-variable.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -0px; } + +.tsd-kind-variable.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -0px; } + +.tsd-kind-variable.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -0px; } + +.tsd-kind-property > .tsd-kind-icon:before { + background-position: -136px -0px; } + +.tsd-kind-property.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -0px; } + +.tsd-kind-property.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -0px; } + +.tsd-kind-property.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -0px; } + +.tsd-kind-property.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -0px; } + +.tsd-kind-property.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -0px; } + +.tsd-kind-property.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -0px; } + +.tsd-kind-property.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -0px; } + +.tsd-kind-property.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -0px; } + +.tsd-kind-property.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -0px; } + +.tsd-kind-property.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -0px; } + +.tsd-kind-property.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -0px; } + +.tsd-kind-property.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -0px; } + +.tsd-kind-get-signature > .tsd-kind-icon:before { + background-position: -136px -17px; } + +.tsd-kind-get-signature.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -17px; } + +.tsd-kind-get-signature.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -17px; } + +.tsd-kind-get-signature.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -17px; } + +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -17px; } + +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -17px; } + +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -17px; } + +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -17px; } + +.tsd-kind-get-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -17px; } + +.tsd-kind-get-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -17px; } + +.tsd-kind-get-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -17px; } + +.tsd-kind-get-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -17px; } + +.tsd-kind-get-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -17px; } + +.tsd-kind-set-signature > .tsd-kind-icon:before { + background-position: -136px -34px; } + +.tsd-kind-set-signature.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -34px; } + +.tsd-kind-set-signature.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -34px; } + +.tsd-kind-set-signature.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -34px; } + +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -34px; } + +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -34px; } + +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -34px; } + +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -34px; } + +.tsd-kind-set-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -34px; } + +.tsd-kind-set-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -34px; } + +.tsd-kind-set-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -34px; } + +.tsd-kind-set-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -34px; } + +.tsd-kind-set-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -34px; } + +.tsd-kind-accessor > .tsd-kind-icon:before { + background-position: -136px -51px; } + +.tsd-kind-accessor.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -51px; } + +.tsd-kind-accessor.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -51px; } + +.tsd-kind-accessor.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -51px; } + +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -51px; } + +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -51px; } + +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -51px; } + +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -51px; } + +.tsd-kind-accessor.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -51px; } + +.tsd-kind-accessor.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -51px; } + +.tsd-kind-accessor.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -51px; } + +.tsd-kind-accessor.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -51px; } + +.tsd-kind-accessor.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -51px; } + +.tsd-kind-function > .tsd-kind-icon:before { + background-position: -136px -68px; } + +.tsd-kind-function.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -68px; } + +.tsd-kind-function.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -68px; } + +.tsd-kind-function.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -68px; } + +.tsd-kind-function.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -68px; } + +.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -68px; } + +.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -68px; } + +.tsd-kind-function.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -68px; } + +.tsd-kind-function.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -68px; } + +.tsd-kind-function.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -68px; } + +.tsd-kind-function.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -68px; } + +.tsd-kind-function.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -68px; } + +.tsd-kind-function.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -68px; } + +.tsd-kind-method > .tsd-kind-icon:before { + background-position: -136px -68px; } + +.tsd-kind-method.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -68px; } + +.tsd-kind-method.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -68px; } + +.tsd-kind-method.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -68px; } + +.tsd-kind-method.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -68px; } + +.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -68px; } + +.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -68px; } + +.tsd-kind-method.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -68px; } + +.tsd-kind-method.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -68px; } + +.tsd-kind-method.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -68px; } + +.tsd-kind-method.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -68px; } + +.tsd-kind-method.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -68px; } + +.tsd-kind-method.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -68px; } + +.tsd-kind-call-signature > .tsd-kind-icon:before { + background-position: -136px -68px; } + +.tsd-kind-call-signature.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -68px; } + +.tsd-kind-call-signature.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -68px; } + +.tsd-kind-call-signature.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -68px; } + +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -68px; } + +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -68px; } + +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -68px; } + +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -68px; } + +.tsd-kind-call-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -68px; } + +.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -68px; } + +.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -68px; } + +.tsd-kind-call-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -68px; } + +.tsd-kind-call-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -68px; } + +.tsd-kind-function.tsd-has-type-parameter > .tsd-kind-icon:before { + background-position: -136px -85px; } + +.tsd-kind-function.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -85px; } + +.tsd-kind-function.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -85px; } + +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -85px; } + +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -85px; } + +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -85px; } + +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -85px; } + +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -85px; } + +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -85px; } + +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -85px; } + +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -85px; } + +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -85px; } + +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -85px; } + +.tsd-kind-method.tsd-has-type-parameter > .tsd-kind-icon:before { + background-position: -136px -85px; } + +.tsd-kind-method.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -85px; } + +.tsd-kind-method.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -85px; } + +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -85px; } + +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -85px; } + +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -85px; } + +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -85px; } + +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -85px; } + +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -85px; } + +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -85px; } + +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -85px; } + +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -85px; } + +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -85px; } + +.tsd-kind-constructor > .tsd-kind-icon:before { + background-position: -136px -102px; } + +.tsd-kind-constructor.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -102px; } + +.tsd-kind-constructor.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -102px; } + +.tsd-kind-constructor.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -102px; } + +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -102px; } + +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -102px; } + +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -102px; } + +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -102px; } + +.tsd-kind-constructor.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -102px; } + +.tsd-kind-constructor.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -102px; } + +.tsd-kind-constructor.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -102px; } + +.tsd-kind-constructor.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -102px; } + +.tsd-kind-constructor.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -102px; } + +.tsd-kind-constructor-signature > .tsd-kind-icon:before { + background-position: -136px -102px; } + +.tsd-kind-constructor-signature.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -102px; } + +.tsd-kind-constructor-signature.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -102px; } + +.tsd-kind-constructor-signature.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -102px; } + +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -102px; } + +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -102px; } + +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -102px; } + +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -102px; } + +.tsd-kind-constructor-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -102px; } + +.tsd-kind-constructor-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -102px; } + +.tsd-kind-constructor-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -102px; } + +.tsd-kind-constructor-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -102px; } + +.tsd-kind-constructor-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -102px; } + +.tsd-kind-index-signature > .tsd-kind-icon:before { + background-position: -136px -119px; } + +.tsd-kind-index-signature.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -119px; } + +.tsd-kind-index-signature.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -119px; } + +.tsd-kind-index-signature.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -119px; } + +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -119px; } + +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -119px; } + +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -119px; } + +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -119px; } + +.tsd-kind-index-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -119px; } + +.tsd-kind-index-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -119px; } + +.tsd-kind-index-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -119px; } + +.tsd-kind-index-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -119px; } + +.tsd-kind-index-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -119px; } + +.tsd-kind-event > .tsd-kind-icon:before { + background-position: -136px -136px; } + +.tsd-kind-event.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -136px; } + +.tsd-kind-event.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -136px; } + +.tsd-kind-event.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -136px; } + +.tsd-kind-event.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -136px; } + +.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -136px; } + +.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -136px; } + +.tsd-kind-event.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -136px; } + +.tsd-kind-event.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -136px; } + +.tsd-kind-event.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -136px; } + +.tsd-kind-event.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -136px; } + +.tsd-kind-event.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -136px; } + +.tsd-kind-event.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -136px; } + +.tsd-is-static > .tsd-kind-icon:before { + background-position: -136px -153px; } + +.tsd-is-static.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -153px; } + +.tsd-is-static.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -153px; } + +.tsd-is-static.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -153px; } + +.tsd-is-static.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -153px; } + +.tsd-is-static.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -153px; } + +.tsd-is-static.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -153px; } + +.tsd-is-static.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -153px; } + +.tsd-is-static.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -153px; } + +.tsd-is-static.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -153px; } + +.tsd-is-static.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -153px; } + +.tsd-is-static.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -153px; } + +.tsd-is-static.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -153px; } + +.tsd-is-static.tsd-kind-function > .tsd-kind-icon:before { + background-position: -136px -170px; } + +.tsd-is-static.tsd-kind-function.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -170px; } + +.tsd-is-static.tsd-kind-function.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -170px; } + +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -170px; } + +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -170px; } + +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -170px; } + +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -170px; } + +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -170px; } + +.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -170px; } + +.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -170px; } + +.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -170px; } + +.tsd-is-static.tsd-kind-function.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -170px; } + +.tsd-is-static.tsd-kind-function.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -170px; } + +.tsd-is-static.tsd-kind-method > .tsd-kind-icon:before { + background-position: -136px -170px; } + +.tsd-is-static.tsd-kind-method.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -170px; } + +.tsd-is-static.tsd-kind-method.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -170px; } + +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -170px; } + +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -170px; } + +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -170px; } + +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -170px; } + +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -170px; } + +.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -170px; } + +.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -170px; } + +.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -170px; } + +.tsd-is-static.tsd-kind-method.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -170px; } + +.tsd-is-static.tsd-kind-method.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -170px; } + +.tsd-is-static.tsd-kind-call-signature > .tsd-kind-icon:before { + background-position: -136px -170px; } + +.tsd-is-static.tsd-kind-call-signature.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -170px; } + +.tsd-is-static.tsd-kind-call-signature.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -170px; } + +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -170px; } + +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -170px; } + +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -170px; } + +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -170px; } + +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -170px; } + +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -170px; } + +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -170px; } + +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -170px; } + +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -170px; } + +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -170px; } + +.tsd-is-static.tsd-kind-event > .tsd-kind-icon:before { + background-position: -136px -187px; } + +.tsd-is-static.tsd-kind-event.tsd-is-protected > .tsd-kind-icon:before { + background-position: -153px -187px; } + +.tsd-is-static.tsd-kind-event.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -187px; } + +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class > .tsd-kind-icon:before { + background-position: -51px -187px; } + +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -68px -187px; } + +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { + background-position: -85px -187px; } + +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -102px -187px; } + +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -187px; } + +.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum > .tsd-kind-icon:before { + background-position: -170px -187px; } + +.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { + background-position: -187px -187px; } + +.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { + background-position: -119px -187px; } + +.tsd-is-static.tsd-kind-event.tsd-parent-kind-interface > .tsd-kind-icon:before { + background-position: -204px -187px; } + +.tsd-is-static.tsd-kind-event.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { + background-position: -221px -187px; } + +.no-transition { + transition: none !important; } + +@keyframes fade-in { + from { + opacity: 0; } + to { + opacity: 1; } } + +@keyframes fade-out { + from { + opacity: 1; + visibility: visible; } + to { + opacity: 0; } } + +@keyframes fade-in-delayed { + 0% { + opacity: 0; } + 33% { + opacity: 0; } + 100% { + opacity: 1; } } + +@keyframes fade-out-delayed { + 0% { + opacity: 1; + visibility: visible; } + 66% { + opacity: 0; } + 100% { + opacity: 0; } } + +@keyframes shift-to-left { + from { + transform: translate(0, 0); } + to { + transform: translate(-25%, 0); } } + +@keyframes unshift-to-left { + from { + transform: translate(-25%, 0); } + to { + transform: translate(0, 0); } } + +@keyframes pop-in-from-right { + from { + transform: translate(100%, 0); } + to { + transform: translate(0, 0); } } + +@keyframes pop-out-to-right { + from { + transform: translate(0, 0); + visibility: visible; } + to { + transform: translate(100%, 0); } } + +body { + background: #fdfdfd; + font-family: "Segoe UI", sans-serif; + font-size: 16px; + color: #222; } + +a { + color: #4da6ff; + text-decoration: none; } + a:hover { + text-decoration: underline; } + +code, pre { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + padding: 0.2em; + margin: 0; + font-size: 14px; + background-color: rgba(0, 0, 0, 0.04); } + +pre { + padding: 10px; } + pre code { + padding: 0; + font-size: 100%; + background-color: transparent; } + +.tsd-typography { + line-height: 1.333em; } + .tsd-typography ul { + list-style: square; + padding: 0 0 0 20px; + margin: 0; } + .tsd-typography h4, .tsd-typography .tsd-index-panel h3, .tsd-index-panel .tsd-typography h3, .tsd-typography h5, .tsd-typography h6 { + font-size: 1em; + margin: 0; } + .tsd-typography h5, .tsd-typography h6 { + font-weight: normal; } + .tsd-typography p, .tsd-typography ul, .tsd-typography ol { + margin: 1em 0; } + +@media (min-width: 901px) and (max-width: 1024px) { + html.default .col-content { + width: 72%; } + html.default .col-menu { + width: 28%; } + html.default .tsd-navigation { + padding-left: 10px; } } + +@media (max-width: 900px) { + html.default .col-content { + float: none; + width: 100%; } + html.default .col-menu { + position: fixed !important; + overflow: auto; + -webkit-overflow-scrolling: touch; + z-index: 1024; + top: 0 !important; + bottom: 0 !important; + left: auto !important; + right: 0 !important; + width: 100%; + padding: 20px 20px 0 0; + max-width: 450px; + visibility: hidden; + background-color: #fff; + transform: translate(100%, 0); } + html.default .col-menu > *:last-child { + padding-bottom: 20px; } + html.default .overlay { + content: ''; + display: block; + position: fixed; + z-index: 1023; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.75); + visibility: hidden; } + html.default.to-has-menu .overlay { + animation: fade-in 0.4s; } + html.default.to-has-menu header, + html.default.to-has-menu footer, + html.default.to-has-menu .col-content { + animation: shift-to-left 0.4s; } + html.default.to-has-menu .col-menu { + animation: pop-in-from-right 0.4s; } + html.default.from-has-menu .overlay { + animation: fade-out 0.4s; } + html.default.from-has-menu header, + html.default.from-has-menu footer, + html.default.from-has-menu .col-content { + animation: unshift-to-left 0.4s; } + html.default.from-has-menu .col-menu { + animation: pop-out-to-right 0.4s; } + html.default.has-menu body { + overflow: hidden; } + html.default.has-menu .overlay { + visibility: visible; } + html.default.has-menu header, + html.default.has-menu footer, + html.default.has-menu .col-content { + transform: translate(-25%, 0); } + html.default.has-menu .col-menu { + visibility: visible; + transform: translate(0, 0); } } + +.tsd-page-title { + padding: 70px 0 20px 0; + margin: 0 0 40px 0; + background: #fff; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.35); } + .tsd-page-title h1 { + margin: 0; } + +.tsd-breadcrumb { + margin: 0; + padding: 0; + color: #808080; } + .tsd-breadcrumb a { + color: #808080; + text-decoration: none; } + .tsd-breadcrumb a:hover { + text-decoration: underline; } + .tsd-breadcrumb li { + display: inline; } + .tsd-breadcrumb li:after { + content: ' / '; } + +html.minimal .container { + margin: 0; } + +html.minimal .container-main { + padding-top: 50px; + padding-bottom: 0; } + +html.minimal .content-wrap { + padding-left: 300px; } + +html.minimal .tsd-navigation { + position: fixed !important; + overflow: auto; + -webkit-overflow-scrolling: touch; + box-sizing: border-box; + z-index: 1; + left: 0; + top: 40px; + bottom: 0; + width: 300px; + padding: 20px; + margin: 0; } + +html.minimal .tsd-member .tsd-member { + margin-left: 0; } + +html.minimal .tsd-page-toolbar { + position: fixed; + z-index: 2; } + +html.minimal #tsd-filter .tsd-filter-group { + right: 0; + transform: none; } + +html.minimal footer { + background-color: transparent; } + html.minimal footer .container { + padding: 0; } + +html.minimal .tsd-generator { + padding: 0; } + +@media (max-width: 900px) { + html.minimal .tsd-navigation { + display: none; } + html.minimal .content-wrap { + padding-left: 0; } } + +dl.tsd-comment-tags { + overflow: hidden; } + dl.tsd-comment-tags dt { + float: left; + padding: 1px 5px; + margin: 0 10px 0 0; + border-radius: 4px; + border: 1px solid #808080; + color: #808080; + font-size: 0.8em; + font-weight: normal; } + dl.tsd-comment-tags dd { + margin: 0 0 10px 0; } + dl.tsd-comment-tags dd:before, dl.tsd-comment-tags dd:after { + display: table; + content: " "; } + dl.tsd-comment-tags dd pre, dl.tsd-comment-tags dd:after { + clear: both; } + dl.tsd-comment-tags p { + margin: 0; } + +.tsd-panel.tsd-comment .lead { + font-size: 1.1em; + line-height: 1.333em; + margin-bottom: 2em; } + .tsd-panel.tsd-comment .lead:last-child { + margin-bottom: 0; } + +.toggle-protected .tsd-is-private { + display: none; } + +.toggle-public .tsd-is-private, +.toggle-public .tsd-is-protected, +.toggle-public .tsd-is-private-protected { + display: none; } + +.toggle-inherited .tsd-is-inherited { + display: none; } + +.toggle-only-exported .tsd-is-not-exported { + display: none; } + +.toggle-externals .tsd-is-external { + display: none; } + +#tsd-filter { + position: relative; + display: inline-block; + height: 40px; + vertical-align: bottom; } + .no-filter #tsd-filter { + display: none; } + #tsd-filter .tsd-filter-group { + display: inline-block; + height: 40px; + vertical-align: bottom; + white-space: nowrap; } + #tsd-filter input { + display: none; } + @media (max-width: 900px) { + #tsd-filter .tsd-filter-group { + display: block; + position: absolute; + top: 40px; + right: 20px; + height: auto; + background-color: #fff; + visibility: hidden; + transform: translate(50%, 0); + box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); } + .has-options #tsd-filter .tsd-filter-group { + visibility: visible; } + .to-has-options #tsd-filter .tsd-filter-group { + animation: fade-in 0.2s; } + .from-has-options #tsd-filter .tsd-filter-group { + animation: fade-out 0.2s; } + #tsd-filter label, + #tsd-filter .tsd-select { + display: block; + padding-right: 20px; } } + +footer { + border-top: 1px solid #eee; + background-color: #fff; } + footer.with-border-bottom { + border-bottom: 1px solid #eee; } + footer .tsd-legend-group { + font-size: 0; } + footer .tsd-legend { + display: inline-block; + width: 25%; + padding: 0; + font-size: 16px; + list-style: none; + line-height: 1.333em; + vertical-align: top; } + @media (max-width: 900px) { + footer .tsd-legend { + width: 50%; } } + +.tsd-hierarchy { + list-style: square; + padding: 0 0 0 20px; + margin: 0; } + .tsd-hierarchy .target { + font-weight: bold; } + +.tsd-index-panel .tsd-index-content { + margin-bottom: -30px !important; } + +.tsd-index-panel .tsd-index-section { + margin-bottom: 30px !important; } + +.tsd-index-panel h3 { + margin: 0 -20px 10px -20px; + padding: 0 20px 10px 20px; + border-bottom: 1px solid #eee; } + +.tsd-index-panel ul.tsd-index-list { + -moz-column-count: 3; + -ms-column-count: 3; + -o-column-count: 3; + column-count: 3; + -moz-column-gap: 20px; + -ms-column-gap: 20px; + -o-column-gap: 20px; + column-gap: 20px; + padding: 0; + list-style: none; + line-height: 1.333em; } + @media (max-width: 900px) { + .tsd-index-panel ul.tsd-index-list { + -moz-column-count: 1; + -ms-column-count: 1; + -o-column-count: 1; + column-count: 1; } } + @media (min-width: 901px) and (max-width: 1024px) { + .tsd-index-panel ul.tsd-index-list { + -moz-column-count: 2; + -ms-column-count: 2; + -o-column-count: 2; + column-count: 2; } } + .tsd-index-panel ul.tsd-index-list li { + -webkit-page-break-inside: avoid; + -moz-page-break-inside: avoid; + -ms-page-break-inside: avoid; + -o-page-break-inside: avoid; + page-break-inside: avoid; } + +.tsd-index-panel a, +.tsd-index-panel .tsd-parent-kind-module a { + color: #9600ff; } + +.tsd-index-panel .tsd-parent-kind-interface a { + color: #7da01f; } + +.tsd-index-panel .tsd-parent-kind-enum a { + color: #cc9900; } + +.tsd-index-panel .tsd-parent-kind-class a { + color: #4da6ff; } + +.tsd-index-panel .tsd-kind-module a { + color: #9600ff; } + +.tsd-index-panel .tsd-kind-interface a { + color: #7da01f; } + +.tsd-index-panel .tsd-kind-enum a { + color: #cc9900; } + +.tsd-index-panel .tsd-kind-class a { + color: #4da6ff; } + +.tsd-index-panel .tsd-is-private a { + color: #808080; } + +.tsd-flag { + display: inline-block; + padding: 1px 5px; + border-radius: 4px; + color: #fff; + background-color: #808080; + text-indent: 0; + font-size: 14px; + font-weight: normal; } + +.tsd-anchor { + position: absolute; + top: -100px; } + +.tsd-member { + position: relative; } + .tsd-member .tsd-anchor + h3 { + margin-top: 0; + margin-bottom: 0; + border-bottom: none; } + +.tsd-navigation { + margin: 0 0 0 40px; } + .tsd-navigation a { + display: block; + padding-top: 2px; + padding-bottom: 2px; + border-left: 2px solid transparent; + color: #222; + text-decoration: none; + transition: border-left-color 0.1s; } + .tsd-navigation a:hover { + text-decoration: underline; } + .tsd-navigation ul { + margin: 0; + padding: 0; + list-style: none; } + .tsd-navigation li { + padding: 0; } + +.tsd-navigation.primary { + padding-bottom: 40px; } + .tsd-navigation.primary a { + display: block; + padding-top: 6px; + padding-bottom: 6px; } + .tsd-navigation.primary ul li a { + padding-left: 5px; } + .tsd-navigation.primary ul li li a { + padding-left: 25px; } + .tsd-navigation.primary ul li li li a { + padding-left: 45px; } + .tsd-navigation.primary ul li li li li a { + padding-left: 65px; } + .tsd-navigation.primary ul li li li li li a { + padding-left: 85px; } + .tsd-navigation.primary ul li li li li li li a { + padding-left: 105px; } + .tsd-navigation.primary > ul { + border-bottom: 1px solid #eee; } + .tsd-navigation.primary li { + border-top: 1px solid #eee; } + .tsd-navigation.primary li.current > a { + font-weight: bold; } + .tsd-navigation.primary li.label span { + display: block; + padding: 20px 0 6px 5px; + color: #808080; } + .tsd-navigation.primary li.globals + li > span, + .tsd-navigation.primary li.globals + li > a { + padding-top: 20px; } + +.tsd-navigation.secondary { + max-height: calc(100vh - 1rem - 40px); + overflow: auto; + position: -webkit-sticky; + position: sticky; + top: calc(.5rem + 40px); + transition: .3s; } + .tsd-navigation.secondary.tsd-navigation--toolbar-hide { + max-height: calc(100vh - 1rem); + top: .5rem; } + .tsd-navigation.secondary ul { + transition: opacity 0.2s; } + .tsd-navigation.secondary ul li a { + padding-left: 25px; } + .tsd-navigation.secondary ul li li a { + padding-left: 45px; } + .tsd-navigation.secondary ul li li li a { + padding-left: 65px; } + .tsd-navigation.secondary ul li li li li a { + padding-left: 85px; } + .tsd-navigation.secondary ul li li li li li a { + padding-left: 105px; } + .tsd-navigation.secondary ul li li li li li li a { + padding-left: 125px; } + .tsd-navigation.secondary ul.current a { + border-left-color: #eee; } + .tsd-navigation.secondary li.focus > a, + .tsd-navigation.secondary ul.current li.focus > a { + border-left-color: #000; } + .tsd-navigation.secondary li.current { + margin-top: 20px; + margin-bottom: 20px; + border-left-color: #eee; } + .tsd-navigation.secondary li.current > a { + font-weight: bold; } + +@media (min-width: 901px) { + .menu-sticky-wrap { + position: static; } } + +.tsd-panel { + margin: 20px 0; + padding: 20px; + background-color: #fff; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); } + .tsd-panel:empty { + display: none; } + .tsd-panel > h1, .tsd-panel > h2, .tsd-panel > h3 { + margin: 1.5em -20px 10px -20px; + padding: 0 20px 10px 20px; + border-bottom: 1px solid #eee; } + .tsd-panel > h1.tsd-before-signature, .tsd-panel > h2.tsd-before-signature, .tsd-panel > h3.tsd-before-signature { + margin-bottom: 0; + border-bottom: 0; } + .tsd-panel table { + display: block; + width: 100%; + overflow: auto; + margin-top: 10px; + word-break: normal; + word-break: keep-all; } + .tsd-panel table th { + font-weight: bold; } + .tsd-panel table th, .tsd-panel table td { + padding: 6px 13px; + border: 1px solid #ddd; } + .tsd-panel table tr { + background-color: #fff; + border-top: 1px solid #ccc; } + .tsd-panel table tr:nth-child(2n) { + background-color: #f8f8f8; } + +.tsd-panel-group { + margin: 60px 0; } + .tsd-panel-group > h1, .tsd-panel-group > h2, .tsd-panel-group > h3 { + padding-left: 20px; + padding-right: 20px; } + +#tsd-search { + transition: background-color 0.2s; } + #tsd-search .title { + position: relative; + z-index: 2; } + #tsd-search .field { + position: absolute; + left: 0; + top: 0; + right: 40px; + height: 40px; } + #tsd-search .field input { + box-sizing: border-box; + position: relative; + top: -50px; + z-index: 1; + width: 100%; + padding: 0 10px; + opacity: 0; + outline: 0; + border: 0; + background: transparent; + color: #222; } + #tsd-search .field label { + position: absolute; + overflow: hidden; + right: -40px; } + #tsd-search .field input, + #tsd-search .title { + transition: opacity 0.2s; } + #tsd-search .results { + position: absolute; + visibility: hidden; + top: 40px; + width: 100%; + margin: 0; + padding: 0; + list-style: none; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); } + #tsd-search .results li { + padding: 0 10px; + background-color: #fdfdfd; } + #tsd-search .results li:nth-child(even) { + background-color: #fff; } + #tsd-search .results li.state { + display: none; } + #tsd-search .results li.current, + #tsd-search .results li:hover { + background-color: #eee; } + #tsd-search .results a { + display: block; } + #tsd-search .results a:before { + top: 10px; } + #tsd-search .results span.parent { + color: #808080; + font-weight: normal; } + #tsd-search.has-focus { + background-color: #eee; } + #tsd-search.has-focus .field input { + top: 0; + opacity: 1; } + #tsd-search.has-focus .title { + z-index: 0; + opacity: 0; } + #tsd-search.has-focus .results { + visibility: visible; } + #tsd-search.loading .results li.state.loading { + display: block; } + #tsd-search.failure .results li.state.failure { + display: block; } + +.tsd-signature { + margin: 0 0 1em 0; + padding: 10px; + border: 1px solid #eee; + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; + font-size: 14px; } + .tsd-signature.tsd-kind-icon { + padding-left: 30px; } + .tsd-signature.tsd-kind-icon:before { + top: 10px; + left: 10px; } + .tsd-panel > .tsd-signature { + margin-left: -20px; + margin-right: -20px; + border-width: 1px 0; } + .tsd-panel > .tsd-signature.tsd-kind-icon { + padding-left: 40px; } + .tsd-panel > .tsd-signature.tsd-kind-icon:before { + left: 20px; } + +.tsd-signature-symbol { + color: #808080; + font-weight: normal; } + +.tsd-signature-type { + font-style: italic; + font-weight: normal; } + +.tsd-signatures { + padding: 0; + margin: 0 0 1em 0; + border: 1px solid #eee; } + .tsd-signatures .tsd-signature { + margin: 0; + border-width: 1px 0 0 0; + transition: background-color 0.1s; } + .tsd-signatures .tsd-signature:first-child { + border-top-width: 0; } + .tsd-signatures .tsd-signature.current { + background-color: #eee; } + .tsd-signatures.active > .tsd-signature { + cursor: pointer; } + .tsd-panel > .tsd-signatures { + margin-left: -20px; + margin-right: -20px; + border-width: 1px 0; } + .tsd-panel > .tsd-signatures .tsd-signature.tsd-kind-icon { + padding-left: 40px; } + .tsd-panel > .tsd-signatures .tsd-signature.tsd-kind-icon:before { + left: 20px; } + .tsd-panel > a.anchor + .tsd-signatures { + border-top-width: 0; + margin-top: -20px; } + +ul.tsd-descriptions { + position: relative; + overflow: hidden; + transition: height 0.3s; + padding: 0; + list-style: none; } + ul.tsd-descriptions.active > .tsd-description { + display: none; } + ul.tsd-descriptions.active > .tsd-description.current { + display: block; } + ul.tsd-descriptions.active > .tsd-description.fade-in { + animation: fade-in-delayed 0.3s; } + ul.tsd-descriptions.active > .tsd-description.fade-out { + animation: fade-out-delayed 0.3s; + position: absolute; + display: block; + top: 0; + left: 0; + right: 0; + opacity: 0; + visibility: hidden; } + ul.tsd-descriptions h4, ul.tsd-descriptions .tsd-index-panel h3, .tsd-index-panel ul.tsd-descriptions h3 { + font-size: 16px; + margin: 1em 0 0.5em 0; } + +ul.tsd-parameters, +ul.tsd-type-parameters { + list-style: square; + margin: 0; + padding-left: 20px; } + ul.tsd-parameters > li.tsd-parameter-signature, + ul.tsd-type-parameters > li.tsd-parameter-signature { + list-style: none; + margin-left: -20px; } + ul.tsd-parameters h5, + ul.tsd-type-parameters h5 { + font-size: 16px; + margin: 1em 0 0.5em 0; } + ul.tsd-parameters .tsd-comment, + ul.tsd-type-parameters .tsd-comment { + margin-top: -0.5em; } + +.tsd-sources { + font-size: 14px; + color: #808080; + margin: 0 0 1em 0; } + .tsd-sources a { + color: #808080; + text-decoration: underline; } + .tsd-sources ul, .tsd-sources p { + margin: 0 !important; } + .tsd-sources ul { + list-style: none; + padding: 0; } + +.tsd-page-toolbar { + position: fixed; + z-index: 1; + top: 0; + left: 0; + width: 100%; + height: 40px; + color: #333; + background: #fff; + border-bottom: 1px solid #eee; + transition: transform .3s linear; } + .tsd-page-toolbar a { + color: #333; + text-decoration: none; } + .tsd-page-toolbar a.title { + font-weight: bold; } + .tsd-page-toolbar a.title:hover { + text-decoration: underline; } + .tsd-page-toolbar .table-wrap { + display: table; + width: 100%; + height: 40px; } + .tsd-page-toolbar .table-cell { + display: table-cell; + position: relative; + white-space: nowrap; + line-height: 40px; } + .tsd-page-toolbar .table-cell:first-child { + width: 100%; } + +.tsd-page-toolbar--hide { + transform: translateY(-100%); } + +.tsd-widget:before, .tsd-select .tsd-select-label:before, .tsd-select .tsd-select-list li:before { + content: ''; + display: inline-block; + width: 40px; + height: 40px; + margin: 0 -8px 0 0; + background-image: url(../images/widgets.png); + background-repeat: no-repeat; + text-indent: -1024px; + vertical-align: bottom; } + @media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { + .tsd-widget:before, .tsd-select .tsd-select-label:before, .tsd-select .tsd-select-list li:before { + background-image: url(../images/widgets@2x.png); + background-size: 320px 40px; } } + +.tsd-widget { + display: inline-block; + overflow: hidden; + opacity: 0.6; + height: 40px; + transition: opacity 0.1s, background-color 0.2s; + vertical-align: bottom; + cursor: pointer; } + .tsd-widget:hover { + opacity: 0.8; } + .tsd-widget.active { + opacity: 1; + background-color: #eee; } + .tsd-widget.no-caption { + width: 40px; } + .tsd-widget.no-caption:before { + margin: 0; } + .tsd-widget.search:before { + background-position: 0 0; } + .tsd-widget.menu:before { + background-position: -40px 0; } + .tsd-widget.options:before { + background-position: -80px 0; } + .tsd-widget.options, .tsd-widget.menu { + display: none; } + @media (max-width: 900px) { + .tsd-widget.options, .tsd-widget.menu { + display: inline-block; } } + input[type=checkbox] + .tsd-widget:before { + background-position: -120px 0; } + input[type=checkbox]:checked + .tsd-widget:before { + background-position: -160px 0; } + +.tsd-select { + position: relative; + display: inline-block; + height: 40px; + transition: opacity 0.1s, background-color 0.2s; + vertical-align: bottom; + cursor: pointer; } + .tsd-select .tsd-select-label { + opacity: 0.6; + transition: opacity 0.2s; } + .tsd-select .tsd-select-label:before { + background-position: -240px 0; } + .tsd-select.active .tsd-select-label { + opacity: 0.8; } + .tsd-select.active .tsd-select-list { + visibility: visible; + opacity: 1; + transition-delay: 0s; } + .tsd-select .tsd-select-list { + position: absolute; + visibility: hidden; + top: 40px; + left: 0; + margin: 0; + padding: 0; + opacity: 0; + list-style: none; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); + transition: visibility 0s 0.2s, opacity 0.2s; } + .tsd-select .tsd-select-list li { + padding: 0 20px 0 0; + background-color: #fdfdfd; } + .tsd-select .tsd-select-list li:before { + background-position: 40px 0; } + .tsd-select .tsd-select-list li:nth-child(even) { + background-color: #fff; } + .tsd-select .tsd-select-list li:hover { + background-color: #eee; } + .tsd-select .tsd-select-list li.selected:before { + background-position: -200px 0; } + @media (max-width: 900px) { + .tsd-select .tsd-select-list { + top: 0; + left: auto; + right: 100%; + margin-right: -5px; } + .tsd-select .tsd-select-label:before { + background-position: -280px 0; } } + +img { + max-width: 100%; } diff --git a/packages/mapbox-gl-esri-sources/docs/assets/images/icons.png b/packages/mapbox-gl-esri-sources/docs/assets/images/icons.png new file mode 100644 index 000000000..3836d5fe4 Binary files /dev/null and b/packages/mapbox-gl-esri-sources/docs/assets/images/icons.png differ diff --git a/packages/mapbox-gl-esri-sources/docs/assets/images/icons@2x.png b/packages/mapbox-gl-esri-sources/docs/assets/images/icons@2x.png new file mode 100644 index 000000000..5a209e2f6 Binary files /dev/null and b/packages/mapbox-gl-esri-sources/docs/assets/images/icons@2x.png differ diff --git a/packages/mapbox-gl-esri-sources/docs/assets/images/widgets.png b/packages/mapbox-gl-esri-sources/docs/assets/images/widgets.png new file mode 100644 index 000000000..c7380532a Binary files /dev/null and b/packages/mapbox-gl-esri-sources/docs/assets/images/widgets.png differ diff --git a/packages/mapbox-gl-esri-sources/docs/assets/images/widgets@2x.png b/packages/mapbox-gl-esri-sources/docs/assets/images/widgets@2x.png new file mode 100644 index 000000000..4bbbd5727 Binary files /dev/null and b/packages/mapbox-gl-esri-sources/docs/assets/images/widgets@2x.png differ diff --git a/packages/mapbox-gl-esri-sources/docs/assets/js/main.js b/packages/mapbox-gl-esri-sources/docs/assets/js/main.js new file mode 100644 index 000000000..5fe710b2a --- /dev/null +++ b/packages/mapbox-gl-esri-sources/docs/assets/js/main.js @@ -0,0 +1 @@ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";function x(e){return null!=e&&e===e.window}var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0>10|55296,1023&r|56320)}function oe(){T()}var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,k="sizzle"+1*new Date,m=n.document,S=0,r=0,p=ue(),x=ue(),N=ue(),A=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){for((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;o--;)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){for(var n=e.split("|"),r=n.length;r--;)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){for(var n,r=a([],e.length,o),i=r.length;i--;)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&void 0!==e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if(void 0!==t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if(void 0!==t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];for(i=t.getElementsByName(e),r=0;o=i[r++];)if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"!==e)return o;for(;n=o[i++];)1===n.nodeType&&r.push(n);return r},b.find.CLASS=d.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);for(n=e;n=n.parentNode;)a.unshift(n);for(n=t;n=n.parentNode;)s.unshift(n);for(;a[r]===s[r];)r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"!=typeof e)return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this);if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};function ve(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v)for(n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return void 0!==k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;l--;)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){for(l=(t=(t||"").match(R)||[""]).length;l--;)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){for(f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;o--;)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
    ",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),i=("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,r.left):(a=parseFloat(o)||0,parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{for(t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position");)e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent;e&&"static"===k.css(e,"position");)e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 0":">",'"':""","'":"'","`":"`"},P=h.invert(L);h.escape=W(L),h.unescape=W(P),h.result=function(n,r,t){h.isArray(r)||(r=[r]);var e=r.length;if(!e)return h.isFunction(t)?t.call(n):t;for(var u=0;u/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};function $(n){return"\\"+U[n]}var J=/(.)^/,U={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},V=/\\|'|\r|\n|\u2028|\u2029/g;h.template=function(i,n,r){!n&&r&&(n=r),n=h.defaults({},n,h.templateSettings);var t,e=RegExp([(n.escape||J).source,(n.interpolate||J).source,(n.evaluate||J).source].join("|")+"|$","g"),o=0,a="__p+='";i.replace(e,function(n,r,t,e,u){return a+=i.slice(o,u).replace(V,$),o=u+n.length,r?a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":t?a+="'+\n((__t=("+t+"))==null?'':__t)+\n'":e&&(a+="';\n"+e+"\n__p+='"),n}),a+="';\n",n.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{t=new Function(n.variable||"obj","_",a)}catch(n){throw n.source=a,n}function u(n){return t.call(this,n,h)}var c=n.variable||"obj";return u.source="function("+c+"){\n"+a+"}",u},h.chain=function(n){var r=h(n);return r._chain=!0,r};function G(n,r){return n._chain?h(r).chain():r}h.mixin=function(t){return h.each(h.functions(t),function(n){var r=h[n]=t[n];h.prototype[n]=function(){var n=[this._wrapped];return u.apply(n,arguments),G(this,r.apply(h,n))}}),h},h.mixin(h),h.each(["pop","push","reverse","shift","sort","splice","unshift"],function(r){var t=e[r];h.prototype[r]=function(){var n=this._wrapped;return t.apply(n,arguments),"shift"!==r&&"splice"!==r||0!==n.length||delete n[0],G(this,n)}}),h.each(["concat","join","slice"],function(n){var r=e[n];h.prototype[n]=function(){return G(this,r.apply(this._wrapped,arguments))}}),h.prototype.value=function(){return this._wrapped},h.prototype.valueOf=h.prototype.toJSON=h.prototype.value,h.prototype.toString=function(){return String(this._wrapped)},"function"==typeof define&&define.amd&&define("underscore",[],function(){return h})}(),function(t){var e="object"==typeof self&&self.self===self&&self||"object"==typeof global&&global.global===global&&global;if("function"==typeof define&&define.amd)define(["underscore","jquery","exports"],function(i,n,r){e.Backbone=t(e,r,i,n)});else if("undefined"!=typeof exports){var n,i=require("underscore");try{n=require("jquery")}catch(r){}t(e,exports,i,n)}else e.Backbone=t(e,{},e._,e.jQuery||e.Zepto||e.ender||e.$)}(function(t,e,i,n){var r=t.Backbone,s=Array.prototype.slice;e.VERSION="1.4.0",e.$=n,e.noConflict=function(){return t.Backbone=r,this},e.emulateHTTP=!1,e.emulateJSON=!1;var h,a=e.Events={},o=/\s+/,u=function(t,e,n,r,s){var h,a=0;if(n&&"object"==typeof n){void 0!==r&&"context"in s&&void 0===s.context&&(s.context=r);for(h=i.keys(n);athis.length&&(r=this.length),r<0&&(r+=this.length+1);var g,m,s=[],a=[],o=[],h=[],u={},l=e.add,c=e.merge,f=e.remove,d=!1,v=this.comparator&&null==r&&!1!==e.sort,p=i.isString(this.comparator)?this.comparator:null;for(m=0;m=this.length)return e.QueryLexer.EOS;var t=this.str.charAt(this.pos);return this.pos+=1,t},e.QueryLexer.prototype.width=function(){return this.pos-this.start},e.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},e.QueryLexer.prototype.backup=function(){this.pos-=1},e.QueryLexer.prototype.acceptDigitRun=function(){for(var t,r;47<(r=(t=this.next()).charCodeAt(0))&&r<58;);t!=e.QueryLexer.EOS&&this.backup()},e.QueryLexer.prototype.more=function(){return this.pos=this.scrollTop||0===this.scrollTop,isShown!==this.showToolbar&&(this.toolbar.classList.toggle("tsd-page-toolbar--hide"),this.secondaryNav.classList.toggle("tsd-navigation--toolbar-hide")),this.lastY=this.scrollTop},Viewport}(typedoc.Events);typedoc.Viewport=Viewport,typedoc.registerService(Viewport,"viewport")}(typedoc||(typedoc={})),function(typedoc){typedoc.pointerDown="mousedown",typedoc.pointerMove="mousemove",typedoc.pointerUp="mouseup",typedoc.pointerDownPosition={x:0,y:0},typedoc.preventNextClick=!1,typedoc.isPointerDown=!1,typedoc.isPointerTouch=!1,typedoc.hasPointerMoved=!1,typedoc.isMobile=/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),typedoc.$html.addClass(typedoc.isMobile?"is-mobile":"not-mobile"),typedoc.isMobile&&"ontouchstart"in document.documentElement&&(typedoc.isPointerTouch=!0,typedoc.pointerDown="touchstart",typedoc.pointerMove="touchmove",typedoc.pointerUp="touchend"),typedoc.$document.on(typedoc.pointerDown,function(e){typedoc.isPointerDown=!0,typedoc.hasPointerMoved=!1;var t="touchstart"==typedoc.pointerDown?e.originalEvent.targetTouches[0]:e;typedoc.pointerDownPosition.y=t.pageY||0,typedoc.pointerDownPosition.x=t.pageX||0}).on(typedoc.pointerMove,function(e){if(typedoc.isPointerDown&&!typedoc.hasPointerMoved){var t="touchstart"==typedoc.pointerDown?e.originalEvent.targetTouches[0]:e,x=typedoc.pointerDownPosition.x-(t.pageX||0),y=typedoc.pointerDownPosition.y-(t.pageY||0);typedoc.hasPointerMoved=10scrollTop;)index-=1;for(;index"+match+""}),parent=row.parent||"";(parent=parent.replace(new RegExp(query,"i"),function(match){return""+match+""}))&&(name=''+parent+"."+name),$results.append('
  • '+name+"
  • ")}}}function setLoadingState(value){loadingState!=value&&($el.removeClass(SearchLoadingState[loadingState].toLowerCase()),loadingState=value,$el.addClass(SearchLoadingState[loadingState].toLowerCase()),value==SearchLoadingState.Ready&&updateResults())}function setHasFocus(value){hasFocus!=value&&(hasFocus=value,$el.toggleClass("has-focus"),value?(setQuery(""),$field.val("")):$field.val(query))}function setQuery(value){query=$.trim(value),updateResults()}function setCurrentResult(dir){var $current=$results.find(".current");if(0==$current.length)$results.find(1==dir?"li:first-child":"li:last-child").addClass("current");else{var $rel=1==dir?$current.next("li"):$current.prev("li");0<$rel.length&&($current.removeClass("current"),$rel.addClass("current"))}}function gotoCurrentResult(){var $current=$results.find(".current");0==$current.length&&($current=$results.find("li:first-child")),0<$current.length&&(window.location.href=$current.find("a").prop("href"),$field.blur())}$results.on("mousedown",function(){resultClicked=!0}).on("mouseup",function(){setHasFocus(resultClicked=!1)}),$field.on("focusin",function(){setHasFocus(!0),loadIndex()}).on("focusout",function(){resultClicked?resultClicked=!1:setTimeout(function(){return setHasFocus(!1)},100)}).on("input",function(){setQuery($.trim(($field.val()||"").toString()))}).on("keydown",function(e){13==e.keyCode||27==e.keyCode||38==e.keyCode||40==e.keyCode?(preventPress=!0,e.preventDefault(),13==e.keyCode?gotoCurrentResult():27==e.keyCode?$field.blur():38==e.keyCode?setCurrentResult(-1):40==e.keyCode&&setCurrentResult(1)):preventPress=!1}).on("keypress",function(e){preventPress&&e.preventDefault()}),$("body").on("keydown",function(e){e.altKey||e.ctrlKey||e.metaKey||!hasFocus&&47this.groups.length-1&&(index=this.groups.length-1),this.index!=index){var to=this.groups[index];if(-1 .tsd-signature");if(!($signatures.length<2)){this.$container=this.$el.siblings(".tsd-descriptions");var $descriptions=this.$container.find("> .tsd-description");this.groups=[],$signatures.each(function(index,el){_this.groups.push(new SignatureGroup($(el),$descriptions.eq(index)))})}},Signature.prototype.onClick=function(e){var _this=this;_(this.groups).forEach(function(group,index){group.$signature.is(e.currentTarget)&&_this.setIndex(index)})},Signature}(Backbone.View);typedoc.registerComponent(Signature,".tsd-signatures")}(typedoc||(typedoc={})),function(typedoc){var Toggle=function(_super){function Toggle(options){var _this=_super.call(this,options)||this;return _this.className=_this.$el.attr("data-toggle")||"",_this.$el.on(typedoc.pointerUp,function(e){return _this.onPointerUp(e)}),_this.$el.on("click",function(e){return e.preventDefault()}),typedoc.$document.on(typedoc.pointerDown,function(e){return _this.onDocumentPointerDown(e)}),typedoc.$document.on(typedoc.pointerUp,function(e){return _this.onDocumentPointerUp(e)}),_this}return __extends(Toggle,_super),Toggle.prototype.setActive=function(value){if(this.active!=value){this.active=value,typedoc.$html.toggleClass("has-"+this.className,value),this.$el.toggleClass("active",value);var transition=(this.active?"to-has-":"from-has-")+this.className;typedoc.$html.addClass(transition),setTimeout(function(){return typedoc.$html.removeClass(transition)},500)}},Toggle.prototype.onPointerUp=function(event){typedoc.hasPointerMoved||(this.setActive(!0),event.preventDefault())},Toggle.prototype.onDocumentPointerDown=function(e){if(this.active){var $path=$(e.target).parents().addBack();if($path.hasClass("col-menu"))return;if($path.hasClass("tsd-filter-group"))return;this.setActive(!1)}},Toggle.prototype.onDocumentPointerUp=function(e){var _this=this;if(!typedoc.hasPointerMoved&&this.active){var $path=$(e.target).parents().addBack();if($path.hasClass("col-menu")){var $link=$path.filter("a");if($link.length){var href=window.location.href;-1!=href.indexOf("#")&&(href=href.substr(0,href.indexOf("#"))),$link.prop("href").substr(0,href.length)==href&&setTimeout(function(){return _this.setActive(!1)},250)}}}},Toggle}(Backbone.View);typedoc.registerComponent(Toggle,"a[data-toggle]")}(typedoc||(typedoc={})),function(typedoc){typedoc.app=new typedoc.Application}(typedoc||(typedoc={})); \ No newline at end of file diff --git a/packages/mapbox-gl-esri-sources/docs/assets/js/search.js b/packages/mapbox-gl-esri-sources/docs/assets/js/search.js new file mode 100644 index 000000000..6bd4ba21c --- /dev/null +++ b/packages/mapbox-gl-esri-sources/docs/assets/js/search.js @@ -0,0 +1,3 @@ +var typedoc = typedoc || {}; + typedoc.search = typedoc.search || {}; + typedoc.search.data = {"kinds":{"64":"Function","128":"Class","256":"Interface","512":"Constructor","1024":"Property","2048":"Method"},"rows":[{"id":0,"kind":256,"name":"SublayerState","url":"interfaces/sublayerstate.html","classes":"tsd-kind-interface"},{"id":1,"kind":1024,"name":"sublayer","url":"interfaces/sublayerstate.html#sublayer","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"SublayerState"},{"id":2,"kind":1024,"name":"opacity","url":"interfaces/sublayerstate.html#opacity","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"SublayerState"},{"id":3,"kind":256,"name":"ArcGISDynamicMapServiceOptions","url":"interfaces/arcgisdynamicmapserviceoptions.html","classes":"tsd-kind-interface"},{"id":4,"kind":1024,"name":"useDevicePixelRatio","url":"interfaces/arcgisdynamicmapserviceoptions.html#usedevicepixelratio","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"ArcGISDynamicMapServiceOptions"},{"id":5,"kind":1024,"name":"layers","url":"interfaces/arcgisdynamicmapserviceoptions.html#layers","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"ArcGISDynamicMapServiceOptions"},{"id":6,"kind":1024,"name":"supportsDynamicLayers","url":"interfaces/arcgisdynamicmapserviceoptions.html#supportsdynamiclayers","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"ArcGISDynamicMapServiceOptions"},{"id":7,"kind":1024,"name":"queryParameters","url":"interfaces/arcgisdynamicmapserviceoptions.html#queryparameters","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"ArcGISDynamicMapServiceOptions"},{"id":8,"kind":128,"name":"ArcGISDynamicMapService","url":"classes/arcgisdynamicmapservice.html","classes":"tsd-kind-class"},{"id":9,"kind":1024,"name":"id","url":"classes/arcgisdynamicmapservice.html#id","classes":"tsd-kind-property tsd-parent-kind-class","parent":"ArcGISDynamicMapService"},{"id":10,"kind":512,"name":"constructor","url":"classes/arcgisdynamicmapservice.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class","parent":"ArcGISDynamicMapService"},{"id":11,"kind":2048,"name":"destroy","url":"classes/arcgisdynamicmapservice.html#destroy","classes":"tsd-kind-method tsd-parent-kind-class","parent":"ArcGISDynamicMapService"},{"id":12,"kind":2048,"name":"updateLayers","url":"classes/arcgisdynamicmapservice.html#updatelayers","classes":"tsd-kind-method tsd-parent-kind-class","parent":"ArcGISDynamicMapService"},{"id":13,"kind":2048,"name":"updateQueryParameters","url":"classes/arcgisdynamicmapservice.html#updatequeryparameters","classes":"tsd-kind-method tsd-parent-kind-class","parent":"ArcGISDynamicMapService"},{"id":14,"kind":2048,"name":"updateUseDevicePixelRatio","url":"classes/arcgisdynamicmapservice.html#updateusedevicepixelratio","classes":"tsd-kind-method tsd-parent-kind-class","parent":"ArcGISDynamicMapService"},{"id":15,"kind":256,"name":"ArcGISVectorSourceOptions","url":"interfaces/arcgisvectorsourceoptions.html","classes":"tsd-kind-interface"},{"id":16,"kind":1024,"name":"displayIncompleteFeatureCollections","url":"interfaces/arcgisvectorsourceoptions.html#displayincompletefeaturecollections","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"ArcGISVectorSourceOptions"},{"id":17,"kind":1024,"name":"geometryPrecision","url":"interfaces/arcgisvectorsourceoptions.html#geometryprecision","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"ArcGISVectorSourceOptions"},{"id":18,"kind":1024,"name":"supportsPagination","url":"interfaces/arcgisvectorsourceoptions.html#supportspagination","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"ArcGISVectorSourceOptions"},{"id":19,"kind":1024,"name":"outFields","url":"interfaces/arcgisvectorsourceoptions.html#outfields","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"ArcGISVectorSourceOptions"},{"id":20,"kind":128,"name":"ArcGISVectorSource","url":"classes/arcgisvectorsource.html","classes":"tsd-kind-class"},{"id":21,"kind":1024,"name":"source","url":"classes/arcgisvectorsource.html#source","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-protected","parent":"ArcGISVectorSource"},{"id":22,"kind":1024,"name":"id","url":"classes/arcgisvectorsource.html#id","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-protected","parent":"ArcGISVectorSource"},{"id":23,"kind":512,"name":"constructor","url":"classes/arcgisvectorsource.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class","parent":"ArcGISVectorSource"},{"id":24,"kind":256,"name":"Image","url":"interfaces/image.html","classes":"tsd-kind-interface"},{"id":25,"kind":1024,"name":"pixelRatio","url":"interfaces/image.html#pixelratio","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"Image"},{"id":26,"kind":1024,"name":"dataURI","url":"interfaces/image.html#datauri","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"Image"},{"id":27,"kind":1024,"name":"width","url":"interfaces/image.html#width","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"Image"},{"id":28,"kind":1024,"name":"height","url":"interfaces/image.html#height","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"Image"},{"id":29,"kind":256,"name":"ImageSet","url":"interfaces/imageset.html","classes":"tsd-kind-interface"},{"id":30,"kind":1024,"name":"id","url":"interfaces/imageset.html#id","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"ImageSet"},{"id":31,"kind":1024,"name":"images","url":"interfaces/imageset.html#images","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"ImageSet"},{"id":32,"kind":128,"name":"ImageList","url":"classes/imagelist.html","classes":"tsd-kind-class"},{"id":33,"kind":512,"name":"constructor","url":"classes/imagelist.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class","parent":"ImageList"},{"id":34,"kind":2048,"name":"addToMap","url":"classes/imagelist.html#addtomap","classes":"tsd-kind-method tsd-parent-kind-class","parent":"ImageList"},{"id":35,"kind":2048,"name":"removeFromMap","url":"classes/imagelist.html#removefrommap","classes":"tsd-kind-method tsd-parent-kind-class","parent":"ImageList"},{"id":36,"kind":64,"name":"styleForFeatureLayer","url":"globals.html#styleforfeaturelayer","classes":"tsd-kind-function"}]}; \ No newline at end of file diff --git a/packages/mapbox-gl-esri-sources/docs/classes/arcgisdynamicmapservice.html b/packages/mapbox-gl-esri-sources/docs/classes/arcgisdynamicmapservice.html new file mode 100644 index 000000000..88ac0613f --- /dev/null +++ b/packages/mapbox-gl-esri-sources/docs/classes/arcgisdynamicmapservice.html @@ -0,0 +1,2769 @@ + + + + + + ArcGISDynamicMapService | @seasketch/mapbox-gl-esri-sources + + + + + +
    +
    +
    +
    + +
    +
    + Options +
    +
    + All +
      +
    • Public
    • +
    • Public/Protected
    • +
    • All
    • +
    +
    + + + + + + +
    +
    + Menu +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +

    Add an Esri Dynamic Map Service as an image source to a MapBox GL JS map, and + use the included methods to update visible sublayers, set layer order and + opacity, support high-dpi screens, and transparently deal with issues related + to crossing the central meridian.

    +
    +
    import { ArcGISDynamicMapService } from "mapbox-gl-esri-sources";
    +
    +// ... setup your map
    +
    +const populatedPlaces = new ArcGISDynamicMapService(
    +  map,
    +  "populated-places-source",
    +  "https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer", {
    +    supportsDynamicLayers: true,
    +    sublayers: [
    +      { sublayer: 0, opacity: 1 },
    +      { sublayer: 1, opacity: 1 },
    +      { sublayer: 2, opacity: 0.5 },
    +    ],
    +    queryParameters: {
    +      format: 'png32'
    +    }
    +  }
    +});
    +
    +// Don't forget to add a layer to reference your source
    +map.addLayer({
    +  id: "ags-layer",
    +  type: "raster",
    +  source: populatedPlaces.id,
    +  paint: {
    +    "raster-fade-duration": 0,
    +    "raster-opacity": 0.9
    +  },
    +});
    +
    +// turn off the third sublayer and update opacity
    +populatedPlaces.updateLayers([
    +  { sublayer: 0, opacity: 0.5 },
    +  { sublayer: 1, opacity: 1 },
    +]);
    +
    +// disable high-dpi screen support
    +populatedPlaces.updateUseDevicePixelRatio(false);
    +
    +
    class
    +

    ArcGISDynamicMapService

    +
    +
    +
    +
    +
    +

    Hierarchy

    +
      +
    • + ArcGISDynamicMapService +
    • +
    +
    +
    +

    Index

    +
    +
    +
    +

    Constructors

    + +
    +
    +

    Properties

    + +
    +
    +

    Methods

    + +
    +
    +
    +
    +
    +

    Constructors

    +
    + +

    constructor

    + +
      +
    • + +
      +
      +

      Parameters

      +
        +
      • +
        map: Map
        +
        +
        +

        MapBox GL JS Map instance

        +
        +
        +
      • +
      • +
        id: string
        +
        +
        +

        ID to be used when adding refering to this source from layers

        +
        +
        +
      • +
      • +
        baseUrl: string
        +
        +
        +

        Location of the service. Should end in /MapServer

        +
        +
        +
      • +
      • +
        Optional options: ArcGISDynamicMapServiceOptions
        +
      • +
      +

      Returns ArcGISDynamicMapService

      +
    • +
    +
    +
    +
    +

    Properties

    +
    + +

    id

    +
    id: string
    + +
    +
    +

    Source id used in the map style

    +
    +
    +
    +
    +
    +

    Methods

    +
    + +

    destroy

    +
      +
    • destroy(): void
    • +
    +
      +
    • + +
      +
      +

      Clears all map event listeners setup by this instance.

      +
      +
      +

      Returns void

      +
    • +
    +
    +
    + +

    updateLayers

    + +
      +
    • + +
      +
      +

      Update the list of sublayers and re-render the the map. If + supportsDynamicLayers is enabled, sublayer order and opacity will be + respected.

      +
      +
      // reverses layer rendering order and sets one sublayer to 50% transparency
      +mapService.updateLayers([
      +  { sublayer: 1, opacity: 0.5 },
      +  { sublayer: 0, opacity: 1 }
      +]);
      +
      +

      Parameters

      +
        +
      • +
        layers: SublayerState[]
        +
        +

        SublayerState is an array of objects with sublayer and + optional opacity props.

        +
        +
      • +
      +

      Returns void

      +
    • +
    +
    +
    + +

    updateQueryParameters

    +
      +
    • updateQueryParameters(queryParameters: {}): void
    • +
    +
      +
    • + +
      +
      +

      Update query params sent with each export request and re-render the map. A + list of supported parameters can be found in the Esri REST API docs. + Query parameters will override any values set by this library, such as + format, dpi, size, and bbox.

      +
      +
      
      +mapServiceSource.updateQueryParameters({
      + format: 'png32',
      + // visualize temporal datasets!
      + historicMoment: slider.value
      +})
      +
      +
      +

      Parameters

      +
        +
      • +
        queryParameters: {}
        +
          +
        • +
          [queryString: string]: string | number
          +
        • +
        +
      • +
      +

      Returns void

      +
    • +
    +
    +
    + +

    updateUseDevicePixelRatio

    +
      +
    • updateUseDevicePixelRatio(enable: boolean): void
    • +
    +
      +
    • + +
      +
      +

      Update support for adjusting image resolution based on devicePixelRatio and + re-render the map. Useful for giving users the option to toggle + high-resolution images depending on network conditions.

      +
      +
      +

      Parameters

      +
        +
      • +
        enable: boolean
        +
        +
        +
      • +
      +

      Returns void

      +
    • +
    +
    +
    +
    +
    +

    Legend

    +
    +
      +
    • Module
    • +
    • Object literal
    • +
    • Variable
    • +
    • Function
    • +
    • Function with type parameter
    • +
    • Index signature
    • +
    • Type alias
    • +
    • Type alias with type parameter
    • +
    +
      +
    • Enumeration
    • +
    • Enumeration member
    • +
    • Property
    • +
    • Method
    • +
    +
      +
    • Interface
    • +
    • Interface with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Index signature
    • +
    +
      +
    • Class
    • +
    • Class with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Accessor
    • +
    • Index signature
    • +
    +
      +
    • Inherited constructor
    • +
    • Inherited property
    • +
    • Inherited method
    • +
    • Inherited accessor
    • +
    +
      +
    • Protected property
    • +
    • Protected method
    • +
    • Protected accessor
    • +
    +
      +
    • Private property
    • +
    • Private method
    • +
    • Private accessor
    • +
    +
      +
    • Static property
    • +
    • Static method
    • +
    +
    +
    +
    +
    +

    Generated using TypeDoc

    +
    +
    +
    + + + \ No newline at end of file diff --git a/packages/mapbox-gl-esri-sources/docs/classes/arcgisvectorsource.html b/packages/mapbox-gl-esri-sources/docs/classes/arcgisvectorsource.html new file mode 100644 index 000000000..e8a7717c9 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/docs/classes/arcgisvectorsource.html @@ -0,0 +1,2617 @@ + + + + + + ArcGISVectorSource | @seasketch/mapbox-gl-esri-sources + + + + + +
    +
    +
    +
    + +
    +
    + Options +
    +
    + All +
      +
    • Public
    • +
    • Public/Protected
    • +
    • All
    • +
    +
    + + + + + + +
    +
    + Menu +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +

    Add ArcGIS Feature Layers to MapBox GL JS maps as a geojson source. These + data sources can be styled using output from + or custom layers that + reference the provided source id.

    +
    + +

    Usage

    +
    +
    import { ArcGISVectorSource } from "mapbox-gl-esri-sources";
    +
    +// setup map...
    +
    +const esriSource = new ArcGISVectorSource(
    +  map,
    +  'cities-source-id',
    +  "https://sampleserver6.arcgisonline.com/arcgis/rest/services/SampleWorldCities/MapServer/0"),
    +  {
    +    geometryPrecision: 5,
    +    outFields: "POP,CITY_NAME"
    +  }
    +);
    +
    +
    class
    +

    ArcGISVectorSource

    +
    +
    +
    +
    +
    +

    Hierarchy

    +
      +
    • + ArcGISVectorSource +
    • +
    +
    +
    +

    Index

    +
    +
    +
    +

    Constructors

    + +
    +
    +

    Properties

    + +
    +
    +
    +
    +
    +

    Constructors

    +
    + +

    constructor

    + +
      +
    • + +
      +
      +

      Creates an instance of ArcGISVectorSource.

      +
      +
      +

      Parameters

      +
        +
      • +
        map: Map
        +
        +
        +

        MapBox GL JS map instance where source will be added

        +
        +
        +
      • +
      • +
        id: string
        +
        +
        +

        ID will be assigned to the GeoJSONSource instance

        +
        +
        +
      • +
      • +
        url: string
        +
        +
        +

        Base url for an ArcGIS Server Feature Layer. Should end in /MapServer/0..n

        +
        +
        +
      • +
      • +
        Optional options: ArcGISVectorSourceOptions
        +
      • +
      +

      Returns ArcGISVectorSource

      +
    • +
    +
    +
    +
    +

    Properties

    +
    + +

    Protected id

    +
    id: string
    + +
    +
    + +

    Protected source

    +
    source: GeoJSONSource
    + +
    +
    +

    GeoJSONSource instance added to the map

    +
    +
    +
    type
    +

    {GeoJSONSource}

    +
    +
    +
    +
    +
    +
    +
    +

    Legend

    +
    +
      +
    • Module
    • +
    • Object literal
    • +
    • Variable
    • +
    • Function
    • +
    • Function with type parameter
    • +
    • Index signature
    • +
    • Type alias
    • +
    • Type alias with type parameter
    • +
    +
      +
    • Enumeration
    • +
    • Enumeration member
    • +
    • Property
    • +
    • Method
    • +
    +
      +
    • Interface
    • +
    • Interface with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Index signature
    • +
    +
      +
    • Class
    • +
    • Class with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Accessor
    • +
    • Index signature
    • +
    +
      +
    • Inherited constructor
    • +
    • Inherited property
    • +
    • Inherited method
    • +
    • Inherited accessor
    • +
    +
      +
    • Protected property
    • +
    • Protected method
    • +
    • Protected accessor
    • +
    +
      +
    • Private property
    • +
    • Private method
    • +
    • Private accessor
    • +
    +
      +
    • Static property
    • +
    • Static method
    • +
    +
    +
    +
    +
    +

    Generated using TypeDoc

    +
    +
    +
    + + + \ No newline at end of file diff --git a/packages/mapbox-gl-esri-sources/docs/classes/imagelist.html b/packages/mapbox-gl-esri-sources/docs/classes/imagelist.html new file mode 100644 index 000000000..b12962a96 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/docs/classes/imagelist.html @@ -0,0 +1,2600 @@ + + + + + + ImageList | @seasketch/mapbox-gl-esri-sources + + + + + +
    +
    +
    +
    + +
    +
    + Options +
    +
    + All +
      +
    • Public
    • +
    • Public/Protected
    • +
    • All
    • +
    +
    + + + + + + +
    +
    + Menu +
    +
    +
    +
    +
    + +
    +
    +
    +

    Hierarchy

    +
      +
    • + ImageList +
    • +
    +
    +
    +

    Index

    +
    +
    +
    +

    Constructors

    + +
    +
    +

    Methods

    + +
    +
    +
    +
    +
    +

    Constructors

    +
    + +

    constructor

    +
      +
    • new ImageList(arcGISVersion?: undefined | number): ImageList
    • +
    +
      +
    • + +

      Parameters

      +
        +
      • +
        Optional arcGISVersion: undefined | number
        +
      • +
      +

      Returns ImageList

      +
    • +
    +
    +
    +
    +

    Methods

    +
    + +

    addToMap

    +
      +
    • addToMap(map: Map): Promise<void[]>
    • +
    +
      +
    • + +
      +
      +

      Add all images to a MapBox GL JS map instance so that they may be used in + style layers. Call before adding layers created by styleForFeatureLayer.

      +
      +

      The ImageList may contain multiple copies of images at different dpi. Since + MapBox GL does not currently support adding images at multiple resolutions + this function will pick those that best match the current devicePixelRatio. + If the devicePixelRatio changes (e.g. switching monitors), the images + will not be updated and may be at a less than ideal resolution, though + mapbox gl will still show them at the correct size.

      +
      +
      memberof
      +

      ImageList

      +
      +
      +
      +

      Parameters

      +
        +
      • +
        map: Map
        +
      • +
      +

      Returns Promise<void[]>

      +
    • +
    +
    +
    + +

    removeFromMap

    +
      +
    • removeFromMap(map: Map): Promise<void[]>
    • +
    +
      +
    • + +
      +
      +

      Remove a previously added ImageList from the map

      +
      +
      +
      memberof
      +

      ImageList

      +
      +
      +
      +

      Parameters

      +
        +
      • +
        map: Map
        +
      • +
      +

      Returns Promise<void[]>

      +
    • +
    +
    +
    +
    +
    +

    Legend

    +
    +
      +
    • Module
    • +
    • Object literal
    • +
    • Variable
    • +
    • Function
    • +
    • Function with type parameter
    • +
    • Index signature
    • +
    • Type alias
    • +
    • Type alias with type parameter
    • +
    +
      +
    • Enumeration
    • +
    • Enumeration member
    • +
    • Property
    • +
    • Method
    • +
    +
      +
    • Interface
    • +
    • Interface with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Index signature
    • +
    +
      +
    • Class
    • +
    • Class with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Accessor
    • +
    • Index signature
    • +
    +
      +
    • Inherited constructor
    • +
    • Inherited property
    • +
    • Inherited method
    • +
    • Inherited accessor
    • +
    +
      +
    • Protected property
    • +
    • Protected method
    • +
    • Protected accessor
    • +
    +
      +
    • Private property
    • +
    • Private method
    • +
    • Private accessor
    • +
    +
      +
    • Static property
    • +
    • Static method
    • +
    +
    +
    +
    +
    +

    Generated using TypeDoc

    +
    +
    +
    + + + \ No newline at end of file diff --git a/packages/mapbox-gl-esri-sources/docs/globals.html b/packages/mapbox-gl-esri-sources/docs/globals.html new file mode 100644 index 000000000..287beb4b7 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/docs/globals.html @@ -0,0 +1,2691 @@ + + + + + + @seasketch/mapbox-gl-esri-sources | @seasketch/mapbox-gl-esri-sources + + + + + +
    +
    +
    +
    + +
    +
    + Options +
    +
    + All +
      +
    • Public
    • +
    • Public/Protected
    • +
    • All
    • +
    +
    + + + + + + +
    +
    + Menu +
    +
    +
    +
    +
    + +
    +
    +
    +

    Easily add Esri map services or vector layers to MapBox GL JS, maintaining a faithful reproduction of cartographic style. In many cases services can render with greater graphical fidelity and performance than in the Esri JS API or ArcGIS Online.

    + vector layers demonstration in mapbox + +

    Examples

    +
    + + +

    ArcGIS Tiled Map Services

    +
    +

    Tiled services host pre-seeded sets of image tiles referenced using z/y/z urls similar to WMTS or TMS layers. You actually don't need this module to load tiled services, but since you are here I'll show you how to do it!

    +

    Anacapa Island Tiled Service Example

    +
    map.on("load", () => {
    +  // add the image source
    +  map.addSource("tile-layer", {
    +    type: "raster",
    +    tiles: [
    +      // Be sure to reference the /tile/ endpoint with appropriate template vars
    +      "https://tiles.arcgis.com/tiles/4TXrdeWh0RyCqPgB/arcgis/rest/services/Anacapa_Island/MapServer/tile/{z}/{y}/{x}",
    +    ],
    +    // Most services have tile sizes of 256. You may use this division trick to
    +    // support higher resolution screens, though watch out for missing tile
    +    // images at higher zoom levels.
    +    tileSize: 256 / window.devicePixelRatio,
    +    // min/max zoom can be based on service metadata
    +    minZoom: 0,
    +    maxZoom: 23,
    +    // MapService metadata also has a "Full Extent", which you can convert to
    +    // degrees and avoid 404 errors from out of bounds requests
    +    bounds: [
    +      -119.45627340923632,
    +      33.9923034787097,
    +      -119.33632759039419,
    +      34.028212713477615,
    +    ],
    +    // More options are detailed in:
    +    // https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/#raster-tiles
    +  });
    +  // now add a layer that references this source
    +  map.addLayer({
    +    id: "esri-tiles",
    +    source: "tile-layer",
    +    type: "raster",
    +    // see https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#raster
    +    // you can control opacity, saturation, and other rendering aspects
    +  });
    +});
    + +

    ArcGIS Dynamic Map Services

    +
    +

    Dynamic Map Services are more difficult to support without this module. These services work similar to older WMS where it's expected that images representing the entire viewport are requested. mapbox-gl-esri-sources provides an ArcGISDynamicService class which will listen to map viewport change events and update this image. It also provides methods to update sublayer visibility, order and opacity.

    +

    By default images will be requested in a resolution that matches the client's devicePixelRatio for higher quality maps on high-dpi devices.

    +
    import { ArcGISDynamicMapService } from "mapbox-gl-esri-sources";
    +
    + // ... setup your map
    + map.on("load", () => {
    +   const populatedPlaces = new ArcGISDynamicMapService(
    +    map,
    +    "populated-places-source",
    +    "https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer", {
    +      supportsDynamicLayers: true,
    +      sublayers: [
    +        { sublayer: 0, opacity: 1 },
    +        { sublayer: 1, opacity: 1 },
    +        { sublayer: 2, opacity: 0.5 },
    +      ],
    +      queryParameters: {
    +        format: 'png32'
    +      }
    +    }
    +  });
    +  // Don't forget to add a layer to reference your source
    +  map.addLayer({
    +    id: "ags-layer",
    +    type: "raster",
    +    source: populatedPlaces.id,
    +    paint: {
    +      // fading looks weird on non-tiled images
    +      "raster-fade-duration": 0,
    +    },
    +  });
    +  // turn off the third sublayer and update opacity
    +  populatedPlaces.updateLayers([
    +    { sublayer: 0, opacity: 0.5 },
    +    { sublayer: 1, opacity: 1 },
    +  ]);
    +});
    +

    API Documentation

    +

    Dynamic Map Service Examples

    + +

    ArcGIS Feature Layers

    +
    +

    Both Esri map and feature services typically support querying vector data through a "feature layer" endpoint and most often this is the best way to display data from ArcGIS in MapBox GL. This module provides both an ArcGISVectorSource class for loading the entire dataset as a GeoJSON source, and the styleForFeatureLayer function which will faithfully translate renderer information from the ArcGIS REST API into GL Style.

    +
    import {
    +  ArcGISVectorSource,
    +  styleForFeatureLayer,
    +} from "mapbox-gl-esri-sources";
    +
    +// setup map...
    +
    +map.on("load", () => {
    +
    +  const { imageList, layers } = styleForFeatureLayer(
    +    "https://sampleserver6.arcgisonline.com/arcgis/rest/services/SampleWorldCities/MapServer/0",
    +    "cities-source-id"
    +  );
    +
    +  const esriSource = new ArcGISVectorSource(
    +    map,
    +    'cities-source-id',
    +    "https://sampleserver6.arcgisonline.com/arcgis/rest/services/SampleWorldCities/MapServer/0"),
    +  );
    +
    +  imageList.addToMap(map);
    +
    +  for (const layer of layers) {
    +    map.addLayer(layer);
    +  }
    +});
    +

    The process of generating images and styles is seperate from loading the vector source to provide more flexibility. Rather than just dynamically loading style and data together, you might generate and cache style information so that it doesn't have to be created on each map load. You could also adjust layers as needed, or even generate vector tiles from complex services using tippecanoe and use the same layers with this new derivative source.

    +

    Vector Layer Examples

    +

    ArcGISVectorSource API | styleForFeatureLayer API

    +
    +
    +

    Index

    +
    + +
    +
    +
    +

    Functions

    +
    + +

    styleForFeatureLayer

    +
      +
    • styleForFeatureLayer(url: string, sourceId: string): Promise<{ imageList: ImageList; layers: Layer[] }>
    • +
    +
      +
    • + +
      +
      +

      This function retrieves rendering and style information from the ArcGIS REST + API for a given Feature Layer + and produces images and style layers that can be used to faithfully represent + these services as vectors in MapBox GL. It can be used in conjunction with + ArcGISVectorSource.

      +
      +

      Style generation is seperated from source handling so that you could even + use tippecanoe or other tools to generate vector tiles from a service and + style them using the generated layers. With this seperation of concerns it's + also possible to cache style information so that it does not need to + always be generated dynamically.

      + +

      Usage

      +
      +
      import { ArcGISVectorSource, styleForFeatureLayer } from "mapbox-gl-esri-sources";
      +
      +// setup map...
      +// add source...
      +
      +const { imageList, layers } = styleForFeatureLayer(
      +  "https://sampleserver6.arcgisonline.com/arcgis/rest/services/SampleWorldCities/MapServer/0",
      +  "cities-source-id"
      +);
      +
      +imageList.addToMap(map);
      +
      +for (const layer of layers) {
      +  map.addLayer(layer);
      +}
      +
      +
      +

      Parameters

      +
        +
      • +
        url: string
        +
        +

        Feature layer endpoint. Should terminate in /MapServer/0..n

        +
        +
      • +
      • +
        sourceId: string
        +
        +

        ID for the source of vector data to be used in rendering.

        +
        +
      • +
      +

      Returns Promise<{ imageList: ImageList; layers: Layer[] }>

      +

      The ImageList.addToMap function should be called before adding the generated layers to the map.

      +
    • +
    +
    +
    +
    +
    +

    Legend

    +
    +
      +
    • Module
    • +
    • Object literal
    • +
    • Variable
    • +
    • Function
    • +
    • Function with type parameter
    • +
    • Index signature
    • +
    • Type alias
    • +
    • Type alias with type parameter
    • +
    +
      +
    • Enumeration
    • +
    • Enumeration member
    • +
    • Property
    • +
    • Method
    • +
    +
      +
    • Interface
    • +
    • Interface with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Index signature
    • +
    +
      +
    • Class
    • +
    • Class with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Accessor
    • +
    • Index signature
    • +
    +
      +
    • Inherited constructor
    • +
    • Inherited property
    • +
    • Inherited method
    • +
    • Inherited accessor
    • +
    +
      +
    • Protected property
    • +
    • Protected method
    • +
    • Protected accessor
    • +
    +
      +
    • Private property
    • +
    • Private method
    • +
    • Private accessor
    • +
    +
      +
    • Static property
    • +
    • Static method
    • +
    +
    +
    +
    +
    +

    Generated using TypeDoc

    +
    +
    +
    + + + \ No newline at end of file diff --git a/packages/mapbox-gl-esri-sources/docs/index.html b/packages/mapbox-gl-esri-sources/docs/index.html new file mode 100644 index 000000000..c3dca8878 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/docs/index.html @@ -0,0 +1,2692 @@ + + + + + + @seasketch/mapbox-gl-esri-sources | @seasketch/mapbox-gl-esri-sources + + + + + +
    +
    +
    +
    + +
    +
    + Options +
    +
    + All +
      +
    • Public
    • +
    • Public/Protected
    • +
    • All
    • +
    +
    + + + + + + +
    +
    + Menu +
    +
    +
    +
    +
    + +
    +
    +
    +

    Easily add Esri map services or vector layers to MapBox GL JS, maintaining a faithful reproduction of cartographic style. In many cases services can render with greater graphical fidelity and performance than in the Esri JS API or ArcGIS Online.

    + vector layers demonstration in mapbox + +

    Examples

    +
    + + +

    ArcGIS Tiled Map Services

    +
    +

    Tiled services host pre-seeded sets of image tiles referenced using z/y/z urls similar to WMTS or TMS layers. You actually don't need this module to load tiled services, but since you are here I'll show you how to do it!

    +

    Anacapa Island Tiled Service Example

    +
    map.on("load", () => {
    +  // add the image source
    +  map.addSource("tile-layer", {
    +    type: "raster",
    +    tiles: [
    +      // Be sure to reference the /tile/ endpoint with appropriate template vars
    +      "https://tiles.arcgis.com/tiles/4TXrdeWh0RyCqPgB/arcgis/rest/services/Anacapa_Island/MapServer/tile/{z}/{y}/{x}",
    +    ],
    +    // Most services have tile sizes of 256. You may use this division trick to
    +    // support higher resolution screens, though watch out for missing tile
    +    // images at higher zoom levels.
    +    tileSize: 256 / window.devicePixelRatio,
    +    // min/max zoom can be based on service metadata
    +    minZoom: 0,
    +    maxZoom: 23,
    +    // MapService metadata also has a "Full Extent", which you can convert to
    +    // degrees and avoid 404 errors from out of bounds requests
    +    bounds: [
    +      -119.45627340923632,
    +      33.9923034787097,
    +      -119.33632759039419,
    +      34.028212713477615,
    +    ],
    +    // More options are detailed in:
    +    // https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/#raster-tiles
    +  });
    +  // now add a layer that references this source
    +  map.addLayer({
    +    id: "esri-tiles",
    +    source: "tile-layer",
    +    type: "raster",
    +    // see https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#raster
    +    // you can control opacity, saturation, and other rendering aspects
    +  });
    +});
    + +

    ArcGIS Dynamic Map Services

    +
    +

    Dynamic Map Services are more difficult to support without this module. These services work similar to older WMS where it's expected that images representing the entire viewport are requested. mapbox-gl-esri-sources provides an ArcGISDynamicService class which will listen to map viewport change events and update this image. It also provides methods to update sublayer visibility, order and opacity.

    +

    By default images will be requested in a resolution that matches the client's devicePixelRatio for higher quality maps on high-dpi devices.

    +
    import { ArcGISDynamicMapService } from "mapbox-gl-esri-sources";
    +
    + // ... setup your map
    + map.on("load", () => {
    +   const populatedPlaces = new ArcGISDynamicMapService(
    +    map,
    +    "populated-places-source",
    +    "https://sampleserver6.arcgisonline.com/arcgis/rest/services/USA/MapServer", {
    +      supportsDynamicLayers: true,
    +      sublayers: [
    +        { sublayer: 0, opacity: 1 },
    +        { sublayer: 1, opacity: 1 },
    +        { sublayer: 2, opacity: 0.5 },
    +      ],
    +      queryParameters: {
    +        format: 'png32'
    +      }
    +    }
    +  });
    +  // Don't forget to add a layer to reference your source
    +  map.addLayer({
    +    id: "ags-layer",
    +    type: "raster",
    +    source: populatedPlaces.id,
    +    paint: {
    +      // fading looks weird on non-tiled images
    +      "raster-fade-duration": 0,
    +    },
    +  });
    +  // turn off the third sublayer and update opacity
    +  populatedPlaces.updateLayers([
    +    { sublayer: 0, opacity: 0.5 },
    +    { sublayer: 1, opacity: 1 },
    +  ]);
    +});
    +

    API Documentation

    +

    Dynamic Map Service Examples

    + +

    ArcGIS Feature Layers

    +
    +

    Both Esri map and feature services typically support querying vector data through a "feature layer" endpoint and most often this is the best way to display data from ArcGIS in MapBox GL. This module provides both an ArcGISVectorSource class for loading the entire dataset as a GeoJSON source, and the styleForFeatureLayer function which will faithfully translate renderer information from the ArcGIS REST API into GL Style.

    +
    import {
    +  ArcGISVectorSource,
    +  styleForFeatureLayer,
    +} from "mapbox-gl-esri-sources";
    +
    +// setup map...
    +
    +map.on("load", () => {
    +
    +  const { imageList, layers } = styleForFeatureLayer(
    +    "https://sampleserver6.arcgisonline.com/arcgis/rest/services/SampleWorldCities/MapServer/0",
    +    "cities-source-id"
    +  );
    +
    +  const esriSource = new ArcGISVectorSource(
    +    map,
    +    'cities-source-id',
    +    "https://sampleserver6.arcgisonline.com/arcgis/rest/services/SampleWorldCities/MapServer/0"),
    +  );
    +
    +  imageList.addToMap(map);
    +
    +  for (const layer of layers) {
    +    map.addLayer(layer);
    +  }
    +});
    +

    The process of generating images and styles is seperate from loading the vector source to provide more flexibility. Rather than just dynamically loading style and data together, you might generate and cache style information so that it doesn't have to be created on each map load. You could also adjust layers as needed, or even generate vector tiles from complex services using tippecanoe and use the same layers with this new derivative source.

    +

    Vector Layer Examples

    +

    ArcGISVectorSource API | styleForFeatureLayer API

    +
    +
    +
    +

    Index

    +
    + +
    +
    +
    +

    Functions

    +
    + +

    styleForFeatureLayer

    +
      +
    • styleForFeatureLayer(url: string, sourceId: string): Promise<{ imageList: ImageList; layers: Layer[] }>
    • +
    +
      +
    • + +
      +
      +

      This function retrieves rendering and style information from the ArcGIS REST + API for a given Feature Layer + and produces images and style layers that can be used to faithfully represent + these services as vectors in MapBox GL. It can be used in conjunction with + ArcGISVectorSource.

      +
      +

      Style generation is seperated from source handling so that you could even + use tippecanoe or other tools to generate vector tiles from a service and + style them using the generated layers. With this seperation of concerns it's + also possible to cache style information so that it does not need to + always be generated dynamically.

      + +

      Usage

      +
      +
      import { ArcGISVectorSource, styleForFeatureLayer } from "mapbox-gl-esri-sources";
      +
      +// setup map...
      +// add source...
      +
      +const { imageList, layers } = styleForFeatureLayer(
      +  "https://sampleserver6.arcgisonline.com/arcgis/rest/services/SampleWorldCities/MapServer/0",
      +  "cities-source-id"
      +);
      +
      +imageList.addToMap(map);
      +
      +for (const layer of layers) {
      +  map.addLayer(layer);
      +}
      +
      +
      +

      Parameters

      +
        +
      • +
        url: string
        +
        +

        Feature layer endpoint. Should terminate in /MapServer/0..n

        +
        +
      • +
      • +
        sourceId: string
        +
        +

        ID for the source of vector data to be used in rendering.

        +
        +
      • +
      +

      Returns Promise<{ imageList: ImageList; layers: Layer[] }>

      +

      The ImageList.addToMap function should be called before adding the generated layers to the map.

      +
    • +
    +
    +
    +
    +
    +

    Legend

    +
    +
      +
    • Module
    • +
    • Object literal
    • +
    • Variable
    • +
    • Function
    • +
    • Function with type parameter
    • +
    • Index signature
    • +
    • Type alias
    • +
    • Type alias with type parameter
    • +
    +
      +
    • Enumeration
    • +
    • Enumeration member
    • +
    • Property
    • +
    • Method
    • +
    +
      +
    • Interface
    • +
    • Interface with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Index signature
    • +
    +
      +
    • Class
    • +
    • Class with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Accessor
    • +
    • Index signature
    • +
    +
      +
    • Inherited constructor
    • +
    • Inherited property
    • +
    • Inherited method
    • +
    • Inherited accessor
    • +
    +
      +
    • Protected property
    • +
    • Protected method
    • +
    • Protected accessor
    • +
    +
      +
    • Private property
    • +
    • Private method
    • +
    • Private accessor
    • +
    +
      +
    • Static property
    • +
    • Static method
    • +
    +
    +
    +
    +
    +

    Generated using TypeDoc

    +
    +
    +
    + + + \ No newline at end of file diff --git a/packages/mapbox-gl-esri-sources/docs/interfaces/arcgisdynamicmapserviceoptions.html b/packages/mapbox-gl-esri-sources/docs/interfaces/arcgisdynamicmapserviceoptions.html new file mode 100644 index 000000000..781758cb0 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/docs/interfaces/arcgisdynamicmapserviceoptions.html @@ -0,0 +1,2573 @@ + + + + + + ArcGISDynamicMapServiceOptions | @seasketch/mapbox-gl-esri-sources + + + + + +
    +
    +
    +
    + +
    +
    + Options +
    +
    + All +
      +
    • Public
    • +
    • Public/Protected
    • +
    • All
    • +
    +
    + + + + + + +
    +
    + Menu +
    +
    +
    +
    +
    + +
    +
    +
    +

    Hierarchy

    +
      +
    • + ArcGISDynamicMapServiceOptions +
    • +
    +
    +
    +

    Index

    +
    + +
    +
    +
    +

    Properties

    +
    + +

    Optional layers

    +
    layers: SublayerState[]
    + +
    +
    +

    List of sublayers to display, in order. Order will be respected only if + supportsDynamicLayers is true. If left undefined the service will be + with the default layers.

    +
    +
    +
    +
    + +

    Optional queryParameters

    +
    queryParameters: undefined | {}
    + +
    +
    +

    All query parameters will be added to each MapServer export request, + overriding any settings made by this library. Useful for specifying image + format, or working with temporal data.

    +
    +
    +
    +
    + +

    Optional supportsDynamicLayers

    +
    supportsDynamicLayers: undefined | false | true
    + +
    +
    +

    Set true if the Map Service supports dynamic layers + , in which case sublayer order and opacity can be specified. If set false + any order and opacity settings will be ignored.

    +
    +
    +
    default
    +

    false

    +
    +
    +
    +
    +
    + +

    Optional useDevicePixelRatio

    +
    useDevicePixelRatio: undefined | false | true
    + +
    +
    +

    Fetch larger images for high-resolution devices.

    +
    +
    +
    default
    +

    true

    +
    +
    +
    +
    +
    +
    +
    +

    Legend

    +
    +
      +
    • Module
    • +
    • Object literal
    • +
    • Variable
    • +
    • Function
    • +
    • Function with type parameter
    • +
    • Index signature
    • +
    • Type alias
    • +
    • Type alias with type parameter
    • +
    +
      +
    • Enumeration
    • +
    • Enumeration member
    • +
    • Property
    • +
    • Method
    • +
    +
      +
    • Interface
    • +
    • Interface with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Index signature
    • +
    +
      +
    • Class
    • +
    • Class with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Accessor
    • +
    • Index signature
    • +
    +
      +
    • Inherited constructor
    • +
    • Inherited property
    • +
    • Inherited method
    • +
    • Inherited accessor
    • +
    +
      +
    • Protected property
    • +
    • Protected method
    • +
    • Protected accessor
    • +
    +
      +
    • Private property
    • +
    • Private method
    • +
    • Private accessor
    • +
    +
      +
    • Static property
    • +
    • Static method
    • +
    +
    +
    +
    +
    +

    Generated using TypeDoc

    +
    +
    +
    + + + \ No newline at end of file diff --git a/packages/mapbox-gl-esri-sources/docs/interfaces/arcgisvectorsourceoptions.html b/packages/mapbox-gl-esri-sources/docs/interfaces/arcgisvectorsourceoptions.html new file mode 100644 index 000000000..84cadeaa2 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/docs/interfaces/arcgisvectorsourceoptions.html @@ -0,0 +1,2599 @@ + + + + + + ArcGISVectorSourceOptions | @seasketch/mapbox-gl-esri-sources + + + + + +
    +
    +
    +
    + +
    +
    + Options +
    +
    + All +
      +
    • Public
    • +
    • Public/Protected
    • +
    • All
    • +
    +
    + + + + + + +
    +
    + Menu +
    +
    +
    +
    +
    + +
    +
    +
    +

    Hierarchy

    +
      +
    • + ArcGISVectorSourceOptions +
    • +
    +
    +
    +

    Index

    +
    + +
    +
    +
    +

    Properties

    +
    + +

    Optional displayIncompleteFeatureCollections

    +
    displayIncompleteFeatureCollections: undefined | false | true
    + +
    +
    +

    ArcGISVectorSource will page through results until the entire dataset is + downloaded. If set to true, data will be rendered each time a page of + features are downloaded. Otherwise, the source data will only be updated + once upon completion.

    +
    +
    +
    type
    +

    {boolean}

    +
    +
    default
    +

    true

    +
    +
    +
    +
    +
    + +

    Optional geometryPrecision

    +
    geometryPrecision: undefined | number
    + +
    +
    +

    Number of digits precision after the decimal to fetch from the origin + server. The default of 6 a good compromise between precision (~10cm) and + download size.

    +
    +
    +
    type
    +

    {number}

    +
    +
    default
    +

    6

    +
    +
    +
    +
    +
    + +

    Optional outFields

    +
    outFields: undefined | string
    + +
    +
    +

    Can be used to limit fields in GeoJSON properties.

    +
    +
    +
    type
    +

    {string}

    +
    +
    default
    +

    "*"

    +
    +
    +
    +
    +
    + +

    Optional supportsPagination

    +
    supportsPagination: undefined | false | true
    + +
    +
    +

    Set to false if the service is known not to support pagination, which will + be true for services that have only a single feature but is also indicated + by the main service metadata.

    +
    +

    If left to the default of true and the server does not support pagination, + the initial fetch of data will fail but a second request will be + constructed without pagination parameters which should succeed.

    +
    +
    type
    +

    {boolean}

    +
    +
    default
    +

    true

    +
    +
    +
    +
    +
    +
    +
    +

    Legend

    +
    +
      +
    • Module
    • +
    • Object literal
    • +
    • Variable
    • +
    • Function
    • +
    • Function with type parameter
    • +
    • Index signature
    • +
    • Type alias
    • +
    • Type alias with type parameter
    • +
    +
      +
    • Enumeration
    • +
    • Enumeration member
    • +
    • Property
    • +
    • Method
    • +
    +
      +
    • Interface
    • +
    • Interface with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Index signature
    • +
    +
      +
    • Class
    • +
    • Class with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Accessor
    • +
    • Index signature
    • +
    +
      +
    • Inherited constructor
    • +
    • Inherited property
    • +
    • Inherited method
    • +
    • Inherited accessor
    • +
    +
      +
    • Protected property
    • +
    • Protected method
    • +
    • Protected accessor
    • +
    +
      +
    • Private property
    • +
    • Private method
    • +
    • Private accessor
    • +
    +
      +
    • Static property
    • +
    • Static method
    • +
    +
    +
    +
    +
    +

    Generated using TypeDoc

    +
    +
    +
    + + + \ No newline at end of file diff --git a/packages/mapbox-gl-esri-sources/docs/interfaces/image.html b/packages/mapbox-gl-esri-sources/docs/interfaces/image.html new file mode 100644 index 000000000..e418dcd8e --- /dev/null +++ b/packages/mapbox-gl-esri-sources/docs/interfaces/image.html @@ -0,0 +1,2537 @@ + + + + + + Image | @seasketch/mapbox-gl-esri-sources + + + + + +
    +
    +
    +
    + +
    +
    + Options +
    +
    + All +
      +
    • Public
    • +
    • Public/Protected
    • +
    • All
    • +
    +
    + + + + + + +
    +
    + Menu +
    +
    +
    +
    +
    + +
    +
    +
    +

    Hierarchy

    +
      +
    • + Image +
    • +
    +
    +
    +

    Index

    +
    +
    +
    +

    Properties

    + +
    +
    +
    +
    +
    +

    Properties

    +
    + +

    dataURI

    +
    dataURI: string
    + +
    +
    + +

    height

    +
    height: number
    + +
    +
    + +

    pixelRatio

    +
    pixelRatio: number
    + +
    +
    + +

    width

    +
    width: number
    + +
    +
    +
    +
    +

    Legend

    +
    +
      +
    • Module
    • +
    • Object literal
    • +
    • Variable
    • +
    • Function
    • +
    • Function with type parameter
    • +
    • Index signature
    • +
    • Type alias
    • +
    • Type alias with type parameter
    • +
    +
      +
    • Enumeration
    • +
    • Enumeration member
    • +
    • Property
    • +
    • Method
    • +
    +
      +
    • Interface
    • +
    • Interface with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Index signature
    • +
    +
      +
    • Class
    • +
    • Class with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Accessor
    • +
    • Index signature
    • +
    +
      +
    • Inherited constructor
    • +
    • Inherited property
    • +
    • Inherited method
    • +
    • Inherited accessor
    • +
    +
      +
    • Protected property
    • +
    • Protected method
    • +
    • Protected accessor
    • +
    +
      +
    • Private property
    • +
    • Private method
    • +
    • Private accessor
    • +
    +
      +
    • Static property
    • +
    • Static method
    • +
    +
    +
    +
    +
    +

    Generated using TypeDoc

    +
    +
    +
    + + + \ No newline at end of file diff --git a/packages/mapbox-gl-esri-sources/docs/interfaces/imageset.html b/packages/mapbox-gl-esri-sources/docs/interfaces/imageset.html new file mode 100644 index 000000000..7204631ce --- /dev/null +++ b/packages/mapbox-gl-esri-sources/docs/interfaces/imageset.html @@ -0,0 +1,2515 @@ + + + + + + ImageSet | @seasketch/mapbox-gl-esri-sources + + + + + +
    +
    +
    +
    + +
    +
    + Options +
    +
    + All +
      +
    • Public
    • +
    • Public/Protected
    • +
    • All
    • +
    +
    + + + + + + +
    +
    + Menu +
    +
    +
    +
    +
    + +
    +
    +
    +

    Hierarchy

    +
      +
    • + ImageSet +
    • +
    +
    +
    +

    Index

    +
    +
    +
    +

    Properties

    + +
    +
    +
    +
    +
    +

    Properties

    +
    + +

    id

    +
    id: string
    + +
    +
    + +

    images

    +
    images: Image[]
    + +
    +
    +
    +
    +

    Legend

    +
    +
      +
    • Module
    • +
    • Object literal
    • +
    • Variable
    • +
    • Function
    • +
    • Function with type parameter
    • +
    • Index signature
    • +
    • Type alias
    • +
    • Type alias with type parameter
    • +
    +
      +
    • Enumeration
    • +
    • Enumeration member
    • +
    • Property
    • +
    • Method
    • +
    +
      +
    • Interface
    • +
    • Interface with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Index signature
    • +
    +
      +
    • Class
    • +
    • Class with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Accessor
    • +
    • Index signature
    • +
    +
      +
    • Inherited constructor
    • +
    • Inherited property
    • +
    • Inherited method
    • +
    • Inherited accessor
    • +
    +
      +
    • Protected property
    • +
    • Protected method
    • +
    • Protected accessor
    • +
    +
      +
    • Private property
    • +
    • Private method
    • +
    • Private accessor
    • +
    +
      +
    • Static property
    • +
    • Static method
    • +
    +
    +
    +
    +
    +

    Generated using TypeDoc

    +
    +
    +
    + + + \ No newline at end of file diff --git a/packages/mapbox-gl-esri-sources/docs/interfaces/sublayerstate.html b/packages/mapbox-gl-esri-sources/docs/interfaces/sublayerstate.html new file mode 100644 index 000000000..af4e7dd18 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/docs/interfaces/sublayerstate.html @@ -0,0 +1,2525 @@ + + + + + + SublayerState | @seasketch/mapbox-gl-esri-sources + + + + + +
    +
    +
    +
    + +
    +
    + Options +
    +
    + All +
      +
    • Public
    • +
    • Public/Protected
    • +
    • All
    • +
    +
    + + + + + + +
    +
    + Menu +
    +
    +
    +
    +
    + +
    +
    +
    +

    Hierarchy

    +
      +
    • + SublayerState +
    • +
    +
    +
    +

    Index

    +
    +
    +
    +

    Properties

    + +
    +
    +
    +
    +
    +

    Properties

    +
    + +

    Optional opacity

    +
    opacity: undefined | number
    + +
    +
    +

    sublayer opacity from 0.0 - 1.0

    +
    +
    +
    +
    + +

    sublayer

    +
    sublayer: number
    + +
    +
    +

    0-based sublayer index

    +
    +
    +
    +
    +
    +
    +

    Legend

    +
    +
      +
    • Module
    • +
    • Object literal
    • +
    • Variable
    • +
    • Function
    • +
    • Function with type parameter
    • +
    • Index signature
    • +
    • Type alias
    • +
    • Type alias with type parameter
    • +
    +
      +
    • Enumeration
    • +
    • Enumeration member
    • +
    • Property
    • +
    • Method
    • +
    +
      +
    • Interface
    • +
    • Interface with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Index signature
    • +
    +
      +
    • Class
    • +
    • Class with type parameter
    • +
    • Constructor
    • +
    • Property
    • +
    • Method
    • +
    • Accessor
    • +
    • Index signature
    • +
    +
      +
    • Inherited constructor
    • +
    • Inherited property
    • +
    • Inherited method
    • +
    • Inherited accessor
    • +
    +
      +
    • Protected property
    • +
    • Protected method
    • +
    • Protected accessor
    • +
    +
      +
    • Private property
    • +
    • Private method
    • +
    • Private accessor
    • +
    +
      +
    • Static property
    • +
    • Static method
    • +
    +
    +
    +
    +
    +

    Generated using TypeDoc

    +
    +
    +
    + + + \ No newline at end of file diff --git a/packages/mapbox-gl-esri-sources/examples/arcgisDynamic.html b/packages/mapbox-gl-esri-sources/examples/arcgisDynamic.html new file mode 100644 index 000000000..8e6606f76 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/examples/arcgisDynamic.html @@ -0,0 +1,258 @@ + + + + + + + + + ArcGIS Dynamic Map Service Demo + + + +
    +
    + +
    + sublayers +
    +
    + + +
    + + +
    + + +
    + + + diff --git a/packages/mapbox-gl-esri-sources/examples/arcgisTiled.html b/packages/mapbox-gl-esri-sources/examples/arcgisTiled.html new file mode 100644 index 000000000..bb67981ec --- /dev/null +++ b/packages/mapbox-gl-esri-sources/examples/arcgisTiled.html @@ -0,0 +1,82 @@ + + + + + + + + ArcGIS Tiled Map Service Demo + + + +
    + + + diff --git a/packages/mapbox-gl-esri-sources/examples/arcgisVectorLayers.html b/packages/mapbox-gl-esri-sources/examples/arcgisVectorLayers.html new file mode 100644 index 000000000..71fa6f78e --- /dev/null +++ b/packages/mapbox-gl-esri-sources/examples/arcgisVectorLayers.html @@ -0,0 +1,318 @@ + + + + + + + + + + ArcGIS Feature Layers Demo + + + +
    +
    +
    + +
    +
    + + + diff --git a/packages/mapbox-gl-esri-sources/index.ts b/packages/mapbox-gl-esri-sources/index.ts new file mode 100644 index 000000000..4fa4ea0ef --- /dev/null +++ b/packages/mapbox-gl-esri-sources/index.ts @@ -0,0 +1,44 @@ +import { + ArcGISDynamicMapService, + ArcGISDynamicMapServiceOptions, +} from "./src/ArcGISDynamicMapService"; +import { + ArcGISVectorSource, + ArcGISVectorSourceOptions, + fetchFeatureLayerData, +} from "./src/ArcGISVectorSource"; +import { ArcGISRESTServiceRequestManager } from "./src/ArcGISRESTServiceRequestManager"; +export { + ArcGISTiledMapService, + ArcGISTiledMapServiceOptions, +} from "./src/ArcGISTiledMapService"; +import { + MapServiceMetadata, + FeatureServerMetadata, + LayersMetadata, +} from "./src/ServiceMetadata"; +export { MapServiceMetadata, FeatureServerMetadata, LayersMetadata }; +export { + CustomGLSource, + CustomGLSourceOptions, + DynamicRenderingSupportOptions, + LegendItem, + SingleImageLegend, + DataTableOfContentsItem, + FolderTableOfContentsItem, +} from "./src/CustomGLSource"; +export { + ArcGISDynamicMapService, + ArcGISVectorSource, + ArcGISDynamicMapServiceOptions, + ArcGISVectorSourceOptions, + ArcGISRESTServiceRequestManager, +}; +export { + ArcGISFeatureLayerSourceOptions, + default as ArcGISFeatureLayerSource, +} from "./src/ArcGISFeatureLayerSource"; +export { generateMetadataForLayer } from "./src/utils"; +export { default as styleForFeatureLayer } from "./src/styleForFeatureLayer"; +export { ImageList } from "./src/ImageList"; +export { fetchFeatureLayerData } from "./src/ArcGISVectorSource"; diff --git a/packages/mapbox-gl-esri-sources/package-lock.json b/packages/mapbox-gl-esri-sources/package-lock.json index 5a0ee03da..eb8a56f58 100644 --- a/packages/mapbox-gl-esri-sources/package-lock.json +++ b/packages/mapbox-gl-esri-sources/package-lock.json @@ -7,10 +7,18 @@ "": { "name": "@seasketch/mapbox-gl-esri-sources", "version": "0.9.0", + "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { + "@mapbox/tilebelt": "^1.0.2", "@terraformer/arcgis": "^2.0.7", + "@types/lodash.debounce": "^4.0.8", + "@types/mapbox__tilebelt": "^1.0.2", + "arcgis-pbf-parser": "^0.0.4", "bytes": "^3.1.2", + "eventemitter3": "^5.0.1", + "lodash.debounce": "^4.0.8", + "tilebelt": "^1.0.1", "uuid": "^8.3.0" }, "devDependencies": { @@ -18,15 +26,20 @@ "@rollup/plugin-node-resolve": "^15.2.1", "@types/arcgis-rest-api": "^10.4.4", "@types/geojson": "^7946.0.7", - "@types/mapbox-gl": "^2.7.13", + "@types/mapbox-gl": "^2.7.15", "@types/uuid": "^8.0.1", "http-server": "^0.12.3", "rollup": "^2.79.1", "rollup-plugin-cleanup": "^3.1.1", "typedoc": "^0.17.0-3", - "typescript": "^3.9.7" + "typescript": "^5.2" } }, + "node_modules/@mapbox/tilebelt": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/tilebelt/-/tilebelt-1.0.2.tgz", + "integrity": "sha512-tGJN2VIgWrXqBTPIxFVklklIpcy6ss8W5ouq+cjNLXPXFraRaDR4Ice+5Q8/uLX+6aH23lWBMydOIn8PcdVcpA==" + }, "node_modules/@rollup/plugin-commonjs": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-14.0.0.tgz", @@ -152,13 +165,33 @@ "node_modules/@types/geojson": { "version": "7946.0.10", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", - "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==", - "dev": true + "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==" + }, + "node_modules/@types/lodash": { + "version": "4.14.200", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.200.tgz", + "integrity": "sha512-YI/M/4HRImtNf3pJgbF+W6FrXovqj+T+/HpENLTooK9PnkacBsDpeP3IpHab40CClUfhNmdM2WTNP2sa2dni5Q==" + }, + "node_modules/@types/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-REumepIJjQFSOaBUoj81U5ZzF9YIhovzE2Lm6ejUbycmwx597k2ivG1cVfPtAj4eVuSbGoZDkJR0sRIahsE6/Q==", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/mapbox__tilebelt": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mapbox__tilebelt/-/mapbox__tilebelt-1.0.2.tgz", + "integrity": "sha512-J7uMzHrdz0A/gKcXYa3GIz+kxqTSYDaiBVCTAomRww0CLn4/t4JCpOQIsVESqinbalVvX7KPBdWEXFs72IjOgQ==", + "dependencies": { + "@types/geojson": "*" + } }, "node_modules/@types/mapbox-gl": { - "version": "2.7.13", - "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-2.7.13.tgz", - "integrity": "sha512-qNffhTdYkeFl8QG9Q1zPPJmcs8PvHgmLa1PcwP1rxvcfMsIgcFr/FnrCttG0ZnH7Kzdd7xfECSRNTWSr4jC3PQ==", + "version": "2.7.17", + "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-2.7.17.tgz", + "integrity": "sha512-5lDO2W6glPCqiRuqKh0a7MPOwnVt1/KWcYnxsL3z5rmjuOcFdHEa+KzUwCzqsAlbAegIIhgQiREZzJ9o1ze1gQ==", "dev": true, "dependencies": { "@types/geojson": "*" @@ -176,6 +209,14 @@ "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", "dev": true }, + "node_modules/arcgis-pbf-parser": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/arcgis-pbf-parser/-/arcgis-pbf-parser-0.0.4.tgz", + "integrity": "sha512-ngieRsLNct1dPyH85DnASiierr+hYO+9ow3zC22lYvNcKtFxVrtd1jbfizwWs9Kp3NpKP4iNnPAONp3acCcceA==", + "dependencies": { + "pbf": "^3.2.1" + } + }, "node_modules/async": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", @@ -314,10 +355,9 @@ "dev": true }, "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" }, "node_modules/follow-redirects": { "version": "1.15.2", @@ -509,6 +549,12 @@ "node": ">=8.0.0" } }, + "node_modules/http-proxy/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, "node_modules/http-server": { "version": "0.12.3", "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.12.3.tgz", @@ -534,6 +580,25 @@ "node": ">=6" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -630,6 +695,11 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, "node_modules/lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", @@ -756,6 +826,18 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/pbf": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/perf-regexes": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/perf-regexes/-/perf-regexes-1.0.1.tgz", @@ -800,6 +882,11 @@ "node": ">=0.4.0" } }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" + }, "node_modules/qs": { "version": "6.11.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", @@ -850,6 +937,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, "node_modules/rollup": { "version": "2.79.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", @@ -970,6 +1065,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tilebelt": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tilebelt/-/tilebelt-1.0.1.tgz", + "integrity": "sha512-cxHzpa5JgsugY9NUVRH43gPaGJw/29LecAn4X7UGOP64+kB8pU4VQ3bIhSyfb5Mk4jDxwl3yk330L/EIhbJ5aw==", + "deprecated": "This module is now under the @mapbox namespace: install @mapbox/tilebelt instead" + }, "node_modules/typedoc": { "version": "0.17.8", "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.17.8.tgz", @@ -1010,16 +1111,16 @@ } }, "node_modules/typescript": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", - "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", + "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/uglify-js": { diff --git a/packages/mapbox-gl-esri-sources/package.json b/packages/mapbox-gl-esri-sources/package.json new file mode 100644 index 000000000..7939afb73 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/package.json @@ -0,0 +1,56 @@ +{ + "name": "@seasketch/mapbox-gl-esri-sources", + "version": "0.9.0", + "description": "Render data services from ArcGIS in MapBox GL JS", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "start": "http-server", + "watch": "rollup -c rollup.config.js --watch", + "test": "jest", + "docs": "rm -rf docs && npx typedoc", + "build": "tsc -b && rollup -c rollup.config.js", + "install": "npm run build" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/seasketch/mapbox-gl-dynamic-image-sources.git" + }, + "keywords": [ + "mapbox", + "mapbox-gl", + "arcgis", + "wms" + ], + "author": "Chad Burt", + "license": "BSD-3-Clause", + "bugs": { + "url": "https://github.com/seasketch/mapbox-gl-dynamic-image-sources/issues" + }, + "homepage": "https://github.com/seasketch/mapbox-gl-dynamic-image-sources#readme", + "devDependencies": { + "@rollup/plugin-commonjs": "^14.0.0", + "@rollup/plugin-node-resolve": "^15.2.1", + "@types/arcgis-rest-api": "^10.4.4", + "@types/geojson": "^7946.0.7", + "@types/mapbox-gl": "^2.7.15", + "@types/uuid": "^8.0.1", + "http-server": "^0.12.3", + "rollup": "^2.79.1", + "rollup-plugin-cleanup": "^3.1.1", + "typedoc": "^0.17.0-3", + "typescript": "^5.2" + }, + "dependencies": { + "@mapbox/tilebelt": "^1.0.2", + "@terraformer/arcgis": "^2.0.7", + "@types/lodash.debounce": "^4.0.8", + "@types/mapbox__tilebelt": "^1.0.2", + "arcgis-pbf-parser": "^0.0.4", + "bytes": "^3.1.2", + "eventemitter3": "^5.0.1", + "lodash.debounce": "^4.0.8", + "tilebelt": "^1.0.1", + "uuid": "^8.3.0" + } +} diff --git a/packages/mapbox-gl-esri-sources/rollup.config.js b/packages/mapbox-gl-esri-sources/rollup.config.js new file mode 100644 index 000000000..1a40a4b2f --- /dev/null +++ b/packages/mapbox-gl-esri-sources/rollup.config.js @@ -0,0 +1,13 @@ +// rollup.config.js +import resolve from "@rollup/plugin-node-resolve"; +import commonjs from "@rollup/plugin-commonjs"; +import cleanup from "rollup-plugin-cleanup"; +export default { + input: "dist/index.js", + output: { + file: "dist/bundle.js", + format: "iife", + name: "MapBoxGLEsriSources", + }, + plugins: [resolve({ browser: true }), commonjs({ browser: true }), cleanup()], +}; diff --git a/packages/mapbox-gl-esri-sources/src/ArcGISDynamicMapService.ts b/packages/mapbox-gl-esri-sources/src/ArcGISDynamicMapService.ts new file mode 100644 index 000000000..11a0d3d81 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/src/ArcGISDynamicMapService.ts @@ -0,0 +1,720 @@ +import { + Map, + ImageSource, + RasterSource, + AnyLayer, + AnySourceData, + ImageSourceOptions, + ImageSourceRaw, + LngLatBounds, +} from "mapbox-gl"; +import { + ComputedMetadata, + CustomGLSource, + CustomGLSourceOptions, + CustomSourceType, + DynamicRenderingSupportOptions, + LegendItem, + OrderedLayerSettings, +} from "./CustomGLSource"; +import { v4 as uuid } from "uuid"; +import { ArcGISRESTServiceRequestManager } from "./ArcGISRESTServiceRequestManager"; +import { LayersMetadata, MapServiceMetadata } from "./ServiceMetadata"; +import { + contentOrFalse, + extentToLatLngBounds, + generateMetadataForLayer, + makeLegend, +} from "./utils"; + +/** @hidden */ +export const blankDataUri = + "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; + +export interface ArcGISDynamicMapServiceOptions extends CustomGLSourceOptions { + /** + * URL for the service. Should end in /MapServer + */ + url: string; + /** + * Fetch larger images for high-resolution devices. + * @default true + * */ + supportHighDpiDisplays?: boolean; + /** + * List of sublayers to display, in order. Order will be respected only if + * `supportsDynamicRendering` is true. If left undefined the service will be + * with the default layers. + * */ + layers?: OrderedLayerSettings; + /** + * All query parameters will be added to each MapServer export request, + * overriding any settings made by this library. Useful for specifying image + * format, or working with temporal data. + * */ + queryParameters?: { + [queryString: string]: string | number; + }; + /** + * Request tiles instead of a single image. + * @default false + */ + useTiles?: boolean; + /** + * 256 or 512 would be most appropriate. default is 256 + */ + tileSize?: number; + token?: string; +} + +export class ArcGISDynamicMapService + implements CustomGLSource +{ + /** Source id used in the map style */ + sourceId: string; + private map?: Map; + private requestManager: ArcGISRESTServiceRequestManager; + private serviceMetadata?: MapServiceMetadata; + private layerMetadata?: LayersMetadata; + private options: ArcGISDynamicMapServiceOptions; + + private layers?: OrderedLayerSettings; + private supportsDynamicLayers = false; + private debounceTimeout?: NodeJS.Timeout; + private _loading = true; + private resolution?: string; + type: CustomSourceType; + url: string; + + /** + * @param {string} sourceId ID to be used when adding refering to this source from layers + * @param {string} baseUrl Location of the service. Should end in /MapServer + * @param {ArcGISDynamicMapServiceOptions} [options] + */ + constructor( + requestManager: ArcGISRESTServiceRequestManager, + options: ArcGISDynamicMapServiceOptions + ) { + this.type = "ArcGISDynamicMapService"; + this.options = options; + this.url = options.url; + this.requestManager = requestManager; + this.sourceId = options?.sourceId || uuid(); + // remove trailing slash if present + options.url = options.url.replace(/\/$/, ""); + if (!/rest\/services/.test(options.url) || !/MapServer/.test(options.url)) { + throw new Error("Invalid ArcGIS REST Service URL"); + } + this.resolution = `(resolution: ${window.devicePixelRatio}dppx)`; + matchMedia(this.resolution).addListener(this.respondToResolutionChange); + } + + private respondToResolutionChange = () => { + if (this.options.supportHighDpiDisplays) { + this.updateSource(); + } + if (this.resolution) { + matchMedia(this.resolution).removeListener( + this.respondToResolutionChange + ); + } + this.resolution = `(resolution: ${window.devicePixelRatio}dppx)`; + matchMedia(this.resolution).addListener(this.respondToResolutionChange); + }; + + /** + * Use ArcGISRESTServiceRequestManager to fetch metadata for the service, + * caching it on the instance for reuse. + */ + private getMetadata() { + if (this.serviceMetadata && this.layerMetadata) { + return Promise.resolve({ + serviceMetadata: this.serviceMetadata, + layers: this.layerMetadata, + }); + } else { + return this.requestManager + .getMapServiceMetadata(this.options.url, { + token: this.options.token, + }) + .then(({ serviceMetadata, layers }) => { + this.serviceMetadata = serviceMetadata; + this.layerMetadata = layers; + this.supportsDynamicLayers = serviceMetadata.supportsDynamicLayers; + return { serviceMetadata, layers }; + }); + } + } + + _computedMetadata?: ComputedMetadata; + + /** + * Returns computed metadata for the service, including bounds, minzoom, maxzoom, and attribution. + * @returns ComputedMetadata + * @throws Error if metadata is not available + * @throws Error if tileInfo is not available + * */ + async getComputedMetadata(): Promise { + if (!this._computedMetadata) { + const { serviceMetadata, layers } = await this.getMetadata(); + let { bounds, minzoom, maxzoom, attribution } = + await this.getComputedProperties(); + attribution = this.options.attributionOverride || attribution; + const results = /\/.+\/MapServer/.exec(this.options.url); + let label = results ? results[0] : false; + if (!label) { + if (this.layerMetadata?.layers?.[0]) { + label = this.layerMetadata.layers[0].name; + } + } + const legendData = await this.requestManager.getLegendMetadata( + this.options.url + ); + + // find hidden layers + // Not as simple as just reading the defaultVisibility property, because + // if a parent layer is hidden, all children are hidden as well. Folders can + // be nested arbitrarily deep. + const hiddenIds = new Set(); + for (const layer of layers.layers) { + if (!layer.defaultVisibility) { + hiddenIds.add(layer.id); + } else { + // check if parents are hidden + if (layer.parentLayer) { + if (hiddenIds.has(layer.parentLayer.id)) { + hiddenIds.add(layer.id); + } else { + // may not be added yet + const parent = layers.layers.find( + (l) => l.id === layer.parentLayer?.id + ); + if (parent && !parent.defaultVisibility) { + hiddenIds.add(layer.id); + hiddenIds.add(parent.id); + } + } + } + } + } + + this._computedMetadata = { + bounds: bounds || undefined, + minzoom, + maxzoom, + attribution, + tableOfContentsItems: layers.layers.map((lyr) => { + const legendLayer = legendData.layers.find( + (l) => l.layerId === lyr.id + ); + const isFolder = lyr.type === "Group Layer"; + if (isFolder) { + return { + type: "folder", + id: lyr.id.toString(), + label: lyr.name, + defaultVisibility: hiddenIds.has(lyr.id) + ? false + : lyr.defaultVisibility, + parentId: lyr.parentLayer + ? lyr.parentLayer.id.toString() + : undefined, + }; + } else { + return { + type: "data", + id: lyr.id.toString(), + label: lyr.name, + defaultVisibility: hiddenIds.has(lyr.id) + ? false + : lyr.defaultVisibility, + metadata: generateMetadataForLayer( + this.options.url + "/" + lyr.id, + this.serviceMetadata!, + lyr + ), + parentId: lyr.parentLayer + ? lyr.parentLayer.id.toString() + : undefined, + legend: makeLegend(legendData, lyr.id), + }; + } + }), + supportsDynamicRendering: { + layerOpacity: this.supportsDynamicLayers, + layerOrder: true, + layerVisibility: true, + }, + }; + } + return this._computedMetadata!; + } + + /** + * Private method used as the basis for getComputedMetadata and also used + * when generating the source data for addToMap. + * @returns Computed properties for the service, including bounds, minzoom, maxzoom, and attribution. + */ + private async getComputedProperties() { + const { serviceMetadata, layers } = await this.getMetadata(); + const levels = serviceMetadata.tileInfo?.lods.map((l) => l.level) || []; + const attribution = + contentOrFalse(layers.layers[0].copyrightText) || + contentOrFalse(serviceMetadata.copyrightText) || + contentOrFalse(serviceMetadata.documentInfo?.Author) || + undefined; + const minzoom = Math.min(...levels); + const maxzoom = Math.max(...levels); + return { + minzoom, + maxzoom, + bounds: await extentToLatLngBounds(serviceMetadata.fullExtent), + attribution, + }; + } + + private onMapData = (event: mapboxgl.MapDataEvent & mapboxgl.EventData) => { + if (event.sourceId && event.sourceId === this.sourceId) { + this._loading = false; + } + }; + + private onMapError = (event: mapboxgl.ErrorEvent & mapboxgl.EventData) => { + if ( + event.sourceId === this.sourceId && + ((event.dataType === "source" && event.sourceDataType === "content") || + (event.type === "error" && + event.error && + "status" in event.error && + event.error.status !== 404)) + ) { + this._loading = false; + } + }; + + async getGLSource(map: Map): Promise { + let { attribution, bounds } = await this.getComputedProperties(); + bounds = bounds || [-89, -179, 89, 179]; + if (this.options.useTiles) { + return { + type: "raster", + tiles: [this.getUrl(map)], + tileSize: this.options.tileSize || 256, + bounds: bounds as [number, number, number, number] | undefined, + attribution, + }; + } else { + const coordinates = this.getCoordinates(map); + // return a blank image until map event listeners are setup + const url = this.getUrl(map); + return { + type: "image", + url, + coordinates, + } as ImageSourceRaw; + } + } + + private getCoordinates(map: Map) { + const bounds = map.getBounds(); + // bbox's rubbing up against max extents appear to cause exceptions + // to be repeatedly thrown in mapbox-gl in globe projection + // TODO: this might be better solved by limiting image to the max + // bounds of the dataset returned by the service + const coordinates = [ + [ + Math.max(bounds.getNorthWest().lng, -179), + Math.min(bounds.getNorthWest().lat, 89), + ], + [ + Math.min(bounds.getNorthEast().lng, 179), + Math.min(bounds.getNorthEast().lat, 89), + ], + [ + Math.min(bounds.getSouthEast().lng, 179), + Math.max(bounds.getSouthEast().lat, -89), + ], + [ + Math.max(bounds.getSouthWest().lng, -179), + Math.max(bounds.getSouthWest().lat, -89), + ], + ]; + return coordinates; + } + + async addToMap(map: Map) { + if (!map) { + throw new Error("Map not provided to addToMap"); + } + const sourceData = await this.getGLSource(map); + map.addSource(this.sourceId, sourceData); + this.addEventListeners(map); + return this.sourceId; + } + + addEventListeners(map: Map) { + if (!map) { + throw new Error("Map not provided to addEventListeners"); + } + if (!this.options?.useTiles) { + if (!this.map || (this.map && this.map !== map)) { + if (this.map) { + this.removeEventListeners(this.map); + } + this.map = map; + map.on("moveend", this.updateSource); + map.on("data", this.onMapData); + map.on("error", this.onMapError); + // Source is added as a blank image at first. Initialize it with + // proper bounds and image + // this.updateSource(); + } + } + } + + removeEventListeners(map: Map) { + if (!this.map) { + throw new Error("Map not set"); + } else if (this.map !== map) { + throw new Error("Map does not match"); + } + delete this.map; + map.off("moveend", this.updateSource); + map.off("data", this.onMapData); + map.off("error", this.onMapError); + } + + removeFromMap(map: Map) { + if (map.getSource(this.sourceId)) { + const layers = map.getStyle().layers || []; + for (const layer of layers) { + if ("source" in layer && layer.source === this.sourceId) { + map.removeLayer(layer.id); + } + } + this.removeEventListeners(map); + map.removeSource(this.sourceId); + this.map = undefined; + } + } + + /** + * Clears all map event listeners setup by this instance. + */ + destroy() { + matchMedia(this.resolution!).removeListener(this.respondToResolutionChange); + if (this.map) { + this.removeFromMap(this.map); + } + } + + private getUrl(map?: Map) { + map = this.map || map; + if (!map) { + return blankDataUri; + } + let url = new URL(this.options.url + "/export"); + url.searchParams.set("f", "image"); + url.searchParams.set("transparent", "true"); + // create bbox in web mercator + const coordinates = this.getCoordinates(map); + let bbox = [ + lon2meters(coordinates[0][0]), + lat2meters(coordinates[2][1]), + lon2meters(coordinates[2][0]), + lat2meters(coordinates[0][1]), + ]; + const groundResolution = getGroundResolution( + map.getZoom() + + (this.options.supportHighDpiDisplays ? window.devicePixelRatio - 1 : 0) + ); + // Width and height can't be based on container width if the map is rotated + const width = Math.round((bbox[2] - bbox[0]) / groundResolution); + const height = Math.round((bbox[3] - bbox[1]) / groundResolution); + + url.searchParams.set("format", "png"); + url.searchParams.set("size", [width, height].join(",")); + if (this.options.supportHighDpiDisplays) { + switch (window.devicePixelRatio) { + case 1: + // standard pixelRatio looks best at 96 + url.searchParams.set("dpi", "96"); + break; + case 2: + // for higher pixelRatios, esri's software seems to like the dpi + // bumped up somewhat higher than a simple formula would suggest + url.searchParams.set("dpi", "220"); + break; + case 3: + url.searchParams.set("dpi", "390"); + break; + default: + url.searchParams.set( + "dpi", + // Bumping pixel ratio a bit. see above + (window.devicePixelRatio * 96 * 1.22).toString() + ); + break; + } + } else { + url.searchParams.set("dpi", "96"); + } + // Default to epsg:3857 + url.searchParams.set("imageSR", "102100"); + url.searchParams.set("bboxSR", "102100"); + // If the map extent crosses the meridian, we need to create a new + // projection and map the x coordinates to that space. The Esri JS API + // exhibits this same behavior. Solution was inspired by: + // * https://github.com/Esri/esri-leaflet/issues/672#issuecomment-160691149 + // * https://gist.github.com/perrygeo/4478844 + if (Math.abs(bbox[0]) > 20037508.34 || Math.abs(bbox[2]) > 20037508.34) { + const centralMeridian = this.map?.getCenter().lng; + if (this.options.supportHighDpiDisplays && window.devicePixelRatio > 1) { + bbox[0] = -(width * groundResolution) / (window.devicePixelRatio * 2); + bbox[2] = (width * groundResolution) / (window.devicePixelRatio * 2); + } else { + bbox[0] = -(width * groundResolution) / 2; + bbox[2] = (width * groundResolution) / 2; + } + const sr = JSON.stringify({ + wkt: `PROJCS["WGS_1984_Web_Mercator_Auxiliary_Sphere",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Mercator_Auxiliary_Sphere"],PARAMETER["False_Easting",0.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",${centralMeridian}],PARAMETER["Standard_Parallel_1",0.0],PARAMETER["Auxiliary_Sphere_Type",0.0],UNIT["Meter",1.0]]`, + }); + url.searchParams.set("imageSR", sr); + url.searchParams.set("bboxSR", sr); + } + + if (Array.isArray(this.layers)) { + if (this.layers.length === 0) { + return blankDataUri; + } else { + url.searchParams.set( + "layers", + `show:${this.layers.map((lyr) => lyr.id).join(",")}` + ); + } + } + + url.searchParams.set("bbox", bbox.join(",")); + + url.searchParams.delete("dynamicLayers"); + let layersInOrder = true; + let hasOpacityUpdates = false; + if (this.supportsDynamicLayers && this.layers) { + for (var i = 0; i < this.layers.length; i++) { + if ( + this.layers[i - 1] && + parseInt(this.layers[i].id) < parseInt(this.layers[i - 1].id) + ) { + layersInOrder = false; + } + const opacity = this.layers[i].opacity; + if (opacity !== undefined && opacity < 1) { + hasOpacityUpdates = true; + } + } + } + if (this.layers && (!layersInOrder || hasOpacityUpdates)) { + // need to provide renderInfo + const dynamicLayers = this.layers.map((lyr) => { + return { + id: lyr.id, + source: { + mapLayerId: lyr.id, + type: "mapLayer", + }, + drawingInfo: { + transparency: + lyr.opacity !== undefined ? 100 - lyr.opacity * 100 : 0, + }, + }; + }); + url.searchParams.set("dynamicLayers", JSON.stringify(dynamicLayers)); + } + for (const key in this.options.queryParameters) { + url.searchParams.set(key, this.options.queryParameters[key].toString()); + } + const tileSize = this.options.tileSize || 256; + if (this.options.useTiles) { + url.searchParams.set("bbox", `seasketch-replace-me`); + if (this.options.supportHighDpiDisplays && window.devicePixelRatio > 1) { + const size = tileSize * window.devicePixelRatio; + url.searchParams.set("size", [size, size].join(",")); + } else { + url.searchParams.set("size", [tileSize, tileSize].join(",")); + } + } + return url.toString().replace("seasketch-replace-me", "{bbox-epsg-3857}"); + } + + /** Whether a source image is currently being fetched over the network */ + get loading(): boolean { + const source = this.map?.getSource(this.sourceId); + if (source && source.type === "raster") { + return this.map!.isSourceLoaded(this.sourceId) === false; + } else { + return this._loading; + } + } + + private updateSource = () => { + this._loading = true; + const source = this.map?.getSource(this.sourceId); + if (source && this.map) { + if (source.type === "raster") { + // @ts-ignore - setTiles is in fact a valid method + source.setTiles([this.getUrl()]); + } else if (source.type === "image") { + const coordinates = this.getCoordinates(this.map); + + const url = this.getUrl(this.map); + // @ts-ignore + const currentUrl = source.url; + if (currentUrl === url) { + return; + } + // @ts-ignore Using a private member here + if (source.url === url) { + return; + } + source.updateImage({ + url, + coordinates, + }); + } else { + // do nothing, source isn't added + } + } + }; + + private debouncedUpdateSource = () => { + if (this.debounceTimeout) { + clearTimeout(this.debounceTimeout); + } + this.debounceTimeout = setTimeout(() => { + delete this.debounceTimeout; + this.updateSource(); + }, 5); + }; + + /** + * Update the list of sublayers and re-render the the map. If + * `supportsDynamicLayers` is enabled, sublayer order and opacity will be + * respected. + * + * ```typescript + * // reverses layer rendering order and sets one sublayer to 50% transparency + * mapService.updateLayers([ + * { sublayer: 1, opacity: 0.5 }, + * { sublayer: 0, opacity: 1 } + * ]); + * ``` + * + * @param layers SublayerState is an array of objects with `sublayer` and + * optional `opacity` props. + * + */ + updateLayers(layers: OrderedLayerSettings) { + // do a deep comparison of layers to detect whether there are any changes + if (JSON.stringify(layers) !== JSON.stringify(this.layers)) { + this.layers = layers; + this.debouncedUpdateSource(); + } + } + + /** + * Update query params sent with each export request and re-render the map. A + * list of supported parameters can be found in the [Esri REST API docs](https://developers.arcgis.com/rest/services-reference/export-map.htm#GUID-C93E8957-99FD-473B-B0E1-68EA315EBD98). + * Query parameters will override any values set by this library, such as + * `format`, `dpi`, `size`, and `bbox`. + * + * ```typescript + * + * mapServiceSource.updateQueryParameters({ + * format: 'png32', + * // visualize temporal datasets! + * historicMoment: slider.value + * }) + * + * ``` + */ + updateQueryParameters(queryParameters: { + [queryString: string]: string | number; + }) { + // do a deep comparison of layers to detect whether there are any changes + if ( + JSON.stringify(this.options.queryParameters) !== + JSON.stringify(queryParameters) + ) { + this.options.queryParameters = queryParameters; + this.debouncedUpdateSource(); + } + } + + /** + * Update support for adjusting image resolution based on devicePixelRatio and + * re-render the map. Useful for giving users the option to toggle + * high-resolution images depending on network conditions. + * @param enable + */ + updateUseDevicePixelRatio(enable: boolean) { + if (enable !== this.options.supportHighDpiDisplays) { + this.options.supportHighDpiDisplays = enable; + this.debouncedUpdateSource(); + } + } + + async getGLStyleLayers() { + return { + layers: [ + { + id: uuid(), + type: "raster", + source: this.sourceId, + paint: { + "raster-fade-duration": this.options.useTiles ? 300 : 0, + }, + }, + ] as AnyLayer[], + }; + } + + get ready() { + return Boolean(this._computedMetadata); + } + + async prepare() { + await this.getComputedMetadata(); + return; + } +} + +/** @hidden */ +function lat2meters(lat: number) { + // thanks! https://gist.github.com/onderaltintas/6649521 + var y = Math.log(Math.tan(((90 + lat) * Math.PI) / 360)) / (Math.PI / 180); + return (y * 20037508.34) / 180; +} + +/** @hidden */ +function lon2meters(lon: number) { + return (lon * 20037508.34) / 180; +} + +/** @hidden */ +function getGroundResolution(level: number) { + let groundResolution = resolutions[level]; + if (!groundResolution) { + groundResolution = (2 * Math.PI * 6378137) / (256 * 2 ** (level + 1)); + resolutions[level] = groundResolution; + } + return groundResolution; +} + +/** @hidden */ +const resolutions: { [level: number]: number } = {}; + +export function isArcGISDynamicMapService( + source: any +): source is ArcGISDynamicMapService { + return source.type === "ArcGISDynamicMapService"; +} diff --git a/packages/mapbox-gl-esri-sources/src/ArcGISFeatureLayerSource.ts b/packages/mapbox-gl-esri-sources/src/ArcGISFeatureLayerSource.ts new file mode 100644 index 000000000..4e2875a17 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/src/ArcGISFeatureLayerSource.ts @@ -0,0 +1,697 @@ +import { + ArcGISRESTServiceRequestManager, + CustomGLSource, + ImageList, + styleForFeatureLayer, +} from "../index"; +import { + ComputedMetadata, + CustomGLSourceOptions, + CustomSourceType, + LegendItem, + OrderedLayerSettings, +} from "./CustomGLSource"; +import { v4 as uuid } from "uuid"; +import { AnyLayer, GeoJSONSourceRaw, Layer, Map } from "mapbox-gl"; +import { + FeatureServerMetadata, + LayersMetadata, + MapServiceMetadata, +} from "./ServiceMetadata"; +import { + contentOrFalse, + extentToLatLngBounds, + generateMetadataForLayer, +} from "./utils"; +import { FeatureCollection } from "geojson"; +import { fetchFeatureCollection, urlForRawGeoJSONData } from "./fetchData"; +import { + QuantizedVectorRequestManager, + getOrCreateQuantizedVectorRequestManager, +} from "./QuantizedVectorRequestManager"; +import * as tilebelt from "@mapbox/tilebelt"; +import { fetchWithTTL } from "./ArcGISRESTServiceRequestManager"; +const tileDecode = require("arcgis-pbf-parser"); + +export const FEATURE_LAYER_RECOMMENDED_BYTE_LIMIT = 2_000_000; +export interface ArcGISFeatureLayerSourceOptions extends CustomGLSourceOptions { + /** + * URL for the service. Should end in /FeatureServer/{layerId} or /MapServer/{layerId}. + */ + url: string; + /** + * All query parameters will be added to each query request, overriding any + * settings made by this library. Useful for filtering or working with + * temporal data. + **/ + queryParameters?: { + [queryString: string]: string | number; + }; + token?: string; + /** + * Indicates how to fetch feature geometry from the service. + * - "raw": The entire dataset will be downloaded and rendered client-side. This + * may be slow for large datasets but is the most efficient option for smaller ones. + * - "quantized": Simplified vectors for only the current viewport will be + * fetched and updated when the map extent changes. + * - "auto": Will attempt the "raw" strategy up to a byte limit (default 2MB) and + * fall back to "quantized" if the limit is exceeded. + * @default "auto" + */ + fetchStrategy?: "auto" | "raw" | "tiled"; + /** + * If fetchStrategy is "auto", this is the byte limit before switching from + * "raw" to "quantized" mode. Default 2MB. + * @default 2_000_000 + */ + autoFetchByteLimit?: number; + cacheKey?: string; +} + +export function isFeatureLayerSource( + source: CustomGLSource +): source is ArcGISFeatureLayerSource { + return source.type === "ArcGISFeatureLayer"; +} + +export default class ArcGISFeatureLayerSource + implements CustomGLSource +{ + /** Source id used in the map style */ + sourceId: string; + layerId: number; + options: ArcGISFeatureLayerSourceOptions; + private map?: Map; + private requestManager: ArcGISRESTServiceRequestManager; + private serviceMetadata?: FeatureServerMetadata | MapServiceMetadata; + private layerMetadata?: LayersMetadata; + private _loading = true; + private featureData?: FeatureCollection; + private rawFeaturesHaveBeenFetched = false; + private exceededBytesLimit = false; + private QuantizedVectorRequestManager?: QuantizedVectorRequestManager; + private cache?: Cache; + private initialFetchStrategy?: "auto" | "tiled" | "raw"; + error?: string; + type: CustomSourceType; + url: string; + + constructor( + requestManager: ArcGISRESTServiceRequestManager, + options: ArcGISFeatureLayerSourceOptions + ) { + this.type = "ArcGISFeatureLayer"; + this.sourceId = options.sourceId || uuid(); + this.options = options; + // options.fetchStrategy = "raw"; + this.requestManager = requestManager; + this.url = this.options.url; + // remove trailing slash if present + options.url = options.url.replace(/\/$/, ""); + if ( + !/rest\/services/.test(options.url) || + (!/MapServer/.test(options.url) && !/FeatureServer/.test(options.url)) + ) { + throw new Error("Invalid ArcGIS REST Service URL"); + } + if (!/\d+$/.test(options.url)) { + throw new Error( + "URL must end in /FeatureServer/{layerId} or /MapServer/{layerId}" + ); + } + this.layerId = parseInt(options.url.match(/\d+$/)?.[0] || "0"); + this.initialFetchStrategy = options.fetchStrategy || "auto"; + const cache = caches + .open(options?.cacheKey || "seasketch-arcgis-rest-services") + .then((cache) => { + this.cache = cache; + }); + } + + _computedMetadata?: ComputedMetadata; + + async getComputedMetadata(): Promise { + try { + if (!this._computedMetadata) { + const { serviceMetadata, layers } = await this.getMetadata(); + const { bounds, minzoom, maxzoom, attribution } = + await this.getComputedProperties(); + const layer = layers.layers.find((l) => l.id === this.layerId); + const glStyle = await this.getGLStyleLayers(); + if (!layer) { + throw new Error("Layer not found"); + } + this._computedMetadata = { + bounds, + minzoom, + maxzoom, + attribution, + supportsDynamicRendering: { + layerOpacity: false, + layerVisibility: false, + layerOrder: false, + }, + tableOfContentsItems: [ + { + type: "data", + defaultVisibility: true, + id: this.sourceId, + label: layer.name!, + metadata: generateMetadataForLayer( + this.options.url.replace(/\/\d+$/, ""), + serviceMetadata, + layer + ), + glStyle: glStyle, + }, + ], + }; + } + return this._computedMetadata; + } catch (e: any) { + this.error = e.toString(); + throw e; + } + } + + /** + * Private method used as the basis for getComputedMetadata and also used + * when generating the source data for addToMap. + * @returns Computed properties for the service, including bounds, minzoom, maxzoom, and attribution. + */ + private async getComputedProperties() { + const { serviceMetadata, layers } = await this.getMetadata(); + const attribution = + this.options.attributionOverride || + contentOrFalse(serviceMetadata.copyrightText) || + undefined; + const layer = layers.layers.find((l) => l.id === this.layerId); + if (!layer) { + throw new Error(`Sublayer ${this.layerId} not found`); + } + const supportedFormats = (layer?.supportedQueryFormats || "") + .split(",") + .map((f) => f.toUpperCase().trim()); + this.tileFormat = supportedFormats.includes("PBF") ? "pbf" : "geojson"; + return { + minzoom: 0, + maxzoom: 24, + bounds: + (await extentToLatLngBounds( + layer?.extent || serviceMetadata.fullExtent + )) || undefined, + attribution, + supportedFormats, + }; + } + + async updateFetchStrategy(fetchStrategy: "auto" | "tiled" | "raw") { + const map = this.map; + if (this.initialFetchStrategy !== fetchStrategy && map) { + this.initialFetchStrategy = fetchStrategy; + this.abortController?.abort(); + const layers = await this.removeFromMap(map); + this.options.fetchStrategy = fetchStrategy; + delete this.featureData; + this.rawFeaturesHaveBeenFetched = false; + await this.addToMap(map); + for (const layer of layers) { + map.addLayer(layer); + } + this.exceededBytesLimit = false; + } + } + + private fireError(e: Error) { + this.map?.fire("error", { + sourceId: this.sourceId, + error: e.message, + }); + } + + /** + * Use ArcGISRESTServiceRequestManager to fetch metadata for the service, + * caching it on the instance for reuse. + */ + private getMetadata() { + if (this.serviceMetadata && this.layerMetadata) { + return Promise.resolve({ + serviceMetadata: this.serviceMetadata, + layers: this.layerMetadata, + }); + } else { + if (/FeatureServer/.test(this.options.url)) { + return this.requestManager + .getFeatureServerMetadata(this.options.url.replace(/\/\d+$/, ""), { + token: this.options.token, + }) + .then(({ serviceMetadata, layers }) => { + this.serviceMetadata = serviceMetadata; + this.layerMetadata = layers; + return { serviceMetadata, layers }; + }); + } else { + return this.requestManager + .getMapServiceMetadata(this.options.url.replace(/\d+[\/]*$/, ""), { + token: this.options.token, + }) + .then(({ serviceMetadata, layers }) => { + this.serviceMetadata = serviceMetadata; + this.layerMetadata = layers; + return { serviceMetadata, layers }; + }); + } + } + } + + get loading() { + if (this.paused) { + return false; + } + if (this.options.fetchStrategy === "raw") { + return Boolean( + this.map?.getSource(this.sourceId) && + this.map?.isSourceLoaded(this.sourceId) === false + ); + } else { + return this._loading; + } + } + + private _glStylePromise?: Promise<{ layers: Layer[]; imageList: ImageList }>; + + private _styleIsResolved = false; + async getGLStyleLayers() { + if (this._glStylePromise) { + return this._glStylePromise; + } else { + this._glStylePromise = new Promise(async (resolve, reject) => { + const { serviceMetadata, layers } = await this.getMetadata(); + const layer = layers.layers.find((l) => l.id === this.layerId); + if (!layer) { + throw new Error("Layer not found"); + } + const styleInfo = styleForFeatureLayer( + this.options.url.replace(/\/\d+$/, ""), + this.layerId, + this.sourceId, + layer + ); + this._styleIsResolved = true; + resolve(styleInfo); + }); + return this._glStylePromise; + } + } + + async getGLSource() { + const { attribution } = await this.getComputedProperties(); + if (this.options.fetchStrategy === "raw") { + return { + type: "geojson", + data: urlForRawGeoJSONData(this.options.url, "*", 6), + attribution: attribution ? attribution : "", + } as GeoJSONSourceRaw; + } else { + return { + type: "geojson", + data: this.featureData || { + type: "FeatureCollection", + features: this.featureData || [], + }, + attribution: attribution ? attribution : "", + } as GeoJSONSourceRaw; + } + } + + addEventListeners(map: Map) { + if (this.map && this.map === map) { + return; + } else if (this.map) { + this.removeEventListeners(map); + } + this.map = map; + if (this.options.fetchStrategy === "raw") { + return; + } + this.QuantizedVectorRequestManager = + getOrCreateQuantizedVectorRequestManager(map); + this._loading = this.featureData ? false : true; + if (!this.rawFeaturesHaveBeenFetched) { + this.fetchFeatures(); + } + } + + removeEventListeners(map: Map) { + delete this.map; + if (this.options.fetchStrategy === "raw") { + return; + } + this.QuantizedVectorRequestManager?.off("update"); + delete this.QuantizedVectorRequestManager; + } + + async addToMap(map: Map) { + const source = await this.getGLSource(); + map.addSource(this.sourceId, source); + this.addEventListeners(map); + return this.sourceId; + } + + private abortController: AbortController | null = null; + + private async fetchFeatures() { + if (this.paused) { + return; + } + if ( + this.options?.fetchStrategy === "tiled" || + this.getCachedAutoFetchStrategy() === "tiled" + ) { + this.options.fetchStrategy = "tiled"; + this.QuantizedVectorRequestManager!.on( + "update", + this.fetchTiles.bind(this) + ); + this.fetchTiles(); + return; + } + if (this.abortController) { + this.abortController.abort(); + } + this.abortController = new AbortController(); + setTimeout(() => { + this.abortController?.abort("timeout"); + }, 10000); + if (this.exceededBytesLimit) { + return; + } + try { + const data = await fetchFeatureCollection( + this.options.url, + 6, + "*", + this.options.fetchStrategy === "raw" + ? 120_000_000 + : this.options.autoFetchByteLimit || + FEATURE_LAYER_RECOMMENDED_BYTE_LIMIT, + this.abortController, + this.options.fetchStrategy === "auto" + ); + this.featureData = data; + const source = this.map?.getSource(this.sourceId); + if (source && source.type === "geojson") { + this.options.fetchStrategy = "raw"; + source.setData(data); + } + this._loading = false; + this.rawFeaturesHaveBeenFetched = true; + } catch (e) { + let shouldFireError = true; + if ( + ("message" in (e as any) && /limit/i.test((e as any).message)) || + this.abortController?.signal?.reason === "timeout" + ) { + this.exceededBytesLimit = true; + if (this.options.fetchStrategy === "auto") { + shouldFireError = false; + this.options.fetchStrategy = "tiled"; + this.QuantizedVectorRequestManager!.on( + "update", + this.fetchTiles.bind(this) + ); + this.fetchTiles(); + this.cacheAutoFetchStrategy("tiled"); + } + } + if (shouldFireError) { + this.fireError(e as Error); + console.error(e); + } + this._loading = false; + } + } + + private cacheAutoFetchStrategy(mode: "raw" | "tiled") { + localStorage.setItem( + `${this.options.url}/fetchStrategy`, + `${mode}-${new Date().getTime()}` + ); + } + + private getCachedAutoFetchStrategy() { + const value = localStorage.getItem(`${this.options.url}/fetchStrategy`); + if (!value || value.length === 0) { + return null; + } else { + const [mode, timestamp] = value.split("-"); + if (new Date().getTime() - parseInt(timestamp) > 1000 * 60 * 60) { + localStorage.setItem(`${this.options.url}/fetchStrategy`, ""); + return null; + } else { + return mode as "raw" | "tiled"; + } + } + } + + private tileFormat = "geojson"; + + private async fetchTiles() { + if (this.paused) { + return; + } + if (this.abortController) { + this.abortController.abort(); + } + this.abortController = new AbortController(); + this._loading = true; + if (!this.QuantizedVectorRequestManager) { + throw new Error("QuantizedVectorRequestManager not initialized"); + } else if (this.options.fetchStrategy !== "tiled") { + throw new Error( + "fetchTiles called when fetchStrategy is not quantized. Was " + + this.options.fetchStrategy + ); + } + const { tiles, tolerance } = + this.QuantizedVectorRequestManager.viewportDetails; + + const fc = { + type: "FeatureCollection", + features: [], + } as FeatureCollection; + const featureIds = new Set(); + try { + let wasAborted = false; + let errorCount = 0; + await Promise.all( + tiles.map((tile) => + (async () => { + const tileBounds = tilebelt.tileToBBOX(tile); + + const extent = { + spatialReference: { + latestWkid: 4326, + wkid: 4326, + }, + xmin: tileBounds[0], + ymin: tileBounds[1], + xmax: tileBounds[2], + ymax: tileBounds[3], + }; + const params = new URLSearchParams({ + f: this.tileFormat, + geometry: JSON.stringify(extent), + outFields: "*", + outSR: "4326", + returnZ: "false", + returnM: "false", + precision: "8", + where: "1=1", + setAttributionFromService: "true", + quantizationParameters: JSON.stringify({ + extent, + tolerance, + mode: "view", + }), + resultType: "tile", + spatialRel: "esriSpatialRelIntersects", + maxAllowableOffset: + this.tileFormat === "geojson" ? tolerance.toString() : "", + geometryType: "esriGeometryEnvelope", + inSR: "4326", + ...this.options.queryParameters, + }); + return fetchWithTTL( + `${`${this.options.url}/query?${params.toString()}`}`, + 60 * 10, + this.cache!, + { signal: this.abortController?.signal }, + `${this.options.url}/query/tiled/${tilebelt.tileToQuadkey( + tile + )}/${params.get("f")}` + ) + .then((response) => + params.get("f") === "pbf" + ? response.arrayBuffer() + : response.json() + ) + .then((data) => { + if (this.abortController?.signal?.aborted) { + return; + } + const collection = + params.get("f") === "pbf" + ? tileDecode(new Uint8Array(data)).featureCollection + : data; + for (const feature of collection.features) { + if (!featureIds.has(feature.id)) { + featureIds.add(feature.id); + fc.features.push(feature); + } + } + }) + .catch((e) => { + if (!/aborted/i.test(e.toString())) { + this.fireError(e as Error); + errorCount++; + console.error(e); + } else { + wasAborted = true; + } + }); + })() + ) + ); + if (this.abortController?.signal?.aborted || wasAborted) { + return; + } + const source = this.map?.getSource(this.sourceId); + if (source && source.type === "geojson" && errorCount < tiles.length) { + source.setData(fc); + this.featureData = fc; + } + this._loading = false; + } catch (e: any) { + if (!/aborted/i.test(e.toString())) { + this.fireError(e as Error); + console.error(e); + } + this._loading = false; + } + } + + async updateLayers(layerSettings: OrderedLayerSettings) { + // TODO: opacity changes + const visible = Boolean(layerSettings.find((l) => l.id === this.sourceId)); + if (this.map) { + const layers = this.map.getStyle().layers || []; + for (const layer of layers) { + if ("source" in layer && layer.source === this.sourceId) { + this.map.setLayoutProperty( + layer.id, + "visibility", + visible ? "visible" : "none" + ); + } + } + } + if (!visible) { + this.pauseUpdates(); + } else if (visible) { + this.resumeUpdates(); + } + } + + paused = false; + + private pauseUpdates() { + if (this.paused === false) { + this.paused = true; + } + } + + private resumeUpdates() { + if (this.paused === true) { + this.paused = false; + if ( + (this.options.fetchStrategy === "raw" || + this.options.fetchStrategy === "auto") && + !this.rawFeaturesHaveBeenFetched + ) { + this.fetchFeatures(); + } else if (this.options.fetchStrategy === "tiled") { + this.fetchTiles(); + } + } + } + + async removeFromMap(map: Map) { + const removedLayers: AnyLayer[] = []; + if (this.map) { + const source = map.getSource(this.sourceId); + if (source) { + const layers = map.getStyle().layers || []; + for (const layer of layers) { + if ("source" in layer && layer.source === this.sourceId) { + removedLayers.push(layer); + map.removeLayer(layer.id); + } + } + map.removeSource(this.sourceId); + } + this.removeEventListeners(map); + } + return removedLayers; + } + + destroy() { + if (this.map) { + this.removeFromMap(this.map); + } + if (this.abortController) { + this.abortController.abort(); + } + } + + async getFetchStrategy() { + if (this.paused) { + this.resumeUpdates(); + } + if (this.options.fetchStrategy === "auto") { + if (this.rawFeaturesHaveBeenFetched) { + return "raw"; + } else if (this.options.fetchStrategy === "auto" && !this.error) { + // wait to finish loading then determine strategy + return new Promise((resolve) => { + const interval = setInterval(() => { + if (this.options.fetchStrategy !== "auto") { + clearInterval(interval); + resolve(this.options.fetchStrategy || "auto"); + } + }, 500); + }); + } else { + // not sure what to do here, punting + return "auto"; + } + } else { + return this.options.fetchStrategy || "raw"; + } + } + + get ready() { + return this._styleIsResolved && Boolean(this._computedMetadata); + } + + async prepare() { + await this.getComputedMetadata(); + return; + } +} + +export function isArcgisFeatureLayerSource( + source: CustomGLSource +): source is ArcGISFeatureLayerSource { + return source.type === "ArcGISFeatureLayer"; +} diff --git a/packages/mapbox-gl-esri-sources/src/ArcGISRESTServiceRequestManager.ts b/packages/mapbox-gl-esri-sources/src/ArcGISRESTServiceRequestManager.ts new file mode 100644 index 000000000..815b75f4d --- /dev/null +++ b/packages/mapbox-gl-esri-sources/src/ArcGISRESTServiceRequestManager.ts @@ -0,0 +1,255 @@ +import { + FeatureServerMetadata, + LayerLegendData, + LayersMetadata, + MapServiceMetadata, +} from "./ServiceMetadata"; + +interface FetchOptions { + signal?: AbortSignal; + token?: string; +} + +export interface MapServiceLegendMetadata { + layers: { + layerId: number; + layerName: string; + layerType: "Feature Layer" | "Raster Layer"; + legend: LayerLegendData[]; + }[]; +} +export class ArcGISRESTServiceRequestManager { + private cache?: Cache; + + constructor(options?: { cacheKey?: string }) { + // TODO: respect cache headers if they exist + if (window.caches) { + const cache = window.caches + .open(options?.cacheKey || "seasketch-arcgis-rest-services") + .then((cache) => { + this.cache = cache; + // evict expired items from cache on startup + cache.keys().then(async (keys) => { + for (const key of keys) { + const res = await cache.match(key); + if (res) { + if (cachedResponseIsExpired(res)) { + cache.delete(key); + } + } + } + }); + }); + } + } + + async getMapServiceMetadata(url: string, options: FetchOptions) { + if (!/rest\/services/.test(url)) { + throw new Error("Invalid ArcGIS REST Service URL"); + } + if (!/MapServer/.test(url)) { + throw new Error("Invalid MapServer URL"); + } + // remove trailing slash if present + url = url.replace(/\/$/, ""); + // remove url params if present + url = url.replace(/\?.*$/, ""); + const params = new URLSearchParams(); + params.set("f", "json"); + if (options?.token) { + params.set("token", options.token); + } + + const requestUrl = `${url}?${params.toString()}`; + const serviceMetadata = await this.fetch( + requestUrl, + options?.signal + ); + const layers = await this.fetch( + `${url}/layers?${params.toString()}` + ); + if ((layers as any).error) { + throw new Error((layers as any).error.message); + } + return { serviceMetadata, layers }; + } + + async getFeatureServerMetadata(url: string, options: FetchOptions) { + // remove trailing slash if present + url = url.replace(/\/$/, ""); + if (!/rest\/services/.test(url)) { + throw new Error("Invalid ArcGIS REST Service URL"); + } + if (!/FeatureServer/.test(url)) { + throw new Error("Invalid FeatureServer URL"); + } + // make sure the url does not include a feature layer id + if (/\d+$/.test(url)) { + throw new Error("Invalid FeatureServer URL"); + } + // remove url params if present + url = url.replace(/\?.*$/, ""); + const params = new URLSearchParams(); + params.set("f", "json"); + if (options?.token) { + params.set("token", options.token); + } + + const requestUrl = `${url}?${params.toString()}`; + const serviceMetadata = await this.fetch( + requestUrl, + options?.signal + ); + const layers = await this.fetch( + `${url}/layers?${params.toString()}` + ); + if ((layers as any).error) { + throw new Error((layers as any).error.message); + } + return { serviceMetadata, layers }; + } + + async getCatalogItems(url: string, options?: FetchOptions) { + if (!/rest\/services/.test(url)) { + throw new Error("Invalid ArcGIS REST Service URL"); + } + // remove trailing slash if present + url = url.replace(/\/$/, ""); + // remove url params if present + url = url.replace(/\?.*$/, ""); + const params = new URLSearchParams(); + params.set("f", "json"); + if (options?.token) { + params.set("token", options?.token); + } + + const requestUrl = `${url}?${params.toString()}`; + const response = await this.fetch<{ + currentVersion: number; + folders: string[]; + services: { + name: string; + type: + | "MapServer" + | "FeatureServer" + | "GPServer" + | "GeometryServer" + | "ImageServer" + | "GeocodeServer" + | string; + }[]; + }>(requestUrl, options?.signal); + return response; + } + + private inFlightRequests: { [url: string]: Promise } = {}; + + private async fetch(url: string, signal?: AbortSignal) { + if (url in this.inFlightRequests) { + return this.inFlightRequests[url].then((json) => json as T); + } + const cache = await this.cache; + this.inFlightRequests[url] = fetchWithTTL(url, 60 * 300, cache, { + signal, + }).then((r) => r.json()); + return new Promise((resolve, reject) => { + this.inFlightRequests[url] + .then((json) => { + if (json["error"]) { + reject(new Error(json["error"].message)); + } else { + resolve(json as T); + } + }) + .catch(reject) + .finally(() => { + delete this.inFlightRequests[url]; + }); + }); + } + + async getLegendMetadata(url: string, token?: string) { + if (!/rest\/services/.test(url)) { + throw new Error("Invalid ArcGIS REST Service URL"); + } + if (!/MapServer/.test(url) && !/FeatureServer/.test(url)) { + throw new Error("Invalid MapServer or FeatureServer URL"); + } + // remove trailing slash if present + url = url.replace(/\/$/, ""); + // remove url params if present + url = url.replace(/\?.*$/, ""); + const params = new URLSearchParams(); + params.set("f", "json"); + if (token) { + params.set("token", token); + } + + const requestUrl = `${url}/legend?${params.toString()}`; + const response = await this.fetch(requestUrl); + return response; + } +} + +function cachedResponseIsExpired(response: Response) { + const cacheControlHeader = response.headers.get("Cache-Control"); + if (cacheControlHeader) { + const expires = /expires=(.*)/i.exec(cacheControlHeader); + if (expires) { + const expiration = new Date(expires[1]); + if (new Date().getTime() > expiration.getTime()) { + return true; + } else { + return false; + } + } + } + return false; +} + +export async function fetchWithTTL( + url: string, + ttl: number, + cache?: Cache, + options?: RequestInit, + cacheKey?: string +): Promise { + if (!options?.signal?.aborted) { + const request = new Request(url, options); + if (options?.signal?.aborted) { + Promise.reject("aborted"); + } + let cachedResponse = await cache?.match( + cacheKey ? new URL(cacheKey) : request + ); + + if (cachedResponse && cachedResponseIsExpired(cachedResponse)) { + cache?.delete(cacheKey ? new URL(cacheKey) : request); + cachedResponse = undefined; + } + if (cachedResponse && cachedResponse.ok) { + return cachedResponse; + } else { + const response = await fetch(url, options); + if (!options?.signal?.aborted) { + const headers = new Headers(response.headers); + headers.set( + "Cache-Control", + `Expires=${new Date(new Date().getTime() + 1000 * ttl).toUTCString()}` + ); + const copy = response.clone(); + const clone = new Response(copy.body, { + headers, + status: response.status, + statusText: response.statusText, + }); + if (clone.ok && clone.status === 200) { + cache?.put(cacheKey || url, clone).catch((e) => { + // do nothing. can happen if request is aborted + }); + } + } + return await response; + } + } +} diff --git a/packages/mapbox-gl-esri-sources/src/ArcGISTiledMapService.ts b/packages/mapbox-gl-esri-sources/src/ArcGISTiledMapService.ts new file mode 100644 index 000000000..73417229e --- /dev/null +++ b/packages/mapbox-gl-esri-sources/src/ArcGISTiledMapService.ts @@ -0,0 +1,351 @@ +import { AnyLayer, Map, RasterLayer, RasterSource } from "mapbox-gl"; +import { ArcGISRESTServiceRequestManager } from "./ArcGISRESTServiceRequestManager"; +import { + ComputedMetadata, + CustomGLSource, + CustomGLSourceOptions, + CustomSourceType, + LegendItem, + OrderedLayerSettings, +} from "./CustomGLSource"; +import { v4 as uuid } from "uuid"; +import { LayersMetadata, MapServiceMetadata } from "./ServiceMetadata"; +import { + contentOrFalse, + extentToLatLngBounds, + generateMetadataForLayer, + makeLegend, + replaceSource, +} from "./utils"; +import * as tilebelt from "@mapbox/tilebelt"; + +export interface ArcGISTiledMapServiceOptions extends CustomGLSourceOptions { + url: string; + supportHighDpiDisplays?: boolean; + token?: string; + /* Overrides the value derived from the service metadata */ + maxZoom?: number; + /** + * Use to set a developer api key for accessing Esri basemaps. + * Not needed for other services + */ + developerApiKey?: string; +} + +/** + * CustomGLSource used to add an ArcGIS Tile MapService. + */ +export class ArcGISTiledMapService + implements CustomGLSource +{ + url: string; + sourceId: string; + options: ArcGISTiledMapServiceOptions; + private map?: Map; + private requestManager: ArcGISRESTServiceRequestManager; + private serviceMetadata?: MapServiceMetadata; + private layerMetadata?: LayersMetadata; + error?: string; + type: CustomSourceType; + + /** + * + * @param requestManager ArcGISRESTServiceRequestManager instance + * @param options.url URL to ArcGIS REST MapServer (should end in /MapServer) + * @param options.supportHighDpiDisplays If true, will detect high-dpi displays and request more tiles at higher resolution + * @param options.credentials Optional. If provided, will use these credentials to request a token for the service. + * + */ + constructor( + requestManager: ArcGISRESTServiceRequestManager, + options: ArcGISTiledMapServiceOptions + ) { + this.type = "ArcGISTiledMapService"; + // remove trailing slash if present + options.url = options.url.replace(/\/$/, ""); + this.url = options.url; + if (!/rest\/services/.test(options.url) || !/MapServer/.test(options.url)) { + throw new Error("Invalid ArcGIS REST Service URL"); + } + this.requestManager = requestManager; + this.sourceId = options.sourceId || uuid(); + this.options = options; + } + + /** + * Use ArcGISRESTServiceRequestManager to fetch metadata for the service, + * caching it on the instance for reuse. + */ + private getMetadata() { + if (this.serviceMetadata && this.layerMetadata) { + return Promise.resolve({ + serviceMetadata: this.serviceMetadata, + layers: this.layerMetadata, + }); + } else { + return this.requestManager + .getMapServiceMetadata(this.options.url, { + token: this.options.token, + }) + .then(({ serviceMetadata, layers }) => { + this.serviceMetadata = serviceMetadata; + this.layerMetadata = layers; + return { serviceMetadata, layers }; + }); + } + } + + private _computedMetadata?: ComputedMetadata; + + /** + * Returns computed metadata for the service, including bounds, minzoom, maxzoom, and attribution. + * @returns ComputedMetadata + * @throws Error if metadata is not available + * @throws Error if tileInfo is not available + * */ + async getComputedMetadata(): Promise { + try { + if (!this._computedMetadata) { + const { serviceMetadata, layers } = await this.getMetadata(); + const { bounds, minzoom, maxzoom, tileSize, attribution } = + await this.getComputedProperties(); + const legendData = await this.requestManager.getLegendMetadata( + this.options.url + ); + const results = /\/([^/]+)\/MapServer/.exec(this.options.url); + let label = results && results.length >= 1 ? results[1] : false; + if (!label) { + if (this.layerMetadata?.layers?.[0]) { + label = this.layerMetadata.layers[0].name; + } + } + + this._computedMetadata = { + bounds: bounds || undefined, + minzoom, + maxzoom, + attribution, + tableOfContentsItems: [ + { + type: "data", + id: this.sourceId, + label: label || "Layer", + defaultVisibility: true, + metadata: generateMetadataForLayer( + this.options.url, + this.serviceMetadata!, + this.layerMetadata!.layers[0] + ), + legend: makeLegend(legendData, legendData.layers[0].layerId), + }, + ], + supportsDynamicRendering: { + layerOrder: false, + layerOpacity: false, + layerVisibility: false, + }, + }; + } + return this._computedMetadata; + } catch (e: any) { + this.error = e.toString(); + throw e; + } + } + + async getThumbnailForCurrentExtent() { + if (!this.map) { + throw new Error("Map not set"); + } + const map = this.map; + const bounds = map.getBounds(); + const boundsArray = bounds.toArray(); + const primaryTile = tilebelt.bboxToTile([ + boundsArray[0][0], + boundsArray[0][1], + boundsArray[1][0], + boundsArray[1][1], + ]); + let [x, y, z] = primaryTile; + const url = `${this.options.url}/tile/${z}/${y}/${x}`; + const res = await fetch(url); + if (res.ok) { + return url; + } else { + const children = tilebelt.getChildren(primaryTile); + for (const child of children) { + let [x, y, z] = primaryTile; + const res = await fetch(url); + if (res.ok) { + return url; + } + } + throw new Error("Could not find valid thumbnail tile"); + } + } + + /** + * Returns true if the source is not yet loaded. Will return to false if tiles + * are loading when the map is moved. + */ + get loading() { + return Boolean( + this.map?.getSource(this.sourceId) && + this.map?.isSourceLoaded(this.sourceId) === false + ); + } + + /** + * Private method used as the basis for getComputedMetadata and also used + * when generating the source data for addToMap. + * @returns Computed properties for the service, including bounds, minzoom, maxzoom, and attribution. + */ + private async getComputedProperties() { + const { serviceMetadata, layers } = await this.getMetadata(); + const levels = serviceMetadata.tileInfo?.lods.map((l) => l.level) || []; + const attribution = + contentOrFalse(layers.layers[0].copyrightText) || + contentOrFalse(serviceMetadata.copyrightText) || + contentOrFalse(serviceMetadata.documentInfo?.Author) || + undefined; + const minzoom = Math.min(...levels); + const maxzoom = Math.max(...levels); + if (!serviceMetadata.tileInfo?.rows) { + throw new Error("Invalid tile info"); + } + return { + minzoom, + maxzoom, + bounds: await extentToLatLngBounds(serviceMetadata.fullExtent), + tileSize: serviceMetadata.tileInfo!.rows, + attribution, + }; + } + + /** + * Add source to map. Does not add any layers to the map. + * @param map Mapbox GL JS Map + * @returns sourceId + */ + async addToMap(map: mapboxgl.Map) { + this.map = map; + const sourceData = await this.getGLSource(); + // It's possible that the map has changed since we started fetching metadata + if (this.map.getSource(this.sourceId)) { + replaceSource(this.sourceId, this.map, sourceData); + } else { + this.map.addSource(this.sourceId, sourceData); + } + return this.sourceId; + } + + async getGLSource() { + let { minzoom, maxzoom, bounds, tileSize, attribution } = + await this.getComputedProperties(); + attribution = this.options.attributionOverride || attribution; + let url = `${this.options.url}/tile/{z}/{y}/{x}`; + if ( + url.indexOf("services.arcgisonline.com") !== -1 && + this.options.developerApiKey + ) { + url = `${url}?token=${this.options.developerApiKey}`; + } + const sourceData = { + type: "raster", + tiles: [url], + tileSize, + minzoom, + maxzoom: this.options.maxZoom || maxzoom, + attribution, + ...(bounds ? { bounds } : {}), + } as RasterSource; + return sourceData; + } + + /** + * Returns a raster layer for the source. + * @returns RasterLayer[] + */ + async getGLStyleLayers() { + return { + layers: [ + { + type: "raster", + source: this.sourceId, + id: uuid(), + paint: { + "raster-fade-duration": 300, + }, + }, + ] as RasterLayer[], + }; + } + + /** + * Remove source from map. If there are any layers on the map that use this + * source, they will also be removed. + * @param map Mapbox GL JS Map + */ + removeFromMap(map: Map) { + const removedLayers: AnyLayer[] = []; + if (map.getSource(this.sourceId)) { + const layers = map.getStyle().layers || []; + for (const layer of layers) { + if ("source" in layer && layer.source === this.sourceId) { + map.removeLayer(layer.id); + removedLayers.push(layer); + } + } + map.removeSource(this.sourceId); + this.map = undefined; + } + return removedLayers; + } + + /** + * Removes the source from the map and removes any event listeners + */ + destroy(): void { + if (this.map) { + this.removeFromMap(this.map); + } + } + + async updateMaxZoom(maxZoom: number | undefined) { + const currentMaxZoom = (await this.getGLSource()).maxzoom; + if (currentMaxZoom !== maxZoom) { + this.options.maxZoom = maxZoom; + if (this.map) { + const map = this.map; + const removedLayers = this.removeFromMap(map); + this.addToMap(map); + for (const layer of removedLayers) { + map.addLayer(layer); + } + } + } + } + + /** noop */ + updateLayers(layers: OrderedLayerSettings) {} + + get ready() { + return Boolean(this._computedMetadata); + } + + async prepare() { + await this.getComputedMetadata(); + return; + } + + addEventListeners(map: Map) { + this.map = map; + } + removeEventListeners(map: Map) {} +} + +export function isArcGISTiledMapservice( + source: CustomGLSource +): source is ArcGISTiledMapService { + return source.type === "ArcGISTiledMapService"; +} diff --git a/packages/mapbox-gl-esri-sources/src/ArcGISVectorSource.ts b/packages/mapbox-gl-esri-sources/src/ArcGISVectorSource.ts new file mode 100644 index 000000000..bec12a868 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/src/ArcGISVectorSource.ts @@ -0,0 +1,352 @@ +import { Map, GeoJSONSource } from "mapbox-gl"; +import { Feature, FeatureCollection } from "geojson"; +// @ts-ignore +// import { arcgisToGeoJSON } from "@terraformer/arcgis"; + +export interface ArcGISVectorSourceOptions { + /** + * Number of digits precision after the decimal to fetch from the origin + * server. The default of 6 a good compromise between precision (~10cm) and + * download size. + * @type {number} + * @default 6 + */ + geometryPrecision?: number; + /** + * Set to false if the service is known not to support pagination, which will + * be true for services that have only a single feature but is also indicated + * by the main service metadata. + * + * If left to the default of true and the server does not support pagination, + * the initial fetch of data will fail but a second request will be + * constructed without pagination parameters which should succeed. + * + * @type {boolean} + * @default true + */ + supportsPagination?: boolean; + /** + * Can be used to limit fields in GeoJSON properties. + * @type {string} + * @default "*" + */ + outFields?: string; + bytesLimit?: number; + onError?: (error: Error) => void; +} + +/** + * Add ArcGIS Feature Layers to MapBox GL JS maps as a geojson source. These + * data sources can be styled using output from + * {@link styleForFeatureLayer } or custom layers that + * reference the provided source id. + * + * ### Usage + * + * ```typescript + * import { ArcGISVectorSource } from "mapbox-gl-esri-sources"; + * + * // setup map... + * + * const esriSource = new ArcGISVectorSource( + * map, + * 'cities-source-id', + * "https://sampleserver6.arcgisonline.com/arcgis/rest/services/SampleWorldCities/MapServer/0"), + * { + * geometryPrecision: 5, + * outFields: "POP,CITY_NAME" + * } + * ); + * ``` + * @class ArcGISVectorSource + */ +export class ArcGISVectorSource { + private data = { + type: "FeatureCollection", + features: [], + } as FeatureCollection; + /** + * GeoJSONSource instance added to the map + * @type {GeoJSONSource} + */ + protected source: GeoJSONSource; + private _id: string; + private baseUrl: string; + private options: + | undefined + | { + geometryPrecision?: number; + }; + private map: Map; + private outFields = "*"; + private supportsPagination = true; + /** Set to true when source is fetching data */ + private _loading = true; + + /** + * Creates an instance of ArcGISVectorSource. + * @param {Map} map MapBox GL JS map instance where source will be added + * @param {string} id ID will be assigned to the GeoJSONSource instance + * @param {string} url Base url for an [ArcGIS Server Feature Layer](https://developers.arcgis.com/rest/services-reference/layer-table.htm). Should end in _/MapServer/0..n_ + */ + constructor( + map: Map, + id: string, + url: string, + options?: ArcGISVectorSourceOptions + ) { + this._id = id; + this.baseUrl = url; + this.options = options; + this.map = map; + this.map.addSource(this._id, { + data: this.data, + type: "geojson", + }); + if ( + options && + "supportsPagination" in options && + options["supportsPagination"] === false + ) { + this.supportsPagination = false; + } + if (options && options.outFields) { + this.outFields = options.outFields; + } + this.source = this.map.getSource(this._id) as GeoJSONSource; + let hadError = false; + const onError = (e: Error) => { + hadError = true; + this._loading = false; + this.map.fire("error", { + source: this.source, + sourceId: this._id, + error: e, + }); + }; + this.map.fire("dataloading", { + source: this.source, + sourceId: this._id, + dataType: "source", + isSourceLoaded: false, + sourceDataType: "content", + }); + fetchFeatureLayerData( + this.baseUrl, + this.outFields, + onError, + this.options?.geometryPrecision, + null, + null, + false, + 1000, + options?.bytesLimit + ) + .then((fc) => { + this._loading = false; + if (!hadError) { + this.source.setData(fc); + } + }) + .catch(onError); + } + + get loading(): boolean { + return this._loading; + } + + get id(): string { + return this._id; + } + + /** + * Remove the source from the map and any related event listeners + */ + destroy() { + this.map.removeSource(this._id); + } +} + +export async function fetchFeatureLayerData( + url: string, + outFields: string, + onError: (error: Error) => void, + geometryPrecision = 6, + abortController: AbortController | null = null, + onPageReceived: + | (( + bytes: number, + loadedFeatures: number, + estimatedFeatures: number + ) => void) + | null = null, + disablePagination = false, + pageSize = 1000, + bytesLimit?: number +) { + const featureCollection: FeatureCollection = { + type: "FeatureCollection", + features: [], + }; + const params = new URLSearchParams({ + inSR: "4326", + outSR: "4326", + where: "1>0", + outFields, + returnGeometry: "true", + geometryPrecision: geometryPrecision.toString(), + returnIdsOnly: "false", + f: "geojson", + }); + await fetchData( + url, + params, + featureCollection, + onError, + abortController || new AbortController(), + onPageReceived, + disablePagination, + pageSize, + bytesLimit + ); + return featureCollection; +} + +async function fetchData( + baseUrl: string, + params: URLSearchParams, + featureCollection: FeatureCollection, + onError: (error: Error) => void, + abortController: AbortController, + onPageReceived: + | (( + bytes: number, + loadedFeatures: number, + estimatedFeatures: number + ) => void) + | null, + disablePagination = false, + pageSize = 1000, + bytesLimit?: number, + bytesReceived?: number, + objectIdFieldName?: string, + expectedFeatureCount?: number +) { + bytesReceived = bytesReceived || 0; + const decoder = new TextDecoder("utf-8"); + params.set("returnIdsOnly", "false"); + if (featureCollection.features.length > 0) { + // fetch next page using objectIds + let featureIds: number[]; + params.delete("where"); + params.delete("resultOffset"); + params.delete("resultRecordCount"); + params.set("orderByFields", objectIdFieldName!); + const lastFeature = + featureCollection.features[featureCollection.features.length - 1]; + params.set("where", `${objectIdFieldName}>${lastFeature.id}`); + } + const response = await fetch(`${baseUrl}/query?${params.toString()}`, { + mode: "cors", + signal: abortController.signal, + }); + const str = await response.text(); + bytesReceived += byteLength(str); + console.log("got", bytesReceived, response); + if (bytesLimit && bytesReceived >= bytesLimit) { + const e = new Error( + `Exceeded bytesLimit. ${bytesReceived} >= ${bytesLimit}` + ); + return onError(e); + } + + const fc = JSON.parse(str) as FeatureCollection & { + error?: { + code: number; + message: string; + }; + exceededTransferLimit?: boolean; + }; + console.log("fc", fc); + if (fc.error) { + console.log("fc.error found", fc.error); + return onError(new Error(fc.error.message)); + } else { + featureCollection.features.push(...fc.features); + + if ( + fc.exceededTransferLimit || + // @ts-ignore + ("properties" in fc && fc.properties.exceededTransferLimit) + ) { + if (!objectIdFieldName) { + // Fetch objectIds to do manual paging + params.set("returnIdsOnly", "true"); + try { + const r = await fetch(`${baseUrl}/query?${params.toString()}`, { + mode: "cors", + signal: abortController.signal, + }); + const featureIds = featureCollection.features.map((f) => f.id); + const objectIdParameters = await r.json(); + expectedFeatureCount = objectIdParameters.objectIds + ? objectIdParameters.objectIds.length + : objectIdParameters.properties.objectIds.length; + objectIdFieldName = + "properties" in objectIdParameters + ? objectIdParameters.properties.objectIdFieldName + : objectIdParameters.objectIdFieldName; + } catch (e) { + return onError(e as Error); + } + } + + console.log("on page receivved", onPageReceived); + if (onPageReceived) { + onPageReceived( + bytesReceived, + featureCollection.features.length, + expectedFeatureCount! + ); + } + + await fetchData( + baseUrl, + params, + featureCollection, + onError, + abortController, + onPageReceived, + disablePagination, + pageSize, + bytesLimit, + bytesReceived, + objectIdFieldName, + expectedFeatureCount + ); + } + } + + if (onPageReceived) { + onPageReceived( + bytesReceived, + featureCollection.features.length, + expectedFeatureCount! + ); + } + + return bytesReceived; +} + +// https://stackoverflow.com/a/23329386/299467 +function byteLength(str: string) { + // returns the byte length of an utf8 string + var s = str.length; + for (var i = str.length - 1; i >= 0; i--) { + var code = str.charCodeAt(i); + if (code > 0x7f && code <= 0x7ff) s++; + else if (code > 0x7ff && code <= 0xffff) s += 2; + if (code >= 0xdc00 && code <= 0xdfff) i--; //trail surrogate + } + return s; +} diff --git a/packages/mapbox-gl-esri-sources/src/CustomGLSource.ts b/packages/mapbox-gl-esri-sources/src/CustomGLSource.ts new file mode 100644 index 000000000..2b4850da6 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/src/CustomGLSource.ts @@ -0,0 +1,147 @@ +import { AnyLayer, AnySourceData, Layer, Map } from "mapbox-gl"; +import { ImageList } from "./ImageList"; + +export interface CustomGLSourceOptions { + /** Optional. If not provided a uuid will be used. */ + sourceId?: string; + attributionOverride?: string; +} + +/** + * LegendItems are assumed to be static and need not be refreshed after updating + * layer visibility. + */ +export interface LegendItem { + /** Use for jsx key, no more */ + id: string; + label: string; + imageUrl: string; + imageWidth?: number; + imageHeight?: number; +} + +/** + * SingleImage legends should be updated whenever layer visibility changes + */ +export interface SingleImageLegend { + url: string; +} + +export interface DynamicRenderingSupportOptions { + layerOrder: boolean; + layerOpacity: boolean; + layerVisibility: boolean; +} + +export interface FolderTableOfContentsItem { + type: "folder"; + id: string; + label: string; + defaultVisibility: boolean; + parentId?: string; +} + +export interface DataTableOfContentsItem { + type: "data"; + id: string; + label: string; + defaultVisibility: boolean; + /** Metadata as prosemirror document */ + metadata: { + type: string; + content: ({ type: string } & any)[]; + }; + legend?: LegendItem[]; + glStyle?: { layers: Layer[]; imageList?: ImageList }; + parentId?: string; +} +export interface ComputedMetadata { + /** xmin, ymin, xmax, ymax */ + bounds?: [number, number, number, number]; + minzoom: number; + maxzoom: number; + attribution?: string; + tableOfContentsItems: (FolderTableOfContentsItem | DataTableOfContentsItem)[]; + supportsDynamicRendering: DynamicRenderingSupportOptions; +} + +export interface LayerSettings { + id: string; + opacity: number; +} + +export type CustomSourceType = + | "ArcGISFeatureLayer" + | "ArcGISTiledMapService" + | "ArcGISDynamicMapService"; + +export type OrderedLayerSettings = LayerSettings[]; +/** + * CustomGLSources are used to add custom data sources to a Mapbox GL JS map. + * Used to support ArcGIS, WMS, (and other?) sources using SeaSketch's + * MapContextManager. + */ +export interface CustomGLSource< + T extends CustomGLSourceOptions, + LegendType = LegendItem[] | SingleImageLegend +> { + url: string; + sourceId: string; + type: CustomSourceType; + /** + * CustomGLSources should trigger data and dataload events on the Map, but + * it won't be possible to call Map.isSourceLoaded(sourceId) on some custom + * types (such as those that rely on geojson layers). This property should + * be used instead. + */ + loading: boolean; + + /** + * Present if there was a problem loading the source. + */ + error?: string; + + // new ( + // requestManager: ArcGISRESTServiceRequestManager, + // options?: T + // ): CustomGLSource; + /** + * Add source to map. Does not add any layers to the map. + * @returns Source ID + * @param map Mapbox GL JS Map + */ + addToMap(map: Map): Promise; + /** + * Remove source from map, including any related layers. + * @param map Mapbox GL JS Map + * @throws If the source is not on the map + */ + removeFromMap(map: Map): void; + /** Removes the source from the map and removes any event listeners */ + destroy(): void; + /** + * If provided, this source uses a single image to represent all sublayers in + * the legend. It will need to be updated with a new image each time rendering + * options are changed. + * */ + getLegend?(): Promise; + getGLSource(map: Map): Promise; + getGLStyleLayers(): Promise<{ layers: Layer[]; imageList?: ImageList }>; + // handling on the server now + getComputedMetadata(): Promise; + updateLayers(layers: OrderedLayerSettings): void; + /** + * Whether computed metadata and layers are cached and ready to be used + * immediately. + * Used by SeaSketch's MapContextManager to determine whether to add + * layers to the map style immediately or wait for the source to be ready. + */ + ready: boolean; + /** + * Make any necessary requests to gather metadata needed to add layers to the + * map. When this promise returns source.ready should be true. + */ + prepare: () => Promise; + addEventListeners: (map: Map) => void; + removeEventListeners: (map: Map) => void; +} diff --git a/packages/mapbox-gl-esri-feature-layers/lib/ImageList.ts b/packages/mapbox-gl-esri-sources/src/ImageList.ts similarity index 92% rename from packages/mapbox-gl-esri-feature-layers/lib/ImageList.ts rename to packages/mapbox-gl-esri-sources/src/ImageList.ts index 18ea9f0b6..e5dcafa03 100644 --- a/packages/mapbox-gl-esri-feature-layers/lib/ImageList.ts +++ b/packages/mapbox-gl-esri-sources/src/ImageList.ts @@ -26,12 +26,8 @@ export class ImageList { private imageSets: (ImageSet | Promise)[] = []; private supportsHighDPILegends = false; - constructor( - arcGISVersion?: number, - isFeatureServer?: boolean, - imageSets?: ImageSet[] - ) { - if (!isFeatureServer && arcGISVersion && arcGISVersion >= 10.6) { + constructor(arcGISVersion?: number, imageSets?: ImageSet[]) { + if (arcGISVersion && arcGISVersion >= 10.6) { this.supportsHighDPILegends = true; } if (imageSets) { @@ -105,19 +101,22 @@ export class ImageList { }, ], }; - const legend2x = await fetchLegendImage( - serviceBaseUrl, - sublayer, - legendIndex, - 2 - ); - const legend3x = await fetchLegendImage( - serviceBaseUrl, - sublayer, - legendIndex, - 3 - ); - imageSet.images.push(legend2x, legend3x); + // FeatureServers don't have a legend endpoint + if (/MapServer/.test(serviceBaseUrl)) { + const legend2x = await fetchLegendImage( + serviceBaseUrl, + sublayer, + legendIndex, + 2 + ); + const legend3x = await fetchLegendImage( + serviceBaseUrl, + sublayer, + legendIndex, + 3 + ); + imageSet.images.push(legend2x, legend3x); + } resolve(imageSet); }) ); @@ -304,6 +303,7 @@ async function fetchLegendImage( pixelRatio: 2 | 3 ): Promise { const legendData = await fetchLegendData(serviceRoot, pixelRatio); + console.log("legendData", serviceRoot, legendData); const sublayerData = legendData.layers.find( (lyr: any) => lyr.layerId === sublayer ); diff --git a/packages/mapbox-gl-esri-sources/src/QuantizedVectorRequestManager.ts b/packages/mapbox-gl-esri-sources/src/QuantizedVectorRequestManager.ts new file mode 100644 index 000000000..5a62a3a85 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/src/QuantizedVectorRequestManager.ts @@ -0,0 +1,196 @@ +// Inspired heavily by +// https://github.com/rowanwins/mapbox-gl-arcgis-featureserver. +// Why go my own way? +// 1. Have a single request manager handle events from a single map for many +// services simultaneously. +// 2. Support switching from raw to quantized after hitting a byte limit. +// 3. Load tiles while map is panning rather than waiting until moveend event. +import { FeatureCollection } from "geojson"; +import { GeoJSONSource, LngLatBounds, Map } from "mapbox-gl"; +const EventEmitter = require("eventemitter3"); +const tilebelt = require("@mapbox/tilebelt"); +const debounce = require("lodash.debounce"); + +const DEBUG = false; + +export class QuantizedVectorRequestManager extends EventEmitter { + map: Map; + constructor(map: Map) { + super(); + this.map = map; + this.addEventListeners(map); + if (DEBUG) { + this.addDebugLayer(); + } + } + + private addDebugLayer() { + this.map.addSource("debug-quantized-vector-request-manager", { + type: "geojson", + data: { + type: "FeatureCollection", + features: [], + }, + }); + this.map.addLayer({ + id: "debug-quantized-vector-request-manager", + type: "line", + source: "debug-quantized-vector-request-manager", + paint: { + "line-color": "red", + "line-width": 2, + }, + }); + } + + private removeDebugLayer() { + this.map.removeLayer("debug-quantized-vector-request-manager"); + this.map.removeSource("debug-quantized-vector-request-manager"); + } + + private addEventListeners(map: Map) { + map.on("moveend", this.updateSources); + map.on("move", this.debouncedUpdateSources); + map.on("remove", this.removeEventListeners); + } + + private removeEventListeners = (map: Map) => { + map.off("moveend", this.updateSources); + map.off("move", this.debouncedUpdateSources); + map.off("remove", this.removeEventListeners); + try { + this.removeDebugLayer(); + } catch (e) { + // do nothing + } + }; + + private displayedTiles = ""; + _viewPortDetails: { tiles: Tile[]; tolerance: number } = { + tiles: [], + tolerance: 0, + }; + + get viewportDetails() { + if (!this._viewPortDetails.tiles.length) { + this.intializeViewportDetails(); + } + return this._viewPortDetails; + } + + intializeViewportDetails() { + if (!this._viewPortDetails.tiles.length) { + const bounds = this.map.getBounds(); + const boundsArray = bounds.toArray(); + const tiles = this.getTilesForBounds(bounds); + const key = tiles + .map((t) => tilebelt.tileToQuadkey(t)) + .sort() + .join(","); + this.displayedTiles = key; + const mapWidth = Math.abs(boundsArray[1][0] - boundsArray[0][0]); + const tolerance = (mapWidth / this.map.getCanvas().width) * 0.4; + this._viewPortDetails = { + tiles, + tolerance, + }; + } + } + + private updateSources = () => { + const bounds = this.map.getBounds(); + const boundsArray = bounds.toArray(); + const tiles = this.getTilesForBounds(bounds); + const key = tiles + .map((t) => tilebelt.tileToQuadkey(t)) + .sort() + .join(","); + if (key !== this.displayedTiles) { + this.displayedTiles = key; + const mapWidth = Math.abs(boundsArray[1][0] - boundsArray[0][0]); + const tolerance = (mapWidth / this.map.getCanvas().width) * 0.4; + this._viewPortDetails = { + tiles, + tolerance, + }; + this.emit("update", { tiles }); + } + if (DEBUG) { + this.updateDebugLayer(tiles); + } + }; + + private updateDebugLayer(tiles: Tile[]) { + const source = this.map.getSource( + "debug-quantized-vector-request-manager" + ) as GeoJSONSource; + const fc = { + type: "FeatureCollection", + features: tiles.map((t: any) => ({ + type: "Feature", + properties: { label: `${t[2]}/${t[0]}/${1}` }, + geometry: tilebelt.tileToGeoJSON(t), + })), + }; + source.setData(fc as FeatureCollection); + } + + private getTilesForBounds(bounds: LngLatBounds) { + const z = this.map.getZoom(); + const boundsArray = bounds.toArray(); + const primaryTile = tilebelt.bboxToTile([ + boundsArray[0][0], + boundsArray[0][1], + boundsArray[1][0], + boundsArray[1][1], + ]); + const zoomLevel = 2 * Math.floor(z / 2); + const tilesToRequest: Tile[] = []; + + if (primaryTile[2] < zoomLevel) { + let candidateTiles: Tile[] = tilebelt.getChildren(primaryTile); + let minZoomOfCandidates = candidateTiles[0][2]; + while (minZoomOfCandidates < zoomLevel) { + const newCandidateTiles: Tile[] = []; + candidateTiles.forEach((t) => + newCandidateTiles.push(...tilebelt.getChildren(t)) + ); + candidateTiles = newCandidateTiles; + minZoomOfCandidates = candidateTiles[0][2]; + } + + for (let index = 0; index < candidateTiles.length; index++) { + if (this.doesTileOverlapBbox(candidateTiles[index], boundsArray)) { + tilesToRequest.push(candidateTiles[index]); + } + } + } else { + tilesToRequest.push(primaryTile); + } + return tilesToRequest; + } + + private doesTileOverlapBbox(tile: number[], bbox: number[][]) { + const tileBounds = tile.length === 4 ? tile : tilebelt.tileToBBOX(tile); + if (tileBounds[2] < bbox[0][0]) return false; + if (tileBounds[0] > bbox[1][0]) return false; + if (tileBounds[3] < bbox[0][1]) return false; + if (tileBounds[1] > bbox[1][1]) return false; + return true; + } + + private debouncedUpdateSources = debounce(this.updateSources, 100, { + maxWait: 200, + }); +} + +const managers = new WeakMap(); + +export function getOrCreateQuantizedVectorRequestManager(map: Map) { + if (!managers.has(map)) { + managers.set(map, new QuantizedVectorRequestManager(map)); + } + return managers.get(map); +} + +type Tile = [number, number, number]; diff --git a/packages/mapbox-gl-esri-sources/src/ServiceMetadata.ts b/packages/mapbox-gl-esri-sources/src/ServiceMetadata.ts new file mode 100644 index 000000000..44193c898 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/src/ServiceMetadata.ts @@ -0,0 +1,150 @@ +import { SpatialReference, esriGeometryType } from "arcgis-rest-api"; +export interface Extent { + xmin: number; + ymin: number; + xmax: number; + ymax: number; + spatialReference: SpatialReference; +} + +export interface SimpleLayerInfo { + id: number; + name: string; + parentLayerId: number; + defaultVisibility: boolean; + subLayerIds: number[] | null; + minScale: number; + maxScale: number; +} + +export interface MapServiceMetadata { + /** ArcGIS Server REST API version */ + currentVersion: number; + serviceDescription: string; + /** Typically just "Layers". Better to use url slug as a name */ + mapName: string; + description: string; + copyrightText: string; + supportsDynamicLayers: boolean; + spatialReference: SpatialReference; + singleFusedMapCache: boolean; + tileInfo?: { + rows: number; + cols: number; + dpi: number; + format: string; + compressionQuality: number; + origin: { + x: number; + y: number; + }; + spatialReference: SpatialReference; + lods: { + level: number; + resolution: number; + scale: number; + }[]; + }; + initialExtent: Extent; + fullExtent: Extent; + supportedImageFormatTypes: string[]; + /** + * comma separated list of supported capabilities - e.g. "Map,Query,Data" + */ + capabilities: string; + maxRecordCount: number; + maxImageHeight: number; + maxImageWidth: number; + minScale: number; + maxScale: number; + tileServers: string[]; + layers: SimpleLayerInfo[]; + documentInfo?: { + Title?: string; + Subject?: string; + Author?: string; + Comments?: string; + Keywords?: string; + }; +} + +export interface FeatureServerMetadata { + /** ArcGIS Server REST API version */ + currentVersion: number; + serviceDescription: string; + maxRecordCount: number; + supportedQueryFormats: string; + supportedExportFormats: string; + capabilities: string; + description: string; + copyrightText: string; + spatialReference: SpatialReference; + initialExtent: Extent; + fullExtent: Extent; + size?: number; + layers: SimpleLayerInfo[]; + documentInfo: undefined; +} + +export interface LayersMetadata { + layers: DetailedLayerMetadata[]; +} + +export interface DetailedLayerMetadata { + currentVersion: number; + id: number; + name: string; + type: "Feature Layer" | "Raster Layer" | "Group Layer"; + description: string; + geometryType: esriGeometryType; + copyrightText: string; + parentLayer: null | { id: number; name: string }; + sublayers: { id: number; name: string }[]; + minScale: number; + maxScale: number; + defaultVisibility: boolean; + extent: { + xmin: number; + ymin: number; + xmax: number; + ymax: number; + spatialReference: SpatialReference; + }; + hasAttachments: boolean; + htmlPopupType: + | "esriServerHTMLPopupTypeNone" + | "esriServerHTMLPopupTypeAsURL" + | "esriServerHTMLPopupTypeAsHTMLText"; + displayField: string; + typeIdField: string; + fields: { + name: string; + type: string; + alias: string; + domain: null | { + type: "codedValue"; + name: string; + codedValues: { + name: string; + code: string; + }[]; + }; + length: number; + editable?: boolean; + nullable?: boolean; + }[]; + maxRecordCount: number; + supportedQueryFormats: string; + advancedQueryCapabilities: { + supportsPagination: boolean; + }; +} + +export interface LayerLegendData { + label: string; + url: string; + imageData: string; + contentType: string; + height: number; + width: 20; +} diff --git a/packages/mapbox-gl-esri-feature-layers/lib/fetchData.ts b/packages/mapbox-gl-esri-sources/src/fetchData.ts similarity index 85% rename from packages/mapbox-gl-esri-feature-layers/lib/fetchData.ts rename to packages/mapbox-gl-esri-sources/src/fetchData.ts index 9f69f316b..4c4fc18c7 100644 --- a/packages/mapbox-gl-esri-feature-layers/lib/fetchData.ts +++ b/packages/mapbox-gl-esri-sources/src/fetchData.ts @@ -1,11 +1,12 @@ import { FeatureCollection } from "geojson"; -import bytes from "bytes"; export function fetchFeatureCollection( url: string, geometryPrecision = 6, outFields = "*", - bytesLimit = 1000000 * 100 + bytesLimit = 1000000 * 100, + abortController: AbortController | null = null, + disablePagination = false ) { return new Promise((resolve, reject) => { fetchFeatureLayerData( @@ -13,9 +14,9 @@ export function fetchFeatureCollection( outFields, reject, geometryPrecision, + abortController, null, - null, - undefined, + disablePagination, undefined, bytesLimit ) @@ -69,6 +70,26 @@ export async function fetchFeatureLayerData( return featureCollection; } +export function urlForRawGeoJSONData( + baseUrl: string, + outFields = "*", + geometryPrecision = 6, + queryOptions?: { [key: string]: string } +) { + const params = new URLSearchParams({ + inSR: "4326", + outSR: "4326", + where: "1>0", + outFields, + returnGeometry: "true", + geometryPrecision: geometryPrecision.toString(), + returnIdsOnly: "false", + f: "geojson", + ...(queryOptions || {}), + }); + return `${baseUrl}/query?${params.toString()}`; +} + async function fetchData( baseUrl: string, params: URLSearchParams, @@ -111,7 +132,7 @@ async function fetchData( bytesReceived += byteLength(str); if (bytesLimit && bytesReceived > bytesLimit) { const e = new Error( - `Exceeded bytesLimit. ${bytes(bytesReceived)} > ${bytes(bytesLimit)}` + `Exceeded bytesLimit. ${bytesReceived} > ${bytesLimit}` ); return onError(e); } @@ -132,6 +153,9 @@ async function fetchData( } else { featureCollection.features.push(...fc.features); if (fc.exceededTransferLimit || fc.properties?.exceededTransferLimit) { + if (disablePagination) { + throw new Error("Exceeded transfer limit. Pagination disabled."); + } if (!objectIdFieldName) { // Fetch objectIds to do manual paging params.set("returnIdsOnly", "true"); @@ -140,7 +164,6 @@ async function fetchData( // mode: "cors", ...(abortController ? { signal: abortController.signal } : {}), }); - const featureIds = featureCollection.features.map((f) => f.id); let objectIdParameters = await r.json(); // FeatureServers (at least on ArcGIS Online) behave differently if (objectIdParameters.properties) { @@ -149,7 +172,7 @@ async function fetchData( expectedFeatureCount = objectIdParameters.objectIds.length; objectIdFieldName = objectIdParameters.objectIdFieldName; } catch (e) { - return onError(e); + return onError(e as Error); } } diff --git a/packages/mapbox-gl-esri-feature-layers/lib/styleForFeatureLayer.ts b/packages/mapbox-gl-esri-sources/src/styleForFeatureLayer.ts similarity index 76% rename from packages/mapbox-gl-esri-feature-layers/lib/styleForFeatureLayer.ts rename to packages/mapbox-gl-esri-sources/src/styleForFeatureLayer.ts index ceae6d8b5..eb476b97a 100644 --- a/packages/mapbox-gl-esri-feature-layers/lib/styleForFeatureLayer.ts +++ b/packages/mapbox-gl-esri-sources/src/styleForFeatureLayer.ts @@ -1,6 +1,6 @@ import { symbolToLayers } from "./symbols/index"; import { Symbol } from "arcgis-rest-api"; -import { Layer } from "mapbox-gl"; +import { Layer, Expression } from "mapbox-gl"; import { ImageList } from "./ImageList"; import esriTS from "./symbols/esriTS"; import { generateId } from "./symbols/utils"; @@ -96,24 +96,27 @@ interface ClassBreaksRenderer { * * ``` * - * @param {string} url Feature layer endpoint. Should terminate in _/MapServer/0..n_ + * @param {string} serviceBaseUrl Main Service url. Should end in MapServer or FeatureServer + * @param {number} sublayer Sublayer to style * @param {string} sourceId ID for the [source](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/) of vector data to be used in rendering. + * @param {any} [serviceMetadata] Optional metadata for the service. If not provided it will be fetched from the service. * @returns The {@link ImageList.addToMap} function should be called before adding the generated layers to the map. */ -export default async function styleForFeatureLayer( - url: string, - sourceId: string +async function styleForFeatureLayer( + serviceBaseUrl: string, + sublayer: number, + sourceId: string, + serviceMetadata?: any ): Promise<{ imageList: ImageList; layers: Layer[] }> { - const rootUrl = url.replace(/\/\d+[\/]*$/, ""); - const sublayer = parseInt(url.match(/\/(\d+)[\/]*$/)![1]); - const response = await fetch(url + "?f=json").then((r) => r.json()); - const renderer = response.drawingInfo.renderer as Renderer; + // remove trailing slash if present + serviceBaseUrl = serviceBaseUrl.replace(/\/$/, ""); + const url = `${serviceBaseUrl}/${sublayer}`; + serviceMetadata = + serviceMetadata || (await fetch(url + "?f=json").then((r) => r.json())); + const renderer = serviceMetadata.drawingInfo.renderer as Renderer; let layers: Layer[] = []; - const imageList = new ImageList( - response.currentVersion, - /FeatureServer/.test(rootUrl) - ); + const imageList = new ImageList(serviceMetadata.currentVersion); let legendItemIndex = 0; switch (renderer.type) { case "uniqueValue": { @@ -128,7 +131,9 @@ export default async function styleForFeatureLayer( const field = renderer.field1; legendItemIndex = renderer.defaultSymbol ? 1 : 0; const fieldTypes = fields.map((f) => { - const fieldRecord = response.fields.find((r: any) => r.name === f); + const fieldRecord = serviceMetadata.fields.find( + (r: any) => r.name === f + ); return FIELD_TYPES[fieldRecord?.type] || "string"; }); for (const info of renderer.uniqueValueInfos) { @@ -142,19 +147,22 @@ export default async function styleForFeatureLayer( info.symbol, sourceId, imageList, - rootUrl, + serviceBaseUrl, sublayer, legendItemIndex++ ).map((lyr) => { + if (info.label?.length) { + lyr.metadata = { label: info.label }; + } if (fields.length === 1) { - lyr.filter = ["==", field, values[0]]; + lyr.filter = ["==", ["get", field], values[0]]; filters.push(lyr.filter); } else { lyr.filter = [ "all", ...fields.map((field) => [ "==", - field, + ["get", field], values[fields.indexOf(field)], ]), ]; @@ -170,11 +178,12 @@ export default async function styleForFeatureLayer( renderer.defaultSymbol, sourceId, imageList, - rootUrl, + serviceBaseUrl, sublayer, 0 ).map((lyr) => { - lyr.filter = ["none", ...filters]; + lyr.filter = ["!=", ["any", ...filters], true]; + lyr.metadata = { label: "Default" }; return lyr; }) ); @@ -189,7 +198,7 @@ export default async function styleForFeatureLayer( renderer.backgroundFillSymbol, sourceId, imageList, - rootUrl, + serviceBaseUrl, sublayer, 0 ) @@ -216,17 +225,23 @@ export default async function styleForFeatureLayer( info.symbol, sourceId, imageList, - rootUrl, + serviceBaseUrl, sublayer, legendItemIndex-- ).map((lyr) => { - const [min, max] = minMaxValues[ - renderer.classBreakInfos.indexOf(info) - ]; + const [min, max] = + minMaxValues[renderer.classBreakInfos.indexOf(info)]; if (renderer.classBreakInfos.indexOf(info) === 0) { - lyr.filter = ["all", ["<=", field, max]]; + lyr.filter = ["all", ["<=", ["get", field], max]]; } else { - lyr.filter = ["all", [">", field, min], ["<=", field, max]]; + lyr.filter = [ + "all", + [">", ["get", field], min], + ["<=", ["get", field], max], + ]; + } + if (info.label?.length) { + lyr.metadata = { label: info.label }; } filters.push(lyr.filter); return lyr; @@ -239,12 +254,13 @@ export default async function styleForFeatureLayer( renderer.defaultSymbol, sourceId, imageList, - rootUrl, + serviceBaseUrl, sublayer, 0 ); for (const index in defaultLayers) { defaultLayers[index].filter = ["none", filters]; + defaultLayers[index].metadata = { label: "Default" }; } layers.push(...defaultLayers); } @@ -255,22 +271,24 @@ export default async function styleForFeatureLayer( renderer.symbol, sourceId, imageList, - rootUrl, + serviceBaseUrl, sublayer, 0 ); break; } - if (response.drawingInfo.labelingInfo) { - for (const info of response.drawingInfo.labelingInfo) { - const layer = esriTS( - info, - response.geometryType, - response.fields.map((f: any) => f.name) - ); - layer.source = sourceId; - layer.id = generateId(); - layers.push(layer); + if (serviceMetadata.drawingInfo.labelingInfo) { + for (const info of serviceMetadata.drawingInfo.labelingInfo) { + if (info.labelExpression) { + const layer = esriTS( + info, + serviceMetadata.geometryType, + serviceMetadata.fields.map((f: any) => f.name) + ); + layer.source = sourceId; + layer.id = generateId(); + layers.push(layer); + } } } return { @@ -313,3 +331,5 @@ const FIELD_TYPES: { [key: string]: "string" | "float" | "integer" } = { esriFieldTypeGlobalID: "string", esriFieldTypeXML: "string", }; + +export default styleForFeatureLayer; diff --git a/packages/mapbox-gl-esri-feature-layers/lib/symbols/drawSMS.ts b/packages/mapbox-gl-esri-sources/src/symbols/drawSMS.ts similarity index 100% rename from packages/mapbox-gl-esri-feature-layers/lib/symbols/drawSMS.ts rename to packages/mapbox-gl-esri-sources/src/symbols/drawSMS.ts diff --git a/packages/mapbox-gl-esri-feature-layers/lib/symbols/esriPFS.ts b/packages/mapbox-gl-esri-sources/src/symbols/esriPFS.ts similarity index 100% rename from packages/mapbox-gl-esri-feature-layers/lib/symbols/esriPFS.ts rename to packages/mapbox-gl-esri-sources/src/symbols/esriPFS.ts diff --git a/packages/mapbox-gl-esri-feature-layers/lib/symbols/esriPMS.ts b/packages/mapbox-gl-esri-sources/src/symbols/esriPMS.ts similarity index 94% rename from packages/mapbox-gl-esri-feature-layers/lib/symbols/esriPMS.ts rename to packages/mapbox-gl-esri-sources/src/symbols/esriPMS.ts index 515c41bef..921095859 100644 --- a/packages/mapbox-gl-esri-feature-layers/lib/symbols/esriPMS.ts +++ b/packages/mapbox-gl-esri-sources/src/symbols/esriPMS.ts @@ -26,7 +26,7 @@ export default ( paint: {}, layout: { "icon-allow-overlap": true, - "icon-rotate": symbol.angle, + "icon-rotate": symbol.angle || 0, "icon-offset": [symbol.xoffset || 0, symbol.yoffset || 0], "icon-image": imageId, }, diff --git a/packages/mapbox-gl-esri-feature-layers/lib/symbols/esriSFS.ts b/packages/mapbox-gl-esri-sources/src/symbols/esriSFS.ts similarity index 100% rename from packages/mapbox-gl-esri-feature-layers/lib/symbols/esriSFS.ts rename to packages/mapbox-gl-esri-sources/src/symbols/esriSFS.ts diff --git a/packages/mapbox-gl-esri-feature-layers/lib/symbols/esriSLS.ts b/packages/mapbox-gl-esri-sources/src/symbols/esriSLS.ts similarity index 100% rename from packages/mapbox-gl-esri-feature-layers/lib/symbols/esriSLS.ts rename to packages/mapbox-gl-esri-sources/src/symbols/esriSLS.ts diff --git a/packages/mapbox-gl-esri-feature-layers/lib/symbols/esriSMS.ts b/packages/mapbox-gl-esri-sources/src/symbols/esriSMS.ts similarity index 100% rename from packages/mapbox-gl-esri-feature-layers/lib/symbols/esriSMS.ts rename to packages/mapbox-gl-esri-sources/src/symbols/esriSMS.ts diff --git a/packages/mapbox-gl-esri-feature-layers/lib/symbols/esriTS.ts b/packages/mapbox-gl-esri-sources/src/symbols/esriTS.ts similarity index 100% rename from packages/mapbox-gl-esri-feature-layers/lib/symbols/esriTS.ts rename to packages/mapbox-gl-esri-sources/src/symbols/esriTS.ts diff --git a/packages/mapbox-gl-esri-feature-layers/lib/symbols/fillPatterns.ts b/packages/mapbox-gl-esri-sources/src/symbols/fillPatterns.ts similarity index 100% rename from packages/mapbox-gl-esri-feature-layers/lib/symbols/fillPatterns.ts rename to packages/mapbox-gl-esri-sources/src/symbols/fillPatterns.ts diff --git a/packages/mapbox-gl-esri-feature-layers/lib/symbols/index.ts b/packages/mapbox-gl-esri-sources/src/symbols/index.ts similarity index 100% rename from packages/mapbox-gl-esri-feature-layers/lib/symbols/index.ts rename to packages/mapbox-gl-esri-sources/src/symbols/index.ts diff --git a/packages/mapbox-gl-esri-feature-layers/lib/symbols/linePatterns.ts b/packages/mapbox-gl-esri-sources/src/symbols/linePatterns.ts similarity index 100% rename from packages/mapbox-gl-esri-feature-layers/lib/symbols/linePatterns.ts rename to packages/mapbox-gl-esri-sources/src/symbols/linePatterns.ts diff --git a/packages/mapbox-gl-esri-feature-layers/lib/symbols/utils.ts b/packages/mapbox-gl-esri-sources/src/symbols/utils.ts similarity index 99% rename from packages/mapbox-gl-esri-feature-layers/lib/symbols/utils.ts rename to packages/mapbox-gl-esri-sources/src/symbols/utils.ts index 8b5625083..6911d7c2f 100644 --- a/packages/mapbox-gl-esri-feature-layers/lib/symbols/utils.ts +++ b/packages/mapbox-gl-esri-sources/src/symbols/utils.ts @@ -1,3 +1,4 @@ +// @ts-ignore import { v4 as uuid } from "uuid"; /** @hidden */ diff --git a/packages/mapbox-gl-esri-sources/src/utils.ts b/packages/mapbox-gl-esri-sources/src/utils.ts new file mode 100644 index 000000000..1a5e212d2 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/src/utils.ts @@ -0,0 +1,330 @@ +import { AnySourceData, Layer, Map } from "mapbox-gl"; +import { + DetailedLayerMetadata, + Extent, + FeatureServerMetadata, + MapServiceMetadata, +} from "./ServiceMetadata"; +import { SpatialReference } from "arcgis-rest-api"; +import { MapServiceLegendMetadata } from "./ArcGISRESTServiceRequestManager"; + +const blankDataUri = + "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; + +/** + * Replaced an existing source, preserving layers and their order by temporarily + * removing them + * @param sourceId ID of the source to replace + * @param map Mapbox GL JS Map instance + * @param sourceData Replacement source options + */ +export function replaceSource( + sourceId: string, + map: Map, + sourceData: AnySourceData +) { + const existingSource = map.getSource(sourceId); + if (!existingSource) { + throw new Error("Source does not exist"); + } + if (existingSource.type !== sourceData.type) { + throw new Error("Source type mismatch"); + } + const allLayers = map.getStyle().layers || []; + const relatedLayers = allLayers.filter((l) => { + return "source" in l && l.source === sourceId; + }); + + relatedLayers.reverse(); + const idx = allLayers.indexOf(relatedLayers[0]); + let before = allLayers[idx + 1]?.id || undefined; + for (const layer of relatedLayers) { + map.removeLayer(layer.id); + } + map.removeSource(sourceId); + map.addSource(sourceId, sourceData); + for (const layer of relatedLayers) { + map.addLayer(layer, before); + before = layer.id; + } +} + +/** + * Convert meters to degrees in web mercator projection + * @param x + * @param y + * @returns [lon, lat] + */ +export function metersToDegrees(x: number, y: number): [number, number] { + var lon = (x * 180) / 20037508.34; + var lat = + (Math.atan(Math.exp((y * Math.PI) / 20037508.34)) * 360) / Math.PI - 90; + return [lon, lat]; +} + +/** + * Convert an ArcGIS REST Service extent to a Mapbox GL JS LatLngBounds + * compatible array + * @param extent + * @returns [xmin, ymin, xmax, ymax] + */ +export async function extentToLatLngBounds( + extent: Extent +): Promise<[number, number, number, number] | void> { + if (extent) { + const wkid = normalizeSpatialReference(extent.spatialReference); + let bounds: [number, number, number, number]; + if (wkid === 4326) { + bounds = [ + Math.max(-180, extent.xmin), + Math.max(-90, extent.ymin), + Math.min(180, extent.xmax), + Math.min(90, extent.ymax), + ]; + } else if (wkid === 3857 || wkid === 102100) { + bounds = [ + ...metersToDegrees(extent.xmin, extent.ymin), + ...metersToDegrees(extent.xmax, extent.ymax), + ]; + } else { + try { + const projected = await projectExtent(extent); + bounds = [ + projected.xmin, + projected.ymin, + projected.xmax, + projected.ymax, + ]; + } catch (e) { + console.error(e); + return; + } + } + if (bounds) { + // check that bounds are valid, e.g. not a super small area around null + // island. these bad bounds can crash mapbox-gl-js + const [xmin, ymin, xmax, ymax] = bounds; + if (xmin === xmax || ymin === ymax) { + return; + } else if ( + Math.abs(ymax - ymin) < 0.001 || + Math.abs(xmax - xmin) < 0.001 + ) { + return; + } else { + if (bounds) { + bounds = enforceBoundsMinMax(bounds); + } + return bounds; + } + } else { + return; + } + } +} + +function enforceBoundsMinMax(bounds: [number, number, number, number]) { + const [xmin, ymin, xmax, ymax] = bounds; + return [ + Math.max(-180, xmin), + Math.max(-90, ymin), + Math.min(180, xmax), + Math.min(90, ymax), + ] as [number, number, number, number]; +} + +export function normalizeSpatialReference(sr: SpatialReference) { + const wkid = "latestWkid" in sr ? sr.latestWkid : "wkid" in sr ? sr.wkid : -1; + if (typeof wkid === "string") { + if (/WGS\s*84/.test(wkid)) { + return 4326; + } else { + return -1; + } + } else { + return wkid || -1; + } +} + +export async function projectExtent(extent: Extent) { + const endpoint = + "https://tasks.arcgisonline.com/arcgis/rest/services/Geometry/GeometryServer/project"; + const params = new URLSearchParams({ + geometries: JSON.stringify({ + geometryType: "esriGeometryEnvelope", + geometries: [extent], + }), + // @ts-ignore + inSR: `${extent.spatialReference.wkid}`, + outSR: "4326", + f: "json", + }); + const response = await fetch(`${endpoint}?${params.toString()}`); + const data = await response.json(); + const projected = data.geometries[0]; + if (projected) { + return projected; + } else { + throw new Error("Failed to reproject"); + } +} + +export function contentOrFalse(str?: string) { + if (str && str.length > 0) { + return str; + } else { + return false; + } +} + +function pickDescription( + info: MapServiceMetadata | FeatureServerMetadata, + layer?: DetailedLayerMetadata +) { + return ( + contentOrFalse(layer?.description) || + contentOrFalse(info.description) || + contentOrFalse(info.documentInfo?.Subject) || + contentOrFalse(info.documentInfo?.Comments) + ); +} + +/** + * Uses service metadata to create a markdown-like prosemirror document which + * represents layer metadata + * @param url + * @param mapServerInfo + * @param layer + * @returns + */ +export function generateMetadataForLayer( + url: string, + mapServerInfo: MapServiceMetadata | FeatureServerMetadata, + layer: DetailedLayerMetadata +) { + const attribution = + contentOrFalse(layer.copyrightText) || + contentOrFalse(mapServerInfo.copyrightText) || + contentOrFalse(mapServerInfo.documentInfo?.Author); + const description = pickDescription(mapServerInfo, layer); + let keywords = + mapServerInfo.documentInfo?.Keywords && + mapServerInfo.documentInfo?.Keywords.length + ? mapServerInfo.documentInfo?.Keywords.split(",") + : []; + return { + type: "doc", + content: [ + { + type: "heading", + attrs: { level: 1 }, + content: [{ type: "text", text: layer.name }], + }, + ...(description + ? [ + { + type: "paragraph", + content: [ + { + type: "text", + text: description, + }, + ], + }, + ] + : []), + ...(attribution + ? [ + { type: "paragraph" }, + { + type: "heading", + attrs: { level: 3 }, + content: [{ type: "text", text: "Attribution" }], + }, + { + type: "paragraph", + content: [ + { + type: "text", + text: attribution, + }, + ], + }, + ] + : []), + ...(keywords && keywords.length + ? [ + { type: "paragraph" }, + { + type: "heading", + attrs: { level: 3 }, + content: [ + { + type: "text", + text: "Keywords", + }, + ], + }, + { + type: "bullet_list", + marks: [], + attrs: {}, + content: keywords.map((word) => ({ + type: "list_item", + content: [ + { + type: "paragraph", + content: [{ type: "text", text: word }], + }, + ], + })), + }, + ] + : []), + { type: "paragraph" }, + { + type: "paragraph", + content: [ + { + type: "text", + marks: [ + { + type: "link", + attrs: { + href: url, + title: "ArcGIS Server", + }, + }, + ], + text: url, + }, + ], + }, + ], + }; +} + +export function makeLegend(data: MapServiceLegendMetadata, layerId: number) { + const legendLayer = data.layers.find((l) => l.layerId === layerId); + if (legendLayer) { + return legendLayer.legend.map((legend) => { + return { + id: legend.url, + label: + legend.label && legend.label.length > 0 + ? legend.label + : legendLayer.legend.length === 1 + ? legendLayer.layerName + : "", + imageUrl: legend?.imageData + ? `data:${legend.contentType};base64,${legend.imageData}` + : blankDataUri, + imageWidth: 20, + imageHeight: 20, + }; + }); + } else { + return undefined; + // throw new Error(`Legend for layerId=${layerId} not found`); + } +} diff --git a/packages/mapbox-gl-esri-sources/tsconfig.json b/packages/mapbox-gl-esri-sources/tsconfig.json new file mode 100644 index 000000000..b6a847bb1 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/tsconfig.json @@ -0,0 +1,69 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es2019" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true /* Generates corresponding '.d.ts' file. */, + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./dist" /* Redirect output structure to the directory. */, + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, + // "strictNullChecks": true /* Enable strict null checks. */, + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, + // "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true /* Skip type checking of declaration files. */, + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } +} diff --git a/packages/mapbox-gl-esri-sources/typedoc.json b/packages/mapbox-gl-esri-sources/typedoc.json new file mode 100644 index 000000000..9a31ce378 --- /dev/null +++ b/packages/mapbox-gl-esri-sources/typedoc.json @@ -0,0 +1,9 @@ +{ + "inputFiles": "./src/index.ts", + "mode": "file", + "out": "./docs", + "excludePrivate": true, + "excludeNotExported": true, + "disableSources": true, + "theme": "minimal" +} diff --git a/packages/spatial-uploads-handler/src/index.ts b/packages/spatial-uploads-handler/src/index.ts index a8c9d6eeb..0da60bddc 100644 --- a/packages/spatial-uploads-handler/src/index.ts +++ b/packages/spatial-uploads-handler/src/index.ts @@ -170,6 +170,10 @@ export default async function handleUpload( let workingFilePath = `${path.join(tmpobj.name, objectKey.split("/")[1])}`; let originalFilePath = workingFilePath; await updateProgress("fetching", 0.0); + console.log( + workingFilePath, + `s3://${path.join(process.env.BUCKET!, objectKey)}` + ); await getObject( workingFilePath, `s3://${path.join(process.env.BUCKET!, objectKey)}`,