@contractspec/example.saas-boilerplate 3.8.9 → 3.8.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +155 -155
- package/CHANGELOG.md +34 -0
- package/dist/billing/billing.entity.js +1 -113
- package/dist/billing/billing.enum.js +1 -19
- package/dist/billing/billing.event.js +1 -90
- package/dist/billing/billing.handler.js +1 -148
- package/dist/billing/billing.operations.js +1 -278
- package/dist/billing/billing.presentation.js +1 -55
- package/dist/billing/billing.schema.js +1 -121
- package/dist/billing/index.js +1 -691
- package/dist/browser/billing/billing.entity.js +1 -113
- package/dist/browser/billing/billing.enum.js +1 -19
- package/dist/browser/billing/billing.event.js +1 -90
- package/dist/browser/billing/billing.handler.js +1 -148
- package/dist/browser/billing/billing.operations.js +1 -278
- package/dist/browser/billing/billing.presentation.js +1 -55
- package/dist/browser/billing/billing.schema.js +1 -121
- package/dist/browser/billing/index.js +1 -691
- package/dist/browser/dashboard/dashboard.presentation.js +1 -55
- package/dist/browser/dashboard/index.js +1 -55
- package/dist/browser/docs/index.js +5 -49
- package/dist/browser/docs/saas-boilerplate.docblock.js +5 -49
- package/dist/browser/example.js +1 -39
- package/dist/browser/handlers/index.js +2 -358
- package/dist/browser/handlers/saas.handlers.js +2 -134
- package/dist/browser/index.js +9 -3591
- package/dist/browser/presentations/index.js +1 -299
- package/dist/browser/project/index.js +1 -793
- package/dist/browser/project/project.entity.js +1 -77
- package/dist/browser/project/project.enum.js +1 -18
- package/dist/browser/project/project.event.js +1 -103
- package/dist/browser/project/project.handler.js +1 -178
- package/dist/browser/project/project.operations.js +1 -372
- package/dist/browser/project/project.presentation.js +1 -180
- package/dist/browser/project/project.schema.js +1 -134
- package/dist/browser/saas-boilerplate.feature.js +1 -304
- package/dist/browser/seeders/index.js +2 -20
- package/dist/browser/settings/index.js +1 -75
- package/dist/browser/settings/settings.entity.js +1 -74
- package/dist/browser/settings/settings.enum.js +1 -11
- package/dist/browser/shared/mock-data.js +1 -104
- package/dist/browser/tests/operations.test-spec.js +1 -112
- package/dist/browser/ui/SaasDashboard.js +1 -1239
- package/dist/browser/ui/SaasDashboard.visualizations.js +1 -249
- package/dist/browser/ui/SaasProjectList.js +1 -162
- package/dist/browser/ui/SaasSettingsPanel.js +1 -145
- package/dist/browser/ui/hooks/index.js +1 -159
- package/dist/browser/ui/hooks/useProjectList.js +1 -66
- package/dist/browser/ui/hooks/useProjectMutations.js +1 -91
- package/dist/browser/ui/index.js +5 -2077
- package/dist/browser/ui/modals/CreateProjectModal.js +1 -153
- package/dist/browser/ui/modals/ProjectActionsModal.js +1 -335
- package/dist/browser/ui/modals/index.js +1 -487
- package/dist/browser/ui/overlays/demo-overlays.js +1 -61
- package/dist/browser/ui/overlays/index.js +1 -61
- package/dist/browser/ui/renderers/index.js +5 -901
- package/dist/browser/ui/renderers/project-list.markdown.js +5 -725
- package/dist/browser/ui/renderers/project-list.renderer.js +1 -177
- package/dist/browser/visualizations/catalog.js +1 -155
- package/dist/browser/visualizations/index.js +1 -217
- package/dist/browser/visualizations/selectors.js +1 -210
- package/dist/dashboard/dashboard.presentation.js +1 -55
- package/dist/dashboard/index.js +1 -55
- package/dist/docs/index.js +5 -49
- package/dist/docs/saas-boilerplate.docblock.js +5 -49
- package/dist/example.js +1 -39
- package/dist/handlers/index.js +2 -358
- package/dist/handlers/saas.handlers.js +2 -134
- package/dist/index.js +9 -3591
- package/dist/node/billing/billing.entity.js +1 -113
- package/dist/node/billing/billing.enum.js +1 -19
- package/dist/node/billing/billing.event.js +1 -90
- package/dist/node/billing/billing.handler.js +1 -148
- package/dist/node/billing/billing.operations.js +1 -278
- package/dist/node/billing/billing.presentation.js +1 -55
- package/dist/node/billing/billing.schema.js +1 -121
- package/dist/node/billing/index.js +1 -691
- package/dist/node/dashboard/dashboard.presentation.js +1 -55
- package/dist/node/dashboard/index.js +1 -55
- package/dist/node/docs/index.js +5 -49
- package/dist/node/docs/saas-boilerplate.docblock.js +5 -49
- package/dist/node/example.js +1 -39
- package/dist/node/handlers/index.js +2 -358
- package/dist/node/handlers/saas.handlers.js +2 -134
- package/dist/node/index.js +9 -3591
- package/dist/node/presentations/index.js +1 -299
- package/dist/node/project/index.js +1 -793
- package/dist/node/project/project.entity.js +1 -77
- package/dist/node/project/project.enum.js +1 -18
- package/dist/node/project/project.event.js +1 -103
- package/dist/node/project/project.handler.js +1 -178
- package/dist/node/project/project.operations.js +1 -372
- package/dist/node/project/project.presentation.js +1 -180
- package/dist/node/project/project.schema.js +1 -134
- package/dist/node/saas-boilerplate.feature.js +1 -304
- package/dist/node/seeders/index.js +2 -20
- package/dist/node/settings/index.js +1 -75
- package/dist/node/settings/settings.entity.js +1 -74
- package/dist/node/settings/settings.enum.js +1 -11
- package/dist/node/shared/mock-data.js +1 -104
- package/dist/node/tests/operations.test-spec.js +1 -112
- package/dist/node/ui/SaasDashboard.js +1 -1239
- package/dist/node/ui/SaasDashboard.visualizations.js +1 -249
- package/dist/node/ui/SaasProjectList.js +1 -162
- package/dist/node/ui/SaasSettingsPanel.js +1 -145
- package/dist/node/ui/hooks/index.js +1 -159
- package/dist/node/ui/hooks/useProjectList.js +1 -66
- package/dist/node/ui/hooks/useProjectMutations.js +1 -91
- package/dist/node/ui/index.js +5 -2077
- package/dist/node/ui/modals/CreateProjectModal.js +1 -153
- package/dist/node/ui/modals/ProjectActionsModal.js +1 -335
- package/dist/node/ui/modals/index.js +1 -487
- package/dist/node/ui/overlays/demo-overlays.js +1 -61
- package/dist/node/ui/overlays/index.js +1 -61
- package/dist/node/ui/renderers/index.js +5 -901
- package/dist/node/ui/renderers/project-list.markdown.js +5 -725
- package/dist/node/ui/renderers/project-list.renderer.js +1 -177
- package/dist/node/visualizations/catalog.js +1 -155
- package/dist/node/visualizations/index.js +1 -217
- package/dist/node/visualizations/selectors.js +1 -210
- package/dist/presentations/index.js +1 -299
- package/dist/project/index.js +1 -793
- package/dist/project/project.entity.js +1 -77
- package/dist/project/project.enum.js +1 -18
- package/dist/project/project.event.js +1 -103
- package/dist/project/project.handler.js +1 -178
- package/dist/project/project.operations.js +1 -372
- package/dist/project/project.presentation.js +1 -180
- package/dist/project/project.schema.js +1 -134
- package/dist/saas-boilerplate.feature.js +1 -304
- package/dist/seeders/index.js +2 -20
- package/dist/settings/index.js +1 -75
- package/dist/settings/settings.entity.js +1 -74
- package/dist/settings/settings.enum.js +1 -11
- package/dist/shared/mock-data.js +1 -104
- package/dist/tests/operations.test-spec.js +1 -112
- package/dist/ui/SaasDashboard.js +1 -1239
- package/dist/ui/SaasDashboard.visualizations.js +1 -249
- package/dist/ui/SaasProjectList.js +1 -162
- package/dist/ui/SaasSettingsPanel.js +1 -145
- package/dist/ui/hooks/index.js +1 -159
- package/dist/ui/hooks/useProjectList.js +1 -66
- package/dist/ui/hooks/useProjectMutations.js +1 -91
- package/dist/ui/index.js +5 -2077
- package/dist/ui/modals/CreateProjectModal.js +1 -153
- package/dist/ui/modals/ProjectActionsModal.js +1 -335
- package/dist/ui/modals/index.js +1 -487
- package/dist/ui/overlays/demo-overlays.js +1 -61
- package/dist/ui/overlays/index.js +1 -61
- package/dist/ui/renderers/index.js +5 -901
- package/dist/ui/renderers/project-list.markdown.js +5 -725
- package/dist/ui/renderers/project-list.renderer.js +1 -177
- package/dist/visualizations/catalog.js +1 -155
- package/dist/visualizations/index.js +1 -217
- package/dist/visualizations/selectors.js +1 -210
- package/package.json +12 -12
|
@@ -1,358 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
{
|
|
4
|
-
id: "proj-1",
|
|
5
|
-
name: "Marketing Website",
|
|
6
|
-
description: "Main company website redesign project",
|
|
7
|
-
slug: "marketing-website",
|
|
8
|
-
organizationId: "demo-org",
|
|
9
|
-
createdBy: "user-1",
|
|
10
|
-
status: "ACTIVE",
|
|
11
|
-
isPublic: false,
|
|
12
|
-
tags: ["marketing", "website", "redesign"],
|
|
13
|
-
createdAt: new Date("2024-01-15T10:00:00Z"),
|
|
14
|
-
updatedAt: new Date("2024-03-20T14:30:00Z")
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
id: "proj-2",
|
|
18
|
-
name: "Mobile App v2",
|
|
19
|
-
description: "Next generation mobile application",
|
|
20
|
-
slug: "mobile-app-v2",
|
|
21
|
-
organizationId: "demo-org",
|
|
22
|
-
createdBy: "user-2",
|
|
23
|
-
status: "ACTIVE",
|
|
24
|
-
isPublic: false,
|
|
25
|
-
tags: ["mobile", "app", "v2"],
|
|
26
|
-
createdAt: new Date("2024-02-01T09:00:00Z"),
|
|
27
|
-
updatedAt: new Date("2024-04-05T11:15:00Z")
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
id: "proj-3",
|
|
31
|
-
name: "API Integration",
|
|
32
|
-
description: "Third-party API integration project",
|
|
33
|
-
slug: "api-integration",
|
|
34
|
-
organizationId: "demo-org",
|
|
35
|
-
createdBy: "user-1",
|
|
36
|
-
status: "DRAFT",
|
|
37
|
-
isPublic: false,
|
|
38
|
-
tags: ["api", "integration"],
|
|
39
|
-
createdAt: new Date("2024-03-10T08:00:00Z"),
|
|
40
|
-
updatedAt: new Date("2024-03-10T08:00:00Z")
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
id: "proj-4",
|
|
44
|
-
name: "Analytics Dashboard",
|
|
45
|
-
description: "Internal analytics and reporting dashboard",
|
|
46
|
-
slug: "analytics-dashboard",
|
|
47
|
-
organizationId: "demo-org",
|
|
48
|
-
createdBy: "user-3",
|
|
49
|
-
status: "ARCHIVED",
|
|
50
|
-
isPublic: true,
|
|
51
|
-
tags: ["analytics", "dashboard", "reporting"],
|
|
52
|
-
createdAt: new Date("2023-10-01T12:00:00Z"),
|
|
53
|
-
updatedAt: new Date("2024-02-28T16:45:00Z")
|
|
54
|
-
}
|
|
55
|
-
];
|
|
56
|
-
var MOCK_SUBSCRIPTION = {
|
|
57
|
-
id: "sub-1",
|
|
58
|
-
organizationId: "demo-org",
|
|
59
|
-
planId: "pro",
|
|
60
|
-
planName: "Professional",
|
|
61
|
-
status: "ACTIVE",
|
|
62
|
-
currentPeriodStart: new Date("2024-04-01T00:00:00Z"),
|
|
63
|
-
currentPeriodEnd: new Date("2024-05-01T00:00:00Z"),
|
|
64
|
-
limits: {
|
|
65
|
-
projects: 25,
|
|
66
|
-
users: 10,
|
|
67
|
-
storage: 50,
|
|
68
|
-
apiCalls: 1e5
|
|
69
|
-
},
|
|
70
|
-
usage: {
|
|
71
|
-
projects: 4,
|
|
72
|
-
users: 5,
|
|
73
|
-
storage: 12.5,
|
|
74
|
-
apiCalls: 45230
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
var MOCK_USAGE_SUMMARY = {
|
|
78
|
-
organizationId: "demo-org",
|
|
79
|
-
period: "current_month",
|
|
80
|
-
apiCalls: {
|
|
81
|
-
total: 45230,
|
|
82
|
-
limit: 1e5,
|
|
83
|
-
percentUsed: 45.23
|
|
84
|
-
},
|
|
85
|
-
storage: {
|
|
86
|
-
totalGb: 12.5,
|
|
87
|
-
limitGb: 50,
|
|
88
|
-
percentUsed: 25
|
|
89
|
-
},
|
|
90
|
-
activeProjects: 4,
|
|
91
|
-
activeUsers: 5,
|
|
92
|
-
breakdown: [
|
|
93
|
-
{ date: "2024-04-01", apiCalls: 3200, storageGb: 12.1 },
|
|
94
|
-
{ date: "2024-04-02", apiCalls: 2800, storageGb: 12.2 },
|
|
95
|
-
{ date: "2024-04-03", apiCalls: 4100, storageGb: 12.3 },
|
|
96
|
-
{ date: "2024-04-04", apiCalls: 3600, storageGb: 12.4 },
|
|
97
|
-
{ date: "2024-04-05", apiCalls: 3800, storageGb: 12.5 }
|
|
98
|
-
]
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
// src/billing/billing.handler.ts
|
|
102
|
-
async function mockGetSubscriptionHandler() {
|
|
103
|
-
return MOCK_SUBSCRIPTION;
|
|
104
|
-
}
|
|
105
|
-
async function mockGetUsageSummaryHandler(input) {
|
|
106
|
-
return {
|
|
107
|
-
...MOCK_USAGE_SUMMARY,
|
|
108
|
-
period: input.period ?? "current_month"
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
async function mockRecordUsageHandler(input) {
|
|
112
|
-
const currentUsage = MOCK_USAGE_SUMMARY.apiCalls.total;
|
|
113
|
-
const newTotal = currentUsage + input.quantity;
|
|
114
|
-
return {
|
|
115
|
-
recorded: true,
|
|
116
|
-
newTotal
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
async function mockCheckFeatureAccessHandler(input) {
|
|
120
|
-
const { feature } = input;
|
|
121
|
-
const featureMap = {
|
|
122
|
-
custom_domains: {
|
|
123
|
-
allowed: true
|
|
124
|
-
},
|
|
125
|
-
api_access: {
|
|
126
|
-
allowed: true,
|
|
127
|
-
currentUsage: MOCK_USAGE_SUMMARY.apiCalls.total,
|
|
128
|
-
limit: MOCK_USAGE_SUMMARY.apiCalls.limit
|
|
129
|
-
},
|
|
130
|
-
advanced_analytics: {
|
|
131
|
-
allowed: false,
|
|
132
|
-
reason: "FEATURE_NOT_INCLUDED"
|
|
133
|
-
},
|
|
134
|
-
unlimited_projects: {
|
|
135
|
-
allowed: false,
|
|
136
|
-
reason: "PLAN_LIMIT",
|
|
137
|
-
currentUsage: MOCK_SUBSCRIPTION.usage.projects,
|
|
138
|
-
limit: MOCK_SUBSCRIPTION.limits.projects
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
return featureMap[feature] ?? { allowed: true };
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// src/project/project.handler.ts
|
|
145
|
-
async function mockListProjectsHandler(input) {
|
|
146
|
-
const { status, search, limit = 20, offset = 0 } = input;
|
|
147
|
-
let filtered = [...MOCK_PROJECTS];
|
|
148
|
-
if (status && status !== "all") {
|
|
149
|
-
filtered = filtered.filter((p) => p.status === status);
|
|
150
|
-
}
|
|
151
|
-
if (search) {
|
|
152
|
-
const q = search.toLowerCase();
|
|
153
|
-
filtered = filtered.filter((p) => p.name.toLowerCase().includes(q) || p.description?.toLowerCase().includes(q) || p.tags.some((t) => t.toLowerCase().includes(q)));
|
|
154
|
-
}
|
|
155
|
-
filtered.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
|
|
156
|
-
const total = filtered.length;
|
|
157
|
-
const projects = filtered.slice(offset, offset + limit);
|
|
158
|
-
return {
|
|
159
|
-
projects,
|
|
160
|
-
total
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
async function mockGetProjectHandler(input) {
|
|
164
|
-
const project = MOCK_PROJECTS.find((p) => p.id === input.projectId);
|
|
165
|
-
if (!project) {
|
|
166
|
-
throw new Error("NOT_FOUND");
|
|
167
|
-
}
|
|
168
|
-
return project;
|
|
169
|
-
}
|
|
170
|
-
async function mockCreateProjectHandler(input, context) {
|
|
171
|
-
if (input.slug) {
|
|
172
|
-
const exists = MOCK_PROJECTS.some((p) => p.slug === input.slug);
|
|
173
|
-
if (exists) {
|
|
174
|
-
throw new Error("SLUG_EXISTS");
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
const now = new Date;
|
|
178
|
-
return {
|
|
179
|
-
id: `proj-${Date.now()}`,
|
|
180
|
-
name: input.name,
|
|
181
|
-
description: input.description,
|
|
182
|
-
slug: input.slug ?? input.name.toLowerCase().replace(/\s+/g, "-"),
|
|
183
|
-
organizationId: context.organizationId,
|
|
184
|
-
createdBy: context.userId,
|
|
185
|
-
status: "DRAFT",
|
|
186
|
-
isPublic: input.isPublic ?? false,
|
|
187
|
-
tags: input.tags ?? [],
|
|
188
|
-
createdAt: now,
|
|
189
|
-
updatedAt: now
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
async function mockUpdateProjectHandler(input) {
|
|
193
|
-
const project = MOCK_PROJECTS.find((p) => p.id === input.projectId);
|
|
194
|
-
if (!project) {
|
|
195
|
-
throw new Error("NOT_FOUND");
|
|
196
|
-
}
|
|
197
|
-
return {
|
|
198
|
-
...project,
|
|
199
|
-
name: input.name ?? project.name,
|
|
200
|
-
description: input.description ?? project.description,
|
|
201
|
-
slug: input.slug ?? project.slug,
|
|
202
|
-
isPublic: input.isPublic ?? project.isPublic,
|
|
203
|
-
tags: input.tags ?? project.tags,
|
|
204
|
-
status: input.status ?? project.status,
|
|
205
|
-
updatedAt: new Date
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
async function mockDeleteProjectHandler(input) {
|
|
209
|
-
const project = MOCK_PROJECTS.find((p) => p.id === input.projectId);
|
|
210
|
-
if (!project) {
|
|
211
|
-
throw new Error("NOT_FOUND");
|
|
212
|
-
}
|
|
213
|
-
return { success: true };
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// src/handlers/saas.handlers.ts
|
|
217
|
-
import { web } from "@contractspec/lib.runtime-sandbox";
|
|
218
|
-
var { generateId } = web;
|
|
219
|
-
function rowToProject(row) {
|
|
220
|
-
return {
|
|
221
|
-
id: row.id,
|
|
222
|
-
projectId: row.projectId,
|
|
223
|
-
organizationId: row.organizationId,
|
|
224
|
-
name: row.name,
|
|
225
|
-
description: row.description ?? undefined,
|
|
226
|
-
status: row.status,
|
|
227
|
-
tier: row.tier,
|
|
228
|
-
createdAt: new Date(row.createdAt),
|
|
229
|
-
updatedAt: new Date(row.updatedAt)
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
function rowToSubscription(row) {
|
|
233
|
-
return {
|
|
234
|
-
id: row.id,
|
|
235
|
-
projectId: row.projectId,
|
|
236
|
-
organizationId: row.organizationId,
|
|
237
|
-
plan: row.plan,
|
|
238
|
-
status: row.status,
|
|
239
|
-
billingCycle: row.billingCycle,
|
|
240
|
-
currentPeriodStart: new Date(row.currentPeriodStart),
|
|
241
|
-
currentPeriodEnd: new Date(row.currentPeriodEnd),
|
|
242
|
-
cancelAtPeriodEnd: Boolean(row.cancelAtPeriodEnd)
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
function createSaasHandlers(db) {
|
|
246
|
-
async function listProjects(input) {
|
|
247
|
-
const {
|
|
248
|
-
projectId,
|
|
249
|
-
organizationId,
|
|
250
|
-
status,
|
|
251
|
-
search,
|
|
252
|
-
limit = 20,
|
|
253
|
-
offset = 0
|
|
254
|
-
} = input;
|
|
255
|
-
let whereClause = "WHERE projectId = ?";
|
|
256
|
-
const params = [projectId];
|
|
257
|
-
if (organizationId) {
|
|
258
|
-
whereClause += " AND organizationId = ?";
|
|
259
|
-
params.push(organizationId);
|
|
260
|
-
}
|
|
261
|
-
if (status && status !== "all") {
|
|
262
|
-
whereClause += " AND status = ?";
|
|
263
|
-
params.push(status);
|
|
264
|
-
}
|
|
265
|
-
if (search) {
|
|
266
|
-
whereClause += " AND (name LIKE ? OR description LIKE ?)";
|
|
267
|
-
params.push(`%${search}%`, `%${search}%`);
|
|
268
|
-
}
|
|
269
|
-
const countResult = (await db.query(`SELECT COUNT(*) as count FROM saas_project ${whereClause}`, params)).rows;
|
|
270
|
-
const total = countResult[0]?.count ?? 0;
|
|
271
|
-
const rows = (await db.query(`SELECT * FROM saas_project ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`, [...params, limit, offset])).rows;
|
|
272
|
-
return {
|
|
273
|
-
items: rows.map(rowToProject),
|
|
274
|
-
total,
|
|
275
|
-
hasMore: offset + rows.length < total
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
async function getProject(id) {
|
|
279
|
-
const rows = (await db.query(`SELECT * FROM saas_project WHERE id = ?`, [id])).rows;
|
|
280
|
-
return rows[0] ? rowToProject(rows[0]) : null;
|
|
281
|
-
}
|
|
282
|
-
async function createProject(input, context) {
|
|
283
|
-
const id = generateId("proj");
|
|
284
|
-
const now = new Date().toISOString();
|
|
285
|
-
await db.execute(`INSERT INTO saas_project (id, projectId, organizationId, name, description, status, tier, createdAt, updatedAt)
|
|
286
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
287
|
-
id,
|
|
288
|
-
context.projectId,
|
|
289
|
-
context.organizationId,
|
|
290
|
-
input.name,
|
|
291
|
-
input.description ?? null,
|
|
292
|
-
"DRAFT",
|
|
293
|
-
input.tier ?? "FREE",
|
|
294
|
-
now,
|
|
295
|
-
now
|
|
296
|
-
]);
|
|
297
|
-
const rows = (await db.query(`SELECT * FROM saas_project WHERE id = ?`, [id])).rows;
|
|
298
|
-
return rowToProject(rows[0]);
|
|
299
|
-
}
|
|
300
|
-
async function updateProject(input) {
|
|
301
|
-
const now = new Date().toISOString();
|
|
302
|
-
const updates = ["updatedAt = ?"];
|
|
303
|
-
const params = [now];
|
|
304
|
-
if (input.name !== undefined) {
|
|
305
|
-
updates.push("name = ?");
|
|
306
|
-
params.push(input.name);
|
|
307
|
-
}
|
|
308
|
-
if (input.description !== undefined) {
|
|
309
|
-
updates.push("description = ?");
|
|
310
|
-
params.push(input.description);
|
|
311
|
-
}
|
|
312
|
-
if (input.status !== undefined) {
|
|
313
|
-
updates.push("status = ?");
|
|
314
|
-
params.push(input.status);
|
|
315
|
-
}
|
|
316
|
-
params.push(input.id);
|
|
317
|
-
await db.execute(`UPDATE saas_project SET ${updates.join(", ")} WHERE id = ?`, params);
|
|
318
|
-
const rows = (await db.query(`SELECT * FROM saas_project WHERE id = ?`, [input.id])).rows;
|
|
319
|
-
if (!rows[0]) {
|
|
320
|
-
throw new Error("NOT_FOUND");
|
|
321
|
-
}
|
|
322
|
-
return rowToProject(rows[0]);
|
|
323
|
-
}
|
|
324
|
-
async function deleteProject(id) {
|
|
325
|
-
await db.execute(`DELETE FROM saas_project WHERE id = ?`, [id]);
|
|
326
|
-
}
|
|
327
|
-
async function getSubscription(input) {
|
|
328
|
-
let query = `SELECT * FROM saas_subscription WHERE projectId = ?`;
|
|
329
|
-
const params = [input.projectId];
|
|
330
|
-
if (input.organizationId) {
|
|
331
|
-
query += " AND organizationId = ?";
|
|
332
|
-
params.push(input.organizationId);
|
|
333
|
-
}
|
|
334
|
-
query += " LIMIT 1";
|
|
335
|
-
const rows = (await db.query(query, params)).rows;
|
|
336
|
-
return rows[0] ? rowToSubscription(rows[0]) : null;
|
|
337
|
-
}
|
|
338
|
-
return {
|
|
339
|
-
listProjects,
|
|
340
|
-
getProject,
|
|
341
|
-
createProject,
|
|
342
|
-
updateProject,
|
|
343
|
-
deleteProject,
|
|
344
|
-
getSubscription
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
export {
|
|
348
|
-
mockUpdateProjectHandler,
|
|
349
|
-
mockRecordUsageHandler,
|
|
350
|
-
mockListProjectsHandler,
|
|
351
|
-
mockGetUsageSummaryHandler,
|
|
352
|
-
mockGetSubscriptionHandler,
|
|
353
|
-
mockGetProjectHandler,
|
|
354
|
-
mockDeleteProjectHandler,
|
|
355
|
-
mockCreateProjectHandler,
|
|
356
|
-
mockCheckFeatureAccessHandler,
|
|
357
|
-
createSaasHandlers
|
|
358
|
-
};
|
|
1
|
+
var $=[{id:"proj-1",name:"Marketing Website",description:"Main company website redesign project",slug:"marketing-website",organizationId:"demo-org",createdBy:"user-1",status:"ACTIVE",isPublic:!1,tags:["marketing","website","redesign"],createdAt:new Date("2024-01-15T10:00:00Z"),updatedAt:new Date("2024-03-20T14:30:00Z")},{id:"proj-2",name:"Mobile App v2",description:"Next generation mobile application",slug:"mobile-app-v2",organizationId:"demo-org",createdBy:"user-2",status:"ACTIVE",isPublic:!1,tags:["mobile","app","v2"],createdAt:new Date("2024-02-01T09:00:00Z"),updatedAt:new Date("2024-04-05T11:15:00Z")},{id:"proj-3",name:"API Integration",description:"Third-party API integration project",slug:"api-integration",organizationId:"demo-org",createdBy:"user-1",status:"DRAFT",isPublic:!1,tags:["api","integration"],createdAt:new Date("2024-03-10T08:00:00Z"),updatedAt:new Date("2024-03-10T08:00:00Z")},{id:"proj-4",name:"Analytics Dashboard",description:"Internal analytics and reporting dashboard",slug:"analytics-dashboard",organizationId:"demo-org",createdBy:"user-3",status:"ARCHIVED",isPublic:!0,tags:["analytics","dashboard","reporting"],createdAt:new Date("2023-10-01T12:00:00Z"),updatedAt:new Date("2024-02-28T16:45:00Z")}],B={id:"sub-1",organizationId:"demo-org",planId:"pro",planName:"Professional",status:"ACTIVE",currentPeriodStart:new Date("2024-04-01T00:00:00Z"),currentPeriodEnd:new Date("2024-05-01T00:00:00Z"),limits:{projects:25,users:10,storage:50,apiCalls:1e5},usage:{projects:4,users:5,storage:12.5,apiCalls:45230}},Z={organizationId:"demo-org",period:"current_month",apiCalls:{total:45230,limit:1e5,percentUsed:45.23},storage:{totalGb:12.5,limitGb:50,percentUsed:25},activeProjects:4,activeUsers:5,breakdown:[{date:"2024-04-01",apiCalls:3200,storageGb:12.1},{date:"2024-04-02",apiCalls:2800,storageGb:12.2},{date:"2024-04-03",apiCalls:4100,storageGb:12.3},{date:"2024-04-04",apiCalls:3600,storageGb:12.4},{date:"2024-04-05",apiCalls:3800,storageGb:12.5}]};async function J(){return B}async function q(H){return{...Z,period:H.period??"current_month"}}async function A(H){return{recorded:!0,newTotal:Z.apiCalls.total+H.quantity}}async function E(H){let{feature:k}=H;return{custom_domains:{allowed:!0},api_access:{allowed:!0,currentUsage:Z.apiCalls.total,limit:Z.apiCalls.limit},advanced_analytics:{allowed:!1,reason:"FEATURE_NOT_INCLUDED"},unlimited_projects:{allowed:!1,reason:"PLAN_LIMIT",currentUsage:B.usage.projects,limit:B.limits.projects}}[k]??{allowed:!0}}async function U(H){let{status:k,search:V,limit:y=20,offset:m=0}=H,W=[...$];if(k&&k!=="all")W=W.filter((v)=>v.status===k);if(V){let v=V.toLowerCase();W=W.filter((F)=>F.name.toLowerCase().includes(v)||F.description?.toLowerCase().includes(v)||F.tags.some((Q)=>Q.toLowerCase().includes(v)))}W.sort((v,F)=>F.updatedAt.getTime()-v.updatedAt.getTime());let g=W.length;return{projects:W.slice(m,m+y),total:g}}async function I(H){let k=$.find((V)=>V.id===H.projectId);if(!k)throw Error("NOT_FOUND");return k}async function R(H,k){if(H.slug){if($.some((m)=>m.slug===H.slug))throw Error("SLUG_EXISTS")}let V=new Date;return{id:`proj-${Date.now()}`,name:H.name,description:H.description,slug:H.slug??H.name.toLowerCase().replace(/\s+/g,"-"),organizationId:k.organizationId,createdBy:k.userId,status:"DRAFT",isPublic:H.isPublic??!1,tags:H.tags??[],createdAt:V,updatedAt:V}}async function K(H){let k=$.find((V)=>V.id===H.projectId);if(!k)throw Error("NOT_FOUND");return{...k,name:H.name??k.name,description:H.description??k.description,slug:H.slug??k.slug,isPublic:H.isPublic??k.isPublic,tags:H.tags??k.tags,status:H.status??k.status,updatedAt:new Date}}async function f(H){if(!$.find((V)=>V.id===H.projectId))throw Error("NOT_FOUND");return{success:!0}}import{web as h}from"@contractspec/lib.runtime-sandbox";var{generateId:P}=h;function z(H){return{id:H.id,projectId:H.projectId,organizationId:H.organizationId,name:H.name,description:H.description??void 0,status:H.status,tier:H.tier,createdAt:new Date(H.createdAt),updatedAt:new Date(H.updatedAt)}}function T(H){return{id:H.id,projectId:H.projectId,organizationId:H.organizationId,plan:H.plan,status:H.status,billingCycle:H.billingCycle,currentPeriodStart:new Date(H.currentPeriodStart),currentPeriodEnd:new Date(H.currentPeriodEnd),cancelAtPeriodEnd:Boolean(H.cancelAtPeriodEnd)}}function c(H){async function k(L){let{projectId:v,organizationId:F,status:Q,search:X,limit:x=20,offset:N=0}=L,D="WHERE projectId = ?",G=[v];if(F)D+=" AND organizationId = ?",G.push(F);if(Q&&Q!=="all")D+=" AND status = ?",G.push(Q);if(X)D+=" AND (name LIKE ? OR description LIKE ?)",G.push(`%${X}%`,`%${X}%`);let Y=(await H.query(`SELECT COUNT(*) as count FROM saas_project ${D}`,G)).rows[0]?.count??0,l=(await H.query(`SELECT * FROM saas_project ${D} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,[...G,x,N])).rows;return{items:l.map(z),total:Y,hasMore:N+l.length<Y}}async function V(L){let v=(await H.query("SELECT * FROM saas_project WHERE id = ?",[L])).rows;return v[0]?z(v[0]):null}async function y(L,v){let F=P("proj"),Q=new Date().toISOString();await H.execute(`INSERT INTO saas_project (id, projectId, organizationId, name, description, status, tier, createdAt, updatedAt)
|
|
2
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,[F,v.projectId,v.organizationId,L.name,L.description??null,"DRAFT",L.tier??"FREE",Q,Q]);let X=(await H.query("SELECT * FROM saas_project WHERE id = ?",[F])).rows;return z(X[0])}async function m(L){let v=new Date().toISOString(),F=["updatedAt = ?"],Q=[v];if(L.name!==void 0)F.push("name = ?"),Q.push(L.name);if(L.description!==void 0)F.push("description = ?"),Q.push(L.description);if(L.status!==void 0)F.push("status = ?"),Q.push(L.status);Q.push(L.id),await H.execute(`UPDATE saas_project SET ${F.join(", ")} WHERE id = ?`,Q);let X=(await H.query("SELECT * FROM saas_project WHERE id = ?",[L.id])).rows;if(!X[0])throw Error("NOT_FOUND");return z(X[0])}async function W(L){await H.execute("DELETE FROM saas_project WHERE id = ?",[L])}async function g(L){let v="SELECT * FROM saas_subscription WHERE projectId = ?",F=[L.projectId];if(L.organizationId)v+=" AND organizationId = ?",F.push(L.organizationId);v+=" LIMIT 1";let Q=(await H.query(v,F)).rows;return Q[0]?T(Q[0]):null}return{listProjects:k,getProject:V,createProject:y,updateProject:m,deleteProject:W,getSubscription:g}}export{K as mockUpdateProjectHandler,A as mockRecordUsageHandler,U as mockListProjectsHandler,q as mockGetUsageSummaryHandler,J as mockGetSubscriptionHandler,I as mockGetProjectHandler,f as mockDeleteProjectHandler,R as mockCreateProjectHandler,E as mockCheckFeatureAccessHandler,c as createSaasHandlers};
|
|
@@ -1,134 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var { generateId } = web;
|
|
4
|
-
function rowToProject(row) {
|
|
5
|
-
return {
|
|
6
|
-
id: row.id,
|
|
7
|
-
projectId: row.projectId,
|
|
8
|
-
organizationId: row.organizationId,
|
|
9
|
-
name: row.name,
|
|
10
|
-
description: row.description ?? undefined,
|
|
11
|
-
status: row.status,
|
|
12
|
-
tier: row.tier,
|
|
13
|
-
createdAt: new Date(row.createdAt),
|
|
14
|
-
updatedAt: new Date(row.updatedAt)
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
function rowToSubscription(row) {
|
|
18
|
-
return {
|
|
19
|
-
id: row.id,
|
|
20
|
-
projectId: row.projectId,
|
|
21
|
-
organizationId: row.organizationId,
|
|
22
|
-
plan: row.plan,
|
|
23
|
-
status: row.status,
|
|
24
|
-
billingCycle: row.billingCycle,
|
|
25
|
-
currentPeriodStart: new Date(row.currentPeriodStart),
|
|
26
|
-
currentPeriodEnd: new Date(row.currentPeriodEnd),
|
|
27
|
-
cancelAtPeriodEnd: Boolean(row.cancelAtPeriodEnd)
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
function createSaasHandlers(db) {
|
|
31
|
-
async function listProjects(input) {
|
|
32
|
-
const {
|
|
33
|
-
projectId,
|
|
34
|
-
organizationId,
|
|
35
|
-
status,
|
|
36
|
-
search,
|
|
37
|
-
limit = 20,
|
|
38
|
-
offset = 0
|
|
39
|
-
} = input;
|
|
40
|
-
let whereClause = "WHERE projectId = ?";
|
|
41
|
-
const params = [projectId];
|
|
42
|
-
if (organizationId) {
|
|
43
|
-
whereClause += " AND organizationId = ?";
|
|
44
|
-
params.push(organizationId);
|
|
45
|
-
}
|
|
46
|
-
if (status && status !== "all") {
|
|
47
|
-
whereClause += " AND status = ?";
|
|
48
|
-
params.push(status);
|
|
49
|
-
}
|
|
50
|
-
if (search) {
|
|
51
|
-
whereClause += " AND (name LIKE ? OR description LIKE ?)";
|
|
52
|
-
params.push(`%${search}%`, `%${search}%`);
|
|
53
|
-
}
|
|
54
|
-
const countResult = (await db.query(`SELECT COUNT(*) as count FROM saas_project ${whereClause}`, params)).rows;
|
|
55
|
-
const total = countResult[0]?.count ?? 0;
|
|
56
|
-
const rows = (await db.query(`SELECT * FROM saas_project ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`, [...params, limit, offset])).rows;
|
|
57
|
-
return {
|
|
58
|
-
items: rows.map(rowToProject),
|
|
59
|
-
total,
|
|
60
|
-
hasMore: offset + rows.length < total
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
async function getProject(id) {
|
|
64
|
-
const rows = (await db.query(`SELECT * FROM saas_project WHERE id = ?`, [id])).rows;
|
|
65
|
-
return rows[0] ? rowToProject(rows[0]) : null;
|
|
66
|
-
}
|
|
67
|
-
async function createProject(input, context) {
|
|
68
|
-
const id = generateId("proj");
|
|
69
|
-
const now = new Date().toISOString();
|
|
70
|
-
await db.execute(`INSERT INTO saas_project (id, projectId, organizationId, name, description, status, tier, createdAt, updatedAt)
|
|
71
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
72
|
-
id,
|
|
73
|
-
context.projectId,
|
|
74
|
-
context.organizationId,
|
|
75
|
-
input.name,
|
|
76
|
-
input.description ?? null,
|
|
77
|
-
"DRAFT",
|
|
78
|
-
input.tier ?? "FREE",
|
|
79
|
-
now,
|
|
80
|
-
now
|
|
81
|
-
]);
|
|
82
|
-
const rows = (await db.query(`SELECT * FROM saas_project WHERE id = ?`, [id])).rows;
|
|
83
|
-
return rowToProject(rows[0]);
|
|
84
|
-
}
|
|
85
|
-
async function updateProject(input) {
|
|
86
|
-
const now = new Date().toISOString();
|
|
87
|
-
const updates = ["updatedAt = ?"];
|
|
88
|
-
const params = [now];
|
|
89
|
-
if (input.name !== undefined) {
|
|
90
|
-
updates.push("name = ?");
|
|
91
|
-
params.push(input.name);
|
|
92
|
-
}
|
|
93
|
-
if (input.description !== undefined) {
|
|
94
|
-
updates.push("description = ?");
|
|
95
|
-
params.push(input.description);
|
|
96
|
-
}
|
|
97
|
-
if (input.status !== undefined) {
|
|
98
|
-
updates.push("status = ?");
|
|
99
|
-
params.push(input.status);
|
|
100
|
-
}
|
|
101
|
-
params.push(input.id);
|
|
102
|
-
await db.execute(`UPDATE saas_project SET ${updates.join(", ")} WHERE id = ?`, params);
|
|
103
|
-
const rows = (await db.query(`SELECT * FROM saas_project WHERE id = ?`, [input.id])).rows;
|
|
104
|
-
if (!rows[0]) {
|
|
105
|
-
throw new Error("NOT_FOUND");
|
|
106
|
-
}
|
|
107
|
-
return rowToProject(rows[0]);
|
|
108
|
-
}
|
|
109
|
-
async function deleteProject(id) {
|
|
110
|
-
await db.execute(`DELETE FROM saas_project WHERE id = ?`, [id]);
|
|
111
|
-
}
|
|
112
|
-
async function getSubscription(input) {
|
|
113
|
-
let query = `SELECT * FROM saas_subscription WHERE projectId = ?`;
|
|
114
|
-
const params = [input.projectId];
|
|
115
|
-
if (input.organizationId) {
|
|
116
|
-
query += " AND organizationId = ?";
|
|
117
|
-
params.push(input.organizationId);
|
|
118
|
-
}
|
|
119
|
-
query += " LIMIT 1";
|
|
120
|
-
const rows = (await db.query(query, params)).rows;
|
|
121
|
-
return rows[0] ? rowToSubscription(rows[0]) : null;
|
|
122
|
-
}
|
|
123
|
-
return {
|
|
124
|
-
listProjects,
|
|
125
|
-
getProject,
|
|
126
|
-
createProject,
|
|
127
|
-
updateProject,
|
|
128
|
-
deleteProject,
|
|
129
|
-
getSubscription
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
export {
|
|
133
|
-
createSaasHandlers
|
|
134
|
-
};
|
|
1
|
+
import{web as W}from"@contractspec/lib.runtime-sandbox";var{generateId:X}=W;function H(k){return{id:k.id,projectId:k.projectId,organizationId:k.organizationId,name:k.name,description:k.description??void 0,status:k.status,tier:k.tier,createdAt:new Date(k.createdAt),updatedAt:new Date(k.updatedAt)}}function Y(k){return{id:k.id,projectId:k.projectId,organizationId:k.organizationId,plan:k.plan,status:k.status,billingCycle:k.billingCycle,currentPeriodStart:new Date(k.currentPeriodStart),currentPeriodEnd:new Date(k.currentPeriodEnd),cancelAtPeriodEnd:Boolean(k.cancelAtPeriodEnd)}}function $(k){async function M(v){let{projectId:B,organizationId:D,status:A,search:E,limit:V=20,offset:J=0}=v,F="WHERE projectId = ?",G=[B];if(D)F+=" AND organizationId = ?",G.push(D);if(A&&A!=="all")F+=" AND status = ?",G.push(A);if(E)F+=" AND (name LIKE ? OR description LIKE ?)",G.push(`%${E}%`,`%${E}%`);let K=(await k.query(`SELECT COUNT(*) as count FROM saas_project ${F}`,G)).rows[0]?.count??0,L=(await k.query(`SELECT * FROM saas_project ${F} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,[...G,V,J])).rows;return{items:L.map(H),total:K,hasMore:J+L.length<K}}async function N(v){let B=(await k.query("SELECT * FROM saas_project WHERE id = ?",[v])).rows;return B[0]?H(B[0]):null}async function O(v,B){let D=X("proj"),A=new Date().toISOString();await k.execute(`INSERT INTO saas_project (id, projectId, organizationId, name, description, status, tier, createdAt, updatedAt)
|
|
2
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,[D,B.projectId,B.organizationId,v.name,v.description??null,"DRAFT",v.tier??"FREE",A,A]);let E=(await k.query("SELECT * FROM saas_project WHERE id = ?",[D])).rows;return H(E[0])}async function Q(v){let B=new Date().toISOString(),D=["updatedAt = ?"],A=[B];if(v.name!==void 0)D.push("name = ?"),A.push(v.name);if(v.description!==void 0)D.push("description = ?"),A.push(v.description);if(v.status!==void 0)D.push("status = ?"),A.push(v.status);A.push(v.id),await k.execute(`UPDATE saas_project SET ${D.join(", ")} WHERE id = ?`,A);let E=(await k.query("SELECT * FROM saas_project WHERE id = ?",[v.id])).rows;if(!E[0])throw Error("NOT_FOUND");return H(E[0])}async function R(v){await k.execute("DELETE FROM saas_project WHERE id = ?",[v])}async function U(v){let B="SELECT * FROM saas_subscription WHERE projectId = ?",D=[v.projectId];if(v.organizationId)B+=" AND organizationId = ?",D.push(v.organizationId);B+=" LIMIT 1";let A=(await k.query(B,D)).rows;return A[0]?Y(A[0]):null}return{listProjects:M,getProject:N,createProject:O,updateProject:Q,deleteProject:R,getSubscription:U}}export{$ as createSaasHandlers};
|