@mostfeatured/dbi 0.2.8 → 0.2.10
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 +132 -97
- 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 +108 -151
- package/dist/src/types/Components/HTMLComponentsV2/svelteRenderer.js.map +1 -1
- package/dist/src/utils/recursiveImport.d.ts.map +1 -1
- package/dist/src/utils/recursiveImport.js.map +1 -1
- package/dist/test/index.js +2 -5
- package/dist/test/index.js.map +1 -1
- package/package.json +1 -6
- package/src/methods/hookInteractionListeners.ts +0 -2
- package/src/types/Components/HTMLComponentsV2/index.ts +3 -19
- package/src/types/Components/HTMLComponentsV2/svelteParser.ts +138 -104
- package/src/types/Components/HTMLComponentsV2/svelteRenderer.ts +127 -153
- package/src/utils/recursiveImport.ts +3 -3
- package/test/index.ts +2 -5
- package/tsconfig.json +1 -1
|
@@ -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,16 @@ 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
|
-
// Debug: Log rendered HTML
|
|
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
|
+
const renderResult = render(Component, { props: { ...data, data } });
|
|
54
|
+
html = renderResult.body || "";
|
|
58
55
|
} catch (error) {
|
|
59
|
-
console.error("Error rendering Svelte component:", error);
|
|
60
56
|
throw error;
|
|
61
57
|
}
|
|
62
58
|
|
|
63
|
-
console.log("Rendered HTML:", html);
|
|
64
|
-
|
|
65
59
|
// For Svelte mode, inject state into interactive elements as a ref
|
|
66
60
|
// Reuse existing ref if data already has one, otherwise create new
|
|
67
61
|
if (data && Object.keys(data).length > 0) {
|
|
@@ -99,13 +93,9 @@ export async function renderSvelteComponent(
|
|
|
99
93
|
});
|
|
100
94
|
}
|
|
101
95
|
|
|
102
|
-
console.log("HTML with state:", html);
|
|
103
|
-
|
|
104
96
|
// Parse the rendered HTML to Discord components
|
|
105
97
|
const components = parseHTMLComponentsV2(dbi, html, dbiName, { data, ttl });
|
|
106
98
|
|
|
107
|
-
console.log("Parsed Components:", JSON.stringify(components, null, 2));
|
|
108
|
-
|
|
109
99
|
// Create handler context (also captures $effect callbacks)
|
|
110
100
|
const handlerContext = createHandlerContext(componentInfo.scriptContent, data);
|
|
111
101
|
const handlers = new Map<string, { handlerFn: Function, context: any }>();
|
|
@@ -158,161 +148,145 @@ function createModuleContext(dbi: DBI<NamespaceEnums>, data: Record<string, any>
|
|
|
158
148
|
*/
|
|
159
149
|
function evaluateCompiledComponent(code: string, context: Record<string, any>): any {
|
|
160
150
|
try {
|
|
161
|
-
//
|
|
162
|
-
|
|
151
|
+
// Load Svelte 5 internal runtime
|
|
152
|
+
const svelteInternal = require("svelte/internal/server");
|
|
153
|
+
|
|
154
|
+
// Process the code to work in our context
|
|
163
155
|
let processedCode = code;
|
|
164
156
|
|
|
165
|
-
//
|
|
157
|
+
// Collect external modules to inject into sandbox
|
|
158
|
+
const externalModules: Record<string, any> = {};
|
|
159
|
+
|
|
160
|
+
// Replace svelte internal imports
|
|
161
|
+
processedCode = processedCode.replace(
|
|
162
|
+
/import\s*\*\s*as\s*(\w+)\s*from\s*["']svelte\/internal\/server["'];?/g,
|
|
163
|
+
'const $1 = __svelteInternal;'
|
|
164
|
+
);
|
|
165
|
+
processedCode = processedCode.replace(
|
|
166
|
+
/import\s*\{([^}]+)\}\s*from\s*["']svelte\/internal\/server["'];?/g,
|
|
167
|
+
(match, imports) => {
|
|
168
|
+
const importList = imports.split(',').map((i: string) => i.trim());
|
|
169
|
+
return `const { ${importList.join(', ')} } = __svelteInternal;`;
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
// Handle external module imports (default imports)
|
|
174
|
+
processedCode = processedCode.replace(
|
|
175
|
+
/import\s+(\w+)\s+from\s*["']([^"']+)["'];?/g,
|
|
176
|
+
(match, varName, modulePath) => {
|
|
177
|
+
// Skip svelte imports
|
|
178
|
+
if (modulePath.startsWith('svelte')) return '';
|
|
179
|
+
try {
|
|
180
|
+
const mod = require(modulePath);
|
|
181
|
+
externalModules[varName] = mod.default || mod;
|
|
182
|
+
return `const ${varName} = __externalModules.${varName};`;
|
|
183
|
+
} catch (e) {
|
|
184
|
+
return '';
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
// Handle named imports from external modules
|
|
190
|
+
processedCode = processedCode.replace(
|
|
191
|
+
/import\s*\{([^}]+)\}\s*from\s*["']([^"']+)["'];?/g,
|
|
192
|
+
(match, imports, modulePath) => {
|
|
193
|
+
// Skip svelte imports
|
|
194
|
+
if (modulePath.startsWith('svelte')) return '';
|
|
195
|
+
try {
|
|
196
|
+
const mod = require(modulePath);
|
|
197
|
+
const importList = imports.split(',').map((i: string) => i.trim());
|
|
198
|
+
importList.forEach((importName: string) => {
|
|
199
|
+
const [name, alias] = importName.split(' as ').map(s => s.trim());
|
|
200
|
+
externalModules[alias || name] = mod[name] || mod.default?.[name];
|
|
201
|
+
});
|
|
202
|
+
return importList.map((importName: string) => {
|
|
203
|
+
const [name, alias] = importName.split(' as ').map(s => s.trim());
|
|
204
|
+
return `const ${alias || name} = __externalModules.${alias || name};`;
|
|
205
|
+
}).join('\n');
|
|
206
|
+
} catch (e) {
|
|
207
|
+
return '';
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// Handle namespace imports from external modules
|
|
213
|
+
processedCode = processedCode.replace(
|
|
214
|
+
/import\s*\*\s*as\s*(\w+)\s*from\s*["']([^"']+)["'];?/g,
|
|
215
|
+
(match, varName, modulePath) => {
|
|
216
|
+
// Skip svelte imports
|
|
217
|
+
if (modulePath.startsWith('svelte')) return '';
|
|
218
|
+
try {
|
|
219
|
+
const mod = require(modulePath);
|
|
220
|
+
externalModules[varName] = mod;
|
|
221
|
+
return `const ${varName} = __externalModules.${varName};`;
|
|
222
|
+
} catch (e) {
|
|
223
|
+
return '';
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
// Remove any remaining import statements
|
|
166
229
|
processedCode = processedCode
|
|
167
230
|
.split('\n')
|
|
168
231
|
.filter(line => !line.trim().startsWith('import '))
|
|
169
232
|
.join('\n');
|
|
170
233
|
|
|
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
|
|
234
|
+
// Replace 'export default' with assignment
|
|
235
|
+
processedCode = processedCode.replace(/export\s+default\s+/g, '__exports.default = ');
|
|
236
|
+
// Replace 'export function' with assignment
|
|
237
|
+
processedCode = processedCode.replace(/export\s+function\s+(\w+)/g, '__exports.$1 = function $1');
|
|
238
|
+
// Replace 'export const' with assignment
|
|
239
|
+
processedCode = processedCode.replace(/export\s+const\s+(\w+)/g, '__exports.$1 = ');
|
|
240
|
+
// Replace 'export let' with assignment
|
|
241
|
+
processedCode = processedCode.replace(/export\s+let\s+(\w+)/g, '__exports.$1 = ');
|
|
242
|
+
|
|
243
|
+
// Create the sandbox context
|
|
244
|
+
const __exports: any = {};
|
|
245
|
+
// Svelte lifecycle functions - no-ops for SSR
|
|
246
|
+
const svelteLifecycle = {
|
|
247
|
+
onMount: () => () => { }, // Returns cleanup function
|
|
248
|
+
onDestroy: () => { },
|
|
249
|
+
beforeUpdate: () => { },
|
|
250
|
+
afterUpdate: () => { },
|
|
234
251
|
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
|
-
},
|
|
252
|
+
untrack: (fn: () => any) => fn(),
|
|
253
|
+
createEventDispatcher: () => () => { },
|
|
270
254
|
};
|
|
271
255
|
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
|
|
256
|
+
// Note: Svelte 5 runes ($state, $derived, etc.) are compile-time features
|
|
257
|
+
// The compiler transforms them, so we don't need runtime implementations.
|
|
258
|
+
// The `$` variable is used by compiled code as the svelte/internal/server namespace.
|
|
259
|
+
|
|
260
|
+
const sandbox = {
|
|
261
|
+
__svelteInternal: svelteInternal,
|
|
262
|
+
__externalModules: externalModules,
|
|
263
|
+
$: svelteInternal, // Direct alias for compiled Svelte code that uses `$`
|
|
264
|
+
__exports,
|
|
265
|
+
console,
|
|
266
|
+
...svelteLifecycle,
|
|
275
267
|
...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
268
|
};
|
|
284
269
|
|
|
285
|
-
|
|
286
|
-
const contextValues = Object.values(allContext);
|
|
287
|
-
|
|
288
|
-
// Wrap in IIFE
|
|
270
|
+
// Wrap code in IIFE
|
|
289
271
|
const wrappedCode = `
|
|
290
|
-
(function(
|
|
272
|
+
(function() {
|
|
291
273
|
${processedCode}
|
|
292
|
-
|
|
293
|
-
})
|
|
274
|
+
})();
|
|
294
275
|
`;
|
|
295
276
|
|
|
296
|
-
//
|
|
297
|
-
|
|
298
|
-
|
|
277
|
+
// Run in VM context for better isolation
|
|
278
|
+
vm.createContext(sandbox);
|
|
279
|
+
vm.runInContext(wrappedCode, sandbox);
|
|
299
280
|
|
|
300
|
-
// Return
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
console.error('Component render error:', e);
|
|
309
|
-
throw e;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
};
|
|
281
|
+
// Return the component
|
|
282
|
+
const Component = sandbox.__exports.default;
|
|
283
|
+
|
|
284
|
+
if (!Component) {
|
|
285
|
+
throw new Error("Svelte component did not export a default component");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return Component;
|
|
313
289
|
} catch (error) {
|
|
314
|
-
console.error("Error evaluating compiled component:", error);
|
|
315
|
-
console.error("Code preview (first 500 chars):", code.substring(0, 500));
|
|
316
290
|
throw error;
|
|
317
291
|
}
|
|
318
292
|
}
|
|
@@ -5,7 +5,7 @@ import path from "path";
|
|
|
5
5
|
* @example
|
|
6
6
|
* await recursiveImport("./src", [".js"], [".d.ts"])
|
|
7
7
|
*/
|
|
8
|
-
export async function recursiveImport(folderPath: string, exts: string[] = [".js"], ignore: string[] = [".d.ts",".js.map",".d.ts.map"]): Promise<any> {
|
|
8
|
+
export async function recursiveImport(folderPath: string, exts: string[] = [".js"], ignore: string[] = [".d.ts", ".js.map", ".d.ts.map"]): Promise<any> {
|
|
9
9
|
let files = await fs.promises.readdir(folderPath, { withFileTypes: true });
|
|
10
10
|
let dirName = __dirname;
|
|
11
11
|
|
|
@@ -24,8 +24,8 @@ export async function recursiveImport(folderPath: string, exts: string[] = [".js
|
|
|
24
24
|
} catch (e: any) {
|
|
25
25
|
// Ignore "Missing 'default' export" errors in Bun runtime
|
|
26
26
|
// The module's side effects still execute before the error is thrown
|
|
27
|
-
if (!e.message?.includes("Missing 'default' export") &&
|
|
28
|
-
|
|
27
|
+
if (!e.message?.includes("Missing 'default' export") &&
|
|
28
|
+
!e.message?.includes("does not provide an export named 'default'")) {
|
|
29
29
|
throw e;
|
|
30
30
|
}
|
|
31
31
|
}
|
package/test/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ import path from "path";
|
|
|
3
3
|
|
|
4
4
|
const dbi = createDBI("svelte", {
|
|
5
5
|
discord: {
|
|
6
|
-
token: "
|
|
6
|
+
token: "ODI0MjEwMTMyMzUwMDA5MzY2.GFvvpD.FsVgZH6oNtUYTapngDjurqCBovx5cpQ4ErwsKg",
|
|
7
7
|
options: {
|
|
8
8
|
intents: [
|
|
9
9
|
"GuildMessages",
|
|
@@ -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
|