Skip to content

Commit edaf792

Browse files
committedFeb 22, 2024
init: project
0 parents  commit edaf792

25 files changed

+3941
-0
lines changed
 

‎.gitignore

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# editors
4+
**/.idea
5+
**/.vscode
6+
7+
# dependencies
8+
**/node_modules
9+
**/.pnp
10+
**/.pnp.js
11+
12+
# testing
13+
**/coverage
14+
**/tmp
15+
16+
# production
17+
**/lib
18+
**/cjs
19+
**/dist
20+
**/build
21+
**/*.tsbuildinfo
22+
23+
# generated
24+
**/.next
25+
**/.turbo
26+
27+
# misc
28+
.DS_Store
29+
.env.local
30+
.env.development.local
31+
.env.production.local
32+
.env.test.local
33+
**/.npmrc
34+
35+
# logs
36+
npm-debug.log*
37+
yarn-debug.log*
38+
yarn-error.log*

‎.prettierignore

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# editors
2+
**/.git
3+
**/.idea
4+
**/.vscode
5+
6+
# dependencies
7+
**/node_modules
8+
**/.pnp.js
9+
**/.pnp
10+
11+
# testing
12+
**/coverage
13+
14+
# production
15+
**/dist
16+
**/build
17+
**/tsconfig.tsbuildinfo
18+
19+
# misc
20+
**/.DS_Store
21+
**/.env.local
22+
**/.env.development.local
23+
**/.env.test.local
24+
**/.env.production.local
25+
26+
**/npm-debug.log*
27+
**/yarn-debug.log*
28+
**/yarn-error.log*
29+
30+
# moleculec
31+
**/*.mol
32+
33+
# generated typing
34+
**/.next
35+
**/next-env.d.ts
36+
**/auto-imports.d.ts
37+
**/vite-imports.d.ts

‎.prettierrc

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"semi": true,
3+
"tabWidth": 2,
4+
"useTabs": false,
5+
"singleQuote": true,
6+
"jsxSingleQuote": false,
7+
"trailingComma": "all",
8+
"endOfLine": "lf",
9+
"printWidth": 120
10+
}

‎package.json

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "btc-wallet-poc",
3+
"private": true,
4+
"workspaces": [
5+
"packages/*"
6+
],
7+
"scripts": {
8+
"build": "turbo run build",
9+
"build:packages": "turbo run build --filter=./packages/*",
10+
"lint:fix": "turbo run lint:fix",
11+
"lint:fix-all": "prettier --write '{packages,apps}/**/*.{js,jsx,ts,tsx,md,json}'",
12+
"clean": "turbo run clean",
13+
"clean:packages": "turbo run clean --filter=./packags/*",
14+
"clean:dependencies": "pnpm clean:sub-dependencies && rimraf node_modules",
15+
"clean:sub-dependencies": "rimraf packages/**/node_modules apps/**/node_modules"
16+
},
17+
"devDependencies": {
18+
"@changesets/cli": "^2.27.1",
19+
"typescript": "^5.3.3",
20+
"prettier": "^3.2.5",
21+
"rimraf": "^5.0.5",
22+
"turbo": "^1.12.4"
23+
},
24+
"packageManager": "^pnpm@8.0.0",
25+
"engines": {
26+
"node": ">=18.0.0",
27+
"pnpm": ">=8.0.0"
28+
}
29+
}

‎packages/sdk/.env.example

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
VITE_BTC_RPC_URL=https://btc_rpc.url
2+
VITE_BTC_RPC_USER=user:password
3+
4+
VITE_OPENAPI_URL=https://open_api.url
5+
VITE_OPENAPI_KEY=key

