@mostfeatured/dbi 0.2.9 → 0.2.11
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/dist/src/methods/hookInteractionListeners.d.ts.map +1 -1
- package/dist/src/methods/hookInteractionListeners.js +0 -2
- package/dist/src/methods/hookInteractionListeners.js.map +1 -1
- package/dist/src/types/Components/HTMLComponentsV2/index.d.ts.map +1 -1
- package/dist/src/types/Components/HTMLComponentsV2/index.js +3 -17
- package/dist/src/types/Components/HTMLComponentsV2/index.js.map +1 -1
- package/dist/src/types/Components/HTMLComponentsV2/svelteParser.d.ts.map +1 -1
- package/dist/src/types/Components/HTMLComponentsV2/svelteParser.js +16 -24
- package/dist/src/types/Components/HTMLComponentsV2/svelteParser.js.map +1 -1
- package/dist/src/types/Components/HTMLComponentsV2/svelteRenderer.d.ts.map +1 -1
- package/dist/src/types/Components/HTMLComponentsV2/svelteRenderer.js +109 -151
- package/dist/src/types/Components/HTMLComponentsV2/svelteRenderer.js.map +1 -1
- package/dist/test/index.js +1 -4
- package/dist/test/index.js.map +1 -1
- package/package.json +1 -1
- package/src/methods/hookInteractionListeners.ts +0 -2
- package/src/types/Components/HTMLComponentsV2/index.ts +3 -19
- package/src/types/Components/HTMLComponentsV2/svelteParser.ts +16 -25
- package/src/types/Components/HTMLComponentsV2/svelteRenderer.ts +128 -153
- package/test/index.ts +1 -4
|
@@ -4,6 +4,7 @@ import { NamespaceEnums } from "../../../../generated/namespaceData";
|
|
|
4
4
|
import { parseHTMLComponentsV2 } from "./parser";
|
|
5
5
|
import { parseSvelteComponent, createHandlerContext, SvelteComponentInfo, HandlerContextResult } from "./svelteParser";
|
|
6
6
|
import * as stuffs from "stuffs";
|
|
7
|
+
import * as vm from "vm";
|
|
7
8
|
|
|
8
9
|
export interface SvelteRenderOptions {
|
|
9
10
|
data?: Record<string, any>;
|
|
@@ -34,7 +35,7 @@ export async function renderSvelteComponent(
|
|
|
34
35
|
// Use the processed source (with auto-generated names injected)
|
|
35
36
|
const processedSource = componentInfo.processedSource;
|
|
36
37
|
|
|
37
|
-
// Compile the Svelte component for SSR
|
|
38
|
+
// Compile the Svelte component for SSR (Svelte 5)
|
|
38
39
|
const compiled = compile(processedSource, {
|
|
39
40
|
generate: "server",
|
|
40
41
|
css: "injected",
|
|
@@ -45,23 +46,17 @@ export async function renderSvelteComponent(
|
|
|
45
46
|
let html = "";
|
|
46
47
|
try {
|
|
47
48
|
const moduleContext = createModuleContext(dbi, data, ttl);
|
|
48
|
-
const
|
|
49
|
+
const Component = evaluateCompiledComponent(compiled.js.code, moduleContext);
|
|
49
50
|
|
|
50
|
-
//
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (process.env.DEBUG_SVELTE) {
|
|
56
|
-
console.log("Rendered HTML:", html);
|
|
57
|
-
}
|
|
51
|
+
// Svelte 5 SSR: Use render from svelte/server
|
|
52
|
+
const { render } = require("svelte/server");
|
|
53
|
+
// Pass data properties as top-level props (Svelte 5 expects flat props object)
|
|
54
|
+
const renderResult = render(Component, { props: data });
|
|
55
|
+
html = renderResult.body || "";
|
|
58
56
|
} catch (error) {
|
|
59
|
-
console.error("Error rendering Svelte component:", error);
|
|
60
57
|
throw error;
|
|
61
58
|
}
|
|
62
59
|
|
|
63
|
-
console.log("Rendered HTML:", html);
|
|
64
|
-
|
|
65
60
|
// For Svelte mode, inject state into interactive elements as a ref
|
|
66
61
|
// Reuse existing ref if data already has one, otherwise create new
|
|
67
62
|
if (data && Object.keys(data).length > 0) {
|
|
@@ -99,13 +94,9 @@ export async function renderSvelteComponent(
|
|
|
99
94
|
});
|
|
100
95
|
}
|
|
101
96
|
|
|
102
|
-
console.log("HTML with state:", html);
|
|
103
|
-
|
|
104
97
|
// Parse the rendered HTML to Discord components
|
|
105
98
|
const components = parseHTMLComponentsV2(dbi, html, dbiName, { data, ttl });
|
|
106
99
|
|
|
107
|
-
console.log("Parsed Components:", JSON.stringify(components, null, 2));
|
|
108
|
-
|
|
109
100
|
// Create handler context (also captures $effect callbacks)
|
|
110
101
|
const handlerContext = createHandlerContext(componentInfo.scriptContent, data);
|
|
111
102
|
const handlers = new Map<string, { handlerFn: Function, context: any }>();
|
|
@@ -158,161 +149,145 @@ function createModuleContext(dbi: DBI<NamespaceEnums>, data: Record<string, any>
|
|
|
158
149
|
*/
|
|
159
150
|
function evaluateCompiledComponent(code: string, context: Record<string, any>): any {
|
|
160
151
|
try {
|
|
161
|
-
//
|
|
162
|
-
|
|
152
|
+
// Load Svelte 5 internal runtime
|
|
153
|
+
const svelteInternal = require("svelte/internal/server");
|
|
154
|
+
|
|
155
|
+
// Process the code to work in our context
|
|
163
156
|
let processedCode = code;
|
|
164
157
|
|
|
165
|
-
//
|
|
158
|
+
// Collect external modules to inject into sandbox
|
|
159
|
+
const externalModules: Record<string, any> = {};
|
|
160
|
+
|
|
161
|
+
// Replace svelte internal imports
|
|
162
|
+
processedCode = processedCode.replace(
|
|
163
|
+
/import\s*\*\s*as\s*(\w+)\s*from\s*["']svelte\/internal\/server["'];?/g,
|
|
164
|
+
'const $1 = __svelteInternal;'
|
|
165
|
+
);
|
|
166
|
+
processedCode = processedCode.replace(
|
|
167
|
+
/import\s*\{([^}]+)\}\s*from\s*["']svelte\/internal\/server["'];?/g,
|
|
168
|
+
(match, imports) => {
|
|
169
|
+
const importList = imports.split(',').map((i: string) => i.trim());
|
|
170
|
+
return `const { ${importList.join(', ')} } = __svelteInternal;`;
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
// Handle external module imports (default imports)
|
|
175
|
+
processedCode = processedCode.replace(
|
|
176
|
+
/import\s+(\w+)\s+from\s*["']([^"']+)["'];?/g,
|
|
177
|
+
(match, varName, modulePath) => {
|
|
178
|
+
// Skip svelte imports
|
|
179
|
+
if (modulePath.startsWith('svelte')) return '';
|
|
180
|
+
try {
|
|
181
|
+
const mod = require(modulePath);
|
|
182
|
+
externalModules[varName] = mod.default || mod;
|
|
183
|
+
return `const ${varName} = __externalModules.${varName};`;
|
|
184
|
+
} catch (e) {
|
|
185
|
+
return '';
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
// Handle named imports from external modules
|
|
191
|
+
processedCode = processedCode.replace(
|
|
192
|
+
/import\s*\{([^}]+)\}\s*from\s*["']([^"']+)["'];?/g,
|
|
193
|
+
(match, imports, modulePath) => {
|
|
194
|
+
// Skip svelte imports
|
|
195
|
+
if (modulePath.startsWith('svelte')) return '';
|
|
196
|
+
try {
|
|
197
|
+
const mod = require(modulePath);
|
|
198
|
+
const importList = imports.split(',').map((i: string) => i.trim());
|
|
199
|
+
importList.forEach((importName: string) => {
|
|
200
|
+
const [name, alias] = importName.split(' as ').map(s => s.trim());
|
|
201
|
+
externalModules[alias || name] = mod[name] || mod.default?.[name];
|
|
202
|
+
});
|
|
203
|
+
return importList.map((importName: string) => {
|
|
204
|
+
const [name, alias] = importName.split(' as ').map(s => s.trim());
|
|
205
|
+
return `const ${alias || name} = __externalModules.${alias || name};`;
|
|
206
|
+
}).join('\n');
|
|
207
|
+
} catch (e) {
|
|
208
|
+
return '';
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
// Handle namespace imports from external modules
|
|
214
|
+
processedCode = processedCode.replace(
|
|
215
|
+
/import\s*\*\s*as\s*(\w+)\s*from\s*["']([^"']+)["'];?/g,
|
|
216
|
+
(match, varName, modulePath) => {
|
|
217
|
+
// Skip svelte imports
|
|
218
|
+
if (modulePath.startsWith('svelte')) return '';
|
|
219
|
+
try {
|
|
220
|
+
const mod = require(modulePath);
|
|
221
|
+
externalModules[varName] = mod;
|
|
222
|
+
return `const ${varName} = __externalModules.${varName};`;
|
|
223
|
+
} catch (e) {
|
|
224
|
+
return '';
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
// Remove any remaining import statements
|
|
166
230
|
processedCode = processedCode
|
|
167
231
|
.split('\n')
|
|
168
232
|
.filter(line => !line.trim().startsWith('import '))
|
|
169
233
|
.join('\n');
|
|
170
234
|
|
|
171
|
-
//
|
|
172
|
-
processedCode = processedCode.replace(/export\s+default\s+/g, '
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
escape_text: (str: any) => String(str)
|
|
189
|
-
.replace(/&/g, "&")
|
|
190
|
-
.replace(/</g, "<")
|
|
191
|
-
.replace(/>/g, ">"),
|
|
192
|
-
escape_attribute_value: (str: any) => String(str)
|
|
193
|
-
.replace(/"/g, """)
|
|
194
|
-
.replace(/'/g, "'"),
|
|
195
|
-
// Array helpers for {#each}
|
|
196
|
-
ensure_array_like: (value: any) => {
|
|
197
|
-
if (Array.isArray(value)) return value;
|
|
198
|
-
if (value == null) return [];
|
|
199
|
-
if (typeof value === 'object' && Symbol.iterator in value) {
|
|
200
|
-
return Array.from(value);
|
|
201
|
-
}
|
|
202
|
-
return [value];
|
|
203
|
-
},
|
|
204
|
-
// Renderer methods
|
|
205
|
-
component: (fn: Function) => fn,
|
|
206
|
-
push: (content: string) => content,
|
|
207
|
-
pop: () => { },
|
|
208
|
-
attr: (name: string, value: any, is_boolean?: boolean) => {
|
|
209
|
-
if (is_boolean && !value) return '';
|
|
210
|
-
if (value == null) return '';
|
|
211
|
-
return ` ${name}="${String(value)}"`;
|
|
212
|
-
},
|
|
213
|
-
spread_attributes: (attrs: Record<string, any>) => {
|
|
214
|
-
return Object.entries(attrs)
|
|
215
|
-
.map(([key, value]) => ` ${key}="${String(value)}"`)
|
|
216
|
-
.join('');
|
|
217
|
-
},
|
|
218
|
-
spread_props: (props: Record<string, any>) => props,
|
|
219
|
-
bind_props: (props: Record<string, any>, names: string[]) => {
|
|
220
|
-
const result: any = {};
|
|
221
|
-
names.forEach(name => {
|
|
222
|
-
if (name in props) result[name] = props[name];
|
|
223
|
-
});
|
|
224
|
-
return result;
|
|
225
|
-
},
|
|
226
|
-
stringify: (value: any) => JSON.stringify(value),
|
|
227
|
-
store_get: (store: any) => store,
|
|
228
|
-
unsubscribe_stores: () => { },
|
|
229
|
-
// Control flow
|
|
230
|
-
each: (items: any[], fn: Function) => {
|
|
231
|
-
return items.map((item, index) => fn(item, index)).join('');
|
|
232
|
-
},
|
|
233
|
-
// Lifecycle
|
|
235
|
+
// Replace 'export default' with assignment
|
|
236
|
+
processedCode = processedCode.replace(/export\s+default\s+/g, '__exports.default = ');
|
|
237
|
+
// Replace 'export function' with assignment
|
|
238
|
+
processedCode = processedCode.replace(/export\s+function\s+(\w+)/g, '__exports.$1 = function $1');
|
|
239
|
+
// Replace 'export const' with assignment
|
|
240
|
+
processedCode = processedCode.replace(/export\s+const\s+(\w+)/g, '__exports.$1 = ');
|
|
241
|
+
// Replace 'export let' with assignment
|
|
242
|
+
processedCode = processedCode.replace(/export\s+let\s+(\w+)/g, '__exports.$1 = ');
|
|
243
|
+
|
|
244
|
+
// Create the sandbox context
|
|
245
|
+
const __exports: any = {};
|
|
246
|
+
// Svelte lifecycle functions - no-ops for SSR
|
|
247
|
+
const svelteLifecycle = {
|
|
248
|
+
onMount: () => () => { }, // Returns cleanup function
|
|
249
|
+
onDestroy: () => { },
|
|
250
|
+
beforeUpdate: () => { },
|
|
251
|
+
afterUpdate: () => { },
|
|
234
252
|
tick: () => Promise.resolve(),
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
validate_component: (component: any) => component,
|
|
238
|
-
validate_store: (store: any) => store,
|
|
239
|
-
// Misc
|
|
240
|
-
noop: () => { },
|
|
241
|
-
run: (fn: Function) => fn(),
|
|
242
|
-
run_all: (fns: Function[]) => fns.forEach(f => f()),
|
|
243
|
-
is_promise: (value: any) => value && typeof value.then === 'function',
|
|
244
|
-
missing_component: { $$render: () => '' },
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
// Create renderer object that accumulates HTML
|
|
248
|
-
let html = '';
|
|
249
|
-
const $$renderer: any = {
|
|
250
|
-
out: '',
|
|
251
|
-
head: '',
|
|
252
|
-
component: (fn: Function) => {
|
|
253
|
-
return fn($$renderer);
|
|
254
|
-
},
|
|
255
|
-
push: (content: string) => {
|
|
256
|
-
html += content;
|
|
257
|
-
return $$renderer;
|
|
258
|
-
},
|
|
259
|
-
pop: () => $$renderer,
|
|
260
|
-
element: (tag: string, fn: Function) => {
|
|
261
|
-
html += `<${tag}`;
|
|
262
|
-
fn();
|
|
263
|
-
html += `>`;
|
|
264
|
-
return $$renderer;
|
|
265
|
-
},
|
|
266
|
-
close: (tag: string) => {
|
|
267
|
-
html += `</${tag}>`;
|
|
268
|
-
return $$renderer;
|
|
269
|
-
},
|
|
253
|
+
untrack: (fn: () => any) => fn(),
|
|
254
|
+
createEventDispatcher: () => () => { },
|
|
270
255
|
};
|
|
271
256
|
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
|
|
257
|
+
// Note: Svelte 5 runes ($state, $derived, etc.) are compile-time features
|
|
258
|
+
// The compiler transforms them, so we don't need runtime implementations.
|
|
259
|
+
// The `$` variable is used by compiled code as the svelte/internal/server namespace.
|
|
260
|
+
|
|
261
|
+
const sandbox = {
|
|
262
|
+
__svelteInternal: svelteInternal,
|
|
263
|
+
__externalModules: externalModules,
|
|
264
|
+
$: svelteInternal, // Direct alias for compiled Svelte code that uses `$`
|
|
265
|
+
__exports,
|
|
266
|
+
console,
|
|
267
|
+
...svelteLifecycle,
|
|
275
268
|
...context,
|
|
276
|
-
$,
|
|
277
|
-
$$renderer,
|
|
278
|
-
module,
|
|
279
|
-
exports,
|
|
280
|
-
// Lifecycle stubs for SSR (actual lifecycle runs in handler context)
|
|
281
|
-
onMount: (fn: Function) => { /* SSR no-op, real onMount runs in handler context */ },
|
|
282
|
-
onDestroy: (fn: Function) => { /* SSR no-op, real onDestroy runs in handler context */ },
|
|
283
269
|
};
|
|
284
270
|
|
|
285
|
-
|
|
286
|
-
const contextValues = Object.values(allContext);
|
|
287
|
-
|
|
288
|
-
// Wrap in IIFE
|
|
271
|
+
// Wrap code in IIFE
|
|
289
272
|
const wrappedCode = `
|
|
290
|
-
(function(
|
|
273
|
+
(function() {
|
|
291
274
|
${processedCode}
|
|
292
|
-
|
|
293
|
-
})
|
|
275
|
+
})();
|
|
294
276
|
`;
|
|
295
277
|
|
|
296
|
-
//
|
|
297
|
-
|
|
298
|
-
|
|
278
|
+
// Run in VM context for better isolation
|
|
279
|
+
vm.createContext(sandbox);
|
|
280
|
+
vm.runInContext(wrappedCode, sandbox);
|
|
299
281
|
|
|
300
|
-
// Return
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
console.error('Component render error:', e);
|
|
309
|
-
throw e;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
};
|
|
282
|
+
// Return the component
|
|
283
|
+
const Component = sandbox.__exports.default;
|
|
284
|
+
|
|
285
|
+
if (!Component) {
|
|
286
|
+
throw new Error("Svelte component did not export a default component");
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return Component;
|
|
313
290
|
} catch (error) {
|
|
314
|
-
console.error("Error evaluating compiled component:", error);
|
|
315
|
-
console.error("Code preview (first 500 chars):", code.substring(0, 500));
|
|
316
291
|
throw error;
|
|
317
292
|
}
|
|
318
293
|
}
|
package/test/index.ts
CHANGED
|
@@ -26,10 +26,7 @@ dbi.register(({ ChatInput, HTMLComponentsV2 }) => {
|
|
|
26
26
|
HTMLComponentsV2({
|
|
27
27
|
name: "product-showcase",
|
|
28
28
|
mode: 'svelte',
|
|
29
|
-
file: path.join(__dirname, "product-showcase.svelte")
|
|
30
|
-
onExecute(ctx) {
|
|
31
|
-
console.log("Product showcase interaction:", ctx.data[0]);
|
|
32
|
-
}
|
|
29
|
+
file: path.join(__dirname, "product-showcase.svelte")
|
|
33
30
|
});
|
|
34
31
|
|
|
35
32
|
// Test command
|