Initial project setup: Add contracts, API definitions, tests, and documentation
- Add Foundry project configuration (foundry.toml, foundry.lock) - Add Solidity contracts (TokenFactory138, BridgeVault138, ComplianceRegistry, etc.) - Add API definitions (OpenAPI, GraphQL, gRPC, AsyncAPI) - Add comprehensive test suite (unit, integration, fuzz, invariants) - Add API services (REST, GraphQL, orchestrator, packet service) - Add documentation (ISO20022 mapping, runbooks, adapter guides) - Add development tools (RBC tool, Swagger UI, mock server) - Update OpenZeppelin submodules to v5.0.0
This commit is contained in:
31
api/services/graphql-api/package.json
Normal file
31
api/services/graphql-api/package.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "@emoney/graphql-api",
|
||||
"version": "1.0.0",
|
||||
"description": "GraphQL API server for eMoney Token Factory",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "node dist/index.js",
|
||||
"dev": "ts-node-dev --respawn --transpile-only src/index.ts",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/server": "^4.9.5",
|
||||
"graphql": "^16.8.1",
|
||||
"graphql-subscriptions": "^2.0.0",
|
||||
"graphql-ws": "^5.14.2",
|
||||
"@graphql-tools/schema": "^10.0.0",
|
||||
"@graphql-tools/load-files": "^6.6.1",
|
||||
"@graphql-tools/merge": "^9.0.0",
|
||||
"@emoney/blockchain": "workspace:*",
|
||||
"@emoney/events": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.0",
|
||||
"typescript": "^5.3.0",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"jest": "^29.7.0",
|
||||
"@types/jest": "^29.5.11"
|
||||
}
|
||||
}
|
||||
|
||||
82
api/services/graphql-api/src/index.ts
Normal file
82
api/services/graphql-api/src/index.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* GraphQL API Server for eMoney Token Factory
|
||||
* Implements GraphQL schema with queries, mutations, and subscriptions
|
||||
*/
|
||||
|
||||
import { ApolloServer } from '@apollo/server';
|
||||
import { expressMiddleware } from '@apollo/server/express4';
|
||||
import { WebSocketServer } from 'ws';
|
||||
import { useServer } from 'graphql-ws/lib/use/ws';
|
||||
import { makeExecutableSchema } from '@graphql-tools/schema';
|
||||
import { loadFilesSync } from '@graphql-tools/load-files';
|
||||
import { mergeTypeDefs } from '@graphql-tools/merge';
|
||||
import express from 'express';
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { resolvers } from './resolvers';
|
||||
import { SubscriptionContext, createSubscriptionContext } from './subscriptions/context';
|
||||
|
||||
// Load GraphQL schema
|
||||
const schemaPath = join(__dirname, '../../../packages/graphql/schema.graphql');
|
||||
const typeDefs = readFileSync(schemaPath, 'utf-8');
|
||||
|
||||
// Create executable schema
|
||||
const schema = makeExecutableSchema({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
});
|
||||
|
||||
// Create Apollo Server
|
||||
const server = new ApolloServer<SubscriptionContext>({
|
||||
schema,
|
||||
plugins: [
|
||||
// WebSocket subscription plugin will be added
|
||||
],
|
||||
});
|
||||
|
||||
// Express app setup
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 4000;
|
||||
|
||||
// Start server
|
||||
async function startServer() {
|
||||
await server.start();
|
||||
|
||||
// GraphQL endpoint
|
||||
app.use(
|
||||
'/graphql',
|
||||
express.json(),
|
||||
expressMiddleware(server, {
|
||||
context: async ({ req }) => {
|
||||
// TODO: Add auth context
|
||||
return {
|
||||
// user: await getUserFromToken(req.headers.authorization),
|
||||
};
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
// WebSocket server for subscriptions
|
||||
const httpServer = app.listen(PORT, () => {
|
||||
const wsServer = new WebSocketServer({
|
||||
server: httpServer,
|
||||
path: '/graphql',
|
||||
});
|
||||
|
||||
useServer(
|
||||
{
|
||||
schema,
|
||||
context: createSubscriptionContext,
|
||||
},
|
||||
wsServer
|
||||
);
|
||||
|
||||
console.log(`GraphQL server ready at http://localhost:${PORT}/graphql`);
|
||||
console.log(`GraphQL subscriptions ready at ws://localhost:${PORT}/graphql`);
|
||||
});
|
||||
}
|
||||
|
||||
startServer().catch(console.error);
|
||||
|
||||
export default app;
|
||||
|
||||
14
api/services/graphql-api/src/resolvers/index.ts
Normal file
14
api/services/graphql-api/src/resolvers/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* GraphQL resolvers
|
||||
*/
|
||||
|
||||
import { queryResolvers } from './queries';
|
||||
import { mutationResolvers } from './mutations';
|
||||
import { subscriptionResolvers } from './subscriptions';
|
||||
|
||||
export const resolvers = {
|
||||
Query: queryResolvers,
|
||||
Mutation: mutationResolvers,
|
||||
Subscription: subscriptionResolvers,
|
||||
};
|
||||
|
||||
119
api/services/graphql-api/src/resolvers/mutations.ts
Normal file
119
api/services/graphql-api/src/resolvers/mutations.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* GraphQL mutation resolvers
|
||||
* Delegates to REST service layer
|
||||
*/
|
||||
|
||||
// Import services
|
||||
import { tokenService } from '../../../rest-api/src/services/token-service';
|
||||
import { lienService } from '../../../rest-api/src/services/lien-service';
|
||||
import { complianceService } from '../../../rest-api/src/services/compliance-service';
|
||||
import { mappingService } from '../../../rest-api/src/services/mapping-service';
|
||||
import { isoService } from '../../../rest-api/src/services/iso-service';
|
||||
import { triggerService } from '../../../rest-api/src/services/trigger-service';
|
||||
import { packetService } from '../../../rest-api/src/services/packet-service';
|
||||
import { bridgeService } from '../../../rest-api/src/services/bridge-service';
|
||||
|
||||
interface GraphQLContext {
|
||||
user?: any;
|
||||
}
|
||||
|
||||
export const mutationResolvers = {
|
||||
deployToken: async (parent: any, args: { input: any }, context: GraphQLContext) => {
|
||||
return await tokenService.deployToken(args.input);
|
||||
},
|
||||
|
||||
updateTokenPolicy: async (parent: any, args: { code: string; policy: any }, context: GraphQLContext) => {
|
||||
return await tokenService.updatePolicy(args.code, args.policy);
|
||||
},
|
||||
|
||||
mintToken: async (parent: any, args: { code: string; input: any }, context: GraphQLContext) => {
|
||||
return await tokenService.mint(args.code, args.input);
|
||||
},
|
||||
|
||||
burnToken: async (parent: any, args: { code: string; input: any }, context: GraphQLContext) => {
|
||||
return await tokenService.burn(args.code, args.input);
|
||||
},
|
||||
|
||||
clawbackToken: async (parent: any, args: { code: string; input: any }, context: GraphQLContext) => {
|
||||
return await tokenService.clawback(args.code, args.input);
|
||||
},
|
||||
|
||||
forceTransferToken: async (parent: any, args: { code: string; input: any }, context: GraphQLContext) => {
|
||||
return await tokenService.forceTransfer(args.code, args.input);
|
||||
},
|
||||
|
||||
placeLien: async (parent: any, args: { input: any }, context: GraphQLContext) => {
|
||||
return await lienService.placeLien(args.input);
|
||||
},
|
||||
|
||||
reduceLien: async (parent: any, args: { lienId: string; reduceBy: string }, context: GraphQLContext) => {
|
||||
return await lienService.reduceLien(args.lienId, args.reduceBy);
|
||||
},
|
||||
|
||||
releaseLien: async (parent: any, args: { lienId: string }, context: GraphQLContext) => {
|
||||
await lienService.releaseLien(args.lienId);
|
||||
return { success: true };
|
||||
},
|
||||
|
||||
setCompliance: async (parent: any, args: { refId: string; input: any }, context: GraphQLContext) => {
|
||||
return await complianceService.setCompliance(args.refId, args.input);
|
||||
},
|
||||
|
||||
setFreeze: async (parent: any, args: { refId: string; frozen: boolean }, context: GraphQLContext) => {
|
||||
return await complianceService.setFrozen(args.refId, { frozen: args.frozen });
|
||||
},
|
||||
|
||||
linkAccountWallet: async (parent: any, args: { input: any }, context: GraphQLContext) => {
|
||||
await mappingService.linkAccountWallet(args.input);
|
||||
return { success: true };
|
||||
},
|
||||
|
||||
unlinkAccountWallet: async (parent: any, args: { input: any }, context: GraphQLContext) => {
|
||||
await mappingService.unlinkAccountWallet(args.input);
|
||||
return { success: true };
|
||||
},
|
||||
|
||||
submitInboundMessage: async (parent: any, args: { input: any }, context: GraphQLContext) => {
|
||||
return await isoService.submitInboundMessage(args.input);
|
||||
},
|
||||
|
||||
submitOutboundMessage: async (parent: any, args: { input: any }, context: GraphQLContext) => {
|
||||
return await isoService.submitOutboundMessage(args.input);
|
||||
},
|
||||
|
||||
validateAndLockTrigger: async (parent: any, args: { triggerId: string; input?: any }, context: GraphQLContext) => {
|
||||
return await triggerService.validateAndLock(args.triggerId, args.input || {});
|
||||
},
|
||||
|
||||
markTriggerSubmitted: async (parent: any, args: { triggerId: string }, context: GraphQLContext) => {
|
||||
return await triggerService.markSubmitted(args.triggerId);
|
||||
},
|
||||
|
||||
confirmTriggerSettled: async (parent: any, args: { triggerId: string }, context: GraphQLContext) => {
|
||||
return await triggerService.confirmSettled(args.triggerId);
|
||||
},
|
||||
|
||||
confirmTriggerRejected: async (parent: any, args: { triggerId: string; reason?: string }, context: GraphQLContext) => {
|
||||
return await triggerService.confirmRejected(args.triggerId, args.reason);
|
||||
},
|
||||
|
||||
generatePacket: async (parent: any, args: { input: any }, context: GraphQLContext) => {
|
||||
return await packetService.generatePacket(args.input);
|
||||
},
|
||||
|
||||
dispatchPacket: async (parent: any, args: { packetId: string; input?: any }, context: GraphQLContext) => {
|
||||
return await packetService.dispatchPacket({ packetId: args.packetId, ...args.input });
|
||||
},
|
||||
|
||||
acknowledgePacket: async (parent: any, args: { packetId: string; ack: any }, context: GraphQLContext) => {
|
||||
return await packetService.acknowledgePacket(args.packetId, args.ack);
|
||||
},
|
||||
|
||||
bridgeLock: async (parent: any, args: { input: any }, context: GraphQLContext) => {
|
||||
return await bridgeService.lock(args.input);
|
||||
},
|
||||
|
||||
bridgeUnlock: async (parent: any, args: { input: any }, context: GraphQLContext) => {
|
||||
return await bridgeService.unlock(args.input);
|
||||
},
|
||||
};
|
||||
183
api/services/graphql-api/src/resolvers/queries.ts
Normal file
183
api/services/graphql-api/src/resolvers/queries.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* GraphQL query resolvers
|
||||
*/
|
||||
|
||||
// Import services (using relative paths since we're in a monorepo)
|
||||
import { tokenService } from '../../../rest-api/src/services/token-service';
|
||||
import { lienService } from '../../../rest-api/src/services/lien-service';
|
||||
import { complianceService } from '../../../rest-api/src/services/compliance-service';
|
||||
import { mappingService } from '../../../rest-api/src/services/mapping-service';
|
||||
import { triggerService } from '../../../rest-api/src/services/trigger-service';
|
||||
import { packetService } from '../../../rest-api/src/services/packet-service';
|
||||
import { bridgeService } from '../../../rest-api/src/services/bridge-service';
|
||||
|
||||
// Type definitions (simplified - in production, use generated types)
|
||||
interface GraphQLContext {
|
||||
user?: any;
|
||||
}
|
||||
|
||||
export const queryResolvers = {
|
||||
token: async (parent: any, args: { code: string }, context: GraphQLContext) => {
|
||||
return await tokenService.getToken(args.code);
|
||||
},
|
||||
|
||||
tokens: async (parent: any, args: { filters?: any; paging?: any }, context: GraphQLContext) => {
|
||||
const result = await tokenService.listTokens({
|
||||
code: args.filters?.code,
|
||||
issuer: args.filters?.issuer,
|
||||
limit: args.paging?.limit || 20,
|
||||
offset: args.paging?.offset || 0,
|
||||
});
|
||||
return {
|
||||
edges: result.tokens.map((token: any) => ({ node: token })),
|
||||
pageInfo: {
|
||||
hasNextPage: result.tokens.length === (args.paging?.limit || 20),
|
||||
hasPreviousPage: (args.paging?.offset || 0) > 0,
|
||||
},
|
||||
totalCount: result.total,
|
||||
};
|
||||
},
|
||||
|
||||
lien: async (parent: any, args: { lienId: string }, context: GraphQLContext) => {
|
||||
return await lienService.getLien(args.lienId);
|
||||
},
|
||||
|
||||
liens: async (parent: any, args: { filters?: any; paging?: any }, context: GraphQLContext) => {
|
||||
const result = await lienService.listLiens({
|
||||
debtor: args.filters?.debtor,
|
||||
active: args.filters?.active,
|
||||
limit: args.paging?.limit || 20,
|
||||
offset: args.paging?.offset || 0,
|
||||
});
|
||||
return {
|
||||
edges: result.liens.map((lien: any) => ({ node: lien })),
|
||||
pageInfo: {
|
||||
hasNextPage: result.liens.length === (args.paging?.limit || 20),
|
||||
hasPreviousPage: (args.paging?.offset || 0) > 0,
|
||||
},
|
||||
totalCount: result.total,
|
||||
};
|
||||
},
|
||||
|
||||
accountLiens: async (parent: any, args: { accountRefId: string }, context: GraphQLContext) => {
|
||||
return await lienService.getAccountLiens(args.accountRefId);
|
||||
},
|
||||
|
||||
accountEncumbrance: async (parent: any, args: { accountRefId: string }, context: GraphQLContext) => {
|
||||
return await lienService.getEncumbrance(args.accountRefId);
|
||||
},
|
||||
|
||||
compliance: async (parent: any, args: { refId: string }, context: GraphQLContext) => {
|
||||
return await complianceService.getProfile(args.refId);
|
||||
},
|
||||
|
||||
accountCompliance: async (parent: any, args: { accountRefId: string }, context: GraphQLContext) => {
|
||||
return await complianceService.getProfile(args.accountRefId);
|
||||
},
|
||||
|
||||
walletCompliance: async (parent: any, args: { walletRefId: string }, context: GraphQLContext) => {
|
||||
return await complianceService.getProfile(args.walletRefId);
|
||||
},
|
||||
|
||||
account: async (parent: any, args: { refId: string }, context: GraphQLContext) => {
|
||||
// In production, fetch from database with nested data
|
||||
const [liens, compliance, wallets] = await Promise.all([
|
||||
lienService.getAccountLiens(args.refId),
|
||||
complianceService.getProfile(args.refId).catch(() => null),
|
||||
mappingService.getAccountWallets(args.refId),
|
||||
]);
|
||||
return {
|
||||
refId: args.refId,
|
||||
liens,
|
||||
compliance,
|
||||
wallets: wallets.map((w: string) => ({ refId: w })),
|
||||
};
|
||||
},
|
||||
|
||||
wallet: async (parent: any, args: { refId: string }, context: GraphQLContext) => {
|
||||
const accounts = await mappingService.getWalletAccounts(args.refId);
|
||||
return {
|
||||
refId: args.refId,
|
||||
accounts: accounts.map((a: string) => ({ refId: a })),
|
||||
};
|
||||
},
|
||||
|
||||
accountWallets: async (parent: any, args: { accountRefId: string }, context: GraphQLContext) => {
|
||||
const wallets = await mappingService.getAccountWallets(args.accountRefId);
|
||||
return wallets.map((w: string) => ({ refId: w }));
|
||||
},
|
||||
|
||||
walletAccounts: async (parent: any, args: { walletRefId: string }, context: GraphQLContext) => {
|
||||
const accounts = await mappingService.getWalletAccounts(args.walletRefId);
|
||||
return accounts.map((a: string) => ({ refId: a }));
|
||||
},
|
||||
|
||||
trigger: async (parent: any, args: { triggerId: string }, context: GraphQLContext) => {
|
||||
const trigger = await triggerService.getTrigger(args.triggerId);
|
||||
if (!trigger) return null;
|
||||
// Fetch nested packets
|
||||
const packetsResult = await packetService.listPackets({ triggerId: args.triggerId });
|
||||
return {
|
||||
...trigger,
|
||||
packets: packetsResult.packets,
|
||||
};
|
||||
},
|
||||
|
||||
triggers: async (parent: any, args: { filters?: any; paging?: any }, context: GraphQLContext) => {
|
||||
const result = await triggerService.listTriggers({
|
||||
rail: args.filters?.rail,
|
||||
state: args.filters?.state,
|
||||
accountRef: args.filters?.accountRef,
|
||||
walletRef: args.filters?.walletRef,
|
||||
limit: args.paging?.limit || 20,
|
||||
offset: args.paging?.offset || 0,
|
||||
});
|
||||
return {
|
||||
edges: result.triggers.map((trigger: any) => ({ node: trigger })),
|
||||
pageInfo: {
|
||||
hasNextPage: result.triggers.length === (args.paging?.limit || 20),
|
||||
hasPreviousPage: (args.paging?.offset || 0) > 0,
|
||||
},
|
||||
totalCount: result.total,
|
||||
};
|
||||
},
|
||||
|
||||
packet: async (parent: any, args: { packetId: string }, context: GraphQLContext) => {
|
||||
return await packetService.getPacket(args.packetId);
|
||||
},
|
||||
|
||||
packets: async (parent: any, args: { filters?: any; paging?: any }, context: GraphQLContext) => {
|
||||
const result = await packetService.listPackets({
|
||||
triggerId: args.filters?.triggerId,
|
||||
status: args.filters?.status,
|
||||
limit: args.paging?.limit || 20,
|
||||
offset: args.paging?.offset || 0,
|
||||
});
|
||||
return {
|
||||
edges: result.packets.map((packet: any) => ({ node: packet })),
|
||||
pageInfo: {
|
||||
hasNextPage: result.packets.length === (args.paging?.limit || 20),
|
||||
hasPreviousPage: (args.paging?.offset || 0) > 0,
|
||||
},
|
||||
totalCount: result.total,
|
||||
};
|
||||
},
|
||||
|
||||
bridgeLock: async (parent: any, args: { lockId: string }, context: GraphQLContext) => {
|
||||
return await bridgeService.getLockStatus(args.lockId);
|
||||
},
|
||||
|
||||
bridgeLocks: async (parent: any, args: { filters?: any; paging?: any }, context: GraphQLContext) => {
|
||||
// In production, implement list locks
|
||||
return {
|
||||
edges: [],
|
||||
pageInfo: { hasNextPage: false, hasPreviousPage: false },
|
||||
totalCount: 0,
|
||||
};
|
||||
},
|
||||
|
||||
bridgeCorridors: async (parent: any, args: any, context: GraphQLContext) => {
|
||||
const result = await bridgeService.getCorridors();
|
||||
return result.corridors;
|
||||
},
|
||||
};
|
||||
87
api/services/graphql-api/src/resolvers/subscriptions.ts
Normal file
87
api/services/graphql-api/src/resolvers/subscriptions.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* GraphQL subscription resolvers
|
||||
* Connect to event bus for real-time updates
|
||||
*/
|
||||
|
||||
import { SubscriptionResolvers } from '../generated/graphql-types';
|
||||
import { eventBusClient } from '@emoney/events';
|
||||
|
||||
export const subscriptionResolvers: SubscriptionResolvers = {
|
||||
onTriggerStateChanged: {
|
||||
subscribe: async (parent, args, context) => {
|
||||
// TODO: Subscribe to triggers.state.updated event
|
||||
return eventBusClient.subscribe(`triggers.state.updated.${args.triggerId}`);
|
||||
},
|
||||
},
|
||||
|
||||
onTriggerCreated: {
|
||||
subscribe: async (parent, args, context) => {
|
||||
// TODO: Subscribe to triggers.created event with filtering
|
||||
return eventBusClient.subscribe('triggers.created');
|
||||
},
|
||||
},
|
||||
|
||||
onLienChanged: {
|
||||
subscribe: async (parent, args, context) => {
|
||||
// TODO: Subscribe to liens events for specific debtor
|
||||
return eventBusClient.subscribe(`liens.${args.debtorRefId}`);
|
||||
},
|
||||
},
|
||||
|
||||
onLienPlaced: {
|
||||
subscribe: async (parent, args, context) => {
|
||||
// TODO: Subscribe to liens.placed event
|
||||
return eventBusClient.subscribe('liens.placed');
|
||||
},
|
||||
},
|
||||
|
||||
onLienReleased: {
|
||||
subscribe: async (parent, args, context) => {
|
||||
// TODO: Subscribe to liens.released event
|
||||
return eventBusClient.subscribe('liens.released');
|
||||
},
|
||||
},
|
||||
|
||||
onPacketStatusChanged: {
|
||||
subscribe: async (parent, args, context) => {
|
||||
// TODO: Subscribe to packets events for specific packet
|
||||
return eventBusClient.subscribe(`packets.${args.packetId}`);
|
||||
},
|
||||
},
|
||||
|
||||
onPacketDispatched: {
|
||||
subscribe: async (parent, args, context) => {
|
||||
// TODO: Subscribe to packets.dispatched event
|
||||
return eventBusClient.subscribe('packets.dispatched');
|
||||
},
|
||||
},
|
||||
|
||||
onPacketAcknowledged: {
|
||||
subscribe: async (parent, args, context) => {
|
||||
// TODO: Subscribe to packets.acknowledged event
|
||||
return eventBusClient.subscribe('packets.acknowledged');
|
||||
},
|
||||
},
|
||||
|
||||
onComplianceChanged: {
|
||||
subscribe: async (parent, args, context) => {
|
||||
// TODO: Subscribe to compliance.updated event for specific ref
|
||||
return eventBusClient.subscribe(`compliance.updated.${args.refId}`);
|
||||
},
|
||||
},
|
||||
|
||||
onFreezeChanged: {
|
||||
subscribe: async (parent, args, context) => {
|
||||
// TODO: Subscribe to compliance freeze changes
|
||||
return eventBusClient.subscribe(`compliance.freeze.${args.refId}`);
|
||||
},
|
||||
},
|
||||
|
||||
onPolicyUpdated: {
|
||||
subscribe: async (parent, args, context) => {
|
||||
// TODO: Subscribe to policy.updated event for specific token
|
||||
return eventBusClient.subscribe(`policy.updated.${args.token}`);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
15
api/services/graphql-api/src/subscriptions/context.ts
Normal file
15
api/services/graphql-api/src/subscriptions/context.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Subscription context for GraphQL WebSocket connections
|
||||
*/
|
||||
|
||||
export interface SubscriptionContext {
|
||||
// TODO: Add subscription context properties
|
||||
connectionParams?: any;
|
||||
}
|
||||
|
||||
export function createSubscriptionContext(connectionParams: any): SubscriptionContext {
|
||||
return {
|
||||
connectionParams,
|
||||
};
|
||||
}
|
||||
|
||||
19
api/services/graphql-api/tsconfig.json
Normal file
19
api/services/graphql-api/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "commonjs",
|
||||
"lib": ["ES2020"],
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user