‎packages/sdk/package.json

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "btc-wallet-poc-sdk",
3+
"version": "0.1.0",
4+
"scripts": {
5+
"test": "vitest",
6+
"build": "tsc -p tsconfig.build.json",
7+
"lint": "prettier --check '{src,tests}/**/*.{js,jsx,ts,tsx}'",
8+
"lint:fix": "prettier --write '{src,tests}/**/*.{js,jsx,ts,tsx}'",
9+
"clean": "pnpm run clean:cache & pnpm run clean:build",
10+
"clean:build": "rimraf lib && pnpm run clean:buildinfo",
11+
"clean:buildinfo": "rimraf tsconfig.*tsbuildinfo",
12+
"clean:cache": "rimraf .turbo"
13+
},
14+
"main": "lib",
15+
"files": [
16+
"lib"
17+
],
18+
"dependencies": {
19+
"bip32": "^4.0.0",
20+
"bitcoinjs-lib": "^6.1.5",
21+
"ecpair": "^2.1.0",
22+
"lodash": "^4.17.21",
23+
"tiny-secp256k1": "^2.2.3"
24+
},
25+
"devDependencies": {
26+
"vitest": "^1.2.0"
27+
},
28+
"publishConfig": {
29+
"access": "public"
30+
}
31+
}

‎packages/sdk/src/api/sendBtc.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { NetworkType } from '../network';
2+
import { TxBuild } from '../transaction/build';
3+
import { UniSatOpenApi } from '../query/uniSatOpenApi';
4+
5+
export async function sendBtc(props: {
6+
from: string;
7+
tos: {
8+
address: string;
9+
value: number;
10+
}[];
11+
openApi: UniSatOpenApi;
12+
networkType: NetworkType;
13+
changeAddress?: string;
14+
fee: number;
15+
}) {
16+
const tx = new TxBuild({
17+
openApi: props.openApi,
18+
changeAddress: props.changeAddress ?? props.from,
19+
networkType: props.networkType,
20+
fee: props.fee,
21+
});
22+
23+
props.tos.forEach((to) => {
24+
tx.addOutput(to.address, to.value);
25+
});
26+
27+
await tx.collectInputs(props.from);
28+
return tx.toPsbt();
29+
}

‎packages/sdk/src/constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const MIN_COLLECTABLE_SATOSHIS = 546;

‎packages/sdk/src/error.ts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export enum ErrorCodes {
2+
UNKNOWN,
3+
INSUFFICIENT_BTC_UTXO,
4+
UNSUPPORTED_ADDRESS_TYPE,
5+
}
6+
7+
export const ErrorMessages = {
8+
[ErrorCodes.UNKNOWN]: "Unknown error",
9+
[ErrorCodes.INSUFFICIENT_BTC_UTXO]: "Insufficient btc utxo",
10+
[ErrorCodes.UNSUPPORTED_ADDRESS_TYPE]: "Unsupported address type",
11+
};
12+
13+
export class TxBuildError extends Error {
14+
public code = ErrorCodes.UNKNOWN;
15+
constructor(
16+
code: ErrorCodes,
17+
message = ErrorMessages[code] || "Unknown error"
18+
) {
19+
super(message);
20+
this.code = code;
21+
Object.setPrototypeOf(this, TxBuildError.prototype);
22+
}
23+
}

‎packages/sdk/src/index.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export * from './types';
2+
export * from './error';
3+
export * from './network';
4+
export * from './constants';
5+
6+
export * from './api/sendBtc';
7+
8+
export * from './query/btcRpc';
9+
export * from './query/uniSatOpenApi';

‎packages/sdk/src/network.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import bitcoin from 'bitcoinjs-lib';
2+
3+
export enum NetworkType {
4+
MAINNET,
5+
TESTNET,
6+
REGTEST,
7+
}
8+
9+
/**
10+
* Convert network type to bitcoinjs-lib network.
11+
*/
12+
export function toPsbtNetwork(networkType: NetworkType): bitcoin.Network {
13+
if (networkType === NetworkType.MAINNET) {
14+
return bitcoin.networks.bitcoin;
15+
} else if (networkType === NetworkType.TESTNET) {
16+
return bitcoin.networks.testnet;
17+
} else {
18+
return bitcoin.networks.regtest;
19+
}
20+
}
21+
22+
/**
23+
* Convert bitcoinjs-lib network to network type.
24+
*/
25+
export function toNetworkType(network: bitcoin.Network): NetworkType {
26+
if (network.bech32 == bitcoin.networks.bitcoin.bech32) {
27+
return NetworkType.MAINNET;
28+
} else if (network.bech32 == bitcoin.networks.testnet.bech32) {
29+
return NetworkType.TESTNET;
30+
} else {
31+
return NetworkType.REGTEST;
32+
}
33+
}

