@soulcraft/sdk 1.0.0 → 1.2.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 (112) hide show
  1. package/dist/client/index.d.ts +3 -0
  2. package/dist/client/index.d.ts.map +1 -1
  3. package/dist/client/index.js +2 -0
  4. package/dist/client/index.js.map +1 -1
  5. package/dist/index.d.ts +7 -6
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/modules/auth/service-token.d.ts +62 -0
  10. package/dist/modules/auth/service-token.d.ts.map +1 -0
  11. package/dist/modules/auth/service-token.js +99 -0
  12. package/dist/modules/auth/service-token.js.map +1 -0
  13. package/dist/modules/billing/firestore-provider.d.ts +60 -0
  14. package/dist/modules/billing/firestore-provider.d.ts.map +1 -0
  15. package/dist/modules/billing/firestore-provider.js +314 -0
  16. package/dist/modules/billing/firestore-provider.js.map +1 -0
  17. package/dist/modules/billing/index.d.ts +58 -0
  18. package/dist/modules/billing/index.d.ts.map +1 -0
  19. package/dist/modules/billing/index.js +164 -0
  20. package/dist/modules/billing/index.js.map +1 -0
  21. package/dist/modules/billing/local-provider.d.ts +38 -0
  22. package/dist/modules/billing/local-provider.d.ts.map +1 -0
  23. package/dist/modules/billing/local-provider.js +242 -0
  24. package/dist/modules/billing/local-provider.js.map +1 -0
  25. package/dist/modules/billing/portal-provider.d.ts +70 -0
  26. package/dist/modules/billing/portal-provider.d.ts.map +1 -0
  27. package/dist/modules/billing/portal-provider.js +204 -0
  28. package/dist/modules/billing/portal-provider.js.map +1 -0
  29. package/dist/modules/billing/types.d.ts +323 -3
  30. package/dist/modules/billing/types.d.ts.map +1 -1
  31. package/dist/modules/billing/types.js +22 -2
  32. package/dist/modules/billing/types.js.map +1 -1
  33. package/dist/modules/billing/usage-buffer.d.ts +72 -0
  34. package/dist/modules/billing/usage-buffer.d.ts.map +1 -0
  35. package/dist/modules/billing/usage-buffer.js +141 -0
  36. package/dist/modules/billing/usage-buffer.js.map +1 -0
  37. package/dist/modules/formats/types.d.ts +65 -3
  38. package/dist/modules/formats/types.d.ts.map +1 -1
  39. package/dist/modules/formats/types.js +40 -3
  40. package/dist/modules/formats/types.js.map +1 -1
  41. package/dist/modules/formats/wdoc.d.ts +263 -0
  42. package/dist/modules/formats/wdoc.d.ts.map +1 -0
  43. package/dist/modules/formats/wdoc.js +21 -0
  44. package/dist/modules/formats/wdoc.js.map +1 -0
  45. package/dist/modules/formats/wquiz.d.ts +122 -0
  46. package/dist/modules/formats/wquiz.d.ts.map +1 -0
  47. package/dist/modules/formats/wquiz.js +23 -0
  48. package/dist/modules/formats/wquiz.js.map +1 -0
  49. package/dist/modules/formats/wslide.d.ts +130 -0
  50. package/dist/modules/formats/wslide.d.ts.map +1 -0
  51. package/dist/modules/formats/wslide.js +23 -0
  52. package/dist/modules/formats/wslide.js.map +1 -0
  53. package/dist/modules/formats/wviz.d.ts +114 -0
  54. package/dist/modules/formats/wviz.d.ts.map +1 -0
  55. package/dist/modules/formats/wviz.js +21 -0
  56. package/dist/modules/formats/wviz.js.map +1 -0
  57. package/dist/modules/hall/browser.d.ts +88 -0
  58. package/dist/modules/hall/browser.d.ts.map +1 -0
  59. package/dist/modules/hall/browser.js +265 -0
  60. package/dist/modules/hall/browser.js.map +1 -0
  61. package/dist/modules/hall/protocol.d.ts +39 -0
  62. package/dist/modules/hall/protocol.d.ts.map +1 -0
  63. package/dist/modules/hall/protocol.js +52 -0
  64. package/dist/modules/hall/protocol.js.map +1 -0
  65. package/dist/modules/hall/server.d.ts +172 -0
  66. package/dist/modules/hall/server.d.ts.map +1 -0
  67. package/dist/modules/hall/server.js +457 -0
  68. package/dist/modules/hall/server.js.map +1 -0
  69. package/dist/modules/hall/types.d.ts +502 -31
  70. package/dist/modules/hall/types.d.ts.map +1 -1
  71. package/dist/modules/hall/types.js +13 -8
  72. package/dist/modules/hall/types.js.map +1 -1
  73. package/dist/modules/kits/index.d.ts +41 -0
  74. package/dist/modules/kits/index.d.ts.map +1 -0
  75. package/dist/modules/kits/index.js +85 -0
  76. package/dist/modules/kits/index.js.map +1 -0
  77. package/dist/modules/kits/types.d.ts +107 -3
  78. package/dist/modules/kits/types.d.ts.map +1 -1
  79. package/dist/modules/kits/types.js +15 -2
  80. package/dist/modules/kits/types.js.map +1 -1
  81. package/dist/modules/license/index.d.ts +53 -0
  82. package/dist/modules/license/index.d.ts.map +1 -0
  83. package/dist/modules/license/index.js +233 -0
  84. package/dist/modules/license/index.js.map +1 -0
  85. package/dist/modules/license/types.d.ts +222 -3
  86. package/dist/modules/license/types.d.ts.map +1 -1
  87. package/dist/modules/license/types.js +21 -2
  88. package/dist/modules/license/types.js.map +1 -1
  89. package/dist/modules/notifications/index.d.ts +40 -0
  90. package/dist/modules/notifications/index.d.ts.map +1 -0
  91. package/dist/modules/notifications/index.js +280 -0
  92. package/dist/modules/notifications/index.js.map +1 -0
  93. package/dist/modules/notifications/types.d.ts +152 -3
  94. package/dist/modules/notifications/types.d.ts.map +1 -1
  95. package/dist/modules/notifications/types.js +21 -2
  96. package/dist/modules/notifications/types.js.map +1 -1
  97. package/dist/server/create-sdk.d.ts +4 -0
  98. package/dist/server/create-sdk.d.ts.map +1 -1
  99. package/dist/server/create-sdk.js +19 -26
  100. package/dist/server/create-sdk.js.map +1 -1
  101. package/dist/server/hall-handlers.d.ts +90 -151
  102. package/dist/server/hall-handlers.d.ts.map +1 -1
  103. package/dist/server/hall-handlers.js +84 -204
  104. package/dist/server/hall-handlers.js.map +1 -1
  105. package/dist/server/index.d.ts +10 -2
  106. package/dist/server/index.d.ts.map +1 -1
  107. package/dist/server/index.js +9 -2
  108. package/dist/server/index.js.map +1 -1
  109. package/dist/types.d.ts +35 -25
  110. package/dist/types.d.ts.map +1 -1
  111. package/docs/USAGE.md +224 -1
  112. package/package.json +13 -5
