@contractspec/example.saas-boilerplate 1.44.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.
- package/.turbo/turbo-build$colon$bundle.log +113 -0
- package/.turbo/turbo-build.log +114 -0
- package/CHANGELOG.md +246 -0
- package/LICENSE +21 -0
- package/README.md +156 -0
- package/dist/billing/billing.entity.d.ts +61 -0
- package/dist/billing/billing.entity.d.ts.map +1 -0
- package/dist/billing/billing.entity.js +122 -0
- package/dist/billing/billing.entity.js.map +1 -0
- package/dist/billing/billing.enum.d.ts +16 -0
- package/dist/billing/billing.enum.d.ts.map +1 -0
- package/dist/billing/billing.enum.js +27 -0
- package/dist/billing/billing.enum.js.map +1 -0
- package/dist/billing/billing.event.d.ts +86 -0
- package/dist/billing/billing.event.d.ts.map +1 -0
- package/dist/billing/billing.event.js +153 -0
- package/dist/billing/billing.event.js.map +1 -0
- package/dist/billing/billing.handler.d.ts +82 -0
- package/dist/billing/billing.handler.d.ts.map +1 -0
- package/dist/billing/billing.handler.js +58 -0
- package/dist/billing/billing.handler.js.map +1 -0
- package/dist/billing/billing.operations.d.ts +166 -0
- package/dist/billing/billing.operations.d.ts.map +1 -0
- package/dist/billing/billing.operations.js +181 -0
- package/dist/billing/billing.operations.js.map +1 -0
- package/dist/billing/billing.presentation.d.ts +15 -0
- package/dist/billing/billing.presentation.d.ts.map +1 -0
- package/dist/billing/billing.presentation.js +59 -0
- package/dist/billing/billing.presentation.js.map +1 -0
- package/dist/billing/billing.schema.d.ts +201 -0
- package/dist/billing/billing.schema.d.ts.map +1 -0
- package/dist/billing/billing.schema.js +214 -0
- package/dist/billing/billing.schema.js.map +1 -0
- package/dist/billing/index.d.ts +8 -0
- package/dist/billing/index.js +9 -0
- package/dist/dashboard/dashboard.presentation.d.ts +15 -0
- package/dist/dashboard/dashboard.presentation.d.ts.map +1 -0
- package/dist/dashboard/dashboard.presentation.js +55 -0
- package/dist/dashboard/dashboard.presentation.js.map +1 -0
- package/dist/dashboard/index.d.ts +2 -0
- package/dist/dashboard/index.js +3 -0
- package/dist/docs/index.d.ts +1 -0
- package/dist/docs/index.js +1 -0
- package/dist/docs/saas-boilerplate.docblock.d.ts +1 -0
- package/dist/docs/saas-boilerplate.docblock.js +100 -0
- package/dist/docs/saas-boilerplate.docblock.js.map +1 -0
- package/dist/example.d.ts +37 -0
- package/dist/example.d.ts.map +1 -0
- package/dist/example.js +46 -0
- package/dist/example.js.map +1 -0
- package/dist/handlers/index.d.ts +3 -0
- package/dist/handlers/index.js +4 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/dist/presentations/index.d.ts +17 -0
- package/dist/presentations/index.d.ts.map +1 -0
- package/dist/presentations/index.js +17 -0
- package/dist/presentations/index.js.map +1 -0
- package/dist/project/index.d.ts +8 -0
- package/dist/project/index.js +9 -0
- package/dist/project/project.entity.d.ts +40 -0
- package/dist/project/project.entity.d.ts.map +1 -0
- package/dist/project/project.entity.js +85 -0
- package/dist/project/project.entity.js.map +1 -0
- package/dist/project/project.enum.d.ts +16 -0
- package/dist/project/project.enum.d.ts.map +1 -0
- package/dist/project/project.enum.js +26 -0
- package/dist/project/project.enum.js.map +1 -0
- package/dist/project/project.event.d.ts +92 -0
- package/dist/project/project.event.d.ts.map +1 -0
- package/dist/project/project.event.js +165 -0
- package/dist/project/project.event.js.map +1 -0
- package/dist/project/project.handler.d.ts +72 -0
- package/dist/project/project.handler.d.ts.map +1 -0
- package/dist/project/project.handler.js +82 -0
- package/dist/project/project.handler.js.map +1 -0
- package/dist/project/project.operations.d.ts +419 -0
- package/dist/project/project.operations.d.ts.map +1 -0
- package/dist/project/project.operations.js +260 -0
- package/dist/project/project.operations.js.map +1 -0
- package/dist/project/project.presentation.d.ts +15 -0
- package/dist/project/project.presentation.d.ts.map +1 -0
- package/dist/project/project.presentation.js +65 -0
- package/dist/project/project.presentation.js.map +1 -0
- package/dist/project/project.schema.d.ts +235 -0
- package/dist/project/project.schema.d.ts.map +1 -0
- package/dist/project/project.schema.js +215 -0
- package/dist/project/project.schema.js.map +1 -0
- package/dist/saas-boilerplate.feature.d.ts +12 -0
- package/dist/saas-boilerplate.feature.d.ts.map +1 -0
- package/dist/saas-boilerplate.feature.js +201 -0
- package/dist/saas-boilerplate.feature.js.map +1 -0
- package/dist/settings/index.d.ts +3 -0
- package/dist/settings/index.js +4 -0
- package/dist/settings/settings.entity.d.ts +37 -0
- package/dist/settings/settings.entity.d.ts.map +1 -0
- package/dist/settings/settings.entity.js +78 -0
- package/dist/settings/settings.entity.js.map +1 -0
- package/dist/settings/settings.enum.d.ts +10 -0
- package/dist/settings/settings.enum.d.ts.map +1 -0
- package/dist/settings/settings.enum.js +21 -0
- package/dist/settings/settings.enum.js.map +1 -0
- package/dist/shared/mock-data.d.ts +86 -0
- package/dist/shared/mock-data.d.ts.map +1 -0
- package/dist/shared/mock-data.js +138 -0
- package/dist/shared/mock-data.js.map +1 -0
- package/example.ts +1 -0
- package/package.json +113 -0
- package/src/billing/billing.entity.ts +158 -0
- package/src/billing/billing.enum.ts +23 -0
- package/src/billing/billing.event.ts +108 -0
- package/src/billing/billing.handler.ts +137 -0
- package/src/billing/billing.operations.ts +187 -0
- package/src/billing/billing.presentation.ts +57 -0
- package/src/billing/billing.schema.ts +133 -0
- package/src/billing/index.ts +64 -0
- package/src/dashboard/dashboard.presentation.ts +57 -0
- package/src/dashboard/index.ts +8 -0
- package/src/docs/index.ts +1 -0
- package/src/docs/saas-boilerplate.docblock.ts +98 -0
- package/src/example.ts +31 -0
- package/src/handlers/index.ts +20 -0
- package/src/index.ts +71 -0
- package/src/presentations/index.ts +36 -0
- package/src/project/index.ts +66 -0
- package/src/project/project.entity.ts +93 -0
- package/src/project/project.enum.ts +22 -0
- package/src/project/project.event.ts +128 -0
- package/src/project/project.handler.ts +168 -0
- package/src/project/project.operations.ts +272 -0
- package/src/project/project.presentation.ts +59 -0
- package/src/project/project.schema.ts +147 -0
- package/src/saas-boilerplate.feature.ts +109 -0
- package/src/settings/index.ts +9 -0
- package/src/settings/settings.entity.ts +89 -0
- package/src/settings/settings.enum.ts +11 -0
- package/src/shared/mock-data.ts +110 -0
- package/tsconfig.json +10 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsdown.config.js +7 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { DocBlock } from '@contractspec/lib.contracts/docs';
|
|
2
|
+
import { registerDocBlocks } from '@contractspec/lib.contracts/docs';
|
|
3
|
+
|
|
4
|
+
const saasBoilerplateDocBlocks: DocBlock[] = [
|
|
5
|
+
{
|
|
6
|
+
id: 'docs.examples.saas-boilerplate.goal',
|
|
7
|
+
title: 'SaaS Boilerplate — Goal',
|
|
8
|
+
summary:
|
|
9
|
+
'Multi-tenant SaaS foundation with orgs, members, projects, settings, and usage.',
|
|
10
|
+
kind: 'goal',
|
|
11
|
+
visibility: 'public',
|
|
12
|
+
route: '/docs/examples/saas-boilerplate/goal',
|
|
13
|
+
tags: ['saas', 'goal'],
|
|
14
|
+
body: `## Why it matters
|
|
15
|
+
- Provides a regenerable SaaS base: orgs, members, projects, settings, usage/billing.
|
|
16
|
+
- Avoids drift across identity, settings, and usage capture.
|
|
17
|
+
|
|
18
|
+
## Business/Product goal
|
|
19
|
+
- Ship SaaS faster with tenant isolation, RBAC, and usage metering baked in.
|
|
20
|
+
- Keep audit/notifications ready for compliance and customer comms.
|
|
21
|
+
|
|
22
|
+
## Success criteria
|
|
23
|
+
- Spec changes to org/project/settings/usage regenerate UI/API/events cleanly.
|
|
24
|
+
- Tenant isolation and RBAC stay enforced; usage data is captured with PII scopes.`,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'docs.examples.saas-boilerplate.usage',
|
|
28
|
+
title: 'SaaS Boilerplate — Usage',
|
|
29
|
+
summary: 'How to seed, extend, and regenerate the SaaS base.',
|
|
30
|
+
kind: 'usage',
|
|
31
|
+
visibility: 'public',
|
|
32
|
+
route: '/docs/examples/saas-boilerplate/usage',
|
|
33
|
+
tags: ['saas', 'usage'],
|
|
34
|
+
body: `## Setup
|
|
35
|
+
1) Seed (if available) or create orgs, members, and projects via UI.
|
|
36
|
+
2) Configure Notifications for invites and project events; set policy.pii for sensitive fields.
|
|
37
|
+
|
|
38
|
+
## Extend & regenerate
|
|
39
|
+
1) Adjust schemas (project metadata, settings, usage records) in spec.
|
|
40
|
+
2) Regenerate to sync UI/API/events and usage metering.
|
|
41
|
+
3) Use Feature Flags to roll out new settings or billing fields gradually.
|
|
42
|
+
|
|
43
|
+
## Guardrails
|
|
44
|
+
- Keep tenant/role context explicit in contracts and presentations.
|
|
45
|
+
- Emit events for invites, project changes, and usage records; log in Audit Trail.
|
|
46
|
+
- Redact sensitive user/org data in markdown/JSON outputs.`,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 'docs.examples.saas-boilerplate.reference',
|
|
50
|
+
title: 'SaaS Boilerplate — Reference',
|
|
51
|
+
summary:
|
|
52
|
+
'Entities, contracts, events, and presentations for the SaaS starter.',
|
|
53
|
+
kind: 'reference',
|
|
54
|
+
visibility: 'public',
|
|
55
|
+
route: '/docs/examples/saas-boilerplate',
|
|
56
|
+
tags: ['saas', 'reference'],
|
|
57
|
+
body: `## Entities
|
|
58
|
+
- Organization, Member, Role, Project, AppSettings, UserSettings, BillingUsage.
|
|
59
|
+
|
|
60
|
+
## Contracts
|
|
61
|
+
- org/project CRUD, invites, role assignment, usage recording.
|
|
62
|
+
|
|
63
|
+
## Events
|
|
64
|
+
- org.created, member.invited/accepted, project.created/updated, usage.recorded.
|
|
65
|
+
|
|
66
|
+
## Presentations
|
|
67
|
+
- Org/project dashboards, member management, settings screens, usage views.
|
|
68
|
+
|
|
69
|
+
## Notes
|
|
70
|
+
- Tenant isolation is mandatory; enforce via RBAC/policies.
|
|
71
|
+
- Usage/Metering drives billing/limits; keep units explicit.`,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 'docs.examples.saas-boilerplate.constraints',
|
|
75
|
+
title: 'SaaS Boilerplate — Constraints & Safety',
|
|
76
|
+
summary:
|
|
77
|
+
'Internal guardrails for tenancy, RBAC, usage metering, and regeneration.',
|
|
78
|
+
kind: 'reference',
|
|
79
|
+
visibility: 'internal',
|
|
80
|
+
route: '/docs/examples/saas-boilerplate/constraints',
|
|
81
|
+
tags: ['saas', 'constraints', 'internal'],
|
|
82
|
+
body: `## Constraints
|
|
83
|
+
- Tenant isolation and RBAC must remain explicit in spec; no implicit defaults in code.
|
|
84
|
+
- Events to emit: org.created, member.invited/accepted, project.created/updated, usage.recorded.
|
|
85
|
+
- Regeneration must not change billing/usage semantics without spec diffs.
|
|
86
|
+
|
|
87
|
+
## PII & Settings
|
|
88
|
+
- Mark PII (user emails, names) for redaction; keep settings scoped to org/member.
|
|
89
|
+
- Avoid leaking secrets/config in markdown/JSON presentations.
|
|
90
|
+
|
|
91
|
+
## Verification
|
|
92
|
+
- Add fixtures for usage recording and role changes.
|
|
93
|
+
- Ensure Audit/Notifications remain wired for invites/project updates.
|
|
94
|
+
- Use Feature Flags for new settings/billing fields; default safe/off.`,
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
registerDocBlocks(saasBoilerplateDocBlocks);
|
package/src/example.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const example = {
|
|
2
|
+
id: 'saas-boilerplate',
|
|
3
|
+
title: 'SaaS Boilerplate',
|
|
4
|
+
summary:
|
|
5
|
+
'Multi-tenant SaaS foundation with orgs, projects, settings, billing usage, and RBAC.',
|
|
6
|
+
tags: ['saas', 'multi-tenant', 'billing', 'rbac'],
|
|
7
|
+
kind: 'template',
|
|
8
|
+
visibility: 'public',
|
|
9
|
+
docs: {
|
|
10
|
+
rootDocId: 'docs.examples.saas-boilerplate',
|
|
11
|
+
},
|
|
12
|
+
entrypoints: {
|
|
13
|
+
packageName: '@contractspec/example.saas-boilerplate',
|
|
14
|
+
feature: './feature',
|
|
15
|
+
contracts: './contracts',
|
|
16
|
+
presentations: './presentations',
|
|
17
|
+
handlers: './handlers',
|
|
18
|
+
docs: './docs',
|
|
19
|
+
},
|
|
20
|
+
surfaces: {
|
|
21
|
+
templates: true,
|
|
22
|
+
sandbox: {
|
|
23
|
+
enabled: true,
|
|
24
|
+
modes: ['playground', 'specs', 'builder', 'markdown', 'evolution'],
|
|
25
|
+
},
|
|
26
|
+
studio: { enabled: true, installable: true },
|
|
27
|
+
mcp: { enabled: true },
|
|
28
|
+
},
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
export default example;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SaaS Boilerplate Handlers - re-exports from domain modules for backward compatibility.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Billing handlers
|
|
6
|
+
export {
|
|
7
|
+
mockGetSubscriptionHandler,
|
|
8
|
+
mockRecordUsageHandler,
|
|
9
|
+
mockGetUsageSummaryHandler,
|
|
10
|
+
mockCheckFeatureAccessHandler,
|
|
11
|
+
} from '../billing/billing.handler';
|
|
12
|
+
|
|
13
|
+
// Project handlers
|
|
14
|
+
export {
|
|
15
|
+
mockCreateProjectHandler,
|
|
16
|
+
mockGetProjectHandler,
|
|
17
|
+
mockListProjectsHandler,
|
|
18
|
+
mockUpdateProjectHandler,
|
|
19
|
+
mockDeleteProjectHandler,
|
|
20
|
+
} from '../project/project.handler';
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// SaaS Boilerplate Example
|
|
2
|
+
// Demonstrates ContractSpec principles for a complete SaaS application
|
|
3
|
+
|
|
4
|
+
// Export all domain modules
|
|
5
|
+
export * from './billing';
|
|
6
|
+
export * from './project';
|
|
7
|
+
export * from './settings';
|
|
8
|
+
export * from './dashboard';
|
|
9
|
+
|
|
10
|
+
// Export feature and example metadata
|
|
11
|
+
export * from './saas-boilerplate.feature';
|
|
12
|
+
export { default as example } from './example';
|
|
13
|
+
|
|
14
|
+
// Import docs for registration
|
|
15
|
+
import './docs';
|
|
16
|
+
|
|
17
|
+
// Schema composition configuration
|
|
18
|
+
import { identityRbacSchemaContribution } from '@contractspec/lib.identity-rbac';
|
|
19
|
+
import { jobsSchemaContribution } from '@contractspec/lib.jobs';
|
|
20
|
+
import { auditTrailSchemaContribution } from '@contractspec/module.audit-trail';
|
|
21
|
+
import { notificationsSchemaContribution } from '@contractspec/module.notifications';
|
|
22
|
+
import type { ModuleSchemaContribution } from '@contractspec/lib.schema';
|
|
23
|
+
import {
|
|
24
|
+
ProjectEntity,
|
|
25
|
+
ProjectMemberEntity,
|
|
26
|
+
ProjectStatusEnum,
|
|
27
|
+
} from './project/project.entity';
|
|
28
|
+
import {
|
|
29
|
+
SettingsEntity,
|
|
30
|
+
FeatureFlagEntity,
|
|
31
|
+
SettingsScopeEnum,
|
|
32
|
+
} from './settings';
|
|
33
|
+
import {
|
|
34
|
+
SubscriptionEntity,
|
|
35
|
+
BillingUsageEntity,
|
|
36
|
+
UsageLimitEntity,
|
|
37
|
+
SubscriptionStatusEnum,
|
|
38
|
+
} from './billing/billing.entity';
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* SaaS boilerplate schema contribution.
|
|
42
|
+
*/
|
|
43
|
+
export const saasBoilerplateSchemaContribution: ModuleSchemaContribution = {
|
|
44
|
+
moduleId: '@contractspec/example.saas-boilerplate',
|
|
45
|
+
entities: [
|
|
46
|
+
ProjectEntity,
|
|
47
|
+
ProjectMemberEntity,
|
|
48
|
+
SettingsEntity,
|
|
49
|
+
FeatureFlagEntity,
|
|
50
|
+
SubscriptionEntity,
|
|
51
|
+
BillingUsageEntity,
|
|
52
|
+
UsageLimitEntity,
|
|
53
|
+
],
|
|
54
|
+
enums: [ProjectStatusEnum, SettingsScopeEnum, SubscriptionStatusEnum],
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Complete schema composition for SaaS Boilerplate.
|
|
59
|
+
* Use with `database schema:compose` to generate Prisma schema.
|
|
60
|
+
*/
|
|
61
|
+
export const schemaComposition = {
|
|
62
|
+
modules: [
|
|
63
|
+
identityRbacSchemaContribution,
|
|
64
|
+
jobsSchemaContribution,
|
|
65
|
+
auditTrailSchemaContribution,
|
|
66
|
+
notificationsSchemaContribution,
|
|
67
|
+
saasBoilerplateSchemaContribution,
|
|
68
|
+
],
|
|
69
|
+
provider: 'postgresql' as const,
|
|
70
|
+
outputPath: './prisma/schema/generated.prisma',
|
|
71
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SaaS Boilerplate Presentations - re-exports from domain modules for backward compatibility.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Billing presentations
|
|
6
|
+
export {
|
|
7
|
+
SubscriptionPresentation,
|
|
8
|
+
UsageDashboardPresentation,
|
|
9
|
+
} from '../billing/billing.presentation';
|
|
10
|
+
|
|
11
|
+
// Project presentations
|
|
12
|
+
export {
|
|
13
|
+
ProjectListPresentation,
|
|
14
|
+
ProjectDetailPresentation,
|
|
15
|
+
} from '../project/project.presentation';
|
|
16
|
+
|
|
17
|
+
// Dashboard presentations
|
|
18
|
+
export {
|
|
19
|
+
SaasDashboardPresentation,
|
|
20
|
+
SettingsPanelPresentation,
|
|
21
|
+
} from '../dashboard/dashboard.presentation';
|
|
22
|
+
|
|
23
|
+
// All presentations collection
|
|
24
|
+
export const SaasBoilerplatePresentations = {
|
|
25
|
+
// Billing
|
|
26
|
+
SubscriptionPresentation: undefined,
|
|
27
|
+
UsageDashboardPresentation: undefined,
|
|
28
|
+
|
|
29
|
+
// Project
|
|
30
|
+
ProjectListPresentation: undefined,
|
|
31
|
+
ProjectDetailPresentation: undefined,
|
|
32
|
+
|
|
33
|
+
// Dashboard
|
|
34
|
+
SaasDashboardPresentation: undefined,
|
|
35
|
+
SettingsPanelPresentation: undefined,
|
|
36
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project domain - project management within organizations.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Enums
|
|
6
|
+
export {
|
|
7
|
+
ProjectStatusSchemaEnum,
|
|
8
|
+
ProjectStatusFilterEnum,
|
|
9
|
+
} from './project.enum';
|
|
10
|
+
|
|
11
|
+
// Schema models
|
|
12
|
+
export {
|
|
13
|
+
ProjectModel,
|
|
14
|
+
CreateProjectInputModel,
|
|
15
|
+
UpdateProjectInputModel,
|
|
16
|
+
GetProjectInputModel,
|
|
17
|
+
DeleteProjectInputModel,
|
|
18
|
+
DeleteProjectOutputModel,
|
|
19
|
+
ProjectDeletedPayloadModel,
|
|
20
|
+
ListProjectsInputModel,
|
|
21
|
+
ListProjectsOutputModel,
|
|
22
|
+
} from './project.schema';
|
|
23
|
+
|
|
24
|
+
// Contracts
|
|
25
|
+
export {
|
|
26
|
+
CreateProjectContract,
|
|
27
|
+
GetProjectContract,
|
|
28
|
+
UpdateProjectContract,
|
|
29
|
+
DeleteProjectContract,
|
|
30
|
+
ListProjectsContract,
|
|
31
|
+
} from './project.operations';
|
|
32
|
+
|
|
33
|
+
// Events
|
|
34
|
+
export {
|
|
35
|
+
ProjectCreatedEvent,
|
|
36
|
+
ProjectUpdatedEvent,
|
|
37
|
+
ProjectDeletedEvent,
|
|
38
|
+
ProjectArchivedEvent,
|
|
39
|
+
} from './project.event';
|
|
40
|
+
|
|
41
|
+
// Entities
|
|
42
|
+
export {
|
|
43
|
+
ProjectStatusEnum,
|
|
44
|
+
ProjectEntity,
|
|
45
|
+
ProjectMemberEntity,
|
|
46
|
+
} from './project.entity';
|
|
47
|
+
|
|
48
|
+
// Presentations
|
|
49
|
+
export {
|
|
50
|
+
ProjectListPresentation,
|
|
51
|
+
ProjectDetailPresentation,
|
|
52
|
+
} from './project.presentation';
|
|
53
|
+
|
|
54
|
+
// Handlers
|
|
55
|
+
export {
|
|
56
|
+
mockListProjectsHandler,
|
|
57
|
+
mockGetProjectHandler,
|
|
58
|
+
mockCreateProjectHandler,
|
|
59
|
+
mockUpdateProjectHandler,
|
|
60
|
+
mockDeleteProjectHandler,
|
|
61
|
+
type Project,
|
|
62
|
+
type CreateProjectInput,
|
|
63
|
+
type UpdateProjectInput,
|
|
64
|
+
type ListProjectsInput,
|
|
65
|
+
type ListProjectsOutput,
|
|
66
|
+
} from './project.handler';
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineEntity,
|
|
3
|
+
defineEntityEnum,
|
|
4
|
+
field,
|
|
5
|
+
index,
|
|
6
|
+
} from '@contractspec/lib.schema';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Project status enum for entities.
|
|
10
|
+
*/
|
|
11
|
+
export const ProjectStatusEnum = defineEntityEnum({
|
|
12
|
+
name: 'ProjectStatus',
|
|
13
|
+
values: ['DRAFT', 'ACTIVE', 'ARCHIVED', 'DELETED'] as const,
|
|
14
|
+
schema: 'saas_app',
|
|
15
|
+
description: 'Status of a project.',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Project entity - team-scoped work container.
|
|
20
|
+
*/
|
|
21
|
+
export const ProjectEntity = defineEntity({
|
|
22
|
+
name: 'Project',
|
|
23
|
+
description: 'A project belonging to an organization.',
|
|
24
|
+
schema: 'saas_app',
|
|
25
|
+
map: 'project',
|
|
26
|
+
fields: {
|
|
27
|
+
id: field.id({ description: 'Unique project ID' }),
|
|
28
|
+
name: field.string({ description: 'Project name' }),
|
|
29
|
+
description: field.string({
|
|
30
|
+
isOptional: true,
|
|
31
|
+
description: 'Project description',
|
|
32
|
+
}),
|
|
33
|
+
slug: field.string({
|
|
34
|
+
isOptional: true,
|
|
35
|
+
description: 'URL-friendly identifier',
|
|
36
|
+
}),
|
|
37
|
+
|
|
38
|
+
// Ownership
|
|
39
|
+
organizationId: field.foreignKey({ description: 'Owning organization' }),
|
|
40
|
+
createdBy: field.foreignKey({
|
|
41
|
+
description: 'User who created the project',
|
|
42
|
+
}),
|
|
43
|
+
|
|
44
|
+
// Status
|
|
45
|
+
status: field.enum('ProjectStatus', { default: 'DRAFT' }),
|
|
46
|
+
|
|
47
|
+
// Settings
|
|
48
|
+
isPublic: field.boolean({
|
|
49
|
+
default: false,
|
|
50
|
+
description: 'Whether project is publicly visible',
|
|
51
|
+
}),
|
|
52
|
+
settings: field.json({
|
|
53
|
+
isOptional: true,
|
|
54
|
+
description: 'Project-specific settings',
|
|
55
|
+
}),
|
|
56
|
+
|
|
57
|
+
// Metadata
|
|
58
|
+
tags: field.string({ isArray: true, description: 'Project tags' }),
|
|
59
|
+
metadata: field.json({ isOptional: true }),
|
|
60
|
+
|
|
61
|
+
// Timestamps
|
|
62
|
+
createdAt: field.createdAt(),
|
|
63
|
+
updatedAt: field.updatedAt(),
|
|
64
|
+
archivedAt: field.dateTime({ isOptional: true }),
|
|
65
|
+
},
|
|
66
|
+
indexes: [
|
|
67
|
+
index.on(['organizationId', 'status']),
|
|
68
|
+
index.on(['organizationId', 'createdAt']),
|
|
69
|
+
index.unique(['organizationId', 'slug']),
|
|
70
|
+
],
|
|
71
|
+
enums: [ProjectStatusEnum],
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* ProjectMember entity - project-level access.
|
|
76
|
+
*/
|
|
77
|
+
export const ProjectMemberEntity = defineEntity({
|
|
78
|
+
name: 'ProjectMember',
|
|
79
|
+
description: 'User access to a specific project.',
|
|
80
|
+
schema: 'saas_app',
|
|
81
|
+
map: 'project_member',
|
|
82
|
+
fields: {
|
|
83
|
+
id: field.id(),
|
|
84
|
+
projectId: field.foreignKey(),
|
|
85
|
+
userId: field.foreignKey(),
|
|
86
|
+
role: field.string({
|
|
87
|
+
description: 'Role in project (owner, editor, viewer)',
|
|
88
|
+
}),
|
|
89
|
+
addedBy: field.string({ isOptional: true }),
|
|
90
|
+
createdAt: field.createdAt(),
|
|
91
|
+
},
|
|
92
|
+
indexes: [index.unique(['projectId', 'userId'])],
|
|
93
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineEnum } from '@contractspec/lib.schema';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Project status enum for contract schemas.
|
|
5
|
+
* Note: Entity enum is defined separately in project.entity.ts
|
|
6
|
+
*/
|
|
7
|
+
export const ProjectStatusSchemaEnum = defineEnum('ProjectStatus', [
|
|
8
|
+
'DRAFT',
|
|
9
|
+
'ACTIVE',
|
|
10
|
+
'ARCHIVED',
|
|
11
|
+
'DELETED',
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Project status filter enum (includes 'all' option).
|
|
16
|
+
*/
|
|
17
|
+
export const ProjectStatusFilterEnum = defineEnum('ProjectStatusFilter', [
|
|
18
|
+
'DRAFT',
|
|
19
|
+
'ACTIVE',
|
|
20
|
+
'ARCHIVED',
|
|
21
|
+
'all',
|
|
22
|
+
]);
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { ScalarTypeEnum, defineSchemaModel } from '@contractspec/lib.schema';
|
|
2
|
+
import { defineEvent } from '@contractspec/lib.contracts';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Payload when a project is created.
|
|
6
|
+
*/
|
|
7
|
+
const ProjectCreatedPayload = defineSchemaModel({
|
|
8
|
+
name: 'ProjectCreatedPayload',
|
|
9
|
+
description: 'Payload when a project is created',
|
|
10
|
+
fields: {
|
|
11
|
+
projectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
12
|
+
name: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
13
|
+
organizationId: {
|
|
14
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
15
|
+
isOptional: false,
|
|
16
|
+
},
|
|
17
|
+
createdBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
18
|
+
createdAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Payload when a project is updated.
|
|
24
|
+
*/
|
|
25
|
+
const ProjectUpdatedPayload = defineSchemaModel({
|
|
26
|
+
name: 'ProjectUpdatedPayload',
|
|
27
|
+
description: 'Payload when a project is updated',
|
|
28
|
+
fields: {
|
|
29
|
+
projectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
30
|
+
updatedFields: {
|
|
31
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
32
|
+
isArray: true,
|
|
33
|
+
isOptional: false,
|
|
34
|
+
},
|
|
35
|
+
updatedBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
36
|
+
updatedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Payload when a project is deleted.
|
|
42
|
+
*/
|
|
43
|
+
const ProjectDeletedPayload = defineSchemaModel({
|
|
44
|
+
name: 'ProjectDeletedPayload',
|
|
45
|
+
description: 'Payload when a project is deleted',
|
|
46
|
+
fields: {
|
|
47
|
+
projectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
48
|
+
organizationId: {
|
|
49
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
50
|
+
isOptional: false,
|
|
51
|
+
},
|
|
52
|
+
deletedBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
53
|
+
deletedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Payload when a project is archived.
|
|
59
|
+
*/
|
|
60
|
+
const ProjectArchivedPayload = defineSchemaModel({
|
|
61
|
+
name: 'ProjectArchivedPayload',
|
|
62
|
+
description: 'Payload when a project is archived',
|
|
63
|
+
fields: {
|
|
64
|
+
projectId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
65
|
+
archivedBy: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
66
|
+
archivedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Event: A new project has been created.
|
|
72
|
+
*/
|
|
73
|
+
export const ProjectCreatedEvent = defineEvent({
|
|
74
|
+
meta: {
|
|
75
|
+
key: 'project.created',
|
|
76
|
+
version: 1,
|
|
77
|
+
description: 'A new project has been created.',
|
|
78
|
+
stability: 'stable',
|
|
79
|
+
owners: ['@saas-team'],
|
|
80
|
+
tags: ['project', 'created'],
|
|
81
|
+
},
|
|
82
|
+
payload: ProjectCreatedPayload,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Event: A project has been updated.
|
|
87
|
+
*/
|
|
88
|
+
export const ProjectUpdatedEvent = defineEvent({
|
|
89
|
+
meta: {
|
|
90
|
+
key: 'project.updated',
|
|
91
|
+
version: 1,
|
|
92
|
+
description: 'A project has been updated.',
|
|
93
|
+
stability: 'stable',
|
|
94
|
+
owners: ['@saas-team'],
|
|
95
|
+
tags: ['project', 'updated'],
|
|
96
|
+
},
|
|
97
|
+
payload: ProjectUpdatedPayload,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Event: A project has been deleted.
|
|
102
|
+
*/
|
|
103
|
+
export const ProjectDeletedEvent = defineEvent({
|
|
104
|
+
meta: {
|
|
105
|
+
key: 'project.deleted',
|
|
106
|
+
version: 1,
|
|
107
|
+
description: 'A project has been deleted.',
|
|
108
|
+
stability: 'stable',
|
|
109
|
+
owners: ['@saas-team'],
|
|
110
|
+
tags: ['project', 'deleted'],
|
|
111
|
+
},
|
|
112
|
+
payload: ProjectDeletedPayload,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Event: A project has been archived.
|
|
117
|
+
*/
|
|
118
|
+
export const ProjectArchivedEvent = defineEvent({
|
|
119
|
+
meta: {
|
|
120
|
+
key: 'project.archived',
|
|
121
|
+
version: 1,
|
|
122
|
+
description: 'A project has been archived.',
|
|
123
|
+
stability: 'stable',
|
|
124
|
+
owners: ['@saas-team'],
|
|
125
|
+
tags: ['project', 'archived'],
|
|
126
|
+
},
|
|
127
|
+
payload: ProjectArchivedPayload,
|
|
128
|
+
});
|