Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions .babelrc.js

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [22.x]
node-version: [22.x, 24.x, 25.x]
steps:
- uses: actions/checkout@v6
- name: Use Node.js ${{ matrix.node-version }}
Expand Down
9 changes: 0 additions & 9 deletions __mocks__/node-fetch.ts

This file was deleted.

42 changes: 30 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
{
"name": "@4c/graphql-node-resource",
"version": "5.1.0",
"type": "module",
"packageManager": "[email protected]",
"main": "lib/index.js",
"exports": {
".": "./lib/index.js",
"./*": "./lib/*.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/4Catalyzer/graphql-node-resource.git"
Expand All @@ -12,11 +17,11 @@
},
"license": "MIT",
"scripts": {
"build": "4c build src",
"build": "tsc",
"prepublishOnly": "yarn run build",
"tdd": "jest --watch",
"test": "yarn lint && yarn typecheck && jest",
"testonly": "jest",
"tdd": "yarn testonly --watch",
"test": "yarn lint && yarn typecheck && yarn testonly",
"testonly": "NODE_OPTIONS='--experimental-vm-modules' jest",
"lint": "4c lint src './*'",
"format": "4c format src './*'",
"release": "4c release",
Expand All @@ -33,8 +38,24 @@
"*": "yarn 4c lint --fix"
},
"jest": {
"preset": "@4c/jest-preset",
"testEnvironment": "node"
"testEnvironment": "node",
"extensionsToTreatAsEsm": [
".ts"
],
"transform": {
"^.+\\.ts$": [
"ts-jest",
{
"useESM": true
}
]
},
"moduleNameMapper": {
"^(\\.{1,2}/.*)\\.js$": "$1"
},
"setupFilesAfterEnv": [
"<rootDir>/test/setup.ts"
]
},
"release": {
"publishDir": "lib",
Expand All @@ -43,13 +64,11 @@
"dependencies": {
"@types/lodash": "^4.17.21",
"@types/node": "^25.0.3",
"@types/node-fetch": "^2.6.13",
"@types/pluralize": "^0.0.33",
"dataloader": "^2.2.3",
"express": "^5.2.1",
"form-data": "^4.0.5",
"lodash": "^4.17.21",
"node-fetch": "^2.6.7",
"pluralize": "^8.0.0",
"utility-types": "^3.11.0"
},
Expand All @@ -58,12 +77,11 @@
"graphql-relay": ">=0.7.0"
},
"devDependencies": {
"@4c/babel-preset": "^10.2.1",
"@4c/cli": "^4.0.4",
"@4c/jest-preset": "^1.8.1",
"@4c/prettier-config": "^1.1.0",
"@4c/tsconfig": "^0.4.1",
"@babel/preset-typescript": "^7.28.5",
"@fetch-mock/jest": "^0.2.20",
"@jest/globals": "^30.0.1",
"@types/jest": "^30.0.0",
"@typescript-eslint/eslint-plugin": "^8.52.0",
"@typescript-eslint/parser": "^8.52.0",
Expand All @@ -73,12 +91,12 @@
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jest": "^29.12.1",
"fetch-mock": "^9.11.0",
"graphql": "^16.12.0",
"graphql-relay": "^0.10.2",
"hookem": "^3.0.4",
"jest": "^30.2.0",
"lint-staged": "^16.2.7",
"ts-jest": "^29.2.5",
"typescript": "^5.9.3"
},
"engines": {
Expand Down
29 changes: 11 additions & 18 deletions src/api/HttpApi.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
import querystring from 'querystring';

import DataLoader from 'dataloader';
import {
Connection,
ConnectionArguments,
connectionFromArray,
forwardConnectionArgs,
} from 'graphql-relay';
import chunk from 'lodash/chunk';
import flatten from 'lodash/flatten';
import omit from 'lodash/omit';
import pick from 'lodash/pick';

import { HttpMethod } from './fetch';
import { Maybe, Obj } from '../utils/typing';
import urlJoin from '../utils/urlJoin';
import { connectionFromArray, forwardConnectionArgs } from 'graphql-relay';
import type { Connection, ConnectionArguments } from 'graphql-relay';
import _ from 'lodash';

import type { HttpMethod } from './fetch.js';
import type { Maybe, Obj } from '../utils/typing.js';
import urlJoin from '../utils/urlJoin.js';

const PAGINATION_ARG_KEYS = Object.keys(forwardConnectionArgs);

Expand Down Expand Up @@ -196,8 +189,8 @@ export default abstract class HttpApi {
path: string,
args: Args,
): Promise<Maybe<PaginationResult<T>>> {
const apiArgs = omit(args, PAGINATION_ARG_KEYS);
const paginationArgs = pick(args, PAGINATION_ARG_KEYS);
const apiArgs = _.omit(args, PAGINATION_ARG_KEYS);
const paginationArgs = _.pick(args, PAGINATION_ARG_KEYS);

// XXX Need to cast the result of the get to a list
const items = await this.get<T[]>(this.makePath(path, apiArgs));
Expand Down Expand Up @@ -286,12 +279,12 @@ export default abstract class HttpApi {
return new DataLoader<any, any>(async (keys) => {
// No need to cache the GET; the DataLoader will cache it.
const chunkedItems = await Promise.all(
chunk<string>(keys, this.numKeysPerChunk || 1).map((chunkKeys) =>
_.chunk<string>(keys, this.numKeysPerChunk || 1).map((chunkKeys) =>
this.request<T[]>('GET', getPath(chunkKeys)),
),
);

const items = flatten<T | null | undefined>(chunkedItems).filter(
const items = _.flatten<T | null | undefined>(chunkedItems).filter(
<TItem>(
item: TItem,
): item is TItem extends null | undefined ? never : TItem => !!item,
Expand Down
2 changes: 0 additions & 2 deletions src/api/HttpError.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import util from 'util';

import { Response } from 'node-fetch';

export interface JsonApiError {
detail?: string;
code: string;
Expand Down
12 changes: 7 additions & 5 deletions src/api/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import FormData from 'form-data';
import _fetch, { RequestInit, Response } from 'node-fetch';

export type File = {
fieldname: string;
Expand All @@ -25,10 +24,13 @@ export type RequestOptions = RequestInit & {
files?: File[];
};

export default function fetch(reqOptions: RequestOptions): Promise<Response> {
const { url, data, headers, files, ...rest } = reqOptions;
export default function apiFetch(
reqOptions: RequestOptions,
): Promise<Response> {
const { url, data, headers, files, method, ...rest } = reqOptions;

const init: RequestInit = {
method,
headers: {
Accept: 'application/json',
...headers,
Expand All @@ -48,7 +50,7 @@ export default function fetch(reqOptions: RequestOptions): Promise<Response> {
formData.append(fieldname, buffer, originalname);
});

init.body = formData;
init.body = formData as unknown as RequestInit['body'];
} else {
init.headers = {
...init.headers,
Expand All @@ -58,5 +60,5 @@ export default function fetch(reqOptions: RequestOptions): Promise<Response> {
}
}

return _fetch(url, init);
return fetch(url, init);
}
18 changes: 8 additions & 10 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { GraphQLFieldConfig, GraphQLInterfaceType } from 'graphql';
import {
ConnectionConfig,
fromGlobalId,
nodeDefinitions,
} from 'graphql-relay';
import { GraphQLInterfaceType } from 'graphql';
import type { GraphQLFieldConfig } from 'graphql';
import { fromGlobalId, nodeDefinitions } from 'graphql-relay';
import type { ConnectionConfig } from 'graphql-relay';

import Resource from './resources/Resource';
import { Context } from './types/Context';
import Resource from './resources/Resource.js';
import type { Context } from './types/Context.js';
// eslint-disable-next-line import/no-cycle
import NodeType from './types/NodeType';
import ResourceCache from './types/ResourceCache';
import NodeType from './types/NodeType.js';
import ResourceCache from './types/ResourceCache.js';

type Config = {
nodeField: GraphQLFieldConfig<any, Context>;
Expand Down
6 changes: 3 additions & 3 deletions src/getNodeResource.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { GraphQLResolveInfo } from 'graphql';
import type { GraphQLResolveInfo } from 'graphql';

import getParentNodeType from './getParentNodeType';
import { Context } from './types/Context';
import getParentNodeType from './getParentNodeType.js';
import type { Context } from './types/Context.js';

export default function getNodeResource(
context: Context,
Expand Down
6 changes: 3 additions & 3 deletions src/getParentNodeType.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { GraphQLResolveInfo } from 'graphql';
import type { GraphQLResolveInfo } from 'graphql';

import NodeType from './types/NodeType';
import asType from './utils/asType';
import NodeType from './types/NodeType.js';
import asType from './utils/asType.js';

export default function getParentNodeType(info: GraphQLResolveInfo) {
return asType(info.parentType, NodeType);
Expand Down
38 changes: 19 additions & 19 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import HttpApi from './api/HttpApi';
import HttpError from './api/HttpError';
import fetch from './api/fetch';
import HttpResource from './resources/HttpResource';
import PaginatedHttpResource from './resources/PaginatedHttpResource';
import Resource from './resources/Resource';
import NodeType from './types/NodeType';
import createResolve from './types/createResolve';
import asType from './utils/asType';
import resolveThunk from './utils/resolveThunk';
import translateKeys from './utils/translateKeys';
import urlJoin from './utils/urlJoin';
import HttpApi from './api/HttpApi.js';
import HttpError from './api/HttpError.js';
import fetch from './api/fetch.js';
import HttpResource from './resources/HttpResource.js';
import PaginatedHttpResource from './resources/PaginatedHttpResource.js';
import Resource from './resources/Resource.js';
import NodeType from './types/NodeType.js';
import createResolve from './types/createResolve.js';
import asType from './utils/asType.js';
import resolveThunk from './utils/resolveThunk.js';
import translateKeys from './utils/translateKeys.js';
import urlJoin from './utils/urlJoin.js';

export const utils = {
asType,
Expand All @@ -29,22 +29,22 @@ export {
Resource,
};

export { RESOURCE_CACHE_KEY } from './types/Context';
export { RESOURCE_CACHE_KEY } from './types/Context.js';

export { setup } from './config';
export { setup } from './config.js';

export type { HttpMethod, RequestOptions } from './api/fetch';
export type { HttpMethod, RequestOptions } from './api/fetch.js';
export type {
Args,
Data,
PaginationResult,
HttpApiOptions,
} from './api/HttpApi';
export type { NodeTypeConfig } from './types/NodeType';
} from './api/HttpApi.js';
export type { NodeTypeConfig } from './types/NodeType.js';

export type { JsonApiError } from './api/HttpError';
export type { JsonApiError } from './api/HttpError.js';
export type {
Endpoint,
HttpContext,
HttpResourceOptions,
} from './resources/HttpResource';
} from './resources/HttpResource.js';
11 changes: 6 additions & 5 deletions src/resources/HttpResource.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import Resource from './Resource';
import HttpApi, { Args, Data } from '../api/HttpApi';
import { Context } from '../types/Context';
import { Maybe, Obj } from '../utils/typing';
import urlJoin from '../utils/urlJoin';
import Resource from './Resource.js';
import HttpApi from '../api/HttpApi.js';
import type { Args, Data } from '../api/HttpApi.js';
import type { Context } from '../types/Context.js';
import type { Maybe, Obj } from '../utils/typing.js';
import urlJoin from '../utils/urlJoin.js';

export type Endpoint = string | ((id?: string) => string);

Expand Down
5 changes: 3 additions & 2 deletions src/resources/PaginatedHttpResource.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import HttpResource from './HttpResource';
import HttpApi, { Args } from '../api/HttpApi';
import HttpResource from './HttpResource.js';
import HttpApi from '../api/HttpApi.js';
import type { Args } from '../api/HttpApi.js';

export default class PaginatedHttpResource<
TApi extends HttpApi = HttpApi,
Expand Down
2 changes: 1 addition & 1 deletion src/resources/Resource.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Maybe, Obj } from '../utils/typing';
import type { Maybe, Obj } from '../utils/typing.js';

export default abstract class Resource<TContext = any> {
protected constructor(public readonly context: TContext) {}
Expand Down
Loading