@soulbatical/tetra-core 0.6.1 → 0.8.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 (33) hide show
  1. package/dist/frontend/index.d.ts +0 -2
  2. package/dist/frontend/index.d.ts.map +1 -1
  3. package/dist/frontend/index.js +0 -1
  4. package/dist/frontend/index.js.map +1 -1
  5. package/dist/shared/billing/SeatBillingService.d.ts +106 -0
  6. package/dist/shared/billing/SeatBillingService.d.ts.map +1 -0
  7. package/dist/shared/billing/SeatBillingService.js +292 -0
  8. package/dist/shared/billing/SeatBillingService.js.map +1 -0
  9. package/dist/shared/billing/index.d.ts +4 -0
  10. package/dist/shared/billing/index.d.ts.map +1 -1
  11. package/dist/shared/billing/index.js +2 -0
  12. package/dist/shared/billing/index.js.map +1 -1
  13. package/dist/shared/billing/seat-pricing.d.ts +53 -0
  14. package/dist/shared/billing/seat-pricing.d.ts.map +1 -0
  15. package/dist/shared/billing/seat-pricing.js +81 -0
  16. package/dist/shared/billing/seat-pricing.js.map +1 -0
  17. package/dist/shared/license/constants.d.ts +12 -10
  18. package/dist/shared/license/constants.d.ts.map +1 -1
  19. package/dist/shared/license/constants.js +12 -10
  20. package/dist/shared/license/constants.js.map +1 -1
  21. package/dist/shared/license/generator.d.ts +9 -6
  22. package/dist/shared/license/generator.d.ts.map +1 -1
  23. package/dist/shared/license/generator.js +15 -13
  24. package/dist/shared/license/generator.js.map +1 -1
  25. package/dist/shared/license/index.d.ts +2 -2
  26. package/dist/shared/license/index.d.ts.map +1 -1
  27. package/dist/shared/license/index.js +4 -3
  28. package/dist/shared/license/index.js.map +1 -1
  29. package/dist/shared/license/validator.d.ts +2 -8
  30. package/dist/shared/license/validator.d.ts.map +1 -1
  31. package/dist/shared/license/validator.js +12 -21
  32. package/dist/shared/license/validator.js.map +1 -1
  33. package/package.json +1 -1
@@ -16,8 +16,6 @@ export { StorageDropzone } from './storage/StorageDropzone.js';
16
16
  export type { StorageDropzoneProps } from './storage/StorageDropzone.js';
17
17
  export { useFeature } from './hooks/useFeature.js';
18
18
  export type { UseFeatureOptions, UseFeatureResult } from './hooks/useFeature.js';
19
- export { useClickToConfirm } from './hooks/useClickToConfirm.js';
20
- export type { UseClickToConfirmOptions, UseClickToConfirmResult } from './hooks/useClickToConfirm.js';
21
19
  export { FeatureTable } from './components/FeatureTable.js';
22
20
  export type { FeatureTableProps, CellRendererMap } from './components/FeatureTable.js';
23
21
  export { FeatureFilters } from './components/FeatureFilters.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/frontend/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACzG,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AACnH,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,YAAY,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEzE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,YAAY,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAEtG,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEvF,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,YAAY,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAE1E,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAExF,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/frontend/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACzG,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AACnH,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,YAAY,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEzE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEjF,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEvF,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,YAAY,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAE1E,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAExF,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
@@ -12,7 +12,6 @@ export { configureStorageUrls } from './storage/storageUrl.js';
12
12
  export { useStorageUpload } from './storage/useStorageUpload.js';
13
13
  export { StorageDropzone } from './storage/StorageDropzone.js';
14
14
  export { useFeature } from './hooks/useFeature.js';
15
- export { useClickToConfirm } from './hooks/useClickToConfirm.js';
16
15
  export { FeatureTable } from './components/FeatureTable.js';
17
16
  export { FeatureFilters } from './components/FeatureFilters.js';
18
17
  export { FeatureForm } from './components/FeatureForm.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/frontend/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEjE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAG/D,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAGnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAGjE,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAG5D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAGhE,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAG1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/frontend/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEjE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAG/D,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAGnD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAG5D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAGhE,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAG1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * SeatBillingService — Per-user (seat) billing with volume discounts
