@lssm/example.saas-boilerplate 0.0.0-canary-20251215234153 → 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
@@ -1 +0,0 @@
1
- import{ScalarTypeEnum as e,defineEnum as t}from"@lssm/lib.schema";import{defineCommand as n,defineQuery as r,defineSchemaModel as i}from"@lssm/lib.contracts";const a=[`example.saas-boilerplate`],o=t(`SubscriptionStatus`,[`TRIALING`,`ACTIVE`,`PAST_DUE`,`CANCELED`,`PAUSED`]),s=t(`FeatureAccessReason`,[`included`,`limit_available`,`limit_reached`,`not_in_plan`]),c=i({name:`Subscription`,description:`Organization subscription details`,fields:{id:{type:e.String_unsecure(),isOptional:!1},organizationId:{type:e.String_unsecure(),isOptional:!1},planId:{type:e.String_unsecure(),isOptional:!1},planName:{type:e.String_unsecure(),isOptional:!1},status:{type:o,isOptional:!1},currentPeriodStart:{type:e.DateTime(),isOptional:!1},currentPeriodEnd:{type:e.DateTime(),isOptional:!1},trialEndsAt:{type:e.DateTime(),isOptional:!0},cancelAtPeriodEnd:{type:e.Boolean(),isOptional:!1}}}),l=i({name:`UsageSummary`,description:`Usage summary for a feature`,fields:{feature:{type:e.String_unsecure(),isOptional:!1},used:{type:e.Int_unsecure(),isOptional:!1},limit:{type:e.Int_unsecure(),isOptional:!0},unit:{type:e.String_unsecure(),isOptional:!0},percentage:{type:e.Float_unsecure(),isOptional:!0}}}),u=i({name:`RecordUsageInput`,description:`Input for recording feature usage`,fields:{feature:{type:e.String_unsecure(),isOptional:!1},quantity:{type:e.Int_unsecure(),isOptional:!1},sourceId:{type:e.String_unsecure(),isOptional:!0},sourceType:{type:e.String_unsecure(),isOptional:!0},metadata:{type:e.JSONObject(),isOptional:!0}}}),d=i({name:`RecordUsageOutput`,description:`Output for recording feature usage`,fields:{recorded:{type:e.Boolean(),isOptional:!1},currentUsage:{type:e.Int_unsecure(),isOptional:!1},limit:{type:e.Int_unsecure(),isOptional:!0},limitReached:{type:e.Boolean(),isOptional:!1}}}),f=i({name:`UsageRecordedPayload`,description:`Payload for usage.recorded event`,fields:{feature:{type:e.String_unsecure(),isOptional:!1},quantity:{type:e.Int_unsecure(),isOptional:!1}}}),p=i({name:`GetUsageSummaryInput`,description:`Input for getting usage summary`,fields:{billingPeriod:{type:e.String_unsecure(),isOptional:!0}}}),m=i({name:`GetUsageSummaryOutput`,description:`Output for usage summary`,fields:{billingPeriod:{type:e.String_unsecure(),isOptional:!1},usage:{type:l,isArray:!0,isOptional:!1}}}),h=i({name:`CheckFeatureAccessInput`,description:`Input for checking feature access`,fields:{feature:{type:e.String_unsecure(),isOptional:!1}}}),g=i({name:`CheckFeatureAccessOutput`,description:`Output for feature access check`,fields:{hasAccess:{type:e.Boolean(),isOptional:!1},reason:{type:s,isOptional:!0},upgradeUrl:{type:e.URL(),isOptional:!0}}}),_=r({meta:{name:`saas.billing.subscription.get`,version:1,stability:`stable`,owners:[...a],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:c},policy:{auth:`user`}}),v=n({meta:{name:`saas.billing.usage.record`,version:1,stability:`stable`,owners:[...a],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:u,output:d},policy:{auth:`user`},sideEffects:{emits:[{name:`billing.usage.recorded`,version:1,when:`Usage is recorded`,payload:f}]}}),y=r({meta:{name:`saas.billing.usage.summary`,version:1,stability:`stable`,owners:[...a],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:p,output:m},policy:{auth:`user`}}),b=r({meta:{name:`saas.billing.feature.check`,version:1,stability:`stable`,owners:[...a],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:h,output:g},policy:{auth:`user`}});export{b as CheckFeatureAccessContract,h as CheckFeatureAccessInputModel,g as CheckFeatureAccessOutputModel,s as FeatureAccessReasonEnum,_ as GetSubscriptionContract,y as GetUsageSummaryContract,p as GetUsageSummaryInputModel,m as GetUsageSummaryOutputModel,v as RecordUsageContract,u as RecordUsageInputModel,d as RecordUsageOutputModel,c as SubscriptionModel,f as UsageRecordedPayloadModel,l as UsageSummaryModel};
@@ -1 +0,0 @@
1
- import{CreateProjectContract as e,CreateProjectInputModel as t,DeleteProjectContract as n,DeleteProjectInputModel as r,DeleteProjectOutputModel as i,GetProjectContract as a,GetProjectInputModel as o,ListProjectsContract as s,ListProjectsInputModel as c,ListProjectsOutputModel as l,ProjectDeletedPayloadModel as u,ProjectModel as d,ProjectStatusFilterEnum as f,UpdateProjectContract as p,UpdateProjectInputModel as m}from"./project.js";import{CheckFeatureAccessContract as h,CheckFeatureAccessInputModel as g,CheckFeatureAccessOutputModel as _,FeatureAccessReasonEnum as v,GetSubscriptionContract as y,GetUsageSummaryContract as b,GetUsageSummaryInputModel as x,GetUsageSummaryOutputModel as S,RecordUsageContract as C,RecordUsageInputModel as w,RecordUsageOutputModel as T,SubscriptionModel as E,UsageRecordedPayloadModel as D,UsageSummaryModel as O}from"./billing.js";export{h as CheckFeatureAccessContract,g as CheckFeatureAccessInputModel,_ as CheckFeatureAccessOutputModel,e as CreateProjectContract,t as CreateProjectInputModel,n as DeleteProjectContract,r as DeleteProjectInputModel,i as DeleteProjectOutputModel,v as FeatureAccessReasonEnum,a as GetProjectContract,o as GetProjectInputModel,y as GetSubscriptionContract,b as GetUsageSummaryContract,x as GetUsageSummaryInputModel,S as GetUsageSummaryOutputModel,s as ListProjectsContract,c as ListProjectsInputModel,l as ListProjectsOutputModel,u as ProjectDeletedPayloadModel,d as ProjectModel,f as ProjectStatusFilterEnum,C as RecordUsageContract,w as RecordUsageInputModel,T as RecordUsageOutputModel,E as SubscriptionModel,p as UpdateProjectContract,m as UpdateProjectInputModel,D as UsageRecordedPayloadModel,O as UsageSummaryModel};
@@ -1 +0,0 @@
1
- import{ScalarTypeEnum as e,defineEnum as t,defineSchemaModel as n}from"@lssm/lib.schema";import{defineCommand as r,defineQuery as i}from"@lssm/lib.contracts/spec";const a=[`example.saas-boilerplate`],o=t(`ProjectStatus`,[`DRAFT`,`ACTIVE`,`ARCHIVED`,`DELETED`]),s=t(`ProjectStatusFilter`,[`DRAFT`,`ACTIVE`,`ARCHIVED`,`all`]),c=n({name:`Project`,description:`A project within an organization`,fields:{id:{type:e.String_unsecure(),isOptional:!1},name:{type:e.String_unsecure(),isOptional:!1},description:{type:e.String_unsecure(),isOptional:!0},slug:{type:e.String_unsecure(),isOptional:!0},organizationId:{type:e.String_unsecure(),isOptional:!1},createdBy:{type:e.String_unsecure(),isOptional:!1},status:{type:o,isOptional:!1},isPublic:{type:e.Boolean(),isOptional:!1},tags:{type:e.String_unsecure(),isArray:!0,isOptional:!1},createdAt:{type:e.DateTime(),isOptional:!1},updatedAt:{type:e.DateTime(),isOptional:!1}}}),l=n({name:`CreateProjectInput`,description:`Input for creating a project`,fields:{name:{type:e.NonEmptyString(),isOptional:!1},description:{type:e.String_unsecure(),isOptional:!0},slug:{type:e.String_unsecure(),isOptional:!0},isPublic:{type:e.Boolean(),isOptional:!0},tags:{type:e.String_unsecure(),isArray:!0,isOptional:!0}}}),u=n({name:`UpdateProjectInput`,description:`Input for updating a project`,fields:{projectId:{type:e.String_unsecure(),isOptional:!1},name:{type:e.String_unsecure(),isOptional:!0},description:{type:e.String_unsecure(),isOptional:!0},slug:{type:e.String_unsecure(),isOptional:!0},isPublic:{type:e.Boolean(),isOptional:!0},tags:{type:e.String_unsecure(),isArray:!0,isOptional:!0},status:{type:o,isOptional:!0}}}),d=n({name:`GetProjectInput`,fields:{projectId:{type:e.String_unsecure(),isOptional:!1}}}),f=n({name:`DeleteProjectInput`,fields:{projectId:{type:e.String_unsecure(),isOptional:!1}}}),p=n({name:`DeleteProjectOutput`,fields:{success:{type:e.Boolean(),isOptional:!1}}}),m=n({name:`ProjectDeletedPayload`,fields:{projectId:{type:e.String_unsecure(),isOptional:!1}}}),h=n({name:`ListProjectsInput`,description:`Input for listing projects`,fields:{status:{type:s,isOptional:!0},search:{type:e.String_unsecure(),isOptional:!0},limit:{type:e.Int_unsecure(),isOptional:!0,defaultValue:20},offset:{type:e.Int_unsecure(),isOptional:!0,defaultValue:0}}}),g=n({name:`ListProjectsOutput`,description:`Output for listing projects`,fields:{projects:{type:c,isArray:!0,isOptional:!1},total:{type:e.Int_unsecure(),isOptional:!1}}}),_=r({meta:{name:`saas.project.create`,version:1,stability:`stable`,owners:[...a],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:l,output:c,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:c}],audit:[`project.created`]}}),v=i({meta:{name:`saas.project.get`,version:1,stability:`stable`,owners:[...a],tags:[`saas`,`project`,`get`],description:`Get a project by ID.`,goal:`Retrieve project details.`,context:`Project detail page, API calls.`},io:{input:d,output:c,errors:{NOT_FOUND:{description:`Project not found`,http:404,gqlCode:`NOT_FOUND`,when:`Project ID is invalid or user lacks access`}}},policy:{auth:`user`}}),y=r({meta:{name:`saas.project.update`,version:1,stability:`stable`,owners:[...a],tags:[`saas`,`project`,`update`],description:`Update project details.`,goal:`Allow project owners/editors to modify project.`,context:`Project settings page.`},io:{input:u,output:c},policy:{auth:`user`},sideEffects:{emits:[{name:`project.updated`,version:1,when:`Project is updated`,payload:c}],audit:[`project.updated`]}}),b=r({meta:{name:`saas.project.delete`,version:1,stability:`stable`,owners:[...a],tags:[`saas`,`project`,`delete`],description:`Delete a project (soft delete).`,goal:`Allow project owners to remove projects.`,context:`Project settings page.`},io:{input:f,output:p},policy:{auth:`user`},sideEffects:{emits:[{name:`project.deleted`,version:1,when:`Project is deleted`,payload:m}],audit:[`project.deleted`]}}),x=i({meta:{name:`saas.project.list`,version:1,stability:`stable`,owners:[...a],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:h,output:g},policy:{auth:`user`}});export{_ as CreateProjectContract,l as CreateProjectInputModel,b as DeleteProjectContract,f as DeleteProjectInputModel,p as DeleteProjectOutputModel,v as GetProjectContract,d as GetProjectInputModel,x as ListProjectsContract,h as ListProjectsInputModel,g as ListProjectsOutputModel,m as ProjectDeletedPayloadModel,c as ProjectModel,s as ProjectStatusFilterEnum,y as UpdateProjectContract,u as UpdateProjectInputModel};
@@ -1 +0,0 @@
1
- import{ProjectEntity as e,ProjectMemberEntity as t,ProjectStatusEnum as n}from"./project.js";import{FeatureFlagEntity as r,SettingsEntity as i,SettingsScopeEnum as a}from"./settings.js";import{BillingUsageEntity as o,SubscriptionEntity as s,SubscriptionStatusEnum as c,UsageLimitEntity as l}from"./billing.js";import{identityRbacEntities as u,identityRbacSchemaContribution as d}from"@lssm/lib.identity-rbac";const f=[e,t,i,r,s,o,l],p={moduleId:`@lssm/example.saas-boilerplate`,entities:f,enums:[n,a,c]};export{o as BillingUsageEntity,r as FeatureFlagEntity,e as ProjectEntity,t as ProjectMemberEntity,n as ProjectStatusEnum,i as SettingsEntity,a as SettingsScopeEnum,s as SubscriptionEntity,c as SubscriptionStatusEnum,l as UsageLimitEntity,u as identityRbacEntities,d as identityRbacSchemaContribution,f as saasBoilerplateEntities,p as saasBoilerplateSchemaContribution};
@@ -1 +0,0 @@
1
- import{defineEntity as e,defineEntityEnum as t,field as n,index as r}from"@lssm/lib.schema";const i=t({name:`SettingsScope`,values:[`APP`,`ORG`,`USER`,`PROJECT`],schema:`saas_app`,description:`Scope of a setting.`}),a=e({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:[i]}),o=e({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{o as FeatureFlagEntity,a as SettingsEntity,i as SettingsScopeEnum};
package/dist/events.js DELETED
@@ -1 +0,0 @@
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=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}}}),c=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}}}),l=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}}}),u=n({name:`project.created`,version:1,description:`A new project has been created.`,payload:r}),d=n({name:`project.updated`,version:1,description:`A project has been updated.`,payload:i}),f=n({name:`project.deleted`,version:1,description:`A project has been deleted.`,payload:a}),p=n({name:`project.archived`,version:1,description:`A project has been archived.`,payload:o}),m=n({name:`billing.usage.recorded`,version:1,description:`Feature usage has been recorded.`,payload:s}),h=n({name:`billing.limit.reached`,version:1,description:`Usage limit has been reached for a feature.`,payload:c}),g=n({name:`billing.subscription.changed`,version:1,description:`Subscription status has changed.`,payload:l}),_={ProjectCreatedEvent:u,ProjectUpdatedEvent:d,ProjectDeletedEvent:f,ProjectArchivedEvent:p,UsageRecordedEvent:m,UsageLimitReachedEvent:h,SubscriptionChangedEvent:g};export{p as ProjectArchivedEvent,u as ProjectCreatedEvent,f as ProjectDeletedEvent,d as ProjectUpdatedEvent,_ as SaasBoilerplateEvents,g as SubscriptionChangedEvent,h as UsageLimitReachedEvent,m as UsageRecordedEvent};
@@ -1 +0,0 @@
1
- import{MOCK_SUBSCRIPTION as e,MOCK_USAGE_SUMMARY as t}from"./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};
@@ -1 +0,0 @@
1
- import{MOCK_PROJECTS as e}from"./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};
@@ -1 +0,0 @@
1
- import{ProjectModel as e}from"../contracts/project.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};
@@ -1,36 +0,0 @@
1
- // Project contracts and models
2
- export {
3
- ProjectStatusFilterEnum,
4
- ProjectModel,
5
- CreateProjectInputModel,
6
- UpdateProjectInputModel,
7
- GetProjectInputModel,
8
- DeleteProjectInputModel,
9
- DeleteProjectOutputModel,
10
- ProjectDeletedPayloadModel,
11
- ListProjectsInputModel,
12
- ListProjectsOutputModel,
13
- CreateProjectContract,
14
- GetProjectContract,
15
- UpdateProjectContract,
16
- DeleteProjectContract,
17
- ListProjectsContract,
18
- } from './project';
19
-
20
- // Billing contracts and models
21
- export {
22
- FeatureAccessReasonEnum,
23
- SubscriptionModel,
24
- UsageSummaryModel,
25
- RecordUsageInputModel,
26
- RecordUsageOutputModel,
27
- UsageRecordedPayloadModel,
28
- GetUsageSummaryInputModel,
29
- GetUsageSummaryOutputModel,
30
- CheckFeatureAccessInputModel,
31
- CheckFeatureAccessOutputModel,
32
- GetSubscriptionContract,
33
- RecordUsageContract,
34
- GetUsageSummaryContract,
35
- CheckFeatureAccessContract,
36
- } from './billing';
@@ -1,314 +0,0 @@
1
- import { defineCommand, defineQuery } from '@lssm/lib.contracts/spec';
2
- import {
3
- defineSchemaModel,
4
- ScalarTypeEnum,
5
- defineEnum,
6
- } from '@lssm/lib.schema';
7
-
8
- const OWNERS = ['example.saas-boilerplate'] as const;
9
-
10
- // ============ Enums (for contract schemas) ============
11
- // Note: Entity enums for Prisma are defined separately in ../entities
12
-
13
- const ProjectStatusSchemaEnum = defineEnum('ProjectStatus', [
14
- 'DRAFT',
15
- 'ACTIVE',
16
- 'ARCHIVED',
17
- 'DELETED',
18
- ]);
19
-
20
- export const ProjectStatusFilterEnum = defineEnum('ProjectStatusFilter', [
21
- 'DRAFT',
22
- 'ACTIVE',
23
- 'ARCHIVED',
24
- 'all',
25
- ]);
26
-
27
- // ============ Schemas ============
28
-
29
- export const ProjectModel = defineSchemaModel({
30
- name: 'Project',
31
- description: 'A project within an organization',
32
- fields: {
33
- id: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
34
- name: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
35
- description: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
36
- slug: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
37
- organizationId: {
38
- type: ScalarTypeEnum.String_unsecure(),
39
- isOptional: false,
40
- },
41
- createdBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
42
- status: { type: ProjectStatusSchemaEnum, isOptional: false },
43
- isPublic: { type: ScalarTypeEnum.Boolean(), isOptional: false },
44
- tags: {
45
- type: ScalarTypeEnum.String_unsecure(),
46
- isArray: true,
47
- isOptional: false,
48
- },
49
- createdAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
50
- updatedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
51
- },
52
- });
53
-
54
- export const CreateProjectInputModel = defineSchemaModel({
55
- name: 'CreateProjectInput',
56
- description: 'Input for creating a project',
57
- fields: {
58
- name: { type: ScalarTypeEnum.NonEmptyString(), isOptional: false },
59
- description: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
60
- slug: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
61
- isPublic: { type: ScalarTypeEnum.Boolean(), isOptional: true },
62
- tags: {
63
- type: ScalarTypeEnum.String_unsecure(),
64
- isArray: true,
65
- isOptional: true,
66
- },
67
- },
68
- });
69
-
70
- export const UpdateProjectInputModel = defineSchemaModel({
71
- name: 'UpdateProjectInput',
72
- description: 'Input for updating a project',
73
- fields: {
74
- projectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
75
- name: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
76
- description: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
77
- slug: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
78
- isPublic: { type: ScalarTypeEnum.Boolean(), isOptional: true },
79
- tags: {
80
- type: ScalarTypeEnum.String_unsecure(),
81
- isArray: true,
82
- isOptional: true,
83
- },
84
- status: { type: ProjectStatusSchemaEnum, isOptional: true },
85
- },
86
- });
87
-
88
- export const GetProjectInputModel = defineSchemaModel({
89
- name: 'GetProjectInput',
90
- fields: {
91
- projectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
92
- },
93
- });
94
-
95
- export const DeleteProjectInputModel = defineSchemaModel({
96
- name: 'DeleteProjectInput',
97
- fields: {
98
- projectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
99
- },
100
- });
101
-
102
- export const DeleteProjectOutputModel = defineSchemaModel({
103
- name: 'DeleteProjectOutput',
104
- fields: {
105
- success: { type: ScalarTypeEnum.Boolean(), isOptional: false },
106
- },
107
- });
108
-
109
- export const ProjectDeletedPayloadModel = defineSchemaModel({
110
- name: 'ProjectDeletedPayload',
111
- fields: {
112
- projectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
113
- },
114
- });
115
-
116
- export const ListProjectsInputModel = defineSchemaModel({
117
- name: 'ListProjectsInput',
118
- description: 'Input for listing projects',
119
- fields: {
120
- status: { type: ProjectStatusFilterEnum, isOptional: true },
121
- search: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
122
- limit: {
123
- type: ScalarTypeEnum.Int_unsecure(),
124
- isOptional: true,
125
- defaultValue: 20,
126
- },
127
- offset: {
128
- type: ScalarTypeEnum.Int_unsecure(),
129
- isOptional: true,
130
- defaultValue: 0,
131
- },
132
- },
133
- });
134
-
135
- export const ListProjectsOutputModel = defineSchemaModel({
136
- name: 'ListProjectsOutput',
137
- description: 'Output for listing projects',
138
- fields: {
139
- projects: { type: ProjectModel, isArray: true, isOptional: false },
140
- total: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
141
- },
142
- });
143
-
144
- // ============ Contracts ============
145
-
146
- /**
147
- * Create a new project.
148
- */
149
- export const CreateProjectContract = defineCommand({
150
- meta: {
151
- name: 'saas.project.create',
152
- version: 1,
153
- stability: 'stable',
154
- owners: [...OWNERS],
155
- tags: ['saas', 'project', 'create'],
156
- description: 'Create a new project in the organization.',
157
- goal: 'Allow users to create projects for organizing work.',
158
- context: 'Called from project creation UI or API.',
159
- },
160
- io: {
161
- input: CreateProjectInputModel,
162
- output: ProjectModel,
163
- errors: {
164
- SLUG_EXISTS: {
165
- description: 'A project with this slug already exists',
166
- http: 409,
167
- gqlCode: 'SLUG_EXISTS',
168
- when: 'Slug is already taken in the organization',
169
- },
170
- LIMIT_REACHED: {
171
- description: 'Project limit reached for this plan',
172
- http: 403,
173
- gqlCode: 'LIMIT_REACHED',
174
- when: 'Organization has reached project limit',
175
- },
176
- },
177
- },
178
- policy: {
179
- auth: 'user',
180
- },
181
- sideEffects: {
182
- emits: [
183
- {
184
- name: 'project.created',
185
- version: 1,
186
- when: 'Project is created',
187
- payload: ProjectModel,
188
- },
189
- ],
190
- audit: ['project.created'],
191
- },
192
- });
193
-
194
- /**
195
- * Get project by ID.
196
- */
197
- export const GetProjectContract = defineQuery({
198
- meta: {
199
- name: 'saas.project.get',
200
- version: 1,
201
- stability: 'stable',
202
- owners: [...OWNERS],
203
- tags: ['saas', 'project', 'get'],
204
- description: 'Get a project by ID.',
205
- goal: 'Retrieve project details.',
206
- context: 'Project detail page, API calls.',
207
- },
208
- io: {
209
- input: GetProjectInputModel,
210
- output: ProjectModel,
211
- errors: {
212
- NOT_FOUND: {
213
- description: 'Project not found',
214
- http: 404,
215
- gqlCode: 'NOT_FOUND',
216
- when: 'Project ID is invalid or user lacks access',
217
- },
218
- },
219
- },
220
- policy: {
221
- auth: 'user',
222
- },
223
- });
224
-
225
- /**
226
- * Update a project.
227
- */
228
- export const UpdateProjectContract = defineCommand({
229
- meta: {
230
- name: 'saas.project.update',
231
- version: 1,
232
- stability: 'stable',
233
- owners: [...OWNERS],
234
- tags: ['saas', 'project', 'update'],
235
- description: 'Update project details.',
236
- goal: 'Allow project owners/editors to modify project.',
237
- context: 'Project settings page.',
238
- },
239
- io: {
240
- input: UpdateProjectInputModel,
241
- output: ProjectModel,
242
- },
243
- policy: {
244
- auth: 'user',
245
- },
246
- sideEffects: {
247
- emits: [
248
- {
249
- name: 'project.updated',
250
- version: 1,
251
- when: 'Project is updated',
252
- payload: ProjectModel,
253
- },
254
- ],
255
- audit: ['project.updated'],
256
- },
257
- });
258
-
259
- /**
260
- * Delete a project.
261
- */
262
- export const DeleteProjectContract = defineCommand({
263
- meta: {
264
- name: 'saas.project.delete',
265
- version: 1,
266
- stability: 'stable',
267
- owners: [...OWNERS],
268
- tags: ['saas', 'project', 'delete'],
269
- description: 'Delete a project (soft delete).',
270
- goal: 'Allow project owners to remove projects.',
271
- context: 'Project settings page.',
272
- },
273
- io: {
274
- input: DeleteProjectInputModel,
275
- output: DeleteProjectOutputModel,
276
- },
277
- policy: {
278
- auth: 'user',
279
- },
280
- sideEffects: {
281
- emits: [
282
- {
283
- name: 'project.deleted',
284
- version: 1,
285
- when: 'Project is deleted',
286
- payload: ProjectDeletedPayloadModel,
287
- },
288
- ],
289
- audit: ['project.deleted'],
290
- },
291
- });
292
-
293
- /**
294
- * List organization projects.
295
- */
296
- export const ListProjectsContract = defineQuery({
297
- meta: {
298
- name: 'saas.project.list',
299
- version: 1,
300
- stability: 'stable',
301
- owners: [...OWNERS],
302
- tags: ['saas', 'project', 'list'],
303
- description: 'List projects in the organization.',
304
- goal: 'Show all projects user has access to.',
305
- context: 'Project list page, dashboard.',
306
- },
307
- io: {
308
- input: ListProjectsInputModel,
309
- output: ListProjectsOutputModel,
310
- },
311
- policy: {
312
- auth: 'user',
313
- },
314
- });
@@ -1,62 +0,0 @@
1
- // Project entities
2
- export {
3
- ProjectStatusEnum,
4
- ProjectEntity,
5
- ProjectMemberEntity,
6
- } from './project';
7
-
8
- // Settings entities
9
- export {
10
- SettingsScopeEnum,
11
- SettingsEntity,
12
- FeatureFlagEntity,
13
- } from './settings';
14
-
15
- // Billing entities
16
- export {
17
- SubscriptionStatusEnum,
18
- SubscriptionEntity,
19
- BillingUsageEntity,
20
- UsageLimitEntity,
21
- } from './billing';
22
-
23
- // Re-export identity-rbac entities for convenience
24
- export {
25
- identityRbacSchemaContribution,
26
- identityRbacEntities,
27
- } from '@lssm/lib.identity-rbac';
28
-
29
- // Schema contribution
30
- import {
31
- ProjectEntity,
32
- ProjectMemberEntity,
33
- ProjectStatusEnum,
34
- } from './project';
35
- import {
36
- SettingsEntity,
37
- FeatureFlagEntity,
38
- SettingsScopeEnum,
39
- } from './settings';
40
- import {
41
- SubscriptionEntity,
42
- BillingUsageEntity,
43
- UsageLimitEntity,
44
- SubscriptionStatusEnum,
45
- } from './billing';
46
- import type { ModuleSchemaContribution } from '@lssm/lib.schema';
47
-
48
- export const saasBoilerplateEntities = [
49
- ProjectEntity,
50
- ProjectMemberEntity,
51
- SettingsEntity,
52
- FeatureFlagEntity,
53
- SubscriptionEntity,
54
- BillingUsageEntity,
55
- UsageLimitEntity,
56
- ];
57
-
58
- export const saasBoilerplateSchemaContribution: ModuleSchemaContribution = {
59
- moduleId: '@lssm/example.saas-boilerplate',
60
- entities: saasBoilerplateEntities,
61
- enums: [ProjectStatusEnum, SettingsScopeEnum, SubscriptionStatusEnum],
62
- };
package/src/events.ts DELETED
@@ -1,177 +0,0 @@
1
- import { ScalarTypeEnum, defineSchemaModel } from '@lssm/lib.schema';
2
- import { defineEvent } from '@lssm/lib.contracts';
3
-
4
- // ============ Project Event Payloads ============
5
-
6
- const ProjectCreatedPayload = defineSchemaModel({
7
- name: 'ProjectCreatedPayload',
8
- description: 'Payload when a project is created',
9
- fields: {
10
- projectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
11
- name: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
12
- organizationId: {
13
- type: ScalarTypeEnum.String_unsecure(),
14
- isOptional: false,
15
- },
16
- createdBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
17
- createdAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
18
- },
19
- });
20
-
21
- const ProjectUpdatedPayload = defineSchemaModel({
22
- name: 'ProjectUpdatedPayload',
23
- description: 'Payload when a project is updated',
24
- fields: {
25
- projectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
26
- updatedFields: {
27
- type: ScalarTypeEnum.String_unsecure(),
28
- isArray: true,
29
- isOptional: false,
30
- },
31
- updatedBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
32
- updatedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
33
- },
34
- });
35
-
36
- const ProjectDeletedPayload = defineSchemaModel({
37
- name: 'ProjectDeletedPayload',
38
- description: 'Payload when a project is deleted',
39
- fields: {
40
- projectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
41
- organizationId: {
42
- type: ScalarTypeEnum.String_unsecure(),
43
- isOptional: false,
44
- },
45
- deletedBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
46
- deletedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
47
- },
48
- });
49
-
50
- const ProjectArchivedPayload = defineSchemaModel({
51
- name: 'ProjectArchivedPayload',
52
- description: 'Payload when a project is archived',
53
- fields: {
54
- projectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
55
- archivedBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
56
- archivedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
57
- },
58
- });
59
-
60
- // ============ Billing Event Payloads ============
61
-
62
- const UsageRecordedPayload = defineSchemaModel({
63
- name: 'UsageRecordedPayload',
64
- description: 'Payload when feature usage is recorded',
65
- fields: {
66
- organizationId: {
67
- type: ScalarTypeEnum.String_unsecure(),
68
- isOptional: false,
69
- },
70
- feature: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
71
- quantity: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
72
- billingPeriod: {
73
- type: ScalarTypeEnum.String_unsecure(),
74
- isOptional: false,
75
- },
76
- recordedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
77
- },
78
- });
79
-
80
- const UsageLimitReachedPayload = defineSchemaModel({
81
- name: 'UsageLimitReachedPayload',
82
- description: 'Payload when usage limit is reached',
83
- fields: {
84
- organizationId: {
85
- type: ScalarTypeEnum.String_unsecure(),
86
- isOptional: false,
87
- },
88
- feature: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
89
- limit: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
90
- currentUsage: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
91
- reachedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
92
- },
93
- });
94
-
95
- const SubscriptionChangedPayload = defineSchemaModel({
96
- name: 'SubscriptionChangedPayload',
97
- description: 'Payload when subscription status changes',
98
- fields: {
99
- organizationId: {
100
- type: ScalarTypeEnum.String_unsecure(),
101
- isOptional: false,
102
- },
103
- previousPlan: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
104
- newPlan: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
105
- previousStatus: {
106
- type: ScalarTypeEnum.String_unsecure(),
107
- isOptional: true,
108
- },
109
- newStatus: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
110
- changedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
111
- },
112
- });
113
-
114
- // ============ Project Events ============
115
-
116
- export const ProjectCreatedEvent = defineEvent({
117
- name: 'project.created',
118
- version: 1,
119
- description: 'A new project has been created.',
120
- payload: ProjectCreatedPayload,
121
- });
122
-
123
- export const ProjectUpdatedEvent = defineEvent({
124
- name: 'project.updated',
125
- version: 1,
126
- description: 'A project has been updated.',
127
- payload: ProjectUpdatedPayload,
128
- });
129
-
130
- export const ProjectDeletedEvent = defineEvent({
131
- name: 'project.deleted',
132
- version: 1,
133
- description: 'A project has been deleted.',
134
- payload: ProjectDeletedPayload,
135
- });
136
-
137
- export const ProjectArchivedEvent = defineEvent({
138
- name: 'project.archived',
139
- version: 1,
140
- description: 'A project has been archived.',
141
- payload: ProjectArchivedPayload,
142
- });
143
-
144
- // ============ Billing Events ============
145
-
146
- export const UsageRecordedEvent = defineEvent({
147
- name: 'billing.usage.recorded',
148
- version: 1,
149
- description: 'Feature usage has been recorded.',
150
- payload: UsageRecordedPayload,
151
- });
152
-
153
- export const UsageLimitReachedEvent = defineEvent({
154
- name: 'billing.limit.reached',
155
- version: 1,
156
- description: 'Usage limit has been reached for a feature.',
157
- payload: UsageLimitReachedPayload,
158
- });
159
-
160
- export const SubscriptionChangedEvent = defineEvent({
161
- name: 'billing.subscription.changed',
162
- version: 1,
163
- description: 'Subscription status has changed.',
164
- payload: SubscriptionChangedPayload,
165
- });
166
-
167
- // ============ All Events ============
168
-
169
- export const SaasBoilerplateEvents = {
170
- ProjectCreatedEvent,
171
- ProjectUpdatedEvent,
172
- ProjectDeletedEvent,
173
- ProjectArchivedEvent,
174
- UsageRecordedEvent,
175
- UsageLimitReachedEvent,
176
- SubscriptionChangedEvent,
177
- };
File without changes