@apollion-dsi/relay 0.25.10 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,42 @@
1
+ import { MutationConfig, MutationParameters } from 'relay-runtime';
2
+ import { Environment } from 'relay-runtime/lib/store/RelayStoreTypes';
3
+ /**
4
+ * Wrapper Promise-based em torno do `commitMutation` do `relay-runtime`.
5
+ *
6
+ * O `commitMutation` original do Relay expõe a conclusão da mutação via os
7
+ * callbacks `onCompleted` e `onError`. Esta função encapsula esses callbacks
8
+ * em uma `Promise`, permitindo o uso natural de `async/await` no consumidor —
9
+ * o que tipicamente cobre 95% dos casos. Para fluxos que precisam de
10
+ * `optimisticResponse`, `updater`, `cacheConfig` etc., todas as demais opções
11
+ * de `MutationConfig` continuam sendo aceitas via `config`.
12
+ *
13
+ * Os campos `onCompleted` e `onError` são omitidos do `config` de propósito:
14
+ * eles são gerenciados internamente para alimentar o resolve/reject da Promise.
15
+ *
16
+ * @typeParam T - Tipo gerado pelo Relay Compiler para a mutação (`graphql`
17
+ * tagged) — fornece o shape de `variables` e `response`.
18
+ *
19
+ * @param environment - Ambiente Relay (geralmente o exposto por
20
+ * `CreateRelayEnvironment`).
21
+ * @param config - Configuração da mutação, sem `onCompleted` e `onError`.
22
+ *
23
+ * @returns Promise que resolve com `T['response']` ou rejeita com o erro
24
+ * retornado pelo Relay.
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * import { commitMutation } from '@apollion-dsi/relay/commitMutation';
29
+ * import { Environment } from './environment';
30
+ * import { MyMutation } from './__generated__/MyMutation.graphql';
31
+ *
32
+ * const response = await commitMutation<MyMutation>(Environment, {
33
+ * mutation: graphql`
34
+ * mutation MyMutation($input: MyInput!) {
35
+ * myMutation(input: $input) { ok }
36
+ * }
37
+ * `,
38
+ * variables: { input: { ... } },
39
+ * });
40
+ * ```
41
+ */
42
+ export declare const commitMutation: <T extends MutationParameters = MutationParameters>(environment: Environment, config: Omit<MutationConfig<T>, "onCompleted" | "onError">) => Promise<T["response"]>;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Barrel público do módulo `commitMutation`.
3
+ *
4
+ * Reexporta a função `commitMutation` para que consumidores possam importar
5
+ * via `@apollion-dsi/relay/commitMutation` (importação granular, evitando
6
+ * carregar o resto do pacote) ou via `@apollion-dsi/relay` (barrel raiz).
7
+ */
8
+ export * from './commitMutation';
package/lib/index.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Barrel público do package `@apollion-dsi/relay`.
3
+ *
4
+ * Reexporta a superfície pública do package: a fábrica de Environment
5
+ * (`CreateRelayEnvironment`), o Context/hook (`EnvironmentProvider` /
6
+ * `useEnvironment`), os tipos de configuração (`RelayArgsInterface`,
7
+ * `Sink`), os utilitários de updater de mutations e o wrapper
8
+ * Promise-based `commitMutation`.
9
+ *
10
+ * Cada módulo também pode ser importado de forma granular via
11
+ * `@apollion-dsi/relay/<nome>` quando o consumidor quiser carregar
12
+ * menos código.
13
+ */
14
+ export { default as CreateRelayEnvironment } from './setupRelayEnvironment/setupRelayEnvironment';
15
+ export * from './useEnvironment';
16
+ export * from './relayArgsInterface';
17
+ export * from './mutationUtils';
18
+ export * from './commitMutation';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Barrel público do módulo `mutationUtils`.
3
+ *
4
+ * Reexporta os helpers para escrever `updater`s de mutations Relay
5
+ * (listas, connections, optimistic responses). Importável via
6
+ * `@apollion-dsi/relay/mutationUtils` (granular) ou via
7
+ * `@apollion-dsi/relay` (barrel raiz).
8
+ */
9
+ export * from './mutationUtils';
@@ -0,0 +1,133 @@
1
+ /**
2
+ * @fileoverview Utilitários para escrever `updater`s de mutations Relay
3
+ * de forma declarativa, sem precisar manipular `RecordProxy` /
4
+ * `ConnectionHandler` à mão em cada caso comum.
5
+ *
6
+ * Cobre os cenários mais frequentes:
7
+ * - inserir/remover itens em listas (`linked records`);
8
+ * - inserir/remover edges em connections (paginação Relay);
9
+ * - copiar campos escalares de um objeto JS para um `RecordProxy`
10
+ * (útil em respostas otimistas).
11
+ */
12
+ import { RecordProxy, RecordSourceSelectorProxy } from 'relay-runtime';
13
+ /**
14
+ * Verifica se um valor é um objeto não-nulo (para detectar campos
15
+ * escalares vs. linked records em `copyObjScalarsToProxy`).
16
+ */
17
+ export declare function isObject(obj: any): boolean;
18
+ /** Argumentos de `listRecordRemoveUpdater`. */
19
+ type ListRecordRemoveUpdaterOptions = {
20
+ parentId: string;
21
+ itemId: string;
22
+ parentFieldName: string;
23
+ store: RecordSourceSelectorProxy;
24
+ };
25
+ /** Argumentos de `listRecordAddUpdater`. */
26
+ type ListRecordAddUpdaterOptions = {
27
+ parentId: string;
28
+ item: Record<string, any>;
29
+ type: string;
30
+ parentFieldName: string;
31
+ store: RecordSourceSelectorProxy;
32
+ };
33
+ /** Argumentos de `optimisticConnectionUpdater`. */
34
+ type OptimisticConnectionUpdaterOptions = {
35
+ parentId: string;
36
+ store: RecordSourceSelectorProxy;
37
+ connectionName: string;
38
+ item: Record<string, any>;
39
+ customNode: RecordProxy;
40
+ itemType: string;
41
+ };
42
+ /** Argumentos de `connectionDeleteEdgeUpdater`. */
43
+ type ConnectionDeleteEdgeUpdaterOptions = {
44
+ parentId: string;
45
+ connectionName: string;
46
+ nodeId: string;
47
+ store: RecordSourceSelectorProxy;
48
+ };
49
+ /** Argumentos de `copyObjScalarsToProxy`. */
50
+ type CopyObjScalarsToProxyOptions = {
51
+ object: Record<string, any>;
52
+ proxy: RecordProxy;
53
+ };
54
+ /**
55
+ * Identificador único gerado uma vez por carregamento do módulo,
56
+ * destinado ao campo `clientMutationId` esperado pelo padrão Relay
57
+ * Modern.
58
+ */
59
+ export declare const ClientMutationID: string;
60
+ /**
61
+ * Remove um item de uma lista de `linked records` num parent.
62
+ *
63
+ * Usa `getLinkedRecords` + `setLinkedRecords` com filtro por `dataID`.
64
+ * Para connections paginadas, prefira `connectionDeleteEdgeUpdater`.
65
+ *
66
+ * @param options.parentId - DataID do parent que possui o linked field.
67
+ * @param options.itemId - DataID do item a remover.
68
+ * @param options.parentFieldName - Nome do field linked no parent.
69
+ * @param options.store - `RecordSourceSelectorProxy` recebido no updater.
70
+ */
71
+ export declare function listRecordRemoveUpdater({ parentId, itemId, parentFieldName, store, }: ListRecordRemoveUpdaterOptions): void;
72
+ /**
73
+ * Adiciona um item ao final de uma lista de `linked records`.
74
+ *
75
+ * Cria um novo `RecordProxy` usando `item.id` como dataID e copia todos
76
+ * os campos de `item` para o novo record (sem validar tipos — escalares
77
+ * e linked refs viram `setValue`).
78
+ *
79
+ * @param options.parentId - DataID do parent.
80
+ * @param options.item - Objeto com os campos do novo record (precisa ter `id`).
81
+ * @param options.type - Tipo GraphQL do record (ex: `'Todo'`).
82
+ * @param options.parentFieldName - Nome do field linked no parent.
83
+ * @param options.store - `RecordSourceSelectorProxy` do updater.
84
+ */
85
+ export declare function listRecordAddUpdater({ parentId, item, type, parentFieldName, store, }: ListRecordAddUpdaterOptions): void;
86
+ /**
87
+ * Insere um edge em uma connection paginada (Relay Connections spec).
88
+ *
89
+ * @param store - `RecordSourceSelectorProxy` do updater.
90
+ * @param parentId - DataID do parent que possui a connection.
91
+ * @param connectionName - Nome da connection no schema (`@connection(key: ...)`).
92
+ * @param edge - O `RecordProxy` do edge já criado pelo chamador.
93
+ * @param before - Se `true`, insere antes do primeiro edge (default `false`).
94
+ */
95
+ export declare function connectionUpdater(store: RecordSourceSelectorProxy, parentId: string, connectionName: string, edge: RecordProxy, before?: boolean): void;
96
+ /**
97
+ * Variante de `connectionUpdater` usada em respostas otimistas: cria o
98
+ * node e o edge "do zero" a partir de um objeto JS, simulando o que o
99
+ * servidor retornaria.
100
+ *
101
+ * @param options.parentId - DataID do parent.
102
+ * @param options.store - `RecordSourceSelectorProxy`.
103
+ * @param options.connectionName - Nome da connection.
104
+ * @param options.item - Objeto com os campos do novo node (precisa ter `id`).
105
+ * @param options.customNode - `RecordProxy` pré-criado (opcional, evita criar via `item`).
106
+ * @param options.itemType - Tipo GraphQL do node (ex: `'Todo'`); o edge
107
+ * será criado como `<itemType>Edge`.
108
+ */
109
+ export declare function optimisticConnectionUpdater({ parentId, store, connectionName, item, customNode, itemType, }: OptimisticConnectionUpdaterOptions): void;
110
+ /**
111
+ * Remove um node de uma connection paginada pelo seu `dataID`.
112
+ *
113
+ * Loga `console.warn` se a connection não for encontrada (geralmente
114
+ * significa que o `connectionName` está errado).
115
+ *
116
+ * @param options.parentId - DataID do parent.
117
+ * @param options.connectionName - Nome da connection.
118
+ * @param options.nodeId - DataID do node a remover.
119
+ * @param options.store - `RecordSourceSelectorProxy`.
120
+ */
121
+ export declare function connectionDeleteEdgeUpdater({ parentId, connectionName, nodeId, store, }: ConnectionDeleteEdgeUpdaterOptions): void;
122
+ /**
123
+ * Copia os campos escalares de um objeto JS para um `RecordProxy`.
124
+ *
125
+ * Ignora campos que são objetos ou arrays (linked records / linked
126
+ * record lists) — esses precisam ser manipulados com
127
+ * `setLinkedRecord`/`setLinkedRecords`.
128
+ *
129
+ * @param options.object - Objeto JS de origem.
130
+ * @param options.proxy - `RecordProxy` de destino.
131
+ */
132
+ export declare function copyObjScalarsToProxy({ object, proxy }: CopyObjScalarsToProxyOptions): void;
133
+ export {};
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Barrel público do módulo `relayArgsInterface`.
3
+ *
4
+ * Reexporta os tipos `RelayArgsInterface` (config aceita por
5
+ * `CreateRelayEnvironment`) e `Sink` (interface do Observable Relay).
6
+ */
7
+ export * from './relayArgsInterface';
@@ -0,0 +1,163 @@
1
+ /**
2
+ * @fileoverview Tipos públicos consumidos pelo `setupRelayEnvironment`
3
+ * (`RelayArgsInterface`) e pelos seus internals (`Sink`).
4
+ *
5
+ * `RelayArgsInterface` é o contrato de configuração de
6
+ * `CreateRelayEnvironment` — qualquer mudança aqui é parte da API
7
+ * pública do package.
8
+ */
9
+ /**
10
+ * Sink do `Observable` Relay. Implementado pelo runtime do Relay e
11
+ * passado para o callback de `Observable.create`. Os internals de
12
+ * fetch/subscription usam essa interface para propagar dados, erros
13
+ * e completação para a stream.
14
+ */
15
+ export interface Sink {
16
+ /** Empurra o próximo valor para a stream. */
17
+ next(value: any): void;
18
+ /** Reporta um erro e fecha a stream. */
19
+ error(error: Error, isUncaughtThrownError?: boolean): void;
20
+ /** Sinaliza completação normal da stream. */
21
+ complete(): void;
22
+ /** `true` quando a stream já foi fechada (sink complete/error). */
23
+ readonly closed: boolean;
24
+ }
25
+ /**
26
+ * Configuração aceita pelo construtor de `CreateRelayEnvironment`.
27
+ *
28
+ * Apenas `url` é obrigatório. Todos os demais campos têm defaults
29
+ * sensatos definidos no construtor.
30
+ *
31
+ * @see {@link "../setupRelayEnvironment/setupRelayEnvironment"} para os defaults.
32
+ */
33
+ export interface RelayArgsInterface {
34
+ /**
35
+ * Endpoint HTTP do servidor GraphQL. Obrigatório.
36
+ */
37
+ url: string;
38
+ /**
39
+ * Endpoint do servidor de autenticação. Usado pelo fluxo de
40
+ * verificação de sessão (`${authUrl}user/me`). Obrigatório quando
41
+ * `useAuthorization` é `true`.
42
+ */
43
+ authUrl?: string;
44
+ /**
45
+ * Endpoint WebSocket para subscriptions GraphQL. Obrigatório quando
46
+ * `useSubscription` é `true`.
47
+ */
48
+ socket?: string;
49
+ /**
50
+ * Lista de delays (ms) entre tentativas de retry, com backoff.
51
+ *
52
+ * @defaultValue `[1s, 2s, 3s, 5s, 8s, 13s, 21s, 34s]` (fibonacci-like)
53
+ */
54
+ retries?: number[];
55
+ /**
56
+ * Timeout por requisição em ms.
57
+ *
58
+ * @defaultValue 15 minutos
59
+ */
60
+ timeout?: number;
61
+ /**
62
+ * Habilita o handler de subscriptions GraphQL via WebSocket.
63
+ *
64
+ * @defaultValue false
65
+ */
66
+ useSubscription?: boolean;
67
+ /**
68
+ * Habilita autenticação via JWT — injeta `Authorization: Bearer <token>`
69
+ * em cada request usando o `sessionToken` armazenado.
70
+ *
71
+ * @defaultValue false
72
+ */
73
+ useAuthorization?: boolean;
74
+ /**
75
+ * Habilita cache de respostas via `QueryResponseCache` do Relay.
76
+ * Por recomendação do time do Relay, vem desligado por padrão.
77
+ *
78
+ * @defaultValue false
79
+ */
80
+ useCache?: boolean;
81
+ /**
82
+ * TTL (ms) das entradas do cache de queries.
83
+ *
84
+ * @defaultValue 8 minutos
85
+ */
86
+ cacheTime?: number;
87
+ /**
88
+ * Tamanho máximo do cache (número de queries distintas mantidas).
89
+ *
90
+ * @defaultValue 250
91
+ */
92
+ cacheSize?: number;
93
+ /**
94
+ * Nome da chave usada em storage para o token de sessão (JWT).
95
+ *
96
+ * @defaultValue `'USER_SESSION_TOKEN'`
97
+ */
98
+ sessionStorageProp?: string;
99
+ /**
100
+ * Nome da chave usada em storage para o refresh token.
101
+ *
102
+ * @defaultValue `'USER_REFRESH_TOKEN'`
103
+ */
104
+ refreshStorageProp?: string;
105
+ /**
106
+ * Rota interna para a qual o usuário será redirecionado quando a
107
+ * sessão expirar (ou em erros de auth, se `redirectOnError` estiver
108
+ * ligado).
109
+ *
110
+ * @defaultValue `'/'`
111
+ */
112
+ loginRoute?: string;
113
+ /**
114
+ * Liga logs verbosos (útil em desenvolvimento). Não habilite em
115
+ * produção.
116
+ *
117
+ * @defaultValue false
118
+ */
119
+ useDebug?: boolean;
120
+ /**
121
+ * Habilita retries em caso de erro/timeout, usando os delays de
122
+ * `retries`.
123
+ *
124
+ * @defaultValue false
125
+ */
126
+ useRetries?: boolean;
127
+ /**
128
+ * Redireciona o usuário para `loginRoute` quando um erro com status
129
+ * em `authenticationErrors` é detectado e a sessão é considerada
130
+ * inválida.
131
+ *
132
+ * @defaultValue false
133
+ */
134
+ redirectOnError?: boolean;
135
+ /**
136
+ * Lista de status HTTP que disparam retry (quando `useRetries` é
137
+ * `true`). Defaults cobrem erros típicos de Cloudflare.
138
+ *
139
+ * @defaultValue `[503, 504, 521, 522, 524]`
140
+ * @see https://support.cloudflare.com/hc/pt-br/articles/115003011431-Solu%C3%A7%C3%A3o-de-problemas-de-erros-5XX-da-Cloudflare
141
+ */
142
+ retryWhen?: number[];
143
+ /**
144
+ * Status considerados como erro de autenticação para o fluxo de
145
+ * `redirectOnError`.
146
+ *
147
+ * @defaultValue `[401, 403]`
148
+ */
149
+ authenticationErrors?: number[];
150
+ /**
151
+ * Estratégia de storage no browser para tokens.
152
+ *
153
+ * @defaultValue `'localStorage'`
154
+ */
155
+ storageType?: 'cookie' | 'localStorage';
156
+ /**
157
+ * Identificador de parceiro enviado em todas as requests via
158
+ * header `X-Partner`. Útil para tenants/whitelabels.
159
+ *
160
+ * @defaultValue undefined
161
+ */
162
+ partner?: string;
163
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @fileoverview Detecção de ambiente de execução (browser/DOM,
3
+ * Worker, viewport) usada por `setupRelayEnvironment` para alternar
4
+ * comportamento entre browser e SSR.
5
+ *
6
+ * Não exposto pelo barrel raiz — implementação interna de
7
+ * `CreateRelayEnvironment`.
8
+ */
9
+ /**
10
+ * Conjunto de flags booleanas indicando capacidades do ambiente atual.
11
+ * Inspirado no antigo `fbjs/ExecutionEnvironment`. Usado para evitar
12
+ * dependências circulares e permitir que o código reaja a SSR sem
13
+ * importar React diretamente.
14
+ */
15
+ declare const _default: {
16
+ /** `true` em browsers. */
17
+ canUseDOM: boolean;
18
+ /** `true` se `Worker` está disponível globalmente. */
19
+ canUseWorkers: boolean;
20
+ /** `true` em browsers que suportam `addEventListener` ou IE `attachEvent`. */
21
+ canUseEventListeners: boolean;
22
+ /** `true` em browsers com `window.screen` (viewport mensurável). */
23
+ canUseViewport: boolean;
24
+ /** `true` quando rodando em um Worker (sem DOM). */
25
+ isInWorker: boolean;
26
+ };
27
+ export default _default;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @fileoverview Fábrica da `fetchFn` do Relay usada por
3
+ * `setupRelayEnvironment`. Encapsula cache de respostas, retries via
4
+ * `fetchWithRetries`, tratamento de erros (incluindo redirect em caso
5
+ * de sessão expirada) e adaptação para o modelo `Observable` que o
6
+ * `Network.create` do Relay espera.
7
+ *
8
+ * Não exposto pelo barrel raiz — implementação interna de
9
+ * `CreateRelayEnvironment`.
10
+ */
11
+ import { CacheConfig, Observable, RequestParameters, UploadableMap, Variables } from 'relay-runtime';
12
+ /**
13
+ * Cria a fetch function que será passada para `Network.create` do Relay.
14
+ *
15
+ * Comportamento:
16
+ * - Para queries com `useCache` ligado, tenta servir do cache
17
+ * (`QueryResponseCache`) antes de fazer a requisição.
18
+ * - Para mutations com `useCache` ligado, limpa o cache (invalidação
19
+ * total — Relay vai re-buscar queries afetadas).
20
+ * - Em caso de erro de autenticação detectado, chama o endpoint
21
+ * `${authUrl}user/me` e, se inválido, limpa storage e redireciona
22
+ * para `loginRoute`.
23
+ * - Erros são propagados via `sink.error()`.
24
+ *
25
+ * @param config - Instância de `CreateRelayEnvironment` configurada.
26
+ * @returns Função compatível com `Network.create(fetchFn, ...)`.
27
+ */
28
+ export declare const createFetchFunction: (config: any) => (request: RequestParameters, variables: Variables, cacheConfig: CacheConfig, uploadables: UploadableMap) => Observable<unknown>;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @fileoverview Implementação de `fetch` com retries e timeout, usada
3
+ * internamente por `fetchQuery` no `setupRelayEnvironment`.
4
+ *
5
+ * Adiciona em cima do `fetch` nativo:
6
+ * - timeout por requisição (`AbortController`);
7
+ * - retry com delays configuráveis (`retries` no config) quando o
8
+ * status HTTP está em `retryWhen` ou houve timeout;
9
+ * - debug logs opcionais via `useDebug`.
10
+ *
11
+ * Não exposto pelo barrel raiz — implementação interna de
12
+ * `CreateRelayEnvironment`.
13
+ */
14
+ /**
15
+ * Subconjunto de `RequestInit` aceito por `fetchWithRetries`. Mantemos
16
+ * apenas os campos efetivamente repassados ao `fetch` nativo.
17
+ */
18
+ export type InitWithRetries = {
19
+ body?: BodyInit | null;
20
+ cache?: RequestCache;
21
+ credentials?: RequestCredentials;
22
+ headers?: HeadersInit;
23
+ method?: string;
24
+ mode?: RequestMode;
25
+ };
26
+ /**
27
+ * Cria uma função `fetch` configurada com timeout e retries.
28
+ *
29
+ * Comportamento:
30
+ * - Cada tentativa tem timeout de `config.timeout` ms (via `AbortController`).
31
+ * - Se houver timeout ou o status estiver em `config.retryWhen`, agenda
32
+ * a próxima tentativa com o delay correspondente em `config.retries`.
33
+ * - Retries só ocorrem quando há DOM disponível e `config.useRetries`
34
+ * é `true` — no SSR a primeira tentativa é a única.
35
+ * - Erros não relacionados a `AbortError` são rejeitados imediatamente.
36
+ *
37
+ * @param config - Configuração do environment (timeout, retries,
38
+ * retryWhen, useRetries, useDebug, url).
39
+ * @returns Função que recebe `RequestInit` e retorna `Promise<Response>`.
40
+ */
41
+ export declare function fetchWithRetries(config: any): (init?: InitWithRetries | null) => Promise<any>;
@@ -0,0 +1,128 @@
1
+ import { Environment } from 'relay-runtime';
2
+ import { RelayArgsInterface } from '../relayArgsInterface';
3
+ declare const kMountEnvironment: unique symbol;
4
+ declare const kFeetchHandler: unique symbol;
5
+ declare const kSubscriptionHandler: unique symbol;
6
+ /**
7
+ * Fábrica de `Environment` Relay com bateria inclusa: autenticação JWT,
8
+ * retries com backoff, suporte a subscriptions via WebSocket, cache de
9
+ * respostas e estratégia plugável de storage (localStorage ou cookie).
10
+ *
11
+ * Esta classe encapsula a configuração que normalmente precisaria ser
12
+ * escrita à mão para cada projeto que usa Relay: criar `Network`,
13
+ * `RecordSource`, `Store`, configurar `fetch` com retries/timeout,
14
+ * adicionar headers de autenticação, lidar com redirecionamento em caso
15
+ * de sessão expirada e opcionalmente registrar um handler de
16
+ * subscriptions GraphQL.
17
+ *
18
+ * A instância exposta possui a propriedade pública `Environment`, que é
19
+ * o objeto que deve ser passado para `react-relay` (via `RelayEnvironmentProvider`
20
+ * ou `QueryRenderer`).
21
+ *
22
+ * @example Setup mínimo
23
+ * ```ts
24
+ * import { CreateRelayEnvironment } from '@apollion-dsi/relay';
25
+ *
26
+ * const { Environment } = new CreateRelayEnvironment({
27
+ * url: 'https://api.example.com/graphql/',
28
+ * });
29
+ * ```
30
+ *
31
+ * @example Com auth JWT e retries
32
+ * ```ts
33
+ * const { Environment } = new CreateRelayEnvironment({
34
+ * url: 'https://api.example.com/graphql/',
35
+ * authUrl: 'https://api.example.com/auth/',
36
+ * useAuthorization: true,
37
+ * useRetries: true,
38
+ * retries: [1000, 2000, 5000],
39
+ * });
40
+ * ```
41
+ *
42
+ * @example Com subscriptions
43
+ * ```ts
44
+ * const { Environment } = new CreateRelayEnvironment({
45
+ * url: 'https://api.example.com/graphql/',
46
+ * socket: 'wss://api.example.com/graphql/',
47
+ * useSubscription: true,
48
+ * });
49
+ * ```
50
+ *
51
+ * @see {@link RelayArgsInterface} para a lista completa de opções de configuração.
52
+ */
53
+ export default class RelayEnvironment implements RelayArgsInterface {
54
+ /** Endpoint HTTP do servidor GraphQL. Obrigatório. */
55
+ url: string;
56
+ /** Endpoint do servidor de autenticação. Obrigatório quando `useAuthorization` é `true`. */
57
+ authUrl?: string;
58
+ /** Endpoint WebSocket para subscriptions. Obrigatório quando `useSubscription` é `true`. */
59
+ socket?: string;
60
+ /** Lista de delays (ms) entre tentativas, quando `useRetries` é `true`. */
61
+ retries?: number[];
62
+ /** Timeout (ms) por requisição. */
63
+ timeout?: number;
64
+ /** Habilita subscriptions GraphQL via WebSocket. */
65
+ useSubscription?: boolean;
66
+ /** Habilita injeção do header `Authorization: Bearer <token>` em cada request. */
67
+ useAuthorization?: boolean;
68
+ /** Habilita cache de respostas (via `QueryResponseCache` do Relay). */
69
+ useCache?: boolean;
70
+ /** TTL (ms) das entradas do cache. */
71
+ cacheTime?: number;
72
+ /** Tamanho máximo do cache (número de queries). */
73
+ cacheSize?: number;
74
+ /** Nome da chave em storage usada para guardar o token de sessão. */
75
+ sessionStorageProp?: string;
76
+ /** Nome da chave em storage usada para guardar o refresh token. */
77
+ refreshStorageProp?: string;
78
+ /** Rota usada para redirecionar o usuário em caso de sessão expirada. */
79
+ loginRoute?: string;
80
+ /** Liga logs de debug (útil em desenvolvimento). */
81
+ useDebug?: boolean;
82
+ /** Habilita o mecanismo de retries em caso de erro/timeout. */
83
+ useRetries?: boolean;
84
+ /** Redireciona automaticamente para `loginRoute` em caso de erro de auth. */
85
+ redirectOnError?: boolean;
86
+ /** Lista de status HTTP que disparam retry (quando `useRetries` é `true`). */
87
+ retryWhen?: number[];
88
+ /** Status HTTP considerados como erros de autenticação (default: 401, 403). */
89
+ authenticationErrors?: number[];
90
+ /** Estratégia de storage no browser. */
91
+ storageType: 'cookie' | 'localStorage';
92
+ /** Identificador opcional do parceiro, enviado no header `X-Partner`. */
93
+ partner?: string;
94
+ /**
95
+ * Função de fetch montada para uso pelo `Network` do Relay. Interno.
96
+ */
97
+ private [kFeetchHandler]?;
98
+ /**
99
+ * Função de subscription montada para uso pelo `Network` do Relay. Interno.
100
+ */
101
+ private [kSubscriptionHandler]?;
102
+ /**
103
+ * Handler de storage. Expõe métodos `getTokens`, `setTokens`, `clear`,
104
+ * `hasChangedSession`. Use diretamente quando precisar manipular tokens
105
+ * fora do fluxo padrão (ex: tela de login).
106
+ */
107
+ StorageHandler: any;
108
+ /**
109
+ * Instância de `Environment` do `relay-runtime` pronta para uso com
110
+ * `RelayEnvironmentProvider` (`react-relay`).
111
+ */
112
+ Environment: Environment;
113
+ /**
114
+ * @param config - Configuração da instância. Veja {@link RelayArgsInterface}.
115
+ * Apenas `url` é obrigatório; demais opções têm defaults sensatos.
116
+ *
117
+ * @throws {Error} Se `url` não for informado.
118
+ * @throws {Error} Se `useAuthorization` for `true` e `authUrl` não for informado.
119
+ * @throws {Error} Se `useSubscription` for `true` e `socket` não for informado.
120
+ */
121
+ constructor(config: RelayArgsInterface);
122
+ /**
123
+ * Monta o `Environment` Relay a partir da fetch function e subscription
124
+ * handler configurados. Chamado uma única vez pelo construtor.
125
+ */
126
+ private [kMountEnvironment];
127
+ }
128
+ export {};
@@ -0,0 +1,78 @@
1
+ /**
2
+ * @fileoverview Helpers internos usados por `setupRelayEnvironment` para
3
+ * montar a Network do Relay: detecção do tipo de operação, construção do
4
+ * body/headers da requisição, parse de respostas (incluindo
5
+ * `multipart/mixed` para `@defer`/`@stream`), redirecionamento em erro
6
+ * de auth e constantes de tempo.
7
+ *
8
+ * Não exposto pelo barrel raiz — faz parte da implementação interna de
9
+ * `CreateRelayEnvironment`.
10
+ */
11
+ import { CacheConfig, RequestParameters, UploadableMap, Variables } from 'relay-runtime';
12
+ import { RelayArgsInterface, Sink } from '../relayArgsInterface';
13
+ /** Verdadeiro se a operação Relay é uma mutation. */
14
+ export declare const isMutation: (request: RequestParameters) => boolean;
15
+ /** Verdadeiro se a operação Relay é uma query. */
16
+ export declare const isQuery: (request: RequestParameters) => boolean;
17
+ /** Verdadeiro quando o consumidor pediu `force: true` no `CacheConfig` (bypass do cache). */
18
+ export declare const forceFetch: (cacheConfig: CacheConfig) => boolean;
19
+ /**
20
+ * Retorna o delimitador (`?` ou `&`) apropriado para anexar query params
21
+ * à URL atual do browser. Retorna string vazia em ambientes sem DOM (SSR).
22
+ */
23
+ export declare const getParamsDelimiter: () => string;
24
+ /**
25
+ * Retorna o `pathname` atual do browser, ou string vazia em ambientes
26
+ * sem DOM (SSR).
27
+ */
28
+ export declare const getBrowserLocation: () => string;
29
+ /**
30
+ * Redireciona o usuário para `redirectTo`, preservando a URL atual em
31
+ * `?redirect=` (para o fluxo de login redirecionar de volta).
32
+ *
33
+ * Não adiciona o parâmetro `redirect` quando a URL atual já é igual ao
34
+ * destino ou quando o destino é a própria `loginRoute`.
35
+ *
36
+ * @param url - URL atual (origem do redirect).
37
+ * @param redirectTo - Destino do redirect.
38
+ * @param config - Configuração do environment (usada para checar `loginRoute`).
39
+ */
40
+ export declare const redirectUser: (url: string, redirectTo: string, config?: RelayArgsInterface) => void;
41
+ /**
42
+ * Constrói o body da requisição GraphQL — multipart se há uploadables,
43
+ * JSON caso contrário.
44
+ *
45
+ * @param request - Parâmetros da requisição Relay.
46
+ * @param variables - Variáveis da operação.
47
+ * @param uploadables - Arquivos para upload (opcional).
48
+ */
49
+ export declare function getRequestBody(request: RequestParameters, variables: Variables, uploadables?: UploadableMap): string | FormData;
50
+ /**
51
+ * Monta os headers HTTP da requisição. Para uploads, usa `Accept: *\/*`
52
+ * e deixa o browser definir o `Content-Type` com boundary. Para JSON,
53
+ * usa `application/json` em ambos.
54
+ *
55
+ * Quando `useAuthorization` está ligado e existe um `sessionToken` em
56
+ * storage, anexa `Authorization: Bearer <token>` — exceto quando o
57
+ * usuário está na própria rota de login (para evitar loops).
58
+ *
59
+ * Se um `partner` está configurado, adiciona o header `X-Partner`.
60
+ */
61
+ export declare const getHeaders: (args: any, uploadables?: UploadableMap) => HeadersInit;
62
+ /**
63
+ * Lê o `Response` do fetch e empurra os dados para o `Sink` do Relay.
64
+ *
65
+ * Suporta três modos:
66
+ * - `multipart/mixed` (usado por `@defer` e `@stream`): lê o stream
67
+ * chunk a chunk e usa `PatchResolver` para repassar cada patch.
68
+ * - `application/json`: parseia o JSON inteiro e chama `callback`.
69
+ * - Outros: lê como texto e empurra como array de strings.
70
+ *
71
+ * Copiado da biblioteca `fetch-multipart-graphql` com adaptações para o
72
+ * `Sink` Relay.
73
+ */
74
+ export declare const handleData: (response: Response, sink: Sink, callback: (data: any) => void) => void;
75
+ /** Constante de tempo: 1 segundo em milissegundos. */
76
+ export declare const ONE_SECOND = 1000;
77
+ /** Constante de tempo: 1 minuto em milissegundos. */
78
+ export declare const ONE_MINUTE: number;
@@ -0,0 +1,59 @@
1
+ import { RelayArgsInterface } from '../relayArgsInterface';
2
+ /** Par de tokens armazenado. */
3
+ type TokenTypes = {
4
+ sessionToken?: string;
5
+ refreshToken?: string;
6
+ };
7
+ declare const kStorageHandler: unique symbol;
8
+ declare const kPropSession: unique symbol;
9
+ declare const kPropRefresh: unique symbol;
10
+ /**
11
+ * Handler de storage usado por `CreateRelayEnvironment`. Encapsula a
12
+ * estratégia escolhida (`storageType`) e padroniza a manipulação de
13
+ * `sessionToken` e `refreshToken` por trás de uma API estável.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const { StorageHandler } = new CreateRelayEnvironment({ ... });
18
+ * StorageHandler.setTokens({ sessionToken: 't', refreshToken: 'r' });
19
+ * const { sessionToken } = StorageHandler.getTokens();
20
+ * StorageHandler.clear();
21
+ * ```
22
+ */
23
+ export default class StorageClass {
24
+ private [kStorageHandler];
25
+ private [kPropSession];
26
+ private [kPropRefresh];
27
+ /**
28
+ * @param strategy - Instância de `CreateRelayEnvironment` (que
29
+ * implementa `RelayArgsInterface`). Os campos lidos são
30
+ * `storageType`, `sessionStorageProp`, `refreshStorageProp`.
31
+ */
32
+ constructor(strategy: RelayArgsInterface);
33
+ /**
34
+ * Lê os tokens atualmente armazenados.
35
+ *
36
+ * @returns Objeto com `sessionToken` e `refreshToken` (podem ser
37
+ * `undefined` quando ausentes).
38
+ */
39
+ getTokens(): TokenTypes;
40
+ /**
41
+ * Grava ambos os tokens em storage. Use após receber resposta de
42
+ * login/refresh.
43
+ */
44
+ setTokens(tokens: TokenTypes): void;
45
+ /**
46
+ * Remove ambos os tokens do storage. Usado em logout ou ao detectar
47
+ * sessão expirada.
48
+ */
49
+ clear(): void;
50
+ /**
51
+ * Compara um novo `sessionToken` com o atualmente armazenado.
52
+ *
53
+ * @param tokens - Objeto contendo o novo `sessionToken` (vindo, por
54
+ * exemplo, de um header de resposta).
55
+ * @returns `true` se o token mudou.
56
+ */
57
+ hasChangedSession({ sessionToken: newSessionToken }: TokenTypes): boolean;
58
+ }
59
+ export {};
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @fileoverview Fábrica do subscription handler GraphQL via WebSocket,
3
+ * usada por `setupRelayEnvironment` quando `useSubscription` está
4
+ * habilitado.
5
+ *
6
+ * Baseado em `graphql-ws` (protocolo `graphql-transport-ws`).
7
+ *
8
+ * Não exposto pelo barrel raiz — implementação interna de
9
+ * `CreateRelayEnvironment`.
10
+ */
11
+ /**
12
+ * Monta a subscription function para o `Network.create` do Relay.
13
+ *
14
+ * Retorna uma função (a ser chamada uma vez, no setup do Environment)
15
+ * que por sua vez retorna a subscribe function que o Relay vai invocar
16
+ * para cada subscription operation. A adaptação de callbacks
17
+ * `next/error/complete` do `graphql-ws` para o `Sink` do Observable
18
+ * Relay é feita aqui.
19
+ *
20
+ * @param settings - Objeto com a URL do endpoint WebSocket (`socket`).
21
+ * Quando `useSubscription` é `true` mas `socket` não foi configurado,
22
+ * o construtor de `CreateRelayEnvironment` lança erro antes desta
23
+ * função ser chamada.
24
+ * @returns Função que, quando invocada, retorna a subscribe function
25
+ * compatível com `Network.create(fetchFn, subscribeFn)`.
26
+ */
27
+ export declare function setupSubscription(settings: {
28
+ socket?: string;
29
+ }): () => any;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Barrel público do módulo `useEnvironment`.
3
+ *
4
+ * Reexporta o hook `useEnvironment` e o componente `EnvironmentProvider`,
5
+ * para que consumidores possam importar via
6
+ * `@apollion-dsi/relay/useEnvironment` (granular) ou via
7
+ * `@apollion-dsi/relay` (barrel raiz).
8
+ */
9
+ export * from './useEnvironment';
@@ -0,0 +1,73 @@
1
+ import React from 'react';
2
+ import { Environment } from 'relay-runtime';
3
+ import { MockEnvironment } from 'relay-test-utils';
4
+ /**
5
+ * Estado interno do Context: contém apenas o `Environment` Relay
6
+ * (real ou mockado em testes).
7
+ */
8
+ type StateType = {
9
+ environment: Environment | MockEnvironment;
10
+ };
11
+ /**
12
+ * Props do `EnvironmentProvider`.
13
+ */
14
+ type EnvironmentProviderInterface = {
15
+ children: React.ReactNode;
16
+ } & StateType;
17
+ /**
18
+ * Provider que injeta o `Environment` Relay na árvore React via Context.
19
+ *
20
+ * Em produção, passe a propriedade `Environment` exposta por
21
+ * `CreateRelayEnvironment`. Em testes, passe um `MockEnvironment` de
22
+ * `relay-test-utils` para facilitar a substituição do backend.
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * import { CreateRelayEnvironment, EnvironmentProvider } from '@apollion-dsi/relay';
27
+ *
28
+ * const { Environment } = new CreateRelayEnvironment({ url: '...' });
29
+ *
30
+ * function App() {
31
+ * return (
32
+ * <EnvironmentProvider environment={Environment}>
33
+ * <Routes />
34
+ * </EnvironmentProvider>
35
+ * );
36
+ * }
37
+ * ```
38
+ *
39
+ * @example Em testes
40
+ * ```tsx
41
+ * import { createMockEnvironment } from 'relay-test-utils';
42
+ *
43
+ * const mock = createMockEnvironment();
44
+ * render(
45
+ * <EnvironmentProvider environment={mock}>
46
+ * <SubjectUnderTest />
47
+ * </EnvironmentProvider>
48
+ * );
49
+ * ```
50
+ */
51
+ declare function EnvironmentProvider({ children, environment }: EnvironmentProviderInterface): React.JSX.Element;
52
+ /**
53
+ * Hook para acessar o `Environment` Relay corrente.
54
+ *
55
+ * Deve ser usado dentro de um `EnvironmentProvider`. Lança erro
56
+ * descritivo caso seja chamado fora dele ou se nenhum environment
57
+ * tenha sido fornecido — falhas silenciosas aqui geralmente viram
58
+ * "loading infinito" no Relay, então preferimos falhar cedo.
59
+ *
60
+ * @returns Objeto com a propriedade `environment`.
61
+ * @throws {Error} Quando usado fora de `EnvironmentProvider` ou quando
62
+ * o environment fornecido é `null`/`undefined`.
63
+ *
64
+ * @example
65
+ * ```tsx
66
+ * function MyComponent() {
67
+ * const { environment } = useEnvironment();
68
+ * // ...
69
+ * }
70
+ * ```
71
+ */
72
+ declare function useEnvironment(): StateType;
73
+ export { EnvironmentProvider, useEnvironment };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apollion-dsi/relay",
3
- "version": "0.25.10",
3
+ "version": "0.26.0",
4
4
  "description": "Frontend services regarding Relay Environment",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib/index.esm.js",
@@ -22,8 +22,7 @@
22
22
  "format": "yarn prettier --write",
23
23
  "validate": "./scripts/validate.sh",
24
24
  "validate:tests": "jest --coverage",
25
- "build": "node ./esbuild",
26
- "postbuild": "tsc --emitDeclarationOnly",
25
+ "build": "node -e \"require('fs').rmSync('./lib',{recursive:true,force:true})\" && node ./esbuild && tsc --emitDeclarationOnly",
27
26
  "test": "yarn validate",
28
27
  "code:check": "yarn lint && yarn coverage"
29
28
  },
