@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
@@ -1,4 +1,676 @@
1
- import { projectListReactRenderer } from "./project-list.renderer.js";
2
- import { projectListMarkdownRenderer, saasBillingMarkdownRenderer, saasDashboardMarkdownRenderer } from "./project-list.markdown.js";
1
+ // @bun
2
+ // src/shared/mock-data.ts
3
+ var MOCK_PROJECTS = [
4
+ {
5
+ id: "proj-1",
6
+ name: "Marketing Website",
7
+ description: "Main company website redesign project",
8
+ slug: "marketing-website",
9
+ organizationId: "demo-org",
10
+ createdBy: "user-1",
11
+ status: "ACTIVE",
12
+ isPublic: false,
13
+ tags: ["marketing", "website", "redesign"],
14
+ createdAt: new Date("2024-01-15T10:00:00Z"),
15
+ updatedAt: new Date("2024-03-20T14:30:00Z")
16
+ },
17
+ {
18
+ id: "proj-2",
19
+ name: "Mobile App v2",
20
+ description: "Next generation mobile application",
21
+ slug: "mobile-app-v2",
22
+ organizationId: "demo-org",
23
+ createdBy: "user-2",
24
+ status: "ACTIVE",
25
+ isPublic: false,
26
+ tags: ["mobile", "app", "v2"],
27
+ createdAt: new Date("2024-02-01T09:00:00Z"),
28
+ updatedAt: new Date("2024-04-05T11:15:00Z")
29
+ },
30
+ {
31
+ id: "proj-3",
32
+ name: "API Integration",
33
+ description: "Third-party API integration project",
34
+ slug: "api-integration",
35
+ organizationId: "demo-org",
36
+ createdBy: "user-1",
37
+ status: "DRAFT",
38
+ isPublic: false,
39
+ tags: ["api", "integration"],
40
+ createdAt: new Date("2024-03-10T08:00:00Z"),
41
+ updatedAt: new Date("2024-03-10T08:00:00Z")
42
+ },
43
+ {
44
+ id: "proj-4",
45
+ name: "Analytics Dashboard",
46
+ description: "Internal analytics and reporting dashboard",
47
+ slug: "analytics-dashboard",
48
+ organizationId: "demo-org",
49
+ createdBy: "user-3",
50
+ status: "ARCHIVED",
51
+ isPublic: true,
52
+ tags: ["analytics", "dashboard", "reporting"],
53
+ createdAt: new Date("2023-10-01T12:00:00Z"),
54
+ updatedAt: new Date("2024-02-28T16:45:00Z")
55
+ }
56
+ ];
57
+ var MOCK_SUBSCRIPTION = {
58
+ id: "sub-1",
59
+ organizationId: "demo-org",
60
+ planId: "pro",
61
+ planName: "Professional",
62
+ status: "ACTIVE",
63
+ currentPeriodStart: new Date("2024-04-01T00:00:00Z"),
64
+ currentPeriodEnd: new Date("2024-05-01T00:00:00Z"),
65
+ limits: {
66
+ projects: 25,
67
+ users: 10,
68
+ storage: 50,
69
+ apiCalls: 1e5
70
+ },
71
+ usage: {
72
+ projects: 4,
73
+ users: 5,
74
+ storage: 12.5,
75
+ apiCalls: 45230
76
+ }
77
+ };
78
+ var MOCK_USAGE_SUMMARY = {
79
+ organizationId: "demo-org",
80
+ period: "current_month",
81
+ apiCalls: {
82
+ total: 45230,
83
+ limit: 1e5,
84
+ percentUsed: 45.23
85
+ },
86
+ storage: {
87
+ totalGb: 12.5,
88
+ limitGb: 50,
89
+ percentUsed: 25
90
+ },
91
+ activeProjects: 4,
92
+ activeUsers: 5,
93
+ breakdown: [
94
+ { date: "2024-04-01", apiCalls: 3200, storageGb: 12.1 },
95
+ { date: "2024-04-02", apiCalls: 2800, storageGb: 12.2 },
96
+ { date: "2024-04-03", apiCalls: 4100, storageGb: 12.3 },
97
+ { date: "2024-04-04", apiCalls: 3600, storageGb: 12.4 },
98
+ { date: "2024-04-05", apiCalls: 3800, storageGb: 12.5 }
99
+ ]
100
+ };
3
101
 
