@contractspec/example.saas-boilerplate 1.46.1 → 1.47.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 +183 -108
- package/.turbo/turbo-build.log +182 -107
- package/CHANGELOG.md +45 -0
- package/README.md +0 -1
- package/dist/billing/billing.event.d.ts +4 -4
- package/dist/billing/billing.event.js +1 -1
- package/dist/billing/billing.operations.d.ts +5 -5
- package/dist/billing/billing.presentation.d.ts +3 -4
- package/dist/billing/billing.presentation.d.ts.map +1 -1
- package/dist/billing/billing.presentation.js +5 -5
- package/dist/billing/billing.presentation.js.map +1 -1
- package/dist/dashboard/dashboard.presentation.d.ts +3 -4
- package/dist/dashboard/dashboard.presentation.d.ts.map +1 -1
- package/dist/dashboard/dashboard.presentation.js +5 -5
- package/dist/dashboard/dashboard.presentation.js.map +1 -1
- package/dist/example.d.ts +2 -2
- package/dist/example.d.ts.map +1 -1
- package/dist/example.js +4 -2
- package/dist/example.js.map +1 -1
- package/dist/handlers/index.d.ts +2 -1
- package/dist/handlers/index.js +2 -1
- package/dist/handlers/saas.handlers.d.ts +68 -0
- package/dist/handlers/saas.handlers.d.ts.map +1 -0
- package/dist/handlers/saas.handlers.js +148 -0
- package/dist/handlers/saas.handlers.js.map +1 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -1
- package/dist/index.js.map +1 -1
- package/dist/project/project.enum.d.ts +3 -3
- package/dist/project/project.event.d.ts +22 -22
- package/dist/project/project.event.d.ts.map +1 -1
- package/dist/project/project.event.js +1 -1
- package/dist/project/project.operations.d.ts +103 -103
- package/dist/project/project.presentation.d.ts +3 -4
- package/dist/project/project.presentation.d.ts.map +1 -1
- package/dist/project/project.presentation.js +5 -5
- package/dist/project/project.presentation.js.map +1 -1
- package/dist/project/project.schema.d.ts +54 -54
- package/dist/saas-boilerplate.feature.d.ts +2 -2
- package/dist/saas-boilerplate.feature.d.ts.map +1 -1
- package/dist/saas-boilerplate.feature.js +9 -2
- package/dist/saas-boilerplate.feature.js.map +1 -1
- package/dist/seeders/index.d.ts +10 -0
- package/dist/seeders/index.d.ts.map +1 -0
- package/dist/seeders/index.js +19 -0
- package/dist/seeders/index.js.map +1 -0
- package/dist/settings/settings.entity.d.ts +24 -24
- package/dist/settings/settings.enum.d.ts +2 -2
- package/dist/shared/overlay-types.d.ts +34 -0
- package/dist/shared/overlay-types.d.ts.map +1 -0
- package/dist/shared/overlay-types.js +0 -0
- package/dist/tests/operations.test-spec.d.ts +10 -0
- package/dist/tests/operations.test-spec.d.ts.map +1 -0
- package/dist/tests/operations.test-spec.js +123 -0
- package/dist/tests/operations.test-spec.js.map +1 -0
- package/dist/ui/SaasDashboard.d.ts +7 -0
- package/dist/ui/SaasDashboard.d.ts.map +1 -0
- package/dist/ui/SaasDashboard.js +298 -0
- package/dist/ui/SaasDashboard.js.map +1 -0
- package/dist/ui/SaasProjectList.d.ts +14 -0
- package/dist/ui/SaasProjectList.d.ts.map +1 -0
- package/dist/ui/SaasProjectList.js +76 -0
- package/dist/ui/SaasProjectList.js.map +1 -0
- package/dist/ui/SaasSettingsPanel.d.ts +7 -0
- package/dist/ui/SaasSettingsPanel.d.ts.map +1 -0
- package/dist/ui/SaasSettingsPanel.js +138 -0
- package/dist/ui/SaasSettingsPanel.js.map +1 -0
- package/dist/ui/hooks/index.d.ts +3 -0
- package/dist/ui/hooks/index.js +6 -0
- package/dist/ui/hooks/useProjectList.d.ts +34 -0
- package/dist/ui/hooks/useProjectList.d.ts.map +1 -0
- package/dist/ui/hooks/useProjectList.js +75 -0
- package/dist/ui/hooks/useProjectList.js.map +1 -0
- package/dist/ui/hooks/useProjectMutations.d.ts +28 -0
- package/dist/ui/hooks/useProjectMutations.d.ts.map +1 -0
- package/dist/ui/hooks/useProjectMutations.js +146 -0
- package/dist/ui/hooks/useProjectMutations.js.map +1 -0
- package/dist/ui/index.d.ts +14 -0
- package/dist/ui/index.js +15 -0
- package/dist/ui/modals/CreateProjectModal.d.ts +23 -0
- package/dist/ui/modals/CreateProjectModal.d.ts.map +1 -0
- package/dist/ui/modals/CreateProjectModal.js +139 -0
- package/dist/ui/modals/CreateProjectModal.js.map +1 -0
- package/dist/ui/modals/ProjectActionsModal.d.ts +38 -0
- package/dist/ui/modals/ProjectActionsModal.d.ts.map +1 -0
- package/dist/ui/modals/ProjectActionsModal.js +292 -0
- package/dist/ui/modals/ProjectActionsModal.js.map +1 -0
- package/dist/ui/modals/index.d.ts +3 -0
- package/dist/ui/modals/index.js +4 -0
- package/dist/ui/overlays/demo-overlays.d.ts +19 -0
- package/dist/ui/overlays/demo-overlays.d.ts.map +1 -0
- package/dist/ui/overlays/demo-overlays.js +70 -0
- package/dist/ui/overlays/demo-overlays.js.map +1 -0
- package/dist/ui/overlays/index.d.ts +2 -0
- package/dist/ui/overlays/index.js +3 -0
- package/dist/ui/renderers/index.d.ts +3 -0
- package/dist/ui/renderers/index.js +4 -0
- package/dist/ui/renderers/project-list.markdown.d.ts +31 -0
- package/dist/ui/renderers/project-list.markdown.d.ts.map +1 -0
- package/dist/ui/renderers/project-list.markdown.js +148 -0
- package/dist/ui/renderers/project-list.markdown.js.map +1 -0
- package/dist/ui/renderers/project-list.renderer.d.ts +9 -0
- package/dist/ui/renderers/project-list.renderer.d.ts.map +1 -0
- package/dist/ui/renderers/project-list.renderer.js +17 -0
- package/dist/ui/renderers/project-list.renderer.js.map +1 -0
- package/package.json +38 -14
- package/src/billing/billing.presentation.ts +5 -6
- package/src/dashboard/dashboard.presentation.ts +5 -6
- package/src/example.ts +3 -3
- package/src/handlers/index.ts +3 -0
- package/src/handlers/saas.handlers.ts +300 -0
- package/src/index.ts +5 -0
- package/src/project/project.presentation.ts +5 -6
- package/src/saas-boilerplate.feature.ts +3 -3
- package/src/seeders/index.ts +28 -0
- package/src/shared/overlay-types.ts +39 -0
- package/src/tests/operations.test-spec.ts +109 -0
- package/src/ui/SaasDashboard.tsx +325 -0
- package/src/ui/SaasProjectList.tsx +113 -0
- package/src/ui/SaasSettingsPanel.tsx +96 -0
- package/src/ui/hooks/index.ts +10 -0
- package/src/ui/hooks/useProjectList.ts +95 -0
- package/src/ui/hooks/useProjectMutations.ts +166 -0
- package/src/ui/index.ts +18 -0
- package/src/ui/modals/CreateProjectModal.tsx +176 -0
- package/src/ui/modals/ProjectActionsModal.tsx +346 -0
- package/src/ui/modals/index.ts +2 -0
- package/src/ui/overlays/demo-overlays.ts +74 -0
- package/src/ui/overlays/index.ts +1 -0
- package/src/ui/renderers/index.ts +7 -0
- package/src/ui/renderers/project-list.markdown.ts +239 -0
- package/src/ui/renderers/project-list.renderer.tsx +22 -0
- package/tsconfig.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { StabilityEnum } from "@contractspec/lib.contracts";
|
|
1
|
+
import { StabilityEnum, definePresentation } from "@contractspec/lib.contracts";
|
|
2
2
|
|
|
3
3
|
//#region src/dashboard/dashboard.presentation.ts
|
|
4
4
|
/**
|
|
5
5
|
* Main dashboard presentation for the SaaS application.
|
|
6
6
|
*/
|
|
7
|
-
const SaasDashboardPresentation = {
|
|
7
|
+
const SaasDashboardPresentation = definePresentation({
|
|
8
8
|
meta: {
|
|
9
9
|
key: "saas.dashboard",
|
|
10
10
|
version: "1.0.0",
|
|
@@ -24,11 +24,11 @@ const SaasDashboardPresentation = {
|
|
|
24
24
|
},
|
|
25
25
|
targets: ["react", "markdown"],
|
|
26
26
|
policy: { flags: ["saas.enabled"] }
|
|
27
|
-
};
|
|
27
|
+
});
|
|
28
28
|
/**
|
|
29
29
|
* Settings panel presentation.
|
|
30
30
|
*/
|
|
31
|
-
const SettingsPanelPresentation = {
|
|
31
|
+
const SettingsPanelPresentation = definePresentation({
|
|
32
32
|
meta: {
|
|
33
33
|
key: "saas.settings",
|
|
34
34
|
version: "1.0.0",
|
|
@@ -48,7 +48,7 @@ const SettingsPanelPresentation = {
|
|
|
48
48
|
},
|
|
49
49
|
targets: ["react"],
|
|
50
50
|
policy: { flags: ["saas.enabled"] }
|
|
51
|
-
};
|
|
51
|
+
});
|
|
52
52
|
|
|
53
53
|
//#endregion
|
|
54
54
|
export { SaasDashboardPresentation, SettingsPanelPresentation };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.presentation.js","names":[
|
|
1
|
+
{"version":3,"file":"dashboard.presentation.js","names":[],"sources":["../../src/dashboard/dashboard.presentation.ts"],"sourcesContent":["import { definePresentation, StabilityEnum } from '@contractspec/lib.contracts';\n\n/**\n * Main dashboard presentation for the SaaS application.\n */\nexport const SaasDashboardPresentation = definePresentation({\n meta: {\n key: 'saas.dashboard',\n version: '1.0.0',\n title: 'SaaS Dashboard',\n description:\n 'Main SaaS dashboard with project overview, usage stats, and quick actions',\n domain: 'saas-boilerplate',\n owners: ['@saas-team'],\n tags: ['dashboard', 'overview'],\n stability: StabilityEnum.Beta,\n goal: 'Overview of SaaS activity and metrics',\n context: 'Main dashboard',\n },\n source: {\n type: 'component',\n framework: 'react',\n componentKey: 'SaasDashboard',\n },\n targets: ['react', 'markdown'],\n policy: {\n flags: ['saas.enabled'],\n },\n});\n\n/**\n * Settings panel presentation.\n */\nexport const SettingsPanelPresentation = definePresentation({\n meta: {\n key: 'saas.settings',\n version: '1.0.0',\n title: 'Settings Panel',\n description: 'Organization and user settings panel',\n domain: 'saas-boilerplate',\n owners: ['@saas-team'],\n tags: ['settings', 'config'],\n stability: StabilityEnum.Beta,\n goal: 'Configure organization and user settings',\n context: 'Settings section',\n },\n source: {\n type: 'component',\n framework: 'react',\n componentKey: 'SettingsPanel',\n },\n targets: ['react'],\n policy: {\n flags: ['saas.enabled'],\n },\n});\n"],"mappings":";;;;;;AAKA,MAAa,4BAA4B,mBAAmB;CAC1D,MAAM;EACJ,KAAK;EACL,SAAS;EACT,OAAO;EACP,aACE;EACF,QAAQ;EACR,QAAQ,CAAC,aAAa;EACtB,MAAM,CAAC,aAAa,WAAW;EAC/B,WAAW,cAAc;EACzB,MAAM;EACN,SAAS;EACV;CACD,QAAQ;EACN,MAAM;EACN,WAAW;EACX,cAAc;EACf;CACD,SAAS,CAAC,SAAS,WAAW;CAC9B,QAAQ,EACN,OAAO,CAAC,eAAe,EACxB;CACF,CAAC;;;;AAKF,MAAa,4BAA4B,mBAAmB;CAC1D,MAAM;EACJ,KAAK;EACL,SAAS;EACT,OAAO;EACP,aAAa;EACb,QAAQ;EACR,QAAQ,CAAC,aAAa;EACtB,MAAM,CAAC,YAAY,SAAS;EAC5B,WAAW,cAAc;EACzB,MAAM;EACN,SAAS;EACV;CACD,QAAQ;EACN,MAAM;EACN,WAAW;EACX,cAAc;EACf;CACD,SAAS,CAAC,QAAQ;CAClB,QAAQ,EACN,OAAO,CAAC,eAAe,EACxB;CACF,CAAC"}
|
package/dist/example.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as _contractspec_lib_contracts0 from "@contractspec/lib.contracts";
|
|
2
2
|
|
|
3
3
|
//#region src/example.d.ts
|
|
4
|
-
declare const example: ExampleSpec;
|
|
4
|
+
declare const example: _contractspec_lib_contracts0.ExampleSpec;
|
|
5
5
|
//#endregion
|
|
6
6
|
export { example as default };
|
|
7
7
|
//# sourceMappingURL=example.d.ts.map
|
package/dist/example.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"example.d.ts","names":[],"sources":["../src/example.ts"],"sourcesContent":[],"mappings":";;;cAEM,
|
|
1
|
+
{"version":3,"file":"example.d.ts","names":[],"sources":["../src/example.ts"],"sourcesContent":[],"mappings":";;;cAEM,SAiCJ,4BAAA,CAjCW"}
|
package/dist/example.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { defineExample } from "@contractspec/lib.contracts";
|
|
2
|
+
|
|
1
3
|
//#region src/example.ts
|
|
2
|
-
const example = {
|
|
4
|
+
const example = defineExample({
|
|
3
5
|
meta: {
|
|
4
6
|
key: "saas-boilerplate",
|
|
5
7
|
version: "1.0.0",
|
|
@@ -43,7 +45,7 @@ const example = {
|
|
|
43
45
|
},
|
|
44
46
|
mcp: { enabled: true }
|
|
45
47
|
}
|
|
46
|
-
};
|
|
48
|
+
});
|
|
47
49
|
var example_default = example;
|
|
48
50
|
|
|
49
51
|
//#endregion
|
package/dist/example.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"example.js","names":[
|
|
1
|
+
{"version":3,"file":"example.js","names":[],"sources":["../src/example.ts"],"sourcesContent":["import { defineExample } from '@contractspec/lib.contracts';\n\nconst example = defineExample({\n meta: {\n key: 'saas-boilerplate',\n version: '1.0.0',\n title: 'SaaS Boilerplate',\n description:\n 'Multi-tenant SaaS foundation with orgs, projects, settings, billing usage, and RBAC.',\n kind: 'template',\n visibility: 'public',\n stability: 'experimental',\n owners: ['@platform.core'],\n tags: ['saas', 'multi-tenant', 'billing', 'rbac'],\n },\n docs: {\n rootDocId: 'docs.examples.saas-boilerplate',\n },\n entrypoints: {\n packageName: '@contractspec/example.saas-boilerplate',\n feature: './feature',\n contracts: './contracts',\n presentations: './presentations',\n handlers: './handlers',\n docs: './docs',\n },\n surfaces: {\n templates: true,\n sandbox: {\n enabled: true,\n modes: ['playground', 'specs', 'builder', 'markdown', 'evolution'],\n },\n studio: { enabled: true, installable: true },\n mcp: { enabled: true },\n },\n});\n\nexport default example;\n"],"mappings":";;;AAEA,MAAM,UAAU,cAAc;CAC5B,MAAM;EACJ,KAAK;EACL,SAAS;EACT,OAAO;EACP,aACE;EACF,MAAM;EACN,YAAY;EACZ,WAAW;EACX,QAAQ,CAAC,iBAAiB;EAC1B,MAAM;GAAC;GAAQ;GAAgB;GAAW;GAAO;EAClD;CACD,MAAM,EACJ,WAAW,kCACZ;CACD,aAAa;EACX,aAAa;EACb,SAAS;EACT,WAAW;EACX,eAAe;EACf,UAAU;EACV,MAAM;EACP;CACD,UAAU;EACR,WAAW;EACX,SAAS;GACP,SAAS;GACT,OAAO;IAAC;IAAc;IAAS;IAAW;IAAY;IAAY;GACnE;EACD,QAAQ;GAAE,SAAS;GAAM,aAAa;GAAM;EAC5C,KAAK,EAAE,SAAS,MAAM;EACvB;CACF,CAAC;AAEF,sBAAe"}
|
package/dist/handlers/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { mockCheckFeatureAccessHandler, mockGetSubscriptionHandler, mockGetUsageSummaryHandler, mockRecordUsageHandler } from "../billing/billing.handler.js";
|
|
2
2
|
import { mockCreateProjectHandler, mockDeleteProjectHandler, mockGetProjectHandler, mockListProjectsHandler, mockUpdateProjectHandler } from "../project/project.handler.js";
|
|
3
|
-
|
|
3
|
+
import { CreateProjectInput, ListProjectsInput, ListProjectsOutput, Project, SaasHandlers, Subscription, UpdateProjectInput, createSaasHandlers } from "./saas.handlers.js";
|
|
4
|
+
export { CreateProjectInput, ListProjectsInput, ListProjectsOutput, Project, SaasHandlers, Subscription, UpdateProjectInput, createSaasHandlers, mockCheckFeatureAccessHandler, mockCreateProjectHandler, mockDeleteProjectHandler, mockGetProjectHandler, mockGetSubscriptionHandler, mockGetUsageSummaryHandler, mockListProjectsHandler, mockRecordUsageHandler, mockUpdateProjectHandler };
|
package/dist/handlers/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mockCheckFeatureAccessHandler, mockGetSubscriptionHandler, mockGetUsageSummaryHandler, mockRecordUsageHandler } from "../billing/billing.handler.js";
|
|
2
2
|
import { mockCreateProjectHandler, mockDeleteProjectHandler, mockGetProjectHandler, mockListProjectsHandler, mockUpdateProjectHandler } from "../project/project.handler.js";
|
|
3
|
+
import { createSaasHandlers } from "./saas.handlers.js";
|
|
3
4
|
|
|
4
|
-
export { mockCheckFeatureAccessHandler, mockCreateProjectHandler, mockDeleteProjectHandler, mockGetProjectHandler, mockGetSubscriptionHandler, mockGetUsageSummaryHandler, mockListProjectsHandler, mockRecordUsageHandler, mockUpdateProjectHandler };
|
|
5
|
+
export { createSaasHandlers, mockCheckFeatureAccessHandler, mockCreateProjectHandler, mockDeleteProjectHandler, mockGetProjectHandler, mockGetSubscriptionHandler, mockGetUsageSummaryHandler, mockListProjectsHandler, mockRecordUsageHandler, mockUpdateProjectHandler };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { DatabasePort } from "@contractspec/lib.runtime-sandbox";
|
|
2
|
+
|
|
3
|
+
//#region src/handlers/saas.handlers.d.ts
|
|
4
|
+
|
|
5
|
+
interface Project {
|
|
6
|
+
id: string;
|
|
7
|
+
projectId: string;
|
|
8
|
+
organizationId: string;
|
|
9
|
+
name: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
status: 'DRAFT' | 'ACTIVE' | 'ARCHIVED';
|
|
12
|
+
tier: 'FREE' | 'PRO' | 'ENTERPRISE';
|
|
13
|
+
createdAt: Date;
|
|
14
|
+
updatedAt: Date;
|
|
15
|
+
}
|
|
16
|
+
interface Subscription {
|
|
17
|
+
id: string;
|
|
18
|
+
projectId: string;
|
|
19
|
+
organizationId: string;
|
|
20
|
+
plan: 'FREE' | 'PRO' | 'ENTERPRISE';
|
|
21
|
+
status: 'ACTIVE' | 'PAST_DUE' | 'CANCELED';
|
|
22
|
+
billingCycle: 'MONTHLY' | 'YEARLY';
|
|
23
|
+
currentPeriodStart: Date;
|
|
24
|
+
currentPeriodEnd: Date;
|
|
25
|
+
cancelAtPeriodEnd: boolean;
|
|
26
|
+
}
|
|
27
|
+
interface ListProjectsInput {
|
|
28
|
+
projectId: string;
|
|
29
|
+
organizationId?: string;
|
|
30
|
+
status?: Project['status'] | 'all';
|
|
31
|
+
search?: string;
|
|
32
|
+
limit?: number;
|
|
33
|
+
offset?: number;
|
|
34
|
+
}
|
|
35
|
+
interface ListProjectsOutput {
|
|
36
|
+
items: Project[];
|
|
37
|
+
total: number;
|
|
38
|
+
hasMore: boolean;
|
|
39
|
+
}
|
|
40
|
+
interface CreateProjectInput {
|
|
41
|
+
name: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
tier?: Project['tier'];
|
|
44
|
+
}
|
|
45
|
+
interface UpdateProjectInput {
|
|
46
|
+
id: string;
|
|
47
|
+
name?: string;
|
|
48
|
+
description?: string;
|
|
49
|
+
status?: Project['status'];
|
|
50
|
+
}
|
|
51
|
+
declare function createSaasHandlers(db: DatabasePort): {
|
|
52
|
+
listProjects: (input: ListProjectsInput) => Promise<ListProjectsOutput>;
|
|
53
|
+
getProject: (id: string) => Promise<Project | null>;
|
|
54
|
+
createProject: (input: CreateProjectInput, context: {
|
|
55
|
+
projectId: string;
|
|
56
|
+
organizationId: string;
|
|
57
|
+
}) => Promise<Project>;
|
|
58
|
+
updateProject: (input: UpdateProjectInput) => Promise<Project>;
|
|
59
|
+
deleteProject: (id: string) => Promise<void>;
|
|
60
|
+
getSubscription: (input: {
|
|
61
|
+
projectId: string;
|
|
62
|
+
organizationId?: string;
|
|
63
|
+
}) => Promise<Subscription | null>;
|
|
64
|
+
};
|
|
65
|
+
type SaasHandlers = ReturnType<typeof createSaasHandlers>;
|
|
66
|
+
//#endregion
|
|
67
|
+
export { CreateProjectInput, ListProjectsInput, ListProjectsOutput, Project, SaasHandlers, Subscription, UpdateProjectInput, createSaasHandlers };
|
|
68
|
+
//# sourceMappingURL=saas.handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"saas.handlers.d.ts","names":[],"sources":["../../src/handlers/saas.handlers.ts"],"sourcesContent":[],"mappings":";;;;AA6CiB,UAjCA,OAAA,CAiCkB;EAMlB,EAAA,EAAA,MAAA;EAMA,SAAA,EAAA,MAAA;EA+DD,cAAA,EAAA,MAAkB;EAAK,IAAA,EAAA,MAAA;EAK5B,WAAA,CAAA,EAAA,MAAA;EACE,MAAA,EAAA,OAAA,GAAA,QAAA,GAAA,UAAA;EAAR,IAAA,EAAA,MAAA,GAAA,KAAA,GAAA,YAAA;EAqD4C,SAAA,EA/JpC,IA+JoC;EAAR,SAAA,EA9J5B,IA8J4B;;AAa5B,UAxKI,YAAA,CAwKJ;EAAR,EAAA,EAAA,MAAA;EA8BiC,SAAA,EAAA,MAAA;EAA6B,cAAA,EAAA,MAAA;EAAR,IAAA,EAAA,MAAA,GAAA,KAAA,GAAA,YAAA;EAyCf,MAAA,EAAA,QAAA,GAAA,UAAA,GAAA,UAAA;EAU9B,YAAA,EAAA,SAAA,GAAA,QAAA;EAAR,kBAAA,EAlPgB,IAkPhB;EAAO,gBAAA,EAjPO,IAiPP;EA0BD,iBAAY,EAAA,OAAA;;UAvQP,iBAAA;;;WAGN;;;;;UAMM,kBAAA;SACR;;;;UAKQ,kBAAA;;;SAGR;;UAGQ,kBAAA;;;;WAIN;;iBA2DK,kBAAA,KAAuB;wBAK5B,sBACN,QAAQ;8BAqD4B,QAAQ;yBAWtC;;;QAEN,QAAQ;yBA8ByB,uBAAqB,QAAQ;iCAyCvB;;;;QAUtC,QAAQ;;KA0BF,YAAA,GAAe,kBAAkB"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { web } from "@contractspec/lib.runtime-sandbox";
|
|
2
|
+
|
|
3
|
+
//#region src/handlers/saas.handlers.ts
|
|
4
|
+
const { generateId } = web;
|
|
5
|
+
function rowToProject(row) {
|
|
6
|
+
return {
|
|
7
|
+
id: row.id,
|
|
8
|
+
projectId: row.projectId,
|
|
9
|
+
organizationId: row.organizationId,
|
|
10
|
+
name: row.name,
|
|
11
|
+
description: row.description ?? void 0,
|
|
12
|
+
status: row.status,
|
|
13
|
+
tier: row.tier,
|
|
14
|
+
createdAt: new Date(row.createdAt),
|
|
15
|
+
updatedAt: new Date(row.updatedAt)
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function rowToSubscription(row) {
|
|
19
|
+
return {
|
|
20
|
+
id: row.id,
|
|
21
|
+
projectId: row.projectId,
|
|
22
|
+
organizationId: row.organizationId,
|
|
23
|
+
plan: row.plan,
|
|
24
|
+
status: row.status,
|
|
25
|
+
billingCycle: row.billingCycle,
|
|
26
|
+
currentPeriodStart: new Date(row.currentPeriodStart),
|
|
27
|
+
currentPeriodEnd: new Date(row.currentPeriodEnd),
|
|
28
|
+
cancelAtPeriodEnd: Boolean(row.cancelAtPeriodEnd)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function createSaasHandlers(db) {
|
|
32
|
+
/**
|
|
33
|
+
* List projects
|
|
34
|
+
*/
|
|
35
|
+
async function listProjects(input) {
|
|
36
|
+
const { projectId, organizationId, status, search, limit = 20, offset = 0 } = input;
|
|
37
|
+
let whereClause = "WHERE projectId = ?";
|
|
38
|
+
const params = [projectId];
|
|
39
|
+
if (organizationId) {
|
|
40
|
+
whereClause += " AND organizationId = ?";
|
|
41
|
+
params.push(organizationId);
|
|
42
|
+
}
|
|
43
|
+
if (status && status !== "all") {
|
|
44
|
+
whereClause += " AND status = ?";
|
|
45
|
+
params.push(status);
|
|
46
|
+
}
|
|
47
|
+
if (search) {
|
|
48
|
+
whereClause += " AND (name LIKE ? OR description LIKE ?)";
|
|
49
|
+
params.push(`%${search}%`, `%${search}%`);
|
|
50
|
+
}
|
|
51
|
+
const total = (await db.query(`SELECT COUNT(*) as count FROM saas_project ${whereClause}`, params)).rows[0]?.count ?? 0;
|
|
52
|
+
const rows = (await db.query(`SELECT * FROM saas_project ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`, [
|
|
53
|
+
...params,
|
|
54
|
+
limit,
|
|
55
|
+
offset
|
|
56
|
+
])).rows;
|
|
57
|
+
return {
|
|
58
|
+
items: rows.map(rowToProject),
|
|
59
|
+
total,
|
|
60
|
+
hasMore: offset + rows.length < total
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get a single project
|
|
65
|
+
*/
|
|
66
|
+
async function getProject(id) {
|
|
67
|
+
const rows = (await db.query(`SELECT * FROM saas_project WHERE id = ?`, [id])).rows;
|
|
68
|
+
return rows[0] ? rowToProject(rows[0]) : null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create a project
|
|
72
|
+
*/
|
|
73
|
+
async function createProject(input, context) {
|
|
74
|
+
const id = generateId("proj");
|
|
75
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
76
|
+
await db.execute(`INSERT INTO saas_project (id, projectId, organizationId, name, description, status, tier, createdAt, updatedAt)
|
|
77
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
78
|
+
id,
|
|
79
|
+
context.projectId,
|
|
80
|
+
context.organizationId,
|
|
81
|
+
input.name,
|
|
82
|
+
input.description ?? null,
|
|
83
|
+
"DRAFT",
|
|
84
|
+
input.tier ?? "FREE",
|
|
85
|
+
now,
|
|
86
|
+
now
|
|
87
|
+
]);
|
|
88
|
+
const rows = (await db.query(`SELECT * FROM saas_project WHERE id = ?`, [id])).rows;
|
|
89
|
+
return rowToProject(rows[0]);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Update a project
|
|
93
|
+
*/
|
|
94
|
+
async function updateProject(input) {
|
|
95
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
96
|
+
const updates = ["updatedAt = ?"];
|
|
97
|
+
const params = [now];
|
|
98
|
+
if (input.name !== void 0) {
|
|
99
|
+
updates.push("name = ?");
|
|
100
|
+
params.push(input.name);
|
|
101
|
+
}
|
|
102
|
+
if (input.description !== void 0) {
|
|
103
|
+
updates.push("description = ?");
|
|
104
|
+
params.push(input.description);
|
|
105
|
+
}
|
|
106
|
+
if (input.status !== void 0) {
|
|
107
|
+
updates.push("status = ?");
|
|
108
|
+
params.push(input.status);
|
|
109
|
+
}
|
|
110
|
+
params.push(input.id);
|
|
111
|
+
await db.execute(`UPDATE saas_project SET ${updates.join(", ")} WHERE id = ?`, params);
|
|
112
|
+
const rows = (await db.query(`SELECT * FROM saas_project WHERE id = ?`, [input.id])).rows;
|
|
113
|
+
if (!rows[0]) throw new Error("NOT_FOUND");
|
|
114
|
+
return rowToProject(rows[0]);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Delete a project
|
|
118
|
+
*/
|
|
119
|
+
async function deleteProject(id) {
|
|
120
|
+
await db.execute(`DELETE FROM saas_project WHERE id = ?`, [id]);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get subscription for an organization
|
|
124
|
+
*/
|
|
125
|
+
async function getSubscription(input) {
|
|
126
|
+
let query = `SELECT * FROM saas_subscription WHERE projectId = ?`;
|
|
127
|
+
const params = [input.projectId];
|
|
128
|
+
if (input.organizationId) {
|
|
129
|
+
query += " AND organizationId = ?";
|
|
130
|
+
params.push(input.organizationId);
|
|
131
|
+
}
|
|
132
|
+
query += " LIMIT 1";
|
|
133
|
+
const rows = (await db.query(query, params)).rows;
|
|
134
|
+
return rows[0] ? rowToSubscription(rows[0]) : null;
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
listProjects,
|
|
138
|
+
getProject,
|
|
139
|
+
createProject,
|
|
140
|
+
updateProject,
|
|
141
|
+
deleteProject,
|
|
142
|
+
getSubscription
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
//#endregion
|
|
147
|
+
export { createSaasHandlers };
|
|
148
|
+
//# sourceMappingURL=saas.handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"saas.handlers.js","names":[],"sources":["../../src/handlers/saas.handlers.ts"],"sourcesContent":["/**\n * Runtime-local SaaS Boilerplate handlers\n *\n * Database-backed handlers for project and billing management.\n */\nimport type { DatabasePort } from '@contractspec/lib.runtime-sandbox';\n/* eslint-disable @typescript-eslint/no-non-null-assertion */\nimport { web } from '@contractspec/lib.runtime-sandbox';\nconst { generateId } = web;\n\n// ============ Types ============\n\nexport interface Project {\n id: string;\n projectId: string;\n organizationId: string;\n name: string;\n description?: string;\n status: 'DRAFT' | 'ACTIVE' | 'ARCHIVED';\n tier: 'FREE' | 'PRO' | 'ENTERPRISE';\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface Subscription {\n id: string;\n projectId: string;\n organizationId: string;\n plan: 'FREE' | 'PRO' | 'ENTERPRISE';\n status: 'ACTIVE' | 'PAST_DUE' | 'CANCELED';\n billingCycle: 'MONTHLY' | 'YEARLY';\n currentPeriodStart: Date;\n currentPeriodEnd: Date;\n cancelAtPeriodEnd: boolean;\n}\n\nexport interface ListProjectsInput {\n projectId: string;\n organizationId?: string;\n status?: Project['status'] | 'all';\n search?: string;\n limit?: number;\n offset?: number;\n}\n\nexport interface ListProjectsOutput {\n items: Project[];\n total: number;\n hasMore: boolean;\n}\n\nexport interface CreateProjectInput {\n name: string;\n description?: string;\n tier?: Project['tier'];\n}\n\nexport interface UpdateProjectInput {\n id: string;\n name?: string;\n description?: string;\n status?: Project['status'];\n}\n\n// ============ Row Types ============\n\ninterface ProjectRow extends Record<string, unknown> {\n id: string;\n projectId: string;\n organizationId: string;\n name: string;\n description: string | null;\n status: string;\n tier: string;\n createdAt: string;\n updatedAt: string;\n}\n\ninterface SubscriptionRow extends Record<string, unknown> {\n id: string;\n projectId: string;\n organizationId: string;\n plan: string;\n status: string;\n billingCycle: string;\n currentPeriodStart: string;\n currentPeriodEnd: string;\n cancelAtPeriodEnd: number;\n}\n\nfunction rowToProject(row: ProjectRow): Project {\n return {\n id: row.id,\n projectId: row.projectId,\n organizationId: row.organizationId,\n name: row.name,\n description: row.description ?? undefined,\n status: row.status as Project['status'],\n tier: row.tier as Project['tier'],\n createdAt: new Date(row.createdAt),\n updatedAt: new Date(row.updatedAt),\n };\n}\n\nfunction rowToSubscription(row: SubscriptionRow): Subscription {\n return {\n id: row.id,\n projectId: row.projectId,\n organizationId: row.organizationId,\n plan: row.plan as Subscription['plan'],\n status: row.status as Subscription['status'],\n billingCycle: row.billingCycle as Subscription['billingCycle'],\n currentPeriodStart: new Date(row.currentPeriodStart),\n currentPeriodEnd: new Date(row.currentPeriodEnd),\n cancelAtPeriodEnd: Boolean(row.cancelAtPeriodEnd),\n };\n}\n\n// ============ Handler Factory ============\n\nexport function createSaasHandlers(db: DatabasePort) {\n /**\n * List projects\n */\n async function listProjects(\n input: ListProjectsInput\n ): Promise<ListProjectsOutput> {\n const {\n projectId,\n organizationId,\n status,\n search,\n limit = 20,\n offset = 0,\n } = input;\n\n let whereClause = 'WHERE projectId = ?';\n const params: (string | number)[] = [projectId];\n\n if (organizationId) {\n whereClause += ' AND organizationId = ?';\n params.push(organizationId);\n }\n\n if (status && status !== 'all') {\n whereClause += ' AND status = ?';\n params.push(status);\n }\n\n if (search) {\n whereClause += ' AND (name LIKE ? OR description LIKE ?)';\n params.push(`%${search}%`, `%${search}%`);\n }\n\n const countResult = (\n await db.query(\n `SELECT COUNT(*) as count FROM saas_project ${whereClause}`,\n params\n )\n ).rows as unknown as { count: number }[];\n const total = countResult[0]?.count ?? 0;\n\n const rows = (\n await db.query(\n `SELECT * FROM saas_project ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`,\n [...params, limit, offset]\n )\n ).rows as unknown as ProjectRow[];\n\n return {\n items: rows.map(rowToProject),\n total,\n hasMore: offset + rows.length < total,\n };\n }\n\n /**\n * Get a single project\n */\n async function getProject(id: string): Promise<Project | null> {\n const rows = (\n await db.query(`SELECT * FROM saas_project WHERE id = ?`, [id])\n ).rows as unknown as ProjectRow[];\n return rows[0] ? rowToProject(rows[0]) : null;\n }\n\n /**\n * Create a project\n */\n async function createProject(\n input: CreateProjectInput,\n context: { projectId: string; organizationId: string }\n ): Promise<Project> {\n const id = generateId('proj');\n const now = new Date().toISOString();\n\n await db.execute(\n `INSERT INTO saas_project (id, projectId, organizationId, name, description, status, tier, createdAt, updatedAt)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n id,\n context.projectId,\n context.organizationId,\n input.name,\n input.description ?? null,\n 'DRAFT',\n input.tier ?? 'FREE',\n now,\n now,\n ]\n );\n\n const rows = (\n await db.query(`SELECT * FROM saas_project WHERE id = ?`, [id])\n ).rows as unknown as ProjectRow[];\n\n return rowToProject(rows[0]!);\n }\n\n /**\n * Update a project\n */\n async function updateProject(input: UpdateProjectInput): Promise<Project> {\n const now = new Date().toISOString();\n const updates: string[] = ['updatedAt = ?'];\n const params: (string | null)[] = [now];\n\n if (input.name !== undefined) {\n updates.push('name = ?');\n params.push(input.name);\n }\n\n if (input.description !== undefined) {\n updates.push('description = ?');\n params.push(input.description);\n }\n\n if (input.status !== undefined) {\n updates.push('status = ?');\n params.push(input.status);\n }\n\n params.push(input.id);\n\n await db.execute(\n `UPDATE saas_project SET ${updates.join(', ')} WHERE id = ?`,\n params\n );\n\n const rows = (\n await db.query(`SELECT * FROM saas_project WHERE id = ?`, [input.id])\n ).rows as unknown as ProjectRow[];\n\n if (!rows[0]) {\n throw new Error('NOT_FOUND');\n }\n\n return rowToProject(rows[0]);\n }\n\n /**\n * Delete a project\n */\n async function deleteProject(id: string): Promise<void> {\n await db.execute(`DELETE FROM saas_project WHERE id = ?`, [id]);\n }\n\n /**\n * Get subscription for an organization\n */\n async function getSubscription(input: {\n projectId: string;\n organizationId?: string;\n }): Promise<Subscription | null> {\n let query = `SELECT * FROM saas_subscription WHERE projectId = ?`;\n const params: string[] = [input.projectId];\n\n if (input.organizationId) {\n query += ' AND organizationId = ?';\n params.push(input.organizationId);\n }\n\n query += ' LIMIT 1';\n\n const rows = (await db.query(query, params))\n .rows as unknown as SubscriptionRow[];\n return rows[0] ? rowToSubscription(rows[0]) : null;\n }\n\n return {\n listProjects,\n getProject,\n createProject,\n updateProject,\n deleteProject,\n getSubscription,\n };\n}\n\nexport type SaasHandlers = ReturnType<typeof createSaasHandlers>;\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { SaasDashboardPresentation, SettingsPanelPresentation } from "./dashboar
|
|
|
10
10
|
import "./dashboard/index.js";
|
|
11
11
|
import example from "./example.js";
|
|
12
12
|
import { CreateProjectInput, ListProjectsInput, ListProjectsOutput, Project, UpdateProjectInput, mockCreateProjectHandler, mockDeleteProjectHandler, mockGetProjectHandler, mockListProjectsHandler, mockUpdateProjectHandler } from "./project/project.handler.js";
|
|
13
|
+
import { SaasHandlers, createSaasHandlers } from "./handlers/saas.handlers.js";
|
|
13
14
|
import { ProjectStatusFilterEnum, ProjectStatusSchemaEnum } from "./project/project.enum.js";
|
|
14
15
|
import { CreateProjectInputModel, DeleteProjectInputModel, DeleteProjectOutputModel, GetProjectInputModel, ListProjectsInputModel, ListProjectsOutputModel, ProjectDeletedPayloadModel, ProjectModel, UpdateProjectInputModel } from "./project/project.schema.js";
|
|
15
16
|
import { CreateProjectContract, DeleteProjectContract, GetProjectContract, ListProjectsContract, UpdateProjectContract } from "./project/project.operations.js";
|
|
@@ -21,6 +22,17 @@ import { SettingsScopeEnum } from "./settings/settings.enum.js";
|
|
|
21
22
|
import { FeatureFlagEntity, SettingsEntity } from "./settings/settings.entity.js";
|
|
22
23
|
import "./settings/index.js";
|
|
23
24
|
import { SaasBoilerplateFeature } from "./saas-boilerplate.feature.js";
|
|
25
|
+
import { SaasDashboard } from "./ui/SaasDashboard.js";
|
|
26
|
+
import { SaasProjectList } from "./ui/SaasProjectList.js";
|
|
27
|
+
import { SaasSettingsPanel } from "./ui/SaasSettingsPanel.js";
|
|
28
|
+
import { CreateProjectModal } from "./ui/modals/CreateProjectModal.js";
|
|
29
|
+
import { ProjectActionsModal } from "./ui/modals/ProjectActionsModal.js";
|
|
30
|
+
import { UseProjectListOptions, useProjectList } from "./ui/hooks/useProjectList.js";
|
|
31
|
+
import { UseProjectMutationsOptions, useProjectMutations } from "./ui/hooks/useProjectMutations.js";
|
|
32
|
+
import { projectListReactRenderer } from "./ui/renderers/project-list.renderer.js";
|
|
33
|
+
import { projectListMarkdownRenderer, saasBillingMarkdownRenderer, saasDashboardMarkdownRenderer } from "./ui/renderers/project-list.markdown.js";
|
|
34
|
+
import { saasDemoOverlay, saasFreeUserOverlay, saasOverlays } from "./ui/overlays/demo-overlays.js";
|
|
35
|
+
import "./ui/index.js";
|
|
24
36
|
import { ModuleSchemaContribution } from "@contractspec/lib.schema";
|
|
25
37
|
|
|
26
38
|
//#region src/index.d.ts
|
|
@@ -38,5 +50,5 @@ declare const schemaComposition: {
|
|
|
38
50
|
outputPath: string;
|
|
39
51
|
};
|
|
40
52
|
//#endregion
|
|
41
|
-
export { BillingUsageEntity, CheckFeatureAccessContract, CheckFeatureAccessInput, CheckFeatureAccessInputModel, CheckFeatureAccessOutput, CheckFeatureAccessOutputModel, CreateProjectContract, CreateProjectInput, CreateProjectInputModel, DeleteProjectContract, DeleteProjectInputModel, DeleteProjectOutputModel, FeatureAccessReasonEnum, FeatureFlagEntity, GetProjectContract, GetProjectInputModel, GetSubscriptionContract, GetUsageSummaryContract, GetUsageSummaryInputModel, GetUsageSummaryOutputModel, ListProjectsContract, ListProjectsInput, ListProjectsInputModel, ListProjectsOutput, ListProjectsOutputModel, Project, ProjectArchivedEvent, ProjectCreatedEvent, ProjectDeletedEvent, ProjectDeletedPayloadModel, ProjectDetailPresentation, ProjectEntity, ProjectListPresentation, ProjectMemberEntity, ProjectModel, ProjectStatusEnum, ProjectStatusFilterEnum, ProjectStatusSchemaEnum, ProjectUpdatedEvent, RecordUsageContract, RecordUsageInput, RecordUsageInputModel, RecordUsageOutputModel, SaasBoilerplateFeature, SaasDashboardPresentation, SettingsEntity, SettingsPanelPresentation, SettingsScopeEnum, Subscription, SubscriptionChangedEvent, SubscriptionEntity, SubscriptionModel, SubscriptionPresentation, SubscriptionStatusEnum, SubscriptionStatusSchemaEnum, UpdateProjectContract, UpdateProjectInput, UpdateProjectInputModel, UsageDashboardPresentation, UsageLimitEntity, UsageLimitReachedEvent, UsageRecordedEvent, UsageRecordedPayloadModel, UsageSummary, UsageSummaryModel, example, mockCheckFeatureAccessHandler, mockCreateProjectHandler, mockDeleteProjectHandler, mockGetProjectHandler, mockGetSubscriptionHandler, mockGetUsageSummaryHandler, mockListProjectsHandler, mockRecordUsageHandler, mockUpdateProjectHandler, saasBoilerplateSchemaContribution, schemaComposition };
|
|
53
|
+
export { BillingUsageEntity, CheckFeatureAccessContract, CheckFeatureAccessInput, CheckFeatureAccessInputModel, CheckFeatureAccessOutput, CheckFeatureAccessOutputModel, CreateProjectContract, CreateProjectInput, CreateProjectInputModel, CreateProjectModal, DeleteProjectContract, DeleteProjectInputModel, DeleteProjectOutputModel, FeatureAccessReasonEnum, FeatureFlagEntity, GetProjectContract, GetProjectInputModel, GetSubscriptionContract, GetUsageSummaryContract, GetUsageSummaryInputModel, GetUsageSummaryOutputModel, ListProjectsContract, ListProjectsInput, ListProjectsInputModel, ListProjectsOutput, ListProjectsOutputModel, Project, ProjectActionsModal, ProjectArchivedEvent, ProjectCreatedEvent, ProjectDeletedEvent, ProjectDeletedPayloadModel, ProjectDetailPresentation, ProjectEntity, ProjectListPresentation, ProjectMemberEntity, ProjectModel, ProjectStatusEnum, ProjectStatusFilterEnum, ProjectStatusSchemaEnum, ProjectUpdatedEvent, RecordUsageContract, RecordUsageInput, RecordUsageInputModel, RecordUsageOutputModel, SaasBoilerplateFeature, SaasDashboard, SaasDashboardPresentation, type SaasHandlers, SaasProjectList, SaasSettingsPanel, SettingsEntity, SettingsPanelPresentation, SettingsScopeEnum, Subscription, SubscriptionChangedEvent, SubscriptionEntity, SubscriptionModel, SubscriptionPresentation, SubscriptionStatusEnum, SubscriptionStatusSchemaEnum, UpdateProjectContract, UpdateProjectInput, UpdateProjectInputModel, UsageDashboardPresentation, UsageLimitEntity, UsageLimitReachedEvent, UsageRecordedEvent, UsageRecordedPayloadModel, UsageSummary, UsageSummaryModel, UseProjectListOptions, UseProjectMutationsOptions, createSaasHandlers, example, mockCheckFeatureAccessHandler, mockCreateProjectHandler, mockDeleteProjectHandler, mockGetProjectHandler, mockGetSubscriptionHandler, mockGetUsageSummaryHandler, mockListProjectsHandler, mockRecordUsageHandler, mockUpdateProjectHandler, projectListMarkdownRenderer, projectListReactRenderer, saasBillingMarkdownRenderer, saasBoilerplateSchemaContribution, saasDashboardMarkdownRenderer, saasDemoOverlay, saasFreeUserOverlay, saasOverlays, schemaComposition, useProjectList, useProjectMutations };
|
|
42
54
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA+Ca,mCAAmC;;;;;cAkBnC;WAUZ"}
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,18 @@ import "./settings/index.js";
|
|
|
21
21
|
import { SaasDashboardPresentation, SettingsPanelPresentation } from "./dashboard/dashboard.presentation.js";
|
|
22
22
|
import "./dashboard/index.js";
|
|
23
23
|
import { SaasBoilerplateFeature } from "./saas-boilerplate.feature.js";
|
|
24
|
+
import { useProjectList } from "./ui/hooks/useProjectList.js";
|
|
25
|
+
import { useProjectMutations } from "./ui/hooks/useProjectMutations.js";
|
|
26
|
+
import { CreateProjectModal } from "./ui/modals/CreateProjectModal.js";
|
|
27
|
+
import { ProjectActionsModal } from "./ui/modals/ProjectActionsModal.js";
|
|
28
|
+
import { SaasDashboard } from "./ui/SaasDashboard.js";
|
|
29
|
+
import { SaasProjectList } from "./ui/SaasProjectList.js";
|
|
30
|
+
import { SaasSettingsPanel } from "./ui/SaasSettingsPanel.js";
|
|
31
|
+
import { projectListReactRenderer } from "./ui/renderers/project-list.renderer.js";
|
|
32
|
+
import { projectListMarkdownRenderer, saasBillingMarkdownRenderer, saasDashboardMarkdownRenderer } from "./ui/renderers/project-list.markdown.js";
|
|
33
|
+
import { saasDemoOverlay, saasFreeUserOverlay, saasOverlays } from "./ui/overlays/demo-overlays.js";
|
|
34
|
+
import "./ui/index.js";
|
|
35
|
+
import { createSaasHandlers } from "./handlers/saas.handlers.js";
|
|
24
36
|
import "./docs/index.js";
|
|
25
37
|
import { identityRbacSchemaContribution } from "@contractspec/lib.identity-rbac";
|
|
26
38
|
import { jobsSchemaContribution } from "@contractspec/lib.jobs";
|
|
@@ -65,5 +77,5 @@ const schemaComposition = {
|
|
|
65
77
|
};
|
|
66
78
|
|
|
67
79
|
//#endregion
|
|
68
|
-
export { BillingUsageEntity, CheckFeatureAccessContract, CheckFeatureAccessInputModel, CheckFeatureAccessOutputModel, CreateProjectContract, CreateProjectInputModel, DeleteProjectContract, DeleteProjectInputModel, DeleteProjectOutputModel, FeatureAccessReasonEnum, FeatureFlagEntity, GetProjectContract, GetProjectInputModel, GetSubscriptionContract, GetUsageSummaryContract, GetUsageSummaryInputModel, GetUsageSummaryOutputModel, ListProjectsContract, ListProjectsInputModel, ListProjectsOutputModel, ProjectArchivedEvent, ProjectCreatedEvent, ProjectDeletedEvent, ProjectDeletedPayloadModel, ProjectDetailPresentation, ProjectEntity, ProjectListPresentation, ProjectMemberEntity, ProjectModel, ProjectStatusEnum, ProjectStatusFilterEnum, ProjectStatusSchemaEnum, ProjectUpdatedEvent, RecordUsageContract, RecordUsageInputModel, RecordUsageOutputModel, SaasBoilerplateFeature, SaasDashboardPresentation, SettingsEntity, SettingsPanelPresentation, SettingsScopeEnum, SubscriptionChangedEvent, SubscriptionEntity, SubscriptionModel, SubscriptionPresentation, SubscriptionStatusEnum, SubscriptionStatusSchemaEnum, UpdateProjectContract, UpdateProjectInputModel, UsageDashboardPresentation, UsageLimitEntity, UsageLimitReachedEvent, UsageRecordedEvent, UsageRecordedPayloadModel, UsageSummaryModel, example_default as example, mockCheckFeatureAccessHandler, mockCreateProjectHandler, mockDeleteProjectHandler, mockGetProjectHandler, mockGetSubscriptionHandler, mockGetUsageSummaryHandler, mockListProjectsHandler, mockRecordUsageHandler, mockUpdateProjectHandler, saasBoilerplateSchemaContribution, schemaComposition };
|
|
80
|
+
export { BillingUsageEntity, CheckFeatureAccessContract, CheckFeatureAccessInputModel, CheckFeatureAccessOutputModel, CreateProjectContract, CreateProjectInputModel, CreateProjectModal, DeleteProjectContract, DeleteProjectInputModel, DeleteProjectOutputModel, FeatureAccessReasonEnum, FeatureFlagEntity, GetProjectContract, GetProjectInputModel, GetSubscriptionContract, GetUsageSummaryContract, GetUsageSummaryInputModel, GetUsageSummaryOutputModel, ListProjectsContract, ListProjectsInputModel, ListProjectsOutputModel, ProjectActionsModal, ProjectArchivedEvent, ProjectCreatedEvent, ProjectDeletedEvent, ProjectDeletedPayloadModel, ProjectDetailPresentation, ProjectEntity, ProjectListPresentation, ProjectMemberEntity, ProjectModel, ProjectStatusEnum, ProjectStatusFilterEnum, ProjectStatusSchemaEnum, ProjectUpdatedEvent, RecordUsageContract, RecordUsageInputModel, RecordUsageOutputModel, SaasBoilerplateFeature, SaasDashboard, SaasDashboardPresentation, SaasProjectList, SaasSettingsPanel, SettingsEntity, SettingsPanelPresentation, SettingsScopeEnum, SubscriptionChangedEvent, SubscriptionEntity, SubscriptionModel, SubscriptionPresentation, SubscriptionStatusEnum, SubscriptionStatusSchemaEnum, UpdateProjectContract, UpdateProjectInputModel, UsageDashboardPresentation, UsageLimitEntity, UsageLimitReachedEvent, UsageRecordedEvent, UsageRecordedPayloadModel, UsageSummaryModel, createSaasHandlers, example_default as example, mockCheckFeatureAccessHandler, mockCreateProjectHandler, mockDeleteProjectHandler, mockGetProjectHandler, mockGetSubscriptionHandler, mockGetUsageSummaryHandler, mockListProjectsHandler, mockRecordUsageHandler, mockUpdateProjectHandler, projectListMarkdownRenderer, projectListReactRenderer, saasBillingMarkdownRenderer, saasBoilerplateSchemaContribution, saasDashboardMarkdownRenderer, saasDemoOverlay, saasFreeUserOverlay, saasOverlays, schemaComposition, useProjectList, useProjectMutations };
|
|
69
81
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["// SaaS Boilerplate Example\n// Demonstrates ContractSpec principles for a complete SaaS application\n\n// Export all domain modules\nexport * from './billing';\nexport * from './project';\nexport * from './settings';\nexport * from './dashboard';\n\n// Export feature and example metadata\nexport * from './saas-boilerplate.feature';\nexport * from './ui';\nexport {\n createSaasHandlers,\n type SaasHandlers,\n} from './handlers/saas.handlers';\nexport { default as example } from './example';\n\n// Import docs for registration\nimport './docs';\n\n// Schema composition configuration\nimport { identityRbacSchemaContribution } from '@contractspec/lib.identity-rbac';\nimport { jobsSchemaContribution } from '@contractspec/lib.jobs';\nimport { auditTrailSchemaContribution } from '@contractspec/module.audit-trail';\nimport { notificationsSchemaContribution } from '@contractspec/module.notifications';\nimport type { ModuleSchemaContribution } from '@contractspec/lib.schema';\nimport {\n ProjectEntity,\n ProjectMemberEntity,\n ProjectStatusEnum,\n} from './project/project.entity';\nimport {\n SettingsEntity,\n FeatureFlagEntity,\n SettingsScopeEnum,\n} from './settings';\nimport {\n SubscriptionEntity,\n BillingUsageEntity,\n UsageLimitEntity,\n SubscriptionStatusEnum,\n} from './billing/billing.entity';\n\n/**\n * SaaS boilerplate schema contribution.\n */\nexport const saasBoilerplateSchemaContribution: ModuleSchemaContribution = {\n moduleId: '@contractspec/example.saas-boilerplate',\n entities: [\n ProjectEntity,\n ProjectMemberEntity,\n SettingsEntity,\n FeatureFlagEntity,\n SubscriptionEntity,\n BillingUsageEntity,\n UsageLimitEntity,\n ],\n enums: [ProjectStatusEnum, SettingsScopeEnum, SubscriptionStatusEnum],\n};\n\n/**\n * Complete schema composition for SaaS Boilerplate.\n * Use with `database schema:compose` to generate Prisma schema.\n */\nexport const schemaComposition = {\n modules: [\n identityRbacSchemaContribution,\n jobsSchemaContribution,\n auditTrailSchemaContribution,\n notificationsSchemaContribution,\n saasBoilerplateSchemaContribution,\n ],\n provider: 'postgresql' as const,\n outputPath: './prisma/schema/generated.prisma',\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,MAAa,oCAA8D;CACzE,UAAU;CACV,UAAU;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CACD,OAAO;EAAC;EAAmB;EAAmB;EAAuB;CACtE;;;;;AAMD,MAAa,oBAAoB;CAC/B,SAAS;EACP;EACA;EACA;EACA;EACA;EACD;CACD,UAAU;CACV,YAAY;CACb"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as _contractspec_lib_schema170 from "@contractspec/lib.schema";
|
|
2
2
|
|
|
3
3
|
//#region src/project/project.enum.d.ts
|
|
4
4
|
|
|
@@ -6,11 +6,11 @@ import * as _contractspec_lib_schema267 from "@contractspec/lib.schema";
|
|
|
6
6
|
* Project status enum for contract schemas.
|
|
7
7
|
* Note: Entity enum is defined separately in project.entity.ts
|
|
8
8
|
*/
|
|
9
|
-
declare const ProjectStatusSchemaEnum:
|
|
9
|
+
declare const ProjectStatusSchemaEnum: _contractspec_lib_schema170.EnumType<[string, string, string, string]>;
|
|
10
10
|
/**
|
|
11
11
|
* Project status filter enum (includes 'all' option).
|
|
12
12
|
*/
|
|
13
|
-
declare const ProjectStatusFilterEnum:
|
|
13
|
+
declare const ProjectStatusFilterEnum: _contractspec_lib_schema170.EnumType<[string, string, string, string]>;
|
|
14
14
|
//#endregion
|
|
15
15
|
export { ProjectStatusFilterEnum, ProjectStatusSchemaEnum };
|
|
16
16
|
//# sourceMappingURL=project.enum.d.ts.map
|
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import * as
|
|
1
|
+
import * as _contractspec_lib_contracts12 from "@contractspec/lib.contracts";
|
|
2
|
+
import * as _contractspec_lib_schema172 from "@contractspec/lib.schema";
|
|
3
3
|
|
|
4
4
|
//#region src/project/project.event.d.ts
|
|
5
5
|
/**
|
|
6
6
|
* Event: A new project has been created.
|
|
7
7
|
*/
|
|
8
|
-
declare const ProjectCreatedEvent:
|
|
8
|
+
declare const ProjectCreatedEvent: _contractspec_lib_contracts12.EventSpec<_contractspec_lib_schema172.SchemaModel<{
|
|
9
9
|
projectId: {
|
|
10
|
-
type:
|
|
10
|
+
type: _contractspec_lib_schema172.FieldType<string, string>;
|
|
11
11
|
isOptional: false;
|
|
12
12
|
};
|
|
13
13
|
name: {
|
|
14
|
-
type:
|
|
14
|
+
type: _contractspec_lib_schema172.FieldType<string, string>;
|
|
15
15
|
isOptional: false;
|
|
16
16
|
};
|
|
17
17
|
organizationId: {
|
|
18
|
-
type:
|
|
18
|
+
type: _contractspec_lib_schema172.FieldType<string, string>;
|
|
19
19
|
isOptional: false;
|
|
20
20
|
};
|
|
21
21
|
createdBy: {
|
|
22
|
-
type:
|
|
22
|
+
type: _contractspec_lib_schema172.FieldType<string, string>;
|
|
23
23
|
isOptional: false;
|
|
24
24
|
};
|
|
25
25
|
createdAt: {
|
|
26
|
-
type:
|
|
26
|
+
type: _contractspec_lib_schema172.FieldType<Date, string>;
|
|
27
27
|
isOptional: false;
|
|
28
28
|
};
|
|
29
29
|
}>>;
|
|
30
30
|
/**
|
|
31
31
|
* Event: A project has been updated.
|
|
32
32
|
*/
|
|
33
|
-
declare const ProjectUpdatedEvent:
|
|
33
|
+
declare const ProjectUpdatedEvent: _contractspec_lib_contracts12.EventSpec<_contractspec_lib_schema172.SchemaModel<{
|
|
34
34
|
projectId: {
|
|
35
|
-
type:
|
|
35
|
+
type: _contractspec_lib_schema172.FieldType<string, string>;
|
|
36
36
|
isOptional: false;
|
|
37
37
|
};
|
|
38
38
|
updatedFields: {
|
|
39
|
-
type:
|
|
39
|
+
type: _contractspec_lib_schema172.FieldType<string, string>;
|
|
40
40
|
isArray: true;
|
|
41
41
|
isOptional: false;
|
|
42
42
|
};
|
|
43
43
|
updatedBy: {
|
|
44
|
-
type:
|
|
44
|
+
type: _contractspec_lib_schema172.FieldType<string, string>;
|
|
45
45
|
isOptional: false;
|
|
46
46
|
};
|
|
47
47
|
updatedAt: {
|
|
48
|
-
type:
|
|
48
|
+
type: _contractspec_lib_schema172.FieldType<Date, string>;
|
|
49
49
|
isOptional: false;
|
|
50
50
|
};
|
|
51
51
|
}>>;
|
|
52
52
|
/**
|
|
53
53
|
* Event: A project has been deleted.
|
|
54
54
|
*/
|
|
55
|
-
declare const ProjectDeletedEvent:
|
|
55
|
+
declare const ProjectDeletedEvent: _contractspec_lib_contracts12.EventSpec<_contractspec_lib_schema172.SchemaModel<{
|
|
56
56
|
projectId: {
|
|
57
|
-
type:
|
|
57
|
+
type: _contractspec_lib_schema172.FieldType<string, string>;
|
|
58
58
|
isOptional: false;
|
|
59
59
|
};
|
|
60
60
|
organizationId: {
|
|
61
|
-
type:
|
|
61
|
+
type: _contractspec_lib_schema172.FieldType<string, string>;
|
|
62
62
|
isOptional: false;
|
|
63
63
|
};
|
|
64
64
|
deletedBy: {
|
|
65
|
-
type:
|
|
65
|
+
type: _contractspec_lib_schema172.FieldType<string, string>;
|
|
66
66
|
isOptional: false;
|
|
67
67
|
};
|
|
68
68
|
deletedAt: {
|
|
69
|
-
type:
|
|
69
|
+
type: _contractspec_lib_schema172.FieldType<Date, string>;
|
|
70
70
|
isOptional: false;
|
|
71
71
|
};
|
|
72
72
|
}>>;
|
|
73
73
|
/**
|
|
74
74
|
* Event: A project has been archived.
|
|
75
75
|
*/
|
|
76
|
-
declare const ProjectArchivedEvent:
|
|
76
|
+
declare const ProjectArchivedEvent: _contractspec_lib_contracts12.EventSpec<_contractspec_lib_schema172.SchemaModel<{
|
|
77
77
|
projectId: {
|
|
78
|
-
type:
|
|
78
|
+
type: _contractspec_lib_schema172.FieldType<string, string>;
|
|
79
79
|
isOptional: false;
|
|
80
80
|
};
|
|
81
81
|
archivedBy: {
|
|
82
|
-
type:
|
|
82
|
+
type: _contractspec_lib_schema172.FieldType<string, string>;
|
|
83
83
|
isOptional: false;
|
|
84
84
|
};
|
|
85
85
|
archivedAt: {
|
|
86
|
-
type:
|
|
86
|
+
type: _contractspec_lib_schema172.FieldType<Date, string>;
|
|
87
87
|
isOptional: false;
|
|
88
88
|
};
|
|
89
89
|
}>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.event.d.ts","names":[],"sources":["../../src/project/project.event.ts"],"sourcesContent":[],"mappings":";;;;;;;cAwEa,qBAAmB,
|
|
1
|
+
{"version":3,"file":"project.event.d.ts","names":[],"sources":["../../src/project/project.event.ts"],"sourcesContent":[],"mappings":";;;;;;;cAwEa,qBAAmB,6BAAA,CAAA,sCAAA;EAAnB,SAAA,EAAA;IAUX,IAAA,EAAA,2BAAA,CAAA,SAAA,CAAA,MAAA,EAAA,MAAA,CAAA;;;;;;;EAV8B,cAAA,EAAA;IAAA,IAAA,uCAAA,CAAA,MAAA,EAAA,MAAA,CAAA;IAenB,UAAA,EAAA,KAAA;EAUX,CAAA;;;;;;IAV8B,IAAA,uCAAA,KAAA,EAAA,MAAA,CAAA;IAAA,UAAA,EAAA,KAAA;EAenB,CAAA;CAUX,CAAA,CAAA;;;;cAzBW,qBAAmB,6BAAA,CAAA,sCAAA;;IAeA,IAAA,EAL9B,2BAAA,CAAA,SAK8B,CAAA,MAAA,EAAA,MAAA,CAAA;IAAA,UAAA,EAAA,KAAA;EAenB,CAAA;EAUX,aAAA,EAAA;;;;;EAV+B,SAAA,EAAA;IAAA,IAAA,uCAAA,CAAA,MAAA,EAAA,MAAA,CAAA;;;;;;;;;;;cAfpB,qBAAmB,6BAAA,CAAA,sCAAA;;UAU9B,2BAAA,CAAA;;;;;;;;;;;;;;;;;;;cAKW,sBAAoB,6BAAA,CAAA,sCAAA;;UAU/B,2BAAA,CAAA"}
|