@soulcraft/sdk 1.0.0 → 1.1.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 (79) hide show
  1. package/dist/index.d.ts +6 -5
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/modules/billing/firestore-provider.d.ts +60 -0
  6. package/dist/modules/billing/firestore-provider.d.ts.map +1 -0
  7. package/dist/modules/billing/firestore-provider.js +314 -0
  8. package/dist/modules/billing/firestore-provider.js.map +1 -0
  9. package/dist/modules/billing/index.d.ts +46 -0
  10. package/dist/modules/billing/index.d.ts.map +1 -0
  11. package/dist/modules/billing/index.js +149 -0
  12. package/dist/modules/billing/index.js.map +1 -0
  13. package/dist/modules/billing/local-provider.d.ts +38 -0
  14. package/dist/modules/billing/local-provider.d.ts.map +1 -0
  15. package/dist/modules/billing/local-provider.js +242 -0
  16. package/dist/modules/billing/local-provider.js.map +1 -0
  17. package/dist/modules/billing/types.d.ts +322 -3
  18. package/dist/modules/billing/types.d.ts.map +1 -1
  19. package/dist/modules/billing/types.js +21 -2
  20. package/dist/modules/billing/types.js.map +1 -1
  21. package/dist/modules/billing/usage-buffer.d.ts +72 -0
  22. package/dist/modules/billing/usage-buffer.d.ts.map +1 -0
  23. package/dist/modules/billing/usage-buffer.js +141 -0
  24. package/dist/modules/billing/usage-buffer.js.map +1 -0
  25. package/dist/modules/formats/types.d.ts +65 -3
  26. package/dist/modules/formats/types.d.ts.map +1 -1
  27. package/dist/modules/formats/types.js +40 -3
  28. package/dist/modules/formats/types.js.map +1 -1
  29. package/dist/modules/formats/wdoc.d.ts +263 -0
  30. package/dist/modules/formats/wdoc.d.ts.map +1 -0
  31. package/dist/modules/formats/wdoc.js +21 -0
  32. package/dist/modules/formats/wdoc.js.map +1 -0
  33. package/dist/modules/formats/wquiz.d.ts +122 -0
  34. package/dist/modules/formats/wquiz.d.ts.map +1 -0
  35. package/dist/modules/formats/wquiz.js +23 -0
  36. package/dist/modules/formats/wquiz.js.map +1 -0
  37. package/dist/modules/formats/wslide.d.ts +130 -0
  38. package/dist/modules/formats/wslide.d.ts.map +1 -0
  39. package/dist/modules/formats/wslide.js +23 -0
  40. package/dist/modules/formats/wslide.js.map +1 -0
  41. package/dist/modules/formats/wviz.d.ts +114 -0
  42. package/dist/modules/formats/wviz.d.ts.map +1 -0
  43. package/dist/modules/formats/wviz.js +21 -0
  44. package/dist/modules/formats/wviz.js.map +1 -0
  45. package/dist/modules/kits/index.d.ts +41 -0
  46. package/dist/modules/kits/index.d.ts.map +1 -0
  47. package/dist/modules/kits/index.js +85 -0
  48. package/dist/modules/kits/index.js.map +1 -0
  49. package/dist/modules/kits/types.d.ts +107 -3
  50. package/dist/modules/kits/types.d.ts.map +1 -1
  51. package/dist/modules/kits/types.js +15 -2
  52. package/dist/modules/kits/types.js.map +1 -1
  53. package/dist/modules/license/index.d.ts +53 -0
  54. package/dist/modules/license/index.d.ts.map +1 -0
  55. package/dist/modules/license/index.js +233 -0
  56. package/dist/modules/license/index.js.map +1 -0
  57. package/dist/modules/license/types.d.ts +222 -3
  58. package/dist/modules/license/types.d.ts.map +1 -1
  59. package/dist/modules/license/types.js +21 -2
  60. package/dist/modules/license/types.js.map +1 -1
  61. package/dist/modules/notifications/index.d.ts +40 -0
  62. package/dist/modules/notifications/index.d.ts.map +1 -0
  63. package/dist/modules/notifications/index.js +280 -0
  64. package/dist/modules/notifications/index.js.map +1 -0
  65. package/dist/modules/notifications/types.d.ts +152 -3
  66. package/dist/modules/notifications/types.d.ts.map +1 -1
  67. package/dist/modules/notifications/types.js +21 -2
  68. package/dist/modules/notifications/types.js.map +1 -1
  69. package/dist/server/create-sdk.d.ts +4 -0
  70. package/dist/server/create-sdk.d.ts.map +1 -1
  71. package/dist/server/create-sdk.js +19 -26
  72. package/dist/server/create-sdk.js.map +1 -1
  73. package/dist/server/index.d.ts +6 -0
  74. package/dist/server/index.d.ts.map +1 -1
  75. package/dist/server/index.js +5 -0
  76. package/dist/server/index.js.map +1 -1
  77. package/dist/types.d.ts +13 -7
  78. package/dist/types.d.ts.map +1 -1
  79. package/package.json +16 -3