@@ -55,6 +55,9 @@ export { WsTransport } from '../transports/ws.js';
55
55
  export { SseTransport } from '../transports/sse.js';
56
56
  export type { SDKTransport } from '../transports/transport.js';
57
57
  export { createBrainyProxy } from '../modules/brainy/proxy.js';
58
+ export { joinHallRoom } from '../modules/hall/browser.js';
59
+ export type { JoinHallRoomOptions } from '../modules/hall/browser.js';
60
+ export type { HallRoomHandle, HallRoomHandleEvents, TranscriptEvent, ConceptMentionEvent, RelationProposedEvent, SpeakerChangedEvent, PeerJoinedEvent, PeerLeftEvent, RoomOptions, RecordingManifest, ConceptInput, } from '../modules/hall/types.js';
58
61
  export type { ClientSDKOptions } from '../types.js';
59
62
  export type { SoulcraftBrainy } from '../modules/brainy/types.js';
60
63
  export type { BrainyChangeEvent } from '../modules/brainy/events.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,YAAY,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAG9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAG9D,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACnD,YAAY,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AACjE,YAAY,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AACpE,OAAO,EACL,QAAQ,EACR,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,sBAAsB,GACvB,MAAM,6BAA6B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,YAAY,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAG9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAG9D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AACzD,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AACrE,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,YAAY,GACb,MAAM,0BAA0B,CAAA;AAGjC,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACnD,YAAY,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AACjE,YAAY,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AACpE,OAAO,EACL,QAAQ,EACR,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,sBAAsB,GACvB,MAAM,6BAA6B,CAAA"}
