@happyvertical/smrt-sites 0.30.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/AGENTS.md +18 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +94 -0
- package/dist/index.d.ts +258 -0
- package/dist/index.js +411 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.json +778 -0
- package/dist/smrt-knowledge.json +603 -0
- package/dist/types.d.ts +85 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +62 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# @happyvertical/smrt-sites
|
|
2
|
+
|
|
3
|
+
Multi-tenant site lifecycle management with agent bindings.
|
|
4
|
+
|
|
5
|
+
## Models
|
|
6
|
+
|
|
7
|
+
- **Site**: `domain` (unique per tenant), `tier` (free/standard/premium), `portalConfig` JSON, database connection, `provisioningStatus`/`provisioningTimestamp`. Status: draft/active/suspended/archived.
|
|
8
|
+
- **SiteAgentBinding**: junction linking sites to agent classes. Per-site `config` overrides and `priority` ordering. `conflictColumns: ['site_id', 'agent_class']`.
|
|
9
|
+
|
|
10
|
+
## SiteService
|
|
11
|
+
|
|
12
|
+
Stateless lifecycle ops: `activate()`, `suspend()`, `archive()`. `bindAgent()` handles upsert (update config if exists, create if not).
|
|
13
|
+
|
|
14
|
+
## Gotchas
|
|
15
|
+
|
|
16
|
+
- **Required tenancy**: both models use `@TenantScoped({ mode: 'required' })` — must have tenant context
|
|
17
|
+
- **portalConfig accepts both object and JSON string** in constructor
|
|
18
|
+
- **SiteAgentBinding.config is nullable**: auto-parsed from JSON on init
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright <2025> <Happy Vertical Corporation>
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# @happyvertical/smrt-sites
|
|
2
|
+
|
|
3
|
+
Site lifecycle management for multi-tenant SMRT networks. Manages deployable websites with provisioning status, tier classification, portal configuration, and agent bindings with priority ordering.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @happyvertical/smrt-sites
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import {
|
|
15
|
+
Site, SiteCollection,
|
|
16
|
+
SiteAgentBinding, SiteAgentBindingCollection,
|
|
17
|
+
SiteService
|
|
18
|
+
} from '@happyvertical/smrt-sites';
|
|
19
|
+
|
|
20
|
+
// Create collections
|
|
21
|
+
const sites = await SiteCollection.create({ db });
|
|
22
|
+
const bindings = await SiteAgentBindingCollection.create({ db });
|
|
23
|
+
|
|
24
|
+
// Use SiteService for lifecycle operations
|
|
25
|
+
const service = new SiteService({ sites, bindings });
|
|
26
|
+
|
|
27
|
+
// Create a site (starts in draft status)
|
|
28
|
+
const site = await service.createSite('tenant-123', {
|
|
29
|
+
name: 'Community Hub',
|
|
30
|
+
domain: 'hub.example.com',
|
|
31
|
+
tier: 'standard',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Activate after validation (requires name + domain)
|
|
35
|
+
await service.activateSite(site.id);
|
|
36
|
+
|
|
37
|
+
// Mark infrastructure as provisioned
|
|
38
|
+
await service.markProvisioned(site.id);
|
|
39
|
+
|
|
40
|
+
// Bind agents with priority ordering (higher = first)
|
|
41
|
+
await service.bindAgent(site.id, 'Praeco', { schedule: '0 * * * *' });
|
|
42
|
+
await service.bindAgent(site.id, 'Caelus', { maxArticles: 50 });
|
|
43
|
+
|
|
44
|
+
// Get enabled agents sorted by priority descending
|
|
45
|
+
const agents = await service.getEnabledAgents(site.id);
|
|
46
|
+
|
|
47
|
+
// Suspend or archive a site
|
|
48
|
+
await service.suspendSite(site.id);
|
|
49
|
+
await service.archiveSite(site.id);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Site lifecycle
|
|
53
|
+
|
|
54
|
+
Sites progress through four statuses: `draft` -> `active` -> `suspended` -> `archived`. The `SiteService` enforces validation on activation (name and domain required) and tracks provisioning state separately via `provisioningStatus` (pending/provisioning/ready/failed). Sites are uniquely identified by `(tenant_id, domain)`.
|
|
55
|
+
|
|
56
|
+
### Agent bindings
|
|
57
|
+
|
|
58
|
+
`SiteAgentBinding` is a junction table linking sites to agent classes. Each binding has a `priority` (higher values execute first), an `enabled` flag, and optional per-site `config` overrides stored as JSON. The `bindAgent()` method upserts -- updating config if a binding already exists, creating one if not. `findEnabled()` returns bindings sorted by priority descending.
|
|
59
|
+
|
|
60
|
+
### Portal configuration
|
|
61
|
+
|
|
62
|
+
Sites store theme, branding, and navigation settings in `portalConfig` as JSON. Use `getPortalConfig()`/`setPortalConfig()` for type-safe access. Both models require tenant context (`@TenantScoped({ mode: 'required' })`).
|
|
63
|
+
|
|
64
|
+
## API
|
|
65
|
+
|
|
66
|
+
### Models
|
|
67
|
+
|
|
68
|
+
| Export | Description |
|
|
69
|
+
|--------|------------|
|
|
70
|
+
| `Site` | Site record with domain, status, tier, provisioning tracking, and portal config |
|
|
71
|
+
| `SiteAgentBinding` | Junction binding an agent class to a site with priority and config overrides |
|
|
72
|
+
|
|
73
|
+
### Collections
|
|
74
|
+
|
|
75
|
+
| Export | Description |
|
|
76
|
+
|--------|------------|
|
|
77
|
+
| `SiteCollection` | CRUD and queries for Site records |
|
|
78
|
+
| `SiteAgentBindingCollection` | Queries by site, agent class, and enabled state with priority sorting |
|
|
79
|
+
|
|
80
|
+
### Services
|
|
81
|
+
|
|
82
|
+
| Export | Description |
|
|
83
|
+
|--------|------------|
|
|
84
|
+
| `SiteService` | High-level lifecycle ops: create, activate, suspend, archive, bind/unbind agents |
|
|
85
|
+
|
|
86
|
+
### Key Types
|
|
87
|
+
|
|
88
|
+
`SiteOptions`, `SiteStatus`, `SiteTier`, `ProvisioningStatus`, `SitePortalConfig`, `SiteServiceOptions`, `SiteAgentBindingOptions`, `CreateSiteData`
|
|
89
|
+
|
|
90
|
+
## Dependencies
|
|
91
|
+
|
|
92
|
+
- `@happyvertical/smrt-core` -- ORM and code generation
|
|
93
|
+
- `@happyvertical/smrt-tenancy` -- required multi-tenant scoping
|
|
94
|
+
- Peer: `@happyvertical/smrt-agents` (optional)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { SmrtCollection } from '@happyvertical/smrt-core';
|
|
2
|
+
import { SmrtObject } from '@happyvertical/smrt-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Data required to create a new site
|
|
6
|
+
*/
|
|
7
|
+
export declare interface CreateSiteData {
|
|
8
|
+
name: string;
|
|
9
|
+
domain: string;
|
|
10
|
+
tier?: SiteTier;
|
|
11
|
+
databaseUrl?: string;
|
|
12
|
+
databaseType?: string;
|
|
13
|
+
portalConfig?: SitePortalConfig;
|
|
14
|
+
repositoryUrl?: string;
|
|
15
|
+
templateName?: string;
|
|
16
|
+
metadata?: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Infrastructure provisioning status
|
|
21
|
+
*/
|
|
22
|
+
export declare type ProvisioningStatus = 'pending' | 'provisioning' | 'ready' | 'failed';
|
|
23
|
+
|
|
24
|
+
export declare class Site extends SmrtObject {
|
|
25
|
+
tenantId: string;
|
|
26
|
+
/** Display name, e.g. "Bentley Alberta" */
|
|
27
|
+
name: string;
|
|
28
|
+
/** Primary domain, e.g. "bentleyalberta.com" */
|
|
29
|
+
domain: string;
|
|
30
|
+
/** Lifecycle status */
|
|
31
|
+
status: SiteStatus;
|
|
32
|
+
/** Pricing/feature tier */
|
|
33
|
+
tier: SiteTier;
|
|
34
|
+
/** Site-specific database connection URL */
|
|
35
|
+
databaseUrl: string;
|
|
36
|
+
/** Database type: sqlite, postgres, libsql */
|
|
37
|
+
databaseType: string;
|
|
38
|
+
/** Theme, branding, navigation settings (JSON) */
|
|
39
|
+
portalConfig: string;
|
|
40
|
+
/** Git repository URL */
|
|
41
|
+
repositoryUrl: string;
|
|
42
|
+
/** Starter template name */
|
|
43
|
+
templateName: string;
|
|
44
|
+
/** Infrastructure provisioning status */
|
|
45
|
+
provisioningStatus: ProvisioningStatus | '';
|
|
46
|
+
/** When provisioning completed */
|
|
47
|
+
provisionedAt: Date | null;
|
|
48
|
+
/** Last provisioning error message */
|
|
49
|
+
provisioningError: string;
|
|
50
|
+
/** Extensible key-value metadata (JSON) */
|
|
51
|
+
metadata: string;
|
|
52
|
+
createdAt: Date;
|
|
53
|
+
updatedAt: Date;
|
|
54
|
+
constructor(options?: SiteOptions);
|
|
55
|
+
/**
|
|
56
|
+
* Check if the site is active
|
|
57
|
+
*/
|
|
58
|
+
isActive(): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Check if the site is provisioned and ready
|
|
61
|
+
*/
|
|
62
|
+
isReady(): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Get portal config as parsed object
|
|
65
|
+
*/
|
|
66
|
+
getPortalConfig(): SitePortalConfig;
|
|
67
|
+
/**
|
|
68
|
+
* Set portal config from object
|
|
69
|
+
*/
|
|
70
|
+
setPortalConfig(config: SitePortalConfig): void;
|
|
71
|
+
/**
|
|
72
|
+
* Get metadata as parsed object
|
|
73
|
+
*/
|
|
74
|
+
getMetadata(): Record<string, unknown>;
|
|
75
|
+
/**
|
|
76
|
+
* Set metadata from object
|
|
77
|
+
*/
|
|
78
|
+
setMetadata(data: Record<string, unknown>): void;
|
|
79
|
+
/**
|
|
80
|
+
* Update metadata by merging with existing values
|
|
81
|
+
*/
|
|
82
|
+
updateMetadata(updates: Record<string, unknown>): void;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export declare class SiteAgentBinding extends SmrtObject {
|
|
86
|
+
tenantId: string;
|
|
87
|
+
/** FK to sites table */
|
|
88
|
+
siteId: string;
|
|
89
|
+
/** Agent class name, e.g. "Praeco" */
|
|
90
|
+
agentClass: string;
|
|
91
|
+
/** Whether this agent is enabled for the site */
|
|
92
|
+
enabled: boolean;
|
|
93
|
+
/** Per-site agent configuration overrides (JSON) */
|
|
94
|
+
config: Record<string, unknown> | null;
|
|
95
|
+
/** Sort priority (higher = first) */
|
|
96
|
+
priority: number;
|
|
97
|
+
constructor(options?: SiteAgentBindingOptions);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export declare class SiteAgentBindingCollection extends SmrtCollection<SiteAgentBinding> {
|
|
101
|
+
static readonly _itemClass: typeof SiteAgentBinding;
|
|
102
|
+
/**
|
|
103
|
+
* Find all agent bindings for a site
|
|
104
|
+
*/
|
|
105
|
+
findBySite(siteId: string): Promise<SiteAgentBinding[]>;
|
|
106
|
+
/**
|
|
107
|
+
* Find all sites using a specific agent class
|
|
108
|
+
*/
|
|
109
|
+
findByAgent(agentClass: string): Promise<SiteAgentBinding[]>;
|
|
110
|
+
/**
|
|
111
|
+
* Find enabled bindings for a site, ordered by priority descending
|
|
112
|
+
*/
|
|
113
|
+
findEnabled(siteId: string): Promise<SiteAgentBinding[]>;
|
|
114
|
+
/**
|
|
115
|
+
* Find a specific site-agent binding
|
|
116
|
+
*/
|
|
117
|
+
findBySiteAndAgent(siteId: string, agentClass: string): Promise<SiteAgentBinding | null>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Options for constructing a SiteAgentBinding instance
|
|
122
|
+
*/
|
|
123
|
+
export declare interface SiteAgentBindingOptions {
|
|
124
|
+
id?: string;
|
|
125
|
+
tenantId?: string;
|
|
126
|
+
siteId?: string;
|
|
127
|
+
agentClass?: string;
|
|
128
|
+
enabled?: boolean;
|
|
129
|
+
config?: Record<string, unknown> | string | null;
|
|
130
|
+
priority?: number;
|
|
131
|
+
createdAt?: Date;
|
|
132
|
+
updatedAt?: Date;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export declare class SiteCollection extends SmrtCollection<Site> {
|
|
136
|
+
static readonly _itemClass: typeof Site;
|
|
137
|
+
/**
|
|
138
|
+
* Find a site by its primary domain
|
|
139
|
+
*/
|
|
140
|
+
findByDomain(domain: string): Promise<Site | null>;
|
|
141
|
+
/**
|
|
142
|
+
* Find all sites belonging to a tenant
|
|
143
|
+
*/
|
|
144
|
+
findByTenant(tenantId: string): Promise<Site[]>;
|
|
145
|
+
/**
|
|
146
|
+
* Find sites by lifecycle status
|
|
147
|
+
*/
|
|
148
|
+
findByStatus(status: SiteStatus): Promise<Site[]>;
|
|
149
|
+
/**
|
|
150
|
+
* Find all active sites
|
|
151
|
+
*/
|
|
152
|
+
findActive(): Promise<Site[]>;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Options for constructing a Site instance
|
|
157
|
+
*/
|
|
158
|
+
export declare interface SiteOptions {
|
|
159
|
+
id?: string;
|
|
160
|
+
tenantId?: string;
|
|
161
|
+
name?: string;
|
|
162
|
+
domain?: string;
|
|
163
|
+
status?: SiteStatus;
|
|
164
|
+
tier?: SiteTier;
|
|
165
|
+
databaseUrl?: string;
|
|
166
|
+
databaseType?: string;
|
|
167
|
+
portalConfig?: SitePortalConfig | string;
|
|
168
|
+
repositoryUrl?: string;
|
|
169
|
+
templateName?: string;
|
|
170
|
+
provisioningStatus?: ProvisioningStatus;
|
|
171
|
+
provisionedAt?: Date | null;
|
|
172
|
+
provisioningError?: string;
|
|
173
|
+
metadata?: Record<string, unknown> | string;
|
|
174
|
+
createdAt?: Date;
|
|
175
|
+
updatedAt?: Date;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Portal configuration for theming, branding, and navigation
|
|
180
|
+
*/
|
|
181
|
+
export declare interface SitePortalConfig {
|
|
182
|
+
theme?: string;
|
|
183
|
+
branding?: Record<string, unknown>;
|
|
184
|
+
navigation?: Array<{
|
|
185
|
+
label: string;
|
|
186
|
+
path: string;
|
|
187
|
+
icon?: string;
|
|
188
|
+
}>;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export declare class SiteService {
|
|
192
|
+
private sites;
|
|
193
|
+
private bindings;
|
|
194
|
+
constructor(options: SiteServiceOptions);
|
|
195
|
+
/**
|
|
196
|
+
* Create a new site in draft status
|
|
197
|
+
*/
|
|
198
|
+
createSite(tenantId: string, data: CreateSiteData): Promise<Site>;
|
|
199
|
+
/**
|
|
200
|
+
* Activate a site — validates required fields then sets status to active
|
|
201
|
+
*/
|
|
202
|
+
activateSite(siteId: string): Promise<Site>;
|
|
203
|
+
/**
|
|
204
|
+
* Suspend a site
|
|
205
|
+
*/
|
|
206
|
+
suspendSite(siteId: string): Promise<Site>;
|
|
207
|
+
/**
|
|
208
|
+
* Archive a site
|
|
209
|
+
*/
|
|
210
|
+
archiveSite(siteId: string): Promise<Site>;
|
|
211
|
+
/**
|
|
212
|
+
* Mark site as provisioned and ready
|
|
213
|
+
*/
|
|
214
|
+
markProvisioned(siteId: string): Promise<Site>;
|
|
215
|
+
/**
|
|
216
|
+
* Mark site provisioning as failed
|
|
217
|
+
*/
|
|
218
|
+
markProvisioningFailed(siteId: string, error: string): Promise<Site>;
|
|
219
|
+
/**
|
|
220
|
+
* Bind an agent to a site
|
|
221
|
+
*/
|
|
222
|
+
bindAgent(siteId: string, agentClass: string, config?: Record<string, unknown>): Promise<SiteAgentBinding>;
|
|
223
|
+
/**
|
|
224
|
+
* Unbind an agent from a site
|
|
225
|
+
*/
|
|
226
|
+
unbindAgent(siteId: string, agentClass: string): Promise<void>;
|
|
227
|
+
/**
|
|
228
|
+
* Get all enabled agent bindings for a site
|
|
229
|
+
*/
|
|
230
|
+
getEnabledAgents(siteId: string): Promise<SiteAgentBinding[]>;
|
|
231
|
+
/**
|
|
232
|
+
* Load a site by ID, throwing if not found
|
|
233
|
+
*/
|
|
234
|
+
private requireSite;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Options for creating a SiteService
|
|
239
|
+
*/
|
|
240
|
+
export declare interface SiteServiceOptions {
|
|
241
|
+
sites: SiteCollection;
|
|
242
|
+
bindings: SiteAgentBindingCollection;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Types for smrt-sites package
|
|
247
|
+
*/
|
|
248
|
+
/**
|
|
249
|
+
* Site lifecycle status
|
|
250
|
+
*/
|
|
251
|
+
export declare type SiteStatus = 'draft' | 'active' | 'suspended' | 'archived';
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Site pricing/feature tier
|
|
255
|
+
*/
|
|
256
|
+
export declare type SiteTier = 'free' | 'standard' | 'premium';
|
|
257
|
+
|
|
258
|
+
export { }
|