package/dist/index.d.ts CHANGED
@@ -25,9 +25,10 @@ export type { AiModule, AiCompleteOptions, AiCompleteResult, AiStreamOptions, Ai
25
25
  export { AI_MODELS } from './modules/ai/types.js';
26
26
  export type { EventsModule, SoulcraftEventMap, SoulcraftEventName, VfsWriteEvent, VfsDeleteEvent, VfsRenameEvent, } from './modules/events/types.js';
27
27
  export type { SkillsModule, Skill, SkillListOptions, SkillInstallOptions, } from './modules/skills/types.js';
28
- export type * from './modules/license/types.js';
29
- export type * from './modules/kits/types.js';
30
- export type * from './modules/formats/types.js';
31
- export type * from './modules/billing/types.js';
32
- export type * from './modules/notifications/types.js';
28
+ export type { LicenseResult, LicenseModule, LicensePlanTier, LicenseFeatures, AICreditCheckResult, AIUsageRecord, AIProviderConfig, } from './modules/license/types.js';
29
+ export type { KitsModule, SoulcraftKitConfig, } from './modules/kits/types.js';
30
+ export type { WvizDocument, WvizEntity, WvizEdge, WvizSnapshot, WvizViewType, WdocDocument, WdocMeta, WdocBlock, WdocInlineContent, WdocTextNode, WdocMark, WdocParagraphBlock, WdocHeadingBlock, WdocBlockquoteBlock, WdocCodeBlock, WdocBulletListBlock, WdocOrderedListBlock, WdocListItemBlock, WdocTaskListBlock, WdocTaskItemBlock, WdocImageBlock, WdocVideoBlock, WdocIconBlock, WdocSvgEmbedBlock, WdocEmbedBlock, WdocTableBlock, WdocTableRowBlock, WdocTableCellBlock, WdocTableHeaderBlock, WdocHorizontalRuleBlock, WdocCalloutBlock, WslideDocument, WslideSlide, WslideBackground, WslideLayout, WslideTransition, WslideConfig, WquizDocument, WquizQuestion, WquizQuestionType, WquizAnswer, WquizScoringRules, SoulcraftFormat, SoulcraftFormatDocument, } from './modules/formats/types.js';
31
+ export { SOULCRAFT_FORMATS } from './modules/formats/types.js';
32
+ export type { BillingModule, BillingPlanType, SubscriptionStatus, ModelUsage, UsageData, UsageStatus, SubscriptionData, LimitCheckResult, BatchIncrementData, CreateCheckoutSessionOptions, CreatePortalSessionOptions, } from './modules/billing/types.js';
33
+ export type { NotificationsModule, Notification, NotificationResult, NotificationSender, EmailNotification, SmsNotification, } from './modules/notifications/types.js';
33
34
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAG9F,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAChE,YAAY,EACV,MAAM,EACN,QAAQ,EACR,MAAM,EACN,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,QAAQ,EACR,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,2BAA2B,CAAA;AAClC,YAAY,EACV,4BAA4B,EAC5B,qBAAqB,GACtB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,QAAQ,EACR,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,sBAAsB,GACvB,MAAM,4BAA4B,CAAA;AAGnC,YAAY,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAGvD,YAAY,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAGjE,YAAY,EACV,UAAU,EACV,UAAU,EACV,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,qBAAqB,EACrB,cAAc,EACd,YAAY,GACb,MAAM,yBAAyB,CAAA;AAGhC,YAAY,EACV,UAAU,EACV,YAAY,EACZ,qBAAqB,EACrB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,QAAQ,GACT,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,gBAAgB,EAChB,WAAW,EACX,mBAAmB,GACpB,MAAM,0BAA0B,CAAA;AAGjC,YAAY,EACV,QAAQ,EACR,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,UAAU,EACV,SAAS,EACT,cAAc,EACd,MAAM,EACN,OAAO,GACR,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAGjD,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,cAAc,GACf,MAAM,2BAA2B,CAAA;AAGlC,YAAY,EACV,YAAY,EACZ,KAAK,EACL,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,2BAA2B,CAAA;AAGlC,mBAAmB,4BAA4B,CAAA;AAC/C,mBAAmB,yBAAyB,CAAA;AAC5C,mBAAmB,4BAA4B,CAAA;AAC/C,mBAAmB,4BAA4B,CAAA;AAC/C,mBAAmB,kCAAkC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -14,4 +14,5 @@ export { createCapabilityToken, verifyCapabilityToken, } from './modules/brainy/
14
14
  export { SDKError, SDKDisconnectedError, SDKTimeoutError, SDKAuthError, SDKForbiddenError, SDKRpcError, SDKMethodNotFoundError, } from './modules/brainy/errors.js';
15
15
  export { SOULCRAFT_USER_FIELDS, SOULCRAFT_SESSION_CONFIG, computeEmailHash, getAuthMode, getOIDCClientConfig, } from './modules/auth/config.js';
16
16
  export { AI_MODELS } from './modules/ai/types.js';
17
+ export { SOULCRAFT_FORMATS } from './modules/formats/types.js';
17
18
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA8BH,OAAO,EACL,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,QAAQ,EACR,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,sBAAsB,GACvB,MAAM,4BAA4B,CAAA;AAgCnC,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,gBAAgB,EAChB,WAAW,EACX,mBAAmB,GACpB,MAAM,0BAA0B,CAAA;AAejC,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA8BH,OAAO,EACL,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,QAAQ,EACR,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,sBAAsB,GACvB,MAAM,4BAA4B,CAAA;AAgCnC,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,gBAAgB,EAChB,WAAW,EACX,mBAAmB,GACpB,MAAM,0BAA0B,CAAA;AAejC,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAoFjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @module billing/firestore-provider
3
+ * @description Firestore-backed billing provider for production deployments.
4
+ *
5
+ * Reads subscription and license data from the `licenses` Firestore collection
6
+ * managed by Portal (`soulcraft.com`). Workshop, Venue, and Academy all write
7
+ * usage counters to the same document so Portal's Admin can see cross-product usage.
8
+ *
9
+ * This provider is activated when `FIREBASE_SERVICE_ACCOUNT_KEY` is set in the
10
+ * environment. `firebase-admin` is loaded lazily via dynamic import so that the
11
+ * SDK does not require the package when using the local provider.
12
+ *
13
+ * **Firestore document structure:**
14
+ * ```
15
+ * licenses/{docId}
16
+ * email: string (user email — used to look up the doc)
17
+ * product: 'workshop' | 'venue' | 'academy'
18
+ * status: 'active' | 'past_due' | 'canceled' | 'trialing'
19
+ * plan: 'standard' | 'byok'
20
+ * credits: {
21
+ * monthlyIncluded: number
22
+ * monthlyUsed: number (SDK increments this)
23
+ * topUpBalance: number (SDK decrements this)
24
+ * inputTokens: number
25
+ * outputTokens: number
26
+ * byModel: Record<model, { input, output }>
27
+ * periodStart: Timestamp
28
+ * periodEnd: Timestamp
29
+ * lastUpdated: Timestamp
30
+ * }
31
+ * stripeCustomerId: string
32
+ * stripeSubscriptionId: string
33
+ * cancelAtPeriodEnd: boolean
34
+ * ```
35
+ *
36
+ * Note: The `userId` passed to provider methods is the user's email address when
37
+ * using Firestore, since the `licenses` collection is keyed by email. Products
38
+ * must pass `user.email` (not `user.id`) when constructing the SDK in Firestore mode.
39
+ */
40
+ import type { BillingProvider, UsageData, SubscriptionData, LimitCheckResult, UsageStatus, BatchIncrementData } from './types.js';
41
+ /**
42
+ * Firestore-backed billing provider for production deployments.
43
+ *
44
+ * The `userId` parameter on all methods must be the user's **email address**,
45
+ * since the Firestore `licenses` collection is indexed by email.
46
+ */
47
+ export declare class FirestoreBillingProvider implements BillingProvider {
48
+ checkLimit(userId: string, hasPersonalKey: boolean): Promise<LimitCheckResult>;
49
+ getUsage(userId: string): Promise<UsageData>;
50
+ getUsageStatus(userId: string, hasPersonalKey: boolean): Promise<UsageStatus>;
51
+ incrementUsage(userId: string, inputTokens: number, outputTokens: number, model: string, useTopUp: boolean): Promise<void>;
52
+ batchIncrementUsage(userId: string, batch: BatchIncrementData): Promise<void>;
53
+ getSubscription(userId: string): Promise<SubscriptionData | undefined>;
54
+ hasActiveSubscription(userId: string): Promise<boolean>;
55
+ getTopUpBalance(userId: string): Promise<number>;
56
+ canAccessPaidFeature(userId: string): Promise<boolean>;
57
+ private _toSubscription;
58
+ private _resetPeriod;
59
+ }
60
+ //# sourceMappingURL=firestore-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firestore-provider.d.ts","sourceRoot":"","sources":["../../../src/modules/billing/firestore-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,SAAS,EACT,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,kBAAkB,EACnB,MAAM,YAAY,CAAA;AAkJnB;;;;;GAKG;AACH,qBAAa,wBAAyB,YAAW,eAAe;IACxD,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA8B9E,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAiB5C,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC;IAgC7E,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB1H,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB7E,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;IAMtE,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAOvD,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAOhD,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAM5D,OAAO,CAAC,eAAe;YA2BT,YAAY;CAe3B"}
@@ -0,0 +1,314 @@
1
+ /**
2
+ * @module billing/firestore-provider
3
+ * @description Firestore-backed billing provider for production deployments.
4
+ *
5
+ * Reads subscription and license data from the `licenses` Firestore collection
6
+ * managed by Portal (`soulcraft.com`). Workshop, Venue, and Academy all write
7
+ * usage counters to the same document so Portal's Admin can see cross-product usage.
8
+ *
9
+ * This provider is activated when `FIREBASE_SERVICE_ACCOUNT_KEY` is set in the
10
+ * environment. `firebase-admin` is loaded lazily via dynamic import so that the
11
+ * SDK does not require the package when using the local provider.
12
+ *
13
+ * **Firestore document structure:**
14
+ * ```
15
+ * licenses/{docId}
16
+ * email: string (user email — used to look up the doc)
17
+ * product: 'workshop' | 'venue' | 'academy'
18
+ * status: 'active' | 'past_due' | 'canceled' | 'trialing'
19
+ * plan: 'standard' | 'byok'
20
+ * credits: {
21
+ * monthlyIncluded: number
22
+ * monthlyUsed: number (SDK increments this)
23
+ * topUpBalance: number (SDK decrements this)
24
+ * inputTokens: number
25
+ * outputTokens: number
26
+ * byModel: Record<model, { input, output }>
27
+ * periodStart: Timestamp
28
+ * periodEnd: Timestamp
29
+ * lastUpdated: Timestamp
30
+ * }
31
+ * stripeCustomerId: string
32
+ * stripeSubscriptionId: string
33
+ * cancelAtPeriodEnd: boolean
34
+ * ```
35
+ *
36
+ * Note: The `userId` passed to provider methods is the user's email address when
37
+ * using Firestore, since the `licenses` collection is keyed by email. Products
38
+ * must pass `user.email` (not `user.id`) when constructing the SDK in Firestore mode.
39
+ */
40
+ // ─────────────────────────────────────────────────────────────────────────────
41
+ // Lazy loader
42
+ // ─────────────────────────────────────────────────────────────────────────────
43
+ let _db;
44
+ let _FieldValue;
45
+ async function _getFirestore() {
46
+ if (_db && _FieldValue)
47
+ return { db: _db, FieldValue: _FieldValue };
48
+ const key = process.env['FIREBASE_SERVICE_ACCOUNT_KEY'];
49
+ if (!key)
50
+ throw new Error('FIREBASE_SERVICE_ACCOUNT_KEY is not set — cannot use Firestore billing provider');
51
+ // firebase-admin is an optional peer dependency — loaded at runtime only when configured.
52
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
+ const admin = await Function('return import("firebase-admin")')();
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ const { getFirestore, FieldValue } = await Function('return import("firebase-admin/firestore")')();
56
+ if (!admin.apps?.length) {
57
+ admin.initializeApp({
58
+ credential: admin.credential.cert(JSON.parse(Buffer.from(key, 'base64').toString('utf-8'))),
59
+ preferRest: true, // Use REST transport — required for Bun compatibility (avoids gRPC).
60
+ });
61
+ }
62
+ _db = getFirestore();
63
+ _FieldValue = FieldValue;
64
+ return { db: _db, FieldValue: _FieldValue };
65
+ }
66
+ const _docCache = new Map();
67
+ const DOC_CACHE_TTL_MS = 5 * 60 * 1000;
68
+ async function _getLicenseDoc(email) {
69
+ const cached = _docCache.get(email);
70
+ if (cached && Date.now() < cached.expiresAt)
71
+ return cached;
72
+ const { db } = await _getFirestore();
73
+ const snap = await db.collection('licenses')
74
+ .where('email', '==', email.toLowerCase())
75
+ .where('status', 'in', ['active', 'trialing', 'past_due'])
76
+ .get();
77
+ if (!snap.docs.length)
78
+ return null;
79
+ const doc = snap.docs[0];
80
+ const data = doc.data() ?? {};
81
+ const result = { data, docId: doc.id, ref: doc.ref, expiresAt: Date.now() + DOC_CACHE_TTL_MS };
82
+ _docCache.set(email, result);
83
+ return result;
84
+ }
85
+ function _invalidateCache(email) {
86
+ _docCache.delete(email);
87
+ }
88
+ // ─────────────────────────────────────────────────────────────────────────────
89
+ // Period helpers
90
+ // ─────────────────────────────────────────────────────────────────────────────
91
+ function _toISO(ts) {
92
+ if (!ts)
93
+ return undefined;
94
+ if (typeof ts === 'string')
95
+ return ts;
96
+ // Firestore Timestamp object
97
+ if (typeof ts === 'object' && ts !== null && 'toDate' in ts) {
98
+ return ts.toDate().toISOString();
99
+ }
100
+ return undefined;
101
+ }
102
+ function _currentPeriod() {
103
+ const d = new Date();
104
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`;
105
+ }
106
+ function _shouldReset(periodEnd) {
107
+ const iso = _toISO(periodEnd);
108
+ if (!iso)
109
+ return true;
110
+ return new Date() >= new Date(iso);
111
+ }
112
+ const FREE_TIER_LIMIT = 100;
113
+ const MODEL_PRICING = {
114
+ 'claude-haiku-4-5-20251001': { input: 0.80, output: 4.00 },
115
+ 'claude-sonnet-4-6': { input: 3.00, output: 15.00 },
116
+ 'claude-opus-4-6': { input: 15.00, output: 75.00 },
117
+ };
118
+ function _estimateCostUSD(byModel) {
119
+ let total = 0;
120
+ for (const [model, counts] of Object.entries(byModel)) {
121
+ const pricing = MODEL_PRICING[model];
122
+ if (!pricing)
123
+ continue;
124
+ total += (counts.input / 1_000_000) * pricing.input;
125
+ total += (counts.output / 1_000_000) * pricing.output;
126
+ }
127
+ return Math.round(total * 10_000) / 10_000;
128
+ }
129
+ // ─────────────────────────────────────────────────────────────────────────────
130
+ // Provider implementation
131
+ // ─────────────────────────────────────────────────────────────────────────────
132
+ /**
133
+ * Firestore-backed billing provider for production deployments.
134
+ *
135
+ * The `userId` parameter on all methods must be the user's **email address**,
136
+ * since the Firestore `licenses` collection is indexed by email.
137
+ */
138
+ export class FirestoreBillingProvider {
139
+ async checkLimit(userId, hasPersonalKey) {
140
+ if (hasPersonalKey) {
141
+ return { ok: true, remaining: Infinity, topUpBalance: 0, totalAvailable: Infinity, useTopUp: false };
142
+ }
143
+ const doc = await _getLicenseDoc(userId);
144
+ if (!doc) {
145
+ // No license — free tier.
146
+ return { ok: false, remaining: 0, topUpBalance: 0, totalAvailable: 0, useTopUp: false, reason: 'no_subscription' };
147
+ }
148
+ const credits = doc.data['credits'] ?? {};
149
+ const monthlyIncluded = credits['monthlyIncluded'] ?? FREE_TIER_LIMIT;
150
+ const monthlyUsed = credits['monthlyUsed'] ?? 0;
151
+ const topUpBalance = credits['topUpBalance'] ?? 0;
152
+ const periodEnd = credits['periodEnd'];
153
+ if (_shouldReset(periodEnd)) {
154
+ await this._resetPeriod(doc);
155
+ return { ok: true, remaining: monthlyIncluded, topUpBalance, totalAvailable: monthlyIncluded + topUpBalance, useTopUp: false };
156
+ }
157
+ const remaining = Math.max(0, monthlyIncluded - monthlyUsed);
158
+ const totalAvailable = remaining + topUpBalance;
159
+ if (remaining > 0)
160
+ return { ok: true, remaining, topUpBalance, totalAvailable, useTopUp: false };
161
+ if (topUpBalance > 0)
162
+ return { ok: true, remaining: 0, topUpBalance, totalAvailable, useTopUp: true };
163
+ return { ok: false, remaining: 0, topUpBalance: 0, totalAvailable: 0, useTopUp: false, reason: 'limit_reached' };
164
+ }
165
+ async getUsage(userId) {
166
+ const doc = await _getLicenseDoc(userId);
167
+ if (!doc) {
168
+ return { currentPeriod: _currentPeriod(), periodEnd: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), messagesUsed: 0, inputTokens: 0, outputTokens: 0, byModel: {}, lastUpdated: new Date().toISOString() };
169
+ }
170
+ const credits = doc.data['credits'] ?? {};
171
+ return {
172
+ currentPeriod: _currentPeriod(),
173
+ periodEnd: _toISO(credits['periodEnd']) ?? new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
174
+ messagesUsed: credits['monthlyUsed'] ?? 0,
175
+ inputTokens: credits['inputTokens'] ?? 0,
176
+ outputTokens: credits['outputTokens'] ?? 0,
177
+ byModel: credits['byModel'] ?? {},
178
+ lastUpdated: _toISO(credits['lastUpdated']) ?? new Date().toISOString(),
179
+ };
180
+ }
181
+ async getUsageStatus(userId, hasPersonalKey) {
182
+ const usage = await this.getUsage(userId);
183
+ const doc = await _getLicenseDoc(userId);
184
+ const credits = doc?.data['credits'] ?? {};
185
+ const topUpBalance = credits['topUpBalance'] ?? 0;
186
+ const monthlyIncluded = credits['monthlyIncluded'] ?? FREE_TIER_LIMIT;
187
+ const subscription = doc ? this._toSubscription(doc.data) : undefined;
188
+ if (hasPersonalKey) {
189
+ return { mode: 'byok', usage, limit: null, remaining: null, topUpBalance, totalAvailable: null, percentUsed: null, resetsAt: usage.periodEnd, estimatedCostUSD: _estimateCostUSD(usage.byModel), ...(subscription ? { subscription } : {}) };
190
+ }
191
+ const isSubscriber = subscription?.status === 'active' || subscription?.status === 'trialing';
192
+ const remaining = Math.max(0, monthlyIncluded - usage.messagesUsed);
193
+ const totalAvailable = remaining + topUpBalance;
194
+ const percentUsed = Math.round((usage.messagesUsed / monthlyIncluded) * 100);
195
+ return {
196
+ mode: isSubscriber ? 'included' : 'free',
197
+ usage,
198
+ limit: monthlyIncluded,
199
+ remaining,
200
+ topUpBalance,
201
+ totalAvailable,
202
+ percentUsed,
203
+ resetsAt: usage.periodEnd,
204
+ estimatedCostUSD: _estimateCostUSD(usage.byModel),
205
+ ...(subscription ? { subscription } : {}),
206
+ isFreeUser: !isSubscriber,
207
+ };
208
+ }
209
+ async incrementUsage(userId, inputTokens, outputTokens, model, useTopUp) {
210
+ const doc = await _getLicenseDoc(userId);
211
+ if (!doc)
212
+ return; // No license doc — free tier, usage not tracked in Firestore.
213
+ const { FieldValue } = await _getFirestore();
214
+ const updates = {
215
+ 'credits.monthlyUsed': FieldValue.increment(1),
216
+ 'credits.inputTokens': FieldValue.increment(inputTokens),
217
+ 'credits.outputTokens': FieldValue.increment(outputTokens),
218
+ [`credits.byModel.${model}.input`]: FieldValue.increment(inputTokens),
219
+ [`credits.byModel.${model}.output`]: FieldValue.increment(outputTokens),
220
+ 'credits.lastUpdated': FieldValue.serverTimestamp(),
221
+ 'updatedAt': FieldValue.serverTimestamp(),
222
+ };
223
+ if (useTopUp)
224
+ updates['credits.topUpBalance'] = FieldValue.increment(-1);
225
+ await doc.ref.update(updates);
226
+ _invalidateCache(userId);
227
+ }
228
+ async batchIncrementUsage(userId, batch) {
229
+ const doc = await _getLicenseDoc(userId);
230
+ if (!doc)
231
+ return;
232
+ const { FieldValue } = await _getFirestore();
233
+ const updates = {
234
+ 'credits.monthlyUsed': FieldValue.increment(batch.messageCount),
235
+ 'credits.inputTokens': FieldValue.increment(batch.inputTokens),
236
+ 'credits.outputTokens': FieldValue.increment(batch.outputTokens),
237
+ 'credits.lastUpdated': FieldValue.serverTimestamp(),
238
+ 'updatedAt': FieldValue.serverTimestamp(),
239
+ };
240
+ if (batch.topUpUsed > 0)
241
+ updates['credits.topUpBalance'] = FieldValue.increment(-batch.topUpUsed);
242
+ for (const [model, counts] of Object.entries(batch.byModel)) {
243
+ updates[`credits.byModel.${model}.input`] = FieldValue.increment(counts.input);
244
+ updates[`credits.byModel.${model}.output`] = FieldValue.increment(counts.output);
245
+ }
246
+ await doc.ref.update(updates);
247
+ _invalidateCache(userId);
248
+ }
249
+ async getSubscription(userId) {
250
+ const doc = await _getLicenseDoc(userId);
251
+ if (!doc)
252
+ return undefined;
253
+ return this._toSubscription(doc.data);
254
+ }
255
+ async hasActiveSubscription(userId) {
256
+ const doc = await _getLicenseDoc(userId);
257
+ if (!doc)
258
+ return false;
259
+ const status = doc.data['status'];
260
+ return status === 'active' || status === 'trialing';
261
+ }
262
+ async getTopUpBalance(userId) {
263
+ const doc = await _getLicenseDoc(userId);
264
+ if (!doc)
265
+ return 0;
266
+ const credits = doc.data['credits'] ?? {};
267
+ return credits['topUpBalance'] ?? 0;
268
+ }
269
+ async canAccessPaidFeature(userId) {
270
+ return this.hasActiveSubscription(userId);
271
+ }
272
+ // ── Internal helpers ────────────────────────────────────────────────────
273
+ _toSubscription(data) {
274
+ const status = data['status'];
275
+ if (!status)
276
+ return undefined;
277
+ const stripeCustomerId = data['stripeCustomerId'];
278
+ const stripeSubscriptionId = data['stripeSubscriptionId'];
279
+ const cancelAtPeriodEnd = data['cancelAtPeriodEnd'];
280
+ const giftedBy = data['giftedBy'];
281
+ const currentPeriodEnd = _toISO(data['credits']?.['periodEnd']);
282
+ const giftedAt = _toISO(data['giftedAt']);
283
+ const expiresAt = _toISO(data['expiresAt']);
284
+ const note = data['note'];
285
+ return {
286
+ plan: data['plan'] ?? 'standard',
287
+ status: status,
288
+ ...(stripeCustomerId ? { stripeCustomerId } : {}),
289
+ ...(stripeSubscriptionId ? { stripeSubscriptionId } : {}),
290
+ ...(currentPeriodEnd ? { currentPeriodEnd } : {}),
291
+ ...(cancelAtPeriodEnd !== undefined ? { cancelAtPeriodEnd } : {}),
292
+ ...(giftedBy ? { giftedBy } : {}),
293
+ ...(giftedAt ? { giftedAt } : {}),
294
+ ...(expiresAt ? { expiresAt } : {}),
295
+ ...(note ? { note } : {}),
296
+ };
297
+ }
298
+ async _resetPeriod(doc) {
299
+ const { FieldValue } = await _getFirestore();
300
+ const newPeriodEnd = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
301
+ await doc.ref.update({
302
+ 'credits.monthlyUsed': 0,
303
+ 'credits.inputTokens': 0,
304
+ 'credits.outputTokens': 0,
305
+ 'credits.byModel': {},
306
+ 'credits.periodStart': FieldValue.serverTimestamp(),
307
+ 'credits.periodEnd': newPeriodEnd,
308
+ 'credits.lastUpdated': FieldValue.serverTimestamp(),
309
+ 'updatedAt': FieldValue.serverTimestamp(),
310
+ });
311
+ _invalidateCache(doc.docId);
312
+ }
313
+ }
314
+ //# sourceMappingURL=firestore-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firestore-provider.js","sourceRoot":"","sources":["../../../src/modules/billing/firestore-provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * @module billing
3
+ * @description Factory for sdk.billing.* — usage metering, subscription management,
4
+ * top-up credits, and Stripe integration.
5
+ *
6
+ * **Provider selection:** The billing module automatically chooses the storage backend:
7
+ * - `FIREBASE_SERVICE_ACCOUNT_KEY` set → `FirestoreBillingProvider` (production)
8
+ * - Not set → `LocalBillingProvider` (dev, stores state in `.soulcraft/billing/`)
9
+ *
10
+ * **Usage metering:** Fire-and-forget via a 5-second write-behind buffer. Recording
11
+ * usage never blocks the AI response path.
12
+ *
13
+ * **Stripe integration:** Full subscription lifecycle — checkout sessions, customer
14
+ * portal, and subscription cancellation. Requires `STRIPE_SECRET_KEY`.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * // Check credits and record usage around an AI call:
19
+ * const check = await sdk.billing.checkLimit(user.email, user.hasByok)
20
+ * if (!check.ok) return c.json({ error: 'Monthly limit reached' }, 402)
21
+ *
22
+ * const response = await sdk.ai.complete(...)
23
+ * sdk.billing.recordUsage(user.email, response.usage.inputTokens, response.usage.outputTokens, response.model)
24
+ * ```
25
+ */
26
+ import type { BillingModule, BillingProvider } from './types.js';
27
+ /**
28
+ * Options for `createBillingModule()`.
29
+ */
30
+ export interface CreateBillingModuleOptions {
31
+ /**
32
+ * Override the billing provider directly.
33
+ * When not provided, the provider is selected from the environment:
34
+ * Firestore when `FIREBASE_SERVICE_ACCOUNT_KEY` is set, local otherwise.
35
+ */
36
+ provider?: BillingProvider;
37
+ }
38
+ /**
39
+ * Create the `sdk.billing.*` module.
40
+ *
41
+ * @param options - Optional provider override (primarily for testing).
42
+ * @returns A fully configured `BillingModule`.
43
+ */
44
+ export declare function createBillingModule(options?: CreateBillingModuleOptions): BillingModule;
45
+ export type { BillingModule, BillingPlanType, SubscriptionStatus, ModelUsage, UsageData, UsageStatus, SubscriptionData, LimitCheckResult, BatchIncrementData, CreateCheckoutSessionOptions, CreatePortalSessionOptions, } from './types.js';
46
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/billing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAMH,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EAOhB,MAAM,YAAY,CAAA;AAMnB;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;CAC3B;AAgBD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,0BAA+B,GAAG,aAAa,CAuH3F;AAGD,YAAY,EACV,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,UAAU,EACV,SAAS,EACT,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,4BAA4B,EAC5B,0BAA0B,GAC3B,MAAM,YAAY,CAAA"}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * @module billing
3
+ * @description Factory for sdk.billing.* — usage metering, subscription management,
4
+ * top-up credits, and Stripe integration.
5
+ *
6
+ * **Provider selection:** The billing module automatically chooses the storage backend:
7
+ * - `FIREBASE_SERVICE_ACCOUNT_KEY` set → `FirestoreBillingProvider` (production)
8
+ * - Not set → `LocalBillingProvider` (dev, stores state in `.soulcraft/billing/`)
9
+ *
10
+ * **Usage metering:** Fire-and-forget via a 5-second write-behind buffer. Recording
11
+ * usage never blocks the AI response path.
12
+ *
13
+ * **Stripe integration:** Full subscription lifecycle — checkout sessions, customer
14
+ * portal, and subscription cancellation. Requires `STRIPE_SECRET_KEY`.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * // Check credits and record usage around an AI call:
19
+ * const check = await sdk.billing.checkLimit(user.email, user.hasByok)
20
+ * if (!check.ok) return c.json({ error: 'Monthly limit reached' }, 402)
21
+ *
22
+ * const response = await sdk.ai.complete(...)
23
+ * sdk.billing.recordUsage(user.email, response.usage.inputTokens, response.usage.outputTokens, response.model)
24
+ * ```
25
+ */
26
+ import Stripe from 'stripe';
27
+ import { LocalBillingProvider } from './local-provider.js';
28
+ import { FirestoreBillingProvider } from './firestore-provider.js';
29
+ import { UsageBuffer } from './usage-buffer.js';
30
+ // ─────────────────────────────────────────────────────────────────────────────
31
+ // Stripe helper
32
+ // ─────────────────────────────────────────────────────────────────────────────
33
+ function _getStripe() {
34
+ const key = process.env['STRIPE_SECRET_KEY'];
35
+ if (!key)
36
+ throw new Error('STRIPE_SECRET_KEY is not set — configure it to use Stripe billing features');
37
+ return new Stripe(key, { apiVersion: '2026-02-25.clover', typescript: true });
38
+ }
39
+ // ─────────────────────────────────────────────────────────────────────────────
40
+ // Factory
41
+ // ─────────────────────────────────────────────────────────────────────────────
42
+ /**
43
+ * Create the `sdk.billing.*` module.
44
+ *
45
+ * @param options - Optional provider override (primarily for testing).
46
+ * @returns A fully configured `BillingModule`.
47
+ */
48
+ export function createBillingModule(options = {}) {
49
+ const provider = options.provider
50
+ ?? (process.env['FIREBASE_SERVICE_ACCOUNT_KEY']
51
+ ? new FirestoreBillingProvider()
52
+ : new LocalBillingProvider());
53
+ const buffer = new UsageBuffer(provider);
54
+ return {
55
+ // ── Usage metering ──────────────────────────────────────────────────────
56
+ async checkLimit(userId, hasPersonalKey) {
57
+ return provider.checkLimit(userId, hasPersonalKey);
58
+ },
59
+ recordUsage(userId, inputTokens, outputTokens, model) {
60
+ // The limit check told us whether to draw from top-up; for the buffer we
61
+ // default to false here — the billing provider tracks which messages used
62
+ // top-up internally via the LimitCheckResult.useTopUp flag returned to the
63
+ // caller. Products that need exact top-up tracking should call the provider
64
+ // directly via the flush path.
65
+ buffer.increment(userId, inputTokens, outputTokens, model, false);
66
+ },
67
+ async getUsage(userId) {
68
+ return provider.getUsage(userId);
69
+ },
70
+ async getUsageStatus(userId, hasPersonalKey) {
71
+ return provider.getUsageStatus(userId, hasPersonalKey);
72
+ },
73
+ // ── Subscription management ─────────────────────────────────────────────
74
+ async getSubscription(userId) {
75
+ return provider.getSubscription(userId);
76
+ },
77
+ async hasActiveSubscription(userId) {
78
+ return provider.hasActiveSubscription(userId);
79
+ },
80
+ async createCheckoutSession(options) {
81
+ const stripe = _getStripe();
82
+ const session = await stripe.checkout.sessions.create({
83
+ mode: 'subscription',
84
+ line_items: [{ price: options.priceId, quantity: 1 }],
85
+ success_url: options.successUrl,
86
+ cancel_url: options.cancelUrl,
87
+ ...(options.customerId ? { customer: options.customerId } : {}),
88
+ ...(options.customerEmail && !options.customerId ? { customer_email: options.customerEmail } : {}),
89
+ });
90
+ if (!session.url)
91
+ throw new Error('Stripe checkout session created but returned no URL');
92
+ return session.url;
93
+ },
94
+ async createPortalSession(options) {
95
+ const stripe = _getStripe();
96
+ const session = await stripe.billingPortal.sessions.create({
97
+ customer: options.customerId,
98
+ return_url: options.returnUrl,
99
+ });
100
+ return session.url;
101
+ },
102
+ async cancelSubscription(userId) {
103
+ const stripe = _getStripe();
104
+ const sub = await provider.getSubscription(userId);
105
+ if (!sub?.stripeSubscriptionId) {
106
+ throw new Error(`No active Stripe subscription found for user ${userId}`);
107
+ }
108
+ await stripe.subscriptions.update(sub.stripeSubscriptionId, { cancel_at_period_end: true });
109
+ },
110
+ // ── Top-up credits ───────────────────────────────────────────────────────
111
+ async getTopUpBalance(userId) {
112
+ return provider.getTopUpBalance(userId);
113
+ },
114
+ async addTopUp(userId, amount) {
115
+ // For the local provider, directly update the balance.
116
+ // For Firestore, we use a targeted increment to avoid race conditions.
117
+ if (provider instanceof LocalBillingProvider) {
118
+ const current = await provider.getTopUpBalance(userId);
119
+ const newBalance = current + amount;
120
+ await provider.setTopUpBalance(userId, newBalance);
121
+ return newBalance;
122
+ }
123
+ // Firestore: increment and then read the new balance.
124
+ if (provider instanceof FirestoreBillingProvider) {
125
+ await provider.batchIncrementUsage(userId, {
126
+ messageCount: 0, inputTokens: 0, outputTokens: 0,
127
+ topUpUsed: -amount, // Negative topUpUsed = add credits
128
+ byModel: {},
129
+ });
130
+ return provider.getTopUpBalance(userId);
131
+ }
132
+ // Generic fallback for custom providers — not atomic but correct.
133
+ const current = await provider.getTopUpBalance(userId);
134
+ return current + amount;
135
+ },
136
+ // ── Feature gating ───────────────────────────────────────────────────────
137
+ async canAccessPaidFeature(userId) {
138
+ return provider.canAccessPaidFeature(userId);
139
+ },
140
+ // ── Lifecycle ────────────────────────────────────────────────────────────
141
+ async flush() {
142
+ await buffer.flush();
143
+ },
144
+ stopFlush() {
145
+ buffer.stop();
146
+ },
147
+ };
148
+ }
149
+ //# sourceMappingURL=index.js.map