@@ -56,5 +56,7 @@ export { WsTransport } from '../transports/ws.js';
56
56
  export { SseTransport } from '../transports/sse.js';
57
57
  // ── Proxy factory ─────────────────────────────────────────────────────────────
58
58
  export { createBrainyProxy } from '../modules/brainy/proxy.js';
59
+ // ── Hall room client (browser WebRTC) ─────────────────────────────────────────
60
+ export { joinHallRoom } from '../modules/hall/browser.js';
59
61
  export { SDKError, SDKDisconnectedError, SDKTimeoutError, SDKAuthError, SDKForbiddenError, SDKRpcError, SDKMethodNotFoundError, } from '../modules/brainy/errors.js';
60
62
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,iFAAiF;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAGnD,iFAAiF;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAM9D,OAAO,EACL,QAAQ,EACR,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,sBAAsB,GACvB,MAAM,6BAA6B,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,iFAAiF;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAGnD,iFAAiF;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAE9D,iFAAiF;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAoBzD,OAAO,EACL,QAAQ,EACR,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,sBAAsB,GACvB,MAAM,6BAA6B,CAAA"}
package/dist/index.d.ts CHANGED
@@ -18,16 +18,17 @@ export { createCapabilityToken, verifyCapabilityToken, } from './modules/brainy/
18
18
  export { SDKError, SDKDisconnectedError, SDKTimeoutError, SDKAuthError, SDKForbiddenError, SDKRpcError, SDKMethodNotFoundError, } from './modules/brainy/errors.js';
19
19
  export type { VfsModule } from './modules/vfs/types.js';
20
20
  export type { VersionsModule } from './modules/versions/types.js';
21
- export type { HallModule, HallConfig, RoomOptions, RecordingManifest, ConceptEvent, RelationProposedEvent, AttentionEvent, RoomEventMap, } from './modules/hall/types.js';
21
+ export type { HallModule, HallRoom, HallRoomHandle, HallRoomEvents, HallRoomHandleEvents, HallConnectionOptions, RoomOptions, ConceptInput, RecordingManifest, TranscriptEvent, ConceptMentionEvent, RelationProposedEvent, SpeakerChangedEvent, PeerJoinedEvent, PeerLeftEvent, } from './modules/hall/types.js';
22
22
  export type { AuthModule, PlatformRole, SoulcraftAuthProvider, SoulcraftOrganization, SoulcraftUserFields, SoulcraftSessionUser, SoulcraftSession, OIDCClientConfig, AuthMode, } from './modules/auth/types.js';
23
23
  export { SOULCRAFT_USER_FIELDS, SOULCRAFT_SESSION_CONFIG, computeEmailHash, getAuthMode, getOIDCClientConfig, } from './modules/auth/config.js';
