diff --git a/app/main.ts b/app/main.ts index 997360fbd903b74fac7e9889a9e116edb8986029..173096c289e2b44ae96d1d99aa3f8a05fd24507c 100644 --- a/app/main.ts +++ b/app/main.ts @@ -14,43 +14,55 @@ logger.setUpLogger( environment === "development" ? LogSeverity.DEFAULT : LogSeverity.INFO, ); -const required = <T>(name: string, t?: T, fallback?: T): T => { - if (t === undefined && fallback === undefined) { +const requiredEnv = <T>( + name: string, + typeFn: (s: string) => T, + fallback?: T, +): T => { + const env = Deno.env.get(name); + if (env === undefined && fallback === undefined) { throw Error(`Environment variable "${name}" is required`); } else { - return t !== undefined ? t : fallback!; + return env !== undefined ? typeFn(env) : fallback!; } }; -const asNumber = (str?: string) => (str ? Number(str) : undefined); -const asBoolean = (str?: string) => (str ? /^true$/i.test(str) : undefined); +const asBoolean = (str: string) => /^true$/i.test(str); + +const fake = requiredEnv("FAKE", asBoolean, false); // For local development. If set, the API returns dummy data const serverConfigFromEnv = (): ServerConfig => { return { - port: required("PORT", asNumber(Deno.env.get("PORT")) || DEFAULT_PORT), - cacheEnabled: required( + port: requiredEnv("PORT", Number, DEFAULT_PORT), + cacheEnabled: requiredEnv( "CACHE_ENABLED", - asBoolean(Deno.env.get("CACHE_ENABLED")), + asBoolean, DEFAULT_CACHE_ENABLED, ), - cacheTtlMsVoltastics: required( + cacheTtlMsVoltastics: requiredEnv( "DEFAULT_CACHE_TTL_MS_VOLTASTICS", - asNumber(Deno.env.get("DEFAULT_CACHE_TTL_MS_VOLTASTICS")), + Number, DEFAULT_CACHE_TTL_MS_VOLTASTICS, ), voltastics: { - baseUrl: required( + baseUrl: requiredEnv( "VOLUNTEERING_VOLTASTICS_API_URL", - Deno.env.get("VOLUNTEERING_VOLTASTICS_API_URL"), + String, + fake ? "dummy value" : undefined, ), - apiToken: required( + apiToken: requiredEnv( "VOLUNTEERING_VOLTASTICS_API_KEY", - Deno.env.get("VOLUNTEERING_VOLTASTICS_API_KEY"), + String, + fake ? "dummy value" : undefined, ), }, - imageProxyBaseUrl: Deno.env.get("IMAGE_PROXY_BASE_URL") || - (environment === "production" + imageProxyBaseUrl: requiredEnv( + "IMAGE_PROXY_BASE_URL", + String, + environment === "production" ? "https://images.holi.social" - : "https://dev-images.holi.social"), + : "https://dev-images.holi.social", + ), + fake, }; }; diff --git a/app/server.ts b/app/server.ts index 5928f15fa36b887c9064b5c951ceed7d4c0b9915..6efff9dfcbdb8d79ce87b6c4ef560ba06638c2f8 100644 --- a/app/server.ts +++ b/app/server.ts @@ -1,8 +1,13 @@ import { + CategoriesResponse, + CitiesResponse, EngagementOpportunitiesParameters, EngagementParameters, + EngagementResponse, EngagementsParameters, + EngagementsResponse, TrackEngagementViewParameters, + TrackEngagementViewResponse, } from "./types.ts"; import { createSchema, createYoga, serve, useResponseCache } from "./deps.ts"; import { @@ -85,32 +90,48 @@ const createResolvers = (config: ServerConfig) => ({ // deno-lint-ignore no-explicit-any _parent: any, parameters: EngagementOpportunitiesParameters, - ) => fetchEngagementOpportunities(config)(parameters), + ): Promise<EngagementsResponse> => + config.fake + ? Promise.resolve({ totalResults: 0, data: [] }) + : fetchEngagementOpportunities(config)(parameters), engagement: ( // deno-lint-ignore no-explicit-any _parent: any, parameters: EngagementParameters, - ) => fetchEngagement(config)(parameters), + ): Promise<EngagementResponse> => + config.fake ? Promise.resolve(null) : fetchEngagement(config)(parameters), categories: ( // deno-lint-ignore no-explicit-any _parent: any, - ) => fetchCategories(config.voltastics), + ): Promise<CategoriesResponse> => + config.fake + ? Promise.resolve({ data: [] }) + : fetchCategories(config.voltastics), engagements: ( // deno-lint-ignore no-explicit-any _parent: any, parameters: EngagementsParameters, - ) => fetchEngagements(config)(parameters), + ): Promise<EngagementsResponse> => + config.fake + ? Promise.resolve({ totalResults: 0, data: [] }) + : fetchEngagements(config)(parameters), cities: ( // deno-lint-ignore no-explicit-any _parent: any, - ) => fetchCities(config.voltastics), + ): Promise<CitiesResponse> => + config.fake + ? Promise.resolve({ data: [] }) + : fetchCities(config.voltastics), }, Mutation: { trackEngagementView: ( // deno-lint-ignore no-explicit-any _parent: any, parameters: TrackEngagementViewParameters, - ) => trackEngagementView(config.voltastics)(parameters), + ): Promise<TrackEngagementViewResponse> => + config.fake + ? Promise.resolve({ id: parameters.id }) + : trackEngagementView(config.voltastics)(parameters), }, }); @@ -123,11 +144,12 @@ export interface VoltasticsConfig { apiToken: string; } export interface ServerConfig { - port?: number; // default: 8004 + port: number; // default: 8004 cacheEnabled: boolean; // default: true - cacheTtlMsVoltastics?: number; // default: 60 seconds + cacheTtlMsVoltastics: number; // default: 60 seconds voltastics: VoltasticsConfig; imageProxyBaseUrl: string; + fake: boolean; // For local development. If set, the API returns dummy data } export const createGraphQLServer = (config: ServerConfig): GraphQLServer => { @@ -136,16 +158,11 @@ export const createGraphQLServer = (config: ServerConfig): GraphQLServer => { useResponseCache({ session: () => null, // global cache, shared by all users ttlPerSchemaCoordinate: { - "Query.engagementOpportunities": config.cacheTtlMsVoltastics || - DEFAULT_CACHE_TTL_MS_VOLTASTICS, - "Query.engagement": config.cacheTtlMsVoltastics || - DEFAULT_CACHE_TTL_MS_VOLTASTICS, - "Query.categories": config.cacheTtlMsVoltastics || - DEFAULT_CACHE_TTL_MS_VOLTASTICS, - "Query.engagements": config.cacheTtlMsVoltastics || - DEFAULT_CACHE_TTL_MS_VOLTASTICS, - "Query.cities": config.cacheTtlMsVoltastics || - DEFAULT_CACHE_TTL_MS_VOLTASTICS, + "Query.engagementOpportunities": config.cacheTtlMsVoltastics, + "Query.engagement": config.cacheTtlMsVoltastics, + "Query.categories": config.cacheTtlMsVoltastics, + "Query.engagements": config.cacheTtlMsVoltastics, + "Query.cities": config.cacheTtlMsVoltastics, }, }), ] @@ -164,13 +181,18 @@ export type GraphQLServer = any; export const startServer = (config: ServerConfig): Promise<void> => { const graphQLServer: GraphQLServer = createGraphQLServer(config); return serve(graphQLServer.handleRequest, { - port: config.port || DEFAULT_PORT, + port: config.port, onListen({ port, hostname }) { logger.info( `Server started at http://${ hostname === "0.0.0.0" ? "localhost" : hostname }:${port}/graphql`, ); + if (config.fake) { + logger.info( + `Server is serving fake data due to FAKE env var set to true`, + ); + } }, }); }; diff --git a/app/voltastics_test.ts b/app/voltastics_test.ts index cae725f622da8a52f00197f6751f10fe17a8900b..5815158bc4459cec5b010838362b5d1178772e09 100644 --- a/app/voltastics_test.ts +++ b/app/voltastics_test.ts @@ -132,6 +132,17 @@ const serverConfigMock: ServerConfig = { cacheEnabled: true, voltastics: voltasticsConfigMock, imageProxyBaseUrl: "https://dev-images.holi.social", + cacheTtlMsVoltastics: 0, + fake: false, +}; + +const noCacheServerConfig = { + cacheEnabled: false, + voltastics: voltasticsConfigMock, + imageProxyBaseUrl: serverConfigMock.imageProxyBaseUrl, + port: 0, + cacheTtlMsVoltastics: 0, + fake: false, }; describe("voltastics", () => { @@ -195,11 +206,7 @@ describe("voltastics", () => { it("correctly parses engagement list", async () => { fetchStub = stubFetch(validEngagementsResponse); - const graphQLServer = createGraphQLServer({ - cacheEnabled: false, - voltastics: voltasticsConfigMock, - imageProxyBaseUrl: serverConfigMock.imageProxyBaseUrl, - }); + const graphQLServer = createGraphQLServer(noCacheServerConfig); const result = await queryEngagementOpportunities(graphQLServer); @@ -254,11 +261,7 @@ describe("voltastics", () => { it("correctly parses engagement", async () => { fetchStub = stubFetch(apiSearchEngagement1); - const graphQLServer = createGraphQLServer({ - cacheEnabled: false, - voltastics: voltasticsConfigMock, - imageProxyBaseUrl: serverConfigMock.imageProxyBaseUrl, - }); + const graphQLServer = createGraphQLServer(noCacheServerConfig); const result = await queryEngagement(graphQLServer); @@ -284,11 +287,7 @@ describe("voltastics", () => { it("correctly parses category list", async () => { fetchStub = stubFetch(apiCategoriesResponse); - const graphQLServer = createGraphQLServer({ - cacheEnabled: false, - voltastics: voltasticsConfigMock, - imageProxyBaseUrl: serverConfigMock.imageProxyBaseUrl, - }); + const graphQLServer = createGraphQLServer(noCacheServerConfig); const result = await queryCategories(graphQLServer); diff --git a/app/voltastics_test_data.ts b/app/voltastics_test_data.ts index e8ec1eb5e6538c2935809ef2e4c4688d11654b1f..5772426590efb82a572ca1258a1c29b0eb6fca7a 100644 --- a/app/voltastics_test_data.ts +++ b/app/voltastics_test_data.ts @@ -43,6 +43,7 @@ export const apiSearchEngagement2: ApiSearchEngagement = { description: "<p>Sie können sich vorstellen, als Ehrenamtliche:r eine Familie zu unterstützen, in der ein Kind lebensverkürzend erkrankt ist? Dann engagieren Sie sich im ambulanten Kinderhospizdienst HHanseStrolche.<br /><br /><strong>Was sind die HHanseStrolche?</strong><br />\nHHanseStrolche heißt unser ambulanter Kinderhospizdienst. Der Träger ist das Theodorus Kinder-Tageshospiz gGmbH in Munich-Eidelstedt. Dabei unterstützen ehrenamtliche Helfer:innen betroffene Familien Zuhause, in ihrem Alltag. Die Hilfe durch die „HHanseStrolche“ ist für die Familien kostenlos. <br /><br /><strong>Warum brauchen die Familien Hilfe?</strong><br />\nDie Diagnose einer unheilbaren Erkrankung eines Kindes ist für Familien sehr belastend. Der Alltag muss neu organisiert werden. Meist muss das Kind rund um die Uhr betreut werden. Mit ihren Sorgen fühlen sich Eltern dabei häufig alleingelassen. In dieser belastenden Situation kommen oft auch die Bedürfnisse der gesunden Geschwisterkinder zu kurz. <br /><br /><strong>Wie können Sie helfen?</strong><br />\nIndem Sie uns als „EhrenStrolch“ durch ihre ehrenamtliche Arbeit unterstützen. Sie gehen in die Familien, entlasten sie in ihrem Alltag und geben ihnen die Möglichkeit, Kraftreserven aufzufüllen und neue Energie zu schöpfen.<br /><br /><strong>Was können Sie konkret tun?</strong><br />\nSie helfen Familien, ihren Alltag besser zu bewältigen. Sie schenken den Eltern, dem erkrankten Kind oder dessen Geschwisterkind Ihre Zeit. Sie werden für die Familie ein Freund und Helfer. Dabei orientieren Sie sich an den konkreten Bedürfnissen der Familie oder der Kinder. Vielleicht lesen Sie dem erkrankten Kind etwas vor. Vielleicht spielen Sie mit dem gesunden Geschwisterkind oder machen einen Ausflug. Vielleicht brauchen die Eltern einfach mal zwei Stunden Zeit für ein ungestörtes Gespräch. Die Familie wird Ihnen zeigen, was sie gerade braucht. <br /><br /><strong>Wo unterstützen und begleiten Sie die Familie?</strong><br />\nSie unterstützen die betroffene Familie in Munich in deren eigenen vier Wänden. Idealerweise ist die Familie für Sie innerhalb von 30 Minuten erreichbar.</p>\n<p><strong>Wie viel Zeit sollten Sie als Helfer:in einbringen?</strong><br />\nDamit die Familien von Ihrer Begleitung wirklich profitieren, ist eine regelmäßige Unterstützung notwendig. So kann die Familie ihren Alltag besser planen. Sie sollten deshalb einmal pro Woche mindestens drei Stunden Zeit für die Familie erübrigen können. Außerdem sollten Sie vor Beginn Ihrer ehrenamtlichen Tätigkeit ausreichend Zeit für die Schulung haben.</p>\n<p><strong>Wie lange dauert die Schulung?</strong><br />\nDie Schulung zum Kinderhospizhelfer dauert insgesamt 120 Stunden und schließt auch eine Hospitanz im Kinder-Tageshospiz ein. Sie findet über einen Zeitraum von vier Monaten statt – alle zwei Wochen an Donnerstag- und Freitagnachmittagen, sowie samstags.<br /><br /><strong>Was sind Inhalte dieser Schulung?</strong><br />\nSie lernen die verschiedenen Krankheitsbilder kennen und die Auswirkungen auf das System Familie. Sie sprechen über den Umgang mit Sterben, Tod und Trauer. Sie beschäftigen sich mit Selbstpflege und Selbstfürsorge. </p><p>\n<strong>Wie können Sie die HHanseStrolche noch unterstützen?</strong></p>\n<p>Wir freuen uns, wenn Sie uns als „HHanseStrolch“ Ihre Zeit spenden. Natürlich können Sie sich im Theodorus Kinder-Tageshospiz auch anderweitig engagieren. Wenn Sie sich für dieses Engagement interessieren, können Sie sich jederzeit bei uns melden.</p>", "source": "Aktion Mensch", + title: "Ehrenamt im Ambulanten Kinderhospizdienst HHanseStrolche", hashTags: [ "Munich", diff --git a/deno.lock b/deno.lock index d10c0cda46ab82f992494953bf76131349607826..288279eb3a9ff1c6422ff4f5a552f8aab8994cc1 100644 --- a/deno.lock +++ b/deno.lock @@ -173,6 +173,154 @@ "https://deno.land/std@0.155.0/testing/bdd.ts": "35060cefd9cc21b414f4d89453b3551a3d52ec50aeff25db432503c5485b2f72", "https://deno.land/std@0.155.0/testing/mock.ts": "d7ad40139cda87476c4dc1efeffe406bbfb4a5f82b688e0b85bffe9e911526a2", "https://esm.sh/@turf/turf@6.5.0": "0feaef127dbec6e12e27eee03e481f795fef567500d2eb5efb1079459c2f2d86", + "https://esm.sh/@turf/turf@6.5.0?pin=v129": "7f21ae610c2aec13c5be97bf136d4a181fdf28b24388af159d06bcf875f1c077", + "https://esm.sh/v129/@turf/along@6.5.0/denonext/along.mjs": "44414b0849204c9af40fa921158b10bae68b342ee8211d29fdbb79ad5b254f7b", + "https://esm.sh/v129/@turf/angle@6.5.0/denonext/angle.mjs": "c9b09724eedf7bea2a4f250cf6ab7f19873b5c5c1e34ccf678f810128711eaee", + "https://esm.sh/v129/@turf/area@6.5.0/denonext/area.mjs": "403cd9e934e2943d3415cd8edd93bd3da80869ab5af66b8d7057d90082842066", + "https://esm.sh/v129/@turf/bbox-clip@6.5.0/denonext/bbox-clip.mjs": "545a95113194f7d5d2ea98812d2b2eebbf67ed598269a4b0dd2d6e362d10b6a6", + "https://esm.sh/v129/@turf/bbox-polygon@6.5.0/denonext/bbox-polygon.mjs": "77fd123ae75b6b5e567e3eebec40fbbfdc4ce107c8fee6b07b1c8142e1dfcb81", + "https://esm.sh/v129/@turf/bbox@6.5.0/denonext/bbox.mjs": "6542931c9150d0ed060e00838ccdebe91ed40a66b529325df547ca63ce261bd5", + "https://esm.sh/v129/@turf/bearing@6.5.0/denonext/bearing.mjs": "d675a86d42facf1bde45b298c695a23afdaa6dbaa5ed7a06c7455e6727f33b17", + "https://esm.sh/v129/@turf/bezier-spline@6.5.0/denonext/bezier-spline.mjs": "635b13a77e535398c7eb845eec9d066b8751f3778cdfdaa7cbe7cac7054a247b", + "https://esm.sh/v129/@turf/boolean-clockwise@6.5.0/denonext/boolean-clockwise.mjs": "f7b70463a9f9c67617b9f4a5dbaba847bcdecc5ea91e8613f3dbc318e6d9278c", + "https://esm.sh/v129/@turf/boolean-contains@6.5.0/denonext/boolean-contains.mjs": "6c22ccc6a770d80faa1a4d2a52da81d6a480ac4a81b11c388409a0b2cb7d0afd", + "https://esm.sh/v129/@turf/boolean-crosses@6.5.0/denonext/boolean-crosses.mjs": "3bde85b4a96aa38c8d16be59f2bd3d9aee6666dcdc1878eb804cbdbc48dadb1d", + "https://esm.sh/v129/@turf/boolean-disjoint@6.5.0/denonext/boolean-disjoint.mjs": "fc4f8c5c3b2d013b088141791b2167e95d47fe10395bfbfe614f5f5e0d726202", + "https://esm.sh/v129/@turf/boolean-equal@6.5.0/denonext/boolean-equal.mjs": "9df7a9ceceb5a5056280d04c4560680b2b82b3f27e7d839a6bf7183c80a47ecb", + "https://esm.sh/v129/@turf/boolean-intersects@6.5.0/denonext/boolean-intersects.mjs": "9a9799ada0099a5f04e2302f83c8c8b48bd6289ea7fb7d302fb06804ae09bec3", + "https://esm.sh/v129/@turf/boolean-overlap@6.5.0/denonext/boolean-overlap.mjs": "8622709d7184a3f886a301ba8a553beb02f77dbc40ee622b731ad0acedbac70e", + "https://esm.sh/v129/@turf/boolean-parallel@6.5.0/denonext/boolean-parallel.mjs": "7f94e3751b6639762d87a0fafc6ee2d3b7d49d3b64a2167907d8c4ad871226db", + "https://esm.sh/v129/@turf/boolean-point-in-polygon@6.5.0/denonext/boolean-point-in-polygon.mjs": "e7b8e0a4e83fcdffba4f90ccdb032ce764e4bd9e3f698aafbb3d9d1a0e93ef4f", + "https://esm.sh/v129/@turf/boolean-point-on-line@6.5.0/denonext/boolean-point-on-line.mjs": "6c940922cedf1f6c00722312a79cf278be54d75179314a1593ad1d87ab4b0f94", + "https://esm.sh/v129/@turf/boolean-within@6.5.0/denonext/boolean-within.mjs": "b6c11e7a2bda52a8d91c0e561c4c5f11fc64c8bbb0f877a5f729f80752a04b3e", + "https://esm.sh/v129/@turf/buffer@6.5.0/denonext/buffer.mjs": "568d6a67715899f792e3bf48703bec13fb2e99b0a3569a5cebd2d1b359a44e93", + "https://esm.sh/v129/@turf/center-mean@6.5.0/denonext/center-mean.mjs": "f0dc4e039337e88283f6e6fe18d947c852168107e3bce46a2be2c9fc479cd109", + "https://esm.sh/v129/@turf/center-median@6.5.0/denonext/center-median.mjs": "14c886ec35a2ac54371a3c51cb88f8292a00c817980da19d53ba4c5f87fd28fe", + "https://esm.sh/v129/@turf/center-of-mass@6.5.0/denonext/center-of-mass.mjs": "cde4b7ff17ca1bf109b24177d467ccd8e7449d8b76b6c9d8614bf4e69177be2f", + "https://esm.sh/v129/@turf/center@6.5.0/denonext/center.mjs": "a80050a48a9d3c872d5187dd861f55590c244c08a301279cfa4d45a3e1ed2185", + "https://esm.sh/v129/@turf/centroid@6.5.0/denonext/centroid.mjs": "23ae0d310225330464fbba9c636c6aad2d06980850552e197325870eb3affc37", + "https://esm.sh/v129/@turf/circle@6.5.0/denonext/circle.mjs": "c03ed4041e91321d01aa7c0633debac471e1c39d708de83680e95260808610b0", + "https://esm.sh/v129/@turf/clean-coords@6.5.0/denonext/clean-coords.mjs": "fa19c67ef4671ed8cf6948b98b43d9399d3b02e661733dc7dd05c98c51b6811b", + "https://esm.sh/v129/@turf/clone@6.5.0/denonext/clone.mjs": "f9dcf99c95e00476bb006f9ac2aaa5ebaa3c2360002869c1dbae9ba708a08071", + "https://esm.sh/v129/@turf/clusters-dbscan@6.5.0/denonext/clusters-dbscan.mjs": "03ad688734c7b5e0dff9b67a77b239a80e61b70861606178e37392165a44af2d", + "https://esm.sh/v129/@turf/clusters-kmeans@6.5.0/denonext/clusters-kmeans.mjs": "a7e825bb00ae84f6fff65093ba530188e37d903d3f4e94a0ce7ea54a78dc0461", + "https://esm.sh/v129/@turf/clusters@6.5.0/denonext/clusters.mjs": "ac41f3f572813ecd7d945e9ebebe9b6e76531c541f90edc201563ee06c2cf87e", + "https://esm.sh/v129/@turf/collect@6.5.0/denonext/collect.mjs": "cf4efee3268bf5b6c004ca5a749a5ec0d003483ee89e544e026b49e9eb4d40b1", + "https://esm.sh/v129/@turf/combine@6.5.0/denonext/combine.mjs": "dccfdb227ef2cd7df996f32f142e6bc19f9d1171fb478030ce836a512e216c0a", + "https://esm.sh/v129/@turf/concave@6.5.0/denonext/concave.mjs": "19da00579571e61504fd6dd31ea1df58f3824b3d0dea24403eb5855741450efc", + "https://esm.sh/v129/@turf/convex@6.5.0/denonext/convex.mjs": "2400dda4ae59660e4615e548674cbbaa8b5b6d3ebb44caae642a9c74b5b97809", + "https://esm.sh/v129/@turf/destination@6.5.0/denonext/destination.mjs": "f0c17b8bef1cb915750aa9fd4e04a29357244e9e1fde9147098e43853a89094e", + "https://esm.sh/v129/@turf/difference@6.5.0/denonext/difference.mjs": "015601f93aae5c3e79b55aa27b57cfbf74ddbfff5413864dd02c6c62e6346ebb", + "https://esm.sh/v129/@turf/dissolve@6.5.0/denonext/dissolve.mjs": "f6241adb4b61cd2bb7ba90399b71da5230c1db8321803bbb55f3a9aa2137b4ef", + "https://esm.sh/v129/@turf/distance-weight@6.5.0/denonext/distance-weight.mjs": "4820ca7173a7122885a62e7fa3c82ea04a3e6f6f8061a6a24de83c2646e9c84f", + "https://esm.sh/v129/@turf/distance@6.5.0/denonext/distance.mjs": "64638e5bcee0dfa28af53c8ba1615282eb555328a8773184a98b5bc8bf097475", + "https://esm.sh/v129/@turf/ellipse@6.5.0/denonext/ellipse.mjs": "127266a576d672f6663262ad58dc8d0280a37497448473d5856ab62b663573c0", + "https://esm.sh/v129/@turf/envelope@6.5.0/denonext/envelope.mjs": "87b5da57a3cb92cacd1baff8cd81bc7f28f603cb05c836f4a350aa07f76b111d", + "https://esm.sh/v129/@turf/explode@6.5.0/denonext/explode.mjs": "f2addac95236611695042fb6c57350ad915b8e329df7a1b499e86ff8b0da48f8", + "https://esm.sh/v129/@turf/flatten@6.5.0/denonext/flatten.mjs": "78fe2fafca48cf1e5de83092653393bcdb5df0adb656fc4533be2b63d5aff641", + "https://esm.sh/v129/@turf/flip@6.5.0/denonext/flip.mjs": "057d9c921cbc25e20e38f5322687bfd49ad28e2151f1f93a6c4bdd42a6527552", + "https://esm.sh/v129/@turf/great-circle@6.5.0/denonext/great-circle.mjs": "aafe10d6d804e6cf4de0999840b4344eec81fc30dc5d757d01024db0acae868c", + "https://esm.sh/v129/@turf/helpers@6.5.0/denonext/helpers.mjs": "3fdabda7695d541fd0715ed5930ff747f9b2149c917b4530665ac8cc0c769d5c", + "https://esm.sh/v129/@turf/hex-grid@6.5.0/denonext/hex-grid.mjs": "0e075fe14fcdf13e2fbeaaf910aeb7ff51885edd3c6a94e7d8855067d2009760", + "https://esm.sh/v129/@turf/interpolate@6.5.0/denonext/interpolate.mjs": "2b5f9e25a6ad170b7da27c4365ab3319f31c3a92a28fd96cbf48bd142bc497c9", + "https://esm.sh/v129/@turf/intersect@6.5.0/denonext/intersect.mjs": "577e7471fb711a7e8840f2c36a91cc28591d97ba27c904e73e225398febd090b", + "https://esm.sh/v129/@turf/invariant@6.5.0/denonext/invariant.mjs": "b95db9d9a995a94ada069b4c6e62da04b90eeda521300fd5f6959176640d6f09", + "https://esm.sh/v129/@turf/isobands@6.5.0/denonext/isobands.mjs": "c288f0582d22545e5181fd5d67b65abf3b20452e782ef4903320fbe6931871e1", + "https://esm.sh/v129/@turf/isolines@6.5.0/denonext/isolines.mjs": "bcc99c57d1598e84a16ef5925fe2726e44e500fe777878a8f6dac79849ed4ded", + "https://esm.sh/v129/@turf/kinks@6.5.0/denonext/kinks.mjs": "370c66f62a1ab9be45b2ca08e5fcd535e197b7d8aac1bcc1e6d1968e4411f162", + "https://esm.sh/v129/@turf/length@6.5.0/denonext/length.mjs": "3ae387f3f62ac526cb7c0dfa96fd2d8f5e7f3db888aed12ed697461e6ff35ed2", + "https://esm.sh/v129/@turf/line-arc@6.5.0/denonext/line-arc.mjs": "67500316840fc25529104bcc3a3c89785755e5265d33c692d24868f902166936", + "https://esm.sh/v129/@turf/line-chunk@6.5.0/denonext/line-chunk.mjs": "2dc3ab5661734aa020be8edf4cd6c1cbabe35d883613bb446a3159ac7ff22139", + "https://esm.sh/v129/@turf/line-intersect@6.5.0/denonext/line-intersect.mjs": "93fe77c2406b0ef81a455a89e9b21ad1ccd4a311782c894d65c19c88f60747b7", + "https://esm.sh/v129/@turf/line-offset@6.5.0/denonext/line-offset.mjs": "d4e15ae69b65e1809062970fa710b93e2da385e6f6b7b5209a63ddc41ff2da55", + "https://esm.sh/v129/@turf/line-overlap@6.5.0/denonext/line-overlap.mjs": "d2f10cfa6fa7acc580a91c6bb2587293573b9a204f465b1ff91ed68f77760315", + "https://esm.sh/v129/@turf/line-segment@6.5.0/denonext/line-segment.mjs": "84036250318905cc4c7916c2a879a966c71aca2659aaf794e14a0d724030f425", + "https://esm.sh/v129/@turf/line-slice-along@6.5.0/denonext/line-slice-along.mjs": "58469549cb2dca8d00645667174fe033e47c437b0b1bfda74cbd2f1c2c58c1c1", + "https://esm.sh/v129/@turf/line-slice@6.5.0/denonext/line-slice.mjs": "3b492f5dc0c19419eef3f3b2a7c51fc33f0362aeec06ed6376d9ac67ce32506b", + "https://esm.sh/v129/@turf/line-split@6.5.0/denonext/line-split.mjs": "06e0e52b4ff7d74bc0c0fc0c21bdbe5410dace9765d8806786f52728f44603b8", + "https://esm.sh/v129/@turf/line-to-polygon@6.5.0/denonext/line-to-polygon.mjs": "99e53e7af0eb7515aab9de5d61a952547075b85f465043032cebbdb0f908cddd", + "https://esm.sh/v129/@turf/mask@6.5.0/denonext/mask.mjs": "c997ffa17373ac7c353e55cb2938f1825e855a3af2290c81c9b3762189972d02", + "https://esm.sh/v129/@turf/meta@6.5.0/denonext/meta.mjs": "c952de931f49cf1a123bbb0361a3d7646f2da96facd984e1fd01a39b6bb6d338", + "https://esm.sh/v129/@turf/midpoint@6.5.0/denonext/midpoint.mjs": "4b699b9bcbfd67d67beab4fa2bfe6e048ad8ec719af3dd8136e08f04d1936a9f", + "https://esm.sh/v129/@turf/moran-index@6.5.0/denonext/moran-index.mjs": "0e11787d4dba15fce94ecf711f4d4f5af68919bb957a89735704079c451361d6", + "https://esm.sh/v129/@turf/nearest-point-on-line@6.5.0/denonext/nearest-point-on-line.mjs": "c531dae2b4da8be9c530cbeac63fd07d19e2d11f7586a992414b6b8e132543c4", + "https://esm.sh/v129/@turf/nearest-point-to-line@6.5.0/denonext/nearest-point-to-line.mjs": "db7162ca6ed4d900e41ce3792aa25c74673f06f475259558bc839527142c3869", + "https://esm.sh/v129/@turf/nearest-point@6.5.0/denonext/nearest-point.mjs": "eda341fd2d8145dcf7c659d38bdb3c39cd436ab738d25a9bbc4eaf8c8b5f8dd6", + "https://esm.sh/v129/@turf/planepoint@6.5.0/denonext/planepoint.mjs": "1e63bb4297acafe776666d000f39c59be3681f6d579429aa8287504f7e9af207", + "https://esm.sh/v129/@turf/point-grid@6.5.0/denonext/point-grid.mjs": "2e77e6ac87757311c856b91b5d3ded5715f710c0cbec0e9747c51980dea072d5", + "https://esm.sh/v129/@turf/point-on-feature@6.5.0/denonext/point-on-feature.mjs": "f7562baf344c5eddfa83aeab61a1c109ca9149084e35f184f111b0f8c5737c35", + "https://esm.sh/v129/@turf/point-to-line-distance@6.5.0/denonext/point-to-line-distance.mjs": "5dd083ac1bbcf3cfca2fc35931d3fdd8fb9630982c873caeb28463274b9e4aed", + "https://esm.sh/v129/@turf/points-within-polygon@6.5.0/denonext/points-within-polygon.mjs": "b83a1b2f1e1ac67c5ba63a37fbf767af28f877532bdee3cc7f35f69af1ca0fa1", + "https://esm.sh/v129/@turf/polygon-smooth@6.5.0/denonext/polygon-smooth.mjs": "8d8181536d096c3e0a73b8b87b3baa889cdd3ec59e7679d0c44022595781b8f9", + "https://esm.sh/v129/@turf/polygon-tangents@6.5.0/denonext/polygon-tangents.mjs": "074621c2528ace81daf21332647c15fd21ff0787692c571d19314d6cb37059ad", + "https://esm.sh/v129/@turf/polygon-to-line@6.5.0/denonext/polygon-to-line.mjs": "6eab9436703a8a7082eea9bc16767bbdb3bb9b56d8de4b6a03c6b2a7dd4f1083", + "https://esm.sh/v129/@turf/polygonize@6.5.0/denonext/polygonize.mjs": "c001bb5bf1133e44397a2723c1bf11705c08c6d3ac4b0dee5ee75289cfc96a1d", + "https://esm.sh/v129/@turf/projection@6.5.0/denonext/projection.mjs": "774d67079ac20c177a4ee0596b336a133a97b923b43892293603bc4e431e31d7", + "https://esm.sh/v129/@turf/random@6.5.0/denonext/random.mjs": "31037b7d83d420fc49dab383c7b44a1e102ef65e9d3559307c1c2734741c79a6", + "https://esm.sh/v129/@turf/rectangle-grid@6.5.0/denonext/rectangle-grid.mjs": "1229f5e52208e91ac521e1d6864053226bcc55ec550896d18ed1677db6bd01a1", + "https://esm.sh/v129/@turf/rewind@6.5.0/denonext/rewind.mjs": "b396dcc12c12d38f12decf0e46de65f8f61d8f4050ffbdb39903c99d6d5c299d", + "https://esm.sh/v129/@turf/rhumb-bearing@6.5.0/denonext/rhumb-bearing.mjs": "c56524a4bfba12013cee26c1f5df1450140bc8d1b329aa358f4614dded2f210d", + "https://esm.sh/v129/@turf/rhumb-destination@6.5.0/denonext/rhumb-destination.mjs": "673561a3934415c366848b007bc8758f74184bdd77a08a30f6f75cf106e6970b", + "https://esm.sh/v129/@turf/rhumb-distance@6.5.0/denonext/rhumb-distance.mjs": "c6150adf0b77ddf572d18736bd9a4d1c6a1ea1cc29a3e0ed2e366a1b38c5e5dc", + "https://esm.sh/v129/@turf/sample@6.5.0/denonext/sample.mjs": "44bd79393b4311aaed9e28fb480e88ee9e3ecb9322f7ef79ff3bdb793c517264", + "https://esm.sh/v129/@turf/sector@6.5.0/denonext/sector.mjs": "4fe647c990e9a21c87f1cf49d79d49a8bd748beeec5166bdfbc66cd2f1a65ff8", + "https://esm.sh/v129/@turf/shortest-path@6.5.0/denonext/shortest-path.mjs": "234f71e103b6739250ac9c74f2801b34ae52d512cb3952d1ef840e4547cbea44", + "https://esm.sh/v129/@turf/simplify@6.5.0/denonext/simplify.mjs": "12d1210caec54f0d4c787e182543e2d2afd83703a6f885138081f9775c6f1a64", + "https://esm.sh/v129/@turf/square-grid@6.5.0/denonext/square-grid.mjs": "e1ac9f165fac9ff76a365dc7c17f8d57d6952d6c7a7b7247a5bb0009b08db679", + "https://esm.sh/v129/@turf/square@6.5.0/denonext/square.mjs": "a3d4569fb8ec0da99e7c9f0a5d0b12afa8ad7fcc1c1aa442f6ed7137b6852148", + "https://esm.sh/v129/@turf/standard-deviational-ellipse@6.5.0/denonext/standard-deviational-ellipse.mjs": "49eebd6815ed06d045c9701ebfafcd7628cfd40f874e1b8e5bb038f3c28416dd", + "https://esm.sh/v129/@turf/tag@6.5.0/denonext/tag.mjs": "ebcb5f1d9133b7ff0237592207704f8937fafb4733272d2e5bee6824c4e7fa40", + "https://esm.sh/v129/@turf/tesselate@6.5.0/denonext/tesselate.mjs": "b76e7111eae4dcc190e9105e974eab693b269c14822a34543729faa05dae1931", + "https://esm.sh/v129/@turf/tin@6.5.0/denonext/tin.mjs": "4410275f2b9eedaa66b1b94618d73aca682e60102cfc62c51b945666ac1a93f2", + "https://esm.sh/v129/@turf/transform-rotate@6.5.0/denonext/transform-rotate.mjs": "3f7a0374168366b022fc4a97b56f5b04d2beab3dbe54da53e6595ace2d59ec4f", + "https://esm.sh/v129/@turf/transform-scale@6.5.0/denonext/transform-scale.mjs": "1ad5eb86b9f7deb9a826152b4b11b54b40a5bfc02e302e4dee800a1fa69fc9f0", + "https://esm.sh/v129/@turf/transform-translate@6.5.0/denonext/transform-translate.mjs": "1067833dbd2293ac94941c6c34d82e7d6643e42408b85e154142cdfaf3fefaea", + "https://esm.sh/v129/@turf/triangle-grid@6.5.0/denonext/triangle-grid.mjs": "7d05363d8011e7a00dfdc4ed0d7c242c3f73c31b0a4c47b49b123b6e0f23a32d", + "https://esm.sh/v129/@turf/truncate@6.5.0/denonext/truncate.mjs": "7e829c8670e4d677e27c7e69004c141e5ce34b2d8c8a2e7f57fc953fee3f369f", + "https://esm.sh/v129/@turf/turf@6.5.0/denonext/turf.mjs": "e4ce111932cc95c818ae47af1e5d294868374af479124acf691a6c6625e94af7", + "https://esm.sh/v129/@turf/union@6.5.0/denonext/union.mjs": "a1a4c4d90a16c4debf159f411d1290ff3dc86748d367631dd085c66f96a9ac92", + "https://esm.sh/v129/@turf/unkink-polygon@6.5.0/denonext/unkink-polygon.mjs": "a23d0a2f1ad0c4edc26ef23969a728340656249e42d6b2714234a54489ab450f", + "https://esm.sh/v129/@turf/voronoi@6.5.0/denonext/voronoi.mjs": "8d738d7b304d4950787980f64531a70a2625b1486733a1e1ebd2a529155ce68e", + "https://esm.sh/v129/call-bind@1.0.2/denonext/call-bind.mjs": "42034a5e30e96e84ae767d59b41cb2342e43ee23b21d2d48a2629cd5f0541928", + "https://esm.sh/v129/call-bind@1.0.2/denonext/callBound.js": "62d697155528545fdb7473bc0033e6a9fe314f52dffd4aeda3618075874d1a31", + "https://esm.sh/v129/concaveman@1.2.1/denonext/concaveman.mjs": "4b3ae6c4cf95fae00cd4f0582386882c7f4713e3b60775ad640a14526b7c859b", + "https://esm.sh/v129/d3-array@1.2.4/denonext/d3-array.mjs": "ee413c9d8dc04471c774e641ca8d616a00e4452c62d3126011ddb8e2e3fdf97d", + "https://esm.sh/v129/d3-geo@1.7.1/denonext/d3-geo.mjs": "930c1a0d229ad046751c0a062107f4480cd85b2616e5e35c2a30a9bbb628a132", + "https://esm.sh/v129/d3-voronoi@1.1.2/denonext/d3-voronoi.mjs": "ced9affdd5f2d08f534856e4ebd1b6cef85d0bbf352a57a96758cd5e520932b3", + "https://esm.sh/v129/deep-equal@1.1.1/denonext/deep-equal.mjs": "3a360e6c89682ba33e59163727631ee91b8ff665e89ea33357e8802db3305cb1", + "https://esm.sh/v129/define-properties@1.2.0/denonext/define-properties.mjs": "2cfbf67fd79fc6b2005f385376e8f642af050f73286813dcc91c691f239306d0", + "https://esm.sh/v129/density-clustering@1.3.0/denonext/density-clustering.mjs": "e78d979f0ad641b6a8acecd76cc23a14a981a5878048cfa06f89e29274da7868", + "https://esm.sh/v129/earcut@2.2.4/denonext/earcut.mjs": "e119e9d83d550927aed5f7e2dbbf173436feed9553b2ae6113cf71b3e9e9180c", + "https://esm.sh/v129/function-bind@1.1.1/denonext/function-bind.mjs": "331f5d2a5830a67a767be946331716b1b9db05125891100a16e24114dba8ef87", + "https://esm.sh/v129/functions-have-names@1.2.3/denonext/functions-have-names.mjs": "c35b1012e1b731a55763a9da124a4be8c64a66e11cfb6837ccd17f8dace84de2", + "https://esm.sh/v129/geojson-equality@0.1.6/denonext/geojson-equality.mjs": "3bb6f72db6f73617c3b4901ad985bd3efefb5da96747e260c557d4629e9b1ec6", + "https://esm.sh/v129/geojson-rbush@3.2.0/denonext/geojson-rbush.mjs": "1f5ad12d678891f511951ac8616e1d8a8c743341aeaefc6dc83388de1815dbd1", + "https://esm.sh/v129/get-intrinsic@1.2.1/denonext/get-intrinsic.mjs": "2ca59cc159364c8916b80ad4a53d4f6e6f9670463c75629bb399e651511c65db", + "https://esm.sh/v129/has-property-descriptors@1.0.0/denonext/has-property-descriptors.mjs": "022ed691c4191f0a5ac50aaf416cb66d3b12a749f9793fe8964d3e3ff6bd58ec", + "https://esm.sh/v129/has-proto@1.0.1/denonext/has-proto.mjs": "443f9773e464b39534dbecaee040e5de8384e8efaeea7a6d3e333df4069d1f85", + "https://esm.sh/v129/has-symbols@1.0.3/denonext/has-symbols.mjs": "452727b5dbec94d538c5e8e062ed82bee701c3b752eb21e6189978e92292b7c6", + "https://esm.sh/v129/has-symbols@1.0.3/denonext/shams.js": "c2cefbc68690dfe7e9590c7720b1ff22d5b09251e936019d27e1b8d319c8fa9d", + "https://esm.sh/v129/has-tostringtag@1.0.0/denonext/shams.js": "11a0e4da09f747f03cc0f154700541d1b4ddd41db789a785ab40283e73e18394", + "https://esm.sh/v129/has@1.0.3/denonext/has.mjs": "d4107cf41c308ef1f7325986a947fd5d663179faaca6c8781971fccf1259e236", + "https://esm.sh/v129/is-arguments@1.1.1/denonext/is-arguments.mjs": "5e4deb2c366d57358e71b9aa27633d01751c679ef36c82a0c7d20c5c5894538c", + "https://esm.sh/v129/is-date-object@1.0.5/denonext/is-date-object.mjs": "0bb7269aca92d4bb9db1af11fdfc5f86c5215ea7d1c518e83a8d592d6e00c2f0", + "https://esm.sh/v129/is-regex@1.1.4/denonext/is-regex.mjs": "5a3fad5f885852f371ba3f9c5b0714f228aa39cd5cc11556452d44f69c2b9716", + "https://esm.sh/v129/object-is@1.1.5/denonext/object-is.mjs": "46ba00df36481ec575a6de76216ef3cc350ccdcdfbb3a44f9cb6373b30243d2b", + "https://esm.sh/v129/object-keys@1.1.1/denonext/object-keys.mjs": "9b1c41ea017ff80bc11f70885530199fbca620a7ac9b5b2bef20c8fb9829a4de", + "https://esm.sh/v129/point-in-polygon@1.1.0/denonext/point-in-polygon.mjs": "a123e6c7dbc3be3746b5b1f47e87e659ad6f26bcd02fff7611d5e4b83c9c324c", + "https://esm.sh/v129/polygon-clipping@0.15.3/denonext/polygon-clipping.mjs": "a96de0c21d577b7497c5063b961acd0fa3a4642d2592865cf18c1fdc87249e2c", + "https://esm.sh/v129/quickselect@1.1.1/denonext/quickselect.mjs": "9c5b4f3f1de466e3e6ebf9ca4dd281a01b91ed259e449088dc26ad0e034fcaf1", + "https://esm.sh/v129/quickselect@2.0.0/denonext/quickselect.mjs": "e03ef0934526ae4bdadb9f90f89e766294739f11ea62a7b2afc2e53069e8619b", + "https://esm.sh/v129/rbush@2.0.2/denonext/rbush.mjs": "df5e7d0b81145bd1afe368ca5b74d4c0e00d3ecbc2d6ee96f3e9a23ed2c1abfe", + "https://esm.sh/v129/rbush@3.0.1/denonext/rbush.mjs": "e3251aea1d308b6aa0237acc8278624ef437900e15510a6d78b2be69faac5516", + "https://esm.sh/v129/regexp.prototype.flags@1.5.0/denonext/regexp.prototype.flags.mjs": "488c2f56012b3f52fe47b7d6356dfc6f8fde44871ba0fe0760ab6e80f506dc92", + "https://esm.sh/v129/robust-predicates@2.0.4/denonext/umd/orient2d.min.js": "890b58754301e6c6931cf93ce6c66eec71babae0d34e30d0d24e5037772102aa", + "https://esm.sh/v129/skmeans@0.9.7/denonext/skmeans.mjs": "496ed2a88b6cf00e67de70aa6d01ab0e9dcb532b2db275f52fc45881553461e9", + "https://esm.sh/v129/splaytree@3.1.2/denonext/splaytree.mjs": "0bb044d03031466d8c51fa8f7d54783659e486588665323b33a5120aecd3ba9d", + "https://esm.sh/v129/tinyqueue@2.0.3/denonext/tinyqueue.mjs": "923c24576a6f8f7a152117f311f83b97b6f5e4cc925312e6cc6a15a290b8ecea", + "https://esm.sh/v129/topojson-client@3.1.0/denonext/topojson-client.mjs": "be4dd835dee50439be15521a32978e851181d3a8b8b61b1c040a5981d04dab6f", + "https://esm.sh/v129/topojson-server@3.0.1/denonext/topojson-server.mjs": "7290cfe0abbf0b77b94224971d136370c25919c3dc6a7b36c49b552e1a4ed473", + "https://esm.sh/v129/turf-jsts@1.2.3/denonext/turf-jsts.mjs": "2239465c9e5f3efc2d45a3349206ee320f7c54f1e6a85145fee4e91735757995", "https://esm.sh/v131/@turf/along@6.5.0/denonext/along.mjs": "6b75598ef1b6aaa039625cf2466bcc7c6431db2212ef9f47a59d0e0ab72268a9", "https://esm.sh/v131/@turf/angle@6.5.0/denonext/angle.mjs": "8afc0d5c2202cef74f72f3e6ce85456b518961e1d3c929d7fa0eb2af5c2cfe23", "https://esm.sh/v131/@turf/area@6.5.0/denonext/area.mjs": "14b9e4f26bc8a11d50b6bca36dc587047d11edcfd6b5ef0550725345e2c9a3fd", @@ -320,5 +468,16 @@ "https://esm.sh/v131/topojson-client@3.1.0/denonext/topojson-client.mjs": "be4dd835dee50439be15521a32978e851181d3a8b8b61b1c040a5981d04dab6f", "https://esm.sh/v131/topojson-server@3.0.1/denonext/topojson-server.mjs": "7290cfe0abbf0b77b94224971d136370c25919c3dc6a7b36c49b552e1a4ed473", "https://esm.sh/v131/turf-jsts@1.2.3/denonext/turf-jsts.mjs": "2239465c9e5f3efc2d45a3349206ee320f7c54f1e6a85145fee4e91735757995" + }, + "npm": { + "specifiers": { + "@types/node": "@types/node@18.16.19" + }, + "packages": { + "@types/node@18.16.19": { + "integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==", + "dependencies": {} + } + } } }