@decocms/start 0.36.0 → 0.36.2
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/package.json
CHANGED
|
@@ -60,7 +60,7 @@ export function generatePackageJson(ctx: MigrationContext): string {
|
|
|
60
60
|
|
|
61
61
|
// Fetch latest versions from npm registry
|
|
62
62
|
const startVersion = getLatestVersion("@decocms/start", "0.34.0");
|
|
63
|
-
const appsVersion = getLatestVersion("@decocms/apps", "0.
|
|
63
|
+
const appsVersion = getLatestVersion("@decocms/apps", "0.27.0");
|
|
64
64
|
|
|
65
65
|
const pkg = {
|
|
66
66
|
name: ctx.siteName,
|
package/src/apps/autoconfig.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-configures known apps from CMS blocks.
|
|
3
3
|
*
|
|
4
|
-
* Scans the decofile for
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Scans the decofile for block keys matching known apps and dynamically imports
|
|
5
|
+
* their `mod.ts` from @decocms/apps. Each app mod exports:
|
|
6
|
+
* - `configure(blockData, resolveSecret)` → configures the app client
|
|
7
|
+
* - `handlers` → record of invoke handler keys → handler functions
|
|
8
|
+
*
|
|
9
|
+
* Zero hardcoded app logic in the framework — all app-specific code lives in
|
|
10
|
+
* @decocms/apps/{app}/mod.ts.
|
|
7
11
|
*
|
|
8
12
|
* Usage in setup.ts:
|
|
9
13
|
* import { autoconfigApps } from "@decocms/start/apps/autoconfig";
|
|
@@ -16,57 +20,96 @@ import { onChange } from "../cms/loader";
|
|
|
16
20
|
import { resolveSecret } from "../sdk/crypto";
|
|
17
21
|
|
|
18
22
|
// ---------------------------------------------------------------------------
|
|
19
|
-
//
|
|
23
|
+
// Block key → @decocms/apps module mapping
|
|
20
24
|
// ---------------------------------------------------------------------------
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Maps CMS block keys (e.g. "deco-resend") to their @decocms/apps module path.
|
|
28
|
+
* To add a new app, just add an entry here — no other code changes needed.
|
|
29
|
+
*/
|
|
30
|
+
const BLOCK_TO_APP: Record<string, string> = {
|
|
31
|
+
"deco-resend": "resend",
|
|
32
|
+
// "deco-analytics": "analytics",
|
|
33
|
+
// "deco-shopify": "shopify",
|
|
34
|
+
// "deco-vtex": "vtex",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Generic app loader
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
interface AppMod {
|
|
42
|
+
configure: (
|
|
43
|
+
blockData: unknown,
|
|
44
|
+
resolveSecret: (value: unknown, envKey: string) => Promise<string | null>,
|
|
45
|
+
) => Promise<boolean>;
|
|
46
|
+
handlers: Record<string, (props: any, request: Request) => Promise<any>>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Import app mod using static imports.
|
|
51
|
+
* CF Workers can't catch errors from dynamic string template imports —
|
|
52
|
+
* the vite plugin crashes with AssertionError before the catch runs.
|
|
53
|
+
* Each known app gets a case here. When adding a new app to BLOCK_TO_APP,
|
|
54
|
+
* also add a case to this switch.
|
|
55
|
+
*/
|
|
56
|
+
async function importAppMod(appName: string): Promise<AppMod | null> {
|
|
57
|
+
try {
|
|
58
|
+
switch (appName) {
|
|
59
|
+
case "resend":
|
|
60
|
+
return await import("@decocms/apps/resend/mod");
|
|
61
|
+
default:
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
25
67
|
}
|
|
26
68
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
);
|
|
43
|
-
return {};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
configureResend({
|
|
47
|
-
apiKey,
|
|
48
|
-
emailFrom: block.emailFrom
|
|
49
|
-
? `${block.emailFrom.name || "Contact"} ${block.emailFrom.domain || "<onboarding@resend.dev>"}`
|
|
50
|
-
: undefined,
|
|
51
|
-
emailTo: block.emailTo,
|
|
52
|
-
subject: block.subject,
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
"resend/actions/emails/send.ts": async (props: any) =>
|
|
57
|
-
sendEmail(props),
|
|
58
|
-
};
|
|
59
|
-
} catch {
|
|
60
|
-
// @decocms/apps not installed or doesn't have resend — skip
|
|
69
|
+
async function loadAndConfigureApp(
|
|
70
|
+
blockKey: string,
|
|
71
|
+
appName: string,
|
|
72
|
+
blockData: unknown,
|
|
73
|
+
): Promise<Record<string, InvokeAction>> {
|
|
74
|
+
const mod = await importAppMod(appName);
|
|
75
|
+
if (!mod) return {};
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const ok = await mod.configure(blockData, resolveSecret);
|
|
79
|
+
if (!ok) {
|
|
80
|
+
console.warn(
|
|
81
|
+
`[autoconfig] ${blockKey}: configure() returned false.` +
|
|
82
|
+
` Set DECO_CRYPTO_KEY to decrypt CMS secrets, or set the app's env var fallback.`,
|
|
83
|
+
);
|
|
61
84
|
return {};
|
|
62
85
|
}
|
|
63
|
-
|
|
64
|
-
};
|
|
86
|
+
|
|
87
|
+
console.log(`[autoconfig] ${blockKey}: configured (${Object.keys(mod.handlers).length} handlers)`);
|
|
88
|
+
return mod.handlers;
|
|
89
|
+
} catch (e) {
|
|
90
|
+
console.warn(`[autoconfig] ${blockKey}:`, e);
|
|
91
|
+
return {};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
65
94
|
|
|
66
95
|
// ---------------------------------------------------------------------------
|
|
67
96
|
// Main
|
|
68
97
|
// ---------------------------------------------------------------------------
|
|
69
98
|
|
|
99
|
+
async function configureAll(blocks: Record<string, unknown>): Promise<Record<string, InvokeAction>> {
|
|
100
|
+
const actions: Record<string, InvokeAction> = {};
|
|
101
|
+
|
|
102
|
+
for (const [blockKey, appName] of Object.entries(BLOCK_TO_APP)) {
|
|
103
|
+
const block = blocks[blockKey];
|
|
104
|
+
if (!block) continue;
|
|
105
|
+
|
|
106
|
+
const appActions = await loadAndConfigureApp(blockKey, appName, block);
|
|
107
|
+
Object.assign(actions, appActions);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return actions;
|
|
111
|
+
}
|
|
112
|
+
|
|
70
113
|
/**
|
|
71
114
|
* Auto-configure apps from CMS blocks.
|
|
72
115
|
* Call in setup.ts after setBlocks(). Also re-runs on admin hot-reload.
|
|
@@ -74,19 +117,7 @@ const KNOWN_APPS: Record<string, AppAutoconfigurator> = {
|
|
|
74
117
|
export async function autoconfigApps(blocks: Record<string, unknown>) {
|
|
75
118
|
if (typeof document !== "undefined") return; // server-only
|
|
76
119
|
|
|
77
|
-
const actions
|
|
78
|
-
|
|
79
|
-
for (const [blockKey, configurator] of Object.entries(KNOWN_APPS)) {
|
|
80
|
-
const block = blocks[blockKey];
|
|
81
|
-
if (!block) continue;
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
const appActions = await configurator(block);
|
|
85
|
-
Object.assign(actions, appActions);
|
|
86
|
-
} catch (e) {
|
|
87
|
-
console.warn(`[autoconfig] ${blockKey}:`, e);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
120
|
+
const actions = await configureAll(blocks);
|
|
90
121
|
|
|
91
122
|
if (Object.keys(actions).length > 0) {
|
|
92
123
|
setInvokeActions(() => ({ ...actions }));
|
|
@@ -95,14 +126,7 @@ export async function autoconfigApps(blocks: Record<string, unknown>) {
|
|
|
95
126
|
// Re-configure on admin hot-reload
|
|
96
127
|
onChange(async (newBlocks) => {
|
|
97
128
|
if (typeof document !== "undefined") return;
|
|
98
|
-
const updatedActions
|
|
99
|
-
for (const [blockKey, configurator] of Object.entries(KNOWN_APPS)) {
|
|
100
|
-
const block = newBlocks[blockKey];
|
|
101
|
-
if (!block) continue;
|
|
102
|
-
try {
|
|
103
|
-
Object.assign(updatedActions, await configurator(block));
|
|
104
|
-
} catch {}
|
|
105
|
-
}
|
|
129
|
+
const updatedActions = await configureAll(newBlocks);
|
|
106
130
|
if (Object.keys(updatedActions).length > 0) {
|
|
107
131
|
setInvokeActions(() => ({ ...updatedActions }));
|
|
108
132
|
}
|