3
+ *
4
+ * Extends the base BillingService with metered/seat-based pricing.
5
+ * Each billing period, Tetra counts authenticated users and creates
6
+ * a Stripe invoice for the correct amount based on the tier.
7
+ *
8
+ * Flow:
9
+ * 1. Customer signs up via Stripe Checkout (subscription with $0 base price)
10
+ * 2. Heartbeat reports user count monthly
11
+ * 3. At billing time: count users → calculate tier price → create Stripe invoice item
12
+ * 4. Stripe charges the customer
13
+ *
14
+ * Usage:
15
+ * ```typescript
16
+ * import { SeatBillingService } from '@soulbatical/tetra-core/billing';
17
+ *
18
+ * const seatBilling = new SeatBillingService({
19
+ * stripe: { secretKey: '...', webhookSecret: '...' },
20
+ * getUserCount: async (orgId) => { ... },
21
+ * ...config,
22
+ * });
23
+ *
24
+ * // Report usage (call monthly via cron or heartbeat)
25
+ * await seatBilling.reportUsage(orgId);
26
+ *
27
+ * // Or report for all active subscriptions
28
+ * await seatBilling.reportAllUsage();
29
+ * ```
30
+ *
31
+ * @module @soulbatical/tetra-core/billing
32
+ */
33
+ import type { SupabaseClient } from '@supabase/supabase-js';
34
+ import type { SeatTier } from './seat-pricing.js';
35
+ export interface SeatBillingConfig {
36
+ stripe: {
37
+ secretKey: string;
38
+ webhookSecret: string;
39
+ };
40
+ /** Stripe Price ID for the base subscription (should be $0 or minimal) */
41
+ stripeBasePriceId: string;
42
+ /** Frontend URL for redirect after checkout */
43
+ frontendUrl: string;
44
+ successPath: string;
45
+ cancelPath: string;
46
+ /** Product name (shown on invoices) */
47
+ productName: string;
48
+ /** Function to count authenticated users for an organization */
49
+ getUserCount: (orgId: string) => Promise<number>;
50
+ /** Function to get a system-level DB client */
51
+ getSystemDB: () => SupabaseClient;
52
+ /** Function to get a webhook-level DB client */
53
+ getWebhookDB: (context: string) => SupabaseClient;
54
+ /** Custom seat tiers (defaults to Tetra standard tiers) */
55
+ tiers?: SeatTier[];
56
+ /** Optional callbacks */
57
+ onSubscriptionCreated?: (orgId: string) => Promise<void>;
58
+ onSubscriptionCanceled?: (orgId: string) => Promise<void>;
59
+ onUsageReported?: (orgId: string, users: number, amountCents: number) => Promise<void>;
60
+ }
61
+ export declare class SeatBillingService {
62
+ private config;
63
+ private stripeClient;
64
+ private tiers;
65
+ constructor(config: SeatBillingConfig);
66
+ private getStripe;
67
+ /**
68
+ * Create a Stripe Checkout session for seat-based billing.
69
+ * The subscription starts with a $0 base price. Actual charges
70
+ * come from usage-based invoice items added each billing period.
71
+ */
72
+ createCheckout(orgId: string, email?: string): Promise<{
73
+ url: string;
74
+ }>;
75
+ /**
76
+ * Report seat usage for a single organization.
77
+ * Counts current users, calculates the tier price, and creates
78
+ * a Stripe invoice item for the upcoming invoice.
79
+ */
80
+ reportUsage(orgId: string): Promise<{
81
+ users: number;
82
+ amountCents: number;
83
+ }>;
84
+ /**
85
+ * Report usage for all active seat-based subscriptions.
86
+ * Call this monthly via a cron job.
87
+ */
88
+ reportAllUsage(): Promise<{
89
+ reported: number;
90
+ errors: number;
91
+ }>;
92
+ /**
93
+ * Calculate the price for a given number of users.
94
+ * Useful for showing estimated pricing in the UI.
95
+ */
96
+ calculatePrice(users: number): import("./seat-pricing.js").SeatPriceResult;
97
+ /**
98
+ * Handle Stripe webhook events for seat billing.
99
+ * Delegates to BillingService for standard subscription events.
100
+ */
101
+ handleStripeWebhook(rawBody: Buffer, signature: string): Promise<void>;
102
+ private handleCheckoutCompleted;
103
+ private handleSubscriptionDeleted;
104
+ private handlePaymentFailed;
105
+ }
106
+ //# sourceMappingURL=SeatBillingService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SeatBillingService.d.ts","sourceRoot":"","sources":["../../../src/shared/billing/SeatBillingService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAKlD,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE;QACN,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IAEF,0EAA0E;IAC1E,iBAAiB,EAAE,MAAM,CAAC;IAE1B,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IAEnB,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC;IAEpB,gEAAgE;IAChE,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAEjD,+CAA+C;IAC/C,WAAW,EAAE,MAAM,cAAc,CAAC;IAElC,gDAAgD;IAChD,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,cAAc,CAAC;IAElD,2DAA2D;IAC3D,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IAEnB,yBAAyB;IACzB,qBAAqB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,sBAAsB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACxF;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,KAAK,CAAa;gBAEd,MAAM,EAAE,iBAAiB;YAKvB,SAAS;IAUvB;;;;OAIG;IACG,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IA4C7E;;;;OAIG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IA4EjF;;;OAGG;IACG,cAAc,IAAI,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAiCrE;;;OAGG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM;IAM5B;;;OAGG;IACG,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YA2B9D,uBAAuB;YA6BvB,yBAAyB;YAwBzB,mBAAmB;CAkBlC"}
@@ -0,0 +1,292 @@
1
+ /**
2
+ * SeatBillingService — Per-user (seat) billing with volume discounts
3
+ *
4
+ * Extends the base BillingService with metered/seat-based pricing.
5
+ * Each billing period, Tetra counts authenticated users and creates
6
+ * a Stripe invoice for the correct amount based on the tier.
7
+ *
8
+ * Flow:
9
+ * 1. Customer signs up via Stripe Checkout (subscription with $0 base price)
10
+ * 2. Heartbeat reports user count monthly
11
+ * 3. At billing time: count users → calculate tier price → create Stripe invoice item
12
+ * 4. Stripe charges the customer
13
+ *
14
+ * Usage:
15
+ * ```typescript
16
+ * import { SeatBillingService } from '@soulbatical/tetra-core/billing';
17
+ *
18
+ * const seatBilling = new SeatBillingService({
19
+ * stripe: { secretKey: '...', webhookSecret: '...' },
20
+ * getUserCount: async (orgId) => { ... },
21
+ * ...config,
22
+ * });
23
+ *
24
+ * // Report usage (call monthly via cron or heartbeat)
25
+ * await seatBilling.reportUsage(orgId);
26
+ *
27
+ * // Or report for all active subscriptions
28
+ * await seatBilling.reportAllUsage();
29
+ * ```
30
+ *
31
+ * @module @soulbatical/tetra-core/billing
32
+ */
33
+ import { calculateSeatPrice, seatPriceToCents, DEFAULT_SEAT_TIERS } from './seat-pricing.js';
34
+ import { createLogger } from '../../utils/logger.js';
35
+ const logger = createLogger('billing:seat');
36
+ export class SeatBillingService {
37
+ config;
38
+ stripeClient = null;
39
+ tiers;
40
+ constructor(config) {
41
+ this.config = config;
42
+ this.tiers = config.tiers ?? DEFAULT_SEAT_TIERS;
43
+ }
44
+ async getStripe() {
45
+ if (!this.stripeClient) {
46
+ const stripe = (await import('stripe')).default;
47
+ this.stripeClient = new stripe(this.config.stripe.secretKey);
48
+ }
49
+ return this.stripeClient;
50
+ }
51
+ // ─── Checkout ─────────────────────────────────────────────
52
+ /**
53
+ * Create a Stripe Checkout session for seat-based billing.
54
+ * The subscription starts with a $0 base price. Actual charges
55
+ * come from usage-based invoice items added each billing period.
56
+ */
57
+ async createCheckout(orgId, email) {
58
+ const stripe = await this.getStripe();
59
+ const db = this.config.getSystemDB();
60
+ // Get or create Stripe customer
61
+ const { data: org } = await db
62
+ .from('organizations')
63
+ .select('id, name, contact_email, stripe_customer_id')
64
+ .eq('id', orgId)
65
+ .single();
66
+ let customerId = org?.stripe_customer_id;
67
+ if (!customerId) {
68
+ const customer = await stripe.customers.create({
69
+ name: org?.name || undefined,
70
+ email: email || org?.contact_email || undefined,
71
+ metadata: { organization_id: orgId },
72
+ });
73
+ customerId = customer.id;
74
+ await db
75
+ .from('organizations')
76
+ .update({ stripe_customer_id: customerId })
77
+ .eq('id', orgId);
78
+ }
79
+ const session = await stripe.checkout.sessions.create({
80
+ customer: customerId,
81
+ mode: 'subscription',
82
+ line_items: [{ price: this.config.stripeBasePriceId, quantity: 1 }],
83
+ success_url: `${this.config.frontendUrl}${this.config.successPath}`,
84
+ cancel_url: `${this.config.frontendUrl}${this.config.cancelPath}`,
85
+ metadata: { org_id: orgId, billing_type: 'seat' },
86
+ subscription_data: {
87
+ metadata: { org_id: orgId, billing_type: 'seat' },
88
+ },
89
+ });
90
+ return { url: session.url };
91
+ }
92
+ // ─── Usage Reporting ──────────────────────────────────────
93
+ /**
94
+ * Report seat usage for a single organization.
95
+ * Counts current users, calculates the tier price, and creates
96
+ * a Stripe invoice item for the upcoming invoice.
97
+ */
98
+ async reportUsage(orgId) {
99
+ const users = await this.config.getUserCount(orgId);
100
+ const price = calculateSeatPrice(users, this.tiers);
101
+ if (price.monthlyPrice === 0) {
102
+ logger.debug({ orgId, users }, 'No charge — within free tier');
103
+ return { users, amountCents: 0 };
104
+ }
105
+ const amountCents = seatPriceToCents(price.monthlyPrice);
106
+ const db = this.config.getSystemDB();
107
+ // Get Stripe subscription
108
+ const { data: sub } = await db
109
+ .from('subscriptions')
110
+ .select('external_subscription_id, external_customer_id')
111
+ .eq('organization_id', orgId)
112
+ .eq('provider', 'stripe')
113
+ .neq('status', 'canceled')
114
+ .order('created_at', { ascending: false })
115
+ .limit(1)
116
+ .single();
117
+ if (!sub?.external_subscription_id) {
118
+ logger.warn({ orgId }, 'No active subscription found — skipping usage report');
119
+ return { users, amountCents: 0 };
120
+ }
121
+ const stripe = await this.getStripe();
122
+ // Get the subscription to find the current invoice
123
+ const subscription = await stripe.subscriptions.retrieve(sub.external_subscription_id);
124
+ // Create an invoice item for the seat usage
125
+ await stripe.invoiceItems.create({
126
+ customer: sub.external_customer_id,
127
+ subscription: sub.external_subscription_id,
128
+ amount: amountCents,
129
+ currency: 'eur',
130
+ description: `${this.config.productName} — ${users} users (${price.tierPricePerUser > 0 ? `€${price.tierPricePerUser}/user` : 'free tier'}${price.minimumApplied ? ', minimum applied' : ''})`,
131
+ metadata: {
132
+ org_id: orgId,
133
+ user_count: String(users),
134
+ tier_price: String(price.tierPricePerUser),
135
+ minimum_applied: String(price.minimumApplied),
136
+ period: subscription.current_period_start
137
+ ? new Date(subscription.current_period_start * 1000).toISOString().slice(0, 7)
138
+ : undefined,
139
+ },
140
+ });
141
+ // Log the usage report
142
+ await db.from('subscription_events').insert({
143
+ organization_id: orgId,
144
+ subscription_id: null,
145
+ event_type: 'usage_reported',
146
+ provider: 'stripe',
147
+ data: {
148
+ user_count: users,
149
+ amount_cents: amountCents,
150
+ tier_price_per_user: price.tierPricePerUser,
151
+ minimum_applied: price.minimumApplied,
152
+ effective_price_per_user: price.effectivePricePerUser,
153
+ },
154
+ source: 'system',
155
+ });
156
+ logger.info({ orgId, users, amountCents, tierPrice: price.tierPricePerUser }, 'Seat usage reported to Stripe');
157
+ await this.config.onUsageReported?.(orgId, users, amountCents);
158
+ return { users, amountCents };
159
+ }
160
+ /**
161
+ * Report usage for all active seat-based subscriptions.
162
+ * Call this monthly via a cron job.
163
+ */
164
+ async reportAllUsage() {
165
+ const db = this.config.getSystemDB();
166
+ const { data: subs } = await db
167
+ .from('subscriptions')
168
+ .select('organization_id')
169
+ .eq('provider', 'stripe')
170
+ .in('status', ['active', 'trialing']);
171
+ if (!subs || subs.length === 0) {
172
+ logger.info('No active subscriptions to report');
173
+ return { reported: 0, errors: 0 };
174
+ }
175
+ let reported = 0;
176
+ let errors = 0;
177
+ for (const sub of subs) {
178
+ try {
179
+ await this.reportUsage(sub.organization_id);
180
+ reported++;
181
+ }
182
+ catch (err) {
183
+ logger.error({ orgId: sub.organization_id, error: err }, 'Failed to report usage');
184
+ errors++;
185
+ }
186
+ }
187
+ logger.info({ reported, errors, total: subs.length }, 'Batch usage reporting complete');
188
+ return { reported, errors };
189
+ }
190
+ // ─── Price calculation (public, for API endpoints) ────────
191
+ /**
192
+ * Calculate the price for a given number of users.
193
+ * Useful for showing estimated pricing in the UI.
194
+ */
195
+ calculatePrice(users) {
196
+ return calculateSeatPrice(users, this.tiers);
197
+ }
198
+ // ─── Webhook handling ─────────────────────────────────────
199
+ /**
200
+ * Handle Stripe webhook events for seat billing.
201
+ * Delegates to BillingService for standard subscription events.
202
+ */
203
+ async handleStripeWebhook(rawBody, signature) {
204
+ const stripe = await this.getStripe();
205
+ let event;
206
+ try {
207
+ event = stripe.webhooks.constructEvent(rawBody, signature, this.config.stripe.webhookSecret);
208
+ }
209
+ catch (err) {
210
+ logger.error({ error: err.message }, 'Stripe signature verification failed');
211
+ throw new Error('Invalid signature');
212
+ }
213
+ const db = this.config.getWebhookDB('stripe');
214
+ logger.info({ type: event.type, id: event.id }, 'Stripe webhook received');
215
+ switch (event.type) {
216
+ case 'checkout.session.completed':
217
+ await this.handleCheckoutCompleted(event, db);
218
+ break;
219
+ case 'customer.subscription.deleted':
220
+ await this.handleSubscriptionDeleted(event, db);
221
+ break;
222
+ case 'invoice.payment_failed':
223
+ await this.handlePaymentFailed(event, db);
224
+ break;
225
+ }
226
+ }
227
+ async handleCheckoutCompleted(event, db) {
228
+ const session = event.data.object;
229
+ const orgId = session.metadata?.org_id;
230
+ if (!orgId)
231
+ return;
232
+ const stripeSubId = typeof session.subscription === 'string' ? session.subscription : null;
233
+ const customerId = typeof session.customer === 'string' ? session.customer : '';
234
+ const now = new Date();
235
+ const periodEnd = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000);
236
+ await db.from('subscriptions').insert({
237
+ organization_id: orgId,
238
+ provider: 'stripe',
239
+ external_subscription_id: stripeSubId,
240
+ external_customer_id: customerId,
241
+ plan: 'tetra-seat',
242
+ billing_cycle: 'monthly',
243
+ price_amount_cents: 0, // Base price is 0, charges come via invoice items
244
+ status: 'active',
245
+ current_period_start: now.toISOString(),
246
+ current_period_end: periodEnd.toISOString(),
247
+ });
248
+ await db.from('organizations').update({ plan: 'tetra-seat' }).eq('id', orgId);
249
+ logger.info({ orgId, provider: 'stripe' }, 'Seat subscription created');
250
+ await this.config.onSubscriptionCreated?.(orgId);
251
+ }
252
+ async handleSubscriptionDeleted(event, db) {
253
+ const stripeSub = event.data.object;
254
+ const { data: sub } = await db
255
+ .from('subscriptions')
256
+ .select('id, organization_id')
257
+ .eq('external_subscription_id', stripeSub.id)
258
+ .single();
259
+ if (!sub)
260
+ return;
261
+ await db
262
+ .from('subscriptions')
263
+ .update({
264
+ status: 'canceled',
265
+ canceled_at: new Date().toISOString(),
266
+ ended_at: new Date().toISOString(),
267
+ })
268
+ .eq('id', sub.id);
269
+ await db.from('organizations').update({ plan: 'free' }).eq('id', sub.organization_id);
270
+ logger.info({ orgId: sub.organization_id }, 'Seat subscription canceled');
271
+ await this.config.onSubscriptionCanceled?.(sub.organization_id);
272
+ }
273
+ async handlePaymentFailed(event, db) {
274
+ const invoice = event.data.object;
275
+ const invoiceAny = invoice;
276
+ const subId = typeof invoiceAny.subscription === 'string' ? invoiceAny.subscription : null;
277
+ if (!subId)
278
+ return;
279
+ const { data: sub } = await db
280
+ .from('subscriptions')
281
+ .select('id, organization_id')
282
+ .eq('external_subscription_id', subId)
283
+ .single();
284
+ if (!sub)
285
+ return;
286
+ await db
287
+ .from('subscriptions')
288
+ .update({ status: 'past_due', updated_at: new Date().toISOString() })
289
+ .eq('id', sub.id);
290
+ }
291
+ }
292
+ //# sourceMappingURL=SeatBillingService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SeatBillingService.js","sourceRoot":"","sources":["../../../src/shared/billing/SeatBillingService.ts"],"names":[],"mappings":"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"}
@@ -21,6 +21,10 @@
21
21
  * ```
22
22
  */
23
23
  export { BillingService } from './BillingService.js';
24
+ export { SeatBillingService } from './SeatBillingService.js';
25
+ export { calculateSeatPrice, seatPriceToCents, DEFAULT_SEAT_TIERS } from './seat-pricing.js';
24
26
  export { addBillingRoutes, addBillingWebhookRoutes } from './routes.js';
25
27
  export type { PlanConfig, BillingConfig, BillingCycle, BillingProvider, SubscriptionStatus, SubscriptionRecord, BillingStatusResponse, BillingRouteOptions, } from './types.js';
28
+ export type { SeatBillingConfig } from './SeatBillingService.js';
29
+ export type { SeatTier, SeatPriceResult } from './seat-pricing.js';
26
30
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/billing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACxE,YAAY,EACV,UAAU,EACV,aAAa,EACb,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/billing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACxE,YAAY,EACV,UAAU,EACV,aAAa,EACb,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
@@ -21,5 +21,7 @@
21
21
  * ```
