bitwrench 2.0.16 → 2.0.17

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 (42) hide show
  1. package/dist/bitwrench-bccl.cjs.js +6 -2
  2. package/dist/bitwrench-bccl.cjs.min.js +3 -3
  3. package/dist/bitwrench-bccl.esm.js +6 -2
  4. package/dist/bitwrench-bccl.esm.min.js +3 -3
  5. package/dist/bitwrench-bccl.umd.js +6 -2
  6. package/dist/bitwrench-bccl.umd.min.js +2 -2
  7. package/dist/bitwrench-code-edit.cjs.js +1 -1
  8. package/dist/bitwrench-code-edit.cjs.min.js +1 -1
  9. package/dist/bitwrench-code-edit.es5.js +1 -1
  10. package/dist/bitwrench-code-edit.es5.min.js +1 -1
  11. package/dist/bitwrench-code-edit.esm.js +1 -1
  12. package/dist/bitwrench-code-edit.esm.min.js +1 -1
  13. package/dist/bitwrench-code-edit.umd.js +1 -1
  14. package/dist/bitwrench-code-edit.umd.min.js +1 -1
  15. package/dist/bitwrench-lean.cjs.js +506 -154
  16. package/dist/bitwrench-lean.cjs.min.js +7 -7
  17. package/dist/bitwrench-lean.es5.js +517 -155
  18. package/dist/bitwrench-lean.es5.min.js +5 -5
  19. package/dist/bitwrench-lean.esm.js +505 -154
  20. package/dist/bitwrench-lean.esm.min.js +6 -6
  21. package/dist/bitwrench-lean.umd.js +506 -154
  22. package/dist/bitwrench-lean.umd.min.js +7 -7
  23. package/dist/bitwrench.cjs.js +511 -155
  24. package/dist/bitwrench.cjs.min.js +8 -8
  25. package/dist/bitwrench.es5.js +525 -156
  26. package/dist/bitwrench.es5.min.js +6 -6
  27. package/dist/bitwrench.esm.js +510 -155
  28. package/dist/bitwrench.esm.min.js +8 -8
  29. package/dist/bitwrench.umd.js +511 -155
  30. package/dist/bitwrench.umd.min.js +8 -8
  31. package/dist/builds.json +82 -82
  32. package/dist/bwserve.cjs.js +16 -2
  33. package/dist/bwserve.esm.js +16 -2
  34. package/dist/sri.json +34 -34
  35. package/package.json +4 -2
  36. package/readme.html +1 -1
  37. package/src/bitwrench-bccl.js +5 -1
  38. package/src/bitwrench.js +502 -151
  39. package/src/bwserve/index.js +12 -1
  40. package/src/bwserve/shell.js +3 -0
  41. package/src/cli/layout-default.js +47 -32
  42. package/src/version.js +3 -3
@@ -1,24 +1,25 @@
1
- /*! bitwrench v2.0.16 | BSD-2-Clause | https://deftio.github.com/bitwrench/pages */
1
+ /*! bitwrench v2.0.17 | BSD-2-Clause | https://deftio.github.com/bitwrench/pages */
2
2
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
4
4
  typeof define === 'function' && define.amd ? define(factory) :
