@mostfeatured/dbi 0.2.14 → 0.2.15

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.
Files changed (45) hide show
  1. package/dist/src/types/Components/HTMLComponentsV2/index.d.ts +33 -1
  2. package/dist/src/types/Components/HTMLComponentsV2/index.d.ts.map +1 -1
  3. package/dist/src/types/Components/HTMLComponentsV2/index.js +408 -82
  4. package/dist/src/types/Components/HTMLComponentsV2/index.js.map +1 -1
  5. package/dist/src/types/Components/HTMLComponentsV2/parser.d.ts +52 -0
  6. package/dist/src/types/Components/HTMLComponentsV2/parser.d.ts.map +1 -1
  7. package/dist/src/types/Components/HTMLComponentsV2/parser.js +275 -0
  8. package/dist/src/types/Components/HTMLComponentsV2/parser.js.map +1 -1
  9. package/dist/src/types/Components/HTMLComponentsV2/svelteParser.d.ts +26 -0
  10. package/dist/src/types/Components/HTMLComponentsV2/svelteParser.d.ts.map +1 -1
  11. package/dist/src/types/Components/HTMLComponentsV2/svelteParser.js +468 -30
  12. package/dist/src/types/Components/HTMLComponentsV2/svelteParser.js.map +1 -1
  13. package/dist/src/types/Components/HTMLComponentsV2/svelteRenderer.d.ts +10 -0
  14. package/dist/src/types/Components/HTMLComponentsV2/svelteRenderer.d.ts.map +1 -1
  15. package/dist/src/types/Components/HTMLComponentsV2/svelteRenderer.js +76 -11
  16. package/dist/src/types/Components/HTMLComponentsV2/svelteRenderer.js.map +1 -1
  17. package/dist/test/index.js +76 -3
  18. package/dist/test/index.js.map +1 -1
  19. package/docs/ADVANCED_FEATURES.md +4 -0
  20. package/docs/API_REFERENCE.md +4 -0
  21. package/docs/CHAT_INPUT.md +4 -0
  22. package/docs/COMPONENTS.md +4 -0
  23. package/docs/EVENTS.md +4 -0
  24. package/docs/GETTING_STARTED.md +4 -0
  25. package/docs/LOCALIZATION.md +4 -0
  26. package/docs/README.md +4 -0
  27. package/docs/SVELTE_COMPONENTS.md +162 -6
  28. package/docs/llm/ADVANCED_FEATURES.txt +521 -0
  29. package/docs/llm/API_REFERENCE.txt +659 -0
  30. package/docs/llm/CHAT_INPUT.txt +514 -0
  31. package/docs/llm/COMPONENTS.txt +595 -0
  32. package/docs/llm/EVENTS.txt +449 -0
  33. package/docs/llm/GETTING_STARTED.txt +296 -0
  34. package/docs/llm/LOCALIZATION.txt +501 -0
  35. package/docs/llm/README.txt +193 -0
  36. package/docs/llm/SVELTE_COMPONENTS.txt +566 -0
  37. package/generated/svelte-dbi.d.ts +122 -0
  38. package/package.json +1 -1
  39. package/src/types/Components/HTMLComponentsV2/index.ts +466 -94
  40. package/src/types/Components/HTMLComponentsV2/parser.ts +317 -0
  41. package/src/types/Components/HTMLComponentsV2/svelteParser.ts +526 -31
  42. package/src/types/Components/HTMLComponentsV2/svelteRenderer.ts +91 -13
  43. package/test/index.ts +76 -3
  44. package/test/product-showcase.svelte +380 -24
  45. package/llm.txt +0 -1088
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseSvelteComponent = parseSvelteComponent;
4
+ exports.validateSvelteComponent = validateSvelteComponent;
5
+ exports.logValidationWarnings = logValidationWarnings;
4
6
  exports.createHandlerContext = createHandlerContext;
5
7
  // Lazy imports to avoid issues with package managers that don't properly hoist dependencies
6
8
  let _parse;