22
22
  */
23
23
  export { BillingService } from './BillingService.js';
24
+ export { SeatBillingService } from './SeatBillingService.js';
25
+ export { calculateSeatPrice, seatPriceToCents, DEFAULT_SEAT_TIERS } from './seat-pricing.js';
24
26
  export { addBillingRoutes, addBillingWebhookRoutes } from './routes.js';
25
27
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/shared/billing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/shared/billing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Seat-based pricing calculator
3
+ *
4
+ * Shared logic for per-user pricing with volume discounts and tier minimums.
5
+ * Used by: website pricing calculator, SeatBillingService, license validation.
6
+ *
7
+ * @module @soulbatical/tetra-core/billing
8
+ */
9
+ export interface SeatTier {
10
+ /** Minimum users for this tier (inclusive) */
11
+ min: number;
12
+ /** Maximum users for this tier (inclusive, Infinity for last tier) */
13
+ max: number;
14
+ /** Price per user per month in EUR */
15
+ pricePerUser: number;
16
+ /** Minimum monthly charge for this tier (prevents price dips at tier boundary) */
17
+ minimum: number;
18
+ }
19
+ /** Default Tetra seat pricing tiers */
20
+ export declare const DEFAULT_SEAT_TIERS: SeatTier[];
21
+ export interface SeatPriceResult {
22
+ /** Total monthly price in EUR */
23
+ monthlyPrice: number;
24
+ /** Total yearly price in EUR */
25
+ yearlyPrice: number;
26
+ /** Number of free users */
27
+ freeUsers: number;
28
+ /** Number of paid users */
29
+ paidUsers: number;
30
+ /** Effective price per user (total / users) */
31
+ effectivePricePerUser: number;
32
+ /** Which tier the user count falls in */
33
+ tierIndex: number;
34
+ /** The tier's listed price per user */
35
+ tierPricePerUser: number;
36
+ /** Whether the minimum was applied */
37
+ minimumApplied: boolean;
38
+ }
39
+ /**
40
+ * Calculate the monthly price for a given number of seat users.
41
+ *
42
+ * Rules:
43
+ * - First 10 users are free
44
+ * - Price per user decreases with volume
45
+ * - Each tier has a minimum monthly charge (prevents price dips at boundary)
46
+ * - For 1000+ users, the last tier rate applies
47
+ */
48
+ export declare function calculateSeatPrice(users: number, tiers?: SeatTier[]): SeatPriceResult;
49
+ /**
50
+ * Convert seat price to Stripe-compatible amount in cents.
51
+ */
52
+ export declare function seatPriceToCents(monthlyPrice: number): number;
53
+ //# sourceMappingURL=seat-pricing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seat-pricing.d.ts","sourceRoot":"","sources":["../../../src/shared/billing/seat-pricing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,QAAQ;IACvB,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,sEAAsE;IACtE,GAAG,EAAE,MAAM,CAAC;IACZ,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,uCAAuC;AACvC,eAAO,MAAM,kBAAkB,EAAE,QAAQ,EAMxC,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,iCAAiC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,qBAAqB,EAAE,MAAM,CAAC;IAC9B,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,gBAAgB,EAAE,MAAM,CAAC;IACzB,sCAAsC;IACtC,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,QAAQ,EAAuB,GACrC,eAAe,CAuDjB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE7D"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Seat-based pricing calculator
3
+ *
4
+ * Shared logic for per-user pricing with volume discounts and tier minimums.
5
+ * Used by: website pricing calculator, SeatBillingService, license validation.
6
+ *
7
+ * @module @soulbatical/tetra-core/billing
8
+ */
9
+ /** Default Tetra seat pricing tiers */
10
+ export const DEFAULT_SEAT_TIERS = [
11
+ { min: 1, max: 10, pricePerUser: 0, minimum: 0 },
12
+ { min: 11, max: 100, pricePerUser: 3.90, minimum: 0 },
13
+ { min: 101, max: 250, pricePerUser: 2.90, minimum: 351 },
14
+ { min: 251, max: 500, pricePerUser: 1.90, minimum: 725 },
15
+ { min: 501, max: 1000, pricePerUser: 0.90, minimum: 950 },
16
+ ];
17
+ /**
18
+ * Calculate the monthly price for a given number of seat users.
19
+ *
20
+ * Rules:
21
+ * - First 10 users are free
22
+ * - Price per user decreases with volume
23
+ * - Each tier has a minimum monthly charge (prevents price dips at boundary)
24
+ * - For 1000+ users, the last tier rate applies
25
+ */
26
+ export function calculateSeatPrice(users, tiers = DEFAULT_SEAT_TIERS) {
27
+ if (users <= 0) {
28
+ return {
29
+ monthlyPrice: 0,
30
+ yearlyPrice: 0,
31
+ freeUsers: 0,
32
+ paidUsers: 0,
33
+ effectivePricePerUser: 0,
34
+ tierIndex: 0,
35
+ tierPricePerUser: 0,
36
+ minimumApplied: false,
37
+ };
38
+ }
39
+ // Find the tier
40
+ let tierIndex = 0;
41
+ let tierPricePerUser = 0;
42
+ for (let i = 0; i < tiers.length; i++) {
43
+ if (users >= tiers[i].min && users <= tiers[i].max) {
44
+ tierIndex = i;
45
+ tierPricePerUser = tiers[i].pricePerUser;
46
+ break;
47
+ }
48
+ }
49
+ // Beyond last tier: use last tier's rate
50
+ if (users > tiers[tiers.length - 1].max) {
51
+ const lastTier = tiers[tiers.length - 1];
52
+ tierIndex = tiers.length - 1;
53
+ tierPricePerUser = lastTier.pricePerUser;
54
+ }
55
+ const tier = tiers[tierIndex];
56
+ const freeUsers = Math.min(users, tiers[0].max);
57
+ const paidUsers = Math.max(0, users - tiers[0].max);
58
+ // Calculate raw price
59
+ const rawPrice = users * tierPricePerUser;
60
+ // Apply minimum
61
+ const minimum = tier?.minimum ?? 0;
62
+ const minimumApplied = rawPrice < minimum && minimum > 0;
63
+ const monthlyPrice = Math.max(rawPrice, minimum);
64
+ return {
65
+ monthlyPrice: Math.round(monthlyPrice * 100) / 100,
66
+ yearlyPrice: Math.round(monthlyPrice * 12 * 100) / 100,
67
+ freeUsers,
68
+ paidUsers,
69
+ effectivePricePerUser: users > 0 ? Math.round((monthlyPrice / users) * 100) / 100 : 0,
70
+ tierIndex,
71
+ tierPricePerUser,
72
+ minimumApplied,
73
+ };
74
+ }
75
+ /**
76
+ * Convert seat price to Stripe-compatible amount in cents.
77
+ */
78
+ export function seatPriceToCents(monthlyPrice) {
79
+ return Math.round(monthlyPrice * 100);
80
+ }
81
+ //# sourceMappingURL=seat-pricing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seat-pricing.js","sourceRoot":"","sources":["../../../src/shared/billing/seat-pricing.ts"],"names":[],"mappings":"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"}
@@ -11,17 +11,19 @@ export declare const PLAN_HIERARCHY: Record<string, number>;
11
11
  export declare const EXPIRY_WARNING_DAYS = 30;
