From 169342fc36b94cafcbcada0d2988821b678b1858 Mon Sep 17 00:00:00 2001 From: Juan Date: Tue, 26 Nov 2024 04:29:30 -0300 Subject: [PATCH 01/10] Compass search --- src/loaders/drop.ts | 82 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 src/loaders/drop.ts diff --git a/src/loaders/drop.ts b/src/loaders/drop.ts new file mode 100644 index 0000000..4421f72 --- /dev/null +++ b/src/loaders/drop.ts @@ -0,0 +1,82 @@ +import { DEFAULT_COMPASS_LIMIT } from '../models/compass' +import { DEFAULT_SEARCH_LIMIT } from '../models/event' +import { Drop, parseDrop } from '../models/drop' +import { queryAggregateCountCompass, queryManyCompass } from './compass' + +export async function searchDrops( + query: string, + abortSignal: AbortSignal, + offset: number = 0, + limit: number = Math.min(DEFAULT_SEARCH_LIMIT, DEFAULT_COMPASS_LIMIT), +): Promise<{ + items: Drop[] + total: number + offset: number + limit: number +}> { + const [items, total] = await Promise.all([ + queryManyCompass( + 'search_drops', + (data: unknown) => parseDrop(data, /*includeDescription*/true), + ` + query SearchDrops( + $limit: Int! + $offset: Int! + $query: String! + ) { + search_drops( + limit: $limit + offset: $offset + args: { + search: $query + } + ) { + id + name + description + image_url + city + country + start_date + end_date + expiry_date + drop_image { + gateways { + type + url + } + } + } + } + `, + { + query, + offset, + limit, + }, + abortSignal + ), + queryAggregateCountCompass( + 'search_drops_aggregate', + ` + query SearchDropsCount($query: String!) { + search_drops_aggregate(args: { search: $query }) { + aggregate { + count + } + } + } + `, + { + query, + }, + abortSignal + ), + ]) + return { + items, + total, + offset, + limit, + } +} From 16c96125eaee7f4bdeb5bb5a9fdc4ebf568635bf Mon Sep 17 00:00:00 2001 From: Juan Date: Mon, 9 Dec 2024 11:11:32 -0300 Subject: [PATCH 02/10] Update imports --- src/loaders/drop.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/loaders/drop.ts b/src/loaders/drop.ts index 4421f72..9a31c99 100644 --- a/src/loaders/drop.ts +++ b/src/loaders/drop.ts @@ -1,7 +1,7 @@ -import { DEFAULT_COMPASS_LIMIT } from '../models/compass' -import { DEFAULT_SEARCH_LIMIT } from '../models/event' -import { Drop, parseDrop } from '../models/drop' -import { queryAggregateCountCompass, queryManyCompass } from './compass' +import { DEFAULT_COMPASS_LIMIT } from 'models/compass' +import { DEFAULT_SEARCH_LIMIT } from 'models/event' +import { Drop, parseDrop } from 'models/drop' +import { queryAggregateCountCompass, queryManyCompass } from 'loaders/compass' export async function searchDrops( query: string, From d521a1b0fe6ef746e5f0cbc5f4af3c31d97e3206 Mon Sep 17 00:00:00 2001 From: Juan Date: Mon, 9 Dec 2024 11:11:45 -0300 Subject: [PATCH 03/10] Replace function --- src/hooks/useEventSearch.ts | 4 +- src/loaders/event.ts | 103 +----------------------------------- 2 files changed, 3 insertions(+), 104 deletions(-) diff --git a/src/hooks/useEventSearch.ts b/src/hooks/useEventSearch.ts index 3f43f1a..d19145c 100644 --- a/src/hooks/useEventSearch.ts +++ b/src/hooks/useEventSearch.ts @@ -2,7 +2,7 @@ import { useCallback, useState } from 'react' import { SEARCH_LIMIT } from 'models/event' import { Drop } from 'models/drop' import { AbortedError } from 'models/error' -import { searchEvents as loadSearchEvents } from 'loaders/event' +import { searchDrops } from 'loaders/drop' function useEventSearch(query?: string, page: number = 1): { loadingEventSearch: boolean @@ -30,7 +30,7 @@ function useEventSearch(query?: string, page: number = 1): { if (total == null || offset <= total) { controller = new AbortController() setLoading(true) - loadSearchEvents( + searchDrops( newQuery ?? query, controller.signal, offset, diff --git a/src/loaders/event.ts b/src/loaders/event.ts index e52b90c..b07a3f3 100644 --- a/src/loaders/event.ts +++ b/src/loaders/event.ts @@ -1,112 +1,11 @@ import { filterInvalidOwners } from 'models/address' -import { DEFAULT_SEARCH_LIMIT, parseEventIds } from 'models/event' +import { parseEventIds } from 'models/event' import { Drop, parseDrop } from 'models/drop' import { POAP_API_URL, POAP_API_KEY } from 'models/poap' import { AbortedError, HttpError } from 'models/error' import { getEventAndOwners, getEventMetrics, getEvents } from 'loaders/api' import { fetchPOAPs } from 'loaders/poap' -export async function searchEvents( - query: string, - abortSignal: AbortSignal, - offset: number = 0, - limit: number = DEFAULT_SEARCH_LIMIT, -): Promise<{ - items: Drop[] - total: number - offset: number - limit: number -}> { - let response: Response - try { - response = await fetch( - `${POAP_API_URL}/paginated-events?name=${encodeURIComponent(query)}` + - `&sort_field=start_date` + - `&sort_dir=desc` + - `&offset=${offset}` + - `&limit=${limit}`, - { - signal: abortSignal instanceof AbortSignal ? abortSignal : null, - headers: { - 'x-api-key': POAP_API_KEY, - }, - } - ) - } catch (err: unknown) { - if (err instanceof Error && err.name === 'AbortError') { - throw new AbortedError( - `Search drops for "${query}" aborted`, - { cause: err } - ) - } - throw new Error( - `Cannot search drops for "${query}": ` + - `response was not success (network error)`, - { cause: err } - ) - } - - if (response.status !== 200) { - let message: string | undefined - try { - const data = await response.json() - if ( - data != null && - typeof data === 'object' && - 'message' in data && - data.message != null && - typeof data.message === 'string' - ) { - message = data.message - } - } catch (err: unknown) { - console.error(err) - } - - if (message) { - throw new HttpError( - `Search events was not success ` + - `(status ${response.status}): ${message}`, - { status: response.status } - ) - } else { - throw new HttpError( - `Search events was not success ` + - `(status ${response.status})`, - { status: response.status } - ) - } - } - - const body: unknown = await response.json() - - if ( - body == null || - typeof body !== 'object' || - !('items' in body) || - body.items == null || - !Array.isArray(body.items) || - !('total' in body) || - body.total == null || - typeof body.total !== 'number' || - !('offset' in body) || - body.offset == null || - typeof body.offset !== 'number' || - !('limit' in body) || - body.limit == null || - typeof body.limit !== 'number' - ) { - throw new Error(`Search events response malformed`) - } - - return { - items: body.items.map((item) => parseDrop(item, /*includeDescription*/false)), - total: body.total, - offset: body.offset, - limit: body.limit, - } -} - export async function fetchEventsOrErrors(eventIds: number[], limit: number = 100): Promise<[Record, Record]> { const eventsMap: Record = {} const errorsMap: Record = {} From a25090788bb10726a17832e0bd8eaed805d1e08d Mon Sep 17 00:00:00 2001 From: Juan Date: Mon, 9 Dec 2024 11:25:02 -0300 Subject: [PATCH 04/10] Make compass abort signal optional --- src/loaders/compass.ts | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/loaders/compass.ts b/src/loaders/compass.ts index e47b479..7e2cc4b 100644 --- a/src/loaders/compass.ts +++ b/src/loaders/compass.ts @@ -1,7 +1,11 @@ import { AbortedError, HttpError } from 'models/error' import { COMPASS_KEY, COMPASS_URL } from 'models/compass' -export async function requestCompass(query: string, variables: Record, abortSignal: AbortSignal): Promise { +export async function requestCompass( + query: string, + variables: Record, + abortSignal?: AbortSignal, +): Promise { let response: Response try { response = await fetch(COMPASS_URL, { @@ -103,7 +107,7 @@ export async function queryCompass( Model: (data: unknown) => T, query: string, variables: Record, - abortSignal: AbortSignal + abortSignal?: AbortSignal ): Promise { const results = await requestCompass(query, variables, abortSignal) @@ -125,7 +129,7 @@ export async function queryFirstCompass( query: string, variables: Record, defaultValue: T, - abortSignal: AbortSignal + abortSignal?: AbortSignal ): Promise { const FirstModel = (data: unknown): T =>{ if (data == null || !Array.isArray(data)) { @@ -155,7 +159,7 @@ export async function queryManyCompass( Model: (data: unknown) => T, query: string, variables: Record, - abortSignal: AbortSignal + abortSignal?: AbortSignal ): Promise { const ManyModel = (data: unknown): T[] => { if (data == null || !Array.isArray(data)) { @@ -178,7 +182,7 @@ export async function queryAllCompass( offsetKey: string, limit: number, total: number, - abortSignal: AbortSignal + abortSignal?: AbortSignal ): Promise { let results: T[] = [] let pageCount = 0 @@ -219,7 +223,7 @@ export async function queryAggregateCountCompass( name: string, query: string, variables: Record, - abortSignal: AbortSignal + abortSignal?: AbortSignal ): Promise { const AggregateCountModel = (data: unknown): number => { if ( @@ -248,7 +252,10 @@ export async function queryAggregateCountCompass( ) } -export async function countCompass(target: string, abortSignal: AbortSignal): Promise { +export async function countCompass( + target: string, + abortSignal?: AbortSignal, +): Promise { return await queryAggregateCountCompass( `${target}_aggregate`, ` From 446c36d38479f7845ff52a222fc02ae81a02cd62 Mon Sep 17 00:00:00 2001 From: Juan Date: Mon, 9 Dec 2024 11:48:46 -0300 Subject: [PATCH 05/10] Make first default value optional --- src/loaders/compass.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/loaders/compass.ts b/src/loaders/compass.ts index 7e2cc4b..31d15d2 100644 --- a/src/loaders/compass.ts +++ b/src/loaders/compass.ts @@ -128,7 +128,7 @@ export async function queryFirstCompass( Model: (data: unknown) => T, query: string, variables: Record, - defaultValue: T, + defaultValue?: T, abortSignal?: AbortSignal ): Promise { const FirstModel = (data: unknown): T =>{ @@ -139,7 +139,11 @@ export async function queryFirstCompass( } if (data.length === 0) { - return defaultValue + if (defaultValue) { + return defaultValue + } + + throw new HttpError(`Model ${name} empty results`, { status: 404 }) } return Model(data[0]) From dfa292c210adebd51f06db01c25a75bf24696ebd Mon Sep 17 00:00:00 2001 From: Juan Date: Mon, 9 Dec 2024 11:49:47 -0300 Subject: [PATCH 06/10] Fetch drops from Compass --- src/hooks/useEvent.ts | 4 +- src/loaders/drop.ts | 120 ++++++++++++++++++++++++++- src/loaders/event.ts | 188 ++---------------------------------------- src/models/event.ts | 1 + 4 files changed, 127 insertions(+), 186 deletions(-) diff --git a/src/hooks/useEvent.ts b/src/hooks/useEvent.ts index 6018478..83243fb 100644 --- a/src/hooks/useEvent.ts +++ b/src/hooks/useEvent.ts @@ -1,7 +1,7 @@ import { useCallback, useState } from 'react' -import { fetchEvent as loadEvent } from 'loaders/event' import { AbortedError } from 'models/error' import { Drop } from 'models/drop' +import { fetchDrop } from 'loaders/drop' function useEvent(eventId?: number): { loadingEvent: boolean @@ -24,7 +24,7 @@ function useEvent(eventId?: number): { setLoading(true) setError(null) setEvent(null) - loadEvent( + fetchDrop( newEventId ?? eventId, /*includeDescription*/false, controller.signal diff --git a/src/loaders/drop.ts b/src/loaders/drop.ts index 9a31c99..b856f8e 100644 --- a/src/loaders/drop.ts +++ b/src/loaders/drop.ts @@ -1,7 +1,8 @@ import { DEFAULT_COMPASS_LIMIT } from 'models/compass' -import { DEFAULT_SEARCH_LIMIT } from 'models/event' +import { DEFAULT_DROP_LIMIT, DEFAULT_SEARCH_LIMIT } from 'models/event' import { Drop, parseDrop } from 'models/drop' -import { queryAggregateCountCompass, queryManyCompass } from 'loaders/compass' +import { queryAggregateCountCompass, queryFirstCompass, queryManyCompass } from 'loaders/compass' +import { HttpError } from 'models/error' export async function searchDrops( query: string, @@ -80,3 +81,118 @@ export async function searchDrops( limit, } } + +export async function fetchDropsOrErrors( + dropIds: number[], + limit: number = Math.min(DEFAULT_DROP_LIMIT, DEFAULT_COMPASS_LIMIT), +): Promise<[ + Record, + Record +]> { + const eventsMap: Record = {} + const errorsMap: Record = {} + + for (let i = 0; i < dropIds.length; i += limit) { + const ids = dropIds.slice(i, i + limit) + + if (ids.length === 0) { + break + } + + try { + const drops = await queryManyCompass( + 'drops', + (data: unknown): Drop => parseDrop(data, /*includeDescription*/false), + ` + query FetchDrops($dropIds: [Int!]) { + drops(where: { id: { _in: $dropIds } }) { + id + name + description + image_url + city + country + start_date + end_date + expiry_date + drop_image { + gateways { + type + url + } + } + } + } + `, + { + dropIds: ids, + }, + ) + + for (const id of ids) { + const drop = drops.find((drop) => drop.id === id) + + if (!drop) { + errorsMap[id] = new HttpError(`Drop '${id}' not found`, { + status: 404, + }) + } else { + eventsMap[id] = drop + } + } + } catch (error: unknown) { + for (const id of ids) { + errorsMap[id] = error instanceof Error + ? error + : new Error(`Failed to fetch drop '${id}'`, { cause: error }) + } + } + } + + return [eventsMap, errorsMap] +} + +export async function fetchDrop( + dropId: number, + includeDescription: boolean, + abortSignal?: AbortSignal, +): Promise { + try { + return await queryFirstCompass( + `drops`, + (data: unknown): Drop => parseDrop(data, includeDescription), + ` + query FetchDrop($dropId: Int!) { + drops(limit: 1, where: { id: { _eq: $dropId } }) { + id + name + description + image_url + city + country + start_date + end_date + expiry_date + drop_image { + gateways { + type + url + } + } + } + } + `, + { + dropId, + }, + undefined, + abortSignal, + ) + } catch (error: unknown) { + if (error instanceof HttpError && error.status === 404) { + return null + } + + throw error + } +} diff --git a/src/loaders/event.ts b/src/loaders/event.ts index b07a3f3..0c0e923 100644 --- a/src/loaders/event.ts +++ b/src/loaders/event.ts @@ -1,186 +1,10 @@ import { filterInvalidOwners } from 'models/address' import { parseEventIds } from 'models/event' -import { Drop, parseDrop } from 'models/drop' -import { POAP_API_URL, POAP_API_KEY } from 'models/poap' -import { AbortedError, HttpError } from 'models/error' +import { Drop } from 'models/drop' +import { HttpError } from 'models/error' import { getEventAndOwners, getEventMetrics, getEvents } from 'loaders/api' import { fetchPOAPs } from 'loaders/poap' - -export async function fetchEventsOrErrors(eventIds: number[], limit: number = 100): Promise<[Record, Record]> { - const eventsMap: Record = {} - const errorsMap: Record = {} - - for (let i = 0; i < eventIds.length; i += limit) { - const ids = eventIds.slice(i, i + limit) - - if (ids.length === 0) { - break - } - - let response: Response - try { - response = await fetch( - `${POAP_API_URL}/paginated-events` + - `?event_ids=${ids.map((eventId) => encodeURIComponent(eventId)).join(',')}` + - `&limit=${limit}`, - { - headers: { - 'x-api-key': POAP_API_KEY, - }, - } - ) - } catch (err: unknown) { - for (const id of ids) { - errorsMap[id] = new Error( - `Cannot fetch drop ${id}: response was not success (network error)`, - { cause: err } - ) - } - - continue - } - - if (response.status !== 200) { - let message: string | undefined - try { - const data = await response.json() - if ( - data != null && - typeof data === 'object' && - 'message' in data && - data.message != null && - typeof data.message === 'string' - ) { - message = data.message - } - } catch (err: unknown) { - console.error(err) - } - - for (const id of ids) { - if (message) { - errorsMap[id] = new HttpError( - `Cannot fetch drop ${id}: ` + - `response was not success (status ${response.status}): ${message}`, - { status: response.status } - ) - } else { - errorsMap[id] = new HttpError( - `Cannot fetch drop ${id}: ` + - `response was not success (status ${response.status})`, - { status: response.status } - ) - } - } - - continue - } - - try { - const data: unknown = await response.json() - if ( - data != null && - typeof data === 'object' && - 'items' in data && - data.items != null && - Array.isArray(data.items) - ) { - for (const item of data.items) { - const event = parseDrop(item, /*includeDescription*/false) - eventsMap[event.id] = event - } - - for (const id of ids) { - if (!(id in eventsMap)) { - errorsMap[id] = new HttpError( - `Drop '${id}' not found on response`, - { status: 404 } - ) - } - } - } else { - for (const id of ids) { - if (!(id in eventsMap)) { - errorsMap[id] = new Error(`Malformed drop response '${id}'`) - } - } - } - } catch (err: unknown) { - console.error(err) - - for (const id of ids) { - if (!(id in eventsMap)) { - errorsMap[id] = new Error(`Malformed drop '${id}': ${err}`) - } - } - } - } - - return [eventsMap, errorsMap] -} - -export async function fetchEvent(eventId: number, includeDescription: boolean, abortSignal?: AbortSignal): Promise { - let response: Response - try { - response = await fetch(`${POAP_API_URL}/events/id/${eventId}`, { - signal: abortSignal instanceof AbortSignal ? abortSignal : null, - headers: { - 'x-api-key': POAP_API_KEY, - }, - }) - } catch (err: unknown) { - if (err instanceof Error && err.name === 'AbortError') { - throw new AbortedError(`Fetch drop ${eventId} aborted`, { cause: err }) - } - throw new Error( - `Cannot fetch drop ${eventId}: response was not success (network error)`, - { cause: err } - ) - } - - if (response.status === 404) { - return null - } - - if (response.status === 400) { - let message: string = 'Unknown error' - try { - const data = await response.json() - if ( - data != null && - typeof data === 'object' && - 'message' in data && - data.message != null && - typeof data.message === 'string' - ) { - message = data.message - } - } catch (err: unknown) { - console.error(err) - } - - throw new HttpError( - `Fetch drop '${eventId}' response was not success: ${message}`, - { status: 400 } - ) - } - - if (response.status !== 200) { - throw new HttpError( - `Fetch drop '${eventId}' response was not success ` + - `(status ${response.status})`, - { status: response.status } - ) - } - - const body: unknown = await response.json() - - if (typeof body !== 'object') { - throw new Error(`Malformed drop (type ${typeof body})`) - } - - return parseDrop(body, includeDescription) -} +import { fetchDrop, fetchDropsOrErrors } from 'loaders/drop' export async function eventLoader({ params, request }) { const force = new URL(request.url).searchParams.get('force') === 'true' @@ -205,7 +29,7 @@ export async function eventLoader({ params, request }) { console.error(err) } - const event = await fetchEvent(params.eventId, /*includeDescription*/true) + const event = await fetchDrop(params.eventId, /*includeDescription*/true) if (!event) { throw new Response('', { @@ -300,7 +124,7 @@ export async function eventsLoader({ params, request }) { events = {} } - const [freshEvents, errors] = await fetchEventsOrErrors( + const [freshEvents, errors] = await fetchDropsOrErrors( notFoundEventIds ?? eventIds ) @@ -312,7 +136,7 @@ export async function eventsLoader({ params, request }) { ([, error]) => error instanceof HttpError && error.status === 404 ) .map(([rawEventId]) => - fetchEvent(parseInt(rawEventId), /*includeDescription*/false) + fetchDrop(parseInt(rawEventId), /*includeDescription*/false) ) ) diff --git a/src/models/event.ts b/src/models/event.ts index e8ffdc7..815470e 100644 --- a/src/models/event.ts +++ b/src/models/event.ts @@ -3,6 +3,7 @@ import { Drop } from 'models/drop' export const SEARCH_LIMIT = 10 +export const DEFAULT_DROP_LIMIT = 100 export const DEFAULT_SEARCH_LIMIT = 10 export function parseEventIds(rawIds: string): number[] { From bb5dabb4b7acd43b1f65da4f08db120925f74e74 Mon Sep 17 00:00:00 2001 From: Juan Date: Mon, 9 Dec 2024 11:54:28 -0300 Subject: [PATCH 07/10] CS --- src/loaders/drop.ts | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/loaders/drop.ts b/src/loaders/drop.ts index b856f8e..d64d507 100644 --- a/src/loaders/drop.ts +++ b/src/loaders/drop.ts @@ -162,25 +162,25 @@ export async function fetchDrop( `drops`, (data: unknown): Drop => parseDrop(data, includeDescription), ` - query FetchDrop($dropId: Int!) { - drops(limit: 1, where: { id: { _eq: $dropId } }) { - id - name - description - image_url - city - country - start_date - end_date - expiry_date - drop_image { - gateways { - type - url - } + query FetchDrop($dropId: Int!) { + drops(limit: 1, where: { id: { _eq: $dropId } }) { + id + name + description + image_url + city + country + start_date + end_date + expiry_date + drop_image { + gateways { + type + url } } } + } `, { dropId, From 7d95e40a72350ad28e19a5588f8abcc38988a8ee Mon Sep 17 00:00:00 2001 From: Juan Date: Mon, 9 Dec 2024 11:55:20 -0300 Subject: [PATCH 08/10] CS --- src/loaders/drop.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/loaders/drop.ts b/src/loaders/drop.ts index d64d507..199e4ef 100644 --- a/src/loaders/drop.ts +++ b/src/loaders/drop.ts @@ -140,11 +140,11 @@ export async function fetchDropsOrErrors( eventsMap[id] = drop } } - } catch (error: unknown) { + } catch (err: unknown) { for (const id of ids) { - errorsMap[id] = error instanceof Error - ? error - : new Error(`Failed to fetch drop '${id}'`, { cause: error }) + errorsMap[id] = err instanceof Error + ? err + : new Error(`Failed to fetch drop '${id}'`, { cause: err }) } } } @@ -188,11 +188,11 @@ export async function fetchDrop( undefined, abortSignal, ) - } catch (error: unknown) { - if (error instanceof HttpError && error.status === 404) { + } catch (err: unknown) { + if (err instanceof HttpError && err.status === 404) { return null } - throw error + throw err } } From 70bd6abf3acb670331777febf3a65c543a3f50c5 Mon Sep 17 00:00:00 2001 From: Juan Date: Mon, 9 Dec 2024 12:02:31 -0300 Subject: [PATCH 09/10] Stream log instead of reject --- src/hooks/useEventsInCommon.ts | 1 - src/loaders/api.ts | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/hooks/useEventsInCommon.ts b/src/hooks/useEventsInCommon.ts index f1c2745..2529334 100644 --- a/src/hooks/useEventsInCommon.ts +++ b/src/hooks/useEventsInCommon.ts @@ -43,7 +43,6 @@ function useEventsInCommon( () => { for (const eventId of eventIds) { if (eventsOwners[eventId] == null) { - console.error('Missing event owners', { eventId }) continue } if ( diff --git a/src/loaders/api.ts b/src/loaders/api.ts index 79225f9..b430675 100644 --- a/src/loaders/api.ts +++ b/src/loaders/api.ts @@ -510,13 +510,10 @@ export async function getInCommonEventsWithEvents( totalEvents ) } else { - resolved = true - inCommonStream.close() - reject(new Error( + console.info( `Received empty list of events ` + `when streaming event '${eventId}' in common` - )) - return + ) } } if ( From 5aeac95ff9697a3112ea3893b43c1a836b483a26 Mon Sep 17 00:00:00 2001 From: Juan Date: Mon, 9 Dec 2024 12:05:34 -0300 Subject: [PATCH 10/10] Version 1.19 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4c1aa29..76af0bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@poap-xyz/poap-family", - "version": "1.18.0", + "version": "1.19.0", "author": { "name": "POAP", "url": "https://poap.xyz"