24
24
  export type { AiModule, AiCompleteOptions, AiCompleteResult, AiStreamOptions, AiStreamEvent, AiToolCall, AiMessage, AiContentBlock, AiTool, AiModel, } from './modules/ai/types.js';
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;AAuCnC,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,62 @@
1
+ /**
2
+ * @module modules/auth/service-token
3
+ * @description Timing-safe bearer token verification for service-to-service requests.
4
+ *
5
+ * Some Soulcraft services authenticate inter-service calls with a plain shared secret
6
+ * rather than HMAC-signed capability tokens. Examples include:
7
+ * - `WORKSHOP_DEPLOY_SECRET` — CI/CD pipelines triggering workshop deploys
8
+ * - `ACADEMY_PUBLISH_SECRET` — Academy publishing webhooks
9
+ *
10
+ * This module provides `verifyServiceToken()` — a single helper that extracts the
11
+ * `Authorization: Bearer <token>` header and compares it against the expected secret
12
+ * using a constant-time comparison to prevent timing-attack leaks.
13
+ *
14
+ * For cross-product server-to-server calls that need user identity, use capability
15
+ * tokens from `@soulcraft/sdk/server` (`createCapabilityToken` / `verifyCapabilityToken`)
16
+ * instead.
17
+ *
18
+ * @example Hono route guarded by a service token
19
+ * ```typescript
20
+ * import { verifyServiceToken } from '@soulcraft/sdk/server'
21
+ *
22
+ * app.post('/api/deploy', async (c) => {
23
+ * if (!verifyServiceToken(c.req.raw, process.env.WORKSHOP_DEPLOY_SECRET!)) {
24
+ * return c.json({ error: 'Unauthorized' }, 401)
25
+ * }
26
+ * await triggerDeploy()
27
+ * return c.json({ ok: true })
28
+ * })
29
+ * ```
30
+ */
31
+ /**
32
+ * Extract the bearer token from an HTTP `Authorization` header.
33
+ *
34
+ * Returns `null` if the header is absent or does not follow the
35
+ * `Bearer <token>` scheme.
36
+ *
37
+ * @param request - A Web API `Request` object (Hono / SvelteKit / Bun / Node18+).
38
+ * @returns The raw token string, or `null` if not present.
39
+ */
40
+ export declare function extractBearerToken(request: Request): string | null;
41
+ /**
42
+ * Verify that the `Authorization: Bearer` token in `request` matches `expected`.
43
+ *
44
+ * Uses a timing-safe comparison (SHA-256 digest of both sides before comparing)
45
+ * to prevent timing attacks that could leak the secret.
46
+ *
47
+ * Returns `false` immediately if `expected` is empty, preventing accidental
48
+ * wide-open access when the env var is not configured.
49
+ *
50
+ * @param request - A Web API `Request` object.
51
+ * @param expected - The expected secret (e.g. `process.env.WORKSHOP_DEPLOY_SECRET`).
52
+ * @returns `true` if the token is present and matches `expected`; `false` otherwise.
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * if (!verifyServiceToken(c.req.raw, process.env.WORKSHOP_DEPLOY_SECRET!)) {
57
+ * return c.json({ error: 'Unauthorized' }, 401)
58
+ * }
59
+ * ```
60
+ */
61
+ export declare function verifyServiceToken(request: Request, expected: string): boolean;
62
+ //# sourceMappingURL=service-token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-token.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/service-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAsBH;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAKlE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAc9E"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @module modules/auth/service-token
3
+ * @description Timing-safe bearer token verification for service-to-service requests.
4
+ *
5
+ * Some Soulcraft services authenticate inter-service calls with a plain shared secret
6
+ * rather than HMAC-signed capability tokens. Examples include:
7
+ * - `WORKSHOP_DEPLOY_SECRET` — CI/CD pipelines triggering workshop deploys
8
+ * - `ACADEMY_PUBLISH_SECRET` — Academy publishing webhooks
9
+ *
10
+ * This module provides `verifyServiceToken()` — a single helper that extracts the
11
+ * `Authorization: Bearer <token>` header and compares it against the expected secret
12
+ * using a constant-time comparison to prevent timing-attack leaks.
13
+ *
14
+ * For cross-product server-to-server calls that need user identity, use capability
15
+ * tokens from `@soulcraft/sdk/server` (`createCapabilityToken` / `verifyCapabilityToken`)
16
+ * instead.
17
+ *
18
+ * @example Hono route guarded by a service token
19
+ * ```typescript
20
+ * import { verifyServiceToken } from '@soulcraft/sdk/server'
21
+ *
22
+ * app.post('/api/deploy', async (c) => {
23
+ * if (!verifyServiceToken(c.req.raw, process.env.WORKSHOP_DEPLOY_SECRET!)) {
24
+ * return c.json({ error: 'Unauthorized' }, 401)
25
+ * }
26
+ * await triggerDeploy()
27
+ * return c.json({ ok: true })
28
+ * })
29
+ * ```
30
+ */
31
+ import { timingSafeEqual, createHash } from 'node:crypto';
32
+ // ─────────────────────────────────────────────────────────────────────────────
33
+ // Utilities
34
+ // ─────────────────────────────────────────────────────────────────────────────
35
+ /**
36
+ * Hash a string to a fixed 32-byte SHA-256 digest for `timingSafeEqual`.
37
+ *
38
+ * Both sides must be the same byte length for `timingSafeEqual` to work.
39
+ * Hashing also ensures comparison time is independent of token length.
40
+ */
41
+ function _toComparableBuffer(value) {
42
+ return createHash('sha256').update(value, 'utf8').digest();
43
+ }
44
+ // ─────────────────────────────────────────────────────────────────────────────
45
+ // Public API
46
+ // ─────────────────────────────────────────────────────────────────────────────
47
+ /**
48
+ * Extract the bearer token from an HTTP `Authorization` header.
49
+ *
50
+ * Returns `null` if the header is absent or does not follow the
51
+ * `Bearer <token>` scheme.
52
+ *
53
+ * @param request - A Web API `Request` object (Hono / SvelteKit / Bun / Node18+).
54
+ * @returns The raw token string, or `null` if not present.
55
+ */
56
+ export function extractBearerToken(request) {
57
+ const header = request.headers.get('authorization');
58
+ if (!header?.startsWith('Bearer '))
59
+ return null;
60
+ const token = header.slice(7).trim();
61
+ return token.length > 0 ? token : null;
62
+ }
63
+ /**
64
+ * Verify that the `Authorization: Bearer` token in `request` matches `expected`.
65
+ *
66
+ * Uses a timing-safe comparison (SHA-256 digest of both sides before comparing)
67
+ * to prevent timing attacks that could leak the secret.
68
+ *
69
+ * Returns `false` immediately if `expected` is empty, preventing accidental
70
+ * wide-open access when the env var is not configured.
71
+ *
72
+ * @param request - A Web API `Request` object.
73
+ * @param expected - The expected secret (e.g. `process.env.WORKSHOP_DEPLOY_SECRET`).
74
+ * @returns `true` if the token is present and matches `expected`; `false` otherwise.
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * if (!verifyServiceToken(c.req.raw, process.env.WORKSHOP_DEPLOY_SECRET!)) {
79
+ * return c.json({ error: 'Unauthorized' }, 401)
80
+ * }
81
+ * ```
82
+ */
83
+ export function verifyServiceToken(request, expected) {
84
+ // Reject immediately if the expected secret is not configured.
85
+ if (!expected)
86
+ return false;
87
+ const actual = extractBearerToken(request);
88
+ if (!actual)
89
+ return false;
90
+ try {
91
+ const a = _toComparableBuffer(actual);
92
+ const b = _toComparableBuffer(expected);
93
+ return timingSafeEqual(a, b);
94
+ }
95
+ catch {
96
+ return false;
97
+ }
98
+ }
99
+ //# sourceMappingURL=service-token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-token.js","sourceRoot":"","sources":["../../../src/modules/auth/service-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEzD,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAEhF;;;;;GAKG;AACH,SAAS,mBAAmB,CAAC,KAAa;IACxC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,MAAM,EAAE,CAAA;AAC5D,CAAC;AAED,gFAAgF;AAChF,aAAa;AACb,gFAAgF;AAEhF;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAgB;IACjD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;IACnD,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAA;IAC/C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IACpC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA;AACxC,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAgB,EAAE,QAAgB;IACnE,+DAA+D;IAC/D,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAA;IAE3B,MAAM,MAAM,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAA;IAC1C,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IAEzB,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAA;QACrC,MAAM,CAAC,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAA;QACvC,OAAO,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC"}
@@ -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