12
12
  /** Separator between HMAC and payload in the license key */
13
13
  export declare const KEY_SEPARATOR = ".";
14
- /** HMAC algorithm */
15
- export declare const HMAC_ALGORITHM = "sha256";
16
- /** Length of the HMAC hex digest */
17
- export declare const HMAC_LENGTH = 64;
14
+ /** Hash algorithm for integrity check */
15
+ export declare const HASH_ALGORITHM = "sha256";
16
+ /** Length of the hash hex digest */
17
+ export declare const HASH_LENGTH = 64;
18
18
  /**
19
- * Signing secret embedded in the package.
19
+ * License integrity model (MUI X style).
20
20
  *
21
- * This is NOT a security mechanism. Like MUI X and AG Grid, the license system
22
- * relies on legal compliance, not technical protection. The key is self-validating
23
- * and the algorithm is public. This secret simply prevents casual key forging
24
- * while keeping validation 100% offline.
21
+ * The hash is simply sha256(payload) no secret key involved.
22
+ * This is an INTEGRITY check, not a security mechanism.
23
+ * Like MUI X and AG Grid, the license system relies on legal compliance,
24
+ * not technical protection. Anyone can generate a valid key.
25
+ *
26
+ * The value is in the ongoing updates, support, and legal right to use —
27
+ * not in preventing key generation.
25
28
  */
