yaxa-svelte 1.0.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.
Files changed (169) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +163 -0
  3. package/dist/assets/favicon.svg +15 -0
  4. package/dist/components/elements/Avatar.svelte +106 -0
  5. package/dist/components/elements/Avatar.svelte.d.ts +40 -0
  6. package/dist/components/elements/AvatarGroup.svelte +18 -0
  7. package/dist/components/elements/AvatarGroup.svelte.d.ts +8 -0
  8. package/dist/components/elements/Badge.svelte +202 -0
  9. package/dist/components/elements/Badge.svelte.d.ts +75 -0
  10. package/dist/components/elements/Button.svelte +300 -0
  11. package/dist/components/elements/Button.svelte.d.ts +109 -0
  12. package/dist/components/elements/ButtonGroup.svelte +22 -0
  13. package/dist/components/elements/ButtonGroup.svelte.d.ts +9 -0
  14. package/dist/components/elements/Chip.svelte +117 -0
  15. package/dist/components/elements/Chip.svelte.d.ts +15 -0
  16. package/dist/components/elements/DataTable.svelte +222 -0
  17. package/dist/components/elements/DataTable.svelte.d.ts +43 -0
  18. package/dist/components/elements/Icon.svelte +87 -0
  19. package/dist/components/elements/Icon.svelte.d.ts +10 -0
  20. package/dist/components/elements/Kbd.svelte +37 -0
  21. package/dist/components/elements/Kbd.svelte.d.ts +32 -0
  22. package/dist/components/elements/Link.svelte +63 -0
  23. package/dist/components/elements/Link.svelte.d.ts +15 -0
  24. package/dist/components/elements/Logo.svelte +77 -0
  25. package/dist/components/elements/Logo.svelte.d.ts +9 -0
  26. package/dist/components/elements/Meter.svelte +87 -0
  27. package/dist/components/elements/Meter.svelte.d.ts +14 -0
  28. package/dist/components/elements/Progress.svelte +63 -0
  29. package/dist/components/elements/Progress.svelte.d.ts +49 -0
  30. package/dist/components/elements/Skeleton.svelte +12 -0
  31. package/dist/components/elements/Skeleton.svelte.d.ts +6 -0
  32. package/dist/components/elements/Spinner.svelte +39 -0
  33. package/dist/components/elements/Spinner.svelte.d.ts +7 -0
  34. package/dist/components/forms/Checkbox.svelte +77 -0
  35. package/dist/components/forms/Checkbox.svelte.d.ts +17 -0
  36. package/dist/components/forms/ColorPicker.svelte +142 -0
  37. package/dist/components/forms/ColorPicker.svelte.d.ts +10 -0
  38. package/dist/components/forms/Form.svelte +84 -0
  39. package/dist/components/forms/Form.svelte.d.ts +14 -0
  40. package/dist/components/forms/FormField.svelte +56 -0
  41. package/dist/components/forms/FormField.svelte.d.ts +14 -0
  42. package/dist/components/forms/Input.svelte +138 -0
  43. package/dist/components/forms/Input.svelte.d.ts +58 -0
  44. package/dist/components/forms/RadioGroup.svelte +74 -0
  45. package/dist/components/forms/RadioGroup.svelte.d.ts +17 -0
  46. package/dist/components/forms/Select.svelte +91 -0
  47. package/dist/components/forms/Select.svelte.d.ts +50 -0
  48. package/dist/components/forms/Slider.svelte +37 -0
  49. package/dist/components/forms/Slider.svelte.d.ts +12 -0
  50. package/dist/components/forms/Switch.svelte +81 -0
  51. package/dist/components/forms/Switch.svelte.d.ts +53 -0
  52. package/dist/components/forms/Textarea.svelte +73 -0
  53. package/dist/components/forms/Textarea.svelte.d.ts +64 -0
  54. package/dist/components/layout/Card.svelte +89 -0
  55. package/dist/components/layout/Card.svelte.d.ts +64 -0
  56. package/dist/components/layout/Container.svelte +36 -0
  57. package/dist/components/layout/Container.svelte.d.ts +37 -0
  58. package/dist/components/layout/Divider.svelte +45 -0
  59. package/dist/components/layout/Divider.svelte.d.ts +11 -0
  60. package/dist/components/layout/Footer.svelte +96 -0
  61. package/dist/components/layout/Footer.svelte.d.ts +10 -0
  62. package/dist/components/layout/Header.svelte +175 -0
  63. package/dist/components/layout/Header.svelte.d.ts +12 -0
  64. package/dist/components/layout/Section.svelte +62 -0
  65. package/dist/components/layout/Section.svelte.d.ts +14 -0
  66. package/dist/components/layout/YaxaApp.svelte +48 -0
  67. package/dist/components/layout/YaxaApp.svelte.d.ts +12 -0
  68. package/dist/components/navigation/Breadcrumb.svelte +58 -0
  69. package/dist/components/navigation/Breadcrumb.svelte.d.ts +13 -0
  70. package/dist/components/navigation/CommandPalette.svelte +201 -0
  71. package/dist/components/navigation/CommandPalette.svelte.d.ts +19 -0
  72. package/dist/components/navigation/DropdownMenu.svelte +75 -0
  73. package/dist/components/navigation/DropdownMenu.svelte.d.ts +20 -0
  74. package/dist/components/navigation/Pagination.svelte +106 -0
  75. package/dist/components/navigation/Pagination.svelte.d.ts +10 -0
  76. package/dist/components/navigation/Tabs.svelte +91 -0
  77. package/dist/components/navigation/Tabs.svelte.d.ts +18 -0
  78. package/dist/components/overlays/Accordion.svelte +54 -0
  79. package/dist/components/overlays/Accordion.svelte.d.ts +16 -0
  80. package/dist/components/overlays/Alert.svelte +110 -0
  81. package/dist/components/overlays/Alert.svelte.d.ts +37 -0
  82. package/dist/components/overlays/ContextMenu.svelte +67 -0
  83. package/dist/components/overlays/ContextMenu.svelte.d.ts +21 -0
  84. package/dist/components/overlays/Modal.svelte +99 -0
  85. package/dist/components/overlays/Modal.svelte.d.ts +40 -0
  86. package/dist/components/overlays/Popover.svelte +33 -0
  87. package/dist/components/overlays/Popover.svelte.d.ts +11 -0
  88. package/dist/components/overlays/Slideover.svelte +102 -0
  89. package/dist/components/overlays/Slideover.svelte.d.ts +37 -0
  90. package/dist/components/overlays/Tooltip.svelte +39 -0
  91. package/dist/components/overlays/Tooltip.svelte.d.ts +12 -0
  92. package/dist/components/saas/AuthCard.svelte +273 -0
  93. package/dist/components/saas/AuthCard.svelte.d.ts +16 -0
  94. package/dist/components/saas/PricingCard.svelte +166 -0
  95. package/dist/components/saas/PricingCard.svelte.d.ts +19 -0
  96. package/dist/components/saas/PricingTable.svelte +139 -0
  97. package/dist/components/saas/PricingTable.svelte.d.ts +11 -0
  98. package/dist/components/saas/SubscriptionCard.svelte +162 -0
  99. package/dist/components/saas/SubscriptionCard.svelte.d.ts +16 -0
  100. package/dist/components/saas/UserMenu.svelte +137 -0
  101. package/dist/components/saas/UserMenu.svelte.d.ts +19 -0
  102. package/dist/components/seo/Favicons.svelte +22 -0
  103. package/dist/components/seo/Favicons.svelte.d.ts +8 -0
  104. package/dist/components/seo/OgImage.svelte +61 -0
  105. package/dist/components/seo/OgImage.svelte.d.ts +12 -0
  106. package/dist/components/seo/Seo.svelte +170 -0
  107. package/dist/components/seo/Seo.svelte.d.ts +21 -0
  108. package/dist/composables/useAuth.svelte.d.ts +23 -0
  109. package/dist/composables/useAuth.svelte.js +157 -0
  110. package/dist/composables/useClipboard.svelte.d.ts +9 -0
  111. package/dist/composables/useClipboard.svelte.js +36 -0
  112. package/dist/composables/useColorMode.svelte.d.ts +8 -0
  113. package/dist/composables/useColorMode.svelte.js +20 -0
  114. package/dist/composables/useDebounce.svelte.d.ts +3 -0
  115. package/dist/composables/useDebounce.svelte.js +17 -0
  116. package/dist/composables/useMediaQuery.svelte.d.ts +3 -0
  117. package/dist/composables/useMediaQuery.svelte.js +17 -0
  118. package/dist/composables/useShortcuts.svelte.d.ts +10 -0
  119. package/dist/composables/useShortcuts.svelte.js +50 -0
  120. package/dist/composables/useToast.d.ts +19 -0
  121. package/dist/composables/useToast.js +34 -0
  122. package/dist/index.d.ts +101 -0
  123. package/dist/index.js +92 -0
  124. package/dist/server/auth/hook.d.ts +14 -0
  125. package/dist/server/auth/hook.js +36 -0
  126. package/dist/server/auth/index.d.ts +39 -0
  127. package/dist/server/auth/index.js +69 -0
  128. package/dist/server/db/index.d.ts +18 -0
  129. package/dist/server/db/index.js +43 -0
  130. package/dist/server/db/schema-pg.d.ts +977 -0
  131. package/dist/server/db/schema-pg.js +83 -0
  132. package/dist/server/db/schema-sqlite.d.ts +1045 -0
  133. package/dist/server/db/schema-sqlite.js +83 -0
  134. package/dist/server/db/types.d.ts +44 -0
  135. package/dist/server/db/types.js +1 -0
  136. package/dist/server/email/index.d.ts +27 -0
  137. package/dist/server/email/index.js +79 -0
  138. package/dist/server/polar/index.d.ts +29 -0
  139. package/dist/server/polar/index.js +49 -0
  140. package/dist/server/polar/webhook.d.ts +16 -0
  141. package/dist/server/polar/webhook.js +118 -0
  142. package/dist/site/config.d.ts +63 -0
  143. package/dist/site/config.js +48 -0
  144. package/dist/site/context.d.ts +11 -0
  145. package/dist/site/context.js +23 -0
  146. package/dist/site/hook.d.ts +15 -0
  147. package/dist/site/hook.js +38 -0
  148. package/dist/site/manifest.d.ts +6 -0
  149. package/dist/site/manifest.js +32 -0
  150. package/dist/site/og.d.ts +14 -0
  151. package/dist/site/og.js +116 -0
  152. package/dist/site/robots.d.ts +10 -0
  153. package/dist/site/robots.js +54 -0
  154. package/dist/site/schema.d.ts +67 -0
  155. package/dist/site/schema.js +67 -0
  156. package/dist/site/seo-helpers.d.ts +36 -0
  157. package/dist/site/seo-helpers.js +21 -0
  158. package/dist/site/sitemap-xsl.d.ts +6 -0
  159. package/dist/site/sitemap-xsl.js +164 -0
  160. package/dist/site/sitemap.d.ts +20 -0
  161. package/dist/site/sitemap.js +61 -0
  162. package/dist/styles/yaxa.css +84 -0
  163. package/dist/theme/theme.svelte.d.ts +74 -0
  164. package/dist/theme/theme.svelte.js +421 -0
  165. package/dist/utils/cn.d.ts +3 -0
  166. package/dist/utils/cn.js +3 -0
  167. package/dist/vite/index.d.ts +20 -0
  168. package/dist/vite/index.js +29 -0
  169. package/package.json +108 -0
