@happyvertical/smrt-template-sveltekit 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 +28 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +110 -0
- package/index.js +99 -0
- package/package.json +45 -0
- package/template/.env.example +11 -0
- package/template/AGENTS.md +25 -0
- package/template/CLAUDE.md +1 -0
- package/template/README.md +204 -0
- package/template/package.json +27 -0
- package/template/smrt.config.ts +49 -0
- package/template/src/app.d.ts +24 -0
- package/template/src/app.html +12 -0
- package/template/src/hooks.server.ts +136 -0
- package/template/src/lib/objects/Item.ts +44 -0
- package/template/src/lib/objects/index.ts +8 -0
- package/template/src/lib/server/smrt.ts +81 -0
- package/template/src/lib/server/tenancy.ts +267 -0
- package/template/src/routes/+page.svelte +108 -0
- package/template/svelte.config.js +16 -0
- package/template/tsconfig.json +16 -0
- package/template/vite.config.ts +21 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# template-sveltekit
|
|
2
|
+
|
|
3
|
+
Base SvelteKit project template used by `smrt init`. Scaffolds a full-stack, multi-tenant app with SMRT integration.
|
|
4
|
+
|
|
5
|
+
## Exports
|
|
6
|
+
|
|
7
|
+
- `getTemplatePath()` — returns path to template directory
|
|
8
|
+
- `copyTemplate(destination, options)` — copies template files with project name substitution. Skips internal-only directories (e.g. `.svelte-kit/` tsconfig stub).
|
|
9
|
+
- `templateInfo` — metadata (SvelteKit 2.x, Svelte 5, REST API, SMRT CLI, SQLite, multi-tenant)
|
|
10
|
+
|
|
11
|
+
## Template Contents
|
|
12
|
+
|
|
13
|
+
- `template/src/hooks.server.ts` — pre-wires `enableTenancy()`, `createSessionHandler({ enterTenantContext: true })`, and a subdomain → tenantId handle, sequenced in that order
|
|
14
|
+
- `template/src/lib/server/tenancy.ts` — pluggable tenant resolver (`subdomainStrategy`, `pathPrefixStrategy`, `headerStrategy`, `createTenantResolver`)
|
|
15
|
+
- `template/src/lib/server/smrt.ts` — centralized SmrtClassOptions / collection factory
|
|
16
|
+
- `template/src/lib/objects/Item.ts` — example `@smrt()` object
|
|
17
|
+
- `template/src/app.d.ts` — `App.Locals` extends `SessionLocals` from `@happyvertical/smrt-users/sveltekit`
|
|
18
|
+
|
|
19
|
+
## Test Infrastructure
|
|
20
|
+
|
|
21
|
+
- Tests live in `__tests__/` at the package root — **not** inside `template/` — because `cpSync` would otherwise scaffold them into consumer projects.
|
|
22
|
+
- The template's `template/tsconfig.json` extends `./.svelte-kit/tsconfig.json` (per SvelteKit convention). To let the package run its tests without first running `svelte-kit sync`, a vitest `globalSetup` hook at `__tests__/setup/svelte-kit-stub.ts` writes a minimal stub at test startup and removes it at teardown. The stub directory is gitignored AND `copyTemplate()` filters it out as defense-in-depth.
|
|
23
|
+
|
|
24
|
+
## Key Patterns
|
|
25
|
+
|
|
26
|
+
- **Pluggable tenant resolver**: `tenancy.ts` exports strategy functions + a `createTenantResolver()` factory. Consumers swap strategies by editing one line.
|
|
27
|
+
- **File copying with placeholder substitution**: project name is replaced in template files during generation.
|
|
28
|
+
- **No template-internal test pollution**: `__tests__/` and the `.svelte-kit/` stub are package-level and excluded from `copyTemplate` output.
|
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,110 @@
|
|
|
1
|
+
# @happyvertical/smrt-template-sveltekit
|
|
2
|
+
|
|
3
|
+
SvelteKit project template with SMRT framework integration. Scaffolds a full-stack app with auto-generated REST API routes, TypeScript, and SQLite.
|
|
4
|
+
|
|
5
|
+
## What This Template Provides
|
|
6
|
+
|
|
7
|
+
- SvelteKit 2.x with Svelte 5 and TypeScript
|
|
8
|
+
- `smrtPlugin()` Vite integration for automatic REST API route generation
|
|
9
|
+
- Example `@smrt()` object (`Item.ts`) with barrel export
|
|
10
|
+
- Server-side SMRT initialization (`src/lib/server/smrt.ts`)
|
|
11
|
+
- `smrt.config.ts` with SQLite database and optional AI provider
|
|
12
|
+
- `.env.example` with starter environment variables
|
|
13
|
+
- **Multi-tenancy pre-wired**: `src/hooks.server.ts` registers the tenancy interceptor, loads sessions via `createSessionHandler({ enterTenantContext: true })`, and resolves the tenant from a leading subdomain (`acme.demo.local` → `tenantId='acme'`). Strategy is swappable in `src/lib/server/tenancy.ts` — see the template's own README for path-prefix / header-based variants.
|
|
14
|
+
|
|
15
|
+
## Prerequisites
|
|
16
|
+
|
|
17
|
+
- Node.js 18+
|
|
18
|
+
- pnpm (recommended) or npm
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
### With smrt CLI (recommended)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
smrt gnode create my-app --template sveltekit
|
|
26
|
+
cd my-app
|
|
27
|
+
npm install
|
|
28
|
+
npm run dev
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Programmatic usage
|
|
32
|
+
|
|
33
|
+
```javascript
|
|
34
|
+
import { copyTemplate } from '@happyvertical/smrt-template-sveltekit';
|
|
35
|
+
|
|
36
|
+
copyTemplate('./my-new-project', {
|
|
37
|
+
name: 'my-app',
|
|
38
|
+
overwrite: false,
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Getting Started (after scaffolding)
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
cd my-app
|
|
46
|
+
npm install
|
|
47
|
+
cp .env.example .env # Edit with your values
|
|
48
|
+
npm run dev # Start dev server at http://localhost:5173
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Environment Variables
|
|
52
|
+
|
|
53
|
+
Defined in `.env.example`:
|
|
54
|
+
|
|
55
|
+
| Variable | Required | Description |
|
|
56
|
+
|----------|----------|-------------|
|
|
57
|
+
| `DATABASE_URL` | Yes | Database path (default: `./data/app.db`) |
|
|
58
|
+
| `DATABASE_TYPE` | Yes | Database engine (default: `sqlite`) |
|
|
59
|
+
| `PUBLIC_SITE_NAME` | No | Display name for the site |
|
|
60
|
+
| `PUBLIC_SITE_URL` | No | Public URL (default: `http://localhost:5173`) |
|
|
61
|
+
| `OPENAI_API_KEY` | No | OpenAI API key for AI features |
|
|
62
|
+
| `ANTHROPIC_API_KEY` | No | Anthropic API key (alternative AI provider) |
|
|
63
|
+
|
|
64
|
+
## Template Structure
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
template/
|
|
68
|
+
├── .env.example # Environment variable defaults
|
|
69
|
+
├── .gitignore
|
|
70
|
+
├── package.json # Dependencies (smrt-core, SvelteKit)
|
|
71
|
+
├── smrt.config.ts # SMRT configuration (DB, AI, schema migration)
|
|
72
|
+
├── svelte.config.js # SvelteKit config (adapter-auto)
|
|
73
|
+
├── tsconfig.json
|
|
74
|
+
├── vite.config.ts # Vite + smrtPlugin() for API route generation
|
|
75
|
+
└── src/
|
|
76
|
+
├── app.d.ts # SvelteKit type declarations (extends SessionLocals)
|
|
77
|
+
├── app.html # HTML shell
|
|
78
|
+
├── hooks.server.ts # Pre-wired auth + tenancy
|
|
79
|
+
├── lib/
|
|
80
|
+
│ ├── objects/
|
|
81
|
+
│ │ ├── index.ts # Barrel export
|
|
82
|
+
│ │ └── Item.ts # Example @smrt() object
|
|
83
|
+
│ └── server/
|
|
84
|
+
│ ├── smrt.ts # Server-side SMRT initialization
|
|
85
|
+
│ └── tenancy.ts # Pluggable tenant resolver
|
|
86
|
+
└── routes/
|
|
87
|
+
└── +page.svelte # Home page
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Exports
|
|
91
|
+
|
|
92
|
+
This package exposes three things for programmatic use:
|
|
93
|
+
|
|
94
|
+
- `getTemplatePath()` -- returns the absolute path to the `template/` directory
|
|
95
|
+
- `copyTemplate(destination, options)` -- copies template files with project name substitution in `package.json`
|
|
96
|
+
- `templateInfo` -- metadata object describing the template
|
|
97
|
+
|
|
98
|
+
## Placeholder Substitution
|
|
99
|
+
|
|
100
|
+
During `smrt gnode create`, these placeholders are replaced in template files:
|
|
101
|
+
|
|
102
|
+
| Placeholder | Value |
|
|
103
|
+
|-------------|-------|
|
|
104
|
+
| `{{PROJECT_NAME}}` | Project name from CLI |
|
|
105
|
+
| `{{PACKAGE_NAME}}` | Lowercase, hyphenated package name |
|
|
106
|
+
|
|
107
|
+
## Related
|
|
108
|
+
|
|
109
|
+
- [SMRT Framework](https://github.com/happyvertical/smrt)
|
|
110
|
+
- [SvelteKit](https://kit.svelte.dev/)
|
package/index.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMRT SvelteKit Template
|
|
3
|
+
*
|
|
4
|
+
* Provides the SvelteKit project template for SMRT framework.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
cpSync,
|
|
9
|
+
existsSync,
|
|
10
|
+
mkdirSync,
|
|
11
|
+
readFileSync,
|
|
12
|
+
writeFileSync,
|
|
13
|
+
} from 'node:fs';
|
|
14
|
+
import { dirname, join } from 'node:path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
|
|
17
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Paths inside `template/` that are not part of the scaffold output. These
|
|
21
|
+
* exist only to support test-time tooling at the monorepo level (e.g. the
|
|
22
|
+
* vendored `.svelte-kit/tsconfig.json` stub that lets the template's own
|
|
23
|
+
* tests typecheck without first running `svelte-kit sync`).
|
|
24
|
+
*/
|
|
25
|
+
const COPY_SKIP = new Set(['.svelte-kit']);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get the path to the template directory
|
|
29
|
+
*/
|
|
30
|
+
export function getTemplatePath() {
|
|
31
|
+
return join(__dirname, 'template');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Copy the template to a destination directory
|
|
36
|
+
*
|
|
37
|
+
* @param {string} destination - Destination directory path
|
|
38
|
+
* @param {object} options - Options for template copying
|
|
39
|
+
* @param {string} [options.name] - Project name (updates package.json)
|
|
40
|
+
* @param {boolean} [options.overwrite=false] - Overwrite existing files
|
|
41
|
+
*/
|
|
42
|
+
export function copyTemplate(destination, options = {}) {
|
|
43
|
+
const templatePath = getTemplatePath();
|
|
44
|
+
|
|
45
|
+
if (!existsSync(templatePath)) {
|
|
46
|
+
throw new Error('Template directory not found');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Create destination if it doesn't exist
|
|
50
|
+
if (!existsSync(destination)) {
|
|
51
|
+
mkdirSync(destination, { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Copy all template files, skipping internal-only directories
|
|
55
|
+
cpSync(templatePath, destination, {
|
|
56
|
+
recursive: true,
|
|
57
|
+
force: options.overwrite || false,
|
|
58
|
+
filter: (src) => {
|
|
59
|
+
const relative = src.slice(templatePath.length + 1);
|
|
60
|
+
if (!relative) return true;
|
|
61
|
+
const topLevel = relative.split(/[\\/]/)[0];
|
|
62
|
+
return !COPY_SKIP.has(topLevel);
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Update package.json with project name if provided
|
|
67
|
+
if (options.name) {
|
|
68
|
+
const packageJsonPath = join(destination, 'package.json');
|
|
69
|
+
if (existsSync(packageJsonPath)) {
|
|
70
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
71
|
+
packageJson.name = options.name;
|
|
72
|
+
writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return destination;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Template metadata
|
|
81
|
+
*/
|
|
82
|
+
export const templateInfo = {
|
|
83
|
+
name: 'sveltekit',
|
|
84
|
+
description: 'SvelteKit project with SMRT framework integration',
|
|
85
|
+
features: [
|
|
86
|
+
'SvelteKit 2.x with Svelte 5',
|
|
87
|
+
'Auto-generated REST API routes',
|
|
88
|
+
'SMRT CLI integration',
|
|
89
|
+
'TypeScript support',
|
|
90
|
+
'SQLite database (configurable)',
|
|
91
|
+
'Multi-tenant ready (session + subdomain tenant resolution wired)',
|
|
92
|
+
],
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export default {
|
|
96
|
+
getTemplatePath,
|
|
97
|
+
copyTemplate,
|
|
98
|
+
templateInfo,
|
|
99
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@happyvertical/smrt-template-sveltekit",
|
|
3
|
+
"version": "0.30.0",
|
|
4
|
+
"description": "SvelteKit project template with SMRT framework integration",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.js",
|
|
9
|
+
"./template/*": "./template/*"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"CLAUDE.md",
|
|
13
|
+
"README.md",
|
|
14
|
+
"index.js",
|
|
15
|
+
"template",
|
|
16
|
+
"AGENTS.md"
|
|
17
|
+
],
|
|
18
|
+
"keywords": [
|
|
19
|
+
"smrt",
|
|
20
|
+
"sveltekit",
|
|
21
|
+
"template",
|
|
22
|
+
"scaffold"
|
|
23
|
+
],
|
|
24
|
+
"author": "HappyVertical",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/happyvertical/smrt.git",
|
|
29
|
+
"directory": "packages/template-sveltekit"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@happyvertical/smrt-core": "0.30.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"vitest": "^4.0.17"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"registry": "https://registry.npmjs.org",
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:watch": "vitest"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# SMRT SvelteKit App
|
|
2
|
+
|
|
3
|
+
This project uses SMRT objects to generate REST routes, CLI commands, MCP
|
|
4
|
+
tools, and agent/developer knowledge artifacts.
|
|
5
|
+
|
|
6
|
+
## Agent Workflow
|
|
7
|
+
|
|
8
|
+
- Treat `.smrt/smrt-knowledge.json` as generated local context.
|
|
9
|
+
- Regenerate knowledge by running the app build or dev server with
|
|
10
|
+
`smrtPlugin()` enabled.
|
|
11
|
+
- Use `smrt knowledge:review-context --format markdown` or the
|
|
12
|
+
`build-domain-review-context` MCP tool before reviewing SMRT object changes.
|
|
13
|
+
- Use `smrt knowledge:architecture-context --format markdown` or the
|
|
14
|
+
`build-domain-architecture-context` MCP tool when planning new SMRT objects.
|
|
15
|
+
- Keep `CLAUDE.md` as the one-line `@AGENTS.md` shim.
|
|
16
|
+
|
|
17
|
+
## Package Guidance
|
|
18
|
+
|
|
19
|
+
- Keep SMRT object relationship metadata close to the `@smrt()` decorator.
|
|
20
|
+
- Use `knowledge: false` only for objects that should stay out of authored
|
|
21
|
+
agent context while remaining in the runtime manifest.
|
|
22
|
+
- Use `knowledge: { tags, summary, risks }` for domain objects with important
|
|
23
|
+
review or architecture constraints.
|
|
24
|
+
- Do not enable `/__smrt/knowledge` HTTP routes in production without explicit
|
|
25
|
+
admin auth.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# SMRT SvelteKit App
|
|
2
|
+
|
|
3
|
+
A SvelteKit application with SMRT framework integration for rapid development of AI-powered, multi-tenant applications.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
1. **Install dependencies**:
|
|
8
|
+
```bash
|
|
9
|
+
npm install
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
2. **Set up environment**:
|
|
13
|
+
```bash
|
|
14
|
+
cp .env.example .env
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
3. **Start development server**:
|
|
18
|
+
```bash
|
|
19
|
+
npm run dev
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
4. **Initialize database** (optional):
|
|
23
|
+
```bash
|
|
24
|
+
smrt db:setup
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Project Structure
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
├── src/
|
|
31
|
+
│ ├── hooks.server.ts # Auth + tenancy wiring (see "Multi-tenancy" below)
|
|
32
|
+
│ ├── lib/
|
|
33
|
+
│ │ ├── objects/ # SMRT objects (auto-generates API routes)
|
|
34
|
+
│ │ │ ├── index.ts # Export all objects here
|
|
35
|
+
│ │ │ └── Item.ts # Example SMRT object
|
|
36
|
+
│ │ └── server/
|
|
37
|
+
│ │ ├── smrt.ts # SMRT configuration
|
|
38
|
+
│ │ └── tenancy.ts # Pluggable tenant resolver
|
|
39
|
+
│ └── routes/
|
|
40
|
+
│ ├── api/ # Auto-generated API routes (don't edit!)
|
|
41
|
+
│ └── +page.svelte # Home page
|
|
42
|
+
├── smrt.config.ts # Root SMRT config
|
|
43
|
+
├── vite.config.ts # Vite + SMRT plugin config
|
|
44
|
+
└── svelte.config.js # SvelteKit config
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Multi-tenancy
|
|
48
|
+
|
|
49
|
+
This template ships with multi-tenancy pre-wired. Out of the box you get:
|
|
50
|
+
|
|
51
|
+
- **Session loading + auth** via `createSessionHandler({ enterTenantContext: true })` from `@happyvertical/smrt-users/sveltekit`. After the hook runs, `event.locals` carries `{ user, permissions, tenantId, sessionId }`.
|
|
52
|
+
- **Auto-scoped REST routes**. The tenancy interceptor is registered globally (`enableTenancy()` in `hooks.server.ts`), so any model decorated with `@TenantScoped()` is filtered by the current tenant in `AsyncLocalStorage`. The generated `src/routes/api/**` endpoints inherit this automatically.
|
|
53
|
+
- **Subdomain-based tenant resolution**. By default, the leading subdomain is the tenant slug:
|
|
54
|
+
|
|
55
|
+
| URL | Tenant ID |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `https://acme.demo.local/dashboard` | `acme` |
|
|
58
|
+
| `https://www.demo.local/` | `null` (reserved) |
|
|
59
|
+
| `https://demo.local/` | `null` (no subdomain) |
|
|
60
|
+
| `http://localhost:5173/` | `null` (root-like host) |
|
|
61
|
+
|
|
62
|
+
### Local development DNS
|
|
63
|
+
|
|
64
|
+
Browsers won't resolve subdomains of `demo.local` to your dev server automatically. Pick one:
|
|
65
|
+
|
|
66
|
+
**Option A — `/etc/hosts` (simplest, fixed list)**
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
127.0.0.1 demo.local
|
|
70
|
+
127.0.0.1 acme.demo.local
|
|
71
|
+
127.0.0.1 shop.acme.demo.local
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Option B — `dnsmasq` (wildcard, recommended)**
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# macOS
|
|
78
|
+
brew install dnsmasq
|
|
79
|
+
echo 'address=/demo.local/127.0.0.1' | sudo tee -a $(brew --prefix)/etc/dnsmasq.conf
|
|
80
|
+
sudo brew services restart dnsmasq
|
|
81
|
+
# Tell macOS to use dnsmasq for `.local` queries
|
|
82
|
+
sudo mkdir -p /etc/resolver
|
|
83
|
+
echo 'nameserver 127.0.0.1' | sudo tee /etc/resolver/local
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
After either, hit `http://acme.demo.local:5173/` and the tenant will resolve to `acme`.
|
|
87
|
+
|
|
88
|
+
### Swapping the resolution strategy
|
|
89
|
+
|
|
90
|
+
`src/lib/server/tenancy.ts` exposes three built-in strategies plus a `createTenantResolver()` factory. Pick whichever matches your routing:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
// Subdomain (default)
|
|
94
|
+
import {
|
|
95
|
+
createTenantResolver,
|
|
96
|
+
subdomainStrategy,
|
|
97
|
+
} from '$lib/server/tenancy';
|
|
98
|
+
export const resolveTenant = createTenantResolver(subdomainStrategy);
|
|
99
|
+
|
|
100
|
+
// Path prefix: /t/<slug>/...
|
|
101
|
+
import {
|
|
102
|
+
createTenantResolver,
|
|
103
|
+
pathPrefixStrategy,
|
|
104
|
+
} from '$lib/server/tenancy';
|
|
105
|
+
export const resolveTenant = createTenantResolver(pathPrefixStrategy());
|
|
106
|
+
|
|
107
|
+
// Custom prefix: /tenant/<slug>/...
|
|
108
|
+
export const resolveTenant = createTenantResolver(
|
|
109
|
+
pathPrefixStrategy({ prefix: '/tenant/' }),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// HTTP header: x-tenant-id: acme
|
|
113
|
+
import {
|
|
114
|
+
createTenantResolver,
|
|
115
|
+
headerStrategy,
|
|
116
|
+
} from '$lib/server/tenancy';
|
|
117
|
+
export const resolveTenant = createTenantResolver(headerStrategy());
|
|
118
|
+
|
|
119
|
+
// Compose: try subdomain, fall back to header
|
|
120
|
+
import {
|
|
121
|
+
createTenantResolver,
|
|
122
|
+
headerStrategy,
|
|
123
|
+
subdomainStrategy,
|
|
124
|
+
} from '$lib/server/tenancy';
|
|
125
|
+
export const resolveTenant = createTenantResolver((event) => {
|
|
126
|
+
const fromSubdomain = subdomainStrategy(event);
|
|
127
|
+
if (fromSubdomain.tenantId) return fromSubdomain;
|
|
128
|
+
return headerStrategy()(event);
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Inside a `+server.ts` or `+page.server.ts` you can read the resolved tenant either from `event.locals.tenantId` (set by the session handler) or — for any tenant-scoped model — just call its collection methods and the global interceptor will filter automatically.
|
|
133
|
+
|
|
134
|
+
## Creating SMRT Objects
|
|
135
|
+
|
|
136
|
+
1. Create a new file in `src/lib/objects/`:
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// src/lib/objects/Product.ts
|
|
140
|
+
import { SmrtObject, smrt } from '@happyvertical/smrt-core';
|
|
141
|
+
import { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';
|
|
142
|
+
|
|
143
|
+
@smrt({
|
|
144
|
+
api: { include: ['list', 'get', 'create', 'update', 'delete'] },
|
|
145
|
+
cli: { include: ['list', 'get'] },
|
|
146
|
+
})
|
|
147
|
+
@TenantScoped({ mode: 'optional' })
|
|
148
|
+
export class Product extends SmrtObject {
|
|
149
|
+
@tenantId({ nullable: true })
|
|
150
|
+
tenantId: string | null = null;
|
|
151
|
+
|
|
152
|
+
name: string = '';
|
|
153
|
+
price: number = 0.0;
|
|
154
|
+
description: string = '';
|
|
155
|
+
active: boolean = true;
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
2. Export it from `src/lib/objects/index.ts`:
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
export { Item } from './Item.js';
|
|
163
|
+
export { Product } from './Product.js';
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
3. Run `npm run dev` - API routes are auto-generated! Because `Product` is `@TenantScoped`, listing via `GET /api/products` will only return rows for the request's tenant. (The generator pluralizes each class's `collection` field; see the "Generated API Routes" table below for the general `/api/{collection}` form.)
|
|
167
|
+
|
|
168
|
+
## CLI Commands
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# List discovered SMRT objects
|
|
172
|
+
smrt objects
|
|
173
|
+
|
|
174
|
+
# View object details
|
|
175
|
+
smrt introspect
|
|
176
|
+
|
|
177
|
+
# Initialize database tables
|
|
178
|
+
smrt db:setup
|
|
179
|
+
|
|
180
|
+
# Regenerate API routes
|
|
181
|
+
smrt generate-routes
|
|
182
|
+
|
|
183
|
+
# Run operations on objects
|
|
184
|
+
smrt item list
|
|
185
|
+
smrt item get <id>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## API Endpoints
|
|
189
|
+
|
|
190
|
+
For each SMRT object, the following endpoints are auto-generated:
|
|
191
|
+
|
|
192
|
+
- `GET /api/{collection}` - List all items
|
|
193
|
+
- `GET /api/{collection}/[id]` - Get single item
|
|
194
|
+
- `POST /api/{collection}` - Create new item
|
|
195
|
+
- `PUT /api/{collection}/[id]` - Update item
|
|
196
|
+
- `DELETE /api/{collection}/[id]` - Delete item
|
|
197
|
+
|
|
198
|
+
Custom methods are exposed as:
|
|
199
|
+
- `POST /api/{collection}/[id]/{method}` - Call custom method
|
|
200
|
+
|
|
201
|
+
## Learn More
|
|
202
|
+
|
|
203
|
+
- [SMRT Framework Documentation](https://github.com/happyvertical/smrt)
|
|
204
|
+
- [SvelteKit Documentation](https://kit.svelte.dev/)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "smrt-sveltekit-app",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "vite dev",
|
|
7
|
+
"build": "vite build",
|
|
8
|
+
"preview": "vite preview",
|
|
9
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
10
|
+
"typecheck": "svelte-kit sync && tsc --noEmit && svelte-check --tsconfig ./tsconfig.json",
|
|
11
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@sveltejs/adapter-auto": "^7.0.1",
|
|
15
|
+
"@sveltejs/kit": "^2.46.0",
|
|
16
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
17
|
+
"svelte": "^5.18.0",
|
|
18
|
+
"svelte-check": "^4.3.5",
|
|
19
|
+
"typescript": "^5.9.3",
|
|
20
|
+
"vite": "^7.3.1"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@happyvertical/smrt-core": "^0.25.8",
|
|
24
|
+
"@happyvertical/smrt-tenancy": "^0.25.8",
|
|
25
|
+
"@happyvertical/smrt-users": "^0.25.8"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMRT Configuration
|
|
3
|
+
*
|
|
4
|
+
* This file configures the SMRT framework for your project.
|
|
5
|
+
* See: https://github.com/happyvertical/smrt
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
// Global SMRT settings
|
|
10
|
+
smrt: {
|
|
11
|
+
logLevel: 'info',
|
|
12
|
+
schemaMigration: {
|
|
13
|
+
strategy: 'auto-add',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
// Agent/developer knowledge artifacts are generated by smrtPlugin().
|
|
18
|
+
// HTTP API routes stay off unless explicitly enabled for a dev/admin-only workflow.
|
|
19
|
+
knowledge: {
|
|
20
|
+
enabled: true,
|
|
21
|
+
api: {
|
|
22
|
+
enabled: false,
|
|
23
|
+
basePath: '/__smrt/knowledge',
|
|
24
|
+
requireAdmin: true,
|
|
25
|
+
includeDocs: false,
|
|
26
|
+
includePrompts: false,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
// Package-specific configuration
|
|
31
|
+
packages: {
|
|
32
|
+
// CLI configuration
|
|
33
|
+
cli: {
|
|
34
|
+
database: {
|
|
35
|
+
type: 'sqlite',
|
|
36
|
+
url: process.env.DATABASE_URL || './data/app.db',
|
|
37
|
+
},
|
|
38
|
+
verbose: false,
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
// AI configuration (optional)
|
|
42
|
+
ai: process.env.OPENAI_API_KEY
|
|
43
|
+
? {
|
|
44
|
+
provider: 'openai',
|
|
45
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
46
|
+
}
|
|
47
|
+
: undefined,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// See https://svelte.dev/docs/kit/types#app.d.ts
|
|
2
|
+
// for information about these interfaces
|
|
3
|
+
|
|
4
|
+
import type { SessionLocals } from '@happyvertical/smrt-users/sveltekit';
|
|
5
|
+
|
|
6
|
+
declare global {
|
|
7
|
+
namespace App {
|
|
8
|
+
// interface Error {}
|
|
9
|
+
interface Locals extends SessionLocals {
|
|
10
|
+
/**
|
|
11
|
+
* Tenant context object set by the smrt-tenancy SvelteKit handle.
|
|
12
|
+
* `event.locals.tenantId` (from SessionLocals) is the canonical id
|
|
13
|
+
* string; this object carries the full context (permissions,
|
|
14
|
+
* superAdminBypass, etc.) when needed.
|
|
15
|
+
*/
|
|
16
|
+
tenantContext?: import('@happyvertical/smrt-tenancy').MinimalTenantContext;
|
|
17
|
+
}
|
|
18
|
+
// interface PageData {}
|
|
19
|
+
// interface PageState {}
|
|
20
|
+
// interface Platform {}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
%sveltekit.head%
|
|
8
|
+
</head>
|
|
9
|
+
<body data-sveltekit-preload-data="hover">
|
|
10
|
+
<div style="display: contents">%sveltekit.body%</div>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|