26
- export declare const SIGNING_SECRET = "tetra-license-v1-soulbatical-bv-2026";
27
29
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/shared/license/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,4FAA4F;AAC5F,eAAO,MAAM,WAAW,IAAI,CAAC;AAE7B,oDAAoD;AACpD,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAIjD,CAAC;AAEF,mDAAmD;AACnD,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,4DAA4D;AAC5D,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC,qBAAqB;AACrB,eAAO,MAAM,cAAc,WAAW,CAAC;AAEvC,oCAAoC;AACpC,eAAO,MAAM,WAAW,KAAK,CAAC;AAE9B;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,yCAAyC,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/shared/license/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,4FAA4F;AAC5F,eAAO,MAAM,WAAW,IAAI,CAAC;AAE7B,oDAAoD;AACpD,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAIjD,CAAC;AAEF,mDAAmD;AACnD,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,4DAA4D;AAC5D,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC,yCAAyC;AACzC,eAAO,MAAM,cAAc,WAAW,CAAC;AAEvC,oCAAoC;AACpC,eAAO,MAAM,WAAW,KAAK,CAAC;AAE9B;;;;;;;;;;GAUG"}
@@ -15,17 +15,19 @@ export const PLAN_HIERARCHY = {
15
15
  export const EXPIRY_WARNING_DAYS = 30;
16
16
  /** Separator between HMAC and payload in the license key */
17
17
  export const KEY_SEPARATOR = '.';
18
- /** HMAC algorithm */
19
- export const HMAC_ALGORITHM = 'sha256';
20
- /** Length of the HMAC hex digest */
21
- export const HMAC_LENGTH = 64; // sha256 = 32 bytes = 64 hex chars
18
+ /** Hash algorithm for integrity check */
19
+ export const HASH_ALGORITHM = 'sha256';
20
+ /** Length of the hash hex digest */
21
+ export const HASH_LENGTH = 64; // sha256 = 32 bytes = 64 hex chars
22
22
  /**
23
- * Signing secret embedded in the package.
23
+ * License integrity model (MUI X style).
24
24
  *
25
- * This is NOT a security mechanism. Like MUI X and AG Grid, the license system
26
- * relies on legal compliance, not technical protection. The key is self-validating
27
- * and the algorithm is public. This secret simply prevents casual key forging
28
- * while keeping validation 100% offline.
25
+ * The hash is simply sha256(payload) no secret key involved.
26
+ * This is an INTEGRITY check, not a security mechanism.
27
+ * Like MUI X and AG Grid, the license system relies on legal compliance,
28
+ * not technical protection. Anyone can generate a valid key.
29
+ *
30
+ * The value is in the ongoing updates, support, and legal right to use —
31
+ * not in preventing key generation.
29
32
  */
30
- export const SIGNING_SECRET = 'tetra-license-v1-soulbatical-bv-2026';
31
33
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/shared/license/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,4FAA4F;AAC5F,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC;AAE7B,oDAAoD;AACpD,MAAM,CAAC,MAAM,cAAc,GAA2B;IACpD,OAAO,EAAE,CAAC;IACV,GAAG,EAAE,CAAC;IACN,UAAU,EAAE,CAAC;CACd,CAAC;AAEF,mDAAmD;AACnD,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC,4DAA4D;AAC5D,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC;AAEjC,qBAAqB;AACrB,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC;AAEvC,oCAAoC;AACpC,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC,CAAC,mCAAmC;AAElE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,sCAAsC,CAAC"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/shared/license/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,4FAA4F;AAC5F,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC;AAE7B,oDAAoD;AACpD,MAAM,CAAC,MAAM,cAAc,GAA2B;IACpD,OAAO,EAAE,CAAC;IACV,GAAG,EAAE,CAAC;IACN,UAAU,EAAE,CAAC;CACd,CAAC;AAEF,mDAAmD;AACnD,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC,4DAA4D;AAC5D,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC;AAEjC,yCAAyC;AACzC,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC;AAEvC,oCAAoC;AACpC,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC,CAAC,mCAAmC;AAElE;;;;;;;;;;GAUG"}
@@ -1,11 +1,14 @@
1
1
  /**
2
- * License Key Generator
2
+ * License Key Generator & Decoder
3
3
  *
4
4
  * Generates self-validating license keys with embedded metadata.
5
- * Format: {HMAC_HEX}.{BASE64_PAYLOAD}
5
+ * Format: {SHA256_HEX}{BASE64_PAYLOAD} (no separator, first 64 chars = hash)
6
6
  *
7
- * The HMAC ensures integrity (not security the signing secret is embedded
8
- * in the package source, just like MUI X and AG Grid).
7
+ * Integrity model (MUI X style): hash = sha256(payload), no secret key.
8
+ * This is legal protection, not technical protection.
9
+ *
10
+ * NOTE: generateLicenseKey is exported for internal/admin use only.
11
+ * Consumer projects should never need to generate keys — they only validate.
9
12
  *
10
13
  * @module @soulbatical/tetra-core/license
11
14
  */