4
- export { projectListMarkdownRenderer, projectListReactRenderer, saasBillingMarkdownRenderer, saasDashboardMarkdownRenderer };
102
+ // src/billing/billing.handler.ts
103
+ async function mockGetSubscriptionHandler() {
104
+ return MOCK_SUBSCRIPTION;
105
+ }
106
+ async function mockGetUsageSummaryHandler(input) {
107
+ return {
108
+ ...MOCK_USAGE_SUMMARY,
109
+ period: input.period ?? "current_month"
110
+ };
111
+ }
112
+ async function mockRecordUsageHandler(input) {
113
+ const currentUsage = MOCK_USAGE_SUMMARY.apiCalls.total;
114
+ const newTotal = currentUsage + input.quantity;
115
+ return {
116
+ recorded: true,
117
+ newTotal
118
+ };
119
+ }
120
+ async function mockCheckFeatureAccessHandler(input) {
121
+ const { feature } = input;
122
+ const featureMap = {
123
+ custom_domains: {
124
+ allowed: true
125
+ },
126
+ api_access: {
127
+ allowed: true,
128
+ currentUsage: MOCK_USAGE_SUMMARY.apiCalls.total,
129
+ limit: MOCK_USAGE_SUMMARY.apiCalls.limit
130
+ },
131
+ advanced_analytics: {
132
+ allowed: false,
133
+ reason: "FEATURE_NOT_INCLUDED"
134
+ },
135
+ unlimited_projects: {
136
+ allowed: false,
137
+ reason: "PLAN_LIMIT",
138
+ currentUsage: MOCK_SUBSCRIPTION.usage.projects,
139
+ limit: MOCK_SUBSCRIPTION.limits.projects
140
+ }
141
+ };
142
+ return featureMap[feature] ?? { allowed: true };
143
+ }
144
+
145
+ // src/project/project.handler.ts
146
+ async function mockListProjectsHandler(input) {
147
+ const { status, search, limit = 20, offset = 0 } = input;
148
+ let filtered = [...MOCK_PROJECTS];
149
+ if (status && status !== "all") {
150
+ filtered = filtered.filter((p) => p.status === status);
151
+ }
152
+ if (search) {
153
+ const q = search.toLowerCase();
154
+ filtered = filtered.filter((p) => p.name.toLowerCase().includes(q) || p.description?.toLowerCase().includes(q) || p.tags.some((t) => t.toLowerCase().includes(q)));
155
+ }
156
+ filtered.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
157
+ const total = filtered.length;
158
+ const projects = filtered.slice(offset, offset + limit);
159
+ return {
160
+ projects,
161
+ total
162
+ };
163
+ }
164
+ async function mockGetProjectHandler(input) {
165
+ const project = MOCK_PROJECTS.find((p) => p.id === input.projectId);
166
+ if (!project) {
167
+ throw new Error("NOT_FOUND");
168
+ }
169
+ return project;
170
+ }
171
+ async function mockCreateProjectHandler(input, context) {
172
+ if (input.slug) {
173
+ const exists = MOCK_PROJECTS.some((p) => p.slug === input.slug);
174
+ if (exists) {
175
+ throw new Error("SLUG_EXISTS");
176
+ }
177
+ }
178
+ const now = new Date;
179
+ return {
180
+ id: `proj-${Date.now()}`,
181
+ name: input.name,
182
+ description: input.description,
183
+ slug: input.slug ?? input.name.toLowerCase().replace(/\s+/g, "-"),
184
+ organizationId: context.organizationId,
185
+ createdBy: context.userId,
186
+ status: "DRAFT",
187
+ isPublic: input.isPublic ?? false,
188
+ tags: input.tags ?? [],
189
+ createdAt: now,
190
+ updatedAt: now
191
+ };
192
+ }
193
+ async function mockUpdateProjectHandler(input) {
194
+ const project = MOCK_PROJECTS.find((p) => p.id === input.projectId);
195
+ if (!project) {
196
+ throw new Error("NOT_FOUND");
197
+ }
198
+ return {
199
+ ...project,
200
+ name: input.name ?? project.name,
201
+ description: input.description ?? project.description,
202
+ slug: input.slug ?? project.slug,
203
+ isPublic: input.isPublic ?? project.isPublic,
204
+ tags: input.tags ?? project.tags,
205
+ status: input.status ?? project.status,
206
+ updatedAt: new Date
207
+ };
208
+ }
209
+ async function mockDeleteProjectHandler(input) {
210
+ const project = MOCK_PROJECTS.find((p) => p.id === input.projectId);
211
+ if (!project) {
212
+ throw new Error("NOT_FOUND");
213
+ }
214
+ return { success: true };
215
+ }
216
+
217
+ // src/handlers/saas.handlers.ts
218
+ import { web } from "@contractspec/lib.runtime-sandbox";
219
+ var { generateId } = web;
220
+ function rowToProject(row) {
221
+ return {
222
+ id: row.id,
223
+ projectId: row.projectId,
224
+ organizationId: row.organizationId,
225
+ name: row.name,
226
+ description: row.description ?? undefined,
227
+ status: row.status,
228
+ tier: row.tier,
229
+ createdAt: new Date(row.createdAt),
230
+ updatedAt: new Date(row.updatedAt)
231
+ };
232
+ }
233
+ function rowToSubscription(row) {
234
+ return {
235
+ id: row.id,
236
+ projectId: row.projectId,
237
+ organizationId: row.organizationId,
238
+ plan: row.plan,
239
+ status: row.status,
240
+ billingCycle: row.billingCycle,
241
+ currentPeriodStart: new Date(row.currentPeriodStart),
242
+ currentPeriodEnd: new Date(row.currentPeriodEnd),
243
+ cancelAtPeriodEnd: Boolean(row.cancelAtPeriodEnd)
244
+ };
245
+ }
246
+ function createSaasHandlers(db) {
247
+ async function listProjects(input) {
248
+ const {
249
+ projectId,
250
+ organizationId,
251
+ status,
252
+ search,
253
+ limit = 20,
254
+ offset = 0
255
+ } = input;
256
+ let whereClause = "WHERE projectId = ?";
257
+ const params = [projectId];
258
+ if (organizationId) {
259
+ whereClause += " AND organizationId = ?";
260
+ params.push(organizationId);
261
+ }
262
+ if (status && status !== "all") {
263
+ whereClause += " AND status = ?";
264
+ params.push(status);
265
+ }
266
+ if (search) {
267
+ whereClause += " AND (name LIKE ? OR description LIKE ?)";
268
+ params.push(`%${search}%`, `%${search}%`);
269
+ }
270
+ const countResult = (await db.query(`SELECT COUNT(*) as count FROM saas_project ${whereClause}`, params)).rows;
271
+ const total = countResult[0]?.count ?? 0;
272
+ const rows = (await db.query(`SELECT * FROM saas_project ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`, [...params, limit, offset])).rows;
273
+ return {
274
+ items: rows.map(rowToProject),
275
+ total,
276
+ hasMore: offset + rows.length < total
277
+ };
278
+ }
279
+ async function getProject(id) {
280
+ const rows = (await db.query(`SELECT * FROM saas_project WHERE id = ?`, [id])).rows;
281
+ return rows[0] ? rowToProject(rows[0]) : null;
282
+ }
283
+ async function createProject(input, context) {
284
+ const id = generateId("proj");
285
+ const now = new Date().toISOString();
286
+ await db.execute(`INSERT INTO saas_project (id, projectId, organizationId, name, description, status, tier, createdAt, updatedAt)
287
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
288
+ id,
289
+ context.projectId,
290
+ context.organizationId,
291
+ input.name,
292
+ input.description ?? null,
293
+ "DRAFT",
294
+ input.tier ?? "FREE",
295
+ now,
296
+ now
297
+ ]);
298
+ const rows = (await db.query(`SELECT * FROM saas_project WHERE id = ?`, [id])).rows;
299
+ return rowToProject(rows[0]);
300
+ }
301
+ async function updateProject(input) {
302
+ const now = new Date().toISOString();
303
+ const updates = ["updatedAt = ?"];
304
+ const params = [now];
305
+ if (input.name !== undefined) {
306
+ updates.push("name = ?");
307
+ params.push(input.name);
308
+ }
309
+ if (input.description !== undefined) {
310
+ updates.push("description = ?");
311
+ params.push(input.description);
312
+ }
313
+ if (input.status !== undefined) {
314
+ updates.push("status = ?");
315
+ params.push(input.status);
316
+ }
317
+ params.push(input.id);
318
+ await db.execute(`UPDATE saas_project SET ${updates.join(", ")} WHERE id = ?`, params);
319
+ const rows = (await db.query(`SELECT * FROM saas_project WHERE id = ?`, [input.id])).rows;
320
+ if (!rows[0]) {
321
+ throw new Error("NOT_FOUND");
322
+ }
323
+ return rowToProject(rows[0]);
324
+ }
325
+ async function deleteProject(id) {
326
+ await db.execute(`DELETE FROM saas_project WHERE id = ?`, [id]);
327
+ }
328
+ async function getSubscription(input) {
329
+ let query = `SELECT * FROM saas_subscription WHERE projectId = ?`;
330
+ const params = [input.projectId];
331
+ if (input.organizationId) {
332
+ query += " AND organizationId = ?";
333
+ params.push(input.organizationId);
334
+ }
335
+ query += " LIMIT 1";
336
+ const rows = (await db.query(query, params)).rows;
337
+ return rows[0] ? rowToSubscription(rows[0]) : null;
338
+ }
339
+ return {
340
+ listProjects,
341
+ getProject,
342
+ createProject,
343
+ updateProject,
344
+ deleteProject,
345
+ getSubscription
346
+ };
347
+ }
348
+ // src/ui/hooks/useProjectList.ts
349
+ import { useCallback, useEffect, useMemo, useState } from "react";
350
+ import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
351
+ function useProjectList(options = {}) {
352
+ const { handlers, projectId } = useTemplateRuntime();
353
+ const { saas: saas2 } = handlers;
354
+ const [data, setData] = useState(null);
355
+ const [subscription, setSubscription] = useState(null);
356
+ const [loading, setLoading] = useState(true);
357
+ const [error, setError] = useState(null);
358
+ const [page, setPage] = useState(1);
359
+ const fetchData = useCallback(async () => {
360
+ setLoading(true);
361
+ setError(null);
362
+ try {
363
+ const [projectsResult, subscriptionResult] = await Promise.all([
364
+ saas2.listProjects({
365
+ projectId,
366
+ status: options.status === "all" ? undefined : options.status,
367
+ search: options.search,
368
+ limit: options.limit ?? 20,
369
+ offset: (page - 1) * (options.limit ?? 20)
370
+ }),
371
+ saas2.getSubscription({ projectId })
372
+ ]);
373
+ setData({
374
+ items: projectsResult.items,
375
+ total: projectsResult.total
376
+ });
377
+ setSubscription(subscriptionResult);
378
+ } catch (err) {
379
+ setError(err instanceof Error ? err : new Error("Unknown error"));
380
+ } finally {
381
+ setLoading(false);
382
+ }
383
+ }, [saas2, projectId, options.status, options.search, options.limit, page]);
384
+ useEffect(() => {
385
+ fetchData();
386
+ }, [fetchData]);
387
+ const stats = useMemo(() => {
388
+ if (!data)
389
+ return null;
390
+ const items = data.items;
391
+ return {
392
+ total: data.total,
393
+ activeCount: items.filter((p) => p.status === "ACTIVE").length,
394
+ draftCount: items.filter((p) => p.status === "DRAFT").length,
395
+ projectLimit: 10,
396
+ usagePercent: Math.min(data.total / 10 * 100, 100)
397
+ };
398
+ }, [data]);
399
+ return {
400
+ data,
401
+ subscription,
402
+ loading,
403
+ error,
404
+ stats,
405
+ page,
406
+ refetch: fetchData,
407
+ nextPage: () => setPage((p) => p + 1),
408
+ prevPage: () => page > 1 && setPage((p) => p - 1)
409
+ };
410
+ }
411
+
412
+ // src/ui/SaasProjectList.tsx
413
+ import {
414
+ StatCard,
415
+ StatCardGroup,
416
+ StatusChip,
417
+ EntityCard,
418
+ EmptyState,
419
+ LoaderBlock,
420
+ ErrorState,
421
+ Button
422
+ } from "@contractspec/lib.design-system";
423
+ import { jsxDEV } from "react/jsx-dev-runtime";
424
+ "use client";
425
+ function getStatusTone(status) {
426
+ switch (status) {
427
+ case "ACTIVE":
428
+ return "success";
429
+ case "DRAFT":
430
+ return "neutral";
431
+ case "ARCHIVED":
432
+ return "danger";
433
+ default:
434
+ return "neutral";
435
+ }
436
+ }
437
+ function SaasProjectList({
438
+ onProjectClick,
439
+ onCreateProject
440
+ }) {
441
+ const { data, loading, error, stats, refetch } = useProjectList();
442
+ if (loading && !data) {
443
+ return /* @__PURE__ */ jsxDEV(LoaderBlock, {
444
+ label: "Loading projects..."
445
+ }, undefined, false, undefined, this);
446
+ }
447
+ if (error) {
448
+ return /* @__PURE__ */ jsxDEV(ErrorState, {
449
+ title: "Failed to load projects",
450
+ description: error.message,
451
+ onRetry: refetch,
452
+ retryLabel: "Retry"
453
+ }, undefined, false, undefined, this);
454
+ }
455
+ if (!data?.items.length) {
456
+ return /* @__PURE__ */ jsxDEV(EmptyState, {
457
+ title: "No projects found",
458
+ description: "Create your first project to get started.",
459
+ primaryAction: onCreateProject ? /* @__PURE__ */ jsxDEV(Button, {
460
+ onPress: onCreateProject,
461
+ children: "Create Project"
462
+ }, undefined, false, undefined, this) : undefined
463
+ }, undefined, false, undefined, this);
464
+ }
465
+ return /* @__PURE__ */ jsxDEV("div", {
466
+ className: "space-y-6",
467
+ children: [
468
+ stats && /* @__PURE__ */ jsxDEV(StatCardGroup, {
469
+ children: [
470
+ /* @__PURE__ */ jsxDEV(StatCard, {
471
+ label: "Total Projects",
472
+ value: stats.total.toString()
473
+ }, undefined, false, undefined, this),
474
+ /* @__PURE__ */ jsxDEV(StatCard, {
475
+ label: "Active",
476
+ value: stats.activeCount.toString()
477
+ }, undefined, false, undefined, this),
478
+ /* @__PURE__ */ jsxDEV(StatCard, {
479
+ label: "Draft",
480
+ value: stats.draftCount.toString()
481
+ }, undefined, false, undefined, this)
482
+ ]
483
+ }, undefined, true, undefined, this),
484
+ /* @__PURE__ */ jsxDEV("div", {
485
+ className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
486
+ children: data.items.map((project) => /* @__PURE__ */ jsxDEV(EntityCard, {
487
+ cardTitle: project.name,
488
+ cardSubtitle: project.tier,
489
+ meta: /* @__PURE__ */ jsxDEV("p", {
490
+ className: "text-muted-foreground text-sm",
491
+ children: project.description
492
+ }, undefined, false, undefined, this),
493
+ chips: /* @__PURE__ */ jsxDEV(StatusChip, {
494
+ tone: getStatusTone(project.status),
495
+ label: project.status
496
+ }, undefined, false, undefined, this),
497
+ footer: /* @__PURE__ */ jsxDEV("span", {
498
+ className: "text-muted-foreground text-xs",
499
+ children: project.updatedAt.toLocaleDateString()
500
+ }, undefined, false, undefined, this),
501
+ onClick: onProjectClick ? () => onProjectClick(project.id) : undefined
502
+ }, project.id, false, undefined, this))
503
+ }, undefined, false, undefined, this)
504
+ ]
505
+ }, undefined, true, undefined, this);
506
+ }
507
+
508
+ // src/ui/renderers/project-list.renderer.tsx
509
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
510
+ var projectListReactRenderer = {
511
+ target: "react",
512
+ render: async (desc, _ctx) => {
513
+ if (desc.source.type !== "component") {
514
+ throw new Error("Invalid source type");
515
+ }
516
+ if (desc.source.componentKey !== "SaasProjectListView") {
517
+ throw new Error(`Unknown component: ${desc.source.componentKey}`);
518
+ }
519
+ return /* @__PURE__ */ jsxDEV2(SaasProjectList, {}, undefined, false, undefined, this);
520
+ }
521
+ };
522
+
523
+ // src/ui/renderers/project-list.markdown.ts
524
+ var projectListMarkdownRenderer = {
525
+ target: "markdown",
526
+ render: async (desc, _ctx) => {
527
+ if (desc.source.type !== "component" || desc.source.componentKey !== "ProjectListView") {
528
+ throw new Error("projectListMarkdownRenderer: not ProjectListView");
529
+ }
530
+ const data = await mockListProjectsHandler({
531
+ limit: 20,
532
+ offset: 0
533
+ });
534
+ const items = data.projects ?? data.items ?? [];
535
+ const lines = [
536
+ "# Projects",
537
+ "",
538
+ `**Total**: ${data.total} projects`,
539
+ ""
540
+ ];
541
+ if (items.length === 0) {
542
+ lines.push("_No projects found._");
543
+ } else {
544
+ lines.push("| Status | Project | Description |");
545
+ lines.push("|--------|---------|-------------|");
546
+ for (const project of items) {
547
+ const status = project.status === "ACTIVE" ? "\u2705" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "\u23F8\uFE0F";
548
+ lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
549
+ }
550
+ }
551
+ return {
552
+ mimeType: "text/markdown",
553
+ body: lines.join(`
554
+ `)
555
+ };
556
+ }
557
+ };
558
+ var saasDashboardMarkdownRenderer = {
559
+ target: "markdown",
560
+ render: async (desc, _ctx) => {
561
+ if (desc.source.type !== "component" || desc.source.componentKey !== "SaasDashboard") {
562
+ throw new Error("saasDashboardMarkdownRenderer: not SaasDashboard");
563
+ }
564
+ const [projectsData, subscription] = await Promise.all([
565
+ mockListProjectsHandler({ limit: 50 }),
566
+ mockGetSubscriptionHandler()
567
+ ]);
568
+ const projects = projectsData.projects ?? [];
569
+ const activeProjects = projects.filter((p) => p.status === "ACTIVE").length;
570
+ const archivedProjects = projects.filter((p) => p.status === "ARCHIVED").length;
571
+ const lines = [
572
+ "# SaaS Dashboard",
573
+ "",
574
+ "> Organization overview and usage summary",
575
+ "",
576
+ "## Summary",
577
+ "",
578
+ "| Metric | Value |",
579
+ "|--------|-------|",
580
+ `| Total Projects | ${projectsData.total} |`,
581
+ `| Active Projects | ${activeProjects} |`,
582
+ `| Archived Projects | ${archivedProjects} |`,
583
+ `| Subscription Plan | ${subscription.planName} |`,
584
+ `| Subscription Status | ${subscription.status} |`,
585
+ "",
586
+ "## Projects",
587
+ ""
588
+ ];
589
+ if (projects.length === 0) {
590
+ lines.push("_No projects yet._");
591
+ } else {
592
+ lines.push("| Status | Project | Description |");
593
+ lines.push("|--------|---------|-------------|");
594
+ for (const project of projects.slice(0, 10)) {
595
+ const status = project.status === "ACTIVE" ? "\u2705" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "\u23F8\uFE0F";
596
+ lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
597
+ }
598
+ if (projects.length > 10) {
599
+ lines.push(`| ... | ... | _${projectsData.total - 10} more projects_ |`);
600
+ }
601
+ }
602
+ lines.push("");
603
+ lines.push("## Subscription");
604
+ lines.push("");
605
+ lines.push(`- **Plan**: ${subscription.planName}`);
606
+ lines.push(`- **Status**: ${subscription.status}`);
607
+ if (subscription.currentPeriodEnd) {
608
+ lines.push(`- **Period End**: ${new Date(subscription.currentPeriodEnd).toLocaleDateString()}`);
609
+ }
610
+ return {
611
+ mimeType: "text/markdown",
612
+ body: lines.join(`
613
+ `)
614
+ };
615
+ }
616
+ };
617
+ var saasBillingMarkdownRenderer = {
618
+ target: "markdown",
619
+ render: async (desc, _ctx) => {
620
+ if (desc.source.type !== "component" || desc.source.componentKey !== "SubscriptionView") {
621
+ throw new Error("saasBillingMarkdownRenderer: not SubscriptionView");
622
+ }
623
+ const subscription = await mockGetSubscriptionHandler();
624
+ const lines = [
625
+ "# Billing & Subscription",
626
+ "",
627
+ "> Current subscription details and billing information",
628
+ "",
629
+ "## Subscription Details",
630
+ "",
631
+ "| Property | Value |",
632
+ "|----------|-------|",
633
+ `| Plan | ${subscription.planName} |`,
634
+ `| Status | ${subscription.status} |`,
635
+ `| ID | ${subscription.id} |`,
636
+ `| Period Start | ${new Date(subscription.currentPeriodStart).toLocaleDateString()} |`,
637
+ `| Period End | ${new Date(subscription.currentPeriodEnd).toLocaleDateString()} |`
638
+ ];
639
+ lines.push("");
640
+ lines.push("## Plan Limits");
641
+ lines.push("");
642
+ lines.push(`- **Projects**: ${subscription.limits.projects}`);
643
+ lines.push(`- **Users**: ${subscription.limits.users}`);
644
+ lines.push("");
645
+ lines.push("## Plan Features");
646
+ lines.push("");
647
+ if (subscription.planName.toLowerCase().includes("free")) {
648
+ lines.push("- \u2705 Up to 3 projects");
649
+ lines.push("- \u2705 Basic support");
650
+ lines.push("- \u274C Priority support");
651
+ lines.push("- \u274C Advanced analytics");
652
+ } else if (subscription.planName.toLowerCase().includes("pro")) {
653
+ lines.push("- \u2705 Unlimited projects");
654
+ lines.push("- \u2705 Priority support");
655
+ lines.push("- \u2705 Advanced analytics");
656
+ lines.push("- \u274C Custom integrations");
657
+ } else {
658
+ lines.push("- \u2705 Unlimited projects");
659
+ lines.push("- \u2705 Priority support");
660
+ lines.push("- \u2705 Advanced analytics");
661
+ lines.push("- \u2705 Custom integrations");
662
+ lines.push("- \u2705 Dedicated support");
663
+ }
664
+ return {
665
+ mimeType: "text/markdown",
666
+ body: lines.join(`
667
+ `)
668
+ };
669
+ }
670
+ };
671
+ export {
672
+ saasDashboardMarkdownRenderer,
673
+ saasBillingMarkdownRenderer,
674
+ projectListReactRenderer,
675
+ projectListMarkdownRenderer
676
+ };