@alchemy/x402 0.2.0 → 0.4.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/README.md +14 -57
- package/dist/cli/index.mjs +6 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -1
package/README.md
CHANGED
|
@@ -12,22 +12,29 @@ pnpm add @alchemy/x402
|
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
# Generate a new wallet
|
|
15
|
-
npx alchemy
|
|
15
|
+
npx @alchemy/x402 wallet generate
|
|
16
16
|
|
|
17
|
-
# Import an existing wallet (accepts key or file
|
|
18
|
-
npx alchemy
|
|
17
|
+
# Import an existing wallet (accepts hex key or path to a key file)
|
|
18
|
+
npx @alchemy/x402 wallet import --private-key 0xac09...
|
|
19
|
+
npx @alchemy/x402 wallet import --private-key /path/to/keyfile
|
|
19
20
|
|
|
20
21
|
# Generate a SIWE token
|
|
21
|
-
npx alchemy
|
|
22
|
+
npx @alchemy/x402 sign-siwe --private-key /path/to/keyfile --expires-after 1h
|
|
22
23
|
|
|
23
24
|
# Create an x402 payment from a PAYMENT-REQUIRED header
|
|
24
|
-
npx alchemy
|
|
25
|
+
npx @alchemy/x402 pay --private-key /path/to/keyfile --payment-required <header>
|
|
25
26
|
```
|
|
26
27
|
|
|
27
28
|
## Library
|
|
28
29
|
|
|
29
30
|
```ts
|
|
30
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
signSiwe,
|
|
33
|
+
generateWallet,
|
|
34
|
+
getWalletAddress,
|
|
35
|
+
createPayment,
|
|
36
|
+
buildX402Client,
|
|
37
|
+
} from "@alchemy/x402";
|
|
31
38
|
```
|
|
32
39
|
|
|
33
40
|
### Generate a wallet
|
|
@@ -90,56 +97,6 @@ All commands and functions accept private keys as:
|
|
|
90
97
|
- Raw hex string: `ac09...`
|
|
91
98
|
- File path: `/path/to/keyfile`
|
|
92
99
|
|
|
93
|
-
## Development
|
|
94
|
-
|
|
95
|
-
### Prerequisites
|
|
96
|
-
|
|
97
|
-
- Node.js >= 20
|
|
98
|
-
- [pnpm](https://pnpm.io/)
|
|
99
|
-
|
|
100
|
-
**Option A** — [mise](https://mise.jdx.dev/) (recommended, installs both Node and pnpm from `.tool-versions`):
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
mise install
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
**Option B** — [corepack](https://nodejs.org/api/corepack.html) (ships with Node, reads `packageManager` from package.json):
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
corepack enable
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### Getting started
|
|
113
|
-
|
|
114
|
-
```bash
|
|
115
|
-
git clone git@github.com:alchemyplatform/alchemy-x402.git
|
|
116
|
-
cd alchemy-x402
|
|
117
|
-
pnpm install
|
|
118
|
-
pnpm run build
|
|
119
|
-
pnpm run typecheck
|
|
120
|
-
pnpm test
|
|
121
|
-
```
|
|
122
|
-
|
|
123
100
|
## For maintainers
|
|
124
101
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
This project uses [Changesets](https://github.com/changesets/changesets) for versioning and npm publishing.
|
|
128
|
-
|
|
129
|
-
When your PR includes a user-facing change, add a changeset:
|
|
130
|
-
|
|
131
|
-
```bash
|
|
132
|
-
pnpm run changeset
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
Select the semver bump type (patch/minor/major) and describe the change. Commit the generated `.changeset/*.md` file with your PR.
|
|
136
|
-
|
|
137
|
-
### Release flow
|
|
138
|
-
|
|
139
|
-
1. Merge PRs with changeset files to `main`
|
|
140
|
-
2. CI automatically opens a "Version Packages" PR that bumps the version and updates `CHANGELOG.md`
|
|
141
|
-
3. Merge the version PR to publish to npm
|
|
142
|
-
|
|
143
|
-
### Secrets
|
|
144
|
-
|
|
145
|
-
Add an `NPM_PUBLISH_TOKEN` secret to the repo (**Settings > Secrets and variables > Actions**) with a token from [npmjs.com](https://www.npmjs.com/settings/~/tokens).
|
|
102
|
+
See [MAINTAINERS.md](./MAINTAINERS.md).
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "module";
|
|
2
3
|
import { Command } from "@commander-js/extra-typings";
|
|
3
4
|
import { randomBytes } from "crypto";
|
|
4
5
|
import ms from "ms";
|
|
@@ -63,7 +64,7 @@ async function signSiwe(opts) {
|
|
|
63
64
|
|
|
64
65
|
//#endregion
|
|
65
66
|
//#region src/cli/commands/sign-siwe.ts
|
|
66
|
-
const signSiweCommand = new Command("sign-siwe").description("Generate a SIWE authentication token").requiredOption("--private-key <key>", "Wallet private key").option("--expires-after <duration>", "Token expiration duration (e.g. 1h, 30m, 7d)", "1h").action(async (opts) => {
|
|
67
|
+
const signSiweCommand = new Command("sign-siwe").description("Generate a SIWE authentication token").requiredOption("--private-key <key-or-path>", "Wallet private key (hex string or path to a key file)").option("--expires-after <duration>", "Token expiration duration (e.g. 1h, 30m, 7d)", "1h").action(async (opts) => {
|
|
67
68
|
const token = await signSiwe({
|
|
68
69
|
privateKey: opts.privateKey,
|
|
69
70
|
expiresAfter: opts.expiresAfter
|
|
@@ -78,7 +79,7 @@ walletCommand.command("generate").description("Generate a new wallet").action(()
|
|
|
78
79
|
const wallet = generateWallet();
|
|
79
80
|
console.log(JSON.stringify(wallet, null, 2));
|
|
80
81
|
});
|
|
81
|
-
walletCommand.command("import").description("Import an existing wallet and display its address").requiredOption("--private-key <key>", "Wallet private key").action((opts) => {
|
|
82
|
+
walletCommand.command("import").description("Import an existing wallet and display its address").requiredOption("--private-key <key-or-path>", "Wallet private key (hex string or path to a key file)").action((opts) => {
|
|
82
83
|
const address = getWalletAddress(opts.privateKey);
|
|
83
84
|
console.log(JSON.stringify({ address }, null, 2));
|
|
84
85
|
});
|
|
@@ -113,7 +114,7 @@ async function createPayment(opts) {
|
|
|
113
114
|
|
|
114
115
|
//#endregion
|
|
115
116
|
//#region src/cli/commands/pay.ts
|
|
116
|
-
const payCommand = new Command("pay").description("Create an x402 payment from a PAYMENT-REQUIRED header").requiredOption("--private-key <key>", "Wallet private key").requiredOption("--payment-required <header>", "Raw PAYMENT-REQUIRED header value").action(async (opts) => {
|
|
117
|
+
const payCommand = new Command("pay").description("Create an x402 payment from a PAYMENT-REQUIRED header").requiredOption("--private-key <key-or-path>", "Wallet private key (hex string or path to a key file)").requiredOption("--payment-required <header>", "Raw PAYMENT-REQUIRED header value").action(async (opts) => {
|
|
117
118
|
const paymentHeader = await createPayment({
|
|
118
119
|
privateKey: opts.privateKey,
|
|
119
120
|
paymentRequiredHeader: opts.paymentRequired
|
|
@@ -123,7 +124,8 @@ const payCommand = new Command("pay").description("Create an x402 payment from a
|
|
|
123
124
|
|
|
124
125
|
//#endregion
|
|
125
126
|
//#region src/cli/index.ts
|
|
126
|
-
const
|
|
127
|
+
const { version } = createRequire(import.meta.url)("../../package.json");
|
|
128
|
+
const program = new Command().name("@alchemy/x402").description("CLI for Alchemy x402 authentication and payments").version(version);
|
|
127
129
|
program.addCommand(signSiweCommand);
|
|
128
130
|
program.addCommand(walletCommand);
|
|
129
131
|
program.addCommand(payCommand);
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/lib/siwe.ts","../src/lib/wallet.ts","../src/lib/payment.ts"],"mappings":";;;KAAY,GAAA;AAAA,UAEK,UAAA;EACf,UAAA,EAAY,GAAA;EACZ,OAAA,EAAS,GAAA;AAAA;AAAA,UAGM,eAAA;EACf,UAAA;EACA,YAAA;EACA,KAAA;EACA,QAAA;AAAA;AAAA,UAGe,oBAAA;EACf,UAAA;EACA,qBAAA;AAAA;;;iBCNoB,QAAA,CAAS,IAAA,EAAM,eAAA,GAAkB,OAAA;;;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/lib/siwe.ts","../src/lib/wallet.ts","../src/lib/payment.ts"],"mappings":";;;KAAY,GAAA;AAAA,UAEK,UAAA;EACf,UAAA,EAAY,GAAA;EACZ,OAAA,EAAS,GAAA;AAAA;AAAA,UAGM,eAAA;EACf,UAAA;EACA,YAAA;EACA,KAAA;EACA,QAAA;AAAA;AAAA,UAGe,oBAAA;EACf,UAAA;EACA,qBAAA;AAAA;;;iBCNoB,QAAA,CAAS,IAAA,EAAM,eAAA,GAAkB,OAAA;;;iBCavC,cAAA,CAAA,GAAkB,UAAA;AAAA,iBAMlB,gBAAA,CAAiB,UAAA,WAAqB,GAAA;;;iBCjBtC,eAAA,CAAgB,UAAA,WAAkB,UAAA;AAAA,iBA8B5B,aAAA,CACpB,IAAA,EAAM,oBAAA,GACL,OAAA"}
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/lib/siwe.ts","../src/lib/wallet.ts","../src/lib/payment.ts"],"mappings":";;;KAAY,GAAA;AAAA,UAEK,UAAA;EACf,UAAA,EAAY,GAAA;EACZ,OAAA,EAAS,GAAA;AAAA;AAAA,UAGM,eAAA;EACf,UAAA;EACA,YAAA;EACA,KAAA;EACA,QAAA;AAAA;AAAA,UAGe,oBAAA;EACf,UAAA;EACA,qBAAA;AAAA;;;iBCNoB,QAAA,CAAS,IAAA,EAAM,eAAA,GAAkB,OAAA;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/lib/siwe.ts","../src/lib/wallet.ts","../src/lib/payment.ts"],"mappings":";;;KAAY,GAAA;AAAA,UAEK,UAAA;EACf,UAAA,EAAY,GAAA;EACZ,OAAA,EAAS,GAAA;AAAA;AAAA,UAGM,eAAA;EACf,UAAA;EACA,YAAA;EACA,KAAA;EACA,QAAA;AAAA;AAAA,UAGe,oBAAA;EACf,UAAA;EACA,qBAAA;AAAA;;;iBCNoB,QAAA,CAAS,IAAA,EAAM,eAAA,GAAkB,OAAA;;;iBCavC,cAAA,CAAA,GAAkB,UAAA;AAAA,iBAMlB,gBAAA,CAAiB,UAAA,WAAqB,GAAA;;;iBCjBtC,eAAA,CAAgB,UAAA,WAAkB,UAAA;AAAA,iBA8B5B,aAAA,CACpB,IAAA,EAAM,oBAAA,GACL,OAAA"}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/lib/wallet.ts","../src/lib/siwe.ts","../src/lib/payment.ts"],"sourcesContent":["import { readFileSync, existsSync } from \"fs\";\nimport { generatePrivateKey, privateKeyToAccount } from \"viem/accounts\";\nimport type { Hex, WalletInfo } from \"../types.js\";\n\nconst RAW_HEX_RE = /^[0-9a-fA-F]{64}$/;\n\nexport function resolvePrivateKey(keyOrPath: string): string {\n if (keyOrPath.startsWith(\"0x\") || RAW_HEX_RE.test(keyOrPath)) {\n return keyOrPath;\n }\n if (existsSync(keyOrPath)) {\n return readFileSync(keyOrPath, \"utf-8\").trim();\n }\n return keyOrPath;\n}\n\nexport function normalizePrivateKey(key: string): Hex {\n const resolved = resolvePrivateKey(key);\n return resolved.startsWith(\"0x\")
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/lib/wallet.ts","../src/lib/siwe.ts","../src/lib/payment.ts"],"sourcesContent":["import { readFileSync, existsSync } from \"fs\";\nimport { generatePrivateKey, privateKeyToAccount } from \"viem/accounts\";\nimport type { Hex, WalletInfo } from \"../types.js\";\n\nconst RAW_HEX_RE = /^[0-9a-fA-F]{64}$/;\n\nexport function resolvePrivateKey(keyOrPath: string): string {\n if (keyOrPath.startsWith(\"0x\") || RAW_HEX_RE.test(keyOrPath)) {\n return keyOrPath;\n }\n if (existsSync(keyOrPath)) {\n return readFileSync(keyOrPath, \"utf-8\").trim();\n }\n return keyOrPath;\n}\n\nexport function normalizePrivateKey(key: string): Hex {\n const resolved = resolvePrivateKey(key);\n return resolved.startsWith(\"0x\")\n ? (resolved as Hex)\n : (`0x${resolved}` as Hex);\n}\n\nexport function generateWallet(): WalletInfo {\n const privateKey = generatePrivateKey();\n const account = privateKeyToAccount(privateKey);\n return { privateKey, address: account.address };\n}\n\nexport function getWalletAddress(privateKey: string): Hex {\n const normalized = normalizePrivateKey(privateKey);\n const account = privateKeyToAccount(normalized);\n return account.address;\n}\n","import { randomBytes } from \"crypto\";\nimport ms from \"ms\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport type { SignSiweOptions } from \"../types.js\";\nimport { normalizePrivateKey } from \"./wallet.js\";\n\nfunction generateNonce(): string {\n return randomBytes(16).toString(\"hex\");\n}\n\nexport async function signSiwe(opts: SignSiweOptions): Promise<string> {\n const normalized = normalizePrivateKey(opts.privateKey);\n const account = privateKeyToAccount(normalized);\n\n const issuedAt = opts.issuedAt ?? new Date().toISOString();\n const nonce = opts.nonce ?? generateNonce();\n\n const duration = ms((opts.expiresAfter ?? \"1h\") as Parameters<typeof ms>[0]);\n if (duration === undefined) {\n throw new Error(`Invalid duration: ${opts.expiresAfter}`);\n }\n const expirationTime = new Date(\n new Date(issuedAt).getTime() + duration,\n ).toISOString();\n\n const message = [\n \"x402.alchemy.com wants you to sign in with your Ethereum account:\",\n account.address,\n \"\",\n \"Sign in to Alchemy Gateway\",\n \"\",\n \"URI: https://x402.alchemy.com\",\n \"Version: 1\",\n \"Chain ID: 8453\",\n `Nonce: ${nonce}`,\n `Issued At: ${issuedAt}`,\n `Expiration Time: ${expirationTime}`,\n ].join(\"\\n\");\n\n const signature = await account.signMessage({ message });\n\n const encodedMessage = Buffer.from(message).toString(\"base64url\");\n return `${encodedMessage}.${signature}`;\n}\n","import { createPublicClient, http } from \"viem\";\nimport { base } from \"viem/chains\";\nimport { privateKeyToAccount } from \"viem/accounts\";\nimport { ExactEvmScheme, toClientEvmSigner } from \"@x402/evm\";\nimport { x402Client } from \"@x402/core/client\";\nimport {\n decodePaymentRequiredHeader,\n encodePaymentSignatureHeader,\n} from \"@x402/core/http\";\nimport type { CreatePaymentOptions } from \"../types.js\";\nimport { normalizePrivateKey } from \"./wallet.js\";\n\nexport function buildX402Client(privateKey: string) {\n const normalized = normalizePrivateKey(privateKey);\n const account = privateKeyToAccount(normalized);\n\n const publicClient = createPublicClient({\n chain: base,\n transport: http(),\n });\n\n const signer = toClientEvmSigner({\n address: account.address,\n signTypedData: async (params) => {\n return account.signTypedData(\n params as Parameters<typeof account.signTypedData>[0],\n );\n },\n readContract: async (params) => {\n return publicClient.readContract(\n params as Parameters<typeof publicClient.readContract>[0],\n );\n },\n });\n\n const scheme = new ExactEvmScheme(signer);\n const client = new x402Client();\n client.register(\"eip155:8453\", scheme);\n client.register(\"eip155:84532\", scheme);\n return client;\n}\n\nexport async function createPayment(\n opts: CreatePaymentOptions,\n): Promise<string> {\n const client = buildX402Client(opts.privateKey);\n const paymentRequired = decodePaymentRequiredHeader(\n opts.paymentRequiredHeader,\n );\n const payload = await client.createPaymentPayload(paymentRequired);\n return encodePaymentSignatureHeader(payload);\n}\n"],"mappings":";;;;;;;;;;;AAIA,MAAM,aAAa;AAEnB,SAAgB,kBAAkB,WAA2B;AAC3D,KAAI,UAAU,WAAW,KAAK,IAAI,WAAW,KAAK,UAAU,CAC1D,QAAO;AAET,KAAI,WAAW,UAAU,CACvB,QAAO,aAAa,WAAW,QAAQ,CAAC,MAAM;AAEhD,QAAO;;AAGT,SAAgB,oBAAoB,KAAkB;CACpD,MAAM,WAAW,kBAAkB,IAAI;AACvC,QAAO,SAAS,WAAW,KAAK,GAC3B,WACA,KAAK;;AAGZ,SAAgB,iBAA6B;CAC3C,MAAM,aAAa,oBAAoB;AAEvC,QAAO;EAAE;EAAY,SADL,oBAAoB,WAAW,CACT;EAAS;;AAGjD,SAAgB,iBAAiB,YAAyB;AAGxD,QADgB,oBADG,oBAAoB,WAAW,CACH,CAChC;;;;;AC1BjB,SAAS,gBAAwB;AAC/B,QAAO,YAAY,GAAG,CAAC,SAAS,MAAM;;AAGxC,eAAsB,SAAS,MAAwC;CAErE,MAAM,UAAU,oBADG,oBAAoB,KAAK,WAAW,CACR;CAE/C,MAAM,WAAW,KAAK,6BAAY,IAAI,MAAM,EAAC,aAAa;CAC1D,MAAM,QAAQ,KAAK,SAAS,eAAe;CAE3C,MAAM,WAAW,GAAI,KAAK,gBAAgB,KAAkC;AAC5E,KAAI,aAAa,OACf,OAAM,IAAI,MAAM,qBAAqB,KAAK,eAAe;CAE3D,MAAM,iBAAiB,IAAI,KACzB,IAAI,KAAK,SAAS,CAAC,SAAS,GAAG,SAChC,CAAC,aAAa;CAEf,MAAM,UAAU;EACd;EACA,QAAQ;EACR;EACA;EACA;EACA;EACA;EACA;EACA,UAAU;EACV,cAAc;EACd,oBAAoB;EACrB,CAAC,KAAK,KAAK;CAEZ,MAAM,YAAY,MAAM,QAAQ,YAAY,EAAE,SAAS,CAAC;AAGxD,QAAO,GADgB,OAAO,KAAK,QAAQ,CAAC,SAAS,YAAY,CACxC,GAAG;;;;;AC9B9B,SAAgB,gBAAgB,YAAoB;CAElD,MAAM,UAAU,oBADG,oBAAoB,WAAW,CACH;CAE/C,MAAM,eAAe,mBAAmB;EACtC,OAAO;EACP,WAAW,MAAM;EAClB,CAAC;CAgBF,MAAM,SAAS,IAAI,eAdJ,kBAAkB;EAC/B,SAAS,QAAQ;EACjB,eAAe,OAAO,WAAW;AAC/B,UAAO,QAAQ,cACb,OACD;;EAEH,cAAc,OAAO,WAAW;AAC9B,UAAO,aAAa,aAClB,OACD;;EAEJ,CAAC,CAEuC;CACzC,MAAM,SAAS,IAAI,YAAY;AAC/B,QAAO,SAAS,eAAe,OAAO;AACtC,QAAO,SAAS,gBAAgB,OAAO;AACvC,QAAO;;AAGT,eAAsB,cACpB,MACiB;CACjB,MAAM,SAAS,gBAAgB,KAAK,WAAW;CAC/C,MAAM,kBAAkB,4BACtB,KAAK,sBACN;AAED,QAAO,6BADS,MAAM,OAAO,qBAAqB,gBAAgB,CACtB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alchemy/x402",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "CLI and library for Alchemy x402 authentication and payments",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"bin": {
|
|
19
19
|
"alchemy-x402": "./dist/cli/index.mjs"
|
|
20
20
|
},
|
|
21
|
+
"lint-staged": {
|
|
22
|
+
"*.{js,ts,mjs,mts,cjs,cts}": "eslint --fix",
|
|
23
|
+
"*.{json,md,yml,yaml}": "prettier --write"
|
|
24
|
+
},
|
|
21
25
|
"dependencies": {
|
|
22
26
|
"@commander-js/extra-typings": "^14.0.0",
|
|
23
27
|
"@x402/core": "^2.4.0",
|
|
@@ -30,10 +34,17 @@
|
|
|
30
34
|
"devDependencies": {
|
|
31
35
|
"@changesets/changelog-github": "^0.5.2",
|
|
32
36
|
"@changesets/cli": "^2.29.8",
|
|
37
|
+
"@eslint/js": "^10.0.1",
|
|
33
38
|
"@types/ms": "^2.1.0",
|
|
34
39
|
"@types/node": "^25.3.0",
|
|
40
|
+
"eslint": "^10.0.2",
|
|
41
|
+
"eslint-config-prettier": "^10.1.8",
|
|
42
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
43
|
+
"lint-staged": "^16.2.7",
|
|
44
|
+
"prettier": "^3.8.1",
|
|
35
45
|
"tsdown": "^0.20.3",
|
|
36
46
|
"typescript": "^5.9.3",
|
|
47
|
+
"typescript-eslint": "^8.56.1",
|
|
37
48
|
"vitest": "^4.0.18"
|
|
38
49
|
},
|
|
39
50
|
"files": [
|
|
@@ -50,6 +61,8 @@
|
|
|
50
61
|
"build": "tsdown",
|
|
51
62
|
"typecheck": "tsc --noEmit",
|
|
52
63
|
"test": "vitest run",
|
|
64
|
+
"lint": "eslint . && prettier --check .",
|
|
65
|
+
"lint:fix": "eslint . --fix && prettier --write .",
|
|
53
66
|
"changeset": "changeset",
|
|
54
67
|
"version-packages": "changeset version",
|
|
55
68
|
"release": "pnpm run build && changeset publish"
|