5
5
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.bw = factory());
6
6
  })(this, (function () { 'use strict';
7
7
 
8
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
8
9
  /**
9
10
  * Auto-generated version file from package.json
10
11
  * DO NOT EDIT DIRECTLY - Use npm run generate-version
11
12
  */
12
13
 
13
14
  const VERSION_INFO = {
14
- version: '2.0.16',
15
+ version: '2.0.17',
15
16
  name: 'bitwrench',
16
17
  description: 'A library for javascript UI functions.',
17
18
  license: 'BSD-2-Clause',
18
19
  homepage: 'https://deftio.github.com/bitwrench/pages',
19
20
  repository: 'git+https://github.com/deftio/bitwrench.git',
20
21
  author: 'manu a. chatterjee <deftio@deftio.com> (https://deftio.com/)',
21
- buildDate: '2026-03-12T08:05:52.043Z'
22
+ buildDate: '2026-03-13T23:15:10.823Z'
22
23
  };
23
24
 
24
25
  /**
@@ -6880,7 +6881,11 @@
6880
6881
  function make(type, props) {
6881
6882
  var def = BCCL[type];
6882
6883
  if (!def) throw new Error('bw.make: unknown component type "' + type + '". Available: ' + Object.keys(BCCL).join(', '));
6883
- return def.make(props || {});
6884
+ var taco = def.make(props || {});
6885
+ if (taco && typeof taco === 'object') {
6886
+ taco._bwFactory = { type: type, props: props || {} };
6887
+ }
6888
+ return taco;
6884
6889
  }
6885
6890
 
6886
6891
  var components = /*#__PURE__*/Object.freeze({
@@ -7001,7 +7006,7 @@
7001
7006
  __monkey_patch_is_nodejs__: {
7002
7007
  _value: 'ignore',
7003
7008
  set: function(x) {
7004
- this._value = (typeof x === 'boolean') ? x : 'ignore';
7009
+ this._value = _is(x, 'boolean') ? x : 'ignore';
7005
7010
  },
7006
7011
  get: function() {
7007
7012
  return this._value;
@@ -7049,6 +7054,67 @@
7049
7054
  configurable: true
7050
7055
  });
7051
7056
 
7057
+ // ── Internal aliases ─────────────────────────────────────────────────────
7058
+ // Short names for frequently-used builtins and internal methods.
7059
+ // Same pattern as v1 (_to = bw.typeOf, etc.).
7060
+ //
7061
+ // Why: Terser can't shorten global property chains (console.warn,
7062
+ // Object.prototype.hasOwnProperty, Array.isArray, document.createElement)
7063
+ // because it can't prove they're side-effect-free. We can, so we alias
7064
+ // them here. Each alias saves bytes in the minified output, and the short
7065
+ // names also reduce visual noise in the hot paths (binding pipeline,
7066
+ // createDOM, etc.).
7067
+ //
7068
+ // Alias Target Sites
7069
+ // ───────── ────────────────────────────────────── ─────
7070
+ // _hop Object.prototype.hasOwnProperty 15
7071
+ // _isA Array.isArray 25
7072
+ // _keys Object.keys 7
7073
+ // _to bw.typeOf (type string) 26
7074
+ // _is type check boolean: _is(x,'string') ~50
7075
+ // _cw console.warn 8
7076
+ // _cl console.log 11
7077
+ // _ce console.error 4
7078
+ // _chp ComponentHandle.prototype 28 (defined after constructor)
7079
+ //
7080
+ // Note: document.createElement etc. are NOT aliased because they require
7081
+ // `this === document` and .bind() would add overhead on every call.
7082
+ // Console aliases use thin wrappers (not direct refs) so test monkey-
7083
+ // patching of console.warn/log/error continues to work.
7084
+ //
7085
+ // `typeof x` for UNDECLARED globals (window, document, process, require,
7086
+ // EventSource, navigator, Promise, __filename, import.meta) MUST stay as
7087
+ // raw `typeof` — calling _to(x) when x doesn't exist throws ReferenceError.
7088
+ //
7089
+ // ── v1 functional type helpers (kept for reference, not currently used) ──
7090
+ // _toa(x, type, trueVal, falseVal) — bw.typeAssign:
7091
+ // returns trueVal if _to(x)===type, else falseVal.
7092
+ // Replaces: (typeof x === 'string') ? A : B → _toa(x,'string',A,B)
7093
+ // _toc(x, type, trueVal, falseVal) — bw.typeConvert:
7094
+ // same as _toa but if trueVal/falseVal are functions, calls them with x.
7095
+ // Replaces: typeof x === 'string' ? fn(x) : default → _toc(x,'string',fn,default)
7096
+ // Uncomment if pattern frequency justifies them:
7097
+ // var _toa = function(x, t, y, n) { return _to(x) === t ? y : n; };
7098
+ // var _toc = function(x, t, y, n) { var r = _to(x)===t; return r ? (_to(y)==='function'?y(x):y) : (_to(n)==='function'?n(x):n); };
7099
+ // ─────────────────────────────────────────────────────────────────────────
7100
+ var _hop = Object.prototype.hasOwnProperty;
7101
+ var _isA = Array.isArray;
7102
+ var _keys = Object.keys;
7103
+ var _to = typeOf; // imported from bitwrench-utils.js
7104
+ var _is = function(x, t) { var r = _to(x); return r === t || r.toLowerCase() === t; };
7105
+ // Console aliases use thin wrappers (not direct references) so that test
7106
+ // code can monkey-patch console.warn/log/error and the patches take effect.
7107
+ var _cw = function() { console.warn.apply(console, arguments); };
7108
+ var _cl = function() { console.log.apply(console, arguments); };
7109
+ var _ce = function() { console.error.apply(console, arguments); };
7110
+
7111
+ /**
7112
+ * Debug flag. When true, emits console.warn for silent binding failures
7113
+ * (missing paths, null refs, auto-created intermediate objects).
7114
+ * @type {boolean}
7115
+ */
7116
+ bw.debug = false;
7117
+
7052
7118
  /**
7053
7119
  * Lazy-resolve Node.js `fs` module.
7054
7120
  * Tries require('fs') first (available in CJS/UMD Node.js builds),
@@ -7196,7 +7262,7 @@
7196
7262
  */
7197
7263
  bw._el = function(id) {
7198
7264
  // Pass-through for DOM elements
7199
- if (typeof id !== 'string') return id || null;
7265
+ if (!_is(id, 'string')) return id || null;
7200
7266
  if (!id) return null;
7201
7267
  if (!bw._isBrowser) return null;
7202
7268
 
@@ -7292,7 +7358,7 @@
7292
7358
  * // => '&lt;b&gt;Hello&lt;&#x2F;b&gt; &amp; &quot;world&quot;'
7293
7359
  */
7294
7360
  bw.escapeHTML = function(str) {
7295
- if (typeof str !== 'string') return '';
7361
+ if (!_is(str, 'string')) return '';
7296
7362
 
7297
7363
  const escapeMap = {
7298
7364
  '&': '&amp;',
@@ -7365,7 +7431,7 @@
7365
7431
  }
7366
7432
 
7367
7433
  // Handle arrays of TACOs
7368
- if (Array.isArray(taco)) {
7434
+ if (_isA(taco)) {
7369
7435
  return taco.map(t => bw.html(t, options)).join('');
7370
7436
  }
7371
7437
 
@@ -7388,15 +7454,15 @@
7388
7454
  if (taco && taco._bwEach && options.state) {
7389
7455
  var eachExpr = taco.expr.replace(/^\$\{|\}$/g, '');
7390
7456
  var arr = bw._evaluatePath(options.state, eachExpr);
7391
- if (!Array.isArray(arr)) return '';
7457
+ if (!_isA(arr)) return '';
7392
7458
  return arr.map(function(item, idx) { return bw.html(taco.factory(item, idx), options); }).join('');
7393
7459
  }
7394
7460
 
7395
7461
  // Handle primitives and non-TACO objects
7396
- if (typeof taco !== 'object' || !taco.t) {
7462
+ if (!_is(taco, 'object') || !taco.t) {
7397
7463
  var str = options.raw ? String(taco) : bw.escapeHTML(String(taco));
7398
7464
  // Resolve template bindings if state provided
7399
- if (options.state && typeof str === 'string' && str.indexOf('${') >= 0) {
7465
+ if (options.state && _is(str, 'string') && str.indexOf('${') >= 0) {
7400
7466
  str = bw._resolveTemplate(str, options.state, !!options.compile);
7401
7467
  }
7402
7468
  return str;
@@ -7416,10 +7482,18 @@
7416
7482
  // Skip null, undefined, false
7417
7483
  if (value == null || value === false) continue;
7418
7484
 
7419
- // Skip event handlers (they're for DOM only)
7420
- if (key.startsWith('on')) continue;
7485
+ // Serialize event handlers via funcRegister
7486
+ if (key.startsWith('on')) {
7487
+ if (_is(value, 'function')) {
7488
+ var fnId = bw.funcRegister(value);
7489
+ attrStr += ' ' + key + '="' + bw.funcGetDispatchStr(fnId, 'event') + '"';
7490
+ } else if (_is(value, 'string')) {
7491
+ attrStr += ' ' + key + '="' + bw.escapeHTML(value) + '"';
7492
+ }
7493
+ continue;
7494
+ }
7421
7495
 
7422
- if (key === 'style' && typeof value === 'object') {
7496
+ if (key === 'style' && _is(value, 'object')) {
7423
7497
  // Convert style object to string
7424
7498
  const styleStr = Object.entries(value)
7425
7499
  .filter(([, v]) => v != null)
@@ -7430,7 +7504,7 @@
7430
7504
  }
7431
7505
  } else if (key === 'class') {
7432
7506
  // Handle class as array or string
7433
- const classStr = Array.isArray(value) ? value.filter(Boolean).join(' ') : String(value);
7507
+ const classStr = _isA(value) ? value.filter(Boolean).join(' ') : String(value);
7434
7508
  if (classStr) {
7435
7509
  attrStr += ` class="${bw.escapeHTML(classStr)}"`;
7436
7510
  }
@@ -7466,13 +7540,184 @@
7466
7540
  // Process content recursively
7467
7541
  let contentStr = content != null ? bw.html(content, options) : '';
7468
7542
  // Resolve template bindings in content if state provided
7469
- if (options.state && typeof contentStr === 'string' && contentStr.indexOf('${') >= 0) {
7543
+ if (options.state && _is(contentStr, 'string') && contentStr.indexOf('${') >= 0) {
7470
7544
  contentStr = bw._resolveTemplate(contentStr, options.state, !!options.compile);
7471
7545
  }
7472
7546
 
7473
7547
  return `<${tag}${attrStr}>${contentStr}</${tag}>`;
7474
7548
  };
7475
7549
 
7550
+ /**
7551
+ * Generate a complete, self-contained HTML document from TACO content.
7552
+ *
7553
+ * Produces a full `<!DOCTYPE html>` page with configurable runtime injection,
7554
+ * func registry emission (so serialized event handlers work), optional theme,
7555
+ * and extra head elements. Designed for static site generation, offline/airgapped
7556
+ * use, and the "static site that isn't static" workflow.
7557
+ *
7558
+ * @param {Object} [opts={}] - Page options
7559
+ * @param {Object|string|Array} [opts.body=''] - Body content: TACO, string, or array
7560
+ * @param {string} [opts.title='bitwrench'] - Page title
7561
+ * @param {Object} [opts.state] - State for ${expr} resolution in bw.html()
7562
+ * @param {string} [opts.runtime='shim'] - Runtime level: 'inline'|'cdn'|'shim'|'none'
7563
+ * @param {string} [opts.css=''] - Additional CSS for <style> block
7564
+ * @param {string|Object} [opts.theme=null] - Theme preset name or config object
7565
+ * @param {Array} [opts.head=[]] - Extra TACO elements rendered into <head>
7566
+ * @param {string} [opts.favicon=''] - Favicon URL
7567
+ * @param {string} [opts.lang='en'] - HTML lang attribute
7568
+ * @returns {string} Complete HTML document string
7569
+ * @category DOM Generation
7570
+ * @see bw.html
7571
+ * @example
7572
+ * bw.htmlPage({
7573
+ * title: 'My App',
7574
+ * body: { t: 'h1', c: 'Hello World' },
7575
+ * runtime: 'shim'
7576
+ * })
7577
+ */
7578
+ bw.htmlPage = function(opts) {
7579
+ opts = opts || {};
7580
+ var title = opts.title || 'bitwrench';
7581
+ var body = opts.body || '';
7582
+ var state = opts.state || undefined;
7583
+ var runtime = opts.runtime || 'shim';
7584
+ var css = opts.css || '';
7585
+ var theme = opts.theme || null;
7586
+ var headExtra = opts.head || [];
7587
+ var favicon = opts.favicon || '';
7588
+ var lang = opts.lang || 'en';
7589
+
7590
+ // Snapshot funcRegistry counter before rendering
7591
+ var fnCounterBefore = bw._fnIDCounter;
7592
+
7593
+ // Render body content
7594
+ var bodyHTML = '';
7595
+ if (_is(body, 'string')) {
7596
+ bodyHTML = body;
7597
+ } else {
7598
+ var htmlOpts = {};
7599
+ if (state) htmlOpts.state = state;
7600
+ bodyHTML = bw.html(body, htmlOpts);
7601
+ }
7602
+
7603
+ // Collect functions registered during this render
7604
+ var fnCounterAfter = bw._fnIDCounter;
7605
+ var registryEntries = '';
7606
+ for (var i = fnCounterBefore; i < fnCounterAfter; i++) {
7607
+ var fnKey = 'bw_fn_' + i;
7608
+ if (bw._fnRegistry[fnKey]) {
7609
+ registryEntries += 'bw._fnRegistry[\'' + fnKey + '\']=' +
7610
+ bw._fnRegistry[fnKey].toString() + ';\n';
7611
+ }
7612
+ }
7613
+
7614
+ // Build runtime script for <head>
7615
+ var runtimeHead = '';
7616
+ if (runtime === 'inline') {
7617
+ // Read UMD bundle synchronously if in Node.js
7618
+ var umdSource = null;
7619
+ if (bw._isNode) {
7620
+ try {
7621
+ var fs = (typeof require === 'function') ? require('fs') : null;
7622
+ var pathMod = (typeof require === 'function') ? require('path') : null;
7623
+ if (fs && pathMod) {
7624
+ // Resolve dist/ relative to this source file
7625
+ var srcDir = '';
7626
+ try { srcDir = pathMod.dirname((typeof __filename !== 'undefined') ? __filename : ''); }
7627
+ catch(e2) { /* ESM: __filename not available */ }
7628
+ if (!srcDir && typeof ({ url: (typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('bitwrench.umd.js', document.baseURI).href)) }) !== 'undefined' && (typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('bitwrench.umd.js', document.baseURI).href))) {
7629
+ var url = (typeof require === 'function') ? require('url') : null;
7630
+ if (url && url.fileURLToPath) srcDir = pathMod.dirname(url.fileURLToPath((typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('bitwrench.umd.js', document.baseURI).href))));
7631
+ }
7632
+ if (srcDir) {
7633
+ var distPath = pathMod.resolve(srcDir, '../dist/bitwrench.umd.min.js');
7634
+ umdSource = fs.readFileSync(distPath, 'utf8');
7635
+ }
7636
+ }
7637
+ } catch(e) { /* fall through */ }
7638
+ }
7639
+ if (umdSource) {
7640
+ runtimeHead = '<script>' + umdSource + '</script>';
7641
+ } else {
7642
+ // Fallback to shim in browser or if dist not available
7643
+ runtimeHead = '<script>' + bw._FUNC_REGISTRY_SHIM + '</script>';
7644
+ }
7645
+ } else if (runtime === 'cdn') {
7646
+ runtimeHead = '<script src="https://cdn.jsdelivr.net/npm/bitwrench@2/dist/bitwrench.umd.min.js"></script>';
7647
+ } else if (runtime === 'shim') {
7648
+ runtimeHead = '<script>' + bw._FUNC_REGISTRY_SHIM + '</script>';
7649
+ }
7650
+ // runtime === 'none' → empty
7651
+
7652
+ // Theme CSS
7653
+ var themeCSS = '';
7654
+ if (theme) {
7655
+ var themeConfig = _is(theme, 'string')
7656
+ ? (THEME_PRESETS[theme.toLowerCase()] || null)
7657
+ : theme;
7658
+ if (themeConfig) {
7659
+ var themeResult = bw.generateTheme('', Object.assign({}, themeConfig, { inject: false }));
7660
+ themeCSS = themeResult.css;
7661
+ }
7662
+ }
7663
+
7664
+ // Extra <head> elements
7665
+ var headHTML = '';
7666
+ if (_isA(headExtra) && headExtra.length > 0) {
7667
+ headHTML = headExtra.map(function(el) { return bw.html(el); }).join('\n');
7668
+ }
7669
+
7670
+ // Favicon
7671
+ var faviconTag = '';
7672
+ if (favicon) {
7673
+ var safeFavicon = favicon.replace(/[&<>"']/g, function(c) {
7674
+ return ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c];
7675
+ });
7676
+ faviconTag = '<link rel="icon" href="' + safeFavicon + '">';
7677
+ }
7678
+
7679
+ // Escaped title
7680
+ var safeTitle = bw.escapeHTML(title);
7681
+
7682
+ // Combine all CSS
7683
+ var allCSS = (themeCSS ? themeCSS + '\n' : '') + css;
7684
+
7685
+ // Body-end script: registry entries + optional loadDefaultStyles
7686
+ var bodyEndScript = '';
7687
+ var bodyEndParts = [];
7688
+ if (registryEntries) {
7689
+ bodyEndParts.push(registryEntries);
7690
+ }
7691
+ if (runtime === 'inline' || runtime === 'cdn') {
7692
+ bodyEndParts.push('if(typeof bw!=="undefined"){bw.loadDefaultStyles();}');
7693
+ }
7694
+ if (bodyEndParts.length > 0) {
7695
+ bodyEndScript = '<script>\n' + bodyEndParts.join('\n') + '\n</script>';
7696
+ }
7697
+
7698
+ // Assemble document
7699
+ var parts = [
7700
+ '<!DOCTYPE html>',
7701
+ '<html lang="' + lang + '">',
7702
+ '<head>',
7703
+ '<meta charset="UTF-8">',
7704
+ '<meta name="viewport" content="width=device-width, initial-scale=1">'
7705
+ ];
7706
+ parts.push('<title>' + safeTitle + '</title>');
7707
+ if (faviconTag) parts.push(faviconTag);
7708
+ if (runtimeHead) parts.push(runtimeHead);
7709
+ if (headHTML) parts.push(headHTML);
7710
+ if (allCSS) parts.push('<style>' + allCSS + '</style>');
7711
+ parts.push('</head>');
7712
+ parts.push('<body>');
7713
+ parts.push(bodyHTML);
7714
+ if (bodyEndScript) parts.push(bodyEndScript);
7715
+ parts.push('</body>');
7716
+ parts.push('</html>');
7717
+
7718
+ return parts.join('\n');
7719
+ };
7720
+
7476
7721
  /**
7477
7722
  * Create a live DOM element from a TACO object (browser only).
7478
7723
  *
@@ -7517,7 +7762,7 @@
7517
7762
  }
7518
7763
 
7519
7764
  // Handle text nodes
7520
- if (typeof taco !== 'object' || !taco.t) {
7765
+ if (!_is(taco, 'object') || !taco.t) {
7521
7766
  return document.createTextNode(String(taco));
7522
7767
  }
7523
7768
 
@@ -7530,16 +7775,16 @@
7530
7775
  for (const [key, value] of Object.entries(attrs)) {
7531
7776
  if (value == null || value === false) continue;
7532
7777
 
7533
- if (key === 'style' && typeof value === 'object') {
7778
+ if (key === 'style' && _is(value, 'object')) {
7534
7779
  // Apply styles directly
7535
7780
  Object.assign(el.style, value);
7536
7781
  } else if (key === 'class') {
7537
7782
  // Handle class as array or string
7538
- const classStr = Array.isArray(value) ? value.filter(Boolean).join(' ') : String(value);
7783
+ const classStr = _isA(value) ? value.filter(Boolean).join(' ') : String(value);
7539
7784
  if (classStr) {
7540
7785
  el.className = classStr;
7541
7786
  }
7542
- } else if (key.startsWith('on') && typeof value === 'function') {
7787
+ } else if (key.startsWith('on') && _is(value, 'function')) {
7543
7788
  // Event handlers
7544
7789
  const eventName = key.slice(2).toLowerCase();
7545
7790
  el.addEventListener(eventName, value);
@@ -7559,7 +7804,7 @@
7559
7804
  // Children with data-bw_id or id attributes get local refs on the parent,
7560
7805
  // so o.render functions can access them without any DOM lookup.
7561
7806
  if (content != null) {
7562
- if (Array.isArray(content)) {
7807
+ if (_isA(content)) {
7563
7808
  content.forEach(child => {
7564
7809
  if (child != null) {
7565
7810
  // Handle ComponentHandle in content arrays (Level 2 children)
@@ -7579,20 +7824,20 @@
7579
7824
  if (childEl._bw_refs) {
7580
7825
  if (!el._bw_refs) el._bw_refs = {};
7581
7826
  for (var rk in childEl._bw_refs) {
7582
- if (Object.prototype.hasOwnProperty.call(childEl._bw_refs, rk)) {
7827
+ if (_hop.call(childEl._bw_refs, rk)) {
7583
7828
  el._bw_refs[rk] = childEl._bw_refs[rk];
7584
7829
  }
7585
7830
  }
7586
7831
  }
7587
7832
  }
7588
7833
  });
7589
- } else if (typeof content === 'object' && content.__bw_raw) {
7834
+ } else if (_is(content, 'object') && content.__bw_raw) {
7590
7835
  // Raw HTML content — inject via innerHTML
7591
7836
  el.innerHTML = content.v;
7592
7837
  } else if (content._bwComponent === true) {
7593
7838
  // Single ComponentHandle as content
7594
7839
  content.mount(el);
7595
- } else if (typeof content === 'object' && content.t) {
7840
+ } else if (_is(content, 'object') && content.t) {
7596
7841
  var childEl = bw.createDOM(content, options);
7597
7842
  el.appendChild(childEl);
7598
7843
  var childBwId = content.a ? (content.a['data-bw_id'] || content.a.id) : null;
@@ -7603,7 +7848,7 @@
7603
7848
  if (childEl._bw_refs) {
7604
7849
  if (!el._bw_refs) el._bw_refs = {};
7605
7850
  for (var rk in childEl._bw_refs) {
7606
- if (Object.prototype.hasOwnProperty.call(childEl._bw_refs, rk)) {
7851
+ if (_hop.call(childEl._bw_refs, rk)) {
7607
7852
  el._bw_refs[rk] = childEl._bw_refs[rk];
7608
7853
  }
7609
7854
  }
@@ -7636,7 +7881,7 @@
7636
7881
  el._bw_render = opts.render;
7637
7882
 
7638
7883
  if (opts.mounted) {
7639
- console.warn('bw.createDOM: o.render and o.mounted are mutually exclusive. o.render wins.');
7884
+ _cw('bw.createDOM: o.render and o.mounted are mutually exclusive. o.render wins.');
7640
7885
  }
7641
7886
 
7642
7887
  // Queue initial render (same timing as mounted)
@@ -7709,7 +7954,7 @@
7709
7954
  const targetEl = bw._el(target);
7710
7955
 
7711
7956
  if (!targetEl) {
7712
- console.error('bw.DOM: Target element not found:', target);
7957
+ _ce('bw.DOM: Target element not found:', target);
7713
7958
  return null;
7714
7959
  }
7715
7960
 
@@ -7749,7 +7994,7 @@
7749
7994
  targetEl.appendChild(taco.element);
7750
7995
  }
7751
7996
  // Handle arrays
7752
- else if (Array.isArray(taco)) {
7997
+ else if (_isA(taco)) {
7753
7998
  taco.forEach(t => {
7754
7999
  if (t != null) {
7755
8000
  if (t._bwComponent === true) {
@@ -7785,7 +8030,7 @@
7785
8030
  bw.compileProps = function(handle, props = {}) {
7786
8031
  const compiledProps = {};
7787
8032
 
7788
- Object.keys(props).forEach(key => {
8033
+ _keys(props).forEach(key => {
7789
8034
  // Create getter/setter for each prop
7790
8035
  Object.defineProperty(compiledProps, key, {
7791
8036
  get() {
@@ -8103,17 +8348,17 @@
8103
8348
  if (attr) {
8104
8349
  // Patch an attribute
8105
8350
  el.setAttribute(attr, String(content));
8106
- } else if (Array.isArray(content)) {
8351
+ } else if (_isA(content)) {
8107
8352
  // Patch with array of children (strings and/or TACOs)
8108
8353
  el.innerHTML = '';
8109
8354
  content.forEach(function(item) {
8110
- if (typeof item === 'string' || typeof item === 'number') {
8355
+ if (_is(item, 'string') || _is(item, 'number')) {
8111
8356
  el.appendChild(document.createTextNode(String(item)));
8112
8357
  } else if (item && item.t) {
8113
8358
  el.appendChild(bw.createDOM(item));
8114
8359
  }
8115
8360
  });
8116
- } else if (typeof content === 'object' && content !== null && content.t) {
8361
+ } else if (_is(content, 'object') && content.t) {
8117
8362
  // Patch with a TACO — replace children
8118
8363
  el.innerHTML = '';
8119
8364
  el.appendChild(bw.createDOM(content));
@@ -8144,7 +8389,7 @@
8144
8389
  bw.patchAll = function(patches) {
8145
8390
  var results = {};
8146
8391
  for (var id in patches) {
8147
- if (Object.prototype.hasOwnProperty.call(patches, id)) {
8392
+ if (_hop.call(patches, id)) {
8148
8393
  results[id] = bw.patch(id, patches[id]);
8149
8394
  }
8150
8395
  }
@@ -8241,7 +8486,7 @@
8241
8486
  snapshot[i].handler(detail);
8242
8487
  called++;
8243
8488
  } catch (err) {
8244
- console.warn('bw.pub: subscriber error on topic "' + topic + '":', err);
8489
+ _cw('bw.pub: subscriber error on topic "' + topic + '":', err);
8245
8490
  }
8246
8491
  }
8247
8492
  return called;
@@ -8337,8 +8582,8 @@
8337
8582
  * @see bw.funcGetDispatchStr
8338
8583
  */
8339
8584
  bw.funcRegister = function(fn, name) {
8340
- if (typeof fn !== 'function') return '';
8341
- var fnID = (typeof name === 'string' && name.length > 0) ? name : ('bw_fn_' + bw._fnIDCounter++);
8585
+ if (!_is(fn, 'function')) return '';
8586
+ var fnID = (_is(name, 'string') && name.length > 0) ? name : ('bw_fn_' + bw._fnIDCounter++);
8342
8587
  bw._fnRegistry[fnID] = fn;
8343
8588
  return fnID;
8344
8589
  };
@@ -8357,7 +8602,7 @@
8357
8602
  bw.funcGetById = function(name, errFn) {
8358
8603
  name = String(name);
8359
8604
  if (name in bw._fnRegistry) return bw._fnRegistry[name];
8360
- return (typeof errFn === 'function') ? errFn : function() { console.warn('bw.funcGetById: unregistered fn "' + name + '"'); };
8605
+ return _is(errFn, 'function') ? errFn : function() { _cw('bw.funcGetById: unregistered fn "' + name + '"'); };
8361
8606
  };
8362
8607
 
8363
8608
  /**
@@ -8398,13 +8643,30 @@
8398
8643
  bw.funcGetRegistry = function() {
8399
8644
  var copy = {};
8400
8645
  for (var k in bw._fnRegistry) {
8401
- if (Object.prototype.hasOwnProperty.call(bw._fnRegistry, k)) {
8646
+ if (_hop.call(bw._fnRegistry, k)) {
8402
8647
  copy[k] = bw._fnRegistry[k];
8403
8648
  }
8404
8649
  }
8405
8650
  return copy;
8406
8651
  };
8407
8652
 
8653
+ /**
8654
+ * Minimal runtime shim for funcRegister dispatch in static HTML.
8655
+ * When embedded in a `<script>` tag, provides just enough infrastructure
8656
+ * for `bw.funcGetById()` calls to resolve. The actual function bodies
8657
+ * are emitted separately as `bw._fnRegistry['bw_fn_X'] = ...;` assignments.
8658
+ * @type {string}
8659
+ * @category Function Registry
8660
+ */
8661
+ bw._FUNC_REGISTRY_SHIM = '(function(){var bw=window.bw||(window.bw={});' +
8662
+ 'if(!bw._fnRegistry)bw._fnRegistry={};' +
8663
+ 'bw.funcGetById=function(n){return bw._fnRegistry[n]||function(){' +
8664
+ 'console.warn("bw: unregistered fn "+n)};};' +
8665
+ 'bw.funcRegister=function(fn,name){' +
8666
+ 'var id=name||("bw_fn_"+(bw._fnIDCounter=(bw._fnIDCounter||0)+1));' +
8667
+ 'bw._fnRegistry[id]=fn;return id;};' +
8668
+ 'window.bw=bw;})();';
8669
+
8408
8670
  // ===================================================================================
8409
8671
  // Template Binding Utilities
8410
8672
  // ===================================================================================
@@ -8432,7 +8694,10 @@
8432
8694
  var parts = path.split('.');
8433
8695
  var val = state;
8434
8696
  for (var i = 0; i < parts.length; i++) {
8435
- if (val == null) return '';
8697
+ if (val == null) {
8698
+ if (bw.debug) _cw('bw.debug: _evaluatePath — null at key "' + parts[i] + '" in path "' + path + '"');
8699
+ return '';
8700
+ }
8436
8701
  val = val[parts[i]];
8437
8702
  }
8438
8703
  return (val == null) ? '' : val;
@@ -8452,7 +8717,7 @@
8452
8717
  */
8453
8718
  bw._compiledExprs = {};
8454
8719
  bw._resolveTemplate = function(str, state, compile) {
8455
- if (typeof str !== 'string' || str.indexOf('${') < 0) return str;
8720
+ if (!_is(str, 'string') || str.indexOf('${') < 0) return str;
8456
8721
  var bindings = bw._parseBindings(str);
8457
8722
  if (bindings.length === 0) return str;
8458
8723
 
@@ -8474,6 +8739,7 @@
8474
8739
  try {
8475
8740
  val = bw._compiledExprs[b.expr](state);
8476
8741
  } catch (e) {
8742
+ if (bw.debug) _cw('bw.debug: _resolveTemplate — Tier 2 eval failed for "${' + b.expr + '}":', e.message);
8477
8743
  val = '';
8478
8744
  }
8479
8745
  } else {
@@ -8582,7 +8848,7 @@
8582
8848
  this._state = {};
8583
8849
  if (o.state) {
8584
8850
  for (var k in o.state) {
8585
- if (Object.prototype.hasOwnProperty.call(o.state, k)) {
8851
+ if (_hop.call(o.state, k)) {
8586
8852
  this._state[k] = o.state[k];
8587
8853
  }
8588
8854
  }
@@ -8591,7 +8857,7 @@
8591
8857
  this._actions = {};
8592
8858
  if (o.actions) {
8593
8859
  for (var k2 in o.actions) {
8594
- if (Object.prototype.hasOwnProperty.call(o.actions, k2)) {
8860
+ if (_hop.call(o.actions, k2)) {
8595
8861
  this._actions[k2] = o.actions[k2];
8596
8862
  }
8597
8863
  }
@@ -8601,7 +8867,7 @@
8601
8867
  if (o.methods) {
8602
8868
  var self = this;
8603
8869
  for (var k3 in o.methods) {
8604
- if (Object.prototype.hasOwnProperty.call(o.methods, k3)) {
8870
+ if (_hop.call(o.methods, k3)) {
8605
8871
  this._methods[k3] = o.methods[k3];
8606
8872
  (function(methodName, methodFn) {
8607
8873
  self[methodName] = function() {
@@ -8634,14 +8900,23 @@
8634
8900
  this._compile = !!o.compile;
8635
8901
  this._bw_refs = {};
8636
8902
  this._refCounter = 0;
8903
+ // Child component ownership (Bug #5)
8904
+ this._children = [];
8905
+ this._parent = null;
8906
+ // Factory metadata for BCCL rebuild (Bug #6)
8907
+ this._factory = taco._bwFactory || null;
8637
8908
  }
8638
8909
 
8910
+ // Short alias for ComponentHandle.prototype (see alias block at top of file).
8911
+ // 28 method definitions × 25 chars = ~700B raw savings in minified output.
8912
+ var _chp = ComponentHandle.prototype;
8913
+
8639
8914
  // ── State Methods ──
8640
8915
 
8641
8916
  /**
8642
8917
  * Get a state value. Dot-path supported: `get('user.name')`
8643
8918
  */
8644
- ComponentHandle.prototype.get = function(key) {
8919
+ _chp.get = function(key) {
8645
8920
  return bw._evaluatePath(this._state, key);
8646
8921
  };
8647
8922
 
@@ -8651,12 +8926,13 @@
8651
8926
  * @param {*} value - New value
8652
8927
  * @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
8653
8928
  */
8654
- ComponentHandle.prototype.set = function(key, value, opts) {
8929
+ _chp.set = function(key, value, opts) {
8655
8930
  // Dot-path set
8656
8931
  var parts = key.split('.');
8657
8932
  var obj = this._state;
8658
8933
  for (var i = 0; i < parts.length - 1; i++) {
8659
- if (obj[parts[i]] == null || typeof obj[parts[i]] !== 'object') {
8934
+ if (!_is(obj[parts[i]], 'object')) {
8935
+ if (bw.debug) _cw('bw.debug: set() — auto-creating intermediate "' + parts[i] + '" in path "' + key + '"');
8660
8936
  obj[parts[i]] = {};
8661
8937
  }
8662
8938
  obj = obj[parts[i]];
@@ -8676,10 +8952,10 @@
8676
8952
  /**
8677
8953
  * Get a shallow clone of the full state.
8678
8954
  */
8679
- ComponentHandle.prototype.getState = function() {
8955
+ _chp.getState = function() {
8680
8956
  var clone = {};
8681
8957
  for (var k in this._state) {
8682
- if (Object.prototype.hasOwnProperty.call(this._state, k)) {
8958
+ if (_hop.call(this._state, k)) {
8683
8959
  clone[k] = this._state[k];
8684
8960
  }
8685
8961
  }
@@ -8691,9 +8967,9 @@
8691
8967
  * @param {Object} updates - Key-value pairs to merge
8692
8968
  * @param {Object} [opts] - Options. `{sync: true}` for immediate flush.
8693
8969
  */
8694
- ComponentHandle.prototype.setState = function(updates, opts) {
8970
+ _chp.setState = function(updates, opts) {
8695
8971
  for (var k in updates) {
8696
- if (Object.prototype.hasOwnProperty.call(updates, k)) {
8972
+ if (_hop.call(updates, k)) {
8697
8973
  this._state[k] = updates[k];
8698
8974
  this._dirtyKeys[k] = true;
8699
8975
  }
@@ -8710,9 +8986,9 @@
8710
8986
  /**
8711
8987
  * Push a value onto an array in state. Clones the array.
8712
8988
  */
8713
- ComponentHandle.prototype.push = function(key, val) {
8989
+ _chp.push = function(key, val) {
8714
8990
  var arr = this.get(key);
8715
- var newArr = Array.isArray(arr) ? arr.slice() : [];
8991
+ var newArr = _isA(arr) ? arr.slice() : [];
8716
8992
  newArr.push(val);
8717
8993
  this.set(key, newArr);
8718
8994
  };
@@ -8720,9 +8996,9 @@
8720
8996
  /**
8721
8997
  * Splice an array in state. Clones the array.
8722
8998
  */
8723
- ComponentHandle.prototype.splice = function(key, start, deleteCount) {
8999
+ _chp.splice = function(key, start, deleteCount) {
8724
9000
  var arr = this.get(key);
8725
- var newArr = Array.isArray(arr) ? arr.slice() : [];
9001
+ var newArr = _isA(arr) ? arr.slice() : [];
8726
9002
  var args = [start, deleteCount].concat(Array.prototype.slice.call(arguments, 3));
8727
9003
  Array.prototype.splice.apply(newArr, args);
8728
9004
  this.set(key, newArr);
@@ -8730,7 +9006,7 @@
8730
9006
 
8731
9007
  // ── Scheduling ──
8732
9008
 
8733
- ComponentHandle.prototype._scheduleDirty = function() {
9009
+ _chp._scheduleDirty = function() {
8734
9010
  if (!this._scheduled) {
8735
9011
  this._scheduled = true;
8736
9012
  bw._dirtyComponents.push(this);
@@ -8745,17 +9021,17 @@
8745
9021
  * Creates binding descriptors with refIds for targeted DOM updates.
8746
9022
  * @private
8747
9023
  */
8748
- ComponentHandle.prototype._compileBindings = function() {
9024
+ _chp._compileBindings = function() {
8749
9025
  this._bindings = [];
8750
9026
  this._refCounter = 0;
8751
- var stateKeys = Object.keys(this._state);
9027
+ var stateKeys = _keys(this._state);
8752
9028
  var self = this;
8753
9029
 
8754
9030
  function walkTaco(taco, path) {
8755
- if (taco == null || typeof taco !== 'object' || !taco.t) return taco;
9031
+ if (!_is(taco, 'object') || !taco.t) return taco;
8756
9032
 
8757
9033
  // Check content for bindings
8758
- if (typeof taco.c === 'string' && taco.c.indexOf('${') >= 0) {
9034
+ if (_is(taco.c, 'string') && taco.c.indexOf('${') >= 0) {
8759
9035
  var refId = 'bw_ref_' + self._refCounter++;
8760
9036
  var parsed = bw._parseBindings(taco.c);
8761
9037
  var deps = [];
@@ -8777,10 +9053,10 @@
8777
9053
  // Check attributes for bindings
8778
9054
  if (taco.a) {
8779
9055
  for (var attrName in taco.a) {
8780
- if (!Object.prototype.hasOwnProperty.call(taco.a, attrName)) continue;
9056
+ if (!_hop.call(taco.a, attrName)) continue;
8781
9057
  if (attrName === 'data-bw_ref') continue;
8782
9058
  var attrVal = taco.a[attrName];
8783
- if (typeof attrVal === 'string' && attrVal.indexOf('${') >= 0) {
9059
+ if (_is(attrVal, 'string') && attrVal.indexOf('${') >= 0) {
8784
9060
  var refId2 = 'bw_ref_' + self._refCounter++;
8785
9061
  var parsed2 = bw._parseBindings(attrVal);
8786
9062
  var deps2 = [];
@@ -8806,9 +9082,27 @@
8806
9082
  }
8807
9083
 
8808
9084
  // Recurse into children
8809
- if (Array.isArray(taco.c)) {
9085
+ if (_isA(taco.c)) {
8810
9086
  for (var i = 0; i < taco.c.length; i++) {
8811
- if (taco.c[i] && typeof taco.c[i] === 'object' && taco.c[i].t) {
9087
+ // Wrap string children with ${expr} in a span so patches target the span, not the parent
9088
+ if (_is(taco.c[i], 'string') && taco.c[i].indexOf('${') >= 0) {
9089
+ var mixedRefId = 'bw_ref_' + self._refCounter++;
9090
+ var mixedParsed = bw._parseBindings(taco.c[i]);
9091
+ var mixedDeps = [];
9092
+ for (var mi = 0; mi < mixedParsed.length; mi++) {
9093
+ mixedDeps = mixedDeps.concat(bw._extractDeps(mixedParsed[mi].expr, stateKeys));
9094
+ }
9095
+ self._bindings.push({
9096
+ expr: taco.c[i],
9097
+ type: 'content',
9098
+ refId: mixedRefId,
9099
+ deps: mixedDeps,
9100
+ template: taco.c[i]
9101
+ });
9102
+ // Replace string with a span wrapper so textContent targets the span only
9103
+ taco.c[i] = { t: 'span', a: { 'data-bw_ref': mixedRefId, style: 'display:contents' }, c: taco.c[i] };
9104
+ }
9105
+ if (_is(taco.c[i], 'object') && taco.c[i].t) {
8812
9106
  walkTaco(taco.c[i], path.concat(i));
8813
9107
  }
8814
9108
  // Handle bw.when/bw.each markers
@@ -8843,7 +9137,7 @@
8843
9137
  taco.c[i]._refId = eachRefId;
8844
9138
  }
8845
9139
  }
8846
- } else if (taco.c && typeof taco.c === 'object' && taco.c.t) {
9140
+ } else if (_is(taco.c, 'object') && taco.c.t) {
8847
9141
  walkTaco(taco.c, path.concat(0));
8848
9142
  }
8849
9143
 
@@ -8859,7 +9153,7 @@
8859
9153
  * Build ref map from the live DOM after createDOM.
8860
9154
  * @private
8861
9155
  */
8862
- ComponentHandle.prototype._collectRefs = function() {
9156
+ _chp._collectRefs = function() {
8863
9157
  this._bw_refs = {};
8864
9158
  if (!this.element) return;
8865
9159
  var els = this.element.querySelectorAll('[data-bw_ref]');
@@ -8880,7 +9174,7 @@
8880
9174
  * Creates DOM, compiles bindings, registers actions, and calls lifecycle hooks.
8881
9175
  * @param {Element} parentEl - DOM element to mount into
8882
9176
  */
8883
- ComponentHandle.prototype.mount = function(parentEl) {
9177
+ _chp.mount = function(parentEl) {
8884
9178
  // willMount hook
8885
9179
  if (this._hooks.willMount) this._hooks.willMount(this);
8886
9180
 
@@ -8902,7 +9196,7 @@
8902
9196
  // Register named actions in function registry
8903
9197
  var self = this;
8904
9198
  for (var actionName in this._actions) {
8905
- if (Object.prototype.hasOwnProperty.call(this._actions, actionName)) {
9199
+ if (_hop.call(this._actions, actionName)) {
8906
9200
  var registeredName = this._bwId + '_' + actionName;
8907
9201
  (function(aName) {
8908
9202
  bw.funcRegister(function(evt) {
@@ -8921,6 +9215,11 @@
8921
9215
  this.element = bw.createDOM(tacoForDOM);
8922
9216
  this.element._bwComponentHandle = this;
8923
9217
  this.element.setAttribute('data-bw_comp_id', this._bwId);
9218
+
9219
+ // Restore o.render from original TACO (stripped by _tacoForDOM)
9220
+ if (this.taco.o && this.taco.o.render) {
9221
+ this.element._bw_render = this.taco.o.render;
9222
+ }
8924
9223
  if (this._userTag) {
8925
9224
  this.element.classList.add(this._userTag);
8926
9225
  }
@@ -8936,6 +9235,16 @@
8936
9235
 
8937
9236
  this.mounted = true;
8938
9237
 
9238
+ // Scan for child ComponentHandles and link parent/child (Bug #5)
9239
+ var childEls = this.element.querySelectorAll('[data-bw_comp_id]');
9240
+ for (var ci = 0; ci < childEls.length; ci++) {
9241
+ var ch = childEls[ci]._bwComponentHandle;
9242
+ if (ch && ch !== this && !ch._parent) {
9243
+ ch._parent = this;
9244
+ this._children.push(ch);
9245
+ }
9246
+ }
9247
+
8939
9248
  // mounted hook (backward compat: fn.length === 2 wraps (el, state))
8940
9249
  if (this._hooks.mounted) {
8941
9250
  if (this._hooks.mounted.length === 2) {
@@ -8944,16 +9253,21 @@
8944
9253
  this._hooks.mounted(this);
8945
9254
  }
8946
9255
  }
9256
+
9257
+ // Invoke o.render on initial mount (if present)
9258
+ if (this.element._bw_render) {
9259
+ this.element._bw_render(this.element, this._state);
9260
+ }
8947
9261
  };
8948
9262
 
8949
9263
  /**
8950
9264
  * Prepare TACO for initial render: resolve when/each markers.
8951
9265
  * @private
8952
9266
  */
8953
- ComponentHandle.prototype._prepareTaco = function(taco) {
8954
- if (!taco || typeof taco !== 'object') return;
9267
+ _chp._prepareTaco = function(taco) {
9268
+ if (!_is(taco, 'object')) return;
8955
9269
 
8956
- if (Array.isArray(taco.c)) {
9270
+ if (_isA(taco.c)) {
8957
9271
  for (var i = taco.c.length - 1; i >= 0; i--) {
8958
9272
  var child = taco.c[i];
8959
9273
  if (child && child._bwWhen) {
@@ -8978,18 +9292,18 @@
8978
9292
  var eachExprStr = child.expr.replace(/^\$\{|\}$/g, '');
8979
9293
  var arr = bw._evaluatePath(this._state, eachExprStr);
8980
9294
  var items = [];
8981
- if (Array.isArray(arr)) {
9295
+ if (_isA(arr)) {
8982
9296
  for (var j = 0; j < arr.length; j++) {
8983
9297
  items.push(child.factory(arr[j], j));
8984
9298
  }
8985
9299
  }
8986
9300
  taco.c[i] = { t: 'span', a: { 'data-bw_each': child._refId, style: 'display:contents' }, c: items };
8987
9301
  }
8988
- if (taco.c[i] && typeof taco.c[i] === 'object' && taco.c[i].t) {
9302
+ if (_is(taco.c[i], 'object') && taco.c[i].t) {
8989
9303
  this._prepareTaco(taco.c[i]);
8990
9304
  }
8991
9305
  }
8992
- } else if (taco.c && typeof taco.c === 'object' && taco.c.t) {
9306
+ } else if (_is(taco.c, 'object') && taco.c.t) {
8993
9307
  this._prepareTaco(taco.c);
8994
9308
  }
8995
9309
  };
@@ -8998,12 +9312,12 @@
8998
9312
  * Wire action name strings (in onclick etc.) to dispatch function calls.
8999
9313
  * @private
9000
9314
  */
9001
- ComponentHandle.prototype._wireActions = function(taco) {
9002
- if (!taco || typeof taco !== 'object' || !taco.t) return;
9315
+ _chp._wireActions = function(taco) {
9316
+ if (!_is(taco, 'object') || !taco.t) return;
9003
9317
  if (taco.a) {
9004
9318
  for (var key in taco.a) {
9005
- if (!Object.prototype.hasOwnProperty.call(taco.a, key)) continue;
9006
- if (key.startsWith('on') && typeof taco.a[key] === 'string') {
9319
+ if (!_hop.call(taco.a, key)) continue;
9320
+ if (key.startsWith('on') && _is(taco.a[key], 'string')) {
9007
9321
  var actionName = taco.a[key];
9008
9322
  if (actionName in this._actions) {
9009
9323
  var registeredName = this._bwId + '_' + actionName;
@@ -9017,11 +9331,11 @@
9017
9331
  }
9018
9332
  }
9019
9333
  }
9020
- if (Array.isArray(taco.c)) {
9334
+ if (_isA(taco.c)) {
9021
9335
  for (var i = 0; i < taco.c.length; i++) {
9022
9336
  this._wireActions(taco.c[i]);
9023
9337
  }
9024
- } else if (taco.c && typeof taco.c === 'object' && taco.c.t) {
9338
+ } else if (_is(taco.c, 'object') && taco.c.t) {
9025
9339
  this._wireActions(taco.c);
9026
9340
  }
9027
9341
  };
@@ -9030,7 +9344,7 @@
9030
9344
  * Deep-clone a TACO tree, preserving _bwWhen/_bwEach markers and their factories.
9031
9345
  * @private
9032
9346
  */
9033
- ComponentHandle.prototype._deepCloneTaco = function(taco) {
9347
+ _chp._deepCloneTaco = function(taco) {
9034
9348
  if (taco == null) return taco;
9035
9349
  // Preserve _bwWhen / _bwEach markers (contain functions)
9036
9350
  if (taco._bwWhen) {
@@ -9042,18 +9356,18 @@
9042
9356
  if (taco._bwEach) {
9043
9357
  return { _bwEach: true, expr: taco.expr, factory: taco.factory, _refId: taco._refId };
9044
9358
  }
9045
- if (typeof taco !== 'object' || !taco.t) return taco;
9359
+ if (!_is(taco, 'object') || !taco.t) return taco;
9046
9360
  var result = { t: taco.t };
9047
9361
  if (taco.a) {
9048
9362
  result.a = {};
9049
9363
  for (var k in taco.a) {
9050
- if (Object.prototype.hasOwnProperty.call(taco.a, k)) result.a[k] = taco.a[k];
9364
+ if (_hop.call(taco.a, k)) result.a[k] = taco.a[k];
9051
9365
  }
9052
9366
  }
9053
9367
  if (taco.c != null) {
9054
- if (Array.isArray(taco.c)) {
9368
+ if (_isA(taco.c)) {
9055
9369
  result.c = taco.c.map(function(child) { return this._deepCloneTaco(child); }.bind(this));
9056
- } else if (typeof taco.c === 'object') {
9370
+ } else if (_is(taco.c, 'object')) {
9057
9371
  result.c = this._deepCloneTaco(taco.c);
9058
9372
  } else {
9059
9373
  result.c = taco.c;
@@ -9067,27 +9381,31 @@
9067
9381
  * Create a copy of TACO suitable for createDOM (strips o to prevent double lifecycle).
9068
9382
  * @private
9069
9383
  */
9070
- ComponentHandle.prototype._tacoForDOM = function(taco) {
9071
- if (!taco || typeof taco !== 'object' || !taco.t) return taco;
9384
+ _chp._tacoForDOM = function(taco) {
9385
+ if (!_is(taco, 'object') || !taco.t) return taco;
9072
9386
  var result = { t: taco.t };
9073
9387
  if (taco.a) result.a = taco.a;
9074
9388
  if (taco.c != null) {
9075
- if (Array.isArray(taco.c)) {
9389
+ if (_isA(taco.c)) {
9076
9390
  result.c = taco.c.map(function(child) { return this._tacoForDOM(child); }.bind(this));
9077
- } else if (typeof taco.c === 'object' && taco.c.t) {
9391
+ } else if (_is(taco.c, 'object') && taco.c.t) {
9078
9392
  result.c = this._tacoForDOM(taco.c);
9079
9393
  } else {
9080
9394
  result.c = taco.c;
9081
9395
  }
9082
9396
  }
9083
9397
  // Intentionally strip o (no mounted/unmount/state/render on sub-elements)
9398
+ if (taco.o && (taco.o.mounted || taco.o.render || taco.o.unmount)) {
9399
+ _cw('bw: _tacoForDOM stripped o.mounted/render/unmount from child <' + taco.t +
9400
+ '>. Use onclick attribute or bw.component() for child interactivity.');
9401
+ }
9084
9402
  return result;
9085
9403
  };
9086
9404
 
9087
9405
  /**
9088
9406
  * Unmount: remove from DOM, deactivate, preserve state for re-mount.
9089
9407
  */
9090
- ComponentHandle.prototype.unmount = function() {
9408
+ _chp.unmount = function() {
9091
9409
  if (!this.mounted) return;
9092
9410
 
9093
9411
  // unmount hook
@@ -9122,12 +9440,23 @@
9122
9440
  /**
9123
9441
  * Destroy: unmount + clear state + unregister actions.
9124
9442
  */
9125
- ComponentHandle.prototype.destroy = function() {
9443
+ _chp.destroy = function() {
9126
9444
  // willDestroy hook
9127
9445
  if (this._hooks.willDestroy) {
9128
9446
  this._hooks.willDestroy(this);
9129
9447
  }
9130
9448
 
9449
+ // Cascade destroy to children depth-first (Bug #5)
9450
+ for (var ci = this._children.length - 1; ci >= 0; ci--) {
9451
+ this._children[ci].destroy();
9452
+ }
9453
+ this._children = [];
9454
+ if (this._parent) {
9455
+ var idx = this._parent._children.indexOf(this);
9456
+ if (idx >= 0) this._parent._children.splice(idx, 1);
9457
+ this._parent = null;
9458
+ }
9459
+
9131
9460
  this.unmount();
9132
9461
 
9133
9462
  // Unregister actions from function registry
@@ -9154,12 +9483,36 @@
9154
9483
  * Flush dirty state: resolve changed bindings and apply to DOM.
9155
9484
  * @private
9156
9485
  */
9157
- ComponentHandle.prototype._flush = function() {
9486
+ _chp._flush = function() {
9158
9487
  this._scheduled = false;
9159
- var changedKeys = Object.keys(this._dirtyKeys);
9488
+ var changedKeys = _keys(this._dirtyKeys);
9160
9489
  this._dirtyKeys = {};
9161
9490
  if (changedKeys.length === 0 || !this.mounted) return;
9162
9491
 
9492
+ // Factory rebuild: if a BCCL factory exists and changed keys overlap factory props,
9493
+ // rebuild the TACO from the factory with merged state (Bug #6)
9494
+ if (this._factory) {
9495
+ var rebuildNeeded = false;
9496
+ for (var fi = 0; fi < changedKeys.length; fi++) {
9497
+ if (_hop.call(this._factory.props, changedKeys[fi])) {
9498
+ rebuildNeeded = true; break;
9499
+ }
9500
+ }
9501
+ if (rebuildNeeded) {
9502
+ var merged = {};
9503
+ for (var mk in this._factory.props) if (_hop.call(this._factory.props, mk)) merged[mk] = this._factory.props[mk];
9504
+ for (var sk in this._state) if (_hop.call(this._state, sk)) merged[sk] = this._state[sk];
9505
+ this._factory.props = merged;
9506
+ var newTaco = bw.make(this._factory.type, merged);
9507
+ newTaco._bwFactory = this._factory;
9508
+ this.taco = newTaco;
9509
+ this._originalTaco = this._deepCloneTaco(newTaco);
9510
+ this._render();
9511
+ if (this._hooks.onUpdate) this._hooks.onUpdate(this, changedKeys);
9512
+ return;
9513
+ }
9514
+ }
9515
+
9163
9516
  // willUpdate hook
9164
9517
  if (this._hooks.willUpdate) {
9165
9518
  this._hooks.willUpdate(this, changedKeys);
@@ -9198,7 +9551,7 @@
9198
9551
  * Returns list of patches to apply.
9199
9552
  * @private
9200
9553
  */
9201
- ComponentHandle.prototype._resolveBindings = function(changedKeys) {
9554
+ _chp._resolveBindings = function(changedKeys) {
9202
9555
  var patches = [];
9203
9556
  for (var i = 0; i < this._bindings.length; i++) {
9204
9557
  var b = this._bindings[i];
@@ -9234,11 +9587,14 @@
9234
9587
  * Apply patches to DOM.
9235
9588
  * @private
9236
9589
  */
9237
- ComponentHandle.prototype._applyPatches = function(patches) {
9590
+ _chp._applyPatches = function(patches) {
9238
9591
  for (var i = 0; i < patches.length; i++) {
9239
9592
  var p = patches[i];
9240
9593
  var el = this._bw_refs[p.refId];
9241
- if (!el) continue;
9594
+ if (!el) {
9595
+ if (bw.debug) _cw('bw.debug: _applyPatches — ref "' + p.refId + '" not found in DOM');
9596
+ continue;
9597
+ }
9242
9598
  if (p.type === 'content') {
9243
9599
  el.textContent = p.value;
9244
9600
  } else if (p.type === 'attribute') {
@@ -9255,7 +9611,7 @@
9255
9611
  * Resolve all bindings and apply (used for initial render).
9256
9612
  * @private
9257
9613
  */
9258
- ComponentHandle.prototype._resolveAndApplyAll = function() {
9614
+ _chp._resolveAndApplyAll = function() {
9259
9615
  var patches = [];
9260
9616
  for (var i = 0; i < this._bindings.length; i++) {
9261
9617
  var b = this._bindings[i];
@@ -9278,7 +9634,7 @@
9278
9634
  * Full re-render for structural changes (when/each branch switches).
9279
9635
  * @private
9280
9636
  */
9281
- ComponentHandle.prototype._render = function() {
9637
+ _chp._render = function() {
9282
9638
  if (!this.element || !this.element.parentNode) return;
9283
9639
  var parent = this.element.parentNode;
9284
9640
  var nextSibling = this.element.nextSibling;
@@ -9318,7 +9674,7 @@
9318
9674
  * @param {string} event - Event name (e.g., 'click')
9319
9675
  * @param {Function} handler - Event handler
9320
9676
  */
9321
- ComponentHandle.prototype.on = function(event, handler) {
9677
+ _chp.on = function(event, handler) {
9322
9678
  if (this.element) {
9323
9679
  this.element.addEventListener(event, handler);
9324
9680
  }
@@ -9330,7 +9686,7 @@
9330
9686
  * @param {string} event - Event name
9331
9687
  * @param {Function} handler - Handler to remove
9332
9688
  */
9333
- ComponentHandle.prototype.off = function(event, handler) {
9689
+ _chp.off = function(event, handler) {
9334
9690
  if (this.element) {
9335
9691
  this.element.removeEventListener(event, handler);
9336
9692
  }
@@ -9345,7 +9701,7 @@
9345
9701
  * @param {Function} handler - Handler function
9346
9702
  * @returns {Function} Unsubscribe function
9347
9703
  */
9348
- ComponentHandle.prototype.sub = function(topic, handler) {
9704
+ _chp.sub = function(topic, handler) {
9349
9705
  var unsub = bw.sub(topic, handler);
9350
9706
  this._subs.push(unsub);
9351
9707
  return unsub;
@@ -9356,10 +9712,10 @@
9356
9712
  * @param {string} name - Action name
9357
9713
  * @param {...*} args - Arguments passed after comp
9358
9714
  */
9359
- ComponentHandle.prototype.action = function(name) {
9715
+ _chp.action = function(name) {
9360
9716
  var fn = this._actions[name];
9361
9717
  if (!fn) {
9362
- console.warn('ComponentHandle.action: unknown action "' + name + '"');
9718
+ _cw('ComponentHandle.action: unknown action "' + name + '"');
9363
9719
  return;
9364
9720
  }
9365
9721
  var args = [this].concat(Array.prototype.slice.call(arguments, 1));
@@ -9371,7 +9727,7 @@
9371
9727
  * @param {string} sel - CSS selector
9372
9728
  * @returns {Element|null}
9373
9729
  */
9374
- ComponentHandle.prototype.select = function(sel) {
9730
+ _chp.select = function(sel) {
9375
9731
  return this.element ? this.element.querySelector(sel) : null;
9376
9732
  };
9377
9733
 
@@ -9380,7 +9736,7 @@
9380
9736
  * @param {string} sel - CSS selector
9381
9737
  * @returns {Element[]}
9382
9738
  */
9383
- ComponentHandle.prototype.selectAll = function(sel) {
9739
+ _chp.selectAll = function(sel) {
9384
9740
  if (!this.element) return [];
9385
9741
  return Array.prototype.slice.call(this.element.querySelectorAll(sel));
9386
9742
  };
@@ -9391,7 +9747,7 @@
9391
9747
  * @param {string} tag - User-defined identifier (e.g. 'dashboard_prod_east')
9392
9748
  * @returns {ComponentHandle} this (for chaining)
9393
9749
  */
9394
- ComponentHandle.prototype.userTag = function(tag) {
9750
+ _chp.userTag = function(tag) {
9395
9751
  this._userTag = tag;
9396
9752
  if (this.element) {
9397
9753
  this.element.classList.add(tag);
@@ -9492,8 +9848,8 @@
9492
9848
  }
9493
9849
  if (!el || !el._bwComponentHandle) return false;
9494
9850
  var comp = el._bwComponentHandle;
9495
- if (typeof comp[action] !== 'function') {
9496
- console.warn('bw.message: unknown action "' + action + '" on component ' + target);
9851
+ if (!_is(comp[action], 'function')) {
9852
+ _cw('bw.message: unknown action "' + action + '" on component ' + target);
9497
9853
  return false;
9498
9854
  }
9499
9855
  comp[action](data);
@@ -9530,7 +9886,7 @@
9530
9886
  },
9531
9887
  focus: function(selector) {
9532
9888
  var el = bw._el(selector);
9533
- if (el && typeof el.focus === 'function') el.focus();
9889
+ if (el && _is(el.focus, 'function')) el.focus();
9534
9890
  },
9535
9891
  download: function(filename, content, mimeType) {
9536
9892
  if (typeof document === 'undefined') return;
@@ -9695,12 +10051,12 @@
9695
10051
  } else if (type === 'remove') {
9696
10052
  var toRemove = bw._el(target);
9697
10053
  if (!toRemove) return false;
9698
- if (typeof bw.cleanup === 'function') bw.cleanup(toRemove);
10054
+ if (_is(bw.cleanup, 'function')) bw.cleanup(toRemove);
9699
10055
  toRemove.remove();
9700
10056
  return true;
9701
10057
 
9702
10058
  } else if (type === 'batch') {
9703
- if (!Array.isArray(msg.ops)) return false;
10059
+ if (!_isA(msg.ops)) return false;
9704
10060
  var allOk = true;
9705
10061
  msg.ops.forEach(function(op) {
9706
10062
  if (!bw.clientApply(op)) allOk = false;
@@ -9716,26 +10072,26 @@
9716
10072
  bw._clientFunctions[msg.name] = new Function('return ' + msg.body)();
9717
10073
  return true;
9718
10074
  } catch (e) {
9719
- console.error('[bw] register error:', msg.name, e);
10075
+ _ce('[bw] register error:', msg.name, e);
9720
10076
  return false;
9721
10077
  }
9722
10078
 
9723
10079
  } else if (type === 'call') {
9724
10080
  if (!msg.name) return false;
9725
10081
  var fn = bw._clientFunctions[msg.name] || bw._builtinClientFunctions[msg.name];
9726
- if (typeof fn !== 'function') return false;
10082
+ if (!_is(fn, 'function')) return false;
9727
10083
  try {
9728
- var args = Array.isArray(msg.args) ? msg.args : [];
10084
+ var args = _isA(msg.args) ? msg.args : [];
9729
10085
  fn.apply(null, args);
9730
10086
  return true;
9731
10087
  } catch (e) {
9732
- console.error('[bw] call error:', msg.name, e);
10088
+ _ce('[bw] call error:', msg.name, e);
9733
10089
  return false;
9734
10090
  }
9735
10091
 
9736
10092
  } else if (type === 'exec') {
9737
10093
  if (!bw._allowExec) {
9738
- console.warn('[bw] exec rejected: allowExec is not enabled');
10094
+ _cw('[bw] exec rejected: allowExec is not enabled');
9739
10095
  return false;
9740
10096
  }
9741
10097
  if (!msg.code) return false;
@@ -9743,7 +10099,7 @@
9743
10099
  new Function(msg.code)();
9744
10100
  return true;
9745
10101
  } catch (e) {
9746
- console.error('[bw] exec error:', e);
10102
+ _ce('[bw] exec error:', e);
9747
10103
  return false;
9748
10104
  }
9749
10105
  }
@@ -9791,7 +10147,7 @@
9791
10147
 
9792
10148
  function handleMessage(data) {
9793
10149
  try {
9794
- var msg = typeof data === 'string' ? bw.clientParse(data) : data;
10150
+ var msg = _is(data, 'string') ? bw.clientParse(data) : data;
9795
10151
  if (onMessage) onMessage(msg);
9796
10152
  if (handlers.message) handlers.message(msg);
9797
10153
  bw.clientApply(msg);
@@ -9829,7 +10185,7 @@
9829
10185
  setStatus('connected');
9830
10186
  conn._pollTimer = setInterval(function() {
9831
10187
  fetch(url).then(function(r) { return r.json(); }).then(function(msgs) {
9832
- if (Array.isArray(msgs)) {
10188
+ if (_isA(msgs)) {
9833
10189
  msgs.forEach(handleMessage);
9834
10190
  } else if (msgs && msgs.type) {
9835
10191
  handleMessage(msgs);
@@ -9911,33 +10267,33 @@
9911
10267
  el = target.element;
9912
10268
  comp = target;
9913
10269
  } else {
9914
- if (typeof target === 'string') {
10270
+ if (_is(target, 'string')) {
9915
10271
  el = bw.$(target)[0];
9916
10272
  }
9917
10273
  if (!el) {
9918
- console.warn('bw.inspect: element not found');
10274
+ _cw('bw.inspect: element not found');
9919
10275
  return null;
9920
10276
  }
9921
10277
  comp = el._bwComponentHandle;
9922
10278
  }
9923
10279
  if (!comp) {
9924
- console.log('bw.inspect: no ComponentHandle on this element');
9925
- console.log(' Tag:', el.tagName);
9926
- console.log(' Classes:', el.className);
9927
- console.log(' _bw_state:', el._bw_state || '(none)');
10280
+ _cl('bw.inspect: no ComponentHandle on this element');
10281
+ _cl(' Tag:', el.tagName);
10282
+ _cl(' Classes:', el.className);
10283
+ _cl(' _bw_state:', el._bw_state || '(none)');
9928
10284
  return null;
9929
10285
  }
9930
10286
  var deps = comp._bindings.reduce(function(s, b) {
9931
10287
  return s.concat(b.deps || []);
9932
10288
  }, []).filter(function(v, i, a) { return a.indexOf(v) === i; });
9933
10289
  console.group('Component: ' + comp._bwId);
9934
- console.log('State:', comp._state);
9935
- console.log('Bindings:', comp._bindings.length, '(deps:', deps, ')');
9936
- console.log('Methods:', Object.keys(comp._methods));
9937
- console.log('Actions:', Object.keys(comp._actions));
9938
- console.log('User tag:', comp._userTag || '(none)');
9939
- console.log('Mounted:', comp.mounted);
9940
- console.log('Element:', comp.element);
10290
+ _cl('State:', comp._state);
10291
+ _cl('Bindings:', comp._bindings.length, '(deps:', deps, ')');
10292
+ _cl('Methods:', _keys(comp._methods));
10293
+ _cl('Actions:', _keys(comp._actions));
10294
+ _cl('User tag:', comp._userTag || '(none)');
10295
+ _cl('Mounted:', comp.mounted);
10296
+ _cl('Element:', comp.element);
9941
10297
  console.groupEnd();
9942
10298
  return comp;
9943
10299
  };
@@ -9960,8 +10316,8 @@
9960
10316
  // Pre-extract all binding expressions
9961
10317
  var precompiled = [];
9962
10318
  function walkExpressions(node) {
9963
- if (!node || typeof node !== 'object') return;
9964
- if (typeof node.c === 'string' && node.c.indexOf('${') >= 0) {
10319
+ if (!_is(node, 'object')) return;
10320
+ if (_is(node.c, 'string') && node.c.indexOf('${') >= 0) {
9965
10321
  var parsed = bw._parseBindings(node.c);
9966
10322
  for (var i = 0; i < parsed.length; i++) {
9967
10323
  try {
@@ -9976,9 +10332,9 @@
9976
10332
  }
9977
10333
  if (node.a) {
9978
10334
  for (var key in node.a) {
9979
- if (Object.prototype.hasOwnProperty.call(node.a, key)) {
10335
+ if (_hop.call(node.a, key)) {
9980
10336
  var v = node.a[key];
9981
- if (typeof v === 'string' && v.indexOf('${') >= 0) {
10337
+ if (_is(v, 'string') && v.indexOf('${') >= 0) {
9982
10338
  var parsed2 = bw._parseBindings(v);
9983
10339
  for (var j = 0; j < parsed2.length; j++) {
9984
10340
  try {
@@ -9994,9 +10350,9 @@
9994
10350
  }
9995
10351
  }
9996
10352
  }
9997
- if (Array.isArray(node.c)) {
10353
+ if (_isA(node.c)) {
9998
10354
  for (var k = 0; k < node.c.length; k++) walkExpressions(node.c[k]);
9999
- } else if (node.c && typeof node.c === 'object' && node.c.t) {
10355
+ } else if (_is(node.c, 'object') && node.c.t) {
10000
10356
  walkExpressions(node.c);
10001
10357
  }
10002
10358
  }
@@ -10008,7 +10364,7 @@
10008
10364
  handle._precompiledBindings = precompiled;
10009
10365
  if (initialState) {
10010
10366
  for (var k in initialState) {
10011
- if (Object.prototype.hasOwnProperty.call(initialState, k)) {
10367
+ if (_hop.call(initialState, k)) {
10012
10368
  handle._state[k] = initialState[k];
10013
10369
  }
10014
10370
  }
@@ -10039,18 +10395,18 @@
10039
10395
  bw.css = function(rules, options = {}) {
10040
10396
  const { minify = false, pretty = !minify } = options;
10041
10397
 
10042
- if (typeof rules === 'string') return rules;
10398
+ if (_is(rules, 'string')) return rules;
10043
10399
 
10044
10400
  let css = '';
10045
10401
  const indent = pretty ? ' ' : '';
10046
10402
  const newline = pretty ? '\n' : '';
10047
10403
  const space = pretty ? ' ' : '';
10048
10404
 
10049
- if (Array.isArray(rules)) {
10405
+ if (_isA(rules)) {
10050
10406
  css = rules.map(rule => bw.css(rule, options)).join(newline);
10051
- } else if (typeof rules === 'object') {
10407
+ } else if (_is(rules, 'object')) {
10052
10408
  Object.entries(rules).forEach(([selector, styles]) => {
10053
- if (typeof styles === 'object' && !Array.isArray(styles)) {
10409
+ if (_is(styles, 'object')) {
10054
10410
  // Handle @media, @keyframes, @supports — recurse into nested block
10055
10411
  if (selector.charAt(0) === '@') {
10056
10412
  const inner = bw.css(styles, options);
@@ -10099,7 +10455,7 @@
10099
10455
  */
10100
10456
  bw.injectCSS = function(css, options = {}) {
10101
10457
  if (!bw._isBrowser) {
10102
- console.warn('bw.injectCSS requires a DOM environment');
10458
+ _cw('bw.injectCSS requires a DOM environment');
10103
10459
  return null;
10104
10460
  }
10105
10461
 
@@ -10116,7 +10472,7 @@
10116
10472
  }
10117
10473
 
10118
10474
  // Convert CSS if needed
10119
- const cssStr = typeof css === 'string' ? css : bw.css(css, options);
10475
+ const cssStr = _is(css, 'string') ? css : bw.css(css, options);
10120
10476
 
10121
10477
  // Set or append CSS
10122
10478
  if (append && styleEl.textContent) {
@@ -10146,7 +10502,7 @@
10146
10502
  var result = {};
10147
10503
  for (var i = 0; i < arguments.length; i++) {
10148
10504
  var arg = arguments[i];
10149
- if (arg && typeof arg === 'object') Object.assign(result, arg);
10505
+ if (_is(arg, 'object')) Object.assign(result, arg);
10150
10506
  }
10151
10507
  return result;
10152
10508
  };
@@ -10269,7 +10625,7 @@
10269
10625
  bw.responsive = function(selector, breakpoints) {
10270
10626
  var sizes = { sm: '576px', md: '768px', lg: '992px', xl: '1200px' };
10271
10627
  var parts = [];
10272
- Object.keys(breakpoints).forEach(function(key) {
10628
+ _keys(breakpoints).forEach(function(key) {
10273
10629
  var rules = {};
10274
10630
  if (key === 'base') {
10275
10631
  rules[selector] = breakpoints[key];
@@ -10341,18 +10697,18 @@
10341
10697
  if (!selector) return [];
10342
10698
 
10343
10699
  // Already an array
10344
- if (Array.isArray(selector)) return selector;
10700
+ if (_isA(selector)) return selector;
10345
10701
 
10346
10702
  // Single element
10347
10703
  if (selector.nodeType) return [selector];
10348
10704
 
10349
10705
  // NodeList or HTMLCollection
10350
- if (selector.length !== undefined && typeof selector !== 'string') {
10706
+ if (selector.length !== undefined && !_is(selector, 'string')) {
10351
10707
  return Array.from(selector);
10352
10708
  }
10353
10709
 
10354
10710
  // CSS selector string
10355
- if (typeof selector === 'string') {
10711
+ if (_is(selector, 'string')) {
10356
10712
  return Array.from(document.querySelectorAll(selector));
10357
10713
  }
10358
10714
 
@@ -10856,7 +11212,7 @@
10856
11212
 
10857
11213
  // Auto-detect columns if not provided
10858
11214
  const cols = columns || (data.length > 0
10859
- ? Object.keys(data[0]).map(key => ({ key, label: key }))
11215
+ ? _keys(data[0]).map(key => ({ key, label: key }))
10860
11216
  : []);
10861
11217
 
10862
11218
  // Current sort state
@@ -10871,7 +11227,7 @@
10871
11227
  const bVal = b[currentSortColumn];
10872
11228
 
10873
11229
  // Handle different types
10874
- if (typeof aVal === 'number' && typeof bVal === 'number') {
11230
+ if (_is(aVal, 'number') && _is(bVal, 'number')) {
10875
11231
  return currentSortDirection === 'asc' ? aVal - bVal : bVal - aVal;
10876
11232
  }
10877
11233
 
@@ -10981,7 +11337,7 @@
10981
11337
  bw.makeTableFromArray = function(config) {
10982
11338
  const { data = [], headerRow = true, columns, ...rest } = config;
10983
11339
 
10984
- if (!Array.isArray(data) || data.length === 0) {
11340
+ if (!_isA(data) || data.length === 0) {
10985
11341
  return bw.makeTable({ data: [], columns: columns || [], ...rest });
10986
11342
  }
10987
11343
 
@@ -11063,7 +11419,7 @@
11063
11419
  className = ''
11064
11420
  } = config;
11065
11421
 
11066
- if (!Array.isArray(data) || data.length === 0) {
11422
+ if (!_isA(data) || data.length === 0) {
11067
11423
  return { t: 'div', a: { class: ('bw_bar_chart_container ' + className).trim() }, c: '' };
11068
11424
  }
11069
11425
 
@@ -11212,7 +11568,7 @@
11212
11568
  */
11213
11569
  bw.render = function(element, position, taco) {
11214
11570
  // Get target element
11215
- const targetEl = typeof element === 'string'
11571
+ const targetEl = _is(element, 'string')
11216
11572
  ? document.querySelector(element)
11217
11573
  : element;
11218
11574
 
@@ -11362,7 +11718,7 @@
11362
11718
  setContent(content) {
11363
11719
  this._taco.c = content;
11364
11720
  if (this.element) {
11365
- if (typeof content === 'string') {
11721
+ if (_is(content, 'string')) {
11366
11722
  this.element.textContent = content;
11367
11723
  } else {
11368
11724
  // Re-render for complex content