@@ -66,8 +68,38 @@ function walkSvelteAst(node, callback) {
66
68
  */
67
69
  async function parseSvelteComponent(source, data) {
68
70
  await ensureImports();
69
- const ast = _parse(source);
71
+ let ast;
72
+ try {
73
+ ast = _parse(source);
74
+ }
75
+ catch (parseError) {
76
+ // Format Svelte parse error with helpful details
77
+ const errorMessage = parseError.message || 'Unknown parse error';
78
+ const location = parseError.start || parseError.loc;
79
+ let details = errorMessage;
80
+ if (location) {
81
+ const lines = source.split('\n');
82
+ const lineNum = location.line || 1;
83
+ const column = location.column || 0;
84
+ const errorLine = lines[lineNum - 1] || '';
85
+ const prevLine = lines[lineNum - 2] || '';
86
+ const nextLine = lines[lineNum] || '';
87
+ details = `
88
+ Svelte Parse Error at line ${lineNum}, column ${column}:
89
+ ${errorMessage}
90
+
91
+ ${lineNum > 1 ? `${lineNum - 1} | ${prevLine}\n` : ''}${lineNum} | ${errorLine}
92
+ ${' '.repeat(String(lineNum).length + 3 + column)}^
93
+ ${nextLine ? `${lineNum + 1} | ${nextLine}` : ''}
94
+ `.trim();
95
+ }
96
+ const enhancedError = new Error(`[DBI-Svelte] Failed to parse Svelte component:\n${details}`);
97
+ enhancedError.originalError = parseError;
98
+ enhancedError.type = 'svelte-parse-error';
99
+ throw enhancedError;
100
+ }
70
101
  const handlers = new Map();
102
+ const modalHandlers = new Map();
71
103
  let scriptContent = "";
72
104
  // Extract script content
73
105
  if (ast.instance) {
@@ -81,6 +113,53 @@ async function parseSvelteComponent(source, data) {
81
113
  walkSvelteAst(ast.html || ast.fragment, (node) => {
82
114
  if (node.type === "Element" || node.type === "InlineComponent" || node.type === "RegularElement" || node.type === "Component") {
83
115
  const attributes = node.attributes || [];
116
+ const nodeName = node.name.toLowerCase();
117
+ // Special handling for <components type="modal"> elements
118
+ if (nodeName === "components") {
119
+ const typeAttr = attributes.find((attr) => attr.type === "Attribute" && attr.name === "type");
120
+ const typeValue = typeAttr ? getAttributeValue(typeAttr) : null;
121
+ if (typeValue === "modal") {
122
+ // This is a modal definition - extract id and onsubmit handler
123
+ const idAttr = attributes.find((attr) => attr.type === "Attribute" && attr.name === "id");
124
+ const modalId = idAttr ? getAttributeValue(idAttr) : null;
125
+ if (modalId) {
126
+ const modalInfo = { modalId };
127
+ // Find onsubmit handler
128
+ for (const attr of attributes) {
129
+ const isOnSubmit = (attr.type === "Attribute" && attr.name === "onsubmit") ||
130
+ (attr.type === "EventHandler" && attr.name === "submit");
131
+ if (isOnSubmit) {
132
+ let handlerName = "";
133
+ if (attr.type === "Attribute" && Array.isArray(attr.value)) {
134
+ const exprValue = attr.value.find((v) => v.type === "ExpressionTag" || v.type === "MustacheTag");
135
+ if (exprValue && exprValue.expression) {
136
+ if (exprValue.expression.type === "Identifier") {
137
+ handlerName = exprValue.expression.name;
138
+ }
139
+ else if (exprValue.expression.type === "CallExpression" && exprValue.expression.callee) {
140
+ handlerName = exprValue.expression.callee.name;
141
+ }
142
+ }
143
+ }
144
+ else if (attr.expression) {
145
+ if (attr.expression.type === "Identifier") {
146
+ handlerName = attr.expression.name;
147
+ }
148
+ else if (attr.expression.type === "CallExpression" && attr.expression.callee) {
149
+ handlerName = attr.expression.callee.name;
150
+ }
151
+ }
152
+ if (handlerName) {
153
+ modalInfo.onsubmitHandler = handlerName;
154
+ }
155
+ break;
156
+ }
157
+ }
158
+ modalHandlers.set(modalId, modalInfo);
159
+ }
160
+ }
161
+ return; // Don't process <components> as regular elements
162
+ }
84
163
  // Find name attribute
85
164
  const nameAttr = attributes.find((attr) => attr.type === "Attribute" && attr.name === "name");
86
165
  // Check if element has an onclick/onchange/handler and get the handler info
@@ -186,10 +265,46 @@ async function parseSvelteComponent(source, data) {
186
265
  const tagEnd = node.start + 1 + node.name.length; // +1 for '<'
187
266
  processedSource = processedSource.slice(0, tagEnd) + ` name="${name}"` + processedSource.slice(tagEnd);
188
267
  }
268
+ // Extract declared props from $props() destructuring
269
+ const declaredProps = [];
270
+ const propsWithDefaults = [];
271
+ const propsMatch = scriptContent.match(/let\s+\{([\s\S]*?)\}\s*=\s*\$props\(\)/);
272
+ if (propsMatch) {
273
+ const propsContent = propsMatch[1];
274
+ // Remove comments first
275
+ const cleanedContent = propsContent
276
+ .replace(/\/\/.*$/gm, '')
277
+ .replace(/\/\*[\s\S]*?\*\//g, '');
278
+ const props = parsePropsContent(cleanedContent);
279
+ for (const prop of props) {
280
+ if (prop.name) {
281
+ declaredProps.push(prop.name);
282
+ if (prop.defaultValue !== undefined) {
283
+ propsWithDefaults.push(prop.name);
284
+ }
285
+ }
286
+ }
287
+ }
288
+ // Extract declared function names
289
+ const declaredFunctions = [];
290
+ const funcRegex = /(?:async\s+)?function\s+(\w+)\s*\(/g;
291
+ let funcMatch;
292
+ while ((funcMatch = funcRegex.exec(scriptContent)) !== null) {
293
+ declaredFunctions.push(funcMatch[1]);
294
+ }
295
+ // Also match arrow functions assigned to variables: const/let/var name = (async) () =>
296
+ const arrowRegex = /(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>/g;
297
+ while ((funcMatch = arrowRegex.exec(scriptContent)) !== null) {
298
+ declaredFunctions.push(funcMatch[1]);
299
+ }
189
300
  return {
190
301
  handlers,
302
+ modalHandlers,
191
303
  scriptContent,
192
- processedSource
304
+ processedSource,
305
+ declaredProps,
306
+ propsWithDefaults,
307
+ declaredFunctions
193
308
  };
194
309
  }
195
310
  /**
@@ -236,6 +351,195 @@ function extractExpressionValue(expr) {
236
351
  }
237
352
  return "";
238
353
  }
354
+ /**
355
+ * Parse $props() destructuring content with proper brace/bracket counting
356
+ * Handles nested objects like: { name, options = { a: 1, b: [1, 2] }, count = 0 }
357
+ */
358
+ function parsePropsContent(content) {
359
+ const props = [];
360
+ let current = '';
361
+ let braceCount = 0;
362
+ let bracketCount = 0;
363
+ let parenCount = 0;
364
+ let inString = null;
365
+ for (let i = 0; i <= content.length; i++) {
366
+ const char = content[i];
367
+ const prevChar = i > 0 ? content[i - 1] : '';
368
+ // Handle string boundaries
369
+ if ((char === '"' || char === "'" || char === '`') && prevChar !== '\\') {
370
+ if (inString === char) {
371
+ inString = null;
372
+ }
373
+ else if (!inString) {
374
+ inString = char;
375
+ }
376
+ }
377
+ // Only process structural characters when not in a string
378
+ if (!inString) {
379
+ if (char === '{')
380
+ braceCount++;
381
+ else if (char === '}')
382
+ braceCount--;
383
+ else if (char === '[')
384
+ bracketCount++;
385
+ else if (char === ']')
386
+ bracketCount--;
387
+ else if (char === '(')
388
+ parenCount++;
389
+ else if (char === ')')
390
+ parenCount--;
391
+ // Split on comma only when at top level (no nested braces/brackets/parens)
392
+ if ((char === ',' || i === content.length) && braceCount === 0 && bracketCount === 0 && parenCount === 0) {
393
+ const trimmed = current.trim();
394
+ if (trimmed) {
395
+ // Parse "name = defaultValue" or just "name"
396
+ const equalsIndex = trimmed.indexOf('=');
397
+ if (equalsIndex > 0) {
398
+ const name = trimmed.substring(0, equalsIndex).trim();
399
+ const defaultValue = trimmed.substring(equalsIndex + 1).trim();
400
+ props.push({ name, defaultValue });
401
+ }
402
+ else {
403
+ props.push({ name: trimmed });
404
+ }
405
+ }
406
+ current = '';
407
+ continue;
408
+ }
409
+ }
410
+ if (i < content.length) {
411
+ current += char;
412
+ }
413
+ }
414
+ return props;
415
+ }
416
+ /**
417
+ * Validate a Svelte component and return warnings
418
+ * Call this during development/registration to catch potential issues early
419
+ */
420
+ function validateSvelteComponent(componentInfo, data = {}, componentName = 'unknown') {
421
+ const warnings = [];
422
+ // Skip internal props/data keys (used by the framework)
423
+ const internalKeys = ['$ref', '$unRef', '__unRefWrapped__', '$autoNames', 'data'];
424
+ // 1. Check for props declared but not provided in data (missing required data)
425
+ // Skip props that have default values - they don't require data
426
+ for (const prop of componentInfo.declaredProps) {
427
+ if (internalKeys.includes(prop))
428
+ continue;
429
+ if (componentInfo.propsWithDefaults.includes(prop))
430
+ continue; // Has default, not required
431
+ if (!(prop in data)) {
432
+ warnings.push({
433
+ type: 'missing-data',
434
+ message: `[${componentName}] Prop "${prop}" is declared in $props() without a default value but not provided in data`,
435
+ details: `Add "${prop}" to your data object or provide a default value in $props()`
436
+ });
437
+ }
438
+ }
439
+ // 2. Check for data provided but not declared in props (potential typo or unused)
440
+ for (const key of Object.keys(data)) {
441
+ if (internalKeys.includes(key))
442
+ continue;
443
+ if (key.startsWith('$'))
444
+ continue; // Skip all $-prefixed internal keys
445
+ if (!componentInfo.declaredProps.includes(key)) {
446
+ warnings.push({
447
+ type: 'unused-data',
448
+ message: `[${componentName}] Data key "${key}" is provided but not declared in $props()`,
449
+ details: `This data won't be accessible in the component. Add it to $props() destructuring.`
450
+ });
451
+ }
452
+ }
453
+ // 3. Check for undefined handlers referenced in elements
454
+ for (const [elementName, handlerInfo] of componentInfo.handlers) {
455
+ if (!componentInfo.declaredFunctions.includes(handlerInfo.handlerName)) {
456
+ warnings.push({
457
+ type: 'undefined-handler',
458
+ message: `[${componentName}] Handler "${handlerInfo.handlerName}" referenced by <${handlerInfo.element} name="${elementName}"> is not defined`,
459
+ details: `Make sure to define "function ${handlerInfo.handlerName}(ctx) { ... }" in your script`
460
+ });
461
+ }
462
+ }
463
+ // 4. Check for undefined modal submit handlers
464
+ for (const [modalId, modalInfo] of componentInfo.modalHandlers) {
465
+ if (modalInfo.onsubmitHandler && !componentInfo.declaredFunctions.includes(modalInfo.onsubmitHandler)) {
466
+ warnings.push({
467
+ type: 'undefined-handler',
468
+ message: `[${componentName}] Modal submit handler "${modalInfo.onsubmitHandler}" for modal "${modalId}" is not defined`,
469
+ details: `Make sure to define "function ${modalInfo.onsubmitHandler}(ctx, fields) { ... }" in your script`
470
+ });
471
+ }
472
+ }
473
+ // 5. Check for modal handler signatures (ctx parameter is optional for regular handlers)
474
+ // Only modal handlers MUST have fields parameter to access submitted data
475
+ const handlerFunctionRegex = /(?:async\s+)?function\s+(\w+)\s*\(\s*(\w*)\s*(?:,\s*(\w+))?\s*\)/g;
476
+ let match;
477
+ const scriptContent = componentInfo.scriptContent;
478
+ while ((match = handlerFunctionRegex.exec(scriptContent)) !== null) {
479
+ const funcName = match[1];
480
+ const firstParam = match[2];
481
+ const secondParam = match[3];
482
+ // Only check modal handlers - they need 'fields' param to access form data
483
+ const isModalHandler = Array.from(componentInfo.modalHandlers.values()).some(m => m.onsubmitHandler === funcName);
484
+ if (isModalHandler && !secondParam) {
485
+ warnings.push({
486
+ type: 'syntax-error',
487
+ message: `[${componentName}] Modal handler "${funcName}" should have "ctx" and "fields" parameters`,
488
+ details: `Change to "function ${funcName}(ctx, fields) { ... }" to receive submitted form data`
489
+ });
490
+ }
491
+ }
492
+ // 6. Check for common template mistakes
493
+ const templateContent = componentInfo.processedSource;
494
+ // Check for onclick= without braces (common mistake)
495
+ const badOnClickRegex = /onclick\s*=\s*["']([^"']+)["']/gi;
496
+ while ((match = badOnClickRegex.exec(templateContent)) !== null) {
497
+ if (!match[1].startsWith('{')) {
498
+ warnings.push({
499
+ type: 'syntax-error',
500
+ message: `[${componentName}] onclick handler should use curly braces: onclick={${match[1]}}`,
501
+ details: `String values are not valid for event handlers. Use onclick={handlerName} syntax.`
502
+ });
503
+ }
504
+ }
505
+ // Check for undefined variables in {expression} blocks (basic check)
506
+ const expressionRegex = /\{([^}]+)\}/g;
507
+ const knownIdentifiers = new Set([
508
+ ...componentInfo.declaredProps,
509
+ ...componentInfo.declaredFunctions,
510
+ // Common Svelte/JS globals
511
+ 'true', 'false', 'null', 'undefined', 'console', 'Math', 'JSON', 'Array', 'Object',
512
+ 'Date', 'Number', 'String', 'Boolean', 'Promise', 'Map', 'Set',
513
+ // Common Svelte constructs
514
+ '#if', '/if', '#each', '/each', '#await', '/await', ':else', ':then', ':catch',
515
+ '@html', '@debug', '@const'
516
+ ]);
517
+ // Add variables from script (let, const, var declarations)
518
+ const varDeclRegex = /(?:let|const|var)\s+(\w+)/g;
519
+ while ((match = varDeclRegex.exec(scriptContent)) !== null) {
520
+ knownIdentifiers.add(match[1]);
521
+ }
522
+ return warnings;
523
+ }
524
+ /**
525
+ * Log validation warnings to console with colors
526
+ */
527
+ function logValidationWarnings(warnings) {
528
+ if (warnings.length === 0)
529
+ return;
530
+ console.warn(`\n⚠️ Svelte Component Validation Warnings (${warnings.length}):`);
531
+ for (const warning of warnings) {
532
+ const icon = warning.type === 'missing-data' ? '❌' :
533
+ warning.type === 'unused-data' ? '⚠️' :
534
+ warning.type === 'undefined-handler' ? '🔗' :
535
+ warning.type === 'syntax-error' ? '💥' : '⚡';
536
+ console.warn(` ${icon} ${warning.message}`);
537
+ if (warning.details) {
538
+ console.warn(` └─ ${warning.details}`);
539
+ }
540
+ }
541
+ console.warn('');
542
+ }
239
543
  function parseImports(script) {
240
544
  const imports = [];
241
545
  // Match: import { a, b as c } from "module"
@@ -332,7 +636,13 @@ function createHandlerContext(scriptContent, initialData = {}, component, ctx) {
332
636
  // 2. Remove $props destructuring
333
637
  // 3. Convert $effect to __registerEffect__
334
638
  // 4. Keep only function declarations
335
- let processedScript = cleanedScript
639
+ // First, remove comments from the script to avoid regex issues with braces in comments
640
+ let scriptWithoutComments = cleanedScript
641
+ // Remove single-line comments (but preserve the newline)
642
+ .replace(/\/\/.*$/gm, '')
643
+ // Remove multi-line comments
644
+ .replace(/\/\*[\s\S]*?\*\//g, '');
645
+ let processedScript = scriptWithoutComments
336
646
  // Remove $state declarations completely or make them var
337
647
  .replace(/let\s+(\w+)\s*=\s*\$state\(([^)]*)\);?/g, 'var $1 = $2;')
338
648
  // Remove $derived declarations but keep the value
@@ -340,33 +650,41 @@ function createHandlerContext(scriptContent, initialData = {}, component, ctx) {
340
650
  // Convert $effect calls to __registerEffect__ calls
341
651
  .replace(/\$effect\s*\(\s*((?:function\s*\([^)]*\)|\([^)]*\)\s*=>|\(\)\s*=>)[^}]*\{[\s\S]*?\}\s*)\);?/g, '__registerEffect__($1);')
342
652
  // Simpler $effect pattern: $effect(() => { ... })
343
- .replace(/\$effect\s*\(\s*\(\)\s*=>\s*\{([\s\S]*?)\}\s*\);?/g, '__registerEffect__(function() {$1});')
344
- // Replace $props destructuring with data access (handles default values)
345
- .replace(/let\s+\{\s*([^}]+)\s*\}\s*=\s*\$props\(\);?/g, (match, vars) => {
346
- return vars.split(',').map((v) => {
347
- v = v.trim();
348
- // Skip empty strings and comments
349
- if (!v || v.startsWith('//'))
350
- return '';
351
- // Remove inline comments from the variable definition
352
- v = v.replace(/\/\/.*$/, '').trim();
353
- if (!v)
354
- return '';
355
- // Skip 'data' prop as it's already defined in the wrapper
356
- if (v === 'data')
653
+ .replace(/\$effect\s*\(\s*\(\)\s*=>\s*\{([\s\S]*?)\}\s*\);?/g, '__registerEffect__(function() {$1});');
654
+ // Handle $props destructuring with proper brace counting (supports nested objects like { options = { a: 1 } })
655
+ processedScript = processedScript.replace(/let\s+\{([\s\S]*?)\}\s*=\s*\$props\(\);?/g, (match) => {
656
+ // Find the opening brace after 'let'
657
+ const letIndex = match.indexOf('{');
658
+ if (letIndex === -1)
659
+ return match;
660
+ // Use brace counting to find the matching closing brace
661
+ let braceCount = 0;
662
+ let startIndex = letIndex;
663
+ let endIndex = -1;
664
+ for (let i = startIndex; i < match.length; i++) {
665
+ if (match[i] === '{')
666
+ braceCount++;
667
+ else if (match[i] === '}') {
668
+ braceCount--;
669
+ if (braceCount === 0) {
670
+ endIndex = i;
671
+ break;
672
+ }
673
+ }
674
+ }
675
+ if (endIndex === -1)
676
+ return match;
677
+ // Extract the content between braces
678
+ const content = match.substring(startIndex + 1, endIndex);
679
+ // Parse props with proper handling of nested braces
680
+ const props = parsePropsContent(content);
681
+ return props.map(prop => {
682
+ if (!prop.name || prop.name === 'data')
357
683
  return '';
358
- // Check if there's a default value: varName = defaultValue
359
- const defaultMatch = v.match(/^(\w+)\s*=\s*(.+)$/);
360
- if (defaultMatch) {
361
- const [, varName, defaultValue] = defaultMatch;
362
- // Skip 'data' prop even with default value
363
- if (varName === 'data')
364
- return '';
365
- // Clean default value from trailing comments
366
- const cleanDefault = defaultValue.replace(/\/\/.*$/, '').trim();
367
- return `var ${varName} = data.${varName} ?? ${cleanDefault};`;
684
+ if (prop.defaultValue !== undefined) {
685
+ return `var ${prop.name} = data.${prop.name} ?? ${prop.defaultValue};`;
368
686
  }
369
- return `var ${v} = data.${v};`;
687
+ return `var ${prop.name} = data.${prop.name};`;
370
688
  }).filter(Boolean).join('\n');
371
689
  });
372
690
  // Add module variable declarations at the beginning of processed script
@@ -417,6 +735,102 @@ function createHandlerContext(scriptContent, initialData = {}, component, ctx) {
417
735
  __destroyCallbacks__.push(fn);
418
736
  }
419
737
 
738
+ // Modal store - stores rendered modal definitions
739
+ var __modals__ = new Map();
740
+
741
+ // Register a modal (called internally when modals are parsed)
742
+ function __registerModal__(modalId, modalDef) {
743
+ __modals__.set(modalId, modalDef);
744
+ }
745
+
746
+ // Show a modal by ID and return a Promise that resolves with the submitted fields
747
+ // Modal definitions are rendered from <components type="modal" id="xxx">
748
+ // Usage:
749
+ // await showModal("edit-product"); // Just show, use onsubmit handler
750
+ // const { fields, interaction } = await showModal("edit-product"); // Get response
751
+ // await showModal("edit-product", { timeout: 60000 }); // 60 second timeout
752
+ // await showModal("edit-product", { timeout: 0 }); // No timeout (unlimited)
753
+ function showModal(modalId, options) {
754
+ if (!__ctx__ || !__ctx__.interaction) {
755
+ return Promise.reject(new Error("showModal requires an interaction context"));
756
+ }
757
+
758
+ // Default timeout: 10 minutes (600000ms), 0 = no timeout
759
+ var timeout = (options && typeof options.timeout === 'number') ? options.timeout : 600000;
760
+
761
+ // Disable auto-render IMMEDIATELY since we're showing a modal
762
+ // This prevents flushRender from trying to update after modal is shown
763
+ __autoRenderEnabled__ = false;
764
+ __asyncInteractionCalled__ = true;
765
+
766
+ // Create a promise that will be resolved when modal is submitted
767
+ return new Promise(async function(resolve, reject) {
768
+ var timeoutId = null;
769
+ var modal = null;
770
+
771
+ try {
772
+ // Re-render to get latest modal definitions (reactive)
773
+ var renderResult = await __component__.toJSON({ data: __data__ });
774
+
775
+ // Get modals from the render result stored on component
776
+ var modals = __component__.__lastRenderModals__;
777
+ if (!modals) {
778
+ reject(new Error("No modals defined in this component. Use <components type=\\"modal\\" id=\\"...\\">"));
779
+ return;
780
+ }
781
+
782
+ modal = modals.get(modalId);
783
+ if (!modal) {
784
+ reject(new Error("Modal '" + modalId + "' not found. Available modals: " + Array.from(modals.keys()).join(", ")));
785
+ return;
786
+ }
787
+
788
+ // Wrapped resolve/reject to clear timeout
789
+ var wrappedResolve = function(result) {
790
+ if (timeoutId) clearTimeout(timeoutId);
791
+ resolve(result);
792
+ };
793
+ var wrappedReject = function(error) {
794
+ if (timeoutId) clearTimeout(timeoutId);
795
+ reject(error);
796
+ };
797
+
798
+ // Store the promise resolver in component's pending modals map
799
+ // This will be resolved when modal submit interaction is received
800
+ __component__._pendingModals.set(modal.customId, { resolve: wrappedResolve, reject: wrappedReject });
801
+
802
+ // Set up timeout if enabled (timeout > 0)
803
+ if (timeout > 0) {
804
+ timeoutId = setTimeout(function() {
805
+ // Clean up pending modal
806
+ __component__._pendingModals.delete(modal.customId);
807
+ reject(new Error("Modal '" + modalId + "' timed out after " + timeout + "ms"));
808
+ }, timeout);
809
+ }
810
+
811
+ // Show the modal using the interaction
812
+ var i = __ctx__.interaction;
813
+
814
+ // Modal must be shown immediately - no deferral allowed
815
+ await i.showModal({
816
+ title: modal.title,
817
+ customId: modal.customId,
818
+ components: modal.components
819
+ });
820
+
821
+ // Note: Promise resolves later when modal is submitted
822
+ // If you don't await for the response, the promise just hangs (which is fine)
823
+ } catch (err) {
824
+ // Clean up pending modal and timeout if show failed
825
+ if (timeoutId) clearTimeout(timeoutId);
826
+ if (modal && modal.customId) {
827
+ __component__._pendingModals.delete(modal.customId);
828
+ }
829
+ reject(new Error("Failed to show modal: " + (err.message || err)));
830
+ }
831
+ });
832
+ }
833
+
420
834
  // Rate-limit aware edit function with retry
421
835
  function __safeEdit__(editFn, retryCount) {
422
836
  retryCount = retryCount || 0;
@@ -539,6 +953,9 @@ function createHandlerContext(scriptContent, initialData = {}, component, ctx) {
539
953
  // Track if we're inside a handler execution
540
954
  var __inHandlerExecution__ = false;
541
955
 
956
+ // Track pending low-priority updates (from intervals/timeouts)
957
+ var __pendingLowPriorityRender__ = false;
958
+
542
959
  // Mark that we have pending data changes
543
960
  function __markDataChanged__() {
544
961
  __hasDataChanges__ = true;
@@ -551,6 +968,20 @@ function createHandlerContext(scriptContent, initialData = {}, component, ctx) {
551
968
  }
552
969
  }
553
970
 
971
+ // Low-priority update - used for background tasks like intervals
972
+ // If a handler is currently running, skip this update (the handler's update will include our changes)
973
+ function lowPriorityUpdate(callback) {
974
+ if (__inHandlerExecution__) {
975
+ // Handler is running - just update data, skip render
976
+ // The handler will render when it finishes
977
+ if (callback) callback();
978
+ return;
979
+ }
980
+
981
+ // No handler running - proceed with update
982
+ if (callback) callback();
983
+ }
984
+
554
985
  // Flush pending render (called after interaction methods complete)
555
986
  function __flushRender__() {
556
987
  if (__hasDataChanges__ && __autoRenderEnabled__) {
@@ -820,7 +1251,13 @@ function createHandlerContext(scriptContent, initialData = {}, component, ctx) {
820
1251
  const factoryFunc = new Function('console', wrappedScript);
821
1252
  const createHandlers = factoryFunc(console);
822
1253
  // Execute with the actual data, component, ctx, and imported modules to get handlers with proper closure
823
- const result = createHandlers(initialData, component, ctx, modules);
1254
+ let result;
1255
+ try {
1256
+ result = createHandlers(initialData, component, ctx, modules);
1257
+ }
1258
+ catch (execError) {
1259
+ throw execError;
1260
+ }
824
1261
  Object.assign(handlers, result.handlers || {});
825
1262
  effects.push(...(result.effects || []));
826
1263
  // Return full result including render helpers
@@ -850,7 +1287,8 @@ function createHandlerContext(scriptContent, initialData = {}, component, ctx) {
850
1287
  };
851
1288
  }
852
1289
  catch (error) {
853
- // Silently fail and return fallback
1290
+ // Log the error for debugging
1291
+ console.error("[DBI-Svelte] createHandlerContext failed:", error);
854
1292
  }
855
1293
  // Function to run all effects (fallback)
856
1294
  const runEffects = () => {