@@ -32,16 +31,16 @@
32
31
  },
33
32
  "dependencies": {
34
33
  "fetch-multipart-graphql": "2.3.1",
35
- "graphql": "15.10.1",
34
+ "graphql": "16.14.2",
36
35
  "graphql-ws": "6.0.8",
37
36
  "js-cookie": "3.0.7",
38
37
  "nanoid": "5.1.11",
39
- "react-relay": "20.1.1",
40
- "relay-runtime": "20.1.1",
41
- "relay-test-utils": "20.1.1"
38
+ "react-relay": "21.0.1",
39
+ "relay-runtime": "21.0.1",
40
+ "relay-test-utils": "21.0.1"
42
41
  },
43
42
  "devDependencies": {
44
- "@apollion-dsi/eslint-config": "0.7.0",
43
+ "@apollion-dsi/eslint-config": "0.8.0",
45
44
  "@babel/core": "7.29.0",
46
45
  "@babel/plugin-transform-class-properties": "7.27.1",
47
46
  "@babel/plugin-transform-runtime": "7.29.0",
@@ -59,12 +58,12 @@
59
58
  "@types/relay-runtime": "20.1.1",
60
59
  "@types/relay-test-utils": "19.0.0",
61
60
  "babel-jest": "30.4.1",
62
- "babel-plugin-relay": "20.1.1",
61
+ "babel-plugin-relay": "21.0.1",
63
62
  "esbuild": "0.28.0",
64
63
  "eslint": "9.29.0",
65
64
  "express": "4.22.2",
66
- "express-graphql": "0.12.0",
67
- "graphql-relay": "0.9.0",
65
+ "graphql-http": "1.22.4",
66
+ "graphql-relay": "0.10.2",
68
67
  "isomorphic-fetch": "3.0.0",
69
68
  "jest": "30.4.2",
70
69
  "jest-environment-jsdom": "30.4.1",
@@ -72,7 +71,7 @@
72
71
  "prettier": "3.8.3",
73
72
  "react": "19.2.6",
74
73
  "react-dom": "19.2.6",
75
- "relay-compiler": "20.1.1",
74
+ "relay-compiler": "21.0.1",
76
75
  "typescript": "6.0.3"
77
76
  }
78
77
  }