@meeovi/api 1.0.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.
- package/index.ts +24 -0
- package/package.json +20 -0
- package/src/client/apollo.ts +20 -0
- package/src/client/rest.ts +5 -0
- package/src/fetcher/fetcherRegistry.ts +18 -0
- package/src/fetcher/graphql.ts +19 -0
- package/src/fetcher/registry.ts +15 -0
- package/src/fetcher/rest.ts +19 -0
- package/src/fetcher/types.ts +15 -0
- package/src/graphql/queries/products.ts +24 -0
- package/src/loader/loadGraphQLNamespaced.ts +49 -0
- package/src/loader/loadGraphql.ts +34 -0
- package/src/loader/loadRest.ts +13 -0
- package/src/loader/nsRegistry.ts +54 -0
- package/src/loader/registry.ts +27 -0
- package/src/plugins/index.ts +13 -0
- package/src/plugins/types.ts +5 -0
- package/src/providers/commerce.ts +21 -0
- package/src/providers/content.ts +0 -0
- package/src/providers/search.ts +0 -0
- package/src/rest/registry.ts +11 -0
- package/src/rest/types.ts +5 -0
- package/src/utils/errors.ts +6 -0
- package/src/utils/fetcher.ts +17 -0
package/index.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { loadGraphQL } from './src/loader/loadGraphql'
|
|
2
|
+
|
|
3
|
+
loadGraphQL()
|
|
4
|
+
|
|
5
|
+
export * from './src/utils/fetcher'
|
|
6
|
+
export * from './src/loader/registry'
|
|
7
|
+
|
|
8
|
+
// Apollo client
|
|
9
|
+
export * from './src/client/apollo'
|
|
10
|
+
|
|
11
|
+
// REST helper
|
|
12
|
+
export * from './src/client/rest'
|
|
13
|
+
|
|
14
|
+
// GraphQL queries/mutations
|
|
15
|
+
export * from './src/graphql/queries/products'
|
|
16
|
+
export * from './src/graphql/mutations/cart'
|
|
17
|
+
|
|
18
|
+
// Providers
|
|
19
|
+
export * from './src/providers/commerce'
|
|
20
|
+
export * from './src/providers/content'
|
|
21
|
+
export * from './src/providers/search'
|
|
22
|
+
|
|
23
|
+
// Utils
|
|
24
|
+
export * from './src/utils/errors'
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@meeovi/api",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"exports": { ".": "./index.ts" },
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [],
|
|
11
|
+
"author": "",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"type": "commonjs",
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@apollo/client": "^4.1.0",
|
|
16
|
+
"cross-fetch": "^4.1.0",
|
|
17
|
+
"graphql": "^16.12.0",
|
|
18
|
+
"graphql-tag": "^2.12.6"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client'
|
|
2
|
+
import fetch from 'cross-fetch'
|
|
3
|
+
|
|
4
|
+
let client: ApolloClient | null = null
|
|
5
|
+
|
|
6
|
+
export function createApolloClient(uri: string) {
|
|
7
|
+
client = new ApolloClient({
|
|
8
|
+
link: new HttpLink({ uri, fetch }),
|
|
9
|
+
cache: new InMemoryCache()
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
return client
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getApolloClient() {
|
|
16
|
+
if (!client) {
|
|
17
|
+
throw new Error('Apollo client not initialized. Call createApolloClient() first.')
|
|
18
|
+
}
|
|
19
|
+
return client
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Fetcher } from './types'
|
|
2
|
+
|
|
3
|
+
const fetchers: Record<string, Fetcher> = {}
|
|
4
|
+
let activeFetcherName: string | null = null
|
|
5
|
+
|
|
6
|
+
export function registerFetcher(name: string, fetcher: Fetcher) {
|
|
7
|
+
fetchers[name] = fetcher
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function setActiveFetcher(name: string) {
|
|
11
|
+
if (!fetchers[name]) throw new Error(`Fetcher "${name}" not registered`)
|
|
12
|
+
activeFetcherName = name
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getFetcher(): Fetcher {
|
|
16
|
+
if (!activeFetcherName) throw new Error('No active fetcher set')
|
|
17
|
+
return fetchers[activeFetcherName]
|
|
18
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
|
|
2
|
+
import { getApolloClient } from '../client/apollo'
|
|
3
|
+
import { Fetcher, FetcherRequest, FetcherResponse } from './types'
|
|
4
|
+
|
|
5
|
+
export const GraphQLFetcher: Fetcher = {
|
|
6
|
+
async execute<T = any>(req: FetcherRequest): Promise<FetcherResponse<T>> {
|
|
7
|
+
try {
|
|
8
|
+
const client = getApolloClient()
|
|
9
|
+
const { data } = await client.query({
|
|
10
|
+
query: req.operation,
|
|
11
|
+
variables: req.variables || {}
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
return { data }
|
|
15
|
+
} catch (error) {
|
|
16
|
+
return { error }
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
import { Fetcher } from './types'
|
|
3
|
+
|
|
4
|
+
let activeFetcher: Fetcher | null = null
|
|
5
|
+
|
|
6
|
+
export function setFetcher(fetcher: Fetcher) {
|
|
7
|
+
activeFetcher = fetcher
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function getFetcher(): Fetcher {
|
|
11
|
+
if (!activeFetcher) {
|
|
12
|
+
throw new Error('No fetcher has been set. Call setFetcher() first.')
|
|
13
|
+
}
|
|
14
|
+
return activeFetcher
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
|
|
2
|
+
import { Fetcher, FetcherRequest, FetcherResponse } from './types'
|
|
3
|
+
|
|
4
|
+
export const RestFetcher: Fetcher = {
|
|
5
|
+
async execute<T = any>(req: FetcherRequest): Promise<FetcherResponse<T>> {
|
|
6
|
+
try {
|
|
7
|
+
const res = await fetch(req.operation, {
|
|
8
|
+
method: req.options?.method || 'GET',
|
|
9
|
+
headers: req.options?.headers,
|
|
10
|
+
body: req.options?.body ? JSON.stringify(req.options.body) : undefined
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
const data = await res.json()
|
|
14
|
+
return { data }
|
|
15
|
+
} catch (error) {
|
|
16
|
+
return { error }
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
export interface FetcherRequest {
|
|
3
|
+
operation: string
|
|
4
|
+
variables?: Record<string, any>
|
|
5
|
+
options?: Record<string, any>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface FetcherResponse<T = any> {
|
|
9
|
+
data?: T
|
|
10
|
+
error?: any
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface Fetcher {
|
|
14
|
+
execute<T = any>(req: FetcherRequest): Promise<FetcherResponse<T>>
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { gql } from 'graphql-tag'
|
|
2
|
+
|
|
3
|
+
export const GET_PRODUCT = gql`
|
|
4
|
+
query GetProduct($id: ID!) {
|
|
5
|
+
product(id: $id) {
|
|
6
|
+
id
|
|
7
|
+
title
|
|
8
|
+
description
|
|
9
|
+
price
|
|
10
|
+
images
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
`
|
|
14
|
+
|
|
15
|
+
export const LIST_PRODUCTS = gql`
|
|
16
|
+
query ListProducts {
|
|
17
|
+
products {
|
|
18
|
+
id
|
|
19
|
+
title
|
|
20
|
+
price
|
|
21
|
+
images
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
`
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
registerNamespacedQuery,
|
|
3
|
+
registerNamespacedMutation,
|
|
4
|
+
registerNamespacedSubscription
|
|
5
|
+
} from './nsRegistry'
|
|
6
|
+
|
|
7
|
+
export function loadGraphQLNamespaced() {
|
|
8
|
+
// queries
|
|
9
|
+
const queryModules = (import.meta as any).glob('../graphql/queries/**/*.ts', { eager: true })
|
|
10
|
+
for (const path in queryModules) {
|
|
11
|
+
const mod = queryModules[path] as Record<string, any>
|
|
12
|
+
const namespace = path
|
|
13
|
+
.replace('../graphql/queries/', '')
|
|
14
|
+
.replace('.ts', '')
|
|
15
|
+
.replace(/\//g, '.')
|
|
16
|
+
|
|
17
|
+
for (const key in mod) {
|
|
18
|
+
registerNamespacedQuery(namespace, key, mod[key])
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// mutations
|
|
23
|
+
const mutationModules = (import.meta as any).glob('../graphql/mutations/**/*.ts', { eager: true })
|
|
24
|
+
for (const path in mutationModules) {
|
|
25
|
+
const mod = mutationModules[path] as Record<string, any>
|
|
26
|
+
const namespace = path
|
|
27
|
+
.replace('../graphql/mutations/', '')
|
|
28
|
+
.replace('.ts', '')
|
|
29
|
+
.replace(/\//g, '.')
|
|
30
|
+
|
|
31
|
+
for (const key in mod) {
|
|
32
|
+
registerNamespacedMutation(namespace, key, mod[key])
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// subscriptions
|
|
37
|
+
const subscriptionModules = (import.meta as any).glob('../graphql/subscriptions/**/*.ts', { eager: true })
|
|
38
|
+
for (const path in subscriptionModules) {
|
|
39
|
+
const mod = subscriptionModules[path] as Record<string, any>
|
|
40
|
+
const namespace = path
|
|
41
|
+
.replace('../graphql/subscriptions/', '')
|
|
42
|
+
.replace('.ts', '')
|
|
43
|
+
.replace(/\//g, '.')
|
|
44
|
+
|
|
45
|
+
for (const key in mod) {
|
|
46
|
+
registerNamespacedSubscription(namespace, key, mod[key])
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {
|
|
2
|
+
registerQuery,
|
|
3
|
+
registerMutation,
|
|
4
|
+
registerSubscription
|
|
5
|
+
} from './registry'
|
|
6
|
+
|
|
7
|
+
export function loadGraphQL() {
|
|
8
|
+
// Load queries
|
|
9
|
+
const queryModules = (import.meta as any).glob('../graphql/queries/*.ts', { eager: true })
|
|
10
|
+
for (const path in queryModules) {
|
|
11
|
+
const mod = queryModules[path]
|
|
12
|
+
for (const key in mod) {
|
|
13
|
+
registerQuery(key, mod[key])
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Load mutations
|
|
18
|
+
const mutationModules = (import.meta as any).glob('../graphql/mutations/*.ts', { eager: true })
|
|
19
|
+
for (const path in mutationModules) {
|
|
20
|
+
const mod = mutationModules[path]
|
|
21
|
+
for (const key in mod) {
|
|
22
|
+
registerMutation(key, mod[key])
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Load subscriptions
|
|
27
|
+
const subscriptionModules = (import.meta as any).glob('../graphql/subscriptions/*.ts', { eager: true })
|
|
28
|
+
for (const path in subscriptionModules) {
|
|
29
|
+
const mod = subscriptionModules[path]
|
|
30
|
+
for (const key in mod) {
|
|
31
|
+
registerSubscription(key, mod[key])
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { registerRestEndpoint } from '../rest/registry'
|
|
2
|
+
import type { RestEndpoint } from '../rest/types'
|
|
3
|
+
|
|
4
|
+
export function loadRest() {
|
|
5
|
+
const modules = (import.meta as any).glob('../rest/*.ts', { eager: true })
|
|
6
|
+
|
|
7
|
+
for (const path in modules) {
|
|
8
|
+
const mod = modules[path] as Record<string, RestEndpoint>
|
|
9
|
+
for (const key in mod) {
|
|
10
|
+
registerRestEndpoint(key, mod[key])
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
type OperationMap = Record<string, any>
|
|
2
|
+
|
|
3
|
+
const queries: OperationMap = {}
|
|
4
|
+
const mutations: OperationMap = {}
|
|
5
|
+
const subscriptions: OperationMap = {}
|
|
6
|
+
|
|
7
|
+
function setNested(map: OperationMap, ns: string, value: any) {
|
|
8
|
+
const parts = ns.split('.')
|
|
9
|
+
let current = map
|
|
10
|
+
|
|
11
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
12
|
+
const part = parts[i]
|
|
13
|
+
current[part] = current[part] || {}
|
|
14
|
+
current = current[part]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
current[parts[parts.length - 1]] = value
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getNested(map: OperationMap, ns: string) {
|
|
21
|
+
const parts = ns.split('.')
|
|
22
|
+
let current: any = map
|
|
23
|
+
|
|
24
|
+
for (const part of parts) {
|
|
25
|
+
current = current?.[part]
|
|
26
|
+
if (!current) return undefined
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return current
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function registerNamespacedQuery(namespace: string, name: string, op: any) {
|
|
33
|
+
setNested(queries, `${namespace}.${name}`, op)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function registerNamespacedMutation(namespace: string, name: string, op: any) {
|
|
37
|
+
setNested(mutations, `${namespace}.${name}`, op)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function registerNamespacedSubscription(namespace: string, name: string, op: any) {
|
|
41
|
+
setNested(subscriptions, `${namespace}.${name}`, op)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getQuery(ns: string) {
|
|
45
|
+
return getNested(queries, ns)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function getMutation(ns: string) {
|
|
49
|
+
return getNested(mutations, ns)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function getSubscription(ns: string) {
|
|
53
|
+
return getNested(subscriptions, ns)
|
|
54
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const queries: Record<string, any> = {}
|
|
2
|
+
const mutations: Record<string, any> = {}
|
|
3
|
+
const subscriptions: Record<string, any> = {}
|
|
4
|
+
|
|
5
|
+
export function registerQuery(name: string, operation: any) {
|
|
6
|
+
queries[name] = operation
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function registerMutation(name: string, operation: any) {
|
|
10
|
+
mutations[name] = operation
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function registerSubscription(name: string, operation: any) {
|
|
14
|
+
subscriptions[name] = operation
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getQuery(name: string) {
|
|
18
|
+
return queries[name]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getMutation(name: string) {
|
|
22
|
+
return mutations[name]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getSubscription(name: string) {
|
|
26
|
+
return subscriptions[name]
|
|
27
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ApiPluginContext } from './types'
|
|
2
|
+
import { registerFetcher, setActiveFetcher } from '../fetcher/fetcherRegistry'
|
|
3
|
+
|
|
4
|
+
export type ApiPlugin = (ctx: ApiPluginContext) => void
|
|
5
|
+
|
|
6
|
+
export function defineApiPlugin(plugin: ApiPlugin) {
|
|
7
|
+
const ctx: ApiPluginContext = {
|
|
8
|
+
registerFetcher,
|
|
9
|
+
setActiveFetcher
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
plugin(ctx)
|
|
13
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { getApolloClient } from '../client/apollo'
|
|
2
|
+
import { GET_PRODUCT, LIST_PRODUCTS } from '../graphql/queries/products'
|
|
3
|
+
|
|
4
|
+
export const MeeoviApiCommerceProvider = {
|
|
5
|
+
async getProduct(id: string) {
|
|
6
|
+
const client = getApolloClient()
|
|
7
|
+
const { data } = await client.query({
|
|
8
|
+
query: GET_PRODUCT,
|
|
9
|
+
variables: { id }
|
|
10
|
+
})
|
|
11
|
+
return data.product
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
async listProducts() {
|
|
15
|
+
const client = getApolloClient()
|
|
16
|
+
const { data } = await client.query({
|
|
17
|
+
query: LIST_PRODUCTS
|
|
18
|
+
})
|
|
19
|
+
return data.products
|
|
20
|
+
}
|
|
21
|
+
}
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { RestEndpoint } from "./types"
|
|
2
|
+
|
|
3
|
+
const restEndpoints: Record<string, RestEndpoint> = {}
|
|
4
|
+
|
|
5
|
+
export function registerRestEndpoint(name: string, endpoint: RestEndpoint) {
|
|
6
|
+
restEndpoints[name] = endpoint
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getRestEndpoint(name: string): RestEndpoint | undefined {
|
|
10
|
+
return restEndpoints[name]
|
|
11
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
import { getFetcher } from '../fetcher/registry'
|
|
3
|
+
import { FetcherRequest, FetcherResponse } from '../fetcher/types'
|
|
4
|
+
|
|
5
|
+
export async function fetcher<T = any>(
|
|
6
|
+
operation: string,
|
|
7
|
+
variables?: Record<string, any>,
|
|
8
|
+
options?: Record<string, any>
|
|
9
|
+
): Promise<FetcherResponse<T>> {
|
|
10
|
+
const activeFetcher = getFetcher()
|
|
11
|
+
|
|
12
|
+
return activeFetcher.execute<T>({
|
|
13
|
+
operation,
|
|
14
|
+
variables,
|
|
15
|
+
options
|
|
16
|
+
})
|
|
17
|
+
}
|