‎packages/sdk/src/query/btcRpc.ts

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
export interface RpcRequestBody {
2+
method: string;
3+
params: any[];
4+
}
5+
6+
export interface RpcResponse<T> {
7+
result: T;
8+
id: number | null;
9+
error: string | null;
10+
}
11+
12+
export class BtcRpc {
13+
public rpcUrl: string;
14+
private readonly rpcUser: string;
15+
16+
constructor(rpcUrl: string, rpcUser: string) {
17+
this.rpcUrl = rpcUrl;
18+
this.rpcUser = btoa(rpcUser);
19+
}
20+
21+
async request<T, R extends RpcResponse<T>>(body: RpcRequestBody): Promise<R> {
22+
const res = await fetch(this.rpcUrl, {
23+
method: 'POST',
24+
headers: {
25+
'Authorization': 'Basic ' + this.rpcUser,
26+
'Content-Type': 'application/json',
27+
},
28+
body: JSON.stringify(body),
29+
});
30+
31+
return await res.json();
32+
}
33+
34+
getBlockchainInfo() {
35+
return this.request({
36+
method: 'getblockchaininfo',
37+
params: [],
38+
});
39+
}
40+
41+
sendRawTransaction(txHex: string) {
42+
return this.request({
43+
method: 'sendrawtransaction',
44+
params: [txHex],
45+
});
46+
}
47+
}
+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
export interface UniSatOpenApiRequestOptions {
2+
params?: Record<string, any>;
3+
}
4+
5+
export interface UniSatOpenApiResponse<T> {
6+
data: T;
7+
msg: string;
8+
code: number;
9+
}
10+
11+
interface UniSatApiBalance {
12+
address: string;
13+
satoshi: number;
14+
pendingSatoshi: number;
15+
utxoCount: number;
16+
btcSatoshi: number;
17+
btcPendingSatoshi: number;
18+
btcUtxoCount: number;
19+
inscriptionSatoshi: number;
20+
inscriptionPendingSatoshi: number;
21+
inscriptionUtxoCount: number;
22+
}
23+
24+
export interface UniSatApiUtxoList {
25+
cursor: 0,
26+
total: 1,
27+
totalConfirmed: 1,
28+
totalUnconfirmed: 0,
29+
totalUnconfirmedSpend: 0,
30+
utxo: UniSatApiUtxo[];
31+
}
32+
export interface UniSatApiUtxo {
33+
txid: string;
34+
vout: number;
35+
satoshi: number;
36+
scriptType: string;
37+
scriptPk: string;
38+
codeType: number;
39+
address: string;
40+
height: number;
41+
idx: number;
42+
isOpInRBF: boolean;
43+
isSpent: boolean;
44+
inscriptions: [];
45+
}
46+
47+
export class UniSatOpenApi {
48+
public apiUrl: string;
49+
private readonly apiKey: string;
50+
51+
constructor(apiUrl: string, apiKey: string) {
52+
this.apiUrl = apiUrl;
53+
this.apiKey = apiKey;
54+
}
55+
56+
async request<T, R extends UniSatOpenApiResponse<T> = UniSatOpenApiResponse<T>>(
57+
route: string,
58+
options?: UniSatOpenApiRequestOptions
59+
): Promise<R> {
60+
const params = options?.params ? '?' + new URLSearchParams(options.params).toString() : '';
61+
const res = await fetch(`${this.apiUrl}/${route}${params}`, {
62+
method: 'GET',
63+
headers: {
64+
'Authorization': `Bearer ${this.apiKey}`,
65+
},
66+
});
67+
68+
return await res.json();
69+
}
70+
71+
getBalance(address: string) {
72+
return this.request<UniSatApiBalance>(`/v1/indexer/address/${address}/balance`);
73+
}
74+
75+
getUtxos(address: string, cursor?: number, size?: number) {
76+
return this.request<UniSatApiUtxoList>(`/v1/indexer/address/${address}/utxo-data`, {
77+
params: {
78+
cursor: cursor ?? 0,
79+
size: size ?? 50,
80+
},
81+
});
82+
}
83+
}

