@happyvertical/smrt-template-sveltekit 0.56.2 → 0.57.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
CHANGED
|
@@ -82,6 +82,14 @@ It is the ground-up alternative to `smrt-saas-starter`.
|
|
|
82
82
|
- `src/hooks.server.ts` re-exports the runtime's `handle`/`init`: URL tenant
|
|
83
83
|
selection stays a separate candidate and only the verified session
|
|
84
84
|
establishes tenant context. Never turn an untrusted header into authority.
|
|
85
|
+
- Commerce is optional and never a template dependency.
|
|
86
|
+
`src/lib/server/commerce.ts` (`bootstrapCommerceTenant`) calls
|
|
87
|
+
`ensureCommerceTenant()` only when the app has installed
|
|
88
|
+
`@happyvertical/smrt-commerce` (resolved from its own `node_modules`, with a
|
|
89
|
+
variable import specifier), passing `smrt.cookbook.json` when present. The
|
|
90
|
+
`/setup` page runs it from `onOwnerClaimed`, after the owner claim created the
|
|
91
|
+
tenant, and logs a failure because the runtime ignores hook errors. Any other
|
|
92
|
+
tenant-creation path an app adds should call it too (#4009).
|
|
85
93
|
- App code uses `runtime.getCollection()`/`runtime.classOptions()`, which are
|
|
86
94
|
request-scoped (RLS transaction) and must never be retained.
|
|
87
95
|
- `Note` is the single example object (the canonical Notes starter, matching
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-template-sveltekit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.57.0",
|
|
4
4
|
"description": "Minimal SvelteKit project template with s-m-r-t framework integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -32,26 +32,26 @@
|
|
|
32
32
|
"directory": "packages/template-sveltekit"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"@happyvertical/smrt-core": "0.
|
|
35
|
+
"@happyvertical/smrt-core": "0.57.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@happyvertical/smrt-app-cli": "0.
|
|
39
|
-
"@happyvertical/smrt-app-mcp": "0.
|
|
40
|
-
"@happyvertical/smrt-app-runtime": "0.
|
|
41
|
-
"@happyvertical/smrt-assets": "0.
|
|
42
|
-
"@happyvertical/smrt-chat": "0.
|
|
43
|
-
"@happyvertical/smrt-cli": "0.
|
|
44
|
-
"@happyvertical/smrt-config": "0.
|
|
45
|
-
"@happyvertical/smrt-jobs": "0.
|
|
46
|
-
"@happyvertical/smrt-mcp-openai": "0.
|
|
47
|
-
"@happyvertical/smrt-playbooks": "0.
|
|
48
|
-
"@happyvertical/smrt-profiles": "0.
|
|
49
|
-
"@happyvertical/smrt-scanner": "0.
|
|
50
|
-
"@happyvertical/smrt-svelte": "0.
|
|
51
|
-
"@happyvertical/smrt-tenancy": "0.
|
|
52
|
-
"@happyvertical/smrt-ui": "0.
|
|
53
|
-
"@happyvertical/smrt-users": "0.
|
|
54
|
-
"@happyvertical/smrt-web": "0.
|
|
38
|
+
"@happyvertical/smrt-app-cli": "0.57.0",
|
|
39
|
+
"@happyvertical/smrt-app-mcp": "0.57.0",
|
|
40
|
+
"@happyvertical/smrt-app-runtime": "0.57.0",
|
|
41
|
+
"@happyvertical/smrt-assets": "0.57.0",
|
|
42
|
+
"@happyvertical/smrt-chat": "0.57.0",
|
|
43
|
+
"@happyvertical/smrt-cli": "0.57.0",
|
|
44
|
+
"@happyvertical/smrt-config": "0.57.0",
|
|
45
|
+
"@happyvertical/smrt-jobs": "0.57.0",
|
|
46
|
+
"@happyvertical/smrt-mcp-openai": "0.57.0",
|
|
47
|
+
"@happyvertical/smrt-playbooks": "0.57.0",
|
|
48
|
+
"@happyvertical/smrt-profiles": "0.57.0",
|
|
49
|
+
"@happyvertical/smrt-scanner": "0.57.0",
|
|
50
|
+
"@happyvertical/smrt-svelte": "0.57.0",
|
|
51
|
+
"@happyvertical/smrt-tenancy": "0.57.0",
|
|
52
|
+
"@happyvertical/smrt-ui": "0.57.0",
|
|
53
|
+
"@happyvertical/smrt-users": "0.57.0",
|
|
54
|
+
"@happyvertical/smrt-web": "0.57.0",
|
|
55
55
|
"@happyvertical/sql": "^0.89.12",
|
|
56
56
|
"@modelcontextprotocol/client": "2.0.0",
|
|
57
57
|
"@modelcontextprotocol/server": "^2.0.0",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"typescript": "6.0.3",
|
|
66
66
|
"vite": "8.1.4",
|
|
67
67
|
"vitest": "4.1.10",
|
|
68
|
-
"@happyvertical/smrt-reports": "0.
|
|
68
|
+
"@happyvertical/smrt-reports": "0.57.0"
|
|
69
69
|
},
|
|
70
70
|
"publishConfig": {
|
|
71
71
|
"registry": "https://registry.npmjs.org",
|
package/template/package.json
CHANGED
|
@@ -39,21 +39,21 @@
|
|
|
39
39
|
"svelte-check": "^4.7.6",
|
|
40
40
|
"typescript": "6.0.3",
|
|
41
41
|
"vite": "8.3.0",
|
|
42
|
-
"@happyvertical/smrt-cli": "^0.
|
|
42
|
+
"@happyvertical/smrt-cli": "^0.57.0"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@happyvertical/smrt-app-runtime": "^0.
|
|
46
|
-
"@happyvertical/smrt-config": "^0.
|
|
47
|
-
"@happyvertical/smrt-core": "^0.
|
|
48
|
-
"@happyvertical/smrt-jobs": "^0.
|
|
49
|
-
"@happyvertical/smrt-profiles": "^0.
|
|
50
|
-
"@happyvertical/smrt-svelte": "^0.
|
|
51
|
-
"@happyvertical/smrt-tenancy": "^0.
|
|
52
|
-
"@happyvertical/smrt-ui": "^0.
|
|
53
|
-
"@happyvertical/smrt-users": "^0.
|
|
54
|
-
"@happyvertical/smrt-web": "^0.
|
|
45
|
+
"@happyvertical/smrt-app-runtime": "^0.57.0",
|
|
46
|
+
"@happyvertical/smrt-config": "^0.57.0",
|
|
47
|
+
"@happyvertical/smrt-core": "^0.57.0",
|
|
48
|
+
"@happyvertical/smrt-jobs": "^0.57.0",
|
|
49
|
+
"@happyvertical/smrt-profiles": "^0.57.0",
|
|
50
|
+
"@happyvertical/smrt-svelte": "^0.57.0",
|
|
51
|
+
"@happyvertical/smrt-tenancy": "^0.57.0",
|
|
52
|
+
"@happyvertical/smrt-ui": "^0.57.0",
|
|
53
|
+
"@happyvertical/smrt-users": "^0.57.0",
|
|
54
|
+
"@happyvertical/smrt-web": "^0.57.0",
|
|
55
55
|
"@happyvertical/sql": "^0.89.12",
|
|
56
56
|
"@modelcontextprotocol/server": "^2.0.0",
|
|
57
|
-
"@happyvertical/smrt-reports": "^0.
|
|
57
|
+
"@happyvertical/smrt-reports": "^0.57.0"
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional commerce tenant bootstrap.
|
|
3
|
+
*
|
|
4
|
+
* The template does not depend on `@happyvertical/smrt-commerce`. An app that
|
|
5
|
+
* installs it (for example through a cookbook commerce recipe) gets one
|
|
6
|
+
* `ensureCommerceTenant()` call for each tenant it creates: the standard tax
|
|
7
|
+
* codes, its tax settings and the cookbook's tax defaults (`smrt.cookbook.json`).
|
|
8
|
+
* Without commerce this does nothing.
|
|
9
|
+
*
|
|
10
|
+
* `src/routes/setup/+page.server.ts` calls it once the owner claim has
|
|
11
|
+
* created the tenant. Call it from any other place that creates tenants.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { existsSync } from 'node:fs';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import type { DatabaseInterface } from '@happyvertical/sql';
|
|
17
|
+
|
|
18
|
+
const COMMERCE_PACKAGE = '@happyvertical/smrt-commerce';
|
|
19
|
+
|
|
20
|
+
/** The slice of the commerce package entry this module calls. */
|
|
21
|
+
export interface CommerceTenantModule {
|
|
22
|
+
ensureCommerceTenant(
|
|
23
|
+
db: DatabaseInterface,
|
|
24
|
+
tenantId: string,
|
|
25
|
+
options?: { cookbook?: { policies: readonly unknown[] } },
|
|
26
|
+
): Promise<{ tenantId: string }>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface CommerceTenantBootstrapOptions {
|
|
30
|
+
/** Project root holding `node_modules` and `smrt.cookbook.json`. */
|
|
31
|
+
readonly root?: string;
|
|
32
|
+
/** Load the commerce entry, or `null` when it is not installed. */
|
|
33
|
+
readonly loadCommerce?: (
|
|
34
|
+
root: string,
|
|
35
|
+
) => Promise<CommerceTenantModule | null>;
|
|
36
|
+
/** Load the app cookbook's policy rows, or `undefined` without one. */
|
|
37
|
+
readonly loadCookbook?: (
|
|
38
|
+
root: string,
|
|
39
|
+
) => Promise<{ policies: readonly unknown[] } | undefined>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function installedCommerce(
|
|
43
|
+
root: string,
|
|
44
|
+
): Promise<CommerceTenantModule | null> {
|
|
45
|
+
// Installed means a direct dependency of this app. The specifier stays a
|
|
46
|
+
// variable so the build never requires a package the app did not add.
|
|
47
|
+
if (!existsSync(join(root, 'node_modules', COMMERCE_PACKAGE, 'package.json')))
|
|
48
|
+
return null;
|
|
49
|
+
const specifier: string = COMMERCE_PACKAGE;
|
|
50
|
+
return (await import(/* @vite-ignore */ specifier)) as CommerceTenantModule;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function appCookbook(
|
|
54
|
+
root: string,
|
|
55
|
+
): Promise<{ policies: readonly unknown[] } | undefined> {
|
|
56
|
+
const { hasCookbook, loadCookbookApp } = await import(
|
|
57
|
+
'@happyvertical/smrt-svelte/cookbook/server'
|
|
58
|
+
);
|
|
59
|
+
if (!(await hasCookbook(root))) return undefined;
|
|
60
|
+
return (await loadCookbookApp({ root })).cookbook;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Initialize commerce for a tenant this app just created. Returns `null` when
|
|
65
|
+
* commerce is not installed. Errors propagate; the call is idempotent, so a
|
|
66
|
+
* caller may retry it.
|
|
67
|
+
*/
|
|
68
|
+
export async function bootstrapCommerceTenant(
|
|
69
|
+
db: DatabaseInterface,
|
|
70
|
+
tenantId: string,
|
|
71
|
+
options: CommerceTenantBootstrapOptions = {},
|
|
72
|
+
): Promise<{ tenantId: string } | null> {
|
|
73
|
+
const root = options.root ?? process.cwd();
|
|
74
|
+
const commerce = await (options.loadCommerce ?? installedCommerce)(root);
|
|
75
|
+
if (!commerce) return null;
|
|
76
|
+
const cookbook = await (options.loadCookbook ?? appCookbook)(root);
|
|
77
|
+
return commerce.ensureCommerceTenant(db, tenantId, cookbook ? { cookbook } : {});
|
|
78
|
+
}
|
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
import { createOwnerSetupPage } from '@happyvertical/smrt-app-runtime/sveltekit';
|
|
2
2
|
|
|
3
|
+
import { bootstrapCommerceTenant } from '$lib/server/commerce';
|
|
3
4
|
import { runtime } from '$lib/server/smrt';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Local-profile, loopback-only, single-use owner bootstrap. The runtime
|
|
7
8
|
* removes the `pnpm app:setup` onboarding hand-off files once the claim commits.
|
|
9
|
+
* When the app installs commerce, the claimed tenant is initialized for it.
|
|
8
10
|
*/
|
|
9
|
-
export const { load, actions } = createOwnerSetupPage(runtime
|
|
11
|
+
export const { load, actions } = createOwnerSetupPage(runtime, {
|
|
12
|
+
onOwnerClaimed: async ({ tenantId }) => {
|
|
13
|
+
// The runtime ignores hook errors because the claim has committed, so
|
|
14
|
+
// report a failure here; rerunning the bootstrap is safe.
|
|
15
|
+
try {
|
|
16
|
+
await bootstrapCommerceTenant(
|
|
17
|
+
(await runtime.localRuntime()).db,
|
|
18
|
+
tenantId,
|
|
19
|
+
);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error(`Commerce bootstrap failed for tenant ${tenantId}`, error);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
});
|
package/template.config.js
CHANGED
|
@@ -19,23 +19,23 @@ export default {
|
|
|
19
19
|
|
|
20
20
|
// Dependencies to add to generated project
|
|
21
21
|
dependencies: {
|
|
22
|
-
'@happyvertical/smrt-app-runtime': '^0.
|
|
23
|
-
'@happyvertical/smrt-config': '^0.
|
|
24
|
-
'@happyvertical/smrt-core': '^0.
|
|
25
|
-
'@happyvertical/smrt-jobs': '^0.
|
|
26
|
-
'@happyvertical/smrt-profiles': '^0.
|
|
27
|
-
'@happyvertical/smrt-reports': '^0.
|
|
28
|
-
'@happyvertical/smrt-svelte': '^0.
|
|
29
|
-
'@happyvertical/smrt-tenancy': '^0.
|
|
30
|
-
'@happyvertical/smrt-ui': '^0.
|
|
31
|
-
'@happyvertical/smrt-users': '^0.
|
|
32
|
-
'@happyvertical/smrt-web': '^0.
|
|
22
|
+
'@happyvertical/smrt-app-runtime': '^0.57.0',
|
|
23
|
+
'@happyvertical/smrt-config': '^0.57.0',
|
|
24
|
+
'@happyvertical/smrt-core': '^0.57.0',
|
|
25
|
+
'@happyvertical/smrt-jobs': '^0.57.0',
|
|
26
|
+
'@happyvertical/smrt-profiles': '^0.57.0',
|
|
27
|
+
'@happyvertical/smrt-reports': '^0.57.0',
|
|
28
|
+
'@happyvertical/smrt-svelte': '^0.57.0',
|
|
29
|
+
'@happyvertical/smrt-tenancy': '^0.57.0',
|
|
30
|
+
'@happyvertical/smrt-ui': '^0.57.0',
|
|
31
|
+
'@happyvertical/smrt-users': '^0.57.0',
|
|
32
|
+
'@happyvertical/smrt-web': '^0.57.0',
|
|
33
33
|
'@happyvertical/sql': '^0.89.12',
|
|
34
34
|
'@modelcontextprotocol/server': '^2.0.0',
|
|
35
35
|
},
|
|
36
36
|
|
|
37
37
|
devDependencies: {
|
|
38
|
-
'@happyvertical/smrt-cli': '^0.
|
|
38
|
+
'@happyvertical/smrt-cli': '^0.57.0',
|
|
39
39
|
'@sveltejs/adapter-node': '^5.5.7',
|
|
40
40
|
'@sveltejs/kit': '2.70.3',
|
|
41
41
|
'@sveltejs/vite-plugin-svelte': '^7.3.0',
|