@lssm/example.saas-boilerplate 0.0.0-canary-20251215234340 → 0.0.0-canary-20251216023757

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 (67) hide show
  1. package/.turbo/turbo-build.log +32 -24
  2. package/CHANGELOG.md +8 -8
  3. package/dist/billing/billing.contracts.js +1 -0
  4. package/dist/billing/billing.enum.js +1 -0
  5. package/dist/billing/billing.event.js +1 -0
  6. package/dist/billing/billing.handler.js +1 -0
  7. package/dist/billing/billing.schema.js +1 -0
  8. package/dist/billing/index.js +1 -0
  9. package/dist/dashboard/index.js +1 -0
  10. package/dist/handlers/index.js +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/presentations/index.js +1 -1
  13. package/dist/project/index.js +1 -0
  14. package/dist/project/project.contracts.js +1 -0
  15. package/dist/project/project.enum.js +1 -0
  16. package/dist/project/project.event.js +1 -0
  17. package/dist/project/project.handler.js +1 -0
  18. package/dist/project/project.presentation.js +1 -0
  19. package/dist/project/project.schema.js +1 -0
  20. package/dist/settings/index.js +1 -0
  21. package/dist/settings/settings.entity.js +1 -0
  22. package/dist/settings/settings.enum.js +1 -0
  23. package/package.json +45 -29
  24. package/src/billing/billing.contracts.ts +116 -0
  25. package/src/{entities/billing.ts → billing/billing.entity.ts} +2 -1
  26. package/src/billing/billing.enum.ts +24 -0
  27. package/src/billing/billing.event.ts +94 -0
  28. package/src/{handlers/billing.handlers.ts → billing/billing.handler.ts} +7 -8
  29. package/src/{presentations/billing.ts → billing/billing.presentation.ts} +1 -3
  30. package/src/{contracts/billing.ts → billing/billing.schema.ts} +29 -131
  31. package/src/billing/index.ts +65 -0
  32. package/src/{presentations/dashboard.ts → dashboard/dashboard.presentation.ts} +1 -3
  33. package/src/dashboard/index.ts +9 -0
  34. package/src/handlers/index.ts +12 -26
  35. package/src/index.ts +43 -6
  36. package/src/presentations/index.ts +24 -30
  37. package/src/project/index.ts +67 -0
  38. package/src/project/project.contracts.ts +185 -0
  39. package/src/{entities/project.ts → project/project.entity.ts} +2 -1
  40. package/src/project/project.enum.ts +23 -0
  41. package/src/project/project.event.ts +109 -0
  42. package/src/{handlers/project.handlers.ts → project/project.handler.ts} +8 -9
  43. package/src/{presentations/project-list.ts → project/project.presentation.ts} +2 -4
  44. package/src/project/project.schema.ts +145 -0
  45. package/src/settings/index.ts +10 -0
  46. package/src/{entities/settings.ts → settings/settings.entity.ts} +3 -11
  47. package/src/settings/settings.enum.ts +12 -0
  48. package/src/{handlers → shared}/mock-data.ts +2 -1
  49. package/tsconfig.tsbuildinfo +1 -1
  50. package/dist/contracts/billing.js +0 -1
  51. package/dist/contracts/index.js +0 -1
  52. package/dist/contracts/project.js +0 -1
  53. package/dist/entities/index.js +0 -1
  54. package/dist/entities/settings.js +0 -1
  55. package/dist/events.js +0 -1
  56. package/dist/handlers/billing.handlers.js +0 -1
  57. package/dist/handlers/project.handlers.js +0 -1
  58. package/dist/presentations/project-list.js +0 -1
  59. package/src/contracts/index.ts +0 -36
  60. package/src/contracts/project.ts +0 -314
  61. package/src/entities/index.ts +0 -62
  62. package/src/events.ts +0 -177
  63. /package/dist/{entities/billing.js → billing/billing.entity.js} +0 -0
  64. /package/dist/{presentations/billing.js → billing/billing.presentation.js} +0 -0
  65. /package/dist/{presentations/dashboard.js → dashboard/dashboard.presentation.js} +0 -0
  66. /package/dist/{entities/project.js → project/project.entity.js} +0 -0
  67. /package/dist/{handlers → shared}/mock-data.js +0 -0
@@ -2,31 +2,39 @@ $ bun build:bundle && bun build:types
2
2
  $ tsdown
3
3
  ℹ tsdown v0.17.4 powered by rolldown v1.0.0-beta.53
4
4
  ℹ config file: /home/runner/work/contractspec/contractspec/packages/examples/saas-boilerplate/tsdown.config.js
5
- ℹ entry: src/events.ts, src/example.ts, src/feature.ts, src/index.ts, src/contracts/billing.ts, src/contracts/index.ts, src/contracts/project.ts, src/docs/index.ts, src/docs/saas-boilerplate.docblock.ts, src/entities/billing.ts, src/entities/index.ts, src/entities/project.ts, src/entities/settings.ts, src/handlers/billing.handlers.ts, src/handlers/index.ts, src/handlers/mock-data.ts, src/handlers/project.handlers.ts, src/presentations/billing.ts, src/presentations/dashboard.ts, src/presentations/index.ts, src/presentations/project-list.ts
5
+ ℹ entry: src/example.ts, src/feature.ts, src/index.ts, src/billing/billing.contracts.ts, src/billing/billing.entity.ts, src/billing/billing.enum.ts, src/billing/billing.event.ts, src/billing/billing.handler.ts, src/billing/billing.presentation.ts, src/billing/billing.schema.ts, src/billing/index.ts, src/dashboard/dashboard.presentation.ts, src/dashboard/index.ts, src/docs/index.ts, src/docs/saas-boilerplate.docblock.ts, src/handlers/index.ts, src/presentations/index.ts, src/project/index.ts, src/project/project.contracts.ts, src/project/project.entity.ts, src/project/project.enum.ts, src/project/project.event.ts, src/project/project.handler.ts, src/project/project.presentation.ts, src/project/project.schema.ts, src/settings/index.ts, src/settings/settings.entity.ts, src/settings/settings.enum.ts, src/shared/mock-data.ts
6
6
  ℹ target: esnext
7
7
  ℹ tsconfig: tsconfig.json
8
8
  ℹ Build start
9
- ℹ dist/contracts/project.js 5.13 kB │ gzip: 1.37 kB
10
- ℹ dist/index.js 5.05 kB │ gzip: 1.33 kB
11
- ℹ dist/contracts/billing.js 4.43 kB │ gzip: 1.25 kB
12
- ℹ dist/events.js 3.50 kB │ gzip: 0.76 kB
13
- ℹ dist/docs/saas-boilerplate.docblock.js 3.46 kB │ gzip: 1.38 kB
14
- ℹ dist/entities/billing.js 2.57 kB │ gzip: 0.94 kB
15
- ℹ dist/handlers/mock-data.js 2.06 kB │ gzip: 0.78 kB
16
- ℹ dist/feature.js 1.95 kB │ gzip: 0.52 kB
17
- ℹ dist/contracts/index.js 1.70 kB │ gzip: 0.44 kB
18
- ℹ dist/entities/project.js 1.64 kB │ gzip: 0.67 kB
19
- ℹ dist/entities/settings.js 1.60 kB │ gzip: 0.67 kB
20
- ℹ dist/handlers/project.handlers.js 1.43 kB │ gzip: 0.64 kB
21
- ℹ dist/entities/index.js 0.87 kB │ gzip: 0.36 kB
22
- ℹ dist/presentations/project-list.js 0.84 kB │ gzip: 0.38 kB
23
- ℹ dist/handlers/index.js 0.78 kB │ gzip: 0.30 kB
24
- ℹ dist/presentations/billing.js 0.77 kB │ gzip: 0.32 kB
25
- ℹ dist/handlers/billing.handlers.js 0.72 kB │ gzip: 0.41 kB
26
- ℹ dist/presentations/dashboard.js 0.71 kB │ gzip: 0.33 kB
27
- ℹ dist/example.js 0.63 kB │ gzip: 0.38 kB
28
- ℹ dist/presentations/index.js 0.52 kB │ gzip: 0.21 kB
29
- ℹ dist/docs/index.js 0.04 kB │ gzip: 0.06 kB
30
- ℹ 21 files, total: 40.39 kB
31
- ✔ Build complete in 42ms
9
+ ℹ dist/index.js 5.02 kB │ gzip: 1.28 kB
10
+ ℹ dist/docs/saas-boilerplate.docblock.js 3.46 kB │ gzip: 1.38 kB
11
+ ℹ dist/project/project.contracts.js 2.78 kB │ gzip: 0.94 kB
12
+ ℹ dist/billing/billing.schema.js 2.73 kB │ gzip: 0.70 kB
13
+ ℹ dist/billing/billing.entity.js 2.57 kB │ gzip: 0.94 kB
14
+ ℹ dist/project/project.schema.js 2.57 kB │ gzip: 0.59 kB
15
+ ℹ dist/shared/mock-data.js 2.06 kB │ gzip: 0.78 kB
16
+ ℹ dist/feature.js 1.95 kB │ gzip: 0.52 kB
17
+ ℹ dist/project/index.js 1.87 kB │ gzip: 0.50 kB
18
+ ℹ dist/billing/index.js 1.87 kB │ gzip: 0.51 kB
19
+ ℹ dist/billing/billing.contracts.js 1.86 kB │ gzip: 0.69 kB
20
+ ℹ dist/project/project.event.js 1.80 kB │ gzip: 0.45 kB
21
+ ℹ dist/billing/billing.event.js 1.65 kB │ gzip: 0.49 kB
22
+ ℹ dist/project/project.entity.js 1.64 kB │ gzip: 0.67 kB
23
+ ℹ dist/settings/settings.entity.js 1.49 kB │ gzip: 0.63 kB
24
+ ℹ dist/project/project.handler.js 1.43 kB │ gzip: 0.65 kB
25
+ ℹ dist/project/project.presentation.js 0.84 kB │ gzip: 0.38 kB
26
+ ℹ dist/billing/billing.presentation.js 0.77 kB │ gzip: 0.32 kB
27
+ ℹ dist/presentations/index.js 0.76 kB │ gzip: 0.25 kB
28
+ ℹ dist/billing/billing.handler.js 0.72 kB │ gzip: 0.42 kB
29
+ ℹ dist/dashboard/dashboard.presentation.js 0.71 kB │ gzip: 0.33 kB
30
+ ℹ dist/handlers/index.js 0.64 kB │ gzip: 0.23 kB
31
+ ℹ dist/example.js 0.63 kB │ gzip: 0.38 kB
32
+ ℹ dist/billing/billing.enum.js 0.29 kB │ gzip: 0.22 kB
33
+ ℹ dist/project/project.enum.js 0.24 kB │ gzip: 0.17 kB
34
+ ℹ dist/settings/settings.enum.js 0.21 kB │ gzip: 0.18 kB
35
+ ℹ dist/settings/index.js 0.21 kB │ gzip: 0.13 kB
36
+ ℹ dist/dashboard/index.js 0.17 kB │ gzip: 0.11 kB
37
+ ℹ dist/docs/index.js 0.04 kB │ gzip: 0.06 kB
38
+ ℹ 29 files, total: 42.99 kB
39
+ ✔ Build complete in 65ms
32
40
  $ tsc --noEmit