@@ -33,11 +36,11 @@ export interface GenerateKeyOptions {
33
36
  /**
34
37
  * Generate a license key.
35
38
  *
36
- * @returns The license key string: {HMAC}.{BASE64_PAYLOAD}
39
+ * @returns The license key string: {SHA256_HASH}{BASE64_PAYLOAD}
37
40
  */
38
41
  export declare function generateLicenseKey(options: GenerateKeyOptions): string;
39
42
  /**
40
- * Decode a license key without validating.
43
+ * Decode a license key without validating the hash.
41
44
  * Returns null if the key format is invalid.
42
45
  */
43
46
  export declare function decodeLicenseKey(key: string): LicensePayload | null;
@@ -1 +1 @@
1
- {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../../src/shared/license/generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5F,MAAM,WAAW,kBAAkB;IACjC,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB;IAChB,IAAI,EAAE,WAAW,CAAC;IAClB,iDAAiD;IACjD,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;IACvB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sDAAsD;IACtD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CA0BtE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAkBnE"}
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../../src/shared/license/generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5F,MAAM,WAAW,kBAAkB;IACjC,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB;IAChB,IAAI,EAAE,WAAW,CAAC;IAClB,iDAAiD;IACjD,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;IACvB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sDAAsD;IACtD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CA0BtE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAiBnE"}
@@ -1,20 +1,23 @@
1
1
  /**
2
- * License Key Generator
2
+ * License Key Generator & Decoder
3
3
  *
4
4
  * Generates self-validating license keys with embedded metadata.
5
- * Format: {HMAC_HEX}.{BASE64_PAYLOAD}
5
+ * Format: {SHA256_HEX}{BASE64_PAYLOAD} (no separator, first 64 chars = hash)
6
6
  *
7
- * The HMAC ensures integrity (not security the signing secret is embedded
8
- * in the package source, just like MUI X and AG Grid).
7
+ * Integrity model (MUI X style): hash = sha256(payload), no secret key.
8
+ * This is legal protection, not technical protection.
9
+ *
10
+ * NOTE: generateLicenseKey is exported for internal/admin use only.
11
+ * Consumer projects should never need to generate keys — they only validate.
9
12
  *
10
13
  * @module @soulbatical/tetra-core/license
11
14
  */
12
15
  import crypto from 'node:crypto';
13
- import { KEY_VERSION, KEY_SEPARATOR, HMAC_ALGORITHM, SIGNING_SECRET } from './constants.js';
16
+ import { KEY_VERSION, HASH_ALGORITHM, HASH_LENGTH } from './constants.js';
14
17
  /**
15
18
  * Generate a license key.
16
19
  *
17
- * @returns The license key string: {HMAC}.{BASE64_PAYLOAD}
20
+ * @returns The license key string: {SHA256_HASH}{BASE64_PAYLOAD}
18
21
  */
19
22
  export function generateLicenseKey(options) {
20
23
  const payload = {
@@ -34,22 +37,21 @@ export function generateLicenseKey(options) {
34
37
  throw new Error(`Invalid expiry date: ${options.expiryDate}`);
35
38
  }
36
39
  const payloadBase64 = Buffer.from(JSON.stringify(payload)).toString('base64url');
37
- const hmac = crypto
38
- .createHmac(HMAC_ALGORITHM, SIGNING_SECRET)
40
+ const hash = crypto
41
+ .createHash(HASH_ALGORITHM)
39
42
  .update(payloadBase64)
40
43
  .digest('hex');
41
- return `${hmac}${KEY_SEPARATOR}${payloadBase64}`;
44
+ return `${hash}${payloadBase64}`;
42
45
  }
43
46
  /**
44
- * Decode a license key without validating.
47
+ * Decode a license key without validating the hash.
45
48
  * Returns null if the key format is invalid.
46
49
  */
47
50
  export function decodeLicenseKey(key) {
48
51
  try {
49
- const separatorIndex = key.indexOf(KEY_SEPARATOR);
50
- if (separatorIndex === -1)
52
+ if (key.length <= HASH_LENGTH)
51
53
  return null;
52
- const payloadBase64 = key.slice(separatorIndex + 1);
54
+ const payloadBase64 = key.slice(HASH_LENGTH);
53
55
  const json = Buffer.from(payloadBase64, 'base64url').toString('utf8');
54
56
  const payload = JSON.parse(json);
55
57
  // Basic shape validation
@@ -1 +1 @@
1
- {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../src/shared/license/generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAuB5F;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAA2B;IAC5D,MAAM,OAAO,GAAmB;QAC9B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;QACtC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,WAAW;QACnC,MAAM,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE;QAC9C,UAAU,EAAE,WAAW;QACvB,GAAG,CAAC,OAAO,CAAC,gBAAgB,IAAI,IAAI,IAAI,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC;QACvF,GAAG,CAAC,OAAO,CAAC,cAAc,IAAI,IAAI,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,EAAE,CAAC;QACjF,GAAG,CAAC,OAAO,CAAC,YAAY,IAAI,IAAI,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC;KAC5E,CAAC;IAEF,yCAAyC;IACzC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,wBAAwB,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACjF,MAAM,IAAI,GAAG,MAAM;SAChB,UAAU,CAAC,cAAc,EAAE,cAAc,CAAC;SAC1C,MAAM,CAAC,aAAa,CAAC;SACrB,MAAM,CAAC,KAAK,CAAC,CAAC;IAEjB,OAAO,GAAG,IAAI,GAAG,aAAa,GAAG,aAAa,EAAE,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,CAAC;QACH,MAAM,cAAc,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAClD,IAAI,cAAc,KAAK,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAEvC,MAAM,aAAa,GAAG,GAAG,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACtE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAmB,CAAC;QAEnD,yBAAyB;QACzB,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;YAChF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../src/shared/license/generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAuB1E;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAA2B;IAC5D,MAAM,OAAO,GAAmB;QAC9B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;QACtC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,WAAW;QACnC,MAAM,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE;QAC9C,UAAU,EAAE,WAAW;QACvB,GAAG,CAAC,OAAO,CAAC,gBAAgB,IAAI,IAAI,IAAI,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC;QACvF,GAAG,CAAC,OAAO,CAAC,cAAc,IAAI,IAAI,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,EAAE,CAAC;QACjF,GAAG,CAAC,OAAO,CAAC,YAAY,IAAI,IAAI,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC;KAC5E,CAAC;IAEF,yCAAyC;IACzC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,wBAAwB,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACjF,MAAM,IAAI,GAAG,MAAM;SAChB,UAAU,CAAC,cAAc,CAAC;SAC1B,MAAM,CAAC,aAAa,CAAC;SACrB,MAAM,CAAC,KAAK,CAAC,CAAC;IAEjB,OAAO,GAAG,IAAI,GAAG,aAAa,EAAE,CAAC;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,IAAI,WAAW;YAAE,OAAO,IAAI,CAAC;QAE3C,MAAM,aAAa,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACtE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAmB,CAAC;QAEnD,yBAAyB;QACzB,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;YAChF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -2,8 +2,8 @@
2
2
  * License Module — Barrel Export
3
3
  *
4
4
  * Offline license key validation for Tetra Pro features.
5
- * Like MUI X and AG Grid: HMAC integrity check, self-validating keys,
6
- * zero network calls. Security relies on legal compliance, not technical protection.
5
+ * Integrity check via sha256 hash (MUI X model), self-validating keys,
6
+ * zero network calls. Legal compliance, not technical protection.
7
7
  *
8
8
  * @module @soulbatical/tetra-core/license
9
9
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/license/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,YAAY,EACV,WAAW,EACX,YAAY,EACZ,cAAc,EACd,cAAc,EACd,uBAAuB,EACvB,YAAY,EACZ,WAAW,EACX,aAAa,GACd,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACtE,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGzD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAG9G,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/license/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,YAAY,EACV,WAAW,EACX,YAAY,EACZ,cAAc,EACd,cAAc,EACd,uBAAuB,EACvB,YAAY,EACZ,WAAW,EACX,aAAa,GACd,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACtE,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGzD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAG9G,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
@@ -2,12 +2,13 @@
2
2
  * License Module — Barrel Export
3
3
  *
4
4
  * Offline license key validation for Tetra Pro features.
5
- * Like MUI X and AG Grid: HMAC integrity check, self-validating keys,
6
- * zero network calls. Security relies on legal compliance, not technical protection.
5
+ * Integrity check via sha256 hash (MUI X model), self-validating keys,
6
+ * zero network calls. Legal compliance, not technical protection.
7
7
  *
8
8
  * @module @soulbatical/tetra-core/license
9
9
  */
10
- // Generator (for admin/CLI use — not typically used in consumer projects)
10
+ // Generator — exported for internal/admin/CLI use only.
11
+ // Consumer projects should use validateLicense, not generateLicenseKey.
11
12
  export { generateLicenseKey, decodeLicenseKey } from './generator.js';
12
13
  // Validator (primary API for consumer projects)
13
14
  export { validateLicense, clearLicenseCache, isFeatureAvailable, validateLicenseUsage } from './validator.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/shared/license/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAcH,0EAA0E;AAC1E,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGtE,gDAAgD;AAChD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE9G,YAAY;AACZ,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/shared/license/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAcH,wDAAwD;AACxD,wEAAwE;AACxE,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGtE,gDAAgD;AAChD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE9G,YAAY;AACZ,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
@@ -2,7 +2,8 @@
2
2
  * License Key Validator
3
3
  *
4
4
  * Validates license keys offline — zero network calls.
5
- * Like MUI X and AG Grid: integrity check via HMAC, then decode metadata.
5
+ * Integrity check via sha256 hash (MUI X model), then decode metadata.
6
+ * No signing secret — this is legal protection, not technical protection.
6
7
  *
7
8
  * @module @soulbatical/tetra-core/license
8
9
  */
@@ -24,13 +25,6 @@ export declare function clearLicenseCache(): void;
24
25
  export declare function isFeatureAvailable(requiredPlan: string, requiredScope: LicenseScope, config?: LicenseConfig): boolean;
25
26
  /**
26
27
  * Validate license usage against org/user limits.
27
- *
28
- * Call this after validateLicense() to check if the current usage
29
- * is within the license's limits. Returns a validation result with
30
- * status 'org_limit_exceeded' or 'user_limit_exceeded' if over limit.
31
- *
32
- * If the license has no limits (maxOrganizations/maxUsersPerOrg undefined),
33
- * this always returns valid.
34
28
  */
35
29
  export declare function validateLicenseUsage(usage: LicenseUsage, config?: LicenseConfig): LicenseValidationResult;
36
30
  //# sourceMappingURL=validator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../src/shared/license/validator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAkB,YAAY,EAAE,uBAAuB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAcrH;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,uBAAuB,CAwB/E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,YAAY,EAC3B,MAAM,CAAC,EAAE,aAAa,GACrB,OAAO,CAWT;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,YAAY,EACnB,MAAM,CAAC,EAAE,aAAa,GACrB,uBAAuB,CA2BzB"}
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../src/shared/license/validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAkB,YAAY,EAAE,uBAAuB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAarH;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,uBAAuB,CAwB/E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,YAAY,EAC3B,MAAM,CAAC,EAAE,aAAa,GACrB,OAAO,CAWT;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,YAAY,EACnB,MAAM,CAAC,EAAE,aAAa,GACrB,uBAAuB,CA2BzB"}
@@ -2,12 +2,13 @@
2
2
  * License Key Validator
3
3
  *
4
4
  * Validates license keys offline — zero network calls.
5
- * Like MUI X and AG Grid: integrity check via HMAC, then decode metadata.
5
+ * Integrity check via sha256 hash (MUI X model), then decode metadata.
6
+ * No signing secret — this is legal protection, not technical protection.
6
7
  *
7
8
  * @module @soulbatical/tetra-core/license
8
9
  */
9
10
  import crypto from 'node:crypto';
10
- import { KEY_SEPARATOR, HMAC_ALGORITHM, SIGNING_SECRET, PLAN_HIERARCHY, EXPIRY_WARNING_DAYS, } from './constants.js';
11
+ import { HASH_ALGORITHM, HASH_LENGTH, PLAN_HIERARCHY, EXPIRY_WARNING_DAYS, } from './constants.js';
11
12
  import { decodeLicenseKey } from './generator.js';
12
13
  /** Cached validation result to avoid re-validating on every call */
13
14
  let cachedResult = null;
@@ -62,13 +63,6 @@ export function isFeatureAvailable(requiredPlan, requiredScope, config) {
62
63
  }
63
64
  /**
64
65
  * Validate license usage against org/user limits.
65
- *
66
- * Call this after validateLicense() to check if the current usage
67
- * is within the license's limits. Returns a validation result with
68
- * status 'org_limit_exceeded' or 'user_limit_exceeded' if over limit.
69
- *
70
- * If the license has no limits (maxOrganizations/maxUsersPerOrg undefined),
71
- * this always returns valid.
72
66
  */
73
67
  export function validateLicenseUsage(usage, config) {
74
68
  const baseResult = validateLicense(config);
@@ -97,23 +91,22 @@ export function validateLicenseUsage(usage, config) {
97
91
  }
98
92
  // ─── Internal ────────────────────────────────────────────────
99
93
  function performValidation(key, config) {
100
- // Step 1: Parse key format
101
- const separatorIndex = key.indexOf(KEY_SEPARATOR);
102
- if (separatorIndex === -1) {
94
+ // Step 1: Check minimum length (64 char hash + at least 1 char payload)
95
+ if (key.length <= HASH_LENGTH) {
103
96
  return {
104
97
  valid: false,
105
98
  status: 'invalid',
106
99
  message: 'Invalid license key format.',
107
100
  };
108
101
  }
109
- const hmacProvided = key.slice(0, separatorIndex);
110
- const payloadBase64 = key.slice(separatorIndex + 1);
111
- // Step 2: Verify HMAC integrity
112
- const hmacComputed = crypto
113
- .createHmac(HMAC_ALGORITHM, SIGNING_SECRET)
102
+ const hashProvided = key.slice(0, HASH_LENGTH);
103
+ const payloadBase64 = key.slice(HASH_LENGTH);
104
+ // Step 2: Verify hash integrity (sha256, no secret — MUI X model)
105
+ const hashComputed = crypto
106
+ .createHash(HASH_ALGORITHM)
114
107
  .update(payloadBase64)
115
108
  .digest('hex');
116
- if (!crypto.timingSafeEqual(Buffer.from(hmacProvided, 'hex'), Buffer.from(hmacComputed, 'hex'))) {
109
+ if (hashProvided !== hashComputed) {
117
110
  return {
118
111
  valid: false,
119
112
  status: 'invalid',
@@ -143,7 +136,6 @@ function performValidation(key, config) {
143
136
  const now = Date.now();
144
137
  const daysRemaining = Math.floor((payload.expiry - now) / (1000 * 60 * 60 * 24));
145
138
  if (payload.model === 'perpetual') {
146
- // Perpetual: key is valid for all releases before expiry date
147
139
  const releaseDate = config?.releaseDate
148
140
  ? new Date(config.releaseDate).getTime()
149
141
  : now;
@@ -158,7 +150,6 @@ function performValidation(key, config) {
158
150
  }
159
151
  }
160
152
  else {
161
- // Subscription: must be currently active
162
153
  if (now > payload.expiry) {
163
154
  return {
164
155
  valid: false,
@@ -177,7 +168,7 @@ function performValidation(key, config) {
177
168
  daysRemaining,
178
169
  message: `Licensed to ${payload.customer} (${payload.plan}). ${daysRemaining > EXPIRY_WARNING_DAYS ? '' : `Expires in ${daysRemaining} days.`}`.trim(),
179
170
  };
180
- // Log expiry warning (not an error, just a heads up)
171
+ // Log expiry warning
181
172
  if (!config?.silent && daysRemaining <= EXPIRY_WARNING_DAYS && daysRemaining > 0) {
182
173
  console.warn(`[Tetra] License expires in ${daysRemaining} days. Renew at https://tetra.soulbatical.com/renew`);
183
174
  }
@@ -1 +1 @@
1
- {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../src/shared/license/validator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../src/shared/license/validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,OAAO,EACL,cAAc,EACd,WAAW,EACX,cAAc,EACd,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,oEAAoE;AACpE,IAAI,YAAY,GAAmC,IAAI,CAAC;AACxD,IAAI,SAAS,GAAkB,IAAI,CAAC;AAEpC;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,MAAsB;IACpD,MAAM,GAAG,GAAG,MAAM,EAAE,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAEhE,kBAAkB;IAClB,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC9B,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,oIAAoI;SAC9I,CAAC;IACJ,CAAC;IAED,6CAA6C;IAC7C,IAAI,SAAS,KAAK,GAAG,IAAI,YAAY,EAAE,CAAC;QACtC,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAE9C,WAAW;IACX,SAAS,GAAG,GAAG,CAAC;IAChB,YAAY,GAAG,MAAM,CAAC;IAEtB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB;IAC/B,YAAY,GAAG,IAAI,CAAC;IACpB,SAAS,GAAG,IAAI,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAChC,YAAoB,EACpB,aAA2B,EAC3B,MAAsB;IAEtB,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAEnD,cAAc;IACd,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC;QAAE,OAAO,KAAK,CAAC;IAEhE,uBAAuB;IACvB,MAAM,YAAY,GAAG,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/D,MAAM,aAAa,GAAG,cAAc,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC;IAC1D,OAAO,YAAY,IAAI,aAAa,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAmB,EACnB,MAAsB;IAEtB,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,OAAO;QAAE,OAAO,UAAU,CAAC;IAEhE,MAAM,EAAE,gBAAgB,EAAE,cAAc,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC;IAEhE,IAAI,gBAAgB,IAAI,IAAI,IAAI,KAAK,CAAC,iBAAiB,GAAG,gBAAgB,EAAE,CAAC;QAC3E,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,oBAAoB;YAC5B,OAAO,EAAE,UAAU,CAAC,OAAO;YAC3B,aAAa,EAAE,UAAU,CAAC,aAAa;YACvC,OAAO,EAAE,gCAAgC,KAAK,CAAC,iBAAiB,IAAI,gBAAgB,gDAAgD;SACrI,CAAC;IACJ,CAAC;IAED,IAAI,cAAc,IAAI,IAAI,IAAI,KAAK,CAAC,SAAS,GAAG,cAAc,EAAE,CAAC;QAC/D,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,qBAAqB;YAC7B,OAAO,EAAE,UAAU,CAAC,OAAO;YAC3B,aAAa,EAAE,UAAU,CAAC,aAAa;YACvC,OAAO,EAAE,wBAAwB,KAAK,CAAC,SAAS,IAAI,cAAc,yDAAyD;SAC5H,CAAC;IACJ,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,gEAAgE;AAEhE,SAAS,iBAAiB,CAAC,GAAW,EAAE,MAAsB;IAC5D,wEAAwE;IACxE,IAAI,GAAG,CAAC,MAAM,IAAI,WAAW,EAAE,CAAC;QAC9B,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,6BAA6B;SACvC,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IAC/C,MAAM,aAAa,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAE7C,kEAAkE;IAClE,MAAM,YAAY,GAAG,MAAM;SACxB,UAAU,CAAC,cAAc,CAAC;SAC1B,MAAM,CAAC,aAAa,CAAC;SACrB,MAAM,CAAC,KAAK,CAAC,CAAC;IAEjB,IAAI,YAAY,KAAK,YAAY,EAAE,CAAC;QAClC,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,qCAAqC;SAC/C,CAAC;IACJ,CAAC;IAED,yBAAyB;IACzB,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,mCAAmC;SAC7C,CAAC;IACJ,CAAC;IAED,qDAAqD;IACrD,MAAM,YAAY,GAAG,MAAM,EAAE,YAAY,CAAC;IAC1C,IAAI,YAAY,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QAC1D,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,gBAAgB;YACxB,OAAO;YACP,OAAO,EAAE,mCAAmC,YAAY,uBAAuB,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;SAC3G,CAAC;IACJ,CAAC;IAED,uBAAuB;IACvB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IAEjF,IAAI,OAAO,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;QAClC,MAAM,WAAW,GAAG,MAAM,EAAE,WAAW;YACrC,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE;YACxC,CAAC,CAAC,GAAG,CAAC;QAER,IAAI,WAAW,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YACjC,OAAO;gBACL,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,SAAS;gBACjB,OAAO;gBACP,aAAa;gBACb,OAAO,EAAE,kCAAkC,MAAM,EAAE,WAAW,IAAI,SAAS,4EAA4E;aACxJ,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YACzB,OAAO;gBACL,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,SAAS;gBACjB,OAAO;gBACP,aAAa;gBACb,OAAO,EAAE,gCAAgC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,kDAAkD;aACnH,CAAC;QACJ,CAAC;IACH,CAAC;IAED,+BAA+B;IAC/B,MAAM,MAAM,GAA4B;QACtC,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,OAAO;QACf,OAAO;QACP,aAAa;QACb,OAAO,EAAE,eAAe,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,IAAI,MAAM,aAAa,GAAG,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,aAAa,QAAQ,EAAE,CAAC,IAAI,EAAE;KACvJ,CAAC;IAEF,qBAAqB;IACrB,IAAI,CAAC,MAAM,EAAE,MAAM,IAAI,aAAa,IAAI,mBAAmB,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QACjF,OAAO,CAAC,IAAI,CACV,8BAA8B,aAAa,qDAAqD,CACjG,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soulbatical/tetra-core",
3
- "version": "0.6.1",
3
+ "version": "0.8.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },