anhedral 0.1.6 → 0.1.8
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 +120 -155
- package/dist/dependencies.d.ts +2 -0
- package/dist/dependencies.js +12 -0
- package/dist/scaffold.js +158 -116
- package/dist/templates/backend.js +135 -234
- package/dist/templates/frontend.js +117 -411
- package/package.json +1 -1
package/dist/scaffold.js
CHANGED
|
@@ -4,7 +4,7 @@ import { env } from 'node:process';
|
|
|
4
4
|
import { anhedralPrint } from './print.js';
|
|
5
5
|
import { appendGitignore, writeFile } from './util.js';
|
|
6
6
|
import { getSkillCommands } from './commands.js';
|
|
7
|
-
import { API_CLIENT_DEPENDENCIES, PACKAGE_MANAGER, ROOT_DEPENDENCIES, SHARED_DB_DEPENDENCIES, SHARED_PACKAGE_DEPENDENCIES, dependencyManifest, } from './dependencies.js';
|
|
7
|
+
import { API_CLIENT_DEPENDENCIES, CONTRACTS_DEPENDENCIES, PACKAGE_MANAGER, ROOT_DEPENDENCIES, SHARED_DB_DEPENDENCIES, SHARED_PACKAGE_DEPENDENCIES, dependencyManifest, } from './dependencies.js';
|
|
8
8
|
import { resolveToolchain } from './toolchain.js';
|
|
9
9
|
import { scaffoldBackend } from './templates/backend.js';
|
|
10
10
|
import { scaffoldExtension } from './templates/extension.js';
|
|
@@ -259,6 +259,7 @@ The generated app is one pnpm monorepo:
|
|
|
259
259
|
├─ packages/
|
|
260
260
|
│ ├─ api-client/ shared typed API client
|
|
261
261
|
│ ├─ config/ shared config constants/helpers
|
|
262
|
+
│ ├─ contracts/ shared Zod request/response contracts
|
|
262
263
|
│ ├─ db/ Drizzle schema, Neon client, migrations
|
|
263
264
|
│ └─ types/ shared TypeScript types
|
|
264
265
|
├─ PRODUCTION.md development and deployment guide
|
|
@@ -292,7 +293,7 @@ Important files:
|
|
|
292
293
|
- \`Backend/src/index.ts\`: Vercel Fastify entrypoint
|
|
293
294
|
- \`Backend/src/app.ts\`: Fastify app construction
|
|
294
295
|
- \`Backend/vercel.json\`: minimal Vercel config
|
|
295
|
-
- \`Backend/src/routes\`: health/auth/subscription routes
|
|
296
|
+
- \`Backend/src/routes\`: health/auth/subscription/storage routes
|
|
296
297
|
|
|
297
298
|
## Extension
|
|
298
299
|
|
|
@@ -337,6 +338,8 @@ Database workflow:
|
|
|
337
338
|
\`\`\`sh
|
|
338
339
|
pnpm db:generate
|
|
339
340
|
pnpm db:migrate
|
|
341
|
+
pnpm db:studio
|
|
342
|
+
pnpm db:check
|
|
340
343
|
\`\`\`
|
|
341
344
|
|
|
342
345
|
Local demo mode is enabled through generated \`.env\` files. Real provider behavior needs Clerk, RevenueCat/Stripe, Neon, and R2 credentials.
|
|
@@ -419,7 +422,8 @@ Steps:
|
|
|
419
422
|
1. Create an R2 bucket.
|
|
420
423
|
2. Create a least-privilege API token for that bucket.
|
|
421
424
|
3. Set \`R2_ACCOUNT_ID\`, \`R2_ACCESS_KEY_ID\`, \`R2_SECRET_ACCESS_KEY\`, and \`R2_BUCKET\` only in Backend envs.
|
|
422
|
-
4.
|
|
425
|
+
4. Use \`POST /storage/uploads\` to create signed upload URLs, then \`GET /storage/files/:key\` or \`DELETE /storage/files/:key\` for user-owned objects.
|
|
426
|
+
5. Configure a public bucket URL or custom domain if uploaded assets need public CDN delivery.
|
|
423
427
|
|
|
424
428
|
## Deploy
|
|
425
429
|
|
|
@@ -462,7 +466,7 @@ Upload \`Extension/.output/*-chrome.zip\` to the Chrome Web Store Developer Dash
|
|
|
462
466
|
- Confirm Clerk works on local web, Vercel web, iOS, Android, and the Chrome extension.
|
|
463
467
|
- Confirm RevenueCat returns the \`pro\` entitlement after Stripe web purchase and native store purchase.
|
|
464
468
|
- Confirm \`pnpm db:migrate\` has run against the production Neon database.
|
|
465
|
-
- Confirm R2 upload
|
|
469
|
+
- Confirm R2 upload, signed URL retrieval, and deletion work from the deployed Backend domain.
|
|
466
470
|
- Confirm both Vercel projects can resolve \`packages/*\` workspace packages.
|
|
467
471
|
- Confirm the Chrome extension ZIP is tested locally with \`chrome://extensions\` before Chrome Web Store upload.
|
|
468
472
|
|
|
@@ -507,6 +511,8 @@ function writeSharedPackages(root) {
|
|
|
507
511
|
typecheck: 'tsc --noEmit',
|
|
508
512
|
'db:generate': 'drizzle-kit generate',
|
|
509
513
|
'db:migrate': 'tsx --env-file=../../.env src/migrate.ts',
|
|
514
|
+
'db:studio': 'drizzle-kit studio',
|
|
515
|
+
'db:check': 'drizzle-kit check',
|
|
510
516
|
},
|
|
511
517
|
dependencies: SHARED_DB_DEPENDENCIES.dependencies,
|
|
512
518
|
devDependencies: SHARED_DB_DEPENDENCIES.devDependencies,
|
|
@@ -583,7 +589,7 @@ export const SUBSCRIPTION_EVENT_TYPES = [
|
|
|
583
589
|
'trial_started', 'trial_converted', 'trial_expired',
|
|
584
590
|
'initial_purchase', 'renewal', 'product_change',
|
|
585
591
|
'cancellation_scheduled', 'cancellation_unscheduled', 'subscription_expired', 'subscription_canceled',
|
|
586
|
-
'
|
|
592
|
+
'billing_issue', 'billing_recovered',
|
|
587
593
|
] as const;
|
|
588
594
|
export type SubscriptionEventType = (typeof SUBSCRIPTION_EVENT_TYPES)[number];
|
|
589
595
|
|
|
@@ -591,14 +597,11 @@ export type SubscriptionMetadata = {
|
|
|
591
597
|
revenueCatProductId?: string;
|
|
592
598
|
lastWebhookUpdate?: string;
|
|
593
599
|
cancelReason?: string;
|
|
594
|
-
redeemCode?: string;
|
|
595
|
-
redeemCodeRedeemedAt?: string;
|
|
596
600
|
};
|
|
597
601
|
|
|
598
602
|
export type SubscriptionEventMetadata = {
|
|
599
603
|
revenueCatEventType?: string;
|
|
600
604
|
revenueCatProductId?: string;
|
|
601
|
-
promoCode?: string;
|
|
602
605
|
billingPeriod?: string;
|
|
603
606
|
price?: { amount: number; currency: string };
|
|
604
607
|
store?: string;
|
|
@@ -606,9 +609,6 @@ export type SubscriptionEventMetadata = {
|
|
|
606
609
|
reason?: string;
|
|
607
610
|
};
|
|
608
611
|
|
|
609
|
-
export const PROMO_CODE_DURATIONS = [1, 6, 12] as const;
|
|
610
|
-
export type PromoCodeDuration = (typeof PROMO_CODE_DURATIONS)[number];
|
|
611
|
-
|
|
612
612
|
export const users = pgTable('users', {
|
|
613
613
|
id: text('id').primaryKey(),
|
|
614
614
|
clerkUserId: text('clerk_user_id').unique(),
|
|
@@ -618,9 +618,6 @@ export const users = pgTable('users', {
|
|
|
618
618
|
profileImageUrl: text('profile_image_url'),
|
|
619
619
|
subscriptionTier: text('subscription_tier').notNull().default('free'),
|
|
620
620
|
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
621
|
createdAt: timestamp('created_at').$defaultFn(() => new Date()).notNull(),
|
|
625
622
|
updatedAt: timestamp('updated_at').$defaultFn(() => new Date()).notNull(),
|
|
626
623
|
}, (t) => [
|
|
@@ -693,27 +690,6 @@ export const trialClaims = pgTable('trial_claims', {
|
|
|
693
690
|
claimedAt: timestamp('claimed_at').$defaultFn(() => new Date()).notNull(),
|
|
694
691
|
}, (t) => [index('trial_claims_email_idx').on(t.email)]);
|
|
695
692
|
|
|
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
693
|
export type Users = InferSelectModel<typeof users>;
|
|
718
694
|
export type NewUsers = InferInsertModel<typeof users>;
|
|
719
695
|
export type Uploads = InferSelectModel<typeof uploads>;
|
|
@@ -724,10 +700,6 @@ export type SubscriptionEvents = InferSelectModel<typeof subscriptionEvents>;
|
|
|
724
700
|
export type NewSubscriptionEvents = InferInsertModel<typeof subscriptionEvents>;
|
|
725
701
|
export type TrialClaims = InferSelectModel<typeof trialClaims>;
|
|
726
702
|
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
703
|
`);
|
|
732
704
|
writeFile(path.join(dbRoot, 'src/queries/users.ts'), `import { eq } from 'drizzle-orm';
|
|
733
705
|
import { db } from '../index';
|
|
@@ -766,20 +738,6 @@ export async function syncUserProfile(input: SyncUserInput) {
|
|
|
766
738
|
return findUserByClerkId(input.clerkUserId);
|
|
767
739
|
}
|
|
768
740
|
|
|
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
741
|
export async function createUploadRecord(
|
|
784
742
|
clerkUserId: string,
|
|
785
743
|
input: { objectKey: string; bucket: string; contentType: string | null },
|
|
@@ -828,50 +786,104 @@ console.log('Migrations completed in', Date.now() - start, 'ms');
|
|
|
828
786
|
`);
|
|
829
787
|
writeFile(path.join(dbRoot, 'migrations/.gitkeep'), '');
|
|
830
788
|
const simplePackages = [
|
|
831
|
-
['
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
789
|
+
['contracts', {
|
|
790
|
+
exports: { '.': './src/index.ts' },
|
|
791
|
+
dependencies: CONTRACTS_DEPENDENCIES.dependencies,
|
|
792
|
+
}, `import { z } from 'zod';
|
|
793
|
+
|
|
794
|
+
export const ClientPlatformSchema = z.enum(['frontend', 'extension']);
|
|
795
|
+
export type ClientPlatform = z.infer<typeof ClientPlatformSchema>;
|
|
796
|
+
|
|
797
|
+
export const ApiErrorSchema = z.object({
|
|
798
|
+
error: z.string(),
|
|
799
|
+
message: z.string(),
|
|
800
|
+
});
|
|
801
|
+
export type ApiError = z.infer<typeof ApiErrorSchema>;
|
|
802
|
+
|
|
803
|
+
export const AuthMeResponseSchema = z.object({
|
|
804
|
+
user: z.object({
|
|
805
|
+
id: z.string(),
|
|
806
|
+
email: z.string().email(),
|
|
807
|
+
firstName: z.string().nullable().optional(),
|
|
808
|
+
lastName: z.string().nullable().optional(),
|
|
809
|
+
displayName: z.string(),
|
|
810
|
+
imageUrl: z.string().url().nullable().optional(),
|
|
811
|
+
}),
|
|
812
|
+
});
|
|
813
|
+
export type AuthMeResponse = z.infer<typeof AuthMeResponseSchema>;
|
|
814
|
+
|
|
815
|
+
export const PricingResponseSchema = z.object({
|
|
816
|
+
tiers: z.array(z.object({
|
|
817
|
+
tier: z.enum(['free', 'pro']),
|
|
818
|
+
displayName: z.string(),
|
|
819
|
+
description: z.string(),
|
|
820
|
+
priceMonthly: z.number().nullable(),
|
|
821
|
+
priceYearly: z.number().nullable(),
|
|
822
|
+
currency: z.string(),
|
|
823
|
+
limits: z.object({
|
|
824
|
+
dailyLimit: z.number().nullable(),
|
|
825
|
+
}),
|
|
826
|
+
paymentInfo: z.object({
|
|
827
|
+
revenueCatEntitlementId: z.string(),
|
|
828
|
+
revenueCatOfferingId: z.string(),
|
|
829
|
+
}).optional(),
|
|
830
|
+
})),
|
|
831
|
+
});
|
|
832
|
+
export type PricingResponse = z.infer<typeof PricingResponseSchema>;
|
|
833
|
+
|
|
834
|
+
export const SubscriptionEntitlementsSchema = z.object({
|
|
835
|
+
pro: z.boolean(),
|
|
836
|
+
inTrial: z.boolean(),
|
|
837
|
+
trialEndsAt: z.string().optional(),
|
|
838
|
+
expiresAt: z.string().optional(),
|
|
839
|
+
periodStart: z.string().optional(),
|
|
840
|
+
periodEnd: z.string().optional(),
|
|
841
|
+
method: z.enum(['trialing', 'redeemed', 'paid']).nullable().optional(),
|
|
842
|
+
managementUrl: z.string().url().optional(),
|
|
843
|
+
cancelAtPeriodEnd: z.boolean().optional(),
|
|
844
|
+
});
|
|
845
|
+
export type SubscriptionEntitlements = z.infer<typeof SubscriptionEntitlementsSchema>;
|
|
846
|
+
|
|
847
|
+
export const SignOutResponseSchema = z.object({
|
|
848
|
+
success: z.boolean(),
|
|
849
|
+
});
|
|
850
|
+
export type SignOutResponse = z.infer<typeof SignOutResponseSchema>;
|
|
851
|
+
|
|
852
|
+
export const CreateUploadRequestSchema = z.object({
|
|
853
|
+
fileName: z.string().min(1).max(200).optional(),
|
|
854
|
+
contentType: z.string().min(1).max(200),
|
|
855
|
+
});
|
|
856
|
+
export type CreateUploadRequest = z.infer<typeof CreateUploadRequestSchema>;
|
|
857
|
+
|
|
858
|
+
export const CreateUploadResponseSchema = z.object({
|
|
859
|
+
objectKey: z.string(),
|
|
860
|
+
uploadUrl: z.string().url(),
|
|
861
|
+
expiresIn: z.number(),
|
|
862
|
+
headers: z.record(z.string(), z.string()),
|
|
863
|
+
});
|
|
864
|
+
export type CreateUploadResponse = z.infer<typeof CreateUploadResponseSchema>;
|
|
865
|
+
|
|
866
|
+
export const StorageFileResponseSchema = z.object({
|
|
867
|
+
objectKey: z.string(),
|
|
868
|
+
downloadUrl: z.string().url(),
|
|
869
|
+
expiresIn: z.number(),
|
|
870
|
+
});
|
|
871
|
+
export type StorageFileResponse = z.infer<typeof StorageFileResponseSchema>;
|
|
872
|
+
`],
|
|
873
|
+
['types', {
|
|
874
|
+
exports: { '.': './src/index.ts' },
|
|
875
|
+
dependencies: { '@anhedral/contracts': 'workspace:*' },
|
|
876
|
+
}, `export type ApiEnvelope<T> = { data: T } | { error: string; message: string };
|
|
877
|
+
export type {
|
|
878
|
+
AuthMeResponse,
|
|
879
|
+
ClientPlatform,
|
|
880
|
+
CreateUploadRequest,
|
|
881
|
+
CreateUploadResponse,
|
|
882
|
+
PricingResponse,
|
|
883
|
+
SignOutResponse,
|
|
884
|
+
StorageFileResponse,
|
|
885
|
+
SubscriptionEntitlements,
|
|
886
|
+
} from '@anhedral/contracts';
|
|
875
887
|
`],
|
|
876
888
|
['config', { exports: { '.': './src/index.ts' } }, `export const DEFAULT_API_PATH_PREFIX = '/api';
|
|
877
889
|
export const DEFAULT_LOCAL_API_URL = 'http://localhost:8787';
|
|
@@ -887,8 +899,24 @@ export function joinApiUrl(baseUrl: string, path: string) {
|
|
|
887
899
|
['api-client', {
|
|
888
900
|
exports: { '.': './src/index.ts' },
|
|
889
901
|
dependencies: API_CLIENT_DEPENDENCIES.dependencies,
|
|
890
|
-
}, `import {
|
|
891
|
-
import
|
|
902
|
+
}, `import type { ZodType } from 'zod';
|
|
903
|
+
import { joinApiUrl } from '@anhedral/config';
|
|
904
|
+
import {
|
|
905
|
+
AuthMeResponseSchema,
|
|
906
|
+
CreateUploadResponseSchema,
|
|
907
|
+
PricingResponseSchema,
|
|
908
|
+
SignOutResponseSchema,
|
|
909
|
+
StorageFileResponseSchema,
|
|
910
|
+
SubscriptionEntitlementsSchema,
|
|
911
|
+
type AuthMeResponse,
|
|
912
|
+
type ClientPlatform,
|
|
913
|
+
type CreateUploadRequest,
|
|
914
|
+
type CreateUploadResponse,
|
|
915
|
+
type PricingResponse,
|
|
916
|
+
type SignOutResponse,
|
|
917
|
+
type StorageFileResponse,
|
|
918
|
+
type SubscriptionEntitlements,
|
|
919
|
+
} from '@anhedral/contracts';
|
|
892
920
|
|
|
893
921
|
export class APIRequestError extends Error {
|
|
894
922
|
constructor(
|
|
@@ -910,10 +938,12 @@ export type ApiClientOptions = {
|
|
|
910
938
|
export class ApiClient {
|
|
911
939
|
constructor(private readonly options: ApiClientOptions) {}
|
|
912
940
|
|
|
913
|
-
async request<T>(path: string, init: RequestInit = {}): Promise<T> {
|
|
941
|
+
async request<T>(path: string, init: RequestInit = {}, schema?: ZodType<T>): Promise<T> {
|
|
914
942
|
const token = await this.options.getToken?.();
|
|
915
943
|
const headers = new Headers(init.headers);
|
|
916
|
-
|
|
944
|
+
if (init.body != null) {
|
|
945
|
+
headers.set('Content-Type', headers.get('Content-Type') ?? 'application/json');
|
|
946
|
+
}
|
|
917
947
|
headers.set('X-Platform', this.options.platform);
|
|
918
948
|
if (token) headers.set('Authorization', \`Bearer \${token}\`);
|
|
919
949
|
|
|
@@ -933,15 +963,16 @@ export class ApiClient {
|
|
|
933
963
|
}
|
|
934
964
|
|
|
935
965
|
if (response.status === 204) return {} as T;
|
|
936
|
-
|
|
966
|
+
const data = await response.json();
|
|
967
|
+
return schema ? schema.parse(data) : data as T;
|
|
937
968
|
}
|
|
938
969
|
|
|
939
970
|
getMe() {
|
|
940
|
-
return this.request<AuthMeResponse>('/auth/me');
|
|
971
|
+
return this.request<AuthMeResponse>('/auth/me', {}, AuthMeResponseSchema);
|
|
941
972
|
}
|
|
942
973
|
|
|
943
974
|
getPricing() {
|
|
944
|
-
return this.request<PricingResponse>('/subscriptions/pricing');
|
|
975
|
+
return this.request<PricingResponse>('/subscriptions/pricing', {}, PricingResponseSchema);
|
|
945
976
|
}
|
|
946
977
|
|
|
947
978
|
getSubscriptionPricing() {
|
|
@@ -951,31 +982,40 @@ export class ApiClient {
|
|
|
951
982
|
getSubscriptionEntitlements(options?: { refresh?: boolean }) {
|
|
952
983
|
return this.request<SubscriptionEntitlements>(
|
|
953
984
|
options?.refresh ? '/subscriptions/entitlements/me?refresh=true' : '/subscriptions/entitlements/me',
|
|
985
|
+
{},
|
|
986
|
+
SubscriptionEntitlementsSchema,
|
|
954
987
|
);
|
|
955
988
|
}
|
|
956
989
|
|
|
957
|
-
|
|
958
|
-
return this.request<
|
|
990
|
+
signOut() {
|
|
991
|
+
return this.request<SignOutResponse>('/auth/signout', {
|
|
959
992
|
method: 'POST',
|
|
960
|
-
|
|
961
|
-
});
|
|
993
|
+
}, SignOutResponseSchema);
|
|
962
994
|
}
|
|
963
995
|
|
|
964
|
-
|
|
965
|
-
return this.request<
|
|
966
|
-
method: '
|
|
996
|
+
deleteAccount() {
|
|
997
|
+
return this.request<void>('/auth/account', {
|
|
998
|
+
method: 'DELETE',
|
|
967
999
|
});
|
|
968
1000
|
}
|
|
969
1001
|
|
|
970
|
-
|
|
971
|
-
return this.request<
|
|
1002
|
+
createUpload(input: CreateUploadRequest) {
|
|
1003
|
+
return this.request<CreateUploadResponse>('/storage/uploads', {
|
|
972
1004
|
method: 'POST',
|
|
973
1005
|
body: JSON.stringify(input),
|
|
974
|
-
});
|
|
1006
|
+
}, CreateUploadResponseSchema);
|
|
975
1007
|
}
|
|
976
1008
|
|
|
977
|
-
|
|
978
|
-
return this.request<
|
|
1009
|
+
getStorageFile(objectKey: string) {
|
|
1010
|
+
return this.request<StorageFileResponse>(
|
|
1011
|
+
\`/storage/files/\${encodeURIComponent(objectKey)}\`,
|
|
1012
|
+
{},
|
|
1013
|
+
StorageFileResponseSchema,
|
|
1014
|
+
);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
deleteStorageFile(objectKey: string) {
|
|
1018
|
+
return this.request<void>(\`/storage/files/\${encodeURIComponent(objectKey)}\`, {
|
|
979
1019
|
method: 'DELETE',
|
|
980
1020
|
});
|
|
981
1021
|
}
|
|
@@ -1136,6 +1176,8 @@ function writeFullstackRootFiles(root, options) {
|
|
|
1136
1176
|
'verify:extension': 'pnpm --filter ./Extension typecheck && pnpm --filter ./Extension zip',
|
|
1137
1177
|
'db:generate': 'pnpm --filter @anhedral/db db:generate',
|
|
1138
1178
|
'db:migrate': 'pnpm --filter @anhedral/db db:migrate',
|
|
1179
|
+
'db:studio': 'pnpm --filter @anhedral/db db:studio',
|
|
1180
|
+
'db:check': 'pnpm --filter @anhedral/db db:check',
|
|
1139
1181
|
};
|
|
1140
1182
|
scripts['dev:frontend'] = 'pnpm --filter ./Frontend dev';
|
|
1141
1183
|
scripts['dev:extension'] = 'pnpm --filter ./Extension dev';
|