‎packages/sdk/src/transaction/build.ts

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import bitcoin from 'bitcoinjs-lib';
2+
import { AddressType, UnspentOutput } from '../types';
3+
import { UniSatOpenApi } from '../query/uniSatOpenApi';
4+
import { NetworkType, toPsbtNetwork } from '../network';
5+
import { ErrorCodes, TxBuildError } from '../error';
6+
import { collectSatoshis } from './utxo';
7+
8+
interface TxInput {
9+
data: {
10+
hash: string;
11+
index: number;
12+
witnessUtxo: { value: number; script: Buffer };
13+
tapInternalKey?: Buffer;
14+
};
15+
utxo: UnspentOutput;
16+
}
17+
18+
interface TxOutput {
19+
address: string;
20+
value: number;
21+
}
22+
23+
export class TxBuild {
24+
inputs: TxInput[] = [];
25+
outputs: TxOutput[] = [];
26+
27+
networkType: NetworkType;
28+
changedAddress: string;
29+
fee: number;
30+
31+
openApi: UniSatOpenApi;
32+
33+
constructor(props: {
34+
openApi: UniSatOpenApi,
35+
networkType: NetworkType,
36+
changeAddress: string,
37+
fee: number,
38+
}) {
39+
this.openApi = props.openApi;
40+
41+
this.networkType = props.networkType;
42+
this.changedAddress = props.changeAddress;
43+
this.fee = props.fee;
44+
}
45+
46+
addInput(utxo: UnspentOutput) {
47+
this.inputs.push(utxoToInput(utxo));
48+
}
49+
50+
addOutput(address: string, value: number) {
51+
this.outputs.push({
52+
address,
53+
value,
54+
});
55+
}
56+
57+
async collectInputs(address: string) {
58+
const outputAmount = this.outputs.reduce((acc, output) => acc + output.value, 0);
59+
const neededAmount = outputAmount + this.fee;
60+
61+
const { utxos, satoshi } = await collectSatoshis(this.openApi, address, neededAmount, this.networkType);
62+
utxos.forEach((utxo) => {
63+
this.addInput(utxo);
64+
});
65+
66+
if (satoshi > neededAmount) {
67+
const change = satoshi - neededAmount;
68+
this.addOutput(this.changedAddress, change);
69+
}
70+
}
71+
72+
toPsbt() {
73+
const network = toPsbtNetwork(this.networkType);
74+
const psbt = new bitcoin.Psbt({ network });
75+
this.inputs.forEach((v, index) => {
76+
psbt.data.addInput(v.data);
77+
});
78+
this.outputs.forEach((v) => {
79+
psbt.addOutput(v);
80+
});
81+
return psbt;
82+
}
83+
}
84+
85+
export function utxoToInput(utxo: UnspentOutput): TxInput {
86+
// TODO: support other address types, currently only P2WPKH
87+
if (utxo.addressType === AddressType.P2WPKH) {
88+
const data = {
89+
hash: utxo.txid,
90+
index: utxo.vout,
91+
witnessUtxo: {
92+
value: utxo.satoshi,
93+
script: Buffer.from(utxo.scriptPk, 'hex'),
94+
},
95+
};
96+
97+
return {
98+
data,
99+
utxo,
100+
};
101+
}
102+
103+
throw new TxBuildError(ErrorCodes.UNSUPPORTED_ADDRESS_TYPE);
104+
}

