@contractspec/example.saas-boilerplate 0.0.0-canary-20260113170453

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 (226) hide show
  1. package/.turbo/turbo-build$colon$bundle.log +188 -0
  2. package/.turbo/turbo-build.log +189 -0
  3. package/CHANGELOG.md +440 -0
  4. package/LICENSE +21 -0
  5. package/README.md +155 -0
  6. package/dist/billing/billing.entity.d.ts +61 -0
  7. package/dist/billing/billing.entity.d.ts.map +1 -0
  8. package/dist/billing/billing.entity.js +122 -0
  9. package/dist/billing/billing.entity.js.map +1 -0
  10. package/dist/billing/billing.enum.d.ts +16 -0
  11. package/dist/billing/billing.enum.d.ts.map +1 -0
  12. package/dist/billing/billing.enum.js +27 -0
  13. package/dist/billing/billing.enum.js.map +1 -0
  14. package/dist/billing/billing.event.d.ts +86 -0
  15. package/dist/billing/billing.event.d.ts.map +1 -0
  16. package/dist/billing/billing.event.js +153 -0
  17. package/dist/billing/billing.event.js.map +1 -0
  18. package/dist/billing/billing.handler.d.ts +82 -0
  19. package/dist/billing/billing.handler.d.ts.map +1 -0
  20. package/dist/billing/billing.handler.js +58 -0
  21. package/dist/billing/billing.handler.js.map +1 -0
  22. package/dist/billing/billing.operations.d.ts +166 -0
  23. package/dist/billing/billing.operations.d.ts.map +1 -0
  24. package/dist/billing/billing.operations.js +181 -0
  25. package/dist/billing/billing.operations.js.map +1 -0
  26. package/dist/billing/billing.presentation.d.ts +14 -0
  27. package/dist/billing/billing.presentation.d.ts.map +1 -0
  28. package/dist/billing/billing.presentation.js +59 -0
  29. package/dist/billing/billing.presentation.js.map +1 -0
  30. package/dist/billing/billing.schema.d.ts +201 -0
  31. package/dist/billing/billing.schema.d.ts.map +1 -0
  32. package/dist/billing/billing.schema.js +214 -0
  33. package/dist/billing/billing.schema.js.map +1 -0
  34. package/dist/billing/index.d.ts +8 -0
  35. package/dist/billing/index.js +9 -0
  36. package/dist/dashboard/dashboard.presentation.d.ts +14 -0
  37. package/dist/dashboard/dashboard.presentation.d.ts.map +1 -0
  38. package/dist/dashboard/dashboard.presentation.js +55 -0
  39. package/dist/dashboard/dashboard.presentation.js.map +1 -0
  40. package/dist/dashboard/index.d.ts +2 -0
  41. package/dist/dashboard/index.js +3 -0
  42. package/dist/docs/index.d.ts +1 -0
  43. package/dist/docs/index.js +1 -0
  44. package/dist/docs/saas-boilerplate.docblock.d.ts +1 -0
  45. package/dist/docs/saas-boilerplate.docblock.js +100 -0
  46. package/dist/docs/saas-boilerplate.docblock.js.map +1 -0
  47. package/dist/example.d.ts +7 -0
  48. package/dist/example.d.ts.map +1 -0
  49. package/dist/example.js +53 -0
  50. package/dist/example.js.map +1 -0
  51. package/dist/handlers/index.d.ts +4 -0
  52. package/dist/handlers/index.js +5 -0
  53. package/dist/handlers/saas.handlers.d.ts +68 -0
  54. package/dist/handlers/saas.handlers.d.ts.map +1 -0
  55. package/dist/handlers/saas.handlers.js +148 -0
  56. package/dist/handlers/saas.handlers.js.map +1 -0
  57. package/dist/index.d.ts +54 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +81 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/presentations/index.d.ts +17 -0
  62. package/dist/presentations/index.d.ts.map +1 -0
  63. package/dist/presentations/index.js +17 -0
  64. package/dist/presentations/index.js.map +1 -0
  65. package/dist/project/index.d.ts +8 -0
  66. package/dist/project/index.js +9 -0
  67. package/dist/project/project.entity.d.ts +40 -0
  68. package/dist/project/project.entity.d.ts.map +1 -0
  69. package/dist/project/project.entity.js +85 -0
  70. package/dist/project/project.entity.js.map +1 -0
  71. package/dist/project/project.enum.d.ts +16 -0
  72. package/dist/project/project.enum.d.ts.map +1 -0
  73. package/dist/project/project.enum.js +26 -0
  74. package/dist/project/project.enum.js.map +1 -0
  75. package/dist/project/project.event.d.ts +92 -0
  76. package/dist/project/project.event.d.ts.map +1 -0
  77. package/dist/project/project.event.js +165 -0
  78. package/dist/project/project.event.js.map +1 -0
  79. package/dist/project/project.handler.d.ts +72 -0
  80. package/dist/project/project.handler.d.ts.map +1 -0
  81. package/dist/project/project.handler.js +82 -0
  82. package/dist/project/project.handler.js.map +1 -0
  83. package/dist/project/project.operations.d.ts +419 -0
  84. package/dist/project/project.operations.d.ts.map +1 -0
  85. package/dist/project/project.operations.js +260 -0
  86. package/dist/project/project.operations.js.map +1 -0
  87. package/dist/project/project.presentation.d.ts +14 -0
  88. package/dist/project/project.presentation.d.ts.map +1 -0
  89. package/dist/project/project.presentation.js +65 -0
  90. package/dist/project/project.presentation.js.map +1 -0
  91. package/dist/project/project.schema.d.ts +235 -0
  92. package/dist/project/project.schema.d.ts.map +1 -0
  93. package/dist/project/project.schema.js +215 -0
  94. package/dist/project/project.schema.js.map +1 -0
  95. package/dist/saas-boilerplate.feature.d.ts +12 -0
  96. package/dist/saas-boilerplate.feature.d.ts.map +1 -0
  97. package/dist/saas-boilerplate.feature.js +208 -0
  98. package/dist/saas-boilerplate.feature.js.map +1 -0
  99. package/dist/seeders/index.d.ts +10 -0
  100. package/dist/seeders/index.d.ts.map +1 -0
  101. package/dist/seeders/index.js +19 -0
  102. package/dist/seeders/index.js.map +1 -0
  103. package/dist/settings/index.d.ts +3 -0
  104. package/dist/settings/index.js +4 -0
  105. package/dist/settings/settings.entity.d.ts +37 -0
  106. package/dist/settings/settings.entity.d.ts.map +1 -0
  107. package/dist/settings/settings.entity.js +78 -0
  108. package/dist/settings/settings.entity.js.map +1 -0
  109. package/dist/settings/settings.enum.d.ts +10 -0
  110. package/dist/settings/settings.enum.d.ts.map +1 -0
  111. package/dist/settings/settings.enum.js +21 -0
  112. package/dist/settings/settings.enum.js.map +1 -0
  113. package/dist/shared/mock-data.d.ts +86 -0
  114. package/dist/shared/mock-data.d.ts.map +1 -0
  115. package/dist/shared/mock-data.js +138 -0
  116. package/dist/shared/mock-data.js.map +1 -0
  117. package/dist/shared/overlay-types.d.ts +34 -0
  118. package/dist/shared/overlay-types.d.ts.map +1 -0
  119. package/dist/shared/overlay-types.js +0 -0
  120. package/dist/tests/operations.test-spec.d.ts +10 -0
  121. package/dist/tests/operations.test-spec.d.ts.map +1 -0
  122. package/dist/tests/operations.test-spec.js +123 -0
  123. package/dist/tests/operations.test-spec.js.map +1 -0
  124. package/dist/ui/SaasDashboard.d.ts +7 -0
  125. package/dist/ui/SaasDashboard.d.ts.map +1 -0
  126. package/dist/ui/SaasDashboard.js +298 -0
  127. package/dist/ui/SaasDashboard.js.map +1 -0
  128. package/dist/ui/SaasProjectList.d.ts +14 -0
  129. package/dist/ui/SaasProjectList.d.ts.map +1 -0
  130. package/dist/ui/SaasProjectList.js +76 -0
  131. package/dist/ui/SaasProjectList.js.map +1 -0
  132. package/dist/ui/SaasSettingsPanel.d.ts +7 -0
  133. package/dist/ui/SaasSettingsPanel.d.ts.map +1 -0
  134. package/dist/ui/SaasSettingsPanel.js +138 -0
  135. package/dist/ui/SaasSettingsPanel.js.map +1 -0
  136. package/dist/ui/hooks/index.d.ts +3 -0
  137. package/dist/ui/hooks/index.js +6 -0
  138. package/dist/ui/hooks/useProjectList.d.ts +34 -0
  139. package/dist/ui/hooks/useProjectList.d.ts.map +1 -0
  140. package/dist/ui/hooks/useProjectList.js +75 -0
  141. package/dist/ui/hooks/useProjectList.js.map +1 -0
  142. package/dist/ui/hooks/useProjectMutations.d.ts +28 -0
  143. package/dist/ui/hooks/useProjectMutations.d.ts.map +1 -0
  144. package/dist/ui/hooks/useProjectMutations.js +146 -0
  145. package/dist/ui/hooks/useProjectMutations.js.map +1 -0
  146. package/dist/ui/index.d.ts +14 -0
  147. package/dist/ui/index.js +15 -0
  148. package/dist/ui/modals/CreateProjectModal.d.ts +23 -0
  149. package/dist/ui/modals/CreateProjectModal.d.ts.map +1 -0
  150. package/dist/ui/modals/CreateProjectModal.js +139 -0
  151. package/dist/ui/modals/CreateProjectModal.js.map +1 -0
  152. package/dist/ui/modals/ProjectActionsModal.d.ts +38 -0
  153. package/dist/ui/modals/ProjectActionsModal.d.ts.map +1 -0
  154. package/dist/ui/modals/ProjectActionsModal.js +292 -0
  155. package/dist/ui/modals/ProjectActionsModal.js.map +1 -0
  156. package/dist/ui/modals/index.d.ts +3 -0
  157. package/dist/ui/modals/index.js +4 -0
  158. package/dist/ui/overlays/demo-overlays.d.ts +19 -0
  159. package/dist/ui/overlays/demo-overlays.d.ts.map +1 -0
  160. package/dist/ui/overlays/demo-overlays.js +70 -0
  161. package/dist/ui/overlays/demo-overlays.js.map +1 -0
  162. package/dist/ui/overlays/index.d.ts +2 -0
  163. package/dist/ui/overlays/index.js +3 -0
  164. package/dist/ui/renderers/index.d.ts +3 -0
  165. package/dist/ui/renderers/index.js +4 -0
  166. package/dist/ui/renderers/project-list.markdown.d.ts +31 -0
  167. package/dist/ui/renderers/project-list.markdown.d.ts.map +1 -0
  168. package/dist/ui/renderers/project-list.markdown.js +148 -0
  169. package/dist/ui/renderers/project-list.markdown.js.map +1 -0
  170. package/dist/ui/renderers/project-list.renderer.d.ts +9 -0
  171. package/dist/ui/renderers/project-list.renderer.d.ts.map +1 -0
  172. package/dist/ui/renderers/project-list.renderer.js +17 -0
  173. package/dist/ui/renderers/project-list.renderer.js.map +1 -0
  174. package/example.ts +1 -0
  175. package/package.json +135 -0
  176. package/src/billing/billing.entity.ts +158 -0
  177. package/src/billing/billing.enum.ts +23 -0
  178. package/src/billing/billing.event.ts +108 -0
  179. package/src/billing/billing.handler.ts +137 -0
  180. package/src/billing/billing.operations.ts +187 -0
  181. package/src/billing/billing.presentation.ts +56 -0
  182. package/src/billing/billing.schema.ts +133 -0
  183. package/src/billing/index.ts +64 -0
  184. package/src/dashboard/dashboard.presentation.ts +56 -0
  185. package/src/dashboard/index.ts +8 -0
  186. package/src/docs/index.ts +1 -0
  187. package/src/docs/saas-boilerplate.docblock.ts +98 -0
  188. package/src/example.ts +38 -0
  189. package/src/handlers/index.ts +23 -0
  190. package/src/handlers/saas.handlers.ts +300 -0
  191. package/src/index.ts +76 -0
  192. package/src/presentations/index.ts +36 -0
  193. package/src/project/index.ts +66 -0
  194. package/src/project/project.entity.ts +93 -0
  195. package/src/project/project.enum.ts +22 -0
  196. package/src/project/project.event.ts +128 -0
  197. package/src/project/project.handler.ts +168 -0
  198. package/src/project/project.operations.ts +272 -0
  199. package/src/project/project.presentation.ts +58 -0
  200. package/src/project/project.schema.ts +147 -0
  201. package/src/saas-boilerplate.feature.ts +113 -0
  202. package/src/seeders/index.ts +28 -0
  203. package/src/settings/index.ts +9 -0
  204. package/src/settings/settings.entity.ts +89 -0
  205. package/src/settings/settings.enum.ts +11 -0
  206. package/src/shared/mock-data.ts +110 -0
  207. package/src/shared/overlay-types.ts +39 -0
  208. package/src/tests/operations.test-spec.ts +109 -0
  209. package/src/ui/SaasDashboard.tsx +325 -0
  210. package/src/ui/SaasProjectList.tsx +113 -0
  211. package/src/ui/SaasSettingsPanel.tsx +96 -0
  212. package/src/ui/hooks/index.ts +10 -0
  213. package/src/ui/hooks/useProjectList.ts +95 -0
  214. package/src/ui/hooks/useProjectMutations.ts +166 -0
  215. package/src/ui/index.ts +18 -0
  216. package/src/ui/modals/CreateProjectModal.tsx +176 -0
  217. package/src/ui/modals/ProjectActionsModal.tsx +346 -0
  218. package/src/ui/modals/index.ts +2 -0
  219. package/src/ui/overlays/demo-overlays.ts +74 -0
  220. package/src/ui/overlays/index.ts +1 -0
  221. package/src/ui/renderers/index.ts +7 -0
  222. package/src/ui/renderers/project-list.markdown.ts +239 -0
  223. package/src/ui/renderers/project-list.renderer.tsx +22 -0
  224. package/tsconfig.json +10 -0
  225. package/tsconfig.tsbuildinfo +1 -0
  226. package/tsdown.config.js +7 -0
