Merge 85746f0461
into 49dcd97d70
This commit is contained in:
commit
7034e0a268
1
web/.gitignore
vendored
1
web/.gitignore
vendored
|
@ -3,3 +3,4 @@
|
|||
node_modules
|
||||
out
|
||||
tsconfig.tsbuildinfo
|
||||
generated
|
||||
|
|
6
web/@types/graphql.d.ts
vendored
Normal file
6
web/@types/graphql.d.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
declare module '*.graphql' {
|
||||
import { DocumentNode } from 'graphql'
|
||||
const Schema: DocumentNode
|
||||
|
||||
export = Schema
|
||||
}
|
38
web/codegen.yml
Normal file
38
web/codegen.yml
Normal file
|
@ -0,0 +1,38 @@
|
|||
overwrite: true
|
||||
schema: lib/api/graphql/**/**.graphql
|
||||
documents: null
|
||||
|
||||
generates:
|
||||
generated/graphql_api.ts:
|
||||
config:
|
||||
useIndexSignature: true
|
||||
contextType: web/lib/api/graphql/types#contextType
|
||||
|
||||
inputMaybeValue: undefined | T
|
||||
maybeValue: undefined | T
|
||||
|
||||
strictScalars: true
|
||||
mappers:
|
||||
Comment: web/lib/api/graphql/types#CommentModel
|
||||
MarketAnswer: web/lib/api/graphql/types#AnswerModel
|
||||
Market: web/lib/api/graphql/types#MarketModel
|
||||
Bet: web/lib/api/graphql/types#BetModel
|
||||
scalars:
|
||||
MarketID: 'string'
|
||||
MarketAnswerID: 'string'
|
||||
BetID: 'string'
|
||||
CommentID: 'string'
|
||||
UserID: 'string'
|
||||
Timestamp: 'number'
|
||||
|
||||
plugins:
|
||||
- 'typescript'
|
||||
- 'typescript-resolvers'
|
||||
|
||||
generated/schema.graphql:
|
||||
plugins:
|
||||
- 'schema-ast'
|
||||
|
||||
hooks:
|
||||
afterAllFileWrite:
|
||||
- prettier --write
|
108
web/lib/api/graphql/datasources/firebaseAPI.ts
Normal file
108
web/lib/api/graphql/datasources/firebaseAPI.ts
Normal file
|
@ -0,0 +1,108 @@
|
|||
import type { Contract, BinaryContract } from 'common/contract'
|
||||
|
||||
import { getOutcomeProbability, getProbability } from 'common/calculate'
|
||||
import { listAllBets } from 'web/lib/firebase/bets'
|
||||
import {
|
||||
getContractFromId,
|
||||
getContractFromSlug,
|
||||
listAllContracts,
|
||||
} from 'web/lib/firebase/contracts'
|
||||
import { listAllComments } from 'web/lib/firebase/comments'
|
||||
import { getUser } from 'web/lib/firebase/users'
|
||||
|
||||
import { DataSource } from 'apollo-datasource'
|
||||
import { InMemoryLRUCache, KeyValueCache } from 'apollo-server-caching'
|
||||
|
||||
/* Simple wrapper around web/lib/firebase functions */
|
||||
export class FirebaseAPI extends DataSource {
|
||||
cache: KeyValueCache
|
||||
context?: any
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.cache = null as any
|
||||
}
|
||||
|
||||
initialize({
|
||||
context,
|
||||
cache,
|
||||
}: { context?: any; cache?: KeyValueCache } = {}) {
|
||||
this.context = context
|
||||
this.cache = cache || new InMemoryLRUCache()
|
||||
}
|
||||
|
||||
didEncounterError(error: any) {
|
||||
throw error
|
||||
}
|
||||
|
||||
cacheKey(id: string | undefined, type: string) {
|
||||
return `firebaseapi-${type}-${id}`
|
||||
}
|
||||
|
||||
async get<T>(
|
||||
id: string | undefined,
|
||||
type: string,
|
||||
func: () => Promise<T>,
|
||||
{ ttlInSeconds = 200 }: { ttlInSeconds?: number } = {}
|
||||
): Promise<T> {
|
||||
const cacheDoc = await this.cache.get(this.cacheKey(id, type))
|
||||
if (cacheDoc) {
|
||||
return JSON.parse(cacheDoc)
|
||||
}
|
||||
|
||||
const doc = await func()
|
||||
|
||||
if (ttlInSeconds) {
|
||||
this.cache.set(this.cacheKey(id, type), JSON.stringify(doc), {
|
||||
ttl: ttlInSeconds,
|
||||
})
|
||||
}
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
async listAllBets(id: string) {
|
||||
return this.get(id, 'listAllBets', () => listAllBets(id))
|
||||
}
|
||||
|
||||
async getContractFromSlug(id: string) {
|
||||
return this.get(id, 'getContractFromSlug', () => getContractFromSlug(id))
|
||||
}
|
||||
|
||||
async getContractFromID(id: string) {
|
||||
return this.get(id, 'market', () => getContractFromId(id))
|
||||
}
|
||||
|
||||
async getOutcomeProbability(contract: Contract, answerID: string) {
|
||||
return this.get(answerID, 'getOutcomeProbability', () =>
|
||||
(async () => getOutcomeProbability(contract, answerID))()
|
||||
)
|
||||
}
|
||||
|
||||
async getProbability(contract: BinaryContract) {
|
||||
return this.get(contract.id, 'getProbability', () =>
|
||||
(async () => getProbability(contract))()
|
||||
)
|
||||
}
|
||||
|
||||
async listAllCommentAnswers(commentId: string, contractID: string) {
|
||||
return this.get(commentId, 'listAllCommentAnswers', async () => {
|
||||
const allComments = await this.listAllComments(contractID)
|
||||
return allComments.filter((c) => c.replyToCommentId === commentId)
|
||||
})
|
||||
}
|
||||
|
||||
async listAllComments(id: string) {
|
||||
return this.get(id, 'listAllComments', () => listAllComments(id))
|
||||
}
|
||||
|
||||
async listAllContracts(limit = 1000, before?: string) {
|
||||
return this.get(before, 'listAllContracts', () =>
|
||||
listAllContracts(limit, before)
|
||||
)
|
||||
}
|
||||
|
||||
async getUser(id: string) {
|
||||
return this.get(id, 'user', () => getUser(id))
|
||||
}
|
||||
}
|
10
web/lib/api/graphql/datasources/index.ts
Normal file
10
web/lib/api/graphql/datasources/index.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import type { contextType } from '../types'
|
||||
|
||||
import { FirebaseAPI } from './firebaseAPI'
|
||||
|
||||
const dataSources = () =>
|
||||
({
|
||||
firebaseAPI: new FirebaseAPI(),
|
||||
} as contextType['dataSources'])
|
||||
|
||||
export default dataSources
|
16
web/lib/api/graphql/index.ts
Normal file
16
web/lib/api/graphql/index.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { ApolloServer } from 'apollo-server-micro'
|
||||
import { ApolloServerPluginLandingPageGraphQLPlayground } from 'apollo-server-core'
|
||||
|
||||
import dataSources from './datasources'
|
||||
import resolvers from './resolvers'
|
||||
import typeDefs from 'web/generated/schema.graphql'
|
||||
|
||||
export const apolloServer = new ApolloServer({
|
||||
csrfPrevention: true,
|
||||
cache: 'bounded',
|
||||
plugins: [ApolloServerPluginLandingPageGraphQLPlayground()],
|
||||
|
||||
dataSources,
|
||||
resolvers,
|
||||
typeDefs,
|
||||
})
|
14
web/lib/api/graphql/resolvers/bet.ts
Normal file
14
web/lib/api/graphql/resolvers/bet.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import type { BetResolvers, Resolvers } from 'web/generated/graphql_api'
|
||||
|
||||
const betResolvers: BetResolvers = {
|
||||
market: async (bet) => bet.contract,
|
||||
|
||||
user: async (bet, _, { dataSources }) =>
|
||||
await dataSources.firebaseAPI.getUser(bet.userId),
|
||||
}
|
||||
|
||||
const resolvers: Resolvers = {
|
||||
Bet: betResolvers,
|
||||
}
|
||||
|
||||
export default resolvers
|
30
web/lib/api/graphql/resolvers/comment.ts
Normal file
30
web/lib/api/graphql/resolvers/comment.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import type { CommentResolvers, Resolvers } from 'web/generated/graphql_api'
|
||||
|
||||
const commentResolvers: CommentResolvers = {
|
||||
market: async (comment) => comment.contract,
|
||||
|
||||
answers: async (comment, _, { dataSources }) => {
|
||||
const result = await dataSources.firebaseAPI.listAllCommentAnswers(
|
||||
comment.id,
|
||||
comment.contract.id
|
||||
)
|
||||
|
||||
return result.map((el) => ({
|
||||
...el,
|
||||
contract: comment.contract,
|
||||
}))
|
||||
},
|
||||
|
||||
user: async (comment) => ({
|
||||
id: comment.userId,
|
||||
avatarUrl: comment.userAvatarUrl,
|
||||
name: comment.userName,
|
||||
username: comment.userUsername,
|
||||
}),
|
||||
}
|
||||
|
||||
const resolvers: Resolvers = {
|
||||
Comment: commentResolvers,
|
||||
}
|
||||
|
||||
export default resolvers
|
19
web/lib/api/graphql/resolvers/index.ts
Normal file
19
web/lib/api/graphql/resolvers/index.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import type { Resolvers } from 'web/generated/graphql_api'
|
||||
|
||||
import { merge } from 'lodash'
|
||||
|
||||
import scalarResolvers from './scalars'
|
||||
import marketsResolvers from './market'
|
||||
import userResolvers from './user'
|
||||
import betResolvers from './bet'
|
||||
import commentResolvers from './comment'
|
||||
|
||||
const resolvers = merge([
|
||||
scalarResolvers,
|
||||
marketsResolvers,
|
||||
betResolvers,
|
||||
userResolvers,
|
||||
commentResolvers,
|
||||
]) as Resolvers
|
||||
|
||||
export default resolvers
|
19
web/lib/api/graphql/resolvers/index.ts.bak
Normal file
19
web/lib/api/graphql/resolvers/index.ts.bak
Normal file
|
@ -0,0 +1,19 @@
|
|||
import type { Resolvers } from 'web/generated/graphql_api'
|
||||
|
||||
import { merge } from 'lodash'
|
||||
|
||||
import scalarResolvers from './scalars'
|
||||
import marketsResolvers from './market'
|
||||
import userResolvers from './user'
|
||||
import betResolvers from './bet'
|
||||
import commentResolvers from './comment'
|
||||
|
||||
const resolvers = merge([
|
||||
scalarResolvers,
|
||||
marketsResolvers,
|
||||
betResolvers,
|
||||
userResolvers,
|
||||
commentResolvers,
|
||||
]) as Resolvers
|
||||
|
||||
export default resolvers
|
122
web/lib/api/graphql/resolvers/market.ts
Normal file
122
web/lib/api/graphql/resolvers/market.ts
Normal file
|
@ -0,0 +1,122 @@
|
|||
import type {
|
||||
MarketAnswerResolvers,
|
||||
MarketResolvers,
|
||||
QueryResolvers,
|
||||
Resolvers,
|
||||
ResolversTypes,
|
||||
} from 'web/generated/graphql_api'
|
||||
import type { MarketModel } from '../types'
|
||||
|
||||
import { UserInputError } from 'apollo-server-micro'
|
||||
|
||||
function contractVerify(contract?: MarketModel) {
|
||||
if (!contract) {
|
||||
throw new UserInputError('Contract not found')
|
||||
}
|
||||
return contract
|
||||
}
|
||||
function augmentContract<T>(contract: MarketModel, l: T[]) {
|
||||
return l.map((el) => ({
|
||||
...el,
|
||||
contract,
|
||||
}))
|
||||
}
|
||||
|
||||
const answerResolvers: MarketAnswerResolvers = {
|
||||
creator: (answer) => ({
|
||||
id: answer.userId,
|
||||
name: answer.name,
|
||||
username: answer.username,
|
||||
avatarUrl: answer.avatarUrl,
|
||||
}),
|
||||
|
||||
probability: (answer, _, { dataSources }) =>
|
||||
dataSources.firebaseAPI.getOutcomeProbability(answer.contract, answer.id),
|
||||
|
||||
market: async (answer) => answer.contract,
|
||||
}
|
||||
|
||||
const marketResolvers: MarketResolvers = {
|
||||
url: async (contract) =>
|
||||
`https://manifold.markets/${contract.creatorUsername}/${contract.slug}`,
|
||||
|
||||
creator: async (contract) => ({
|
||||
id: contract.creatorId,
|
||||
username: contract.creatorUsername,
|
||||
name: contract.creatorName,
|
||||
avatarUrl: contract.creatorAvatarUrl,
|
||||
}),
|
||||
|
||||
outcome: async (contract: MarketModel, _, { dataSources }) => {
|
||||
switch (contract.outcomeType) {
|
||||
case 'BINARY':
|
||||
return {
|
||||
__typename: 'MarketOutcomeBinary',
|
||||
probability: await dataSources.firebaseAPI.getProbability(contract),
|
||||
} as ResolversTypes['MarketOutcomeBinary']
|
||||
|
||||
case 'FREE_RESPONSE':
|
||||
return {
|
||||
__typename: 'MarketOutcomeFreeResponse',
|
||||
answers: augmentContract(contract, contract.answers),
|
||||
} as ResolversTypes['MarketOutcomeFreeResponse']
|
||||
|
||||
case 'PSEUDO_NUMERIC':
|
||||
case 'NUMERIC':
|
||||
return {
|
||||
__typename: 'MarketOutcomeNumeric',
|
||||
min: contract.min,
|
||||
max: contract.max,
|
||||
} as ResolversTypes['MarketOutcomeNumeric']
|
||||
}
|
||||
},
|
||||
|
||||
pool: async (contract) => contract.pool.YES + contract.pool.NO || undefined,
|
||||
|
||||
closeTime: async (contract) =>
|
||||
contract.resolutionTime && contract.closeTime
|
||||
? Math.min(contract.resolutionTime, contract.closeTime)
|
||||
: contract.closeTime,
|
||||
|
||||
bets: async (contract, _, { dataSources }) =>
|
||||
augmentContract(
|
||||
contract,
|
||||
await dataSources.firebaseAPI.listAllBets(contract.id)
|
||||
),
|
||||
|
||||
comments: async (contract, _, { dataSources }) =>
|
||||
augmentContract(
|
||||
contract,
|
||||
await dataSources.firebaseAPI.listAllComments(contract.id)
|
||||
),
|
||||
}
|
||||
|
||||
const queryResolvers: QueryResolvers = {
|
||||
markets: async (_, { before, limit }, { dataSources }) => {
|
||||
if (limit < 1 || limit > 1000) {
|
||||
throw new UserInputError('limit must be between 1 and 1000')
|
||||
}
|
||||
|
||||
try {
|
||||
return await dataSources.firebaseAPI.listAllContracts(limit, before)
|
||||
} catch (e) {
|
||||
throw new UserInputError(
|
||||
'Failed to fetch markets (did you pass an invalid ID as the before parameter?)'
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
market: async (_, { id }, { dataSources }) =>
|
||||
contractVerify(await dataSources.firebaseAPI.getContractFromID(id)),
|
||||
|
||||
slug: async (_, { url }, { dataSources }) =>
|
||||
contractVerify(await dataSources.firebaseAPI.getContractFromSlug(url)),
|
||||
}
|
||||
|
||||
const resolver: Resolvers = {
|
||||
Query: queryResolvers,
|
||||
Market: marketResolvers,
|
||||
MarketAnswer: answerResolvers,
|
||||
}
|
||||
|
||||
export default resolver
|
5
web/lib/api/graphql/resolvers/scalars.ts
Normal file
5
web/lib/api/graphql/resolvers/scalars.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { TimestampResolver } from 'graphql-scalars'
|
||||
|
||||
const resolvers = { Timestamp: TimestampResolver }
|
||||
|
||||
export default resolvers
|
11
web/lib/api/graphql/resolvers/user.ts
Normal file
11
web/lib/api/graphql/resolvers/user.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import type { Resolvers, UserResolvers } from 'web/generated/graphql_api'
|
||||
|
||||
const userResolvers: UserResolvers = {
|
||||
id: (user) => user.id,
|
||||
}
|
||||
|
||||
const resolvers: Resolvers = {
|
||||
User: userResolvers,
|
||||
}
|
||||
|
||||
export default resolvers
|
51
web/lib/api/graphql/typedefs/bet.graphql
Normal file
51
web/lib/api/graphql/typedefs/bet.graphql
Normal file
|
@ -0,0 +1,51 @@
|
|||
scalar BetID
|
||||
|
||||
type Sale {
|
||||
# amount user makes from sale
|
||||
amount: Int
|
||||
}
|
||||
|
||||
type Bet {
|
||||
id: BetID!
|
||||
user: User
|
||||
market: Market!
|
||||
|
||||
# bet size; negative if SELL bet
|
||||
amount: Int!
|
||||
outcome: String!
|
||||
# dynamic parimutuel pool weight; negative if SELL bet
|
||||
shares: Float!
|
||||
|
||||
probBefore: Float!
|
||||
probAfter: Float!
|
||||
|
||||
sale: Sale
|
||||
|
||||
# true if this BUY bet has been sold
|
||||
isSold: Boolean
|
||||
isAnte: Boolean
|
||||
|
||||
createdTime: Timestamp
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
"""
|
||||
Places a new bet on behalf of the authorized user.
|
||||
"""
|
||||
placeBet(
|
||||
"""
|
||||
The amount to bet, in M$, before fees.
|
||||
"""
|
||||
amount: Int!
|
||||
|
||||
"""
|
||||
The ID of the contract (market) to bet on.
|
||||
"""
|
||||
contractId: MarketID!
|
||||
|
||||
"""
|
||||
The outcome to bet on. For binary markets, this is YES or NO. For free response markets, this is the ID of the free response answer. For numeric markets, this is a string representing the target bucket, and an additional value parameter is required which is a number representing the target value. (Bet on numeric markets at your own peril.)
|
||||
"""
|
||||
outcome: String!
|
||||
): Bet
|
||||
}
|
12
web/lib/api/graphql/typedefs/comments.graphql
Normal file
12
web/lib/api/graphql/typedefs/comments.graphql
Normal file
|
@ -0,0 +1,12 @@
|
|||
scalar CommentID
|
||||
|
||||
type Comment {
|
||||
id: CommentID!
|
||||
market: Market!
|
||||
answerOutcome: Int
|
||||
text: String!
|
||||
createdTime: Timestamp!
|
||||
replyTo: Comment
|
||||
answers: [Comment!]!
|
||||
user: User!
|
||||
}
|
155
web/lib/api/graphql/typedefs/market.graphql
Normal file
155
web/lib/api/graphql/typedefs/market.graphql
Normal file
|
@ -0,0 +1,155 @@
|
|||
scalar MarketID
|
||||
|
||||
type Market {
|
||||
id: MarketID!
|
||||
|
||||
creator: User!
|
||||
bets: [Bet!]!
|
||||
comments: [Comment!]!
|
||||
outcome: MarketOutcome!
|
||||
|
||||
createdTime: Timestamp!
|
||||
# Min of creator's chosen date, and resolutionTime
|
||||
closeTime: Timestamp
|
||||
question: String!
|
||||
description: String!
|
||||
tags: [String!]
|
||||
url: String!
|
||||
pool: Float
|
||||
volume: Float!
|
||||
volume7Days: Float!
|
||||
volume24Hours: Float!
|
||||
isResolved: Boolean!
|
||||
resolutionTime: Int
|
||||
resolution: String
|
||||
|
||||
p: Float
|
||||
|
||||
totalLiquidity: Float
|
||||
|
||||
# Method of market making (cpmm, dpm, etc)
|
||||
# ... TODO: This should be a enum
|
||||
mechanism: String!
|
||||
}
|
||||
|
||||
type MarketOutcomeBinary {
|
||||
probability: Float!
|
||||
}
|
||||
input MarketOutcomeBinaryInput {
|
||||
probability: Float!
|
||||
}
|
||||
|
||||
scalar MarketAnswerID
|
||||
type MarketAnswer {
|
||||
id: MarketAnswerID!
|
||||
text: String!
|
||||
market: Market
|
||||
creator: User!
|
||||
createdTime: Timestamp!
|
||||
number: Int!
|
||||
probability: Float!
|
||||
}
|
||||
type MarketOutcomeFreeResponse {
|
||||
answers: [MarketAnswer!]!
|
||||
}
|
||||
|
||||
type MarketOutcomeNumeric {
|
||||
min: Float!
|
||||
max: Float!
|
||||
}
|
||||
input MarketOutcomeNumericInput {
|
||||
min: Float!
|
||||
max: Float!
|
||||
}
|
||||
|
||||
enum MarketType {
|
||||
BINARY
|
||||
FREE_RESPONSE
|
||||
NUMERIC
|
||||
}
|
||||
|
||||
union MarketOutcome =
|
||||
MarketOutcomeFreeResponse
|
||||
| MarketOutcomeNumeric
|
||||
| MarketOutcomeBinary
|
||||
|
||||
type Query {
|
||||
"""
|
||||
Lists all markets, ordered by creation date descending.
|
||||
"""
|
||||
markets(
|
||||
"""
|
||||
The ID of the market before which the list will start.
|
||||
For example, if you ask for the most recent 10 markets, and then perform a second query for 10 more markets with before=[the id of the 10th market], you will get markets 11 through 20.
|
||||
"""
|
||||
before: MarketID
|
||||
|
||||
"""
|
||||
How many markets to return. The maximum and the default is 1000.
|
||||
"""
|
||||
limit: Int = 1000
|
||||
): [Market!]!
|
||||
|
||||
"""
|
||||
Gets information about a single market by ID.
|
||||
|
||||
Example request: https://manifold.markets/api/v0/market/3zspH9sSzMlbFQLn9GKR
|
||||
"""
|
||||
market(id: MarketID!): Market!
|
||||
|
||||
"""
|
||||
Gets information about a single market by slug (the portion of the URL path after the username).
|
||||
"""
|
||||
slug(url: String!): Market!
|
||||
}
|
||||
|
||||
input MarketInput {
|
||||
"""
|
||||
The headline question for the market.
|
||||
"""
|
||||
question: String!
|
||||
|
||||
"""
|
||||
A long description describing the rules for the market.
|
||||
"""
|
||||
description: String!
|
||||
|
||||
"""
|
||||
The time at which the market will close, represented as milliseconds since the epoch.
|
||||
"""
|
||||
closeTime: Timestamp
|
||||
|
||||
"""
|
||||
An array of string tags for the market.
|
||||
"""
|
||||
tags: [String!]
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
"""
|
||||
Creates a new binary market on behalf of the authorized user.
|
||||
"""
|
||||
createMarketBinary(
|
||||
"""
|
||||
Condition of market at creation
|
||||
"""
|
||||
initial: MarketOutcomeBinaryInput
|
||||
|
||||
input: MarketInput
|
||||
): Market
|
||||
"""
|
||||
Creates a new free response market on behalf of the authorized user.
|
||||
"""
|
||||
createMarketFreeResponse(input: MarketInput): Market
|
||||
"""
|
||||
Creates a new numeric market on behalf of the authorized user.
|
||||
"""
|
||||
createMarketNumeric(
|
||||
"""
|
||||
Condition of market at creation
|
||||
"""
|
||||
initial: MarketOutcomeNumericInput
|
||||
|
||||
input: MarketInput
|
||||
): Market
|
||||
}
|
1
web/lib/api/graphql/typedefs/scalars.graphql
Normal file
1
web/lib/api/graphql/typedefs/scalars.graphql
Normal file
|
@ -0,0 +1 @@
|
|||
scalar Timestamp
|
8
web/lib/api/graphql/typedefs/user.graphql
Normal file
8
web/lib/api/graphql/typedefs/user.graphql
Normal file
|
@ -0,0 +1,8 @@
|
|||
scalar UserID
|
||||
|
||||
type User {
|
||||
id: UserID!
|
||||
username: String!
|
||||
name: String!
|
||||
avatarUrl: String
|
||||
}
|
18
web/lib/api/graphql/types.ts
Normal file
18
web/lib/api/graphql/types.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import type { FirebaseAPI } from './datasources/firebaseAPI'
|
||||
import type { Answer } from 'common/answer'
|
||||
import type { Bet } from 'common/bet'
|
||||
import type { Comment } from 'common/comment'
|
||||
import type { Contract } from 'common/contract'
|
||||
|
||||
/* Model types, used internally by resolvers */
|
||||
export type MarketModel = Contract
|
||||
export type AnswerModel = Answer & { contract: MarketModel }
|
||||
export type CommentModel = Comment & { contract: MarketModel }
|
||||
export type BetModel = Bet & { contract: MarketModel }
|
||||
|
||||
/* Context type */
|
||||
export type contextType = {
|
||||
dataSources: {
|
||||
firebaseAPI: FirebaseAPI
|
||||
}
|
||||
}
|
|
@ -2,6 +2,17 @@ const API_DOCS_URL = 'https://docs.manifold.markets/api'
|
|||
|
||||
/** @type {import('next').NextConfig} */
|
||||
module.exports = {
|
||||
webpack: (config) => {
|
||||
config.module.rules.push({
|
||||
test: /\.(graphql|gql)$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'graphql-tag/loader',
|
||||
})
|
||||
return config
|
||||
},
|
||||
webpackDevMiddleware: (config) => {
|
||||
return config
|
||||
},
|
||||
staticPageGenerationTimeout: 600, // e.g. stats page
|
||||
reactStrictMode: true,
|
||||
optimizeFonts: false,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "concurrently -n NEXT,TS -c magenta,cyan \"next dev -p 3000\" \"yarn ts --watch\"",
|
||||
"devdev": "cross-env NEXT_PUBLIC_FIREBASE_ENV=DEV concurrently -n NEXT,TS -c magenta,cyan \"cross-env FIREBASE_ENV=DEV next dev -p 3000\" \"cross-env FIREBASE_ENV=DEV yarn ts --watch\"",
|
||||
"devdev": "cross-env NEXT_PUBLIC_FIREBASE_ENV=DEV concurrently -n NEXT,TS,GRAPHQL -c magenta,cyan,yellow \"cross-env FIREBASE_ENV=DEV next dev -p 3000\" \"cross-env FIREBASE_ENV=DEV yarn ts --watch\" \"yarn generate --watch lib/api/graphql/**/**.graphql --watch lib/api/graphql/types.ts \"",
|
||||
"dev:dev": "yarn devdev",
|
||||
"dev:the": "cross-env NEXT_PUBLIC_FIREBASE_ENV=THEOREMONE concurrently -n NEXT,TS -c magenta,cyan \"cross-env FIREBASE_ENV=THEOREMONE next dev -p 3000\" \"cross-env FIREBASE_ENV=THEOREMONE yarn ts --watch\"",
|
||||
"dev:emulate": "cross-env NEXT_PUBLIC_FIREBASE_EMULATE=TRUE yarn devdev",
|
||||
|
@ -15,7 +15,12 @@
|
|||
"format": "npx prettier --write .",
|
||||
"postbuild": "next-sitemap",
|
||||
"verify": "(cd .. && yarn verify)",
|
||||
"verify:dir": "npx prettier --check .; yarn lint --max-warnings 0; tsc --pretty --project tsconfig.json --noEmit"
|
||||
"verify:dir": "npx prettier --check .; yarn lint --max-warnings 0; tsc --pretty --project tsconfig.json --noEmit",
|
||||
"generate": "graphql-codegen --config codegen.yml",
|
||||
"predev": "yarn generate",
|
||||
"prebuild": "yarn generate",
|
||||
"prets": "yarn generate",
|
||||
"prelint": "yarn generate"
|
||||
},
|
||||
"dependencies": {
|
||||
"@amplitude/analytics-browser": "0.4.1",
|
||||
|
@ -31,14 +36,19 @@
|
|||
"@tiptap/react": "2.0.0-beta.114",
|
||||
"@tiptap/starter-kit": "2.0.0-beta.190",
|
||||
"algoliasearch": "4.13.0",
|
||||
"apollo-server-caching": "3.3.0",
|
||||
"apollo-server-micro": "3.10.0",
|
||||
"clsx": "1.1.1",
|
||||
"cors": "2.8.5",
|
||||
"daisyui": "1.16.4",
|
||||
"dayjs": "1.10.7",
|
||||
"firebase": "9.6.0",
|
||||
"graphql": "16.5.0",
|
||||
"graphql-scalars": "1.17.0",
|
||||
"gridjs": "5.0.2",
|
||||
"gridjs-react": "5.0.2",
|
||||
"lodash": "4.17.21",
|
||||
"micro": "9.4.0",
|
||||
"nanoid": "^3.3.4",
|
||||
"next": "12.1.2",
|
||||
"node-fetch": "3.2.4",
|
||||
|
@ -52,6 +62,9 @@
|
|||
"string-similarity": "^4.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/cli": "2.6.2",
|
||||
"@graphql-codegen/typescript": "2.5.1",
|
||||
"@graphql-codegen/typescript-resolvers": "2.6.6",
|
||||
"@tailwindcss/forms": "0.4.0",
|
||||
"@tailwindcss/line-clamp": "^0.3.1",
|
||||
"@tailwindcss/typography": "^0.5.1",
|
||||
|
|
21
web/pages/api/graphql/index.ts
Normal file
21
web/pages/api/graphql/index.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
import { apolloServer } from 'web/lib/api/graphql'
|
||||
|
||||
const startServer = apolloServer.start()
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
bodyParser: false,
|
||||
},
|
||||
}
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
await startServer
|
||||
await apolloServer.createHandler({
|
||||
path: '/api/graphql',
|
||||
})(req, res)
|
||||
}
|
Loading…
Reference in New Issue
Block a user