‎packages/sdk/src/transaction/utxo.ts

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import bitcoin from 'bitcoinjs-lib';
2+
import { UniSatOpenApi } from '../query/uniSatOpenApi';
3+
import { ErrorCodes, TxBuildError } from '../error';
4+
import { AddressType, UnspentOutput } from '../types';
5+
import { NetworkType, toPsbtNetwork } from '../network';
6+
import { MIN_COLLECTABLE_SATOSHIS } from '../constants';
7+
8+
export async function collectSatoshis(openApi: UniSatOpenApi, address: string, satoshi: number, networkType: NetworkType): Promise<{
9+
utxos: UnspentOutput[];
10+
satoshi: number;
11+
}> {
12+
const res = await openApi.getUtxos(address);
13+
const utxos = res.data.utxo;
14+
15+
const collected = [];
16+
let collectedAmount = 0;
17+
for (const utxo of utxos) {
18+
if (utxo.satoshi < MIN_COLLECTABLE_SATOSHIS) {
19+
continue;
20+
}
21+
if (collectedAmount >= satoshi) {
22+
break;
23+
}
24+
collected.push(utxo);
25+
collectedAmount += utxo.satoshi;
26+
}
27+
28+
if (collectedAmount < satoshi) {
29+
throw new TxBuildError(ErrorCodes.INSUFFICIENT_BTC_UTXO);
30+
}
31+
32+
const convertedUtxos = collected.map((utxo) => {
33+
return {
34+
txid: utxo.txid,
35+
vout: utxo.vout,
36+
satoshi: utxo.satoshi,
37+
scriptPk: utxo.scriptPk,
38+
pubkey: bitcoin.address.fromOutputScript(
39+
bitcoin.address.toOutputScript(address, toPsbtNetwork(networkType)),
40+
toPsbtNetwork(networkType)
41+
).toString(),
42+
// TODO: support other address types
43+
addressType: AddressType.P2WPKH,
44+
};
45+
});
46+
47+
return {
48+
utxos: convertedUtxos,
49+
satoshi: collectedAmount,
50+
};
51+
}

‎packages/sdk/src/types.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export interface UnspentOutput {
2+
txid: string;
3+
vout: number;
4+
satoshi: number;
5+
scriptPk: string;
6+
pubkey: string;
7+
addressType: AddressType;
8+
}
9+
10+
export enum AddressType {
11+
P2PKH,
12+
P2WPKH,
13+
P2TR,
14+
P2SH_P2WPKH,
15+
M44_P2WPKH, // deprecated
16+
M44_P2TR, // deprecated
17+
P2WSH,
18+
P2SH,
19+
UNKNOWN,
20+
}

‎packages/sdk/tests/Address.test.ts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import bitcoin from 'bitcoinjs-lib';
2+
import { describe, expect, it } from 'vitest';
3+
import { ECPair, network } from './shared/env';
4+
5+
describe('Address', () => {
6+
it('Create SegWit address', () => {
7+
const keyPair = ECPair.fromPrivateKey(
8+
Buffer.from('8d3c23d340ac0841e6c3b58a9bbccb9a28e94ab444f972cff35736fa2fcf9f3f', 'hex'),
9+
{ network }
10+
);
11+
12+
expect(keyPair.publicKey.toString('hex')).toEqual('037dff8ff2e0bd222690d785f9277e0c4800fc88b0fad522f1442f21a8226253ce');
13+
14+
const { address } = bitcoin.payments.p2wpkh({
15+
pubkey: keyPair.publicKey,
16+
network,
17+
});
18+
19+
expect(address).toEqual('tb1qm06rvrq8jyyckzc5v709u7qpthel9j4d9f7nh3');
20+
});
21+
});

‎packages/sdk/tests/Query.test.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { describe, it } from 'vitest';
2+
import { accounts, btcRpc, openApi } from './shared/env';
3+
4+
describe('Query', () => {
5+
it('Get BlockchainInfo', async () => {
6+
const res = await btcRpc.getBlockchainInfo();
7+
console.log(res);
8+
});
9+
it('Get balance', async () => {
10+
const res = await openApi.getBalance(accounts.charlie.p2wpkh.address);
11+
console.log(res);
12+
});
13+
it('Get Non-inscription UTXOs', async () => {
14+
const res = await openApi.getUtxos(accounts.charlie.p2wpkh.address);
15+
console.log(res.data.utxo);
16+
});
17+
});
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { describe, it } from 'vitest';
2+
import { accounts, btcRpc, openApi } from './shared/env';
3+
import { MIN_COLLECTABLE_SATOSHIS, NetworkType, sendBtc } from '../src';
4+
5+
describe('Transaction', () => {
6+
it('Create SegWit BTC transfer', async () => {
7+
const psbt = await sendBtc({
8+
from: accounts.charlie.p2wpkh.address,
9+
tos: [
10+
{
11+
address: accounts.charlie.p2wpkh.address,
12+
value: 1000,
13+
},
14+
],
15+
networkType: NetworkType.TESTNET,
16+
fee: 200,
17+
openApi,
18+
});
19+
20+
console.log(psbt.txOutputs);
21+
22+
// Sign & finalize inputs
23+
psbt.signAllInputs(accounts.charlie.keyPair);
24+
psbt.finalizeAllInputs();
25+
26+
// Broadcast transaction
27+
const tx = psbt.extractTransaction();
28+
const res = await btcRpc.sendRawTransaction(tx.toHex());
29+
console.log(res);
30+
});
31+
});