@@ -0,0 +1,272 @@
1
+ import {
2
+ defineCommand,
3
+ defineQuery,
4
+ } from '@contractspec/lib.contracts/operations';
5
+ import {
6
+ CreateProjectInputModel,
7
+ DeleteProjectInputModel,
8
+ DeleteProjectOutputModel,
9
+ GetProjectInputModel,
10
+ ListProjectsInputModel,
11
+ ListProjectsOutputModel,
12
+ ProjectDeletedPayloadModel,
13
+ ProjectModel,
14
+ UpdateProjectInputModel,
15
+ } from './project.schema';
16
+
17
+ const OWNERS = ['example.saas-boilerplate'] as const;
18
+
19
+ /**
20
+ * Create a new project.
21
+ */
22
+ export const CreateProjectContract = defineCommand({
23
+ meta: {
24
+ key: 'saas.project.create',
25
+ version: '1.0.0',
26
+ stability: 'stable',
27
+ owners: [...OWNERS],
28
+ tags: ['saas', 'project', 'create'],
29
+ description: 'Create a new project in the organization.',
30
+ goal: 'Allow users to create projects for organizing work.',
31
+ context: 'Called from project creation UI or API.',
32
+ },
33
+ io: {
34
+ input: CreateProjectInputModel,
35
+ output: ProjectModel,
36
+ errors: {
37
+ SLUG_EXISTS: {
38
+ description: 'A project with this slug already exists',
39
+ http: 409,
40
+ gqlCode: 'SLUG_EXISTS',
41
+ when: 'Slug is already taken in the organization',
42
+ },
43
+ LIMIT_REACHED: {
44
+ description: 'Project limit reached for this plan',
45
+ http: 403,
46
+ gqlCode: 'LIMIT_REACHED',
47
+ when: 'Organization has reached project limit',
48
+ },
49
+ },
50
+ },
51
+ policy: {
52
+ auth: 'user',
53
+ },
54
+ sideEffects: {
55
+ emits: [
56
+ {
57
+ key: 'project.created',
58
+ version: '1.0.0',
59
+ when: 'Project is created',
60
+ payload: ProjectModel,
61
+ },
62
+ ],
63
+ audit: ['project.created'],
64
+ },
65
+ acceptance: {
66
+ scenarios: [
67
+ {
68
+ key: 'create-project-happy-path',
69
+ given: ['User is authenticated'],
70
+ when: ['User creates project'],
71
+ then: ['Project is created', 'ProjectCreated event is emitted'],
72
+ },
73
+ ],
74
+ examples: [
75
+ {
76
+ key: 'create-basic',
77
+ input: { name: 'Website Redesign', slug: 'website-redesign' },
78
+ output: { id: 'proj-123', name: 'Website Redesign', isArchived: false },
79
+ },
80
+ ],
81
+ },
82
+ });
83
+
84
+ /**
85
+ * Get project by ID.
86
+ */
87
+ export const GetProjectContract = defineQuery({
88
+ meta: {
89
+ key: 'saas.project.get',
90
+ version: '1.0.0',
91
+ stability: 'stable',
92
+ owners: [...OWNERS],
93
+ tags: ['saas', 'project', 'get'],
94
+ description: 'Get a project by ID.',
95
+ goal: 'Retrieve project details.',
96
+ context: 'Project detail page, API calls.',
97
+ },
98
+ io: {
99
+ input: GetProjectInputModel,
100
+ output: ProjectModel,
101
+ errors: {
102
+ NOT_FOUND: {
103
+ description: 'Project not found',
104
+ http: 404,
105
+ gqlCode: 'NOT_FOUND',
106
+ when: 'Project ID is invalid or user lacks access',
107
+ },
108
+ },
109
+ },
110
+ policy: {
111
+ auth: 'user',
112
+ },
113
+ acceptance: {
114
+ scenarios: [
115
+ {
116
+ key: 'get-project-happy-path',
117
+ given: ['Project exists'],
118
+ when: ['User requests project'],
119
+ then: ['Project details are returned'],
120
+ },
121
+ ],
122
+ examples: [
123
+ {
124
+ key: 'get-existing',
125
+ input: { projectId: 'proj-123' },
126
+ output: { id: 'proj-123', name: 'Website Redesign' },
127
+ },
128
+ ],
129
+ },
130
+ });
131
+
132
+ /**
133
+ * Update a project.
134
+ */
135
+ export const UpdateProjectContract = defineCommand({
136
+ meta: {
137
+ key: 'saas.project.update',
138
+ version: '1.0.0',
139
+ stability: 'stable',
140
+ owners: [...OWNERS],
141
+ tags: ['saas', 'project', 'update'],
142
+ description: 'Update project details.',
143
+ goal: 'Allow project owners/editors to modify project.',
144
+ context: 'Project settings page.',
145
+ },
146
+ io: {
147
+ input: UpdateProjectInputModel,
148
+ output: ProjectModel,
149
+ },
150
+ policy: {
151
+ auth: 'user',
152
+ },
153
+ sideEffects: {
154
+ emits: [
155
+ {
156
+ key: 'project.updated',
157
+ version: '1.0.0',
158
+ when: 'Project is updated',
159
+ payload: ProjectModel,
160
+ },
161
+ ],
162
+ audit: ['project.updated'],
163
+ },
164
+ acceptance: {
165
+ scenarios: [
166
+ {
167
+ key: 'update-project-happy-path',
168
+ given: ['Project exists'],
169
+ when: ['User updates description'],
170
+ then: ['Project is updated', 'ProjectUpdated event is emitted'],
171
+ },
172
+ ],
173
+ examples: [
174
+ {
175
+ key: 'update-desc',
176
+ input: { projectId: 'proj-123', description: 'New description' },
177
+ output: { id: 'proj-123', description: 'New description' },
178
+ },
179
+ ],
180
+ },
181
+ });
182
+
183
+ /**
184
+ * Delete a project.
185
+ */
186
+ export const DeleteProjectContract = defineCommand({
187
+ meta: {
188
+ key: 'saas.project.delete',
189
+ version: '1.0.0',
190
+ stability: 'stable',
191
+ owners: [...OWNERS],
192
+ tags: ['saas', 'project', 'delete'],
193
+ description: 'Delete a project (soft delete).',
194
+ goal: 'Allow project owners to remove projects.',
195
+ context: 'Project settings page.',
196
+ },
197
+ io: {
198
+ input: DeleteProjectInputModel,
199
+ output: DeleteProjectOutputModel,
200
+ },
201
+ policy: {
202
+ auth: 'user',
203
+ },
204
+ sideEffects: {
205
+ emits: [
206
+ {
207
+ key: 'project.deleted',
208
+ version: '1.0.0',
209
+ when: 'Project is deleted',
210
+ payload: ProjectDeletedPayloadModel,
211
+ },
212
+ ],
213
+ audit: ['project.deleted'],
214
+ },
215
+ acceptance: {
216
+ scenarios: [
217
+ {
218
+ key: 'delete-project-happy-path',
219
+ given: ['Project exists'],
220
+ when: ['User deletes project'],
221
+ then: ['Project is deleted', 'ProjectDeleted event is emitted'],
222
+ },
223
+ ],
224
+ examples: [
225
+ {
226
+ key: 'delete-existing',
227
+ input: { projectId: 'proj-123' },
228
+ output: { success: true },
229
+ },
230
+ ],
231
+ },
232
+ });
233
+
234
+ /**
235
+ * List organization projects.
236
+ */
237
+ export const ListProjectsContract = defineQuery({
238
+ meta: {
239
+ key: 'saas.project.list',
240
+ version: '1.0.0',
241
+ stability: 'stable',
242
+ owners: [...OWNERS],
243
+ tags: ['saas', 'project', 'list'],
244
+ description: 'List projects in the organization.',
245
+ goal: 'Show all projects user has access to.',
246
+ context: 'Project list page, dashboard.',
247
+ },
248
+ io: {
249
+ input: ListProjectsInputModel,
250
+ output: ListProjectsOutputModel,
251
+ },
252
+ policy: {
253
+ auth: 'user',
254
+ },
255
+ acceptance: {
256
+ scenarios: [
257
+ {
258
+ key: 'list-projects-happy-path',
259
+ given: ['Projects exist'],
260
+ when: ['User lists projects'],
261
+ then: ['List of projects is returned'],
262
+ },
263
+ ],
264
+ examples: [
265
+ {
266
+ key: 'list-all',
267
+ input: { limit: 10 },
268
+ output: { items: [], total: 5 },
269
+ },
270
+ ],
271
+ },
272
+ });
@@ -0,0 +1,58 @@
1
+ import { definePresentation, StabilityEnum } from '@contractspec/lib.contracts';
2
+ import { ProjectModel } from './project.schema';
3
+
4
+ /**
5
+ * Presentation for displaying a list of projects.
6
+ */
7
+ export const ProjectListPresentation = definePresentation({
8
+ meta: {
9
+ key: 'saas.project.list',
10
+ version: '1.0.0',
11
+ title: 'Project List',
12
+ description:
13
+ 'List view of projects with status, tags, and last updated info',
14
+ domain: 'saas-boilerplate',
15
+ owners: ['@saas-team'],
16
+ tags: ['project', 'list', 'dashboard'],
17
+ stability: StabilityEnum.Beta,
18
+ goal: 'Browse and manage projects',
19
+ context: 'Project list page',
20
+ },
21
+ source: {
22
+ type: 'component',
23
+ framework: 'react',
24
+ componentKey: 'ProjectListView',
25
+ props: ProjectModel,
26
+ },
27
+ targets: ['react', 'markdown', 'application/json'],
28
+ policy: {
29
+ flags: ['saas.projects.enabled'],
30
+ },
31
+ });
32
+
33
+ /**
34
+ * Presentation for project detail view.
35
+ */
36
+ export const ProjectDetailPresentation = definePresentation({
37
+ meta: {
38
+ key: 'saas.project.detail',
39
+ version: '1.0.0',
40
+ title: 'Project Details',
41
+ description: 'Detailed view of a project with settings and activity',
42
+ domain: 'saas-boilerplate',
43
+ owners: ['@saas-team'],
44
+ tags: ['project', 'detail'],
45
+ stability: StabilityEnum.Beta,
46
+ goal: 'View and edit project details',
47
+ context: 'Project detail page',
48
+ },
49
+ source: {
50
+ type: 'component',
51
+ framework: 'react',
52
+ componentKey: 'ProjectDetailView',
53
+ },
54
+ targets: ['react', 'markdown'],
55
+ policy: {
56
+ flags: ['saas.projects.enabled'],
57
+ },
58
+ });
@@ -0,0 +1,147 @@
1
+ import { defineSchemaModel, ScalarTypeEnum } from '@contractspec/lib.schema';
2
+ import {
3
+ ProjectStatusSchemaEnum,
4
+ ProjectStatusFilterEnum,
5
+ } from './project.enum';
6
+
7
+ /**
8
+ * A project within an organization.
9
+ */
10
+ export const ProjectModel = defineSchemaModel({
11
+ name: 'Project',
12
+ description: 'A project within an organization',
13
+ fields: {
14
+ id: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
15
+ name: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
16
+ description: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
17
+ slug: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
18
+ organizationId: {
19
+ type: ScalarTypeEnum.String_unsecure(),
20
+ isOptional: false,
21
+ },
22
+ createdBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
23
+ status: { type: ProjectStatusSchemaEnum, isOptional: false },
24
+ isPublic: { type: ScalarTypeEnum.Boolean(), isOptional: false },
25
+ tags: {
26
+ type: ScalarTypeEnum.String_unsecure(),
27
+ isArray: true,
28
+ isOptional: false,
29
+ },
30
+ createdAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
31
+ updatedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
32
+ },
33
+ });
34
+
35
+ /**
36
+ * Input for creating a project.
37
+ */
38
+ export const CreateProjectInputModel = defineSchemaModel({
39
+ name: 'CreateProjectInput',
40
+ description: 'Input for creating a project',
41
+ fields: {
42
+ name: { type: ScalarTypeEnum.NonEmptyString(), isOptional: false },
43
+ description: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
44
+ slug: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
45
+ isPublic: { type: ScalarTypeEnum.Boolean(), isOptional: true },
46
+ tags: {
47
+ type: ScalarTypeEnum.String_unsecure(),
48
+ isArray: true,
49
+ isOptional: true,
50
+ },
51
+ },
52
+ });
53
+
54
+ /**
55
+ * Input for updating a project.
56
+ */
57
+ export const UpdateProjectInputModel = defineSchemaModel({
58
+ name: 'UpdateProjectInput',
59
+ description: 'Input for updating a project',
60
+ fields: {
61
+ projectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
62
+ name: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
63
+ description: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
64
+ slug: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
65
+ isPublic: { type: ScalarTypeEnum.Boolean(), isOptional: true },
66
+ tags: {
67
+ type: ScalarTypeEnum.String_unsecure(),
68
+ isArray: true,
69
+ isOptional: true,
70
+ },
71
+ status: { type: ProjectStatusSchemaEnum, isOptional: true },
72
+ },
73
+ });
74
+
75
+ /**
76
+ * Input for getting a project.
77
+ */
78
+ export const GetProjectInputModel = defineSchemaModel({
79
+ name: 'GetProjectInput',
80
+ fields: {
81
+ projectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
82
+ },
83
+ });
84
+
85
+ /**
86
+ * Input for deleting a project.
87
+ */
88
+ export const DeleteProjectInputModel = defineSchemaModel({
89
+ name: 'DeleteProjectInput',
90
+ fields: {
91
+ projectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
92
+ },
93
+ });
94
+
95
+ /**
96
+ * Output for delete operation.
97
+ */
98
+ export const DeleteProjectOutputModel = defineSchemaModel({
99
+ name: 'DeleteProjectOutput',
100
+ fields: {
101
+ success: { type: ScalarTypeEnum.Boolean(), isOptional: false },
102
+ },
103
+ });
104
+
105
+ /**
106
+ * Payload for project deleted event.
107
+ */
108
+ export const ProjectDeletedPayloadModel = defineSchemaModel({
109
+ name: 'ProjectDeletedPayload',
110
+ fields: {
111
+ projectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
112
+ },
113
+ });
114
+
115
+ /**
116
+ * Input for listing projects.
117
+ */
118
+ export const ListProjectsInputModel = defineSchemaModel({
119
+ name: 'ListProjectsInput',
120
+ description: 'Input for listing projects',
121
+ fields: {
122
+ status: { type: ProjectStatusFilterEnum, isOptional: true },
123
+ search: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
124
+ limit: {
125
+ type: ScalarTypeEnum.Int_unsecure(),
126
+ isOptional: true,
127
+ defaultValue: 20,
128
+ },
129
+ offset: {
130
+ type: ScalarTypeEnum.Int_unsecure(),
131
+ isOptional: true,
132
+ defaultValue: 0,
133
+ },
134
+ },
135
+ });
136
+
137
+ /**
138
+ * Output for listing projects.
139
+ */
140
+ export const ListProjectsOutputModel = defineSchemaModel({
141
+ name: 'ListProjectsOutput',
142
+ description: 'Output for listing projects',
143
+ fields: {
144
+ projects: { type: ProjectModel, isArray: true, isOptional: false },
145
+ total: { type: ScalarTypeEnum.Int_unsecure(), isOptional: false },
146
+ },
147
+ });
@@ -0,0 +1,113 @@
1
+ /**
2
+ * SaaS Boilerplate Feature Module Specification
3
+ *
4
+ * Defines the feature module for the SaaS application foundation.
5
+ */
6
+ import { defineFeature } from '@contractspec/lib.contracts';
7
+
8
+ /**
9
+ * SaaS Boilerplate feature module that bundles project management,
10
+ * billing, and settings operations into an installable feature.
11
+ */
12
+ export const SaasBoilerplateFeature = defineFeature({
13
+ meta: {
14
+ key: 'saas-boilerplate',
15
+ title: 'SaaS Boilerplate',
16
+ description:
17
+ 'SaaS application foundation with projects, billing, and settings',
18
+ domain: 'saas',
19
+ owners: ['@saas-team'],
20
+ tags: ['saas', 'projects', 'billing'],
21
+ stability: 'experimental',
22
+ version: '1.0.0',
23
+ },
24
+
25
+ // All contract operations included in this feature
26
+ operations: [
27
+ // Project operations
28
+ { key: 'saas.project.create', version: '1.0.0' },
29
+ { key: 'saas.project.get', version: '1.0.0' },
30
+ { key: 'saas.project.update', version: '1.0.0' },
31
+ { key: 'saas.project.delete', version: '1.0.0' },
32
+ { key: 'saas.project.list', version: '1.0.0' },
33
+
34
+ // Billing operations
35
+ { key: 'saas.billing.subscription.get', version: '1.0.0' },
36
+ { key: 'saas.billing.usage.record', version: '1.0.0' },
37
+ { key: 'saas.billing.usage.summary', version: '1.0.0' },
38
+ { key: 'saas.billing.feature.check', version: '1.0.0' },
39
+ ],
40
+
41
+ // Events emitted by this feature
42
+ events: [
43
+ // Project events
44
+ { key: 'project.created', version: '1.0.0' },
45
+ { key: 'project.updated', version: '1.0.0' },
46
+ { key: 'project.deleted', version: '1.0.0' },
47
+ { key: 'project.archived', version: '1.0.0' },
48
+
49
+ // Billing events
50
+ { key: 'billing.usage.recorded', version: '1.0.0' },
51
+ { key: 'billing.subscription.changed', version: '1.0.0' },
52
+ { key: 'billing.limit.reached', version: '1.0.0' },
53
+ ],
54
+
55
+ // Presentations associated with this feature
56
+ presentations: [
57
+ { key: 'saas.dashboard', version: '1.0.0' },
58
+ { key: 'saas.project.list', version: '1.0.0' },
59
+ { key: 'saas.project.detail', version: '1.0.0' },
60
+ { key: 'saas.billing.subscription', version: '1.0.0' },
61
+ { key: 'saas.billing.usage', version: '1.0.0' },
62
+ { key: 'saas.settings', version: '1.0.0' },
63
+ ],
64
+
65
+ // Link operations to their primary presentations
66
+ opToPresentation: [
67
+ {
68
+ op: { key: 'saas.project.list', version: '1.0.0' },
69
+ pres: { key: 'saas.project.list', version: '1.0.0' },
70
+ },
71
+ {
72
+ op: { key: 'saas.project.get', version: '1.0.0' },
73
+ pres: { key: 'saas.project.detail', version: '1.0.0' },
74
+ },
75
+ {
76
+ op: { key: 'saas.billing.subscription.get', version: '1.0.0' },
77
+ pres: { key: 'saas.billing.subscription', version: '1.0.0' },
78
+ },
79
+ {
80
+ op: { key: 'saas.billing.usage.summary', version: '1.0.0' },
81
+ pres: { key: 'saas.billing.usage', version: '1.0.0' },
82
+ },
83
+ ],
84
+
85
+ // Target requirements for multi-surface rendering
86
+ presentationsTargets: [
87
+ { key: 'saas.dashboard', version: '1.0.0', targets: ['react', 'markdown'] },
88
+ {
89
+ key: 'saas.project.list',
90
+ version: '1.0.0',
91
+ targets: ['react', 'markdown', 'application/json'],
92
+ },
93
+ {
94
+ key: 'saas.billing.subscription',
95
+ version: '1.0.0',
96
+ targets: ['react', 'markdown'],
97
+ },
98
+ {
99
+ key: 'saas.billing.usage',
100
+ version: '1.0.0',
101
+ targets: ['react', 'markdown'],
102
+ },
103
+ ],
104
+
105
+ // Capability requirements
106
+ capabilities: {
107
+ requires: [
108
+ { key: 'identity', version: '1.0.0' },
109
+ { key: 'audit-trail', version: '1.0.0' },
110
+ { key: 'notifications', version: '1.0.0' },
111
+ ],
112
+ },
113
+ });
@@ -0,0 +1,28 @@
1
+ import type { DatabasePort } from '@contractspec/lib.runtime-sandbox';
2
+
3
+ export async function seedSaasBoilerplate(params: {
4
+ projectId: string;
5
+ db: DatabasePort;
6
+ }) {
7
+ const { projectId, db } = params;
8
+
9
+ const existing = await db.query(
10
+ `SELECT COUNT(*) as count FROM saas_project WHERE "projectId" = $1`,
11
+ [projectId]
12
+ );
13
+ if ((existing.rows[0]?.count as number) > 0) return;
14
+
15
+ await db.execute(
16
+ `INSERT INTO saas_project (id, "projectId", "organizationId", name, description, status, tier)
17
+ VALUES ($1, $2, $3, $4, $5, $6, $7)`,
18
+ [
19
+ 'saas_proj_1',
20
+ projectId,
21
+ 'org_demo',
22
+ 'Demo Project',
23
+ 'A demo SaaS project',
24
+ 'ACTIVE',
25
+ 'PRO',
26
+ ]
27
+ );
28
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Settings domain - application, organization, and user settings.
3
+ */
4
+
5
+ // Enums
6
+ export { SettingsScopeEnum } from './settings.enum';
7
+
8
+ // Entities
9
+ export { SettingsEntity, FeatureFlagEntity } from './settings.entity';