@lssm/example.saas-boilerplate 0.0.0-canary-20251215234340 → 0.0.0-canary-20251216024228
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +32 -24
- package/CHANGELOG.md +8 -8
- package/dist/billing/billing.contracts.js +1 -0
- package/dist/billing/billing.enum.js +1 -0
- package/dist/billing/billing.event.js +1 -0
- package/dist/billing/billing.handler.js +1 -0
- package/dist/billing/billing.schema.js +1 -0
- package/dist/billing/index.js +1 -0
- package/dist/dashboard/index.js +1 -0
- package/dist/handlers/index.js +1 -1
- package/dist/index.js +1 -1
- package/dist/presentations/index.js +1 -1
- package/dist/project/index.js +1 -0
- package/dist/project/project.contracts.js +1 -0
- package/dist/project/project.enum.js +1 -0
- package/dist/project/project.event.js +1 -0
- package/dist/project/project.handler.js +1 -0
- package/dist/project/project.presentation.js +1 -0
- package/dist/project/project.schema.js +1 -0
- package/dist/settings/index.js +1 -0
- package/dist/settings/settings.entity.js +1 -0
- package/dist/settings/settings.enum.js +1 -0
- package/package.json +45 -29
- package/src/billing/billing.contracts.ts +116 -0
- package/src/{entities/billing.ts → billing/billing.entity.ts} +2 -1
- package/src/billing/billing.enum.ts +24 -0
- package/src/billing/billing.event.ts +94 -0
- package/src/{handlers/billing.handlers.ts → billing/billing.handler.ts} +7 -8
- package/src/{presentations/billing.ts → billing/billing.presentation.ts} +1 -3
- package/src/{contracts/billing.ts → billing/billing.schema.ts} +29 -131
- package/src/billing/index.ts +65 -0
- package/src/{presentations/dashboard.ts → dashboard/dashboard.presentation.ts} +1 -3
- package/src/dashboard/index.ts +9 -0
- package/src/handlers/index.ts +12 -26
- package/src/index.ts +43 -6
- package/src/presentations/index.ts +24 -30
- package/src/project/index.ts +67 -0
- package/src/project/project.contracts.ts +185 -0
- package/src/{entities/project.ts → project/project.entity.ts} +2 -1
- package/src/project/project.enum.ts +23 -0
- package/src/project/project.event.ts +109 -0
- package/src/{handlers/project.handlers.ts → project/project.handler.ts} +8 -9
- package/src/{presentations/project-list.ts → project/project.presentation.ts} +2 -4
- package/src/project/project.schema.ts +145 -0
- package/src/settings/index.ts +10 -0
- package/src/{entities/settings.ts → settings/settings.entity.ts} +3 -11
- package/src/settings/settings.enum.ts +12 -0
- package/src/{handlers → shared}/mock-data.ts +2 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/contracts/billing.js +0 -1
- package/dist/contracts/index.js +0 -1
- package/dist/contracts/project.js +0 -1
- package/dist/entities/index.js +0 -1
- package/dist/entities/settings.js +0 -1
- package/dist/events.js +0 -1
- package/dist/handlers/billing.handlers.js +0 -1
- package/dist/handlers/project.handlers.js +0 -1
- package/dist/presentations/project-list.js +0 -1
- package/src/contracts/index.ts +0 -36
- package/src/contracts/project.ts +0 -314
- package/src/entities/index.ts +0 -62
- package/src/events.ts +0 -177
- /package/dist/{entities/billing.js → billing/billing.entity.js} +0 -0
- /package/dist/{presentations/billing.js → billing/billing.presentation.js} +0 -0
- /package/dist/{presentations/dashboard.js → dashboard/dashboard.presentation.js} +0 -0
- /package/dist/{entities/project.js → project/project.entity.js} +0 -0
- /package/dist/{handlers → shared}/mock-data.js +0 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -2,31 +2,39 @@ $ bun build:bundle && bun build:types
|
|
|
2
2
|
$ tsdown
|
|
3
3
|
[34mℹ[39m tsdown [2mv0.17.4[22m powered by rolldown [2mv1.0.0-beta.53[22m
|
|
4
4
|
[34mℹ[39m config file: [4m/home/runner/work/contractspec/contractspec/packages/examples/saas-boilerplate/tsdown.config.js[24m
|
|
5
|
-
[34mℹ[39m entry: [34msrc/
|
|
5
|
+
[34mℹ[39m entry: [34msrc/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[39m
|
|
6
6
|
[34mℹ[39m target: [34mesnext[39m
|
|
7
7
|
[34mℹ[39m tsconfig: [34mtsconfig.json[39m
|
|
8
8
|
[34mℹ[39m Build start
|
|
9
|
-
[34mℹ[39m [2mdist/[22m[
|
|
10
|
-
[34mℹ[39m [2mdist/[22m[
|
|
11
|
-
[34mℹ[39m [2mdist/[22m[
|
|
12
|
-
[34mℹ[39m [2mdist/[22m[
|
|
13
|
-
[34mℹ[39m [2mdist/[22m[
|
|
14
|
-
[34mℹ[39m [2mdist/[22m[
|
|
15
|
-
[34mℹ[39m [2mdist/[22m[
|
|
16
|
-
[34mℹ[39m [2mdist/[22m[1mfeature.js[22m
|
|
17
|
-
[34mℹ[39m [2mdist/[22m[
|
|
18
|
-
[34mℹ[39m [2mdist/[22m[
|
|
19
|
-
[34mℹ[39m [2mdist/[22m[
|
|
20
|
-
[34mℹ[39m [2mdist/[22m[
|
|
21
|
-
[34mℹ[39m [2mdist/[22m[
|
|
22
|
-
[34mℹ[39m [2mdist/[22m[
|
|
23
|
-
[34mℹ[39m [2mdist/[22m[
|
|
24
|
-
[34mℹ[39m [2mdist/[22m[
|
|
25
|
-
[34mℹ[39m [2mdist/[22m[
|
|
26
|
-
[34mℹ[39m [2mdist/[22m[
|
|
27
|
-
[34mℹ[39m [2mdist/[22m[
|
|
28
|
-
[34mℹ[39m [2mdist/[22m[
|
|
29
|
-
[34mℹ[39m [2mdist/[22m[
|
|
30
|
-
[34mℹ[39m
|
|
31
|
-
[
|
|
9
|
+
[34mℹ[39m [2mdist/[22m[1mindex.js[22m [2m5.02 kB[22m [2m│ gzip: 1.28 kB[22m
|
|
10
|
+
[34mℹ[39m [2mdist/[22m[1mdocs/saas-boilerplate.docblock.js[22m [2m3.46 kB[22m [2m│ gzip: 1.38 kB[22m
|
|
11
|
+
[34mℹ[39m [2mdist/[22m[1mproject/project.contracts.js[22m [2m2.78 kB[22m [2m│ gzip: 0.94 kB[22m
|
|
12
|
+
[34mℹ[39m [2mdist/[22m[1mbilling/billing.schema.js[22m [2m2.73 kB[22m [2m│ gzip: 0.70 kB[22m
|
|
13
|
+
[34mℹ[39m [2mdist/[22m[1mbilling/billing.entity.js[22m [2m2.57 kB[22m [2m│ gzip: 0.94 kB[22m
|
|
14
|
+
[34mℹ[39m [2mdist/[22m[1mproject/project.schema.js[22m [2m2.57 kB[22m [2m│ gzip: 0.59 kB[22m
|
|
15
|
+
[34mℹ[39m [2mdist/[22m[1mshared/mock-data.js[22m [2m2.06 kB[22m [2m│ gzip: 0.78 kB[22m
|
|
16
|
+
[34mℹ[39m [2mdist/[22m[1mfeature.js[22m [2m1.95 kB[22m [2m│ gzip: 0.52 kB[22m
|
|
17
|
+
[34mℹ[39m [2mdist/[22m[1mproject/index.js[22m [2m1.87 kB[22m [2m│ gzip: 0.50 kB[22m
|
|
18
|
+
[34mℹ[39m [2mdist/[22m[1mbilling/index.js[22m [2m1.87 kB[22m [2m│ gzip: 0.51 kB[22m
|
|
19
|
+
[34mℹ[39m [2mdist/[22m[1mbilling/billing.contracts.js[22m [2m1.86 kB[22m [2m│ gzip: 0.69 kB[22m
|
|
20
|
+
[34mℹ[39m [2mdist/[22m[1mproject/project.event.js[22m [2m1.80 kB[22m [2m│ gzip: 0.45 kB[22m
|
|
21
|
+
[34mℹ[39m [2mdist/[22m[1mbilling/billing.event.js[22m [2m1.65 kB[22m [2m│ gzip: 0.49 kB[22m
|
|
22
|
+
[34mℹ[39m [2mdist/[22m[1mproject/project.entity.js[22m [2m1.64 kB[22m [2m│ gzip: 0.67 kB[22m
|
|
23
|
+
[34mℹ[39m [2mdist/[22m[1msettings/settings.entity.js[22m [2m1.49 kB[22m [2m│ gzip: 0.63 kB[22m
|
|
24
|
+
[34mℹ[39m [2mdist/[22m[1mproject/project.handler.js[22m [2m1.43 kB[22m [2m│ gzip: 0.65 kB[22m
|
|
25
|
+
[34mℹ[39m [2mdist/[22m[1mproject/project.presentation.js[22m [2m0.84 kB[22m [2m│ gzip: 0.38 kB[22m
|
|
26
|
+
[34mℹ[39m [2mdist/[22m[1mbilling/billing.presentation.js[22m [2m0.77 kB[22m [2m│ gzip: 0.32 kB[22m
|
|
27
|
+
[34mℹ[39m [2mdist/[22m[1mpresentations/index.js[22m [2m0.76 kB[22m [2m│ gzip: 0.25 kB[22m
|
|
28
|
+
[34mℹ[39m [2mdist/[22m[1mbilling/billing.handler.js[22m [2m0.72 kB[22m [2m│ gzip: 0.42 kB[22m
|
|
29
|
+
[34mℹ[39m [2mdist/[22m[1mdashboard/dashboard.presentation.js[22m [2m0.71 kB[22m [2m│ gzip: 0.33 kB[22m
|
|
30
|
+
[34mℹ[39m [2mdist/[22m[1mhandlers/index.js[22m [2m0.64 kB[22m [2m│ gzip: 0.23 kB[22m
|
|
31
|
+
[34mℹ[39m [2mdist/[22m[1mexample.js[22m [2m0.63 kB[22m [2m│ gzip: 0.38 kB[22m
|
|
32
|
+
[34mℹ[39m [2mdist/[22m[1mbilling/billing.enum.js[22m [2m0.29 kB[22m [2m│ gzip: 0.22 kB[22m
|
|
33
|
+
[34mℹ[39m [2mdist/[22m[1mproject/project.enum.js[22m [2m0.24 kB[22m [2m│ gzip: 0.17 kB[22m
|
|
34
|
+
[34mℹ[39m [2mdist/[22m[1msettings/settings.enum.js[22m [2m0.21 kB[22m [2m│ gzip: 0.18 kB[22m
|
|
35
|
+
[34mℹ[39m [2mdist/[22m[1msettings/index.js[22m [2m0.21 kB[22m [2m│ gzip: 0.13 kB[22m
|
|
36
|
+
[34mℹ[39m [2mdist/[22m[1mdashboard/index.js[22m [2m0.17 kB[22m [2m│ gzip: 0.11 kB[22m
|
|
37
|
+
[34mℹ[39m [2mdist/[22m[1mdocs/index.js[22m [2m0.04 kB[22m [2m│ gzip: 0.06 kB[22m
|
|
38
|
+
[34mℹ[39m 29 files, total: 42.99 kB
|
|
39
|
+
[32m✔[39m Build complete in [32m117ms[39m
|
|
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-
|
|
3
|
+
## 0.0.0-canary-20251216024228
|
|
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-
|
|
10
|
-
- @lssm/lib.schema@0.0.0-canary-
|
|
11
|
-
- @lssm/lib.jobs@0.0.0-canary-
|
|
12
|
-
- @lssm/lib.bus@0.0.0-canary-
|
|
13
|
-
- @lssm/lib.identity-rbac@0.0.0-canary-
|
|
14
|
-
- @lssm/module.audit-trail@0.0.0-canary-
|
|
15
|
-
- @lssm/module.notifications@0.0.0-canary-
|
|
9
|
+
- @lssm/lib.contracts@0.0.0-canary-20251216024228
|
|
10
|
+
- @lssm/lib.schema@0.0.0-canary-20251216024228
|
|
11
|
+
- @lssm/lib.jobs@0.0.0-canary-20251216024228
|
|
12
|
+
- @lssm/lib.bus@0.0.0-canary-20251216024228
|
|
13
|
+
- @lssm/lib.identity-rbac@0.0.0-canary-20251216024228
|
|
14
|
+
- @lssm/module.audit-trail@0.0.0-canary-20251216024228
|
|
15
|
+
- @lssm/module.notifications@0.0.0-canary-20251216024228
|
|
@@ -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};
|
package/dist/handlers/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
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
|
|
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{
|
|
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-
|
|
3
|
+
"version": "0.0.0-canary-20251216024228",
|
|
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
|
-
"./
|
|
11
|
-
"./
|
|
12
|
-
"./
|
|
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
|
-
"./
|
|
28
|
-
"./
|
|
29
|
-
"./
|
|
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
|
-
"./
|
|
64
|
-
"./
|
|
65
|
-
"./
|
|
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
|
-
"./
|
|
81
|
-
"./
|
|
82
|
-
"./
|
|
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
|
+
|