@autumnsgrove/groveengine 0.9.3 → 0.9.4
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/dist/components/admin/GutterManager.svelte +314 -35
- package/dist/components/admin/MarkdownEditor.svelte +105 -40
- package/dist/components/admin/MarkdownEditor.svelte.d.ts +2 -0
- package/dist/components/custom/ContentWithGutter.svelte +12 -2
- package/dist/components/custom/GutterItem.svelte +122 -47
- package/dist/components/custom/TableOfContents.svelte +36 -3
- package/dist/components/quota/UpgradePrompt.svelte +12 -16
- package/dist/config/index.d.ts +4 -3
- package/dist/config/index.js +4 -3
- package/dist/config/tiers.d.ts +134 -0
- package/dist/config/tiers.js +402 -0
- package/dist/groveauth/types.d.ts +4 -1
- package/dist/groveauth/types.js +9 -13
- package/dist/payments/shop.d.ts +3 -3
- package/dist/payments/shop.js +271 -95
- package/dist/server/billing.d.ts +29 -0
- package/dist/server/billing.js +80 -0
- package/dist/server/env-validation.d.ts +68 -0
- package/dist/server/env-validation.js +95 -0
- package/dist/server/rate-limits/config.d.ts +24 -96
- package/dist/server/rate-limits/config.js +35 -59
- package/dist/server/rate-limits/tenant.d.ts +1 -69
- package/dist/server/services/database.d.ts +1 -1
- package/dist/server/services/database.js +69 -59
- package/dist/server/services/storage.d.ts +2 -1
- package/dist/server/services/storage.js +115 -91
- package/dist/server/tier-features.d.ts +56 -0
- package/dist/server/tier-features.js +79 -0
- package/dist/ui/components/chrome/Footer.svelte +16 -2
- package/dist/ui/components/chrome/Header.svelte +15 -19
- package/dist/ui/components/chrome/Header.svelte.d.ts +2 -2
- package/dist/ui/components/chrome/ThemeToggle.svelte +86 -27
- package/dist/ui/components/icons/index.d.ts +3 -3
- package/dist/ui/components/icons/index.js +6 -6
- package/dist/ui/components/icons/lucide.d.ts +15 -2
- package/dist/ui/components/icons/lucide.js +15 -3
- package/dist/ui/components/nature/GroveDivider.svelte +24 -28
- package/dist/ui/components/nature/GroveDivider.svelte.d.ts +5 -7
- package/dist/ui/components/nature/{Logo.svelte.d.ts → LogoArchive.svelte.d.ts} +3 -3
- package/dist/ui/components/nature/creatures/Bee.svelte +2 -2
- package/dist/ui/components/nature/creatures/Butterfly.svelte +3 -3
- package/dist/ui/components/nature/creatures/Owl.svelte +2 -2
- package/dist/ui/components/nature/ground/FlowerWild.svelte +3 -3
- package/dist/ui/components/nature/index.d.ts +12 -11
- package/dist/ui/components/nature/index.js +14 -12
- package/dist/ui/components/nature/palette.d.ts +106 -10
- package/dist/ui/components/nature/palette.js +211 -147
- package/dist/ui/components/nature/sky/Sun.svelte +2 -2
- package/dist/ui/components/nature/structural/LatticeWithVine.svelte +2 -2
- package/dist/ui/components/nature/water/LilyPad.svelte +2 -2
- package/dist/ui/components/ui/GlassLogo.svelte +354 -300
- package/dist/ui/components/ui/GlassLogo.svelte.d.ts +76 -13
- package/dist/ui/components/ui/GlassLogoArchive.svelte +415 -0
- package/dist/ui/components/ui/GlassLogoArchive.svelte.d.ts +23 -0
- package/dist/ui/components/ui/Logo.svelte +269 -169
- package/dist/ui/components/ui/Logo.svelte.d.ts +93 -14
- package/dist/ui/components/ui/LogoArchive.svelte +220 -0
- package/dist/ui/components/ui/LogoArchive.svelte.d.ts +20 -0
- package/dist/ui/components/ui/LogoLoader.svelte +1 -1
- package/dist/ui/components/ui/index.d.ts +30 -28
- package/dist/ui/components/ui/index.js +31 -29
- package/dist/ui/stores/season.d.ts +12 -2
- package/dist/ui/stores/season.js +101 -18
- package/dist/ui/types/index.d.ts +6 -0
- package/dist/ui/types/index.js +7 -0
- package/dist/ui/types/season.d.ts +36 -0
- package/dist/ui/types/season.js +86 -0
- package/dist/ui/utils/color.d.ts +69 -0
- package/dist/ui/utils/color.js +155 -0
- package/dist/ui/utils/index.d.ts +2 -1
- package/dist/ui/utils/index.js +5 -2
- package/package.json +22 -20
- package/static/favicon.png +0 -0
- package/static/favicon.svg +6 -0
- package/static/fonts/alagard.ttf +0 -0
- package/LICENSE +0 -378
- /package/dist/ui/components/nature/{Logo.svelte → LogoArchive.svelte} +0 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Tier Configuration
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for all tier-related data across the Grove ecosystem.
|
|
5
|
+
* This file consolidates feature limits, rate limits, pricing, and display info.
|
|
6
|
+
*
|
|
7
|
+
* Tier progression:
|
|
8
|
+
* - free: Meadow-only (no blog) - coming soon
|
|
9
|
+
* - seedling: $8/mo entry tier
|
|
10
|
+
* - sapling: $12/mo mid tier - coming soon
|
|
11
|
+
* - oak: $25/mo with BYOD domain - future
|
|
12
|
+
* - evergreen: $35/mo full service - future
|
|
13
|
+
*/
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// TIER DEFINITIONS
|
|
16
|
+
// =============================================================================
|
|
17
|
+
export const TIERS = {
|
|
18
|
+
free: {
|
|
19
|
+
id: "free",
|
|
20
|
+
order: 0,
|
|
21
|
+
status: "coming_soon",
|
|
22
|
+
limits: {
|
|
23
|
+
posts: 0,
|
|
24
|
+
storage: 0,
|
|
25
|
+
themes: 0,
|
|
26
|
+
navPages: 0,
|
|
27
|
+
commentsPerWeek: 20,
|
|
28
|
+
},
|
|
29
|
+
features: {
|
|
30
|
+
blog: false,
|
|
31
|
+
meadow: true,
|
|
32
|
+
emailForwarding: false,
|
|
33
|
+
fullEmail: false,
|
|
34
|
+
customDomain: false,
|
|
35
|
+
byod: false,
|
|
36
|
+
themeCustomizer: false,
|
|
37
|
+
customFonts: false,
|
|
38
|
+
centennial: false,
|
|
39
|
+
shop: false,
|
|
40
|
+
ai: false,
|
|
41
|
+
analytics: false,
|
|
42
|
+
},
|
|
43
|
+
rateLimits: {
|
|
44
|
+
requests: { limit: 50, windowSeconds: 60 },
|
|
45
|
+
writes: { limit: 20, windowSeconds: 3600 },
|
|
46
|
+
uploads: { limit: 0, windowSeconds: 86400 },
|
|
47
|
+
ai: { limit: 0, windowSeconds: 86400 },
|
|
48
|
+
},
|
|
49
|
+
pricing: {
|
|
50
|
+
monthlyPrice: 0,
|
|
51
|
+
yearlyPrice: 0,
|
|
52
|
+
monthlyPriceCents: 0,
|
|
53
|
+
yearlyPriceCents: 0,
|
|
54
|
+
},
|
|
55
|
+
display: {
|
|
56
|
+
name: "Free",
|
|
57
|
+
tagline: "Just visiting",
|
|
58
|
+
description: "Hang out in Meadow, follow blogs, react and comment.",
|
|
59
|
+
icon: "user",
|
|
60
|
+
bestFor: "Readers",
|
|
61
|
+
featureStrings: [
|
|
62
|
+
"Meadow access",
|
|
63
|
+
"20 comments/week",
|
|
64
|
+
"Follow blogs",
|
|
65
|
+
"React to posts",
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
support: { level: "help_center", displayString: "Help Center" },
|
|
69
|
+
},
|
|
70
|
+
seedling: {
|
|
71
|
+
id: "seedling",
|
|
72
|
+
order: 1,
|
|
73
|
+
status: "available",
|
|
74
|
+
limits: {
|
|
75
|
+
posts: 50,
|
|
76
|
+
storage: 1 * 1024 * 1024 * 1024, // 1 GB
|
|
77
|
+
themes: 3,
|
|
78
|
+
navPages: 0,
|
|
79
|
+
commentsPerWeek: Infinity,
|
|
80
|
+
},
|
|
81
|
+
features: {
|
|
82
|
+
blog: true,
|
|
83
|
+
meadow: true,
|
|
84
|
+
emailForwarding: false,
|
|
85
|
+
fullEmail: false,
|
|
86
|
+
customDomain: false,
|
|
87
|
+
byod: false,
|
|
88
|
+
themeCustomizer: false,
|
|
89
|
+
customFonts: false,
|
|
90
|
+
centennial: false,
|
|
91
|
+
shop: false,
|
|
92
|
+
ai: true,
|
|
93
|
+
analytics: false,
|
|
94
|
+
},
|
|
95
|
+
rateLimits: {
|
|
96
|
+
requests: { limit: 100, windowSeconds: 60 },
|
|
97
|
+
writes: { limit: 50, windowSeconds: 3600 },
|
|
98
|
+
uploads: { limit: 10, windowSeconds: 86400 },
|
|
99
|
+
ai: { limit: 25, windowSeconds: 86400 },
|
|
100
|
+
},
|
|
101
|
+
pricing: {
|
|
102
|
+
monthlyPrice: 8,
|
|
103
|
+
yearlyPrice: 81.6,
|
|
104
|
+
monthlyPriceCents: 800,
|
|
105
|
+
yearlyPriceCents: 8160,
|
|
106
|
+
},
|
|
107
|
+
display: {
|
|
108
|
+
name: "Seedling",
|
|
109
|
+
tagline: "Just planted",
|
|
110
|
+
description: "Perfect for getting started. A quiet corner to call your own.",
|
|
111
|
+
icon: "sprout",
|
|
112
|
+
bestFor: "Curious",
|
|
113
|
+
featureStrings: [
|
|
114
|
+
"50 posts",
|
|
115
|
+
"1 GB storage",
|
|
116
|
+
"3 curated themes",
|
|
117
|
+
"Meadow access",
|
|
118
|
+
"RSS feed",
|
|
119
|
+
"No ads ever",
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
support: { level: "community", displayString: "Community" },
|
|
123
|
+
},
|
|
124
|
+
sapling: {
|
|
125
|
+
id: "sapling",
|
|
126
|
+
order: 2,
|
|
127
|
+
status: "coming_soon",
|
|
128
|
+
limits: {
|
|
129
|
+
posts: 250,
|
|
130
|
+
storage: 5 * 1024 * 1024 * 1024, // 5 GB
|
|
131
|
+
themes: 10,
|
|
132
|
+
navPages: 3,
|
|
133
|
+
commentsPerWeek: Infinity,
|
|
134
|
+
},
|
|
135
|
+
features: {
|
|
136
|
+
blog: true,
|
|
137
|
+
meadow: true,
|
|
138
|
+
emailForwarding: true,
|
|
139
|
+
fullEmail: false,
|
|
140
|
+
customDomain: false,
|
|
141
|
+
byod: false,
|
|
142
|
+
themeCustomizer: false,
|
|
143
|
+
customFonts: false,
|
|
144
|
+
centennial: true,
|
|
145
|
+
shop: true,
|
|
146
|
+
ai: true,
|
|
147
|
+
analytics: false,
|
|
148
|
+
},
|
|
149
|
+
rateLimits: {
|
|
150
|
+
requests: { limit: 500, windowSeconds: 60 },
|
|
151
|
+
writes: { limit: 200, windowSeconds: 3600 },
|
|
152
|
+
uploads: { limit: 50, windowSeconds: 86400 },
|
|
153
|
+
ai: { limit: 100, windowSeconds: 86400 },
|
|
154
|
+
},
|
|
155
|
+
pricing: {
|
|
156
|
+
monthlyPrice: 12,
|
|
157
|
+
yearlyPrice: 122.4,
|
|
158
|
+
monthlyPriceCents: 1200,
|
|
159
|
+
yearlyPriceCents: 12240,
|
|
160
|
+
},
|
|
161
|
+
display: {
|
|
162
|
+
name: "Sapling",
|
|
163
|
+
tagline: "Growing strong",
|
|
164
|
+
description: "For blogs finding their voice. Room to stretch and grow.",
|
|
165
|
+
icon: "tree-deciduous",
|
|
166
|
+
bestFor: "Hobbyists",
|
|
167
|
+
featureStrings: [
|
|
168
|
+
"250 posts",
|
|
169
|
+
"5 GB storage",
|
|
170
|
+
"10 themes",
|
|
171
|
+
"3 nav pages",
|
|
172
|
+
"Email forwarding",
|
|
173
|
+
"Centennial eligible",
|
|
174
|
+
"Everything in Seedling",
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
support: { level: "email", displayString: "Email" },
|
|
178
|
+
},
|
|
179
|
+
oak: {
|
|
180
|
+
id: "oak",
|
|
181
|
+
order: 3,
|
|
182
|
+
status: "future",
|
|
183
|
+
limits: {
|
|
184
|
+
posts: Infinity,
|
|
185
|
+
storage: 20 * 1024 * 1024 * 1024, // 20 GB
|
|
186
|
+
themes: Infinity,
|
|
187
|
+
navPages: 5,
|
|
188
|
+
commentsPerWeek: Infinity,
|
|
189
|
+
},
|
|
190
|
+
features: {
|
|
191
|
+
blog: true,
|
|
192
|
+
meadow: true,
|
|
193
|
+
emailForwarding: true,
|
|
194
|
+
fullEmail: true,
|
|
195
|
+
customDomain: true,
|
|
196
|
+
byod: true,
|
|
197
|
+
themeCustomizer: true,
|
|
198
|
+
customFonts: false,
|
|
199
|
+
centennial: true,
|
|
200
|
+
shop: true,
|
|
201
|
+
ai: true,
|
|
202
|
+
analytics: true,
|
|
203
|
+
},
|
|
204
|
+
rateLimits: {
|
|
205
|
+
requests: { limit: 1000, windowSeconds: 60 },
|
|
206
|
+
writes: { limit: 500, windowSeconds: 3600 },
|
|
207
|
+
uploads: { limit: 200, windowSeconds: 86400 },
|
|
208
|
+
ai: { limit: 500, windowSeconds: 86400 },
|
|
209
|
+
},
|
|
210
|
+
pricing: {
|
|
211
|
+
monthlyPrice: 25,
|
|
212
|
+
yearlyPrice: 255,
|
|
213
|
+
monthlyPriceCents: 2500,
|
|
214
|
+
yearlyPriceCents: 25500,
|
|
215
|
+
},
|
|
216
|
+
display: {
|
|
217
|
+
name: "Oak",
|
|
218
|
+
tagline: "Deep roots",
|
|
219
|
+
description: "Full creative control. Your blog, your rules.",
|
|
220
|
+
icon: "trees",
|
|
221
|
+
bestFor: "Serious Bloggers",
|
|
222
|
+
featureStrings: [
|
|
223
|
+
"Unlimited posts",
|
|
224
|
+
"20 GB storage",
|
|
225
|
+
"Theme customizer",
|
|
226
|
+
"5 nav pages",
|
|
227
|
+
"Bring your own domain",
|
|
228
|
+
"Centennial eligible",
|
|
229
|
+
"Priority support",
|
|
230
|
+
],
|
|
231
|
+
},
|
|
232
|
+
support: { level: "priority", displayString: "Priority" },
|
|
233
|
+
},
|
|
234
|
+
evergreen: {
|
|
235
|
+
id: "evergreen",
|
|
236
|
+
order: 4,
|
|
237
|
+
status: "future",
|
|
238
|
+
limits: {
|
|
239
|
+
posts: Infinity,
|
|
240
|
+
storage: 100 * 1024 * 1024 * 1024, // 100 GB
|
|
241
|
+
themes: Infinity,
|
|
242
|
+
navPages: 8,
|
|
243
|
+
commentsPerWeek: Infinity,
|
|
244
|
+
},
|
|
245
|
+
features: {
|
|
246
|
+
blog: true,
|
|
247
|
+
meadow: true,
|
|
248
|
+
emailForwarding: true,
|
|
249
|
+
fullEmail: true,
|
|
250
|
+
customDomain: true,
|
|
251
|
+
byod: false, // Domain included
|
|
252
|
+
themeCustomizer: true,
|
|
253
|
+
customFonts: true,
|
|
254
|
+
centennial: true,
|
|
255
|
+
shop: true,
|
|
256
|
+
ai: true,
|
|
257
|
+
analytics: true,
|
|
258
|
+
},
|
|
259
|
+
rateLimits: {
|
|
260
|
+
requests: { limit: 5000, windowSeconds: 60 },
|
|
261
|
+
writes: { limit: 2000, windowSeconds: 3600 },
|
|
262
|
+
uploads: { limit: 1000, windowSeconds: 86400 },
|
|
263
|
+
ai: { limit: 2500, windowSeconds: 86400 },
|
|
264
|
+
},
|
|
265
|
+
pricing: {
|
|
266
|
+
monthlyPrice: 35,
|
|
267
|
+
yearlyPrice: 357,
|
|
268
|
+
monthlyPriceCents: 3500,
|
|
269
|
+
yearlyPriceCents: 35700,
|
|
270
|
+
},
|
|
271
|
+
display: {
|
|
272
|
+
name: "Evergreen",
|
|
273
|
+
tagline: "Always flourishing",
|
|
274
|
+
description: "The complete package. Everything Grove has to offer.",
|
|
275
|
+
icon: "crown",
|
|
276
|
+
bestFor: "Professionals",
|
|
277
|
+
featureStrings: [
|
|
278
|
+
"Unlimited everything",
|
|
279
|
+
"100 GB storage",
|
|
280
|
+
"Custom fonts",
|
|
281
|
+
"8 nav pages",
|
|
282
|
+
"Domain included",
|
|
283
|
+
"Centennial eligible",
|
|
284
|
+
"8 hrs/mo dedicated support",
|
|
285
|
+
],
|
|
286
|
+
},
|
|
287
|
+
support: {
|
|
288
|
+
level: "dedicated",
|
|
289
|
+
displayString: "8hrs + Priority",
|
|
290
|
+
includedHours: 8,
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
// =============================================================================
|
|
295
|
+
// HELPER ARRAYS
|
|
296
|
+
// =============================================================================
|
|
297
|
+
export const TIER_ORDER = [
|
|
298
|
+
"free",
|
|
299
|
+
"seedling",
|
|
300
|
+
"sapling",
|
|
301
|
+
"oak",
|
|
302
|
+
"evergreen",
|
|
303
|
+
];
|
|
304
|
+
export const PAID_TIERS = [
|
|
305
|
+
"seedling",
|
|
306
|
+
"sapling",
|
|
307
|
+
"oak",
|
|
308
|
+
"evergreen",
|
|
309
|
+
];
|
|
310
|
+
// =============================================================================
|
|
311
|
+
// HELPER FUNCTIONS
|
|
312
|
+
// =============================================================================
|
|
313
|
+
/**
|
|
314
|
+
* Get tier config by key (throws if invalid).
|
|
315
|
+
*/
|
|
316
|
+
export function getTier(key) {
|
|
317
|
+
return TIERS[key];
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Get tier config safely (returns undefined if invalid).
|
|
321
|
+
*/
|
|
322
|
+
export function getTierSafe(key) {
|
|
323
|
+
return isValidTier(key) ? TIERS[key] : undefined;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Check if a string is a valid tier key.
|
|
327
|
+
*/
|
|
328
|
+
export function isValidTier(key) {
|
|
329
|
+
return key in TIERS;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Check if a tier is a paid tier.
|
|
333
|
+
*/
|
|
334
|
+
export function isPaidTier(key) {
|
|
335
|
+
return PAID_TIERS.includes(key);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Get all tiers with 'available' status.
|
|
339
|
+
*/
|
|
340
|
+
export function getAvailableTiers() {
|
|
341
|
+
return TIER_ORDER.map((k) => TIERS[k]).filter((t) => t.status === "available");
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Get all tiers in order.
|
|
345
|
+
*/
|
|
346
|
+
export function getTiersInOrder() {
|
|
347
|
+
return TIER_ORDER.map((k) => TIERS[k]);
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Check if a tier has a specific feature enabled.
|
|
351
|
+
*/
|
|
352
|
+
export function tierHasFeature(tier, feature) {
|
|
353
|
+
return TIERS[tier].features[feature];
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Get a specific limit for a tier.
|
|
357
|
+
*/
|
|
358
|
+
export function getTierLimit(tier, limit) {
|
|
359
|
+
return TIERS[tier].limits[limit];
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Get rate limits for a tier.
|
|
363
|
+
*/
|
|
364
|
+
export function getTierRateLimits(tier) {
|
|
365
|
+
return TIERS[tier].rateLimits;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Format storage size for display.
|
|
369
|
+
*/
|
|
370
|
+
export function formatStorage(bytes) {
|
|
371
|
+
if (bytes === 0)
|
|
372
|
+
return "—";
|
|
373
|
+
if (bytes === Infinity)
|
|
374
|
+
return "Unlimited";
|
|
375
|
+
const gb = bytes / (1024 * 1024 * 1024);
|
|
376
|
+
return gb >= 1 ? `${gb} GB` : `${bytes / (1024 * 1024)} MB`;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Format a numeric limit for display.
|
|
380
|
+
*/
|
|
381
|
+
export function formatLimit(value) {
|
|
382
|
+
if (value === 0)
|
|
383
|
+
return "—";
|
|
384
|
+
if (value === Infinity)
|
|
385
|
+
return "Unlimited";
|
|
386
|
+
return value.toString();
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Get the next tier in the upgrade path.
|
|
390
|
+
*/
|
|
391
|
+
export function getNextTier(current) {
|
|
392
|
+
const idx = TIER_ORDER.indexOf(current);
|
|
393
|
+
return idx === -1 || idx === TIER_ORDER.length - 1
|
|
394
|
+
? null
|
|
395
|
+
: TIER_ORDER[idx + 1];
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Get all tiers that have a specific feature enabled.
|
|
399
|
+
*/
|
|
400
|
+
export function getTiersWithFeature(feature) {
|
|
401
|
+
return TIER_ORDER.filter((key) => TIERS[key].features[feature]);
|
|
402
|
+
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Type definitions for integrating with GroveAuth service.
|
|
5
5
|
*/
|
|
6
|
+
import { type PaidTierKey } from "../config/tiers.js";
|
|
6
7
|
export interface GroveAuthConfig {
|
|
7
8
|
/** Client ID registered with GroveAuth */
|
|
8
9
|
clientId: string;
|
|
@@ -105,7 +106,7 @@ export interface LoginUrlResult {
|
|
|
105
106
|
state: string;
|
|
106
107
|
codeVerifier: string;
|
|
107
108
|
}
|
|
108
|
-
export type SubscriptionTier =
|
|
109
|
+
export type SubscriptionTier = PaidTierKey;
|
|
109
110
|
export interface UserSubscription {
|
|
110
111
|
id: string;
|
|
111
112
|
user_id: string;
|
|
@@ -146,6 +147,7 @@ export interface CanPostResponse {
|
|
|
146
147
|
}
|
|
147
148
|
/**
|
|
148
149
|
* Post limits per subscription tier.
|
|
150
|
+
* Derived from the unified tier config.
|
|
149
151
|
*
|
|
150
152
|
* Business rationale:
|
|
151
153
|
* - Seedling (50 posts): Entry-level tier for curious newcomers testing the
|
|
@@ -165,6 +167,7 @@ export interface CanPostResponse {
|
|
|
165
167
|
export declare const TIER_POST_LIMITS: Record<SubscriptionTier, number | null>;
|
|
166
168
|
/**
|
|
167
169
|
* Human-readable tier names for UI display.
|
|
170
|
+
* Derived from the unified tier config.
|
|
168
171
|
*/
|
|
169
172
|
export declare const TIER_NAMES: Record<SubscriptionTier, string>;
|
|
170
173
|
/**
|
package/dist/groveauth/types.js
CHANGED
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Type definitions for integrating with GroveAuth service.
|
|
5
5
|
*/
|
|
6
|
+
import { TIERS, PAID_TIERS } from "../config/tiers.js";
|
|
6
7
|
// =============================================================================
|
|
7
|
-
// POST LIMIT CONSTANTS
|
|
8
|
+
// POST LIMIT CONSTANTS (derived from unified config)
|
|
8
9
|
// =============================================================================
|
|
9
10
|
/**
|
|
10
11
|
* Post limits per subscription tier.
|
|
12
|
+
* Derived from the unified tier config.
|
|
11
13
|
*
|
|
12
14
|
* Business rationale:
|
|
13
15
|
* - Seedling (50 posts): Entry-level tier for curious newcomers testing the
|
|
@@ -24,21 +26,15 @@
|
|
|
24
26
|
*
|
|
25
27
|
* @see docs/implementing-post-limits.md for full specification
|
|
26
28
|
*/
|
|
27
|
-
export const TIER_POST_LIMITS =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
evergreen: null, // Unlimited for professionals
|
|
32
|
-
};
|
|
29
|
+
export const TIER_POST_LIMITS = Object.fromEntries(PAID_TIERS.map((key) => [
|
|
30
|
+
key,
|
|
31
|
+
TIERS[key].limits.posts === Infinity ? null : TIERS[key].limits.posts,
|
|
32
|
+
]));
|
|
33
33
|
/**
|
|
34
34
|
* Human-readable tier names for UI display.
|
|
35
|
+
* Derived from the unified tier config.
|
|
35
36
|
*/
|
|
36
|
-
export const TIER_NAMES =
|
|
37
|
-
seedling: "Seedling",
|
|
38
|
-
sapling: "Sapling",
|
|
39
|
-
oak: "Oak",
|
|
40
|
-
evergreen: "Evergreen",
|
|
41
|
-
};
|
|
37
|
+
export const TIER_NAMES = Object.fromEntries(PAID_TIERS.map((key) => [key, TIERS[key].display.name]));
|
|
42
38
|
/**
|
|
43
39
|
* Client-side error class for GroveAuth operations.
|
|
44
40
|
* Thrown when authentication or subscription operations fail.
|
package/dist/payments/shop.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Utilities for managing products, orders, and customers in D1.
|
|
5
5
|
*/
|
|
6
|
-
import type { Product, ProductVariant, ProductType, ProductStatus, Order, OrderStatus, PaymentStatus, Customer, PricingType, BillingInterval } from
|
|
6
|
+
import type { Product, ProductVariant, ProductType, ProductStatus, Order, OrderStatus, PaymentStatus, Customer, PricingType, BillingInterval } from "./types.js";
|
|
7
7
|
interface D1Database {
|
|
8
8
|
prepare(query: string): D1PreparedStatement;
|
|
9
9
|
batch<T = unknown>(statements: D1PreparedStatement[]): Promise<D1Result<T>[]>;
|
|
@@ -73,7 +73,7 @@ export declare function createVariant(db: D1Database, productId: string, tenantI
|
|
|
73
73
|
billingInterval?: BillingInterval;
|
|
74
74
|
billingIntervalCount?: number;
|
|
75
75
|
inventoryQuantity?: number;
|
|
76
|
-
inventoryPolicy?:
|
|
76
|
+
inventoryPolicy?: "deny" | "continue";
|
|
77
77
|
trackInventory?: boolean;
|
|
78
78
|
downloadUrl?: string;
|
|
79
79
|
downloadLimit?: number;
|
|
@@ -93,7 +93,7 @@ export declare function updateVariant(db: D1Database, variantId: string, data: P
|
|
|
93
93
|
billingInterval: BillingInterval;
|
|
94
94
|
billingIntervalCount: number;
|
|
95
95
|
inventoryQuantity: number;
|
|
96
|
-
inventoryPolicy:
|
|
96
|
+
inventoryPolicy: "deny" | "continue";
|
|
97
97
|
trackInventory: boolean;
|
|
98
98
|
downloadUrl: string;
|
|
99
99
|
downloadLimit: number;
|