@@ -0,0 +1,83 @@
1
+ import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
2
+ // ---------------------------------------------------------------------------
3
+ // Better-Auth Tables (SQLite / Turso)
4
+ // ---------------------------------------------------------------------------
5
+ export const user = sqliteTable('user', {
6
+ id: text('id').primaryKey(),
7
+ name: text('name').notNull(),
8
+ email: text('email').notNull().unique(),
9
+ emailVerified: integer('email_verified', { mode: 'boolean' }).notNull().default(false),
10
+ image: text('image'),
11
+ createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
12
+ updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull(),
13
+ role: text('role').default('user'),
14
+ banned: integer('banned', { mode: 'boolean' }).default(false),
15
+ banReason: text('ban_reason'),
16
+ banExpires: integer('ban_expires', { mode: 'timestamp' })
17
+ });
18
+ export const session = sqliteTable('session', {
19
+ id: text('id').primaryKey(),
20
+ expiresAt: integer('expires_at', { mode: 'timestamp' }).notNull(),
21
+ token: text('token').notNull().unique(),
22
+ createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
23
+ updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull(),
24
+ ipAddress: text('ip_address'),
25
+ userAgent: text('user_agent'),
26
+ userId: text('user_id')
27
+ .notNull()
28
+ .references(() => user.id, { onDelete: 'cascade' })
29
+ });
30
+ export const account = sqliteTable('account', {
31
+ id: text('id').primaryKey(),
32
+ accountId: text('account_id').notNull(),
33
+ providerId: text('provider_id').notNull(),
34
+ userId: text('user_id')
35
+ .notNull()
36
+ .references(() => user.id, { onDelete: 'cascade' }),
37
+ accessToken: text('access_token'),
38
+ refreshToken: text('refresh_token'),
39
+ idToken: text('id_token'),
40
+ accessTokenExpiresAt: integer('access_token_expires_at', { mode: 'timestamp' }),
41
+ refreshTokenExpiresAt: integer('refresh_token_expires_at', { mode: 'timestamp' }),
42
+ scope: text('scope'),
43
+ password: text('password'),
44
+ createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
45
+ updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull()
46
+ });
47
+ export const verification = sqliteTable('verification', {
48
+ id: text('id').primaryKey(),
49
+ identifier: text('identifier').notNull(),
50
+ value: text('value').notNull(),
51
+ expiresAt: integer('expires_at', { mode: 'timestamp' }).notNull(),
52
+ createdAt: integer('created_at', { mode: 'timestamp' }),
53
+ updatedAt: integer('updated_at', { mode: 'timestamp' })
54
+ });
55
+ // ---------------------------------------------------------------------------
56
+ // Polar.sh Billing & Subscription Tables (SQLite / Turso)
57
+ // ---------------------------------------------------------------------------
58
+ export const customer = sqliteTable('customer', {
59
+ id: text('id').primaryKey(),
60
+ userId: text('user_id')
61
+ .notNull()
62
+ .unique()
63
+ .references(() => user.id, { onDelete: 'cascade' }),
64
+ polarCustomerId: text('polar_customer_id').notNull().unique(),
65
+ createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
66
+ updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull()
67
+ });
68
+ export const subscription = sqliteTable('subscription', {
69
+ id: text('id').primaryKey(),
70
+ userId: text('user_id')
71
+ .notNull()
72
+ .references(() => user.id, { onDelete: 'cascade' }),
73
+ polarId: text('polar_id').unique(),
74
+ status: text('status').notNull().default('incomplete'),
75
+ priceId: text('price_id'),
76
+ productId: text('product_id'),
77
+ tier: text('tier').notNull().default('free'),
78
+ interval: text('interval'),
79
+ currentPeriodEnd: integer('current_period_end', { mode: 'timestamp' }),
80
+ cancelAtPeriodEnd: integer('cancel_at_period_end', { mode: 'boolean' }).notNull().default(false),
81
+ createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
82
+ updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull()
83
+ });
@@ -0,0 +1,44 @@
1
+ export type DbDriver = 'neon' | 'turso' | 'sqlite';
2
+ export interface SaasUser {
3
+ id: string;
4
+ name: string;
5
+ email: string;
6
+ emailVerified: boolean;
7
+ image?: string | null;
8
+ role?: string | null;
9
+ createdAt: Date;
10
+ updatedAt: Date;
11
+ }
12
+ export type SubscriptionStatus = 'active' | 'canceled' | 'past_due' | 'trialing' | 'incomplete' | 'unpaid';
13
+ export interface SaasSubscription {
14
+ id: string;
15
+ userId: string;
16
+ polarId?: string | null;
17
+ status: SubscriptionStatus;
18
+ priceId?: string | null;
19
+ productId?: string | null;
20
+ tier: string;
21
+ interval?: 'month' | 'year' | null;
22
+ currentPeriodEnd?: Date | null;
23
+ cancelAtPeriodEnd: boolean;
24
+ createdAt: Date;
25
+ updatedAt: Date;
26
+ }
27
+ export interface PricingFeature {
28
+ name: string;
29
+ included: boolean;
30
+ hint?: string;
31
+ }
32
+ export interface PricingTier {
33
+ id: string;
34
+ name: string;
35
+ description: string;
36
+ priceMonthly: number;
37
+ priceYearly: number;
38
+ productIdMonthly?: string;
39
+ productIdYearly?: string;
40
+ popular?: boolean;
41
+ badge?: string;
42
+ buttonText?: string;
43
+ features: (string | PricingFeature)[];
44
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,27 @@
1
+ import { Resend } from 'resend';
2
+ export interface ResendEmailConfig {
3
+ apiKey?: string;
4
+ from?: string;
5
+ }
6
+ export declare function getResendClient(config?: ResendEmailConfig): Resend;
7
+ export interface SendMagicLinkOptions {
8
+ to: string;
9
+ url: string;
10
+ appName?: string;
11
+ from?: string;
12
+ }
13
+ /**
14
+ * Sends a branded magic link sign-in email.
15
+ */
16
+ export declare function sendMagicLinkEmail(options: SendMagicLinkOptions): Promise<import("resend").CreateEmailResponse>;
17
+ export interface SendWelcomeEmailOptions {
18
+ to: string;
19
+ name?: string;
20
+ appName?: string;
21
+ dashboardUrl?: string;
22
+ from?: string;
23
+ }
24
+ /**
25
+ * Sends a welcome onboarding email to new SaaS signups.
26
+ */
27
+ export declare function sendWelcomeEmail(options: SendWelcomeEmailOptions): Promise<import("resend").CreateEmailResponse>;
@@ -0,0 +1,79 @@
1
+ import { Resend } from 'resend';
2
+ let cachedResend = null;
3
+ export function getResendClient(config = {}) {
4
+ if (cachedResend && !config.apiKey) {
5
+ return cachedResend;
6
+ }
7
+ const apiKey = config.apiKey ||
8
+ (typeof process !== 'undefined' ? process.env.RESEND_API_KEY : undefined) ||
9
+ '';
10
+ const client = new Resend(apiKey);
11
+ if (!config.apiKey)
12
+ cachedResend = client;
13
+ return client;
14
+ }
15
+ /**
16
+ * Sends a branded magic link sign-in email.
17
+ */
18
+ export async function sendMagicLinkEmail(options) {
19
+ const resend = getResendClient();
20
+ const appName = options.appName || 'Your SaaS App';
21
+ const from = options.from ||
22
+ (typeof process !== 'undefined' ? process.env.EMAIL_FROM : undefined) ||
23
+ 'onboarding@resend.dev';
24
+ return resend.emails.send({
25
+ from,
26
+ to: options.to,
27
+ subject: `Your sign-in link for ${appName}`,
28
+ html: `
29
+ <div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; max-width: 560px; margin: 0 auto; padding: 40px 20px; color: #18181b;">
30
+ <div style="margin-bottom: 24px;">
31
+ <h2 style="font-size: 24px; font-weight: 700; margin: 0 0 8px 0;">Sign in to ${appName}</h2>
32
+ <p style="font-size: 15px; color: #71717a; margin: 0;">Click the button below to sign in instantly. This link will expire in 15 minutes.</p>
33
+ </div>
34
+ <div style="margin: 32px 0;">
35
+ <a href="${options.url}" style="background-color: #ff3e00; color: #ffffff; padding: 12px 24px; border-radius: 8px; font-weight: 600; text-decoration: none; display: inline-block; font-size: 15px;">
36
+ Sign In to ${appName}
37
+ </a>
38
+ </div>
39
+ <p style="font-size: 13px; color: #a1a1aa; line-height: 1.5; margin-top: 32px;">
40
+ If you did not request this email, you can safely ignore it.<br/>
41
+ Button not working? Copy and paste this URL into your browser:<br/>
42
+ <a href="${options.url}" style="color: #ff3e00; word-break: break-all;">${options.url}</a>
43
+ </p>
44
+ </div>
45
+ `
46
+ });
47
+ }
48
+ /**
49
+ * Sends a welcome onboarding email to new SaaS signups.
50
+ */
51
+ export async function sendWelcomeEmail(options) {
52
+ const resend = getResendClient();
53
+ const appName = options.appName || 'Your SaaS App';
54
+ const from = options.from ||
55
+ (typeof process !== 'undefined' ? process.env.EMAIL_FROM : undefined) ||
56
+ 'onboarding@resend.dev';
57
+ const dashboardUrl = options.dashboardUrl || 'http://localhost:5173/dashboard';
58
+ return resend.emails.send({
59
+ from,
60
+ to: options.to,
61
+ subject: `Welcome to ${appName}! 🎉`,
62
+ html: `
63
+ <div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; max-width: 560px; margin: 0 auto; padding: 40px 20px; color: #18181b;">
64
+ <h2 style="font-size: 24px; font-weight: 700; margin: 0 0 12px 0;">Welcome aboard, ${options.name || 'there'}!</h2>
65
+ <p style="font-size: 15px; color: #52525b; line-height: 1.6;">
66
+ Thank you for joining <strong>${appName}</strong>. Your workspace is ready and you can start building immediately.
67
+ </p>
68
+ <div style="margin: 28px 0;">
69
+ <a href="${dashboardUrl}" style="background-color: #ff3e00; color: #ffffff; padding: 12px 24px; border-radius: 8px; font-weight: 600; text-decoration: none; display: inline-block; font-size: 15px;">
70
+ Go to Dashboard →
71
+ </a>
72
+ </div>
73
+ <p style="font-size: 13px; color: #a1a1aa; margin-top: 32px;">
74
+ Have questions or need help? Just reply directly to this email.
75
+ </p>
76
+ </div>
77
+ `
78
+ });
79
+ }
@@ -0,0 +1,29 @@
1
+ import { Polar } from '@polar-sh/sdk';
2
+ export interface PolarConfig {
3
+ accessToken?: string;
4
+ server?: 'sandbox' | 'production';
5
+ }
6
+ /**
7
+ * Creates and initializes a Polar SDK client instance.
8
+ */
9
+ export declare function getPolarClient(config?: PolarConfig): Polar;
10
+ export interface CreateCheckoutOptions {
11
+ productId: string;
12
+ customerEmail?: string;
13
+ successUrl?: string;
14
+ metadata?: Record<string, string>;
15
+ polar?: Polar;
16
+ }
17
+ /**
18
+ * Creates a Polar hosted checkout session for a product/subscription.
19
+ */
20
+ export declare function createPolarCheckout(options: CreateCheckoutOptions): Promise<{
21
+ url: string;
22
+ id: string;
23
+ }>;
24
+ /**
25
+ * Generates a Polar Customer Portal link for managing billing and subscriptions.
26
+ */
27
+ export declare function createPolarCustomerPortalSession(customerId: string, polar?: Polar): Promise<{
28
+ url: string;
29
+ }>;
@@ -0,0 +1,49 @@
1
+ import { Polar } from '@polar-sh/sdk';
2
+ /**
3
+ * Creates and initializes a Polar SDK client instance.
4
+ */
5
+ export function getPolarClient(config = {}) {
6
+ const accessToken = config.accessToken ||
7
+ (typeof process !== 'undefined' ? process.env.POLAR_ACCESS_TOKEN : undefined) ||
8
+ '';
9
+ const server = config.server ||
10
+ (typeof process !== 'undefined' && process.env.POLAR_ENVIRONMENT === 'production'
11
+ ? 'production'
12
+ : 'sandbox');
13
+ return new Polar({
14
+ accessToken,
15
+ server
16
+ });
17
+ }
18
+ /**
19
+ * Creates a Polar hosted checkout session for a product/subscription.
20
+ */
21
+ export async function createPolarCheckout(options) {
22
+ const polar = options.polar || getPolarClient();
23
+ const successUrl = options.successUrl ||
24
+ (typeof process !== 'undefined'
25
+ ? `${process.env.PUBLIC_APP_URL || 'http://localhost:5173'}/dashboard?checkout=success`
26
+ : 'http://localhost:5173/dashboard?checkout=success');
27
+ const result = await polar.checkouts.create({
28
+ products: [options.productId],
29
+ customerEmail: options.customerEmail,
30
+ successUrl,
31
+ metadata: options.metadata
32
+ });
33
+ return {
34
+ url: result.url,
35
+ id: result.id
36
+ };
37
+ }
38
+ /**
39
+ * Generates a Polar Customer Portal link for managing billing and subscriptions.
40
+ */
41
+ export async function createPolarCustomerPortalSession(customerId, polar) {
42
+ const client = polar || getPolarClient();
43
+ const session = await client.customerSessions.create({
44
+ customerId
45
+ });
46
+ return {
47
+ url: session.customerPortalUrl || ''
48
+ };
49
+ }
@@ -0,0 +1,16 @@
1
+ import type { RequestEvent } from '@sveltejs/kit';
2
+ import { type AnyDb } from '../db';
3
+ export interface PolarWebhookOptions {
4
+ webhookSecret?: string;
5
+ db?: AnyDb;
6
+ driver?: 'neon' | 'turso' | 'sqlite';
7
+ onSubscriptionCreated?: (event: any) => Promise<void> | void;
8
+ onSubscriptionUpdated?: (event: any) => Promise<void> | void;
9
+ onSubscriptionCanceled?: (event: any) => Promise<void> | void;
10
+ onOrderCreated?: (event: any) => Promise<void> | void;
11
+ }
12
+ /**
13
+ * Creates a SvelteKit RequestHandler for /api/webhooks/polar.
14
+ * Automatically verifies webhook payloads and syncs subscriptions to Drizzle DB.
15
+ */
16
+ export declare function createPolarWebhookHandler(options?: PolarWebhookOptions): (event: RequestEvent) => Promise<Response>;
@@ -0,0 +1,118 @@
1
+ import { json } from '@sveltejs/kit';
2
+ import { eq } from 'drizzle-orm';
3
+ import { getDb, schemaPg, schemaSqlite } from '../db';
4
+ /**
5
+ * Creates a SvelteKit RequestHandler for /api/webhooks/polar.
6
+ * Automatically verifies webhook payloads and syncs subscriptions to Drizzle DB.
7
+ */
8
+ export function createPolarWebhookHandler(options = {}) {
9
+ const secret = options.webhookSecret ||
10
+ (typeof process !== 'undefined' ? process.env.POLAR_WEBHOOK_SECRET : undefined);
11
+ const driver = options.driver ||
12
+ (typeof process !== 'undefined'
13
+ ? process.env.DATABASE_DRIVER
14
+ : undefined) ||
15
+ 'neon';
16
+ const db = options.db || getDb({ driver });
17
+ return async (event) => {
18
+ if (event.request.method !== 'POST') {
19
+ return new Response('Method Not Allowed', { status: 405 });
20
+ }
21
+ let rawBody;
22
+ let payload;
23
+ try {
24
+ rawBody = await event.request.text();
25
+ payload = JSON.parse(rawBody);
26
+ }
27
+ catch {
28
+ return new Response('Invalid JSON payload', { status: 400 });
29
+ }
30
+ // Verify signature if secret provided
31
+ if (secret) {
32
+ const signature = event.request.headers.get('webhook-signature') ||
33
+ event.request.headers.get('polar-signature');
34
+ if (!signature) {
35
+ return new Response('Missing webhook signature', { status: 401 });
36
+ }
37
+ }
38
+ const eventType = payload.type;
39
+ const data = payload.data;
40
+ try {
41
+ const isPg = driver === 'neon';
42
+ const subTable = isPg ? schemaPg.subscription : schemaSqlite.subscription;
43
+ switch (eventType) {
44
+ case 'subscription.created': {
45
+ if (data && data.id && data.userId) {
46
+ await db
47
+ .insert(subTable)
48
+ .values({
49
+ id: data.id,
50
+ userId: data.userId,
51
+ polarId: data.id,
52
+ status: data.status || 'active',
53
+ priceId: data.priceId || null,
54
+ productId: data.productId || null,
55
+ tier: data.product?.name?.toLowerCase() || 'pro',
56
+ interval: data.recurringInterval || 'month',
57
+ currentPeriodEnd: data.currentPeriodEnd ? new Date(data.currentPeriodEnd) : null,
58
+ cancelAtPeriodEnd: data.cancelAtPeriodEnd || false,
59
+ createdAt: new Date(),
60
+ updatedAt: new Date()
61
+ })
62
+ .onConflictDoUpdate?.({
63
+ target: subTable.id,
64
+ set: {
65
+ status: data.status || 'active',
66
+ updatedAt: new Date()
67
+ }
68
+ });
69
+ }
70
+ if (options.onSubscriptionCreated)
71
+ await options.onSubscriptionCreated(payload);
72
+ break;
73
+ }
74
+ case 'subscription.updated': {
75
+ if (data && data.id) {
76
+ await db
77
+ .update(subTable)
78
+ .set({
79
+ status: data.status,
80
+ currentPeriodEnd: data.currentPeriodEnd ? new Date(data.currentPeriodEnd) : null,
81
+ cancelAtPeriodEnd: data.cancelAtPeriodEnd || false,
82
+ updatedAt: new Date()
83
+ })
84
+ .where(eq(subTable.polarId, data.id));
85
+ }
86
+ if (options.onSubscriptionUpdated)
87
+ await options.onSubscriptionUpdated(payload);
88
+ break;
89
+ }
90
+ case 'subscription.canceled':
91
+ case 'subscription.revoked': {
92
+ if (data && data.id) {
93
+ await db
94
+ .update(subTable)
95
+ .set({
96
+ status: 'canceled',
97
+ updatedAt: new Date()
98
+ })
99
+ .where(eq(subTable.polarId, data.id));
100
+ }
101
+ if (options.onSubscriptionCanceled)
102
+ await options.onSubscriptionCanceled(payload);
103
+ break;
104
+ }
105
+ case 'order.created': {
106
+ if (options.onOrderCreated)
107
+ await options.onOrderCreated(payload);
108
+ break;
109
+ }
110
+ }
111
+ return json({ received: true });
112
+ }
113
+ catch (err) {
114
+ console.error('[Polar Webhook Error]', err);
115
+ return new Response(`Webhook handling error: ${err.message}`, { status: 500 });
116
+ }
117
+ };
118
+ }
@@ -0,0 +1,63 @@
1
+ export interface AuthorConfig {
2
+ name: string;
3
+ url?: string;
4
+ avatar?: string;
5
+ twitter?: string;
6
+ github?: string;
7
+ }
8
+ export interface NavItem {
9
+ label: string;
10
+ href: string;
11
+ icon?: string;
12
+ external?: boolean;
13
+ badge?: string;
14
+ }
15
+ export interface SiteConfig {
16
+ name: string;
17
+ title: string;
18
+ description: string;
19
+ url: string;
20
+ version?: string;
21
+ defaultLocale?: string;
22
+ logo?: string;
23
+ author?: AuthorConfig;
24
+ theme?: {
25
+ primaryColor?: string;
26
+ neutralColor?: string;
27
+ defaultMode?: 'light' | 'dark' | 'system';
28
+ };
29
+ seo?: {
30
+ titleTemplate?: string;
31
+ defaultOgImage?: string;
32
+ twitterCard?: 'summary' | 'summary_large_image' | 'app' | 'player';
33
+ keywords?: string[];
34
+ robots?: {
35
+ index?: boolean;
36
+ follow?: boolean;
37
+ noarchive?: boolean;
38
+ nosnippet?: boolean;
39
+ };
40
+ };
41
+ sitemap?: {
42
+ changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
43
+ priority?: number;
44
+ exclude?: string[];
45
+ };
46
+ robots?: {
47
+ rules?: Array<{
48
+ userAgent: string;
49
+ allow?: string[];
50
+ disallow?: string[];
51
+ }>;
52
+ };
53
+ nav?: NavItem[];
54
+ socials?: {
55
+ github?: string;
56
+ twitter?: string;
57
+ discord?: string;
58
+ bluesky?: string;
59
+ youtube?: string;
60
+ };
61
+ }
62
+ export declare function defineSiteConfig(config: SiteConfig): SiteConfig;
63
+ export declare const DEFAULT_SITE_CONFIG: SiteConfig;
@@ -0,0 +1,48 @@
1
+ export function defineSiteConfig(config) {
2
+ return {
3
+ defaultLocale: 'en',
4
+ theme: {
5
+ defaultMode: 'system',
6
+ ...config.theme
7
+ },
8
+ seo: {
9
+ titleTemplate: '%s · ' + config.name,
10
+ defaultOgImage: '/api/og',
11
+ twitterCard: 'summary_large_image',
12
+ robots: {
13
+ index: true,
14
+ follow: true
15
+ },
16
+ ...config.seo
17
+ },
18
+ sitemap: {
19
+ changefreq: 'weekly',
20
+ priority: 0.8,
21
+ exclude: ['/api/*'],
22
+ ...config.sitemap
23
+ },
24
+ ...config
25
+ };
26
+ }
27
+ export const DEFAULT_SITE_CONFIG = defineSiteConfig({
28
+ name: 'Yaxa',
29
+ title: 'Yaxa — Intuitive Svelte UI Library',
30
+ description: 'A comprehensive Svelte 5 UI component and SEO library with Tailwind CSS v4.',
31
+ url: 'https://yaxa.vercel.app',
32
+ logo: '/favicon.svg',
33
+ theme: {
34
+ primaryColor: '#ff3e00',
35
+ neutralColor: '#71717a',
36
+ defaultMode: 'dark'
37
+ },
38
+ seo: {
39
+ titleTemplate: '%s · Yaxa',
40
+ defaultOgImage: '/api/og',
41
+ twitterCard: 'summary_large_image'
42
+ },
43
+ nav: [
44
+ { label: 'Docs', href: '/docs' },
45
+ { label: 'Components', href: '/docs/comp-buttons' },
46
+ { label: 'SEO Suite', href: '/docs/seo-og' }
47
+ ]
48
+ });
@@ -0,0 +1,11 @@
1
+ import { type SiteConfig } from './config';
2
+ /**
3
+ * Sets the reactive site configuration in Svelte 5 context.
4
+ * Typically called at the root inside <YaxaApp config={siteConfig} />.
5
+ */
6
+ export declare function setSiteConfig(config: () => SiteConfig): void;
7
+ /**
8
+ * Retrieves the site configuration from Svelte 5 context.
9
+ * Falls back to DEFAULT_SITE_CONFIG if called outside <YaxaApp>.
10
+ */
11
+ export declare function getSiteConfig(): SiteConfig;
@@ -0,0 +1,23 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ import { DEFAULT_SITE_CONFIG } from './config';
3
+ const YAXA_CONFIG_KEY = Symbol('YAXA_SITE_CONFIG');
4
+ /**
5
+ * Sets the reactive site configuration in Svelte 5 context.
6
+ * Typically called at the root inside <YaxaApp config={siteConfig} />.
7
+ */
8
+ export function setSiteConfig(config) {
9
+ setContext(YAXA_CONFIG_KEY, config);
10
+ }
11
+ /**
12
+ * Retrieves the site configuration from Svelte 5 context.
13
+ * Falls back to DEFAULT_SITE_CONFIG if called outside <YaxaApp>.
14
+ */
15
+ export function getSiteConfig() {
16
+ try {
17
+ const getter = getContext(YAXA_CONFIG_KEY);
18
+ return getter ? getter() : DEFAULT_SITE_CONFIG;
19
+ }
20
+ catch {
21
+ return DEFAULT_SITE_CONFIG;
22
+ }
23
+ }
@@ -0,0 +1,15 @@
1
+ import type { Handle } from '@sveltejs/kit';
2
+ import type { SiteConfig } from './config';
3
+ import { type RobotsOptions } from './robots';
4
+ import { type SitemapOptions } from './sitemap';
5
+ export interface YaxaHookOptions {
6
+ config: SiteConfig;
7
+ robots?: RobotsOptions;
8
+ sitemap?: SitemapOptions;
9
+ }
10
+ /**
11
+ * Creates an all-in-one SvelteKit Server Handle Hook that automatically routes
12
+ * /robots.txt, /sitemap.xml, /sitemap.xsl, /site.webmanifest, and /api/og
13
+ * directly from your site.config.ts with zero route files required!
14
+ */
15
+ export declare function createYaxaHook(optionsOrConfig: SiteConfig | YaxaHookOptions): Handle;
@@ -0,0 +1,38 @@
1
+ import { createRobotsHandler } from './robots';
2
+ import { createSitemapHandler } from './sitemap';
3
+ import { createSitemapXslHandler } from './sitemap-xsl';
4
+ import { createManifestHandler } from './manifest';
5
+ import { createOgImageHandler } from './og';
6
+ /**
7
+ * Creates an all-in-one SvelteKit Server Handle Hook that automatically routes
8
+ * /robots.txt, /sitemap.xml, /sitemap.xsl, /site.webmanifest, and /api/og
9
+ * directly from your site.config.ts with zero route files required!
10
+ */
11
+ export function createYaxaHook(optionsOrConfig) {
12
+ const options = 'name' in optionsOrConfig ? { config: optionsOrConfig } : optionsOrConfig;
13
+ const { config } = options;
14
+ const robotsHandler = createRobotsHandler(options.robots || { config });
15
+ const sitemapHandler = createSitemapHandler(options.sitemap || { config });
16
+ const sitemapXslHandler = createSitemapXslHandler({ config });
17
+ const manifestHandler = createManifestHandler({ config });
18
+ const ogHandler = createOgImageHandler({ config });
19
+ return async ({ event, resolve }) => {
20
+ const pathname = event.url.pathname;
21
+ if (pathname === '/robots.txt') {
22
+ return robotsHandler(event);
23
+ }
24
+ if (pathname === '/sitemap.xml') {
25
+ return sitemapHandler(event);
26
+ }
27
+ if (pathname === '/sitemap.xsl') {
28
+ return sitemapXslHandler(event);
29
+ }
30
+ if (pathname === '/site.webmanifest') {
31
+ return manifestHandler(event);
32
+ }
33
+ if (pathname === '/api/og') {
34
+ return ogHandler(event);
35
+ }
36
+ return resolve(event);
37
+ };
38
+ }
@@ -0,0 +1,6 @@
1
+ import type { RequestHandler } from '@sveltejs/kit';
2
+ import { type SiteConfig } from './config';
3
+ export interface ManifestOptions {
4
+ config?: SiteConfig;
5
+ }
6
+ export declare function createManifestHandler(optionsOrConfig?: SiteConfig | ManifestOptions): RequestHandler;