package/CHANGELOG.md CHANGED
@@ -1,15 +1,15 @@
1
1
  # @lssm/example.saas-boilerplate
2
2
 
3
- ## 0.0.0-canary-20251215234340
3
+ ## 0.0.0-canary-20251216023757
4
4
 
5
5
  ### Patch Changes
6
6
 
7
7
  - 3086383: refactor: dependencies upgrade
8
8
  - Updated dependencies [3086383]
9
- - @lssm/lib.contracts@0.0.0-canary-20251215234340
10
- - @lssm/lib.schema@0.0.0-canary-20251215234340
11
- - @lssm/lib.jobs@0.0.0-canary-20251215234340
12
- - @lssm/lib.bus@0.0.0-canary-20251215234340
13
- - @lssm/lib.identity-rbac@0.0.0-canary-20251215234340
14
- - @lssm/module.audit-trail@0.0.0-canary-20251215234340
15
- - @lssm/module.notifications@0.0.0-canary-20251215234340
9
+ - @lssm/lib.contracts@0.0.0-canary-20251216023757
10
+ - @lssm/lib.schema@0.0.0-canary-20251216023757
11
+ - @lssm/lib.jobs@0.0.0-canary-20251216023757
12
+ - @lssm/lib.bus@0.0.0-canary-20251216023757
13
+ - @lssm/lib.identity-rbac@0.0.0-canary-20251216023757
14
+ - @lssm/module.audit-trail@0.0.0-canary-20251216023757
15
+ - @lssm/module.notifications@0.0.0-canary-20251216023757
@@ -0,0 +1 @@
1
+ import{CheckFeatureAccessInputModel as e,CheckFeatureAccessOutputModel as t,GetUsageSummaryInputModel as n,GetUsageSummaryOutputModel as r,RecordUsageInputModel as i,RecordUsageOutputModel as a,SubscriptionModel as o,UsageRecordedPayloadModel as s}from"./billing.schema.js";import{defineCommand as c,defineQuery as l}from"@lssm/lib.contracts";const u=[`example.saas-boilerplate`],d=l({meta:{name:`saas.billing.subscription.get`,version:1,stability:`stable`,owners:[...u],tags:[`saas`,`billing`,`subscription`],description:`Get organization subscription status.`,goal:`Show current plan and billing status.`,context:`Billing page, plan upgrade prompts.`},io:{input:null,output:o},policy:{auth:`user`}}),f=c({meta:{name:`saas.billing.usage.record`,version:1,stability:`stable`,owners:[...u],tags:[`saas`,`billing`,`usage`],description:`Record usage of a metered feature.`,goal:`Track feature usage for billing.`,context:`Called by services when metered features are used.`},io:{input:i,output:a},policy:{auth:`user`},sideEffects:{emits:[{name:`billing.usage.recorded`,version:1,when:`Usage is recorded`,payload:s}]}}),p=l({meta:{name:`saas.billing.usage.summary`,version:1,stability:`stable`,owners:[...u],tags:[`saas`,`billing`,`usage`],description:`Get usage summary for the current billing period.`,goal:`Show usage vs limits.`,context:`Billing page, usage dashboards.`},io:{input:n,output:r},policy:{auth:`user`}}),m=l({meta:{name:`saas.billing.feature.check`,version:1,stability:`stable`,owners:[...u],tags:[`saas`,`billing`,`feature`],description:`Check if organization has access to a feature.`,goal:`Gate features based on plan/usage.`,context:`Feature access checks, upgrade prompts.`},io:{input:e,output:t},policy:{auth:`user`}});export{m as CheckFeatureAccessContract,d as GetSubscriptionContract,p as GetUsageSummaryContract,f as RecordUsageContract};
@@ -0,0 +1 @@
1
+ import{defineEnum as e}from"@lssm/lib.schema";const t=e(`SubscriptionStatus`,[`TRIALING`,`ACTIVE`,`PAST_DUE`,`CANCELED`,`PAUSED`]),n=e(`FeatureAccessReason`,[`included`,`limit_available`,`limit_reached`,`not_in_plan`]);export{n as FeatureAccessReasonEnum,t as SubscriptionStatusSchemaEnum};
@@ -0,0 +1 @@
1
+ import{ScalarTypeEnum as e,defineSchemaModel as t}from"@lssm/lib.schema";import{defineEvent as n}from"@lssm/lib.contracts";const r=t({name:`UsageRecordedPayload`,description:`Payload when feature usage is recorded`,fields:{organizationId:{type:e.String_unsecure(),isOptional:!1},feature:{type:e.String_unsecure(),isOptional:!1},quantity:{type:e.Int_unsecure(),isOptional:!1},billingPeriod:{type:e.String_unsecure(),isOptional:!1},recordedAt:{type:e.DateTime(),isOptional:!1}}}),i=t({name:`UsageLimitReachedPayload`,description:`Payload when usage limit is reached`,fields:{organizationId:{type:e.String_unsecure(),isOptional:!1},feature:{type:e.String_unsecure(),isOptional:!1},limit:{type:e.Int_unsecure(),isOptional:!1},currentUsage:{type:e.Int_unsecure(),isOptional:!1},reachedAt:{type:e.DateTime(),isOptional:!1}}}),a=t({name:`SubscriptionChangedPayload`,description:`Payload when subscription status changes`,fields:{organizationId:{type:e.String_unsecure(),isOptional:!1},previousPlan:{type:e.String_unsecure(),isOptional:!0},newPlan:{type:e.String_unsecure(),isOptional:!1},previousStatus:{type:e.String_unsecure(),isOptional:!0},newStatus:{type:e.String_unsecure(),isOptional:!1},changedAt:{type:e.DateTime(),isOptional:!1}}}),o=n({name:`billing.usage.recorded`,version:1,description:`Feature usage has been recorded.`,payload:r}),s=n({name:`billing.limit.reached`,version:1,description:`Usage limit has been reached for a feature.`,payload:i}),c=n({name:`billing.subscription.changed`,version:1,description:`Subscription status has changed.`,payload:a});export{c as SubscriptionChangedEvent,s as UsageLimitReachedEvent,o as UsageRecordedEvent};
@@ -0,0 +1 @@
1
+ import{MOCK_SUBSCRIPTION as e,MOCK_USAGE_SUMMARY as t}from"../shared/mock-data.js";async function n(){return e}async function r(e){return{...t,period:e.period??`current_month`}}async function i(e){return{recorded:!0,newTotal:t.apiCalls.total+e.quantity}}async function a(n){let{feature:r}=n;return{custom_domains:{allowed:!0},api_access:{allowed:!0,currentUsage:t.apiCalls.total,limit:t.apiCalls.limit},advanced_analytics:{allowed:!1,reason:`FEATURE_NOT_INCLUDED`},unlimited_projects:{allowed:!1,reason:`PLAN_LIMIT`,currentUsage:e.usage.projects,limit:e.limits.projects}}[r]??{allowed:!0}}export{a as mockCheckFeatureAccessHandler,n as mockGetSubscriptionHandler,r as mockGetUsageSummaryHandler,i as mockRecordUsageHandler};
@@ -0,0 +1 @@
1
+ import{FeatureAccessReasonEnum as e,SubscriptionStatusSchemaEnum as t}from"./billing.enum.js";import{ScalarTypeEnum as n,defineSchemaModel as r}from"@lssm/lib.schema";const i=r({name:`Subscription`,description:`Organization subscription details`,fields:{id:{type:n.String_unsecure(),isOptional:!1},organizationId:{type:n.String_unsecure(),isOptional:!1},planId:{type:n.String_unsecure(),isOptional:!1},planName:{type:n.String_unsecure(),isOptional:!1},status:{type:t,isOptional:!1},currentPeriodStart:{type:n.DateTime(),isOptional:!1},currentPeriodEnd:{type:n.DateTime(),isOptional:!1},trialEndsAt:{type:n.DateTime(),isOptional:!0},cancelAtPeriodEnd:{type:n.Boolean(),isOptional:!1}}}),a=r({name:`UsageSummary`,description:`Usage summary for a feature`,fields:{feature:{type:n.String_unsecure(),isOptional:!1},used:{type:n.Int_unsecure(),isOptional:!1},limit:{type:n.Int_unsecure(),isOptional:!0},unit:{type:n.String_unsecure(),isOptional:!0},percentage:{type:n.Float_unsecure(),isOptional:!0}}}),o=r({name:`RecordUsageInput`,description:`Input for recording feature usage`,fields:{feature:{type:n.String_unsecure(),isOptional:!1},quantity:{type:n.Int_unsecure(),isOptional:!1},sourceId:{type:n.String_unsecure(),isOptional:!0},sourceType:{type:n.String_unsecure(),isOptional:!0},metadata:{type:n.JSONObject(),isOptional:!0}}}),s=r({name:`RecordUsageOutput`,description:`Output for recording feature usage`,fields:{recorded:{type:n.Boolean(),isOptional:!1},currentUsage:{type:n.Int_unsecure(),isOptional:!1},limit:{type:n.Int_unsecure(),isOptional:!0},limitReached:{type:n.Boolean(),isOptional:!1}}}),c=r({name:`UsageRecordedPayload`,description:`Payload for usage.recorded event`,fields:{feature:{type:n.String_unsecure(),isOptional:!1},quantity:{type:n.Int_unsecure(),isOptional:!1}}}),l=r({name:`GetUsageSummaryInput`,description:`Input for getting usage summary`,fields:{billingPeriod:{type:n.String_unsecure(),isOptional:!0}}}),u=r({name:`GetUsageSummaryOutput`,description:`Output for usage summary`,fields:{billingPeriod:{type:n.String_unsecure(),isOptional:!1},usage:{type:a,isArray:!0,isOptional:!1}}}),d=r({name:`CheckFeatureAccessInput`,description:`Input for checking feature access`,fields:{feature:{type:n.String_unsecure(),isOptional:!1}}}),f=r({name:`CheckFeatureAccessOutput`,description:`Output for feature access check`,fields:{hasAccess:{type:n.Boolean(),isOptional:!1},reason:{type:e,isOptional:!0},upgradeUrl:{type:n.URL(),isOptional:!0}}});export{d as CheckFeatureAccessInputModel,f as CheckFeatureAccessOutputModel,l as GetUsageSummaryInputModel,u as GetUsageSummaryOutputModel,o as RecordUsageInputModel,s as RecordUsageOutputModel,i as SubscriptionModel,c as UsageRecordedPayloadModel,a as UsageSummaryModel};
@@ -0,0 +1 @@
1
+ import{FeatureAccessReasonEnum as e,SubscriptionStatusSchemaEnum as t}from"./billing.enum.js";import{CheckFeatureAccessInputModel as n,CheckFeatureAccessOutputModel as r,GetUsageSummaryInputModel as i,GetUsageSummaryOutputModel as a,RecordUsageInputModel as o,RecordUsageOutputModel as s,SubscriptionModel as c,UsageRecordedPayloadModel as l,UsageSummaryModel as u}from"./billing.schema.js";import{CheckFeatureAccessContract as d,GetSubscriptionContract as f,GetUsageSummaryContract as p,RecordUsageContract as m}from"./billing.contracts.js";import{SubscriptionChangedEvent as h,UsageLimitReachedEvent as g,UsageRecordedEvent as _}from"./billing.event.js";import{BillingUsageEntity as v,SubscriptionEntity as y,SubscriptionStatusEnum as b,UsageLimitEntity as x}from"./billing.entity.js";import{SubscriptionPresentation as S,UsageDashboardPresentation as C}from"./billing.presentation.js";import{mockCheckFeatureAccessHandler as w,mockGetSubscriptionHandler as T,mockGetUsageSummaryHandler as E,mockRecordUsageHandler as D}from"./billing.handler.js";export{v as BillingUsageEntity,d as CheckFeatureAccessContract,n as CheckFeatureAccessInputModel,r as CheckFeatureAccessOutputModel,e as FeatureAccessReasonEnum,f as GetSubscriptionContract,p as GetUsageSummaryContract,i as GetUsageSummaryInputModel,a as GetUsageSummaryOutputModel,m as RecordUsageContract,o as RecordUsageInputModel,s as RecordUsageOutputModel,h as SubscriptionChangedEvent,y as SubscriptionEntity,c as SubscriptionModel,S as SubscriptionPresentation,b as SubscriptionStatusEnum,t as SubscriptionStatusSchemaEnum,C as UsageDashboardPresentation,x as UsageLimitEntity,g as UsageLimitReachedEvent,_ as UsageRecordedEvent,l as UsageRecordedPayloadModel,u as UsageSummaryModel,w as mockCheckFeatureAccessHandler,T as mockGetSubscriptionHandler,E as mockGetUsageSummaryHandler,D as mockRecordUsageHandler};
@@ -0,0 +1 @@
1
+ import{SaasDashboardPresentation as e,SettingsPanelPresentation as t}from"./dashboard.presentation.js";export{e as SaasDashboardPresentation,t as SettingsPanelPresentation};
@@ -1 +1 @@
1
- import{MOCK_PROJECTS as e,MOCK_SUBSCRIPTION as t,MOCK_USAGE_SUMMARY as n}from"./mock-data.js";import{mockCreateProjectHandler as r,mockDeleteProjectHandler as i,mockGetProjectHandler as a,mockListProjectsHandler as o,mockUpdateProjectHandler as s}from"./project.handlers.js";import{mockCheckFeatureAccessHandler as c,mockGetSubscriptionHandler as l,mockGetUsageSummaryHandler as u,mockRecordUsageHandler as d}from"./billing.handlers.js";export{e as MOCK_PROJECTS,t as MOCK_SUBSCRIPTION,n as MOCK_USAGE_SUMMARY,c as mockCheckFeatureAccessHandler,r as mockCreateProjectHandler,i as mockDeleteProjectHandler,a as mockGetProjectHandler,l as mockGetSubscriptionHandler,u as mockGetUsageSummaryHandler,o as mockListProjectsHandler,d as mockRecordUsageHandler,s as mockUpdateProjectHandler};
1
+ import{mockCheckFeatureAccessHandler as e,mockGetSubscriptionHandler as t,mockGetUsageSummaryHandler as n,mockRecordUsageHandler as r}from"../billing/billing.handler.js";import{mockCreateProjectHandler as i,mockDeleteProjectHandler as a,mockGetProjectHandler as o,mockListProjectsHandler as s,mockUpdateProjectHandler as c}from"../project/project.handler.js";export{e as mockCheckFeatureAccessHandler,i as mockCreateProjectHandler,a as mockDeleteProjectHandler,o as mockGetProjectHandler,t as mockGetSubscriptionHandler,n as mockGetUsageSummaryHandler,s as mockListProjectsHandler,r as mockRecordUsageHandler,c as mockUpdateProjectHandler};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{ProjectArchivedEvent as e,ProjectCreatedEvent as t,ProjectDeletedEvent as n,ProjectUpdatedEvent as r,SaasBoilerplateEvents as i,SubscriptionChangedEvent as a,UsageLimitReachedEvent as o,UsageRecordedEvent as s}from"./events.js";import c from"./example.js";import{SaasBoilerplateFeature as l}from"./feature.js";import{ProjectEntity as u,ProjectMemberEntity as d,ProjectStatusEnum as f}from"./entities/project.js";import{FeatureFlagEntity as p,SettingsEntity as m,SettingsScopeEnum as h}from"./entities/settings.js";import{BillingUsageEntity as g,SubscriptionEntity as _,SubscriptionStatusEnum as v,UsageLimitEntity as y}from"./entities/billing.js";import{identityRbacEntities as b,identityRbacSchemaContribution as x,saasBoilerplateEntities as S,saasBoilerplateSchemaContribution as C}from"./entities/index.js";import{CreateProjectContract as w,CreateProjectInputModel as T,DeleteProjectContract as E,DeleteProjectInputModel as D,DeleteProjectOutputModel as O,GetProjectContract as k,GetProjectInputModel as A,ListProjectsContract as j,ListProjectsInputModel as M,ListProjectsOutputModel as N,ProjectDeletedPayloadModel as P,ProjectModel as F,ProjectStatusFilterEnum as I,UpdateProjectContract as L,UpdateProjectInputModel as R}from"./contracts/project.js";import{CheckFeatureAccessContract as z,CheckFeatureAccessInputModel as B,CheckFeatureAccessOutputModel as V,FeatureAccessReasonEnum as ee,GetSubscriptionContract as H,GetUsageSummaryContract as U,GetUsageSummaryInputModel as W,GetUsageSummaryOutputModel as G,RecordUsageContract as K,RecordUsageInputModel as q,RecordUsageOutputModel as J,SubscriptionModel as Y,UsageRecordedPayloadModel as X,UsageSummaryModel as Z}from"./contracts/billing.js";import"./contracts/index.js";import{MOCK_PROJECTS as Q,MOCK_SUBSCRIPTION as $,MOCK_USAGE_SUMMARY as te}from"./handlers/mock-data.js";import{mockCreateProjectHandler as ne,mockDeleteProjectHandler as re,mockGetProjectHandler as ie,mockListProjectsHandler as ae,mockUpdateProjectHandler as oe}from"./handlers/project.handlers.js";import{mockCheckFeatureAccessHandler as se,mockGetSubscriptionHandler as ce,mockGetUsageSummaryHandler as le,mockRecordUsageHandler as ue}from"./handlers/billing.handlers.js";import{ProjectDetailPresentation as de,ProjectListPresentation as fe}from"./presentations/project-list.js";import{SubscriptionPresentation as pe,UsageDashboardPresentation as me}from"./presentations/billing.js";import{SaasDashboardPresentation as he,SettingsPanelPresentation as ge}from"./presentations/dashboard.js";import{SaasBoilerplatePresentations as _e}from"./presentations/index.js";import"./docs/index.js";import{identityRbacSchemaContribution as ve}from"@lssm/lib.identity-rbac";import{jobsSchemaContribution as ye}from"@lssm/lib.jobs";import{auditTrailSchemaContribution as be}from"@lssm/module.audit-trail";import{notificationsSchemaContribution as xe}from"@lssm/module.notifications";const Se={modules:[ve,ye,be,xe,C],provider:`postgresql`,outputPath:`./prisma/schema/generated.prisma`};export{g as BillingUsageEntity,z as CheckFeatureAccessContract,B as CheckFeatureAccessInputModel,V as CheckFeatureAccessOutputModel,w as CreateProjectContract,T as CreateProjectInputModel,E as DeleteProjectContract,D as DeleteProjectInputModel,O as DeleteProjectOutputModel,ee as FeatureAccessReasonEnum,p as FeatureFlagEntity,k as GetProjectContract,A as GetProjectInputModel,H as GetSubscriptionContract,U as GetUsageSummaryContract,W as GetUsageSummaryInputModel,G as GetUsageSummaryOutputModel,j as ListProjectsContract,M as ListProjectsInputModel,N as ListProjectsOutputModel,Q as MOCK_PROJECTS,$ as MOCK_SUBSCRIPTION,te as MOCK_USAGE_SUMMARY,e as ProjectArchivedEvent,t as ProjectCreatedEvent,n as ProjectDeletedEvent,P as ProjectDeletedPayloadModel,de as ProjectDetailPresentation,u as ProjectEntity,fe as ProjectListPresentation,d as ProjectMemberEntity,F as ProjectModel,f as ProjectStatusEnum,I as ProjectStatusFilterEnum,r as ProjectUpdatedEvent,K as RecordUsageContract,q as RecordUsageInputModel,J as RecordUsageOutputModel,i as SaasBoilerplateEvents,l as SaasBoilerplateFeature,_e as SaasBoilerplatePresentations,he as SaasDashboardPresentation,m as SettingsEntity,ge as SettingsPanelPresentation,h as SettingsScopeEnum,a as SubscriptionChangedEvent,_ as SubscriptionEntity,Y as SubscriptionModel,pe as SubscriptionPresentation,v as SubscriptionStatusEnum,L as UpdateProjectContract,R as UpdateProjectInputModel,me as UsageDashboardPresentation,y as UsageLimitEntity,o as UsageLimitReachedEvent,s as UsageRecordedEvent,X as UsageRecordedPayloadModel,Z as UsageSummaryModel,c as example,b as identityRbacEntities,x as identityRbacSchemaContribution,se as mockCheckFeatureAccessHandler,ne as mockCreateProjectHandler,re as mockDeleteProjectHandler,ie as mockGetProjectHandler,ce as mockGetSubscriptionHandler,le as mockGetUsageSummaryHandler,ae as mockListProjectsHandler,ue as mockRecordUsageHandler,oe as mockUpdateProjectHandler,S as saasBoilerplateEntities,C as saasBoilerplateSchemaContribution,Se as schemaComposition};
1
+ import e from"./example.js";import{SaasBoilerplateFeature as t}from"./feature.js";import{FeatureAccessReasonEnum as n,SubscriptionStatusSchemaEnum as r}from"./billing/billing.enum.js";import{CheckFeatureAccessInputModel as i,CheckFeatureAccessOutputModel as a,GetUsageSummaryInputModel as o,GetUsageSummaryOutputModel as s,RecordUsageInputModel as c,RecordUsageOutputModel as ee,SubscriptionModel as te,UsageRecordedPayloadModel as ne,UsageSummaryModel as re}from"./billing/billing.schema.js";import{CheckFeatureAccessContract as l,GetSubscriptionContract as u,GetUsageSummaryContract as d,RecordUsageContract as f}from"./billing/billing.contracts.js";import{SubscriptionChangedEvent as p,UsageLimitReachedEvent as m,UsageRecordedEvent as h}from"./billing/billing.event.js";import{BillingUsageEntity as g,SubscriptionEntity as _,SubscriptionStatusEnum as v,UsageLimitEntity as y}from"./billing/billing.entity.js";import{SubscriptionPresentation as b,UsageDashboardPresentation as x}from"./billing/billing.presentation.js";import{mockCheckFeatureAccessHandler as S,mockGetSubscriptionHandler as C,mockGetUsageSummaryHandler as w,mockRecordUsageHandler as T}from"./billing/billing.handler.js";import"./billing/index.js";import{ProjectStatusFilterEnum as E,ProjectStatusSchemaEnum as D}from"./project/project.enum.js";import{CreateProjectInputModel as O,DeleteProjectInputModel as k,DeleteProjectOutputModel as A,GetProjectInputModel as j,ListProjectsInputModel as M,ListProjectsOutputModel as N,ProjectDeletedPayloadModel as P,ProjectModel as F,UpdateProjectInputModel as ie}from"./project/project.schema.js";import{CreateProjectContract as I,DeleteProjectContract as L,GetProjectContract as R,ListProjectsContract as z,UpdateProjectContract as B}from"./project/project.contracts.js";import{ProjectArchivedEvent as V,ProjectCreatedEvent as H,ProjectDeletedEvent as U,ProjectUpdatedEvent as W}from"./project/project.event.js";import{ProjectEntity as G,ProjectMemberEntity as K,ProjectStatusEnum as q}from"./project/project.entity.js";import{ProjectDetailPresentation as J,ProjectListPresentation as Y}from"./project/project.presentation.js";import{mockCreateProjectHandler as ae,mockDeleteProjectHandler as oe,mockGetProjectHandler as se,mockListProjectsHandler as ce,mockUpdateProjectHandler as le}from"./project/project.handler.js";import"./project/index.js";import{SettingsScopeEnum as X}from"./settings/settings.enum.js";import{FeatureFlagEntity as Z,SettingsEntity as Q}from"./settings/settings.entity.js";import"./settings/index.js";import{SaasDashboardPresentation as ue,SettingsPanelPresentation as de}from"./dashboard/dashboard.presentation.js";import"./docs/index.js";import{identityRbacSchemaContribution as fe}from"@lssm/lib.identity-rbac";import{jobsSchemaContribution as pe}from"@lssm/lib.jobs";import{auditTrailSchemaContribution as me}from"@lssm/module.audit-trail";import{notificationsSchemaContribution as he}from"@lssm/module.notifications";const $={moduleId:`@lssm/example.saas-boilerplate`,entities:[G,K,Q,Z,_,g,y],enums:[q,X,v]},ge={modules:[fe,pe,me,he,$],provider:`postgresql`,outputPath:`./prisma/schema/generated.prisma`};export{g as BillingUsageEntity,l as CheckFeatureAccessContract,i as CheckFeatureAccessInputModel,a as CheckFeatureAccessOutputModel,I as CreateProjectContract,O as CreateProjectInputModel,L as DeleteProjectContract,k as DeleteProjectInputModel,A as DeleteProjectOutputModel,n as FeatureAccessReasonEnum,Z as FeatureFlagEntity,R as GetProjectContract,j as GetProjectInputModel,u as GetSubscriptionContract,d as GetUsageSummaryContract,o as GetUsageSummaryInputModel,s as GetUsageSummaryOutputModel,z as ListProjectsContract,M as ListProjectsInputModel,N as ListProjectsOutputModel,V as ProjectArchivedEvent,H as ProjectCreatedEvent,U as ProjectDeletedEvent,P as ProjectDeletedPayloadModel,J as ProjectDetailPresentation,G as ProjectEntity,Y as ProjectListPresentation,K as ProjectMemberEntity,F as ProjectModel,q as ProjectStatusEnum,E as ProjectStatusFilterEnum,D as ProjectStatusSchemaEnum,W as ProjectUpdatedEvent,f as RecordUsageContract,c as RecordUsageInputModel,ee as RecordUsageOutputModel,t as SaasBoilerplateFeature,ue as SaasDashboardPresentation,Q as SettingsEntity,de as SettingsPanelPresentation,X as SettingsScopeEnum,p as SubscriptionChangedEvent,_ as SubscriptionEntity,te as SubscriptionModel,b as SubscriptionPresentation,v as SubscriptionStatusEnum,r as SubscriptionStatusSchemaEnum,B as UpdateProjectContract,ie as UpdateProjectInputModel,x as UsageDashboardPresentation,y as UsageLimitEntity,m as UsageLimitReachedEvent,h as UsageRecordedEvent,ne as UsageRecordedPayloadModel,re as UsageSummaryModel,e as example,S as mockCheckFeatureAccessHandler,ae as mockCreateProjectHandler,oe as mockDeleteProjectHandler,se as mockGetProjectHandler,C as mockGetSubscriptionHandler,w as mockGetUsageSummaryHandler,ce as mockListProjectsHandler,T as mockRecordUsageHandler,le as mockUpdateProjectHandler,$ as saasBoilerplateSchemaContribution,ge as schemaComposition};
@@ -1 +1 @@
1
- import{ProjectDetailPresentation as e,ProjectListPresentation as t}from"./project-list.js";import{SubscriptionPresentation as n,UsageDashboardPresentation as r}from"./billing.js";import{SaasDashboardPresentation as i,SettingsPanelPresentation as a}from"./dashboard.js";const o=[i,t,e,n,r,a];export{e as ProjectDetailPresentation,t as ProjectListPresentation,o as SaasBoilerplatePresentations,i as SaasDashboardPresentation,a as SettingsPanelPresentation,n as SubscriptionPresentation,r as UsageDashboardPresentation};
1
+ import{SubscriptionPresentation as e,UsageDashboardPresentation as t}from"../billing/billing.presentation.js";import{ProjectDetailPresentation as n,ProjectListPresentation as r}from"../project/project.presentation.js";import{SaasDashboardPresentation as i,SettingsPanelPresentation as a}from"../dashboard/dashboard.presentation.js";const o={SubscriptionPresentation:void 0,UsageDashboardPresentation:void 0,ProjectListPresentation:void 0,ProjectDetailPresentation:void 0,SaasDashboardPresentation:void 0,SettingsPanelPresentation:void 0};export{n as ProjectDetailPresentation,r as ProjectListPresentation,o as SaasBoilerplatePresentations,i as SaasDashboardPresentation,a as SettingsPanelPresentation,e as SubscriptionPresentation,t as UsageDashboardPresentation};
@@ -0,0 +1 @@
1
+ import{ProjectStatusFilterEnum as e,ProjectStatusSchemaEnum as t}from"./project.enum.js";import{CreateProjectInputModel as n,DeleteProjectInputModel as r,DeleteProjectOutputModel as i,GetProjectInputModel as a,ListProjectsInputModel as o,ListProjectsOutputModel as s,ProjectDeletedPayloadModel as c,ProjectModel as l,UpdateProjectInputModel as u}from"./project.schema.js";import{CreateProjectContract as d,DeleteProjectContract as f,GetProjectContract as p,ListProjectsContract as m,UpdateProjectContract as h}from"./project.contracts.js";import{ProjectArchivedEvent as g,ProjectCreatedEvent as _,ProjectDeletedEvent as v,ProjectUpdatedEvent as y}from"./project.event.js";import{ProjectEntity as b,ProjectMemberEntity as x,ProjectStatusEnum as S}from"./project.entity.js";import{ProjectDetailPresentation as C,ProjectListPresentation as w}from"./project.presentation.js";import{mockCreateProjectHandler as T,mockDeleteProjectHandler as E,mockGetProjectHandler as D,mockListProjectsHandler as O,mockUpdateProjectHandler as k}from"./project.handler.js";export{d as CreateProjectContract,n as CreateProjectInputModel,f as DeleteProjectContract,r as DeleteProjectInputModel,i as DeleteProjectOutputModel,p as GetProjectContract,a as GetProjectInputModel,m as ListProjectsContract,o as ListProjectsInputModel,s as ListProjectsOutputModel,g as ProjectArchivedEvent,_ as ProjectCreatedEvent,v as ProjectDeletedEvent,c as ProjectDeletedPayloadModel,C as ProjectDetailPresentation,b as ProjectEntity,w as ProjectListPresentation,x as ProjectMemberEntity,l as ProjectModel,S as ProjectStatusEnum,e as ProjectStatusFilterEnum,t as ProjectStatusSchemaEnum,y as ProjectUpdatedEvent,h as UpdateProjectContract,u as UpdateProjectInputModel,T as mockCreateProjectHandler,E as mockDeleteProjectHandler,D as mockGetProjectHandler,O as mockListProjectsHandler,k as mockUpdateProjectHandler};
@@ -0,0 +1 @@
1
+ import{CreateProjectInputModel as e,DeleteProjectInputModel as t,DeleteProjectOutputModel as n,GetProjectInputModel as r,ListProjectsInputModel as i,ListProjectsOutputModel as a,ProjectDeletedPayloadModel as o,ProjectModel as s,UpdateProjectInputModel as c}from"./project.schema.js";import{defineCommand as l,defineQuery as u}from"@lssm/lib.contracts/spec";const d=[`example.saas-boilerplate`],f=l({meta:{name:`saas.project.create`,version:1,stability:`stable`,owners:[...d],tags:[`saas`,`project`,`create`],description:`Create a new project in the organization.`,goal:`Allow users to create projects for organizing work.`,context:`Called from project creation UI or API.`},io:{input:e,output:s,errors:{SLUG_EXISTS:{description:`A project with this slug already exists`,http:409,gqlCode:`SLUG_EXISTS`,when:`Slug is already taken in the organization`},LIMIT_REACHED:{description:`Project limit reached for this plan`,http:403,gqlCode:`LIMIT_REACHED`,when:`Organization has reached project limit`}}},policy:{auth:`user`},sideEffects:{emits:[{name:`project.created`,version:1,when:`Project is created`,payload:s}],audit:[`project.created`]}}),p=u({meta:{name:`saas.project.get`,version:1,stability:`stable`,owners:[...d],tags:[`saas`,`project`,`get`],description:`Get a project by ID.`,goal:`Retrieve project details.`,context:`Project detail page, API calls.`},io:{input:r,output:s,errors:{NOT_FOUND:{description:`Project not found`,http:404,gqlCode:`NOT_FOUND`,when:`Project ID is invalid or user lacks access`}}},policy:{auth:`user`}}),m=l({meta:{name:`saas.project.update`,version:1,stability:`stable`,owners:[...d],tags:[`saas`,`project`,`update`],description:`Update project details.`,goal:`Allow project owners/editors to modify project.`,context:`Project settings page.`},io:{input:c,output:s},policy:{auth:`user`},sideEffects:{emits:[{name:`project.updated`,version:1,when:`Project is updated`,payload:s}],audit:[`project.updated`]}}),h=l({meta:{name:`saas.project.delete`,version:1,stability:`stable`,owners:[...d],tags:[`saas`,`project`,`delete`],description:`Delete a project (soft delete).`,goal:`Allow project owners to remove projects.`,context:`Project settings page.`},io:{input:t,output:n},policy:{auth:`user`},sideEffects:{emits:[{name:`project.deleted`,version:1,when:`Project is deleted`,payload:o}],audit:[`project.deleted`]}}),g=u({meta:{name:`saas.project.list`,version:1,stability:`stable`,owners:[...d],tags:[`saas`,`project`,`list`],description:`List projects in the organization.`,goal:`Show all projects user has access to.`,context:`Project list page, dashboard.`},io:{input:i,output:a},policy:{auth:`user`}});export{f as CreateProjectContract,h as DeleteProjectContract,p as GetProjectContract,g as ListProjectsContract,m as UpdateProjectContract};
@@ -0,0 +1 @@
1
+ import{defineEnum as e}from"@lssm/lib.schema";const t=e(`ProjectStatus`,[`DRAFT`,`ACTIVE`,`ARCHIVED`,`DELETED`]),n=e(`ProjectStatusFilter`,[`DRAFT`,`ACTIVE`,`ARCHIVED`,`all`]);export{n as ProjectStatusFilterEnum,t as ProjectStatusSchemaEnum};
@@ -0,0 +1 @@
1
+ import{ScalarTypeEnum as e,defineSchemaModel as t}from"@lssm/lib.schema";import{defineEvent as n}from"@lssm/lib.contracts";const r=t({name:`ProjectCreatedPayload`,description:`Payload when a project is created`,fields:{projectId:{type:e.String_unsecure(),isOptional:!1},name:{type:e.String_unsecure(),isOptional:!1},organizationId:{type:e.String_unsecure(),isOptional:!1},createdBy:{type:e.String_unsecure(),isOptional:!1},createdAt:{type:e.DateTime(),isOptional:!1}}}),i=t({name:`ProjectUpdatedPayload`,description:`Payload when a project is updated`,fields:{projectId:{type:e.String_unsecure(),isOptional:!1},updatedFields:{type:e.String_unsecure(),isArray:!0,isOptional:!1},updatedBy:{type:e.String_unsecure(),isOptional:!1},updatedAt:{type:e.DateTime(),isOptional:!1}}}),a=t({name:`ProjectDeletedPayload`,description:`Payload when a project is deleted`,fields:{projectId:{type:e.String_unsecure(),isOptional:!1},organizationId:{type:e.String_unsecure(),isOptional:!1},deletedBy:{type:e.String_unsecure(),isOptional:!1},deletedAt:{type:e.DateTime(),isOptional:!1}}}),o=t({name:`ProjectArchivedPayload`,description:`Payload when a project is archived`,fields:{projectId:{type:e.String_unsecure(),isOptional:!1},archivedBy:{type:e.String_unsecure(),isOptional:!1},archivedAt:{type:e.DateTime(),isOptional:!1}}}),s=n({name:`project.created`,version:1,description:`A new project has been created.`,payload:r}),c=n({name:`project.updated`,version:1,description:`A project has been updated.`,payload:i}),l=n({name:`project.deleted`,version:1,description:`A project has been deleted.`,payload:a}),u=n({name:`project.archived`,version:1,description:`A project has been archived.`,payload:o});export{u as ProjectArchivedEvent,s as ProjectCreatedEvent,l as ProjectDeletedEvent,c as ProjectUpdatedEvent};
@@ -0,0 +1 @@
1
+ import{MOCK_PROJECTS as e}from"../shared/mock-data.js";async function t(t){let{status:n,search:r,limit:i=20,offset:a=0}=t,o=[...e];if(n&&n!==`all`&&(o=o.filter(e=>e.status===n)),r){let e=r.toLowerCase();o=o.filter(t=>t.name.toLowerCase().includes(e)||t.description?.toLowerCase().includes(e)||t.tags.some(t=>t.toLowerCase().includes(e)))}o.sort((e,t)=>t.updatedAt.getTime()-e.updatedAt.getTime());let s=o.length;return{projects:o.slice(a,a+i),total:s}}async function n(t){let n=e.find(e=>e.id===t.projectId);if(!n)throw Error(`NOT_FOUND`);return n}async function r(t,n){if(t.slug&&e.some(e=>e.slug===t.slug))throw Error(`SLUG_EXISTS`);let r=new Date;return{id:`proj-${Date.now()}`,name:t.name,description:t.description,slug:t.slug??t.name.toLowerCase().replace(/\s+/g,`-`),organizationId:n.organizationId,createdBy:n.userId,status:`DRAFT`,isPublic:t.isPublic??!1,tags:t.tags??[],createdAt:r,updatedAt:r}}async function i(t){let n=e.find(e=>e.id===t.projectId);if(!n)throw Error(`NOT_FOUND`);return{...n,name:t.name??n.name,description:t.description??n.description,slug:t.slug??n.slug,isPublic:t.isPublic??n.isPublic,tags:t.tags??n.tags,status:t.status??n.status,updatedAt:new Date}}async function a(t){if(!e.find(e=>e.id===t.projectId))throw Error(`NOT_FOUND`);return{success:!0}}export{r as mockCreateProjectHandler,a as mockDeleteProjectHandler,n as mockGetProjectHandler,t as mockListProjectsHandler,i as mockUpdateProjectHandler};
@@ -0,0 +1 @@
1
+ import{ProjectModel as e}from"./project.schema.js";const t={meta:{name:`saas.project.list`,version:1,description:`List view of projects with status, tags, and last updated info`,domain:`saas-boilerplate`,owners:[`saas-team`],tags:[`project`,`list`,`dashboard`]},source:{type:`component`,framework:`react`,componentKey:`ProjectListView`,props:e},targets:[`react`,`markdown`,`application/json`],policy:{flags:[`saas.projects.enabled`]}},n={meta:{name:`saas.project.detail`,version:1,description:`Detailed view of a project with settings and activity`,domain:`saas-boilerplate`,owners:[`saas-team`],tags:[`project`,`detail`]},source:{type:`component`,framework:`react`,componentKey:`ProjectDetailView`},targets:[`react`,`markdown`],policy:{flags:[`saas.projects.enabled`]}};export{n as ProjectDetailPresentation,t as ProjectListPresentation};
@@ -0,0 +1 @@
1
+ import{ProjectStatusFilterEnum as e,ProjectStatusSchemaEnum as t}from"./project.enum.js";import{ScalarTypeEnum as n,defineSchemaModel as r}from"@lssm/lib.schema";const i=r({name:`Project`,description:`A project within an organization`,fields:{id:{type:n.String_unsecure(),isOptional:!1},name:{type:n.String_unsecure(),isOptional:!1},description:{type:n.String_unsecure(),isOptional:!0},slug:{type:n.String_unsecure(),isOptional:!0},organizationId:{type:n.String_unsecure(),isOptional:!1},createdBy:{type:n.String_unsecure(),isOptional:!1},status:{type:t,isOptional:!1},isPublic:{type:n.Boolean(),isOptional:!1},tags:{type:n.String_unsecure(),isArray:!0,isOptional:!1},createdAt:{type:n.DateTime(),isOptional:!1},updatedAt:{type:n.DateTime(),isOptional:!1}}}),a=r({name:`CreateProjectInput`,description:`Input for creating a project`,fields:{name:{type:n.NonEmptyString(),isOptional:!1},description:{type:n.String_unsecure(),isOptional:!0},slug:{type:n.String_unsecure(),isOptional:!0},isPublic:{type:n.Boolean(),isOptional:!0},tags:{type:n.String_unsecure(),isArray:!0,isOptional:!0}}}),o=r({name:`UpdateProjectInput`,description:`Input for updating a project`,fields:{projectId:{type:n.String_unsecure(),isOptional:!1},name:{type:n.String_unsecure(),isOptional:!0},description:{type:n.String_unsecure(),isOptional:!0},slug:{type:n.String_unsecure(),isOptional:!0},isPublic:{type:n.Boolean(),isOptional:!0},tags:{type:n.String_unsecure(),isArray:!0,isOptional:!0},status:{type:t,isOptional:!0}}}),s=r({name:`GetProjectInput`,fields:{projectId:{type:n.String_unsecure(),isOptional:!1}}}),c=r({name:`DeleteProjectInput`,fields:{projectId:{type:n.String_unsecure(),isOptional:!1}}}),l=r({name:`DeleteProjectOutput`,fields:{success:{type:n.Boolean(),isOptional:!1}}}),u=r({name:`ProjectDeletedPayload`,fields:{projectId:{type:n.String_unsecure(),isOptional:!1}}}),d=r({name:`ListProjectsInput`,description:`Input for listing projects`,fields:{status:{type:e,isOptional:!0},search:{type:n.String_unsecure(),isOptional:!0},limit:{type:n.Int_unsecure(),isOptional:!0,defaultValue:20},offset:{type:n.Int_unsecure(),isOptional:!0,defaultValue:0}}}),f=r({name:`ListProjectsOutput`,description:`Output for listing projects`,fields:{projects:{type:i,isArray:!0,isOptional:!1},total:{type:n.Int_unsecure(),isOptional:!1}}});export{a as CreateProjectInputModel,c as DeleteProjectInputModel,l as DeleteProjectOutputModel,s as GetProjectInputModel,d as ListProjectsInputModel,f as ListProjectsOutputModel,u as ProjectDeletedPayloadModel,i as ProjectModel,o as UpdateProjectInputModel};
@@ -0,0 +1 @@
1
+ import{SettingsScopeEnum as e}from"./settings.enum.js";import{FeatureFlagEntity as t,SettingsEntity as n}from"./settings.entity.js";export{t as FeatureFlagEntity,n as SettingsEntity,e as SettingsScopeEnum};
@@ -0,0 +1 @@
1
+ import{SettingsScopeEnum as e}from"./settings.enum.js";import{defineEntity as t,field as n,index as r}from"@lssm/lib.schema";const i=t({name:`Settings`,description:`Application, organization, or user settings.`,schema:`saas_app`,map:`settings`,fields:{id:n.id(),key:n.string({description:`Setting key (e.g., "theme", "notifications.email")`}),scope:n.enum(`SettingsScope`),scopeId:n.string({isOptional:!0,description:`ID of scoped entity (org, user, project)`}),value:n.json({description:`Setting value`}),valueType:n.string({default:`"string"`,description:`Type hint for value`}),schema:n.json({isOptional:!0,description:`JSON schema for validation`}),description:n.string({isOptional:!0}),isSecret:n.boolean({default:!1,description:`Whether value should be encrypted`}),createdAt:n.createdAt(),updatedAt:n.updatedAt()},indexes:[r.unique([`scope`,`scopeId`,`key`]),r.on([`scope`,`key`])],enums:[e]}),a=t({name:`FeatureFlag`,description:`Feature flags for progressive rollout.`,schema:`saas_app`,map:`feature_flag`,fields:{id:n.id(),key:n.string({isUnique:!0,description:`Feature flag key`}),name:n.string({description:`Human-readable name`}),description:n.string({isOptional:!0}),enabled:n.boolean({default:!1}),defaultValue:n.boolean({default:!1}),rules:n.json({isOptional:!0,description:`Targeting rules`}),rolloutPercentage:n.int({default:0,description:`Percentage rollout (0-100)`}),createdAt:n.createdAt(),updatedAt:n.updatedAt()}});export{a as FeatureFlagEntity,i as SettingsEntity};
@@ -0,0 +1 @@
1
+ import{defineEntityEnum as e}from"@lssm/lib.schema";const t=e({name:`SettingsScope`,values:[`APP`,`ORG`,`USER`,`PROJECT`],schema:`saas_app`,description:`Scope of a setting.`});export{t as SettingsScopeEnum};
package/package.json CHANGED
@@ -1,32 +1,40 @@
1
1
  {
2
2
  "name": "@lssm/example.saas-boilerplate",
3
- "version": "0.0.0-canary-20251215234340",
3
+ "version": "0.0.0-canary-20251216023757",
4
4
  "description": "SaaS Boilerplate - Users, Orgs, Projects, Billing, Settings",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
8
  "exports": {
9
9
  ".": "./src/index.ts",
10
- "./contracts": "./src/contracts/index.ts",
11
- "./contracts/billing": "./src/contracts/billing.ts",
12
- "./contracts/project": "./src/contracts/project.ts",
10
+ "./billing": "./src/billing/index.ts",
11
+ "./billing/billing.contracts": "./src/billing/billing.contracts.ts",
12
+ "./billing/billing.entity": "./src/billing/billing.entity.ts",
13
+ "./billing/billing.enum": "./src/billing/billing.enum.ts",
14
+ "./billing/billing.event": "./src/billing/billing.event.ts",
15
+ "./billing/billing.handler": "./src/billing/billing.handler.ts",
16
+ "./billing/billing.presentation": "./src/billing/billing.presentation.ts",
17
+ "./billing/billing.schema": "./src/billing/billing.schema.ts",
18
+ "./dashboard": "./src/dashboard/index.ts",
19
+ "./dashboard/dashboard.presentation": "./src/dashboard/dashboard.presentation.ts",
13
20
  "./docs": "./src/docs/index.ts",
14
21
  "./docs/saas-boilerplate.docblock": "./src/docs/saas-boilerplate.docblock.ts",
15
- "./entities": "./src/entities/index.ts",
16
- "./entities/billing": "./src/entities/billing.ts",
17
- "./entities/project": "./src/entities/project.ts",
18
- "./entities/settings": "./src/entities/settings.ts",
19
- "./events": "./src/events.ts",
20
22
  "./example": "./src/example.ts",
21
23
  "./feature": "./src/feature.ts",
22
24
  "./handlers": "./src/handlers/index.ts",
23
- "./handlers/billing.handlers": "./src/handlers/billing.handlers.ts",
24
- "./handlers/mock-data": "./src/handlers/mock-data.ts",
25
- "./handlers/project.handlers": "./src/handlers/project.handlers.ts",
26
25
  "./presentations": "./src/presentations/index.ts",
27
- "./presentations/billing": "./src/presentations/billing.ts",
28
- "./presentations/dashboard": "./src/presentations/dashboard.ts",
29
- "./presentations/project-list": "./src/presentations/project-list.ts",
26
+ "./project": "./src/project/index.ts",
27
+ "./project/project.contracts": "./src/project/project.contracts.ts",
28
+ "./project/project.entity": "./src/project/project.entity.ts",
29
+ "./project/project.enum": "./src/project/project.enum.ts",
30
+ "./project/project.event": "./src/project/project.event.ts",
31
+ "./project/project.handler": "./src/project/project.handler.ts",
32
+ "./project/project.presentation": "./src/project/project.presentation.ts",
33
+ "./project/project.schema": "./src/project/project.schema.ts",
34
+ "./settings": "./src/settings/index.ts",
35
+ "./settings/settings.entity": "./src/settings/settings.entity.ts",
36
+ "./settings/settings.enum": "./src/settings/settings.enum.ts",
37
+ "./shared/mock-data": "./src/shared/mock-data.ts",
30
38
  "./*": "./*"
31
39
  },
32
40
  "scripts": {
@@ -60,26 +68,34 @@
60
68
  "publishConfig": {
61
69
  "exports": {
62
70
  ".": "./dist/index.js",
63
- "./contracts": "./dist/contracts/index.js",
64
- "./contracts/billing": "./dist/contracts/billing.js",
65
- "./contracts/project": "./dist/contracts/project.js",
71
+ "./billing": "./dist/billing/index.js",
72
+ "./billing/billing.contracts": "./dist/billing/billing.contracts.js",
73
+ "./billing/billing.entity": "./dist/billing/billing.entity.js",
74
+ "./billing/billing.enum": "./dist/billing/billing.enum.js",
75
+ "./billing/billing.event": "./dist/billing/billing.event.js",
76
+ "./billing/billing.handler": "./dist/billing/billing.handler.js",
77
+ "./billing/billing.presentation": "./dist/billing/billing.presentation.js",
78
+ "./billing/billing.schema": "./dist/billing/billing.schema.js",
79
+ "./dashboard": "./dist/dashboard/index.js",
80
+ "./dashboard/dashboard.presentation": "./dist/dashboard/dashboard.presentation.js",
66
81
  "./docs": "./dist/docs/index.js",
67
82
  "./docs/saas-boilerplate.docblock": "./dist/docs/saas-boilerplate.docblock.js",
68
- "./entities": "./dist/entities/index.js",
69
- "./entities/billing": "./dist/entities/billing.js",
70
- "./entities/project": "./dist/entities/project.js",
71
- "./entities/settings": "./dist/entities/settings.js",
72
- "./events": "./dist/events.js",
73
83
  "./example": "./dist/example.js",
74
84
  "./feature": "./dist/feature.js",
75
85
  "./handlers": "./dist/handlers/index.js",
76
- "./handlers/billing.handlers": "./dist/handlers/billing.handlers.js",
77
- "./handlers/mock-data": "./dist/handlers/mock-data.js",
78
- "./handlers/project.handlers": "./dist/handlers/project.handlers.js",
79
86
  "./presentations": "./dist/presentations/index.js",
80
- "./presentations/billing": "./dist/presentations/billing.js",
81
- "./presentations/dashboard": "./dist/presentations/dashboard.js",
82
- "./presentations/project-list": "./dist/presentations/project-list.js",
87
+ "./project": "./dist/project/index.js",
88
+ "./project/project.contracts": "./dist/project/project.contracts.js",
89
+ "./project/project.entity": "./dist/project/project.entity.js",
90
+ "./project/project.enum": "./dist/project/project.enum.js",
91
+ "./project/project.event": "./dist/project/project.event.js",
92
+ "./project/project.handler": "./dist/project/project.handler.js",
93
+ "./project/project.presentation": "./dist/project/project.presentation.js",
94
+ "./project/project.schema": "./dist/project/project.schema.js",
95
+ "./settings": "./dist/settings/index.js",
96
+ "./settings/settings.entity": "./dist/settings/settings.entity.js",
97
+ "./settings/settings.enum": "./dist/settings/settings.enum.js",
98
+ "./shared/mock-data": "./dist/shared/mock-data.js",
83
99
  "./*": "./*"
84
100
  }
85
101
  }
@@ -0,0 +1,116 @@
1
+ import { defineCommand, defineQuery } from '@lssm/lib.contracts';
2
+ import {
3
+ SubscriptionModel,
4
+ RecordUsageInputModel,
5
+ RecordUsageOutputModel,
6
+ UsageRecordedPayloadModel,
7
+ GetUsageSummaryInputModel,
8
+ GetUsageSummaryOutputModel,
9
+ CheckFeatureAccessInputModel,
10
+ CheckFeatureAccessOutputModel,
11
+ } from './billing.schema';
12
+
13
+ const OWNERS = ['example.saas-boilerplate'] as const;
14
+
15
+ /**
16
+ * Get subscription status.
17
+ */
18
+ export const GetSubscriptionContract = defineQuery({
19
+ meta: {
20
+ name: 'saas.billing.subscription.get',
21
+ version: 1,
22
+ stability: 'stable',
23
+ owners: [...OWNERS],
24
+ tags: ['saas', 'billing', 'subscription'],
25
+ description: 'Get organization subscription status.',
26
+ goal: 'Show current plan and billing status.',
27
+ context: 'Billing page, plan upgrade prompts.',
28
+ },
29
+ io: {
30
+ input: null,
31
+ output: SubscriptionModel,
32
+ },
33
+ policy: {
34
+ auth: 'user',
35
+ },
36
+ });
37
+
38
+ /**
39
+ * Record feature usage.
40
+ */
41
+ export const RecordUsageContract = defineCommand({
42
+ meta: {
43
+ name: 'saas.billing.usage.record',
44
+ version: 1,
45
+ stability: 'stable',
46
+ owners: [...OWNERS],
47
+ tags: ['saas', 'billing', 'usage'],
48
+ description: 'Record usage of a metered feature.',
49
+ goal: 'Track feature usage for billing.',
50
+ context: 'Called by services when metered features are used.',
51
+ },
52
+ io: {
53
+ input: RecordUsageInputModel,
54
+ output: RecordUsageOutputModel,
55
+ },
56
+ policy: {
57
+ auth: 'user',
58
+ },
59
+ sideEffects: {
60
+ emits: [
61
+ {
62
+ name: 'billing.usage.recorded',
63
+ version: 1,
64
+ when: 'Usage is recorded',
65
+ payload: UsageRecordedPayloadModel,
66
+ },
67
+ ],
68
+ },
69
+ });
70
+
71
+ /**
72
+ * Get usage summary.
73
+ */
74
+ export const GetUsageSummaryContract = defineQuery({
75
+ meta: {
76
+ name: 'saas.billing.usage.summary',
77
+ version: 1,
78
+ stability: 'stable',
79
+ owners: [...OWNERS],
80
+ tags: ['saas', 'billing', 'usage'],
81
+ description: 'Get usage summary for the current billing period.',
82
+ goal: 'Show usage vs limits.',
83
+ context: 'Billing page, usage dashboards.',
84
+ },
85
+ io: {
86
+ input: GetUsageSummaryInputModel,
87
+ output: GetUsageSummaryOutputModel,
88
+ },
89
+ policy: {
90
+ auth: 'user',
91
+ },
92
+ });
93
+
94
+ /**
95
+ * Check feature access.
96
+ */
97
+ export const CheckFeatureAccessContract = defineQuery({
98
+ meta: {
99
+ name: 'saas.billing.feature.check',
100
+ version: 1,
101
+ stability: 'stable',
102
+ owners: [...OWNERS],
103
+ tags: ['saas', 'billing', 'feature'],
104
+ description: 'Check if organization has access to a feature.',
105
+ goal: 'Gate features based on plan/usage.',
106
+ context: 'Feature access checks, upgrade prompts.',
107
+ },
108
+ io: {
109
+ input: CheckFeatureAccessInputModel,
110
+ output: CheckFeatureAccessOutputModel,
111
+ },
112
+ policy: {
113
+ auth: 'user',
114
+ },
115
+ });
116
+
@@ -1,7 +1,7 @@
1
1
  import { defineEntity, defineEntityEnum, field, index } from '@lssm/lib.schema';
2
2
 
3
3
  /**
4
- * Subscription status enum.
4
+ * Subscription status enum for entities.
5
5
  */
6
6
  export const SubscriptionStatusEnum = defineEntityEnum({
7
7
  name: 'SubscriptionStatus',
@@ -151,3 +151,4 @@ export const UsageLimitEntity = defineEntity({
151
151
  },
152
152
  indexes: [index.unique(['planId', 'feature'])],
153
153
  });
154
+
@@ -0,0 +1,24 @@
1
+ import { defineEnum } from '@lssm/lib.schema';
2
+
3
+ /**
4
+ * Subscription status enum for contract schemas.
5
+ * Note: Entity enum is defined separately in billing.entity.ts
6
+ */
7
+ export const SubscriptionStatusSchemaEnum = defineEnum('SubscriptionStatus', [
8
+ 'TRIALING',
9
+ 'ACTIVE',
10
+ 'PAST_DUE',
11
+ 'CANCELED',
12
+ 'PAUSED',
13
+ ]);
14
+
15
+ /**
16
+ * Feature access reason enum.
17
+ */
18
+ export const FeatureAccessReasonEnum = defineEnum('FeatureAccessReason', [
19
+ 'included',
20
+ 'limit_available',
21
+ 'limit_reached',
22
+ 'not_in_plan',
23
+ ]);
24
+
@@ -0,0 +1,94 @@
1
+ import { ScalarTypeEnum, defineSchemaModel } from '@lssm/lib.schema';
2
+ import { defineEvent } from '@lssm/lib.contracts';
3
+
4
+ /**
5
+ * Payload when feature usage is recorded.
6
+ */
7
+ const UsageRecordedPayload = defineSchemaModel({
8
+ name: 'UsageRecordedPayload',
9
+ description: 'Payload when feature usage is recorded',
10
+ fields: {
11
+ organizationId: {
12
+ type: ScalarTypeEnum.String_unsecure(),
13
+ isOptional: false,
14
+ },
15
+ feature: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
16
+ quantity: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
17
+ billingPeriod: {
18
+ type: ScalarTypeEnum.String_unsecure(),
19
+ isOptional: false,
20
+ },
21
+ recordedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
22
+ },
23
+ });
24
+
25
+ /**
26
+ * Payload when usage limit is reached.
27
+ */
28
+ const UsageLimitReachedPayload = defineSchemaModel({
29
+ name: 'UsageLimitReachedPayload',
30
+ description: 'Payload when usage limit is reached',
31
+ fields: {
32
+ organizationId: {
33
+ type: ScalarTypeEnum.String_unsecure(),
34
+ isOptional: false,
35
+ },
36
+ feature: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
37
+ limit: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
38
+ currentUsage: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
39
+ reachedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
40
+ },
41
+ });
42
+
43
+ /**
44
+ * Payload when subscription status changes.
45
+ */
46
+ const SubscriptionChangedPayload = defineSchemaModel({
47
+ name: 'SubscriptionChangedPayload',
48
+ description: 'Payload when subscription status changes',
49
+ fields: {
50
+ organizationId: {
51
+ type: ScalarTypeEnum.String_unsecure(),
52
+ isOptional: false,
53
+ },
54
+ previousPlan: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
55
+ newPlan: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
56
+ previousStatus: {
57
+ type: ScalarTypeEnum.String_unsecure(),
58
+ isOptional: true,
59
+ },
60
+ newStatus: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
61
+ changedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
62
+ },
63
+ });
64
+
65
+ /**
66
+ * Event: Feature usage has been recorded.
67
+ */
68
+ export const UsageRecordedEvent = defineEvent({
69
+ name: 'billing.usage.recorded',
70
+ version: 1,
71
+ description: 'Feature usage has been recorded.',
72
+ payload: UsageRecordedPayload,
73
+ });
74
+
75
+ /**
76
+ * Event: Usage limit has been reached for a feature.
77
+ */
78
+ export const UsageLimitReachedEvent = defineEvent({
79
+ name: 'billing.limit.reached',
80
+ version: 1,
81
+ description: 'Usage limit has been reached for a feature.',
82
+ payload: UsageLimitReachedPayload,
83
+ });
84
+
85
+ /**
86
+ * Event: Subscription status has changed.
87
+ */
88
+ export const SubscriptionChangedEvent = defineEvent({
89
+ name: 'billing.subscription.changed',
90
+ version: 1,
91
+ description: 'Subscription status has changed.',
92
+ payload: SubscriptionChangedPayload,
93
+ });
94
+