@contractspec/example.saas-boilerplate 1.57.0 → 1.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/.turbo/turbo-build.log +160 -180
  2. package/.turbo/turbo-prebuild.log +1 -0
  3. package/CHANGELOG.md +20 -0
  4. package/dist/billing/billing.entity.d.ts +40 -45
  5. package/dist/billing/billing.entity.d.ts.map +1 -1
  6. package/dist/billing/billing.entity.js +110 -118
  7. package/dist/billing/billing.enum.d.ts +2 -7
  8. package/dist/billing/billing.enum.d.ts.map +1 -1
  9. package/dist/billing/billing.enum.js +17 -24
  10. package/dist/billing/billing.event.d.ts +67 -73
  11. package/dist/billing/billing.event.d.ts.map +1 -1
  12. package/dist/billing/billing.event.js +84 -146
  13. package/dist/billing/billing.handler.d.ts +59 -62
  14. package/dist/billing/billing.handler.d.ts.map +1 -1
  15. package/dist/billing/billing.handler.js +140 -49
  16. package/dist/billing/billing.operations.d.ts +138 -144
  17. package/dist/billing/billing.operations.d.ts.map +1 -1
  18. package/dist/billing/billing.operations.js +273 -175
  19. package/dist/billing/billing.presentation.d.ts +2 -7
  20. package/dist/billing/billing.presentation.d.ts.map +1 -1
  21. package/dist/billing/billing.presentation.js +51 -57
  22. package/dist/billing/billing.schema.d.ts +159 -164
  23. package/dist/billing/billing.schema.d.ts.map +1 -1
  24. package/dist/billing/billing.schema.js +112 -204
  25. package/dist/billing/index.d.ts +11 -8
  26. package/dist/billing/index.d.ts.map +1 -0
  27. package/dist/billing/index.js +689 -9
  28. package/dist/browser/billing/billing.entity.js +113 -0
  29. package/dist/browser/billing/billing.enum.js +19 -0
  30. package/dist/browser/billing/billing.event.js +90 -0
  31. package/dist/browser/billing/billing.handler.js +148 -0
  32. package/dist/browser/billing/billing.operations.js +278 -0
  33. package/dist/browser/billing/billing.presentation.js +52 -0
  34. package/dist/browser/billing/billing.schema.js +121 -0
  35. package/dist/browser/billing/index.js +688 -0
  36. package/dist/browser/dashboard/dashboard.presentation.js +52 -0
  37. package/dist/browser/dashboard/index.js +52 -0
  38. package/dist/browser/docs/index.js +93 -0
  39. package/dist/browser/docs/saas-boilerplate.docblock.js +93 -0
  40. package/dist/browser/example.js +39 -0
  41. package/dist/browser/handlers/index.js +358 -0
  42. package/dist/browser/handlers/saas.handlers.js +134 -0
  43. package/dist/browser/index.js +3340 -0
  44. package/dist/browser/presentations/index.js +290 -0
  45. package/dist/browser/project/index.js +790 -0
  46. package/dist/browser/project/project.entity.js +77 -0
  47. package/dist/browser/project/project.enum.js +18 -0
  48. package/dist/browser/project/project.event.js +103 -0
  49. package/dist/browser/project/project.handler.js +178 -0
  50. package/dist/browser/project/project.operations.js +372 -0
  51. package/dist/browser/project/project.presentation.js +177 -0
  52. package/dist/browser/project/project.schema.js +134 -0
  53. package/dist/browser/saas-boilerplate.feature.js +88 -0
  54. package/dist/browser/seeders/index.js +20 -0
  55. package/dist/browser/settings/index.js +75 -0
  56. package/dist/browser/settings/settings.entity.js +74 -0
  57. package/dist/browser/settings/settings.enum.js +11 -0
  58. package/dist/browser/shared/mock-data.js +104 -0
  59. package/dist/browser/shared/overlay-types.js +0 -0
  60. package/dist/browser/tests/operations.test-spec.js +112 -0
  61. package/dist/browser/ui/SaasDashboard.js +988 -0
  62. package/dist/browser/ui/SaasProjectList.js +162 -0
  63. package/dist/browser/ui/SaasSettingsPanel.js +145 -0
  64. package/dist/browser/ui/hooks/index.js +159 -0
  65. package/dist/browser/ui/hooks/useProjectList.js +66 -0
  66. package/dist/browser/ui/hooks/useProjectMutations.js +91 -0
  67. package/dist/browser/ui/index.js +1808 -0
  68. package/dist/browser/ui/modals/CreateProjectModal.js +153 -0
  69. package/dist/browser/ui/modals/ProjectActionsModal.js +335 -0
  70. package/dist/browser/ui/modals/index.js +487 -0
  71. package/dist/browser/ui/overlays/demo-overlays.js +61 -0
  72. package/dist/browser/ui/overlays/index.js +61 -0
  73. package/dist/browser/ui/renderers/index.js +675 -0
  74. package/dist/browser/ui/renderers/project-list.markdown.js +499 -0
  75. package/dist/browser/ui/renderers/project-list.renderer.js +177 -0
  76. package/dist/dashboard/dashboard.presentation.d.ts +2 -7
  77. package/dist/dashboard/dashboard.presentation.d.ts.map +1 -1
  78. package/dist/dashboard/dashboard.presentation.js +51 -53
  79. package/dist/dashboard/index.d.ts +5 -2
  80. package/dist/dashboard/index.d.ts.map +1 -0
  81. package/dist/dashboard/index.js +53 -3
  82. package/dist/docs/index.d.ts +2 -1
  83. package/dist/docs/index.d.ts.map +1 -0
  84. package/dist/docs/index.js +94 -1
  85. package/dist/docs/saas-boilerplate.docblock.d.ts +2 -1
  86. package/dist/docs/saas-boilerplate.docblock.d.ts.map +1 -0
  87. package/dist/docs/saas-boilerplate.docblock.js +45 -51
  88. package/dist/example.d.ts +2 -6
  89. package/dist/example.d.ts.map +1 -1
  90. package/dist/example.js +38 -50
  91. package/dist/handlers/index.d.ts +7 -4
  92. package/dist/handlers/index.d.ts.map +1 -0
  93. package/dist/handlers/index.js +358 -4
  94. package/dist/handlers/saas.handlers.d.ts +60 -59
  95. package/dist/handlers/saas.handlers.d.ts.map +1 -1
  96. package/dist/handlers/saas.handlers.js +127 -140
  97. package/dist/index.d.ts +15 -45
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +3335 -75
  100. package/dist/node/billing/billing.entity.js +113 -0
  101. package/dist/node/billing/billing.enum.js +19 -0
  102. package/dist/node/billing/billing.event.js +90 -0
  103. package/dist/node/billing/billing.handler.js +148 -0
  104. package/dist/node/billing/billing.operations.js +278 -0
  105. package/dist/node/billing/billing.presentation.js +52 -0
  106. package/dist/node/billing/billing.schema.js +121 -0
  107. package/dist/node/billing/index.js +688 -0
  108. package/dist/node/dashboard/dashboard.presentation.js +52 -0
  109. package/dist/node/dashboard/index.js +52 -0
  110. package/dist/node/docs/index.js +93 -0
  111. package/dist/node/docs/saas-boilerplate.docblock.js +93 -0
  112. package/dist/node/example.js +39 -0
  113. package/dist/node/handlers/index.js +358 -0
  114. package/dist/node/handlers/saas.handlers.js +134 -0
  115. package/dist/node/index.js +3340 -0
  116. package/dist/node/presentations/index.js +290 -0
  117. package/dist/node/project/index.js +790 -0
  118. package/dist/node/project/project.entity.js +77 -0
  119. package/dist/node/project/project.enum.js +18 -0
  120. package/dist/node/project/project.event.js +103 -0
  121. package/dist/node/project/project.handler.js +178 -0
  122. package/dist/node/project/project.operations.js +372 -0
  123. package/dist/node/project/project.presentation.js +177 -0
  124. package/dist/node/project/project.schema.js +134 -0
  125. package/dist/node/saas-boilerplate.feature.js +88 -0
  126. package/dist/node/seeders/index.js +20 -0
  127. package/dist/node/settings/index.js +75 -0
  128. package/dist/node/settings/settings.entity.js +74 -0
  129. package/dist/node/settings/settings.enum.js +11 -0
  130. package/dist/node/shared/mock-data.js +104 -0
  131. package/dist/node/shared/overlay-types.js +0 -0
  132. package/dist/node/tests/operations.test-spec.js +112 -0
  133. package/dist/node/ui/SaasDashboard.js +988 -0
  134. package/dist/node/ui/SaasProjectList.js +162 -0
  135. package/dist/node/ui/SaasSettingsPanel.js +145 -0
  136. package/dist/node/ui/hooks/index.js +159 -0
  137. package/dist/node/ui/hooks/useProjectList.js +66 -0
  138. package/dist/node/ui/hooks/useProjectMutations.js +91 -0
  139. package/dist/node/ui/index.js +1808 -0
  140. package/dist/node/ui/modals/CreateProjectModal.js +153 -0
  141. package/dist/node/ui/modals/ProjectActionsModal.js +335 -0
  142. package/dist/node/ui/modals/index.js +487 -0
  143. package/dist/node/ui/overlays/demo-overlays.js +61 -0
  144. package/dist/node/ui/overlays/index.js +61 -0
  145. package/dist/node/ui/renderers/index.js +675 -0
  146. package/dist/node/ui/renderers/project-list.markdown.js +499 -0
  147. package/dist/node/ui/renderers/project-list.renderer.js +177 -0
  148. package/dist/presentations/index.d.ts +13 -14
  149. package/dist/presentations/index.d.ts.map +1 -1
  150. package/dist/presentations/index.js +289 -15
  151. package/dist/project/index.d.ts +11 -8
  152. package/dist/project/index.d.ts.map +1 -0
  153. package/dist/project/index.js +791 -9
  154. package/dist/project/project.entity.d.ts +23 -28
  155. package/dist/project/project.entity.d.ts.map +1 -1
  156. package/dist/project/project.entity.js +75 -82
  157. package/dist/project/project.enum.d.ts +2 -7
  158. package/dist/project/project.enum.d.ts.map +1 -1
  159. package/dist/project/project.enum.js +16 -23
  160. package/dist/project/project.event.d.ts +69 -75
  161. package/dist/project/project.event.d.ts.map +1 -1
  162. package/dist/project/project.event.js +95 -156
  163. package/dist/project/project.handler.d.ts +44 -47
  164. package/dist/project/project.handler.d.ts.map +1 -1
  165. package/dist/project/project.handler.js +168 -71
  166. package/dist/project/project.operations.d.ts +341 -347
  167. package/dist/project/project.operations.d.ts.map +1 -1
  168. package/dist/project/project.operations.js +366 -253
  169. package/dist/project/project.presentation.d.ts +2 -7
  170. package/dist/project/project.presentation.d.ts.map +1 -1
  171. package/dist/project/project.presentation.js +174 -61
  172. package/dist/project/project.schema.d.ts +191 -196
  173. package/dist/project/project.schema.d.ts.map +1 -1
  174. package/dist/project/project.schema.js +125 -205
  175. package/dist/saas-boilerplate.feature.d.ts +1 -6
  176. package/dist/saas-boilerplate.feature.d.ts.map +1 -1
  177. package/dist/saas-boilerplate.feature.js +87 -206
  178. package/dist/seeders/index.d.ts +4 -8
  179. package/dist/seeders/index.d.ts.map +1 -1
  180. package/dist/seeders/index.js +18 -16
  181. package/dist/settings/index.d.ts +6 -3
  182. package/dist/settings/index.d.ts.map +1 -0
  183. package/dist/settings/index.js +75 -3
  184. package/dist/settings/settings.entity.d.ts +23 -28
  185. package/dist/settings/settings.entity.d.ts.map +1 -1
  186. package/dist/settings/settings.entity.js +72 -75
  187. package/dist/settings/settings.enum.d.ts +1 -6
  188. package/dist/settings/settings.enum.d.ts.map +1 -1
  189. package/dist/settings/settings.enum.js +10 -19
  190. package/dist/shared/mock-data.d.ts +74 -77
  191. package/dist/shared/mock-data.d.ts.map +1 -1
  192. package/dist/shared/mock-data.js +102 -135
  193. package/dist/shared/overlay-types.d.ts +25 -28
  194. package/dist/shared/overlay-types.d.ts.map +1 -1
  195. package/dist/shared/overlay-types.js +1 -0
  196. package/dist/tests/operations.test-spec.d.ts +4 -9
  197. package/dist/tests/operations.test-spec.d.ts.map +1 -1
  198. package/dist/tests/operations.test-spec.js +108 -118
  199. package/dist/ui/SaasDashboard.d.ts +1 -6
  200. package/dist/ui/SaasDashboard.d.ts.map +1 -1
  201. package/dist/ui/SaasDashboard.js +977 -286
  202. package/dist/ui/SaasProjectList.d.ts +4 -11
  203. package/dist/ui/SaasProjectList.d.ts.map +1 -1
  204. package/dist/ui/SaasProjectList.js +159 -72
  205. package/dist/ui/SaasSettingsPanel.d.ts +1 -6
  206. package/dist/ui/SaasSettingsPanel.d.ts.map +1 -1
  207. package/dist/ui/SaasSettingsPanel.js +142 -134
  208. package/dist/ui/hooks/index.d.ts +3 -3
  209. package/dist/ui/hooks/index.d.ts.map +1 -0
  210. package/dist/ui/hooks/index.js +158 -4
  211. package/dist/ui/hooks/useProjectList.d.ts +26 -30
  212. package/dist/ui/hooks/useProjectList.d.ts.map +1 -1
  213. package/dist/ui/hooks/useProjectList.js +63 -71
  214. package/dist/ui/hooks/useProjectMutations.d.ts +20 -24
  215. package/dist/ui/hooks/useProjectMutations.d.ts.map +1 -1
  216. package/dist/ui/hooks/useProjectMutations.js +88 -142
  217. package/dist/ui/index.d.ts +8 -14
  218. package/dist/ui/index.d.ts.map +1 -0
  219. package/dist/ui/index.js +1809 -15
  220. package/dist/ui/modals/CreateProjectModal.d.ts +10 -19
  221. package/dist/ui/modals/CreateProjectModal.d.ts.map +1 -1
  222. package/dist/ui/modals/CreateProjectModal.js +150 -135
  223. package/dist/ui/modals/ProjectActionsModal.d.ts +20 -33
  224. package/dist/ui/modals/ProjectActionsModal.d.ts.map +1 -1
  225. package/dist/ui/modals/ProjectActionsModal.js +333 -289
  226. package/dist/ui/modals/index.d.ts +3 -3
  227. package/dist/ui/modals/index.d.ts.map +1 -0
  228. package/dist/ui/modals/index.js +487 -3
  229. package/dist/ui/overlays/demo-overlays.d.ts +10 -8
  230. package/dist/ui/overlays/demo-overlays.d.ts.map +1 -1
  231. package/dist/ui/overlays/demo-overlays.js +60 -68
  232. package/dist/ui/overlays/index.d.ts +2 -2
  233. package/dist/ui/overlays/index.d.ts.map +1 -0
  234. package/dist/ui/overlays/index.js +62 -3
  235. package/dist/ui/renderers/index.d.ts +3 -3
  236. package/dist/ui/renderers/index.d.ts.map +1 -0
  237. package/dist/ui/renderers/index.js +675 -3
  238. package/dist/ui/renderers/project-list.markdown.d.ts +15 -14
  239. package/dist/ui/renderers/project-list.markdown.d.ts.map +1 -1
  240. package/dist/ui/renderers/project-list.markdown.js +496 -146
  241. package/dist/ui/renderers/project-list.renderer.d.ts +6 -7
  242. package/dist/ui/renderers/project-list.renderer.d.ts.map +1 -1
  243. package/dist/ui/renderers/project-list.renderer.js +176 -15
  244. package/package.json +508 -98
  245. package/tsdown.config.js +1 -2
  246. package/.turbo/turbo-build$colon$bundle.log +0 -180
  247. package/dist/billing/billing.entity.js.map +0 -1
  248. package/dist/billing/billing.enum.js.map +0 -1
  249. package/dist/billing/billing.event.js.map +0 -1
  250. package/dist/billing/billing.handler.js.map +0 -1
  251. package/dist/billing/billing.operations.js.map +0 -1
  252. package/dist/billing/billing.presentation.js.map +0 -1
  253. package/dist/billing/billing.schema.js.map +0 -1
  254. package/dist/dashboard/dashboard.presentation.js.map +0 -1
  255. package/dist/docs/saas-boilerplate.docblock.js.map +0 -1
  256. package/dist/example.js.map +0 -1
  257. package/dist/handlers/saas.handlers.js.map +0 -1
  258. package/dist/index.js.map +0 -1
  259. package/dist/presentations/index.js.map +0 -1
  260. package/dist/project/project.entity.js.map +0 -1
  261. package/dist/project/project.enum.js.map +0 -1
  262. package/dist/project/project.event.js.map +0 -1
  263. package/dist/project/project.handler.js.map +0 -1
  264. package/dist/project/project.operations.js.map +0 -1
  265. package/dist/project/project.presentation.js.map +0 -1
  266. package/dist/project/project.schema.js.map +0 -1
  267. package/dist/saas-boilerplate.feature.js.map +0 -1
  268. package/dist/seeders/index.js.map +0 -1
  269. package/dist/settings/settings.entity.js.map +0 -1
  270. package/dist/settings/settings.enum.js.map +0 -1
  271. package/dist/shared/mock-data.js.map +0 -1
  272. package/dist/tests/operations.test-spec.js.map +0 -1
  273. package/dist/ui/SaasDashboard.js.map +0 -1
  274. package/dist/ui/SaasProjectList.js.map +0 -1
  275. package/dist/ui/SaasSettingsPanel.js.map +0 -1
  276. package/dist/ui/hooks/useProjectList.js.map +0 -1
  277. package/dist/ui/hooks/useProjectMutations.js.map +0 -1
  278. package/dist/ui/modals/CreateProjectModal.js.map +0 -1
  279. package/dist/ui/modals/ProjectActionsModal.js.map +0 -1
  280. package/dist/ui/overlays/demo-overlays.js.map +0 -1
  281. package/dist/ui/renderers/project-list.markdown.js.map +0 -1
  282. package/dist/ui/renderers/project-list.renderer.js.map +0 -1
  283. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,688 @@