‎packages/sdk/tests/shared/env.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import ECPairFactory from 'ecpair';
2+
import bitcoin from 'bitcoinjs-lib';
3+
import * as ecc from 'tiny-secp256k1';
4+
import { BtcRpc, UniSatOpenApi } from '../../src';
5+
6+
export const ECPair = ECPairFactory(ecc);
7+
export const network = bitcoin.networks.testnet;
8+
9+
export const btcRpc = new BtcRpc(process.env.VITE_BTC_RPC_URL!, process.env.VITE_BTC_RPC_USER!);
10+
11+
export const openApi = new UniSatOpenApi(process.env.VITE_OPENAPI_URL!, process.env.VITE_OPENAPI_KEY!);
12+
13+
export const accounts = {
14+
charlie: createAccount('8d3c23d340ac0841e6c3b58a9bbccb9a28e94ab444f972cff35736fa2fcf9f3f', network),
15+
};
16+
17+
function createAccount(privateKey: string, _network?: bitcoin.Network) {
18+
const keyPair = ECPair.fromPrivateKey(
19+
Buffer.from(privateKey, 'hex'),
20+
{ network: _network });
21+
const p2wpkh = bitcoin.payments.p2wpkh({
22+
pubkey: keyPair.publicKey,
23+
network: _network,
24+
});
25+
26+
return {
27+
privateKey,
28+
keyPair,
29+
p2wpkh: {
30+
address: p2wpkh.address!,
31+
pubkey: p2wpkh.pubkey!,
32+
data: p2wpkh.data!,
33+
},
34+
};
35+
}

‎packages/sdk/tsconfig.build.json

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"compilerOptions": {
4+
"module": "CommonJS",
5+
"rootDir": "src",
6+
"outDir": "lib",
7+
"noEmit": false
8+
},
9+
"exclude": ["tests"]
10+
}

‎packages/sdk/tsconfig.json

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"compilerOptions": {
3+
"moduleResolution": "Node",
4+
"module": "ESNext",
5+
"target": "ESNext",
6+
"lib": [
7+
"ESNext",
8+
"DOM"
9+
],
10+
"strict": true,
11+
"noEmit": true,
12+
"allowJs": true,
13+
"sourceMap": true,
14+
"composite": true,
15+
"skipLibCheck": true,
16+
"esModuleInterop": true,
17+
"strictNullChecks": true,
18+
"resolveJsonModule": true,
19+
"isolatedModules": true
20+
},
21+
"exclude": [
22+
"node_modules",
23+
"lib"
24+
]
25+
}

‎pnpm-lock.yaml

+3,223
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎pnpm-workspace.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
packages:
2+
- "packages/*"
3+
- "apps/*"

‎turbo.json

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"$schema": "https://turbo.build/schema.json",
3+
"globalDependencies": ["**/.env.*local"],
4+
"pipeline": {
5+
"dev": {
6+
"cache": false,
7+
"persistent": true,
8+
"dependsOn": ["clean", "^build"]
9+
},
10+
"build": {
11+
"outputs": ["lib/**", "dist/**", ".next/**", "!.next/cache/**"],
12+
"dependsOn": ["^build"],
13+
"cache": false
14+
},
15+
"test": {
16+
"outputs": ["coverage/**"],
17+
"dependsOn": []
18+
},
19+
"lint:fix": {
20+
"cache": false
21+
},
22+
"clean": {
23+
"cache": false
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)
Please sign in to comment.