anhedral 0.1.6 → 0.1.7

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 CHANGED
@@ -1,74 +1,92 @@
1
- # anhedral
1
+ # Anhedral
2
2
 
3
- Opinionated init CLI for product teams shipping one stable stack: Expo + React Native Reusables, Fastify, WXT, Drizzle, Neon, Clerk, RevenueCat + Stripe, Cloudflare R2/CDN, and Vercel.
3
+ [![npm version](https://img.shields.io/npm/v/anhedral.svg)](https://www.npmjs.com/package/anhedral)
4
+ [![license](https://img.shields.io/npm/l/anhedral.svg)](./LICENSE)
5
+ [![node](https://img.shields.io/node/v/anhedral.svg)](https://www.npmjs.com/package/anhedral)
4
6
 
5
- ## Requirements
7
+ One command to start a production-shaped Expo, Fastify, and Chrome extension application.
6
8
 
7
- - Node.js 20+
8
- - `pnpm` available on your machine
9
+ Anhedral is an opinionated init CLI for teams that want one stable full-stack architecture instead of a menu of half-finished choices. It generates a single pnpm monorepo with an Expo app for web/iOS/Android, a Fastify API, a WXT Chrome extension, shared packages, and deployment-ready structure for Vercel, EAS, and the Chrome Web Store.
9
10
 
10
- ## Usage
11
+ ![Anhedral init CLI](docs/anhedral-cli-init.png)
11
12
 
12
- Run the latest published CLI directly without installing it globally:
13
+ ## Quick Start
13
14
 
14
15
  ```sh
15
16
  pnpm dlx anhedral@latest init
16
17
  ```
17
18
 
19
+ Or with npm:
20
+
18
21
  ```sh
19
22
  npx anhedral@latest init
20
23
  ```
21
24
 
22
- Use `--skip-install` when you want deterministic file generation without running dependency installation during init:
25
+ Use `--skip-install` when you want deterministic file generation before installing dependencies:
23
26
 
24
27
  ```sh
25
28
  pnpm dlx anhedral@latest init --skip-install
26
29
  pnpm install
27
30
  ```
28
31
 
29
- Global install also works:
32
+ ## What It Generates
30
33
 
31
- ```sh
32
- pnpm add -g anhedral@latest
33
- anhedral init
34
+ ```txt
35
+ .
36
+ ├─ Frontend/ Expo + React Native Reusables app
37
+ ├─ Backend/ Fastify API
38
+ ├─ Extension/ WXT Chrome extension
39
+ ├─ packages/
40
+ │ ├─ api-client/ shared typed API client
41
+ │ ├─ config/ shared config helpers
42
+ │ ├─ db/ Drizzle schema, Neon client, migrations
43
+ │ └─ types/ shared TypeScript types
44
+ ├─ PRODUCTION.md deployment checklist
45
+ ├─ turbo.json workspace task graph
46
+ └─ package.json root scripts
34
47
  ```
35
48
 
36
- ```sh
37
- npm install -g anhedral@latest
38
- anhedral init
39
- ```
49
+ The generated app is intentionally one repository. Deploy the same Git repo as two Vercel projects:
50
+
51
+ - `Frontend`: Expo web export, built with `pnpm build:web`, output to `dist`
52
+ - `Backend`: Fastify API, built with `pnpm build`, entrypoint at `src/index.ts`
53
+
54
+ The same `Frontend` source is used for native iOS and Android builds through EAS. The `Extension` source builds a WXT Chrome extension ZIP for the Chrome Web Store.
40
55
 
41
56
  ## Stack
42
57
 
43
- `init` generates the only supported stack:
58
+ Anhedral supports one carefully maintained stack:
44
59
 
45
60
  - Expo + React Native Reusables frontend
46
61
  - Fastify backend
47
- - WXT extension
62
+ - WXT Chrome extension
48
63
  - Neon + Drizzle
49
64
  - Cloudflare R2/CDN
50
65
  - Clerk auth
51
66
  - RevenueCat + Stripe
52
- - Vercel deploy
53
-
54
- The scaffold is a pnpm workspace with top-level `Frontend`, `Backend`, `Extension`, and `packages/*` folders. Shared code such as the Drizzle schema lives under `packages/db` so apps do not need separate database schemas.
67
+ - Vercel deployment
68
+ - pnpm workspaces + Turborepo
55
69
 
56
- Each scaffold writes a `stack.json` file with the selected architecture, generated outputs, and the verified dependency manifest used for that run.
70
+ The goal is stability. The generated project avoids framework sprawl, duplicate schemas, and separate frontend/backend/extension repositories.
57
71
 
58
- ## Vercel Deployment
59
-
60
- The generated workspace is one Git repository with two Vercel projects:
72
+ ## Local Development
61
73
 
62
- - `Frontend`: Expo web build. Build command: `pnpm build:web`. Output directory: `dist`.
63
- - `Backend`: Fastify API. Build command: `pnpm build`. Vercel detects `src/index.ts` as the Fastify entrypoint.
74
+ From the generated project root:
64
75
 
65
- The `Frontend` source is also the EAS source for iOS and Android builds; Vercel only deploys its web export.
76
+ ```sh
77
+ pnpm install
78
+ pnpm dev
79
+ ```
66
80
 
67
- Import the same repository twice in Vercel and set each project Root Directory to the app path above. Enable source access outside the app root so `packages/*` workspace dependencies are available during builds.
81
+ Run one surface at a time:
68
82
 
69
- Vercel deployments are Git-driven. After both projects are connected, pushes and pull requests deploy the Expo web app and Fastify API automatically from GitHub commits.
83
+ ```sh
84
+ pnpm dev:frontend
85
+ pnpm dev:backend
86
+ pnpm dev:extension
87
+ ```
70
88
 
71
- Generated projects also include `PRODUCTION.md` with the provider checklist and these root verification scripts:
89
+ Verify before deployment:
72
90
 
73
91
  ```sh
74
92
  pnpm verify
@@ -77,98 +95,71 @@ pnpm verify:backend
77
95
  pnpm verify:extension
78
96
  ```
79
97
 
80
- ## Generated Provider Setup
98
+ Database workflow:
99
+
100
+ ```sh
101
+ pnpm db:generate
102
+ pnpm db:migrate
103
+ ```
104
+
105
+ ## Provider Setup
81
106
 
82
- New projects ship with placeholder env values so the repository can be inspected before accounts are provisioned. A production UI requires real keys from each provider. The generated `README.md` and `PRODUCTION.md` now include setup links and exact env placement for:
107
+ Generated projects include placeholder env values so the repository can be inspected before accounts are provisioned. Real auth, billing, uploads, native builds, and extension sign-in require provider keys.
83
108
 
84
- - Neon database: `DATABASE_URL`
85
- - Clerk auth: `EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY`, `CLERK_PUBLISHABLE_KEY`, `CLERK_SECRET_KEY`, `VITE_CLERK_PUBLISHABLE_KEY`
109
+ The generated `README.md` and `PRODUCTION.md` explain where every key belongs:
110
+
111
+ - Neon: `DATABASE_URL`
112
+ - Clerk: `EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY`, `CLERK_PUBLISHABLE_KEY`, `CLERK_SECRET_KEY`, `VITE_CLERK_PUBLISHABLE_KEY`
86
113
  - RevenueCat + Stripe: `EXPO_PUBLIC_RC_*`, `RC_SECRET_API_KEY`, `RC_WEBHOOK_SECRET`
87
114
  - Cloudflare R2/CDN: `R2_ACCOUNT_ID`, `R2_ACCESS_KEY_ID`, `R2_SECRET_ACCESS_KEY`, `R2_BUCKET`
88
- - Vercel: two projects from the same Git repo, rooted at `Frontend` and `Backend`
115
+ - Vercel: two projects from the same Git repository, rooted at `Frontend` and `Backend`
89
116
  - EAS: native iOS and Android builds from `Frontend`
90
117
  - Chrome Web Store: WXT ZIP upload from `Extension/.output`
91
118
 
92
- ## Native App Deployment
119
+ ## Deployment
93
120
 
94
- Use `Frontend` as the Expo project root for iOS and Android. Vercel only deploys the web export from this app; native builds go through EAS.
121
+ ### Vercel
95
122
 
96
- ```sh
97
- cd Frontend
98
- pnpm dlx eas-cli@latest login
99
- pnpm dlx eas-cli@latest init
100
- ```
123
+ Import the same GitHub repository twice:
101
124
 
102
- The scaffold generates `Frontend/eas.json` with `development`, `preview`, and `production` profiles.
125
+ | Project | Root Directory | Build Command | Output |
126
+ | --- | --- | --- | --- |
127
+ | Frontend | `Frontend` | `pnpm build:web` | `dist` |
128
+ | Backend | `Backend` | `pnpm build` | Fastify entrypoint |
103
129
 
104
- Build commands:
130
+ Enable Vercel access to source files outside each project root so workspace packages under `packages/*` resolve during builds.
105
131
 
106
- ```sh
107
- # Android
108
- pnpm dlx eas-cli@latest build --platform android --profile production
132
+ ### EAS
109
133
 
110
- # iOS
111
- pnpm dlx eas-cli@latest build --platform ios --profile production
112
-
113
- # Both platforms
114
- pnpm dlx eas-cli@latest build --platform all --profile production
115
- ```
116
-
117
- Submit latest successful builds to the stores:
134
+ Use `Frontend` as the Expo project root:
118
135
 
119
136
  ```sh
120
- # Google Play
121
- pnpm dlx eas-cli@latest submit --platform android --latest --profile production
122
-
123
- # App Store Connect / TestFlight
124
- pnpm dlx eas-cli@latest submit --platform ios --latest --profile production
125
-
126
- # Both platforms
137
+ cd Frontend
138
+ pnpm dlx eas-cli@latest login
139
+ pnpm dlx eas-cli@latest init
140
+ pnpm dlx eas-cli@latest build --platform all --profile production
127
141
  pnpm dlx eas-cli@latest submit --platform all --latest --profile production
128
142
  ```
129
143
 
130
- One-step build and submit:
144
+ ### Chrome Extension
131
145
 
132
146
  ```sh
133
- pnpm dlx eas-cli@latest build --platform android --profile production --auto-submit
134
- pnpm dlx eas-cli@latest build --platform ios --profile production --auto-submit
135
- pnpm dlx eas-cli@latest build --platform all --profile production --auto-submit
147
+ pnpm extension:zip
136
148
  ```
137
149
 
138
- Useful release checks:
150
+ Upload `Extension/.output/*-chrome.zip` to the Chrome Web Store Developer Dashboard.
139
151
 
140
- ```sh
141
- pnpm dlx eas-cli@latest build:list
142
- pnpm dlx eas-cli@latest build:version:get --platform all --profile production
143
- pnpm dlx eas-cli@latest credentials
144
- ```
152
+ ## Dependency Policy
145
153
 
146
- ## Extension Release
154
+ `pnpm dlx anhedral@latest init` gives users the latest published Anhedral CLI, but generated projects use the dependency manifest verified for that release.
147
155
 
148
- The WXT extension is built from `Extension`. It is not deployed by Vercel.
156
+ Runtime dependency versions live in `src/dependencies.ts`. Generated projects also receive a `stack.json` file recording the exact stack and dependency manifest used for that init run.
149
157
 
150
- ```sh
151
- cd Extension
152
- pnpm build
153
- pnpm zip
154
- ```
158
+ This keeps generated apps reproducible while still allowing Anhedral itself to move forward quickly through verified releases.
155
159
 
156
- The Chrome ZIP is generated under `.output/`. Upload that ZIP in the Chrome Web Store Developer Dashboard:
157
-
158
- 1. Open the Chrome Web Store Developer Dashboard.
159
- 2. Create the item the first time, or open the existing extension item.
160
- 3. Upload the generated Chrome ZIP.
161
- 4. Complete or update the store listing, screenshots, privacy practices, permissions justification, and distribution settings.
162
- 5. Submit the item for review.
163
-
164
- For automated store submissions later, WXT supports:
165
-
166
- ```sh
167
- pnpm wxt submit init
168
- pnpm wxt submit
169
- ```
160
+ ## Development
170
161
 
171
- ## Local Development
162
+ Work on the CLI locally:
172
163
 
173
164
  ```sh
174
165
  pnpm install
@@ -176,80 +167,29 @@ pnpm build
176
167
  pnpm test:all
177
168
  ```
178
169
 
179
- ## Publishing
180
-
181
- This package is published once to the npm registry. `pnpm`, `npm`, `yarn`, and `bun` users all install that same published package.
182
-
183
- Official releases are automatic on merges to `main`. GitHub Actions bumps the patch version, runs the release checks once, creates a release commit and tag, and publishes that version to npm.
184
-
185
- Recommended release flow:
170
+ Refresh the checked-in demo output:
186
171
 
187
172
  ```sh
188
- git push origin main
173
+ pnpm demo:refresh
189
174
  ```
190
175
 
191
- Release publishing is handled by:
192
-
193
- - `.github/workflows/release-on-main.yml`: runs on pushes to `main`, bumps `package.json`, verifies the repo, pushes a `v*.*.*` tag, and publishes to npm.
194
- - `.github/workflows/release.yml`: manual retry workflow for publishing the current checked-out package version.
195
-
196
- The repository is private, so npm provenance publishing is not supported. GitHub Actions publishes with `--provenance=false` and skips npm lifecycle scripts because `pnpm release:check` has already run. Configure an npm automation token as `NPM_TOKEN` in the `npm` GitHub environment or repository secrets.
197
-
198
- Manual local publish is still available as an emergency fallback:
176
+ Validate upstream scaffold drift:
199
177
 
200
178
  ```sh
201
- pnpm release:check
202
- npm publish --provenance=false --ignore-scripts
179
+ pnpm test:e2e:latest
203
180
  ```
204
181
 
205
- ## Maintenance
206
-
207
- This repo is maintained in two lanes:
208
-
209
- - `stable`: the default user-facing toolchain, pinned to known-good upstream CLI and package versions in `src/dependencies.ts`.
210
- - `latest`: an early-warning lane that tracks upstream scaffold tools without pinning, so breakage is detected before it reaches the default path.
211
-
212
- The practical rule is simple: users get the latest verified Anhedral release, not uncontrolled registry drift at init time. Detect drift on `latest`, verify it, then promote the pinned dependency manifest only after the e2e matrix passes.
213
-
214
- ### Dependency policy
215
-
216
- - Runtime dependency versions for generated projects live in `src/dependencies.ts`.
217
- - Generated package manifests and install commands must read from that manifest.
218
- - Avoid raw `@latest` and caret ranges in generated output unless a package manager or platform command explicitly requires it.
219
- - `pnpm dlx anhedral@latest init` means the newest published Anhedral CLI. The generated project should still be reproducible for that exact Anhedral version.
220
- - `stack.json` records the dependency manifest so support and debugging can identify exactly what was generated.
221
-
222
- ### Recommended automation
223
-
224
- - Renovate is configured in `renovate.json` to keep this repo's own dependencies and generated dependency manifest current.
225
- - GitHub Actions CI runs on pushes and pull requests in `.github/workflows/ci.yml`.
226
- - Weekly toolchain drift checks run in `.github/workflows/toolchain-drift.yml` for both `stable` and `latest`.
227
- - Automatic releases publish through `.github/workflows/release-on-main.yml`; `.github/workflows/release.yml` is for manual retry only.
228
- - Stable dependency pins in `src/dependencies.ts` are annotated so Renovate can open PRs when upstream packages and scaffold CLIs publish updates.
229
- - `prepublishOnly` stays strict for manual local publishing. GitHub Actions skips npm lifecycle scripts during publish after running `pnpm release:check`.
182
+ ## Release
230
183
 
231
- ### Upgrade workflow
184
+ Official releases are automatic on merges to `main`. The release workflow bumps the patch version, runs the release checks, creates a release commit and tag, and publishes to npm.
232
185
 
233
- Use this sequence when Renovate opens a dependency or toolchain PR:
186
+ Manual fallback:
234
187
 
235
188
  ```sh
236
- pnpm install --frozen-lockfile
237
- pnpm test
238
- pnpm test:e2e
239
- npm pack --dry-run
240
- ```
241
-
242
- If you want to validate upstream scaffold drift manually, use the explicit latest-lane script:
243
-
244
- ```sh
245
- pnpm test:e2e:latest
189
+ pnpm release:check
190
+ npm publish --provenance=false --ignore-scripts
246
191
  ```
247
192
 
248
- When a dependency pin PR passes and the generated outputs look good, update the `VERIFIED_AT` date in `src/dependencies.ts` and release normally.
249
-
250
- ### Release policy
193
+ ## License
251
194
 
252
- - Publish `patch` releases for scaffold fixes, dependency bumps, and safer defaults.
253
- - Publish `minor` releases for new stacks, new flags, or new generated capabilities.
254
- - Publish `major` releases for breaking CLI changes or incompatible generated-project structure changes.
255
- - Never publish from a dirty worktree.
195
+ MIT
package/dist/scaffold.js CHANGED
@@ -462,7 +462,7 @@ Upload \`Extension/.output/*-chrome.zip\` to the Chrome Web Store Developer Dash
462
462
  - Confirm Clerk works on local web, Vercel web, iOS, Android, and the Chrome extension.
463
463
  - Confirm RevenueCat returns the \`pro\` entitlement after Stripe web purchase and native store purchase.
464
464
  - Confirm \`pnpm db:migrate\` has run against the production Neon database.
465
- - Confirm R2 upload and signed avatar URL retrieval work from the deployed Backend domain.
465
+ - Confirm R2 upload and signed URL retrieval work from the deployed Backend domain if you expose storage routes.
466
466
  - Confirm both Vercel projects can resolve \`packages/*\` workspace packages.
467
467
  - Confirm the Chrome extension ZIP is tested locally with \`chrome://extensions\` before Chrome Web Store upload.
468
468
 
@@ -583,7 +583,7 @@ export const SUBSCRIPTION_EVENT_TYPES = [
583
583
  'trial_started', 'trial_converted', 'trial_expired',
584
584
  'initial_purchase', 'renewal', 'product_change',
585
585
  'cancellation_scheduled', 'cancellation_unscheduled', 'subscription_expired', 'subscription_canceled',
586
- 'promo_redeemed', 'billing_issue', 'billing_recovered',
586
+ 'billing_issue', 'billing_recovered',
587
587
  ] as const;
588
588
  export type SubscriptionEventType = (typeof SUBSCRIPTION_EVENT_TYPES)[number];
589
589
 
@@ -591,14 +591,11 @@ export type SubscriptionMetadata = {
591
591
  revenueCatProductId?: string;
592
592
  lastWebhookUpdate?: string;
593
593
  cancelReason?: string;
594
- redeemCode?: string;
595
- redeemCodeRedeemedAt?: string;
596
594
  };
597
595
 
598
596
  export type SubscriptionEventMetadata = {
599
597
  revenueCatEventType?: string;
600
598
  revenueCatProductId?: string;
601
- promoCode?: string;
602
599
  billingPeriod?: string;
603
600
  price?: { amount: number; currency: string };
604
601
  store?: string;
@@ -606,9 +603,6 @@ export type SubscriptionEventMetadata = {
606
603
  reason?: string;
607
604
  };
608
605
 
609
- export const PROMO_CODE_DURATIONS = [1, 6, 12] as const;
610
- export type PromoCodeDuration = (typeof PROMO_CODE_DURATIONS)[number];
611
-
612
606
  export const users = pgTable('users', {
613
607
  id: text('id').primaryKey(),
614
608
  clerkUserId: text('clerk_user_id').unique(),
@@ -618,9 +612,6 @@ export const users = pgTable('users', {
618
612
  profileImageUrl: text('profile_image_url'),
619
613
  subscriptionTier: text('subscription_tier').notNull().default('free'),
620
614
  subscriptionStatus: text('subscription_status').notNull().default('active'),
621
- avatarObjectKey: text('avatar_object_key'),
622
- avatarMimeType: text('avatar_mime_type'),
623
- creditsBalance: integer('credits_balance').notNull().default(250),
624
615
  createdAt: timestamp('created_at').$defaultFn(() => new Date()).notNull(),
625
616
  updatedAt: timestamp('updated_at').$defaultFn(() => new Date()).notNull(),
626
617
  }, (t) => [
@@ -693,27 +684,6 @@ export const trialClaims = pgTable('trial_claims', {
693
684
  claimedAt: timestamp('claimed_at').$defaultFn(() => new Date()).notNull(),
694
685
  }, (t) => [index('trial_claims_email_idx').on(t.email)]);
695
686
 
696
- export const promoCodes = pgTable('promo_codes', {
697
- id: text('id').primaryKey(),
698
- code: text('code').notNull().unique(),
699
- months: integer('months').$type<PromoCodeDuration>().notNull(),
700
- maxRedemptions: integer('max_redemptions').notNull().default(1),
701
- redeemedCount: integer('redeemed_count').notNull().default(0),
702
- expiresAt: timestamp('expires_at'),
703
- createdAt: timestamp('created_at').$defaultFn(() => new Date()).notNull(),
704
- }, (t) => [index('promo_codes_code_idx').on(t.code)]);
705
-
706
- export const promoRedemptions = pgTable('promo_redemptions', {
707
- id: text('id').primaryKey(),
708
- promoCodeId: text('promo_code_id').notNull().references(() => promoCodes.id, { onDelete: 'cascade' }),
709
- userId: text('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
710
- redeemedAt: timestamp('redeemed_at').$defaultFn(() => new Date()).notNull(),
711
- entitlementExpiresAt: timestamp('entitlement_expires_at').notNull(),
712
- }, (t) => [
713
- index('promo_redemptions_user_idx').on(t.userId),
714
- index('promo_redemptions_code_idx').on(t.promoCodeId),
715
- ]);
716
-
717
687
  export type Users = InferSelectModel<typeof users>;
718
688
  export type NewUsers = InferInsertModel<typeof users>;
719
689
  export type Uploads = InferSelectModel<typeof uploads>;
@@ -724,10 +694,6 @@ export type SubscriptionEvents = InferSelectModel<typeof subscriptionEvents>;
724
694
  export type NewSubscriptionEvents = InferInsertModel<typeof subscriptionEvents>;
725
695
  export type TrialClaims = InferSelectModel<typeof trialClaims>;
726
696
  export type NewTrialClaims = InferInsertModel<typeof trialClaims>;
727
- export type PromoCodes = InferSelectModel<typeof promoCodes>;
728
- export type NewPromoCodes = InferInsertModel<typeof promoCodes>;
729
- export type PromoRedemptions = InferSelectModel<typeof promoRedemptions>;
730
- export type NewPromoRedemptions = InferInsertModel<typeof promoRedemptions>;
731
697
  `);
732
698
  writeFile(path.join(dbRoot, 'src/queries/users.ts'), `import { eq } from 'drizzle-orm';
733
699
  import { db } from '../index';
@@ -766,20 +732,6 @@ export async function syncUserProfile(input: SyncUserInput) {
766
732
  return findUserByClerkId(input.clerkUserId);
767
733
  }
768
734
 
769
- export async function updateAvatarForUser(
770
- clerkUserId: string,
771
- input: { objectKey: string; contentType: string | null },
772
- ) {
773
- await db
774
- .update(users)
775
- .set({
776
- avatarObjectKey: input.objectKey,
777
- avatarMimeType: input.contentType,
778
- updatedAt: new Date(),
779
- })
780
- .where(eq(users.clerkUserId, clerkUserId));
781
- }
782
-
783
735
  export async function createUploadRecord(
784
736
  clerkUserId: string,
785
737
  input: { objectKey: string; bucket: string; contentType: string | null },
@@ -849,10 +801,6 @@ export type AuthMeResponse = {
849
801
  lastName?: string | null;
850
802
  displayName: string;
851
803
  imageUrl?: string | null;
852
- avatarUrl?: string | null;
853
- creditsBalance: number;
854
- subscriptionTier: string;
855
- subscriptionStatus: string;
856
804
  };
857
805
  };
858
806
  export type PricingResponse = {
@@ -954,26 +902,12 @@ export class ApiClient {
954
902
  );
955
903
  }
956
904
 
957
- redeemCode(code: string) {
958
- return this.request<{ ok: boolean; expiresAt: string }>('/subscriptions/redeem', {
959
- method: 'POST',
960
- body: JSON.stringify({ code }),
961
- });
962
- }
963
-
964
905
  signOut() {
965
906
  return this.request<{ success: boolean }>('/auth/signout', {
966
907
  method: 'POST',
967
908
  });
968
909
  }
969
910
 
970
- uploadAvatar(input: { base64: string; mimeType: string; fileName?: string }) {
971
- return this.request<{ ok: boolean; avatarUrl: string }>('/auth/avatar', {
972
- method: 'POST',
973
- body: JSON.stringify(input),
974
- });
975
- }
976
-
977
911
  deleteAccount() {
978
912
  return this.request<void>('/auth/account', {
979
913
  method: 'DELETE',