1
+ // src/billing/billing.entity.ts
2
+ import {
3
+ defineEntity,
4
+ defineEntityEnum,
5
+ field,
6
+ index
7
+ } from "@contractspec/lib.schema";
8
+ var SubscriptionStatusEnum = defineEntityEnum({
9
+ name: "SubscriptionStatus",
10
+ values: ["TRIALING", "ACTIVE", "PAST_DUE", "CANCELED", "PAUSED"],
11
+ schema: "saas_app",
12
+ description: "Status of a subscription."
13
+ });
14
+ var SubscriptionEntity = defineEntity({
15
+ name: "Subscription",
16
+ description: "Organization subscription/plan information.",
17
+ schema: "saas_app",
18
+ map: "subscription",
19
+ fields: {
20
+ id: field.id(),
21
+ organizationId: field.foreignKey({ isUnique: true }),
22
+ planId: field.string({ description: "Plan identifier" }),
23
+ planName: field.string({ description: "Plan display name" }),
24
+ status: field.enum("SubscriptionStatus"),
25
+ currentPeriodStart: field.dateTime(),
26
+ currentPeriodEnd: field.dateTime(),
27
+ trialEndsAt: field.dateTime({ isOptional: true }),
28
+ cancelAtPeriodEnd: field.boolean({ default: false }),
29
+ canceledAt: field.dateTime({ isOptional: true }),
30
+ stripeSubscriptionId: field.string({ isOptional: true }),
31
+ stripeCustomerId: field.string({ isOptional: true }),
32
+ metadata: field.json({ isOptional: true }),
33
+ createdAt: field.createdAt(),
34
+ updatedAt: field.updatedAt()
35
+ },
36
+ enums: [SubscriptionStatusEnum]
37
+ });
38
+ var BillingUsageEntity = defineEntity({
39
+ name: "BillingUsage",
40
+ description: "Track usage of metered features.",
41
+ schema: "saas_app",
42
+ map: "billing_usage",
43
+ fields: {
44
+ id: field.id(),
45
+ organizationId: field.foreignKey(),
46
+ feature: field.string({
47
+ description: 'Feature being tracked (e.g., "api_calls", "storage_gb")'
48
+ }),
49
+ quantity: field.int({ description: "Usage quantity" }),
50
+ unit: field.string({
51
+ isOptional: true,
52
+ description: "Unit of measurement"
53
+ }),
54
+ billingPeriod: field.string({
55
+ description: 'Billing period (e.g., "2024-01")'
56
+ }),
57
+ recordedAt: field.dateTime({ description: "When usage was recorded" }),
58
+ sourceId: field.string({
59
+ isOptional: true,
60
+ description: "Source of usage (e.g., request ID)"
61
+ }),
62
+ sourceType: field.string({ isOptional: true }),
63
+ metadata: field.json({ isOptional: true })
64
+ },
65
+ indexes: [
66
+ index.on(["organizationId", "feature", "billingPeriod"]),
67
+ index.on(["organizationId", "recordedAt"])
68
+ ]
69
+ });
70
+ var UsageLimitEntity = defineEntity({
71
+ name: "UsageLimit",
72
+ description: "Usage limits per plan/organization.",
73
+ schema: "saas_app",
74
+ map: "usage_limit",
75
+ fields: {
76
+ id: field.id(),
77
+ planId: field.string({
78
+ isOptional: true,
79
+ description: "Plan this limit applies to"
80
+ }),
81
+ organizationId: field.string({
82
+ isOptional: true,
83
+ description: "Org-specific override"
84
+ }),
85
+ feature: field.string({ description: "Feature being limited" }),
86
+ limit: field.int({ description: "Maximum allowed usage" }),
87
+ resetPeriod: field.string({
88
+ default: '"monthly"',
89
+ description: "When limit resets"
90
+ }),
91
+ isSoftLimit: field.boolean({
92
+ default: false,
93
+ description: "Whether to warn vs block"
94
+ }),
95
+ overage: field.boolean({
96
+ default: false,
97
+ description: "Whether overage is allowed"
98
+ }),
99
+ overageRate: field.float({
100
+ isOptional: true,
101
+ description: "Cost per unit over limit"
102
+ }),
103
+ createdAt: field.createdAt(),
104
+ updatedAt: field.updatedAt()
105
+ },
106
+ indexes: [index.unique(["planId", "feature"])]
107
+ });
108
+
109
+ // src/billing/billing.enum.ts
110
+ import { defineEnum } from "@contractspec/lib.schema";
111
+ var SubscriptionStatusSchemaEnum = defineEnum("SubscriptionStatus", [
112
+ "TRIALING",
113
+ "ACTIVE",
114
+ "PAST_DUE",
115
+ "CANCELED",
116
+ "PAUSED"
117
+ ]);
118
+ var FeatureAccessReasonEnum = defineEnum("FeatureAccessReason", [
119
+ "included",
120
+ "limit_available",
121
+ "limit_reached",
122
+ "not_in_plan"
123
+ ]);
124
+
125
+ // src/billing/billing.event.ts
126
+ import { ScalarTypeEnum, defineSchemaModel } from "@contractspec/lib.schema";
127
+ import { defineEvent } from "@contractspec/lib.contracts";
128
+ var UsageRecordedPayload = defineSchemaModel({
129
+ name: "UsageRecordedPayload",
130
+ description: "Payload when feature usage is recorded",
131
+ fields: {
132
+ organizationId: {
133
+ type: ScalarTypeEnum.String_unsecure(),
134
+ isOptional: false
135
+ },
136
+ feature: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
137
+ quantity: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
138
+ billingPeriod: {
139
+ type: ScalarTypeEnum.String_unsecure(),
140
+ isOptional: false
141
+ },
142
+ recordedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false }
143
+ }
144
+ });
145
+ var UsageLimitReachedPayload = defineSchemaModel({
146
+ name: "UsageLimitReachedPayload",
147
+ description: "Payload when usage limit is reached",
148
+ fields: {
149
+ organizationId: {
150
+ type: ScalarTypeEnum.String_unsecure(),
151
+ isOptional: false
152
+ },
153
+ feature: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
154
+ limit: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
155
+ currentUsage: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
156
+ reachedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false }
157
+ }
158
+ });
159
+ var SubscriptionChangedPayload = defineSchemaModel({
160
+ name: "SubscriptionChangedPayload",
161
+ description: "Payload when subscription status changes",
162
+ fields: {
163
+ organizationId: {
164
+ type: ScalarTypeEnum.String_unsecure(),
165
+ isOptional: false
166
+ },
167
+ previousPlan: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
168
+ newPlan: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
169
+ previousStatus: {
170
+ type: ScalarTypeEnum.String_unsecure(),
171
+ isOptional: true
172
+ },
173
+ newStatus: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
174
+ changedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false }
175
+ }
176
+ });
177
+ var UsageRecordedEvent = defineEvent({
178
+ meta: {
179
+ key: "billing.usage.recorded",
180
+ version: "1.0.0",
181
+ description: "Feature usage has been recorded.",
182
+ stability: "stable",
183
+ owners: ["@saas-team"],
184
+ tags: ["billing", "usage", "recorded"]
185
+ },
186
+ payload: UsageRecordedPayload
187
+ });
188
+ var UsageLimitReachedEvent = defineEvent({
189
+ meta: {
190
+ key: "billing.limit.reached",
191
+ version: "1.0.0",
192
+ description: "Usage limit has been reached for a feature.",
193
+ stability: "stable",
194
+ owners: ["@saas-team"],
195
+ tags: ["billing", "limit", "reached"]
196
+ },
197
+ payload: UsageLimitReachedPayload
198
+ });
199
+ var SubscriptionChangedEvent = defineEvent({
200
+ meta: {
201
+ key: "billing.subscription.changed",
202
+ version: "1.0.0",
203
+ description: "Subscription status has changed.",
204
+ stability: "stable",
205
+ owners: ["@saas-team"],
206
+ tags: ["billing", "subscription", "changed"]
207
+ },
208
+ payload: SubscriptionChangedPayload
209
+ });
210
+
211
+ // src/shared/mock-data.ts
212
+ var MOCK_PROJECTS = [
213
+ {
214
+ id: "proj-1",
215
+ name: "Marketing Website",
216
+ description: "Main company website redesign project",
217
+ slug: "marketing-website",
218
+ organizationId: "demo-org",
219
+ createdBy: "user-1",
220
+ status: "ACTIVE",
221
+ isPublic: false,
222
+ tags: ["marketing", "website", "redesign"],
223
+ createdAt: new Date("2024-01-15T10:00:00Z"),
224
+ updatedAt: new Date("2024-03-20T14:30:00Z")
225
+ },
226
+ {
227
+ id: "proj-2",
228
+ name: "Mobile App v2",
229
+ description: "Next generation mobile application",
230
+ slug: "mobile-app-v2",
231
+ organizationId: "demo-org",
232
+ createdBy: "user-2",
233
+ status: "ACTIVE",
234
+ isPublic: false,
235
+ tags: ["mobile", "app", "v2"],
236
+ createdAt: new Date("2024-02-01T09:00:00Z"),
237
+ updatedAt: new Date("2024-04-05T11:15:00Z")
238
+ },
239
+ {
240
+ id: "proj-3",
241
+ name: "API Integration",
242
+ description: "Third-party API integration project",
243
+ slug: "api-integration",
244
+ organizationId: "demo-org",
245
+ createdBy: "user-1",
246
+ status: "DRAFT",
247
+ isPublic: false,
248
+ tags: ["api", "integration"],
249
+ createdAt: new Date("2024-03-10T08:00:00Z"),
250
+ updatedAt: new Date("2024-03-10T08:00:00Z")
251
+ },
252
+ {
253
+ id: "proj-4",
254
+ name: "Analytics Dashboard",
255
+ description: "Internal analytics and reporting dashboard",
256
+ slug: "analytics-dashboard",
257
+ organizationId: "demo-org",
258
+ createdBy: "user-3",
259
+ status: "ARCHIVED",
260
+ isPublic: true,
261
+ tags: ["analytics", "dashboard", "reporting"],
262
+ createdAt: new Date("2023-10-01T12:00:00Z"),
263
+ updatedAt: new Date("2024-02-28T16:45:00Z")
264
+ }
265
+ ];
266
+ var MOCK_SUBSCRIPTION = {
267
+ id: "sub-1",
268
+ organizationId: "demo-org",
269
+ planId: "pro",
270
+ planName: "Professional",
271
+ status: "ACTIVE",
272
+ currentPeriodStart: new Date("2024-04-01T00:00:00Z"),
273
+ currentPeriodEnd: new Date("2024-05-01T00:00:00Z"),
274
+ limits: {
275
+ projects: 25,
276
+ users: 10,
277
+ storage: 50,
278
+ apiCalls: 1e5
279
+ },
280
+ usage: {
281
+ projects: 4,
282
+ users: 5,
283
+ storage: 12.5,
284
+ apiCalls: 45230
285
+ }
286
+ };
287
+ var MOCK_USAGE_SUMMARY = {
288
+ organizationId: "demo-org",
289
+ period: "current_month",
290
+ apiCalls: {
291
+ total: 45230,
292
+ limit: 1e5,
293
+ percentUsed: 45.23
294
+ },
295
+ storage: {
296
+ totalGb: 12.5,
297
+ limitGb: 50,
298
+ percentUsed: 25
299
+ },
300
+ activeProjects: 4,
301
+ activeUsers: 5,
302
+ breakdown: [
303
+ { date: "2024-04-01", apiCalls: 3200, storageGb: 12.1 },
304
+ { date: "2024-04-02", apiCalls: 2800, storageGb: 12.2 },
305
+ { date: "2024-04-03", apiCalls: 4100, storageGb: 12.3 },
306
+ { date: "2024-04-04", apiCalls: 3600, storageGb: 12.4 },
307
+ { date: "2024-04-05", apiCalls: 3800, storageGb: 12.5 }
308
+ ]
309
+ };
310
+
311
+ // src/billing/billing.handler.ts
312
+ async function mockGetSubscriptionHandler() {
313
+ return MOCK_SUBSCRIPTION;
314
+ }
315
+ async function mockGetUsageSummaryHandler(input) {
316
+ return {
317
+ ...MOCK_USAGE_SUMMARY,
318
+ period: input.period ?? "current_month"
319
+ };
320
+ }
321
+ async function mockRecordUsageHandler(input) {
322
+ const currentUsage = MOCK_USAGE_SUMMARY.apiCalls.total;
323
+ const newTotal = currentUsage + input.quantity;
324
+ return {
325
+ recorded: true,
326
+ newTotal
327
+ };
328
+ }
329
+ async function mockCheckFeatureAccessHandler(input) {
330
+ const { feature } = input;
331
+ const featureMap = {
332
+ custom_domains: {
333
+ allowed: true
334
+ },
335
+ api_access: {
336
+ allowed: true,
337
+ currentUsage: MOCK_USAGE_SUMMARY.apiCalls.total,
338
+ limit: MOCK_USAGE_SUMMARY.apiCalls.limit
339
+ },
340
+ advanced_analytics: {
341
+ allowed: false,
342
+ reason: "FEATURE_NOT_INCLUDED"
343
+ },
344
+ unlimited_projects: {
345
+ allowed: false,
346
+ reason: "PLAN_LIMIT",
347
+ currentUsage: MOCK_SUBSCRIPTION.usage.projects,
348
+ limit: MOCK_SUBSCRIPTION.limits.projects
349
+ }
350
+ };
351
+ return featureMap[feature] ?? { allowed: true };
352
+ }
353
+
354
+ // src/billing/billing.schema.ts
355
+ import { defineSchemaModel as defineSchemaModel2, ScalarTypeEnum as ScalarTypeEnum2 } from "@contractspec/lib.schema";
356
+ var SubscriptionModel = defineSchemaModel2({
357
+ name: "Subscription",
358
+ description: "Organization subscription details",
359
+ fields: {
360
+ id: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
361
+ organizationId: {
362
+ type: ScalarTypeEnum2.String_unsecure(),
363
+ isOptional: false
364
+ },
365
+ planId: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
366
+ planName: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
367
+ status: { type: SubscriptionStatusSchemaEnum, isOptional: false },
368
+ currentPeriodStart: { type: ScalarTypeEnum2.DateTime(), isOptional: false },
369
+ currentPeriodEnd: { type: ScalarTypeEnum2.DateTime(), isOptional: false },
370
+ trialEndsAt: { type: ScalarTypeEnum2.DateTime(), isOptional: true },
371
+ cancelAtPeriodEnd: { type: ScalarTypeEnum2.Boolean(), isOptional: false }
372
+ }
373
+ });
374
+ var UsageSummaryModel = defineSchemaModel2({
375
+ name: "UsageSummary",
376
+ description: "Usage summary for a feature",
377
+ fields: {
378
+ feature: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
379
+ used: { type: ScalarTypeEnum2.Int_unsecure(), isOptional: false },
380
+ limit: { type: ScalarTypeEnum2.Int_unsecure(), isOptional: true },
381
+ unit: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true },
382
+ percentage: { type: ScalarTypeEnum2.Float_unsecure(), isOptional: true }
383
+ }
384
+ });
385
+ var RecordUsageInputModel = defineSchemaModel2({
386
+ name: "RecordUsageInput",
387
+ description: "Input for recording feature usage",
388
+ fields: {
389
+ feature: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
390
+ quantity: { type: ScalarTypeEnum2.Int_unsecure(), isOptional: false },
391
+ sourceId: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true },
392
+ sourceType: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true },
393
+ metadata: { type: ScalarTypeEnum2.JSONObject(), isOptional: true }
394
+ }
395
+ });
396
+ var RecordUsageOutputModel = defineSchemaModel2({
397
+ name: "RecordUsageOutput",
398
+ description: "Output for recording feature usage",
399
+ fields: {
400
+ recorded: { type: ScalarTypeEnum2.Boolean(), isOptional: false },
401
+ currentUsage: { type: ScalarTypeEnum2.Int_unsecure(), isOptional: false },
402
+ limit: { type: ScalarTypeEnum2.Int_unsecure(), isOptional: true },
403
+ limitReached: { type: ScalarTypeEnum2.Boolean(), isOptional: false }
404
+ }
405
+ });
406
+ var UsageRecordedPayloadModel = defineSchemaModel2({
407
+ name: "UsageRecordedPayload",
408
+ description: "Payload for usage.recorded event",
409
+ fields: {
410
+ feature: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false },
411
+ quantity: { type: ScalarTypeEnum2.Int_unsecure(), isOptional: false }
412
+ }
413
+ });
414
+ var GetUsageSummaryInputModel = defineSchemaModel2({
415
+ name: "GetUsageSummaryInput",
416
+ description: "Input for getting usage summary",
417
+ fields: {
418
+ billingPeriod: { type: ScalarTypeEnum2.String_unsecure(), isOptional: true }
419
+ }
420
+ });
421
+ var GetUsageSummaryOutputModel = defineSchemaModel2({
422
+ name: "GetUsageSummaryOutput",
423
+ description: "Output for usage summary",
424
+ fields: {
425
+ billingPeriod: {
426
+ type: ScalarTypeEnum2.String_unsecure(),
427
+ isOptional: false
428
+ },
429
+ usage: { type: UsageSummaryModel, isArray: true, isOptional: false }
430
+ }
431
+ });
432
+ var CheckFeatureAccessInputModel = defineSchemaModel2({
433
+ name: "CheckFeatureAccessInput",
434
+ description: "Input for checking feature access",
435
+ fields: {
436
+ feature: { type: ScalarTypeEnum2.String_unsecure(), isOptional: false }
437
+ }
438
+ });
439
+ var CheckFeatureAccessOutputModel = defineSchemaModel2({
440
+ name: "CheckFeatureAccessOutput",
441
+ description: "Output for feature access check",
442
+ fields: {
443
+ hasAccess: { type: ScalarTypeEnum2.Boolean(), isOptional: false },
444
+ reason: { type: FeatureAccessReasonEnum, isOptional: true },
445
+ upgradeUrl: { type: ScalarTypeEnum2.URL(), isOptional: true }
446
+ }
447
+ });
448
+
449
+ // src/billing/billing.operations.ts
450
+ import { defineCommand, defineQuery } from "@contractspec/lib.contracts";
451
+ var OWNERS = ["@example.saas-boilerplate"];
452
+ var GetSubscriptionContract = defineQuery({
453
+ meta: {
454
+ key: "saas.billing.subscription.get",
455
+ version: "1.0.0",
456
+ stability: "stable",
457
+ owners: [...OWNERS],
458
+ tags: ["saas", "billing", "subscription"],
459
+ description: "Get organization subscription status.",
460
+ goal: "Show current plan and billing status.",
461
+ context: "Billing page, plan upgrade prompts."
462
+ },
463
+ io: {
464
+ input: null,
465
+ output: SubscriptionModel
466
+ },
467
+ policy: {
468
+ auth: "user"
469
+ },
470
+ acceptance: {
471
+ scenarios: [
472
+ {
473
+ key: "get-subscription-happy-path",
474
+ given: ["Organization has active subscription"],
475
+ when: ["User requests subscription status"],
476
+ then: ["Subscription details are returned"]
477
+ }
478
+ ],
479
+ examples: [
480
+ {
481
+ key: "get-basic",
482
+ input: null,
483
+ output: {
484
+ plan: "pro",
485
+ status: "active",
486
+ currentPeriodEnd: "2025-02-01T00:00:00Z"
487
+ }
488
+ }
489
+ ]
490
+ }
491
+ });
492
+ var RecordUsageContract = defineCommand({
493
+ meta: {
494
+ key: "saas.billing.usage.record",
495
+ version: "1.0.0",
496
+ stability: "stable",
497
+ owners: [...OWNERS],
498
+ tags: ["saas", "billing", "usage"],
499
+ description: "Record usage of a metered feature.",
500
+ goal: "Track feature usage for billing.",
501
+ context: "Called by services when metered features are used."
502
+ },
503
+ io: {
504
+ input: RecordUsageInputModel,
505
+ output: RecordUsageOutputModel
506
+ },
507
+ policy: {
508
+ auth: "user"
509
+ },
510
+ sideEffects: {
511
+ emits: [
512
+ {
513
+ key: "billing.usage.recorded",
514
+ version: "1.0.0",
515
+ when: "Usage is recorded",
516
+ payload: UsageRecordedPayloadModel
517
+ }
518
+ ]
519
+ },
520
+ acceptance: {
521
+ scenarios: [
522
+ {
523
+ key: "record-usage-happy-path",
524
+ given: ["Organization exists"],
525
+ when: ["System records feature usage"],
526
+ then: ["Usage is recorded"]
527
+ }
528
+ ],
529
+ examples: [
530
+ {
531
+ key: "record-api-call",
532
+ input: { feature: "api_calls", quantity: 1, idempotencyKey: "abc-123" },
533
+ output: { recorded: true, currentUsage: 100 }
534
+ }
535
+ ]
536
+ }
537
+ });
538
+ var GetUsageSummaryContract = defineQuery({
539
+ meta: {
540
+ key: "saas.billing.usage.summary",
541
+ version: "1.0.0",
542
+ stability: "stable",
543
+ owners: [...OWNERS],
544
+ tags: ["saas", "billing", "usage"],
545
+ description: "Get usage summary for the current billing period.",
546
+ goal: "Show usage vs limits.",
547
+ context: "Billing page, usage dashboards."
548
+ },
549
+ io: {
550
+ input: GetUsageSummaryInputModel,
551
+ output: GetUsageSummaryOutputModel
552
+ },
553
+ policy: {
554
+ auth: "user"
555
+ },
556
+ acceptance: {
557
+ scenarios: [
558
+ {
559
+ key: "get-usage-happy-path",
560
+ given: ["Organization has usage history"],
561
+ when: ["User requests usage summary"],
562
+ then: ["Usage metrics are returned"]
563
+ }
564
+ ],
565
+ examples: [
566
+ {
567
+ key: "get-current-usage",
568
+ input: { period: "current" },
569
+ output: { features: [{ name: "api_calls", used: 100, limit: 1000 }] }
570
+ }
571
+ ]
572
+ }
573
+ });
574
+ var CheckFeatureAccessContract = defineQuery({
575
+ meta: {
576
+ key: "saas.billing.feature.check",
577
+ version: "1.0.0",
578
+ stability: "stable",
579
+ owners: [...OWNERS],
580
+ tags: ["saas", "billing", "feature"],
581
+ description: "Check if organization has access to a feature.",
582
+ goal: "Gate features based on plan/usage.",
583
+ context: "Feature access checks, upgrade prompts."
584
+ },
585
+ io: {
586
+ input: CheckFeatureAccessInputModel,
587
+ output: CheckFeatureAccessOutputModel
588
+ },
589
+ policy: {
590
+ auth: "user"
591
+ },
592
+ acceptance: {
593
+ scenarios: [
594
+ {
595
+ key: "check-access-granted",
596
+ given: ["Organization is on Pro plan"],
597
+ when: ["User checks access to Pro feature"],
598
+ then: ["Access is granted"]
599
+ }
600
+ ],
601
+ examples: [
602
+ {
603
+ key: "check-advanced-reports",
604
+ input: { feature: "advanced_reports" },
605
+ output: { hasAccess: true, reason: "Included in Pro plan" }
606
+ }
607
+ ]
608
+ }
609
+ });
610
+
611
+ // src/billing/billing.presentation.ts
612
+ import { definePresentation, StabilityEnum } from "@contractspec/lib.contracts";
613
+ var SubscriptionPresentation = definePresentation({
614
+ meta: {
615
+ key: "saas.billing.subscription",
616
+ version: "1.0.0",
617
+ title: "Subscription Status",
618
+ description: "Subscription status with plan info, limits, and current usage",
619
+ domain: "saas-boilerplate",
620
+ owners: ["@saas-team"],
621
+ tags: ["billing", "subscription"],
622
+ stability: StabilityEnum.Beta,
623
+ goal: "View subscription plan and status",
624
+ context: "Billing section"
625
+ },
626
+ source: {
627
+ type: "component",
628
+ framework: "react",
629
+ componentKey: "SubscriptionView"
630
+ },
631
+ targets: ["react", "markdown"],
632
+ policy: {
633
+ flags: ["saas.billing.enabled"]
634
+ }
635
+ });
636
+ var UsageDashboardPresentation = definePresentation({
637
+ meta: {
638
+ key: "saas.billing.usage",
639
+ version: "1.0.0",
640
+ title: "Usage Dashboard",
641
+ description: "Usage metrics and breakdown by resource type",
642
+ domain: "saas-boilerplate",
643
+ owners: ["@saas-team"],
644
+ tags: ["billing", "usage", "metrics"],
645
+ stability: StabilityEnum.Beta,
646
+ goal: "Monitor feature usage and limits",
647
+ context: "Billing section"
648
+ },
649
+ source: {
650
+ type: "component",
651
+ framework: "react",
652
+ componentKey: "UsageDashboardView"
653
+ },
654
+ targets: ["react", "markdown"],
655
+ policy: {
656
+ flags: ["saas.billing.enabled"]
657
+ }
658
+ });
659
+ export {
660
+ mockRecordUsageHandler,
661
+ mockGetUsageSummaryHandler,
662
+ mockGetSubscriptionHandler,
663
+ mockCheckFeatureAccessHandler,
664
+ UsageSummaryModel,
665
+ UsageRecordedPayloadModel,
666
+ UsageRecordedEvent,
667
+ UsageLimitReachedEvent,
668
+ UsageLimitEntity,
669
+ UsageDashboardPresentation,
670
+ SubscriptionStatusSchemaEnum,
671
+ SubscriptionStatusEnum,
672
+ SubscriptionPresentation,
673
+ SubscriptionModel,
674
+ SubscriptionEntity,
675
+ SubscriptionChangedEvent,
676
+ RecordUsageOutputModel,
677
+ RecordUsageInputModel,
678
+ RecordUsageContract,
679
+ GetUsageSummaryOutputModel,
680
+ GetUsageSummaryInputModel,
681
+ GetUsageSummaryContract,
682
+ GetSubscriptionContract,
683
+ FeatureAccessReasonEnum,
684
+ CheckFeatureAccessOutputModel,
685
+ CheckFeatureAccessInputModel,
686
+ CheckFeatureAccessContract,
687
+ BillingUsageEntity
688
+ };