@lmjs/core 1.0.7 → 2.0.0

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.
@@ -0,0 +1,473 @@
1
+ // dom-shim.js — a minimal jQuery-compatible layer.
2
+ //
3
+ // Scope, deliberately narrow: covers exactly the surface LumenJS's own core
4
+ // (_re.js, walk.js, ws.js, lstnrs.js) uses internally, plus the documented
5
+ // app-facing API (el.css/.data/.val/.attr, el[0], .trigger) from
6
+ // lumenjs-spec.md §6.3/§10. Scoped by grepping actual usage across the core
7
+ // files, not guessed.
8
+ //
9
+ // This is NOT a jQuery replacement for everything — optional plugins
10
+ // (Select2, Flickity, the colorpicker, etc.) stay on real jQuery when a page
11
+ // opts into them, since they're third-party code written against jQuery's
12
+ // actual $.fn plugin architecture. This shim only has to satisfy the core
13
+ // runtime, which is what makes it small.
14
+ (function (global) {
15
+ function toArray(x) {
16
+ if (x == null) return [];
17
+ if (typeof NodeList !== 'undefined' && x instanceof NodeList) return Array.prototype.slice.call(x);
18
+ if (typeof HTMLCollection !== 'undefined' && x instanceof HTMLCollection) return Array.prototype.slice.call(x);
19
+ if (Array.isArray(x)) return x;
20
+ if (x instanceof $) return Array.prototype.slice.call(x);
21
+ return [x];
22
+ }
23
+
24
+ function $(selector, context) {
25
+ if (!(this instanceof $)) return new $(selector, context);
26
+
27
+ var els = [];
28
+ var sel = undefined;
29
+ if (!selector) {
30
+ els = [];
31
+ } else if (typeof selector === 'string') {
32
+ var trimmed = selector.trim();
33
+ if (trimmed[0] === '<') {
34
+ var tpl = document.createElement('template');
35
+ tpl.innerHTML = trimmed;
36
+ els = toArray(tpl.content.childNodes).filter(function (n) { return n.nodeType === 1; });
37
+ } else {
38
+ sel = selector;
39
+ var root = context ? (context instanceof $ ? context[0] : context) : document;
40
+ els = root ? toArray(root.querySelectorAll(selector)) : [];
41
+ }
42
+ } else {
43
+ els = toArray(selector);
44
+ }
45
+
46
+ for (var i = 0; i < els.length; i++) this[i] = els[i];
47
+ this.length = els.length;
48
+ this.selector = sel;
49
+ return this;
50
+ }
51
+
52
+ $.fn = $.prototype;
53
+ $.fn.jquery = 'lumenjs-shim';
54
+
55
+ $.fn.extend = function (methods) {
56
+ for (var k in methods) if (Object.prototype.hasOwnProperty.call(methods, k)) $.fn[k] = methods[k];
57
+ return $;
58
+ };
59
+ $.extend = function (target) {
60
+ for (var i = 1; i < arguments.length; i++) {
61
+ var src = arguments[i];
62
+ for (var k in src) if (Object.prototype.hasOwnProperty.call(src, k)) target[k] = src[k];
63
+ }
64
+ return target;
65
+ };
66
+
67
+ // jQuery's own Event wrapper — used internally by the vendored WebWorker
68
+ // helper (`new $.Event(type)`, then it assigns custom properties onto
69
+ // the result). A plain native Event supports arbitrary property
70
+ // assignment already, so this just needs to exist as a constructor.
71
+ $.Event = function (type, props) {
72
+ var e = typeof type === 'string' ? new Event(type, { bubbles: true, cancelable: true }) : type;
73
+ if (props) for (var k in props) e[k] = props[k];
74
+ return e;
75
+ };
76
+
77
+ $.proxy = function (fn, context) { return fn.bind(context); };
78
+ $.trim = function (s) { return (s == null ? '' : String(s)).trim(); };
79
+ $.inArray = function (val, arr) { return Array.prototype.indexOf.call(arr, val); };
80
+ $.each = function (arr, fn) {
81
+ if (Array.isArray(arr) || arr instanceof $) {
82
+ for (var i = 0; i < arr.length; i++) if (fn.call(arr[i], i, arr[i]) === false) break;
83
+ } else {
84
+ for (var k in arr) if (fn.call(arr[k], k, arr[k]) === false) break;
85
+ }
86
+ return arr;
87
+ };
88
+ // Minimal fetch-based $.ajax — covers ws.js's one call site (a plain
89
+ // GET/POST with success/error callbacks), not jQuery's full option set.
90
+ // Fires the global ajaxStart/ajaxStop events real jQuery provides for a
91
+ // loading-indicator pattern (`$(document).ajaxStart(...)`) — used at
92
+ // _re.js's top level.
93
+ var _ajaxActive = 0;
94
+ $.ajax = function (opts) {
95
+ opts = opts || {};
96
+ var method = (opts.method || opts.type || 'GET').toUpperCase();
97
+ var init = { method: method, headers: opts.headers || {} };
98
+ if (opts.data != null && method !== 'GET') {
99
+ init.body = typeof opts.data === 'string' ? opts.data : JSON.stringify(opts.data);
100
+ }
101
+ if (_ajaxActive === 0) $(document).trigger('ajaxStart');
102
+ _ajaxActive++;
103
+ $(document).trigger('ajaxSend');
104
+ return fetch(opts.url, init)
105
+ .then(function (res) {
106
+ var ct = res.headers.get('content-type') || '';
107
+ return ct.indexOf('json') > -1 ? res.json() : res.text();
108
+ })
109
+ .then(function (data) {
110
+ opts.success && opts.success(data);
111
+ $(document).trigger('ajaxSuccess');
112
+ return data;
113
+ })
114
+ .catch(function (err) {
115
+ opts.error && opts.error(err);
116
+ $(document).trigger('ajaxError');
117
+ throw err;
118
+ })
119
+ .finally(function () {
120
+ _ajaxActive--;
121
+ $(document).trigger('ajaxComplete');
122
+ if (_ajaxActive === 0) $(document).trigger('ajaxStop');
123
+ });
124
+ };
125
+
126
+ $.fn.each = function (fn) {
127
+ for (var i = 0; i < this.length; i++) if (fn.call(this[i], i, this[i]) === false) break;
128
+ return this;
129
+ };
130
+ $.fn.get = function (i) { return i === undefined ? toArray(this) : this[i]; };
131
+
132
+ $.fn.attr = function (name, value) {
133
+ if (value === undefined) return this[0] ? this[0].getAttribute(name) : undefined;
134
+ return this.each(function () { this.setAttribute(name, value); });
135
+ };
136
+ $.fn.removeAttr = function (name) {
137
+ return this.each(function () { this.removeAttribute(name); });
138
+ };
139
+ // Not a real jQuery method — LumenJS's own $.fn.extend addition
140
+ // (lstnrs.js), kept here since the core relies on it directly.
141
+ //
142
+ // 2026-09-16, real bug found and fixed: native `hasAttribute` can never
143
+ // see a `@`-prefixed name (`@click`, `@sort`, ...) — vendor/
144
+ // reconnecting-websocket.js monkey-patches HTMLElement.prototype.
145
+ // setAttribute/getAttribute/removeAttribute so any `@`-prefixed write
146
+ // is redirected to `el.events[name]` instead of the real attribute
147
+ // store (deliberate, real V2 framework behavior, not something this
148
+ // shim invented — see that file). Native `hasAttribute` was never part
149
+ // of that patch, so it always returned false for these, and lstnrs.js's
150
+ // processEv() bails immediately on `!el.hasAttr("@" + n)` — meaning
151
+ // every `@`-event handler's before/after-guard and payload lookup was
152
+ // unreachable. `getAttribute` *is* patch-aware, so route through that
153
+ // instead — correct for both real attributes (native getAttribute
154
+ // returns null when absent) and `@`-prefixed ones.
155
+ $.fn.hasAttr = function (name) {
156
+ return !!(this[0] && this[0].getAttribute(name) != null);
157
+ };
158
+ $.fn.data = function (key, value) {
159
+ if (value === undefined) {
160
+ if (!this[0]) return undefined;
161
+ if (key === undefined) return Object.assign({}, this[0].dataset);
162
+ var v = this[0].dataset[key];
163
+ try { return JSON.parse(v); } catch (e) { return v; }
164
+ }
165
+ return this.each(function () { this.dataset[key] = typeof value === 'string' ? value : JSON.stringify(value); });
166
+ };
167
+ $.fn.val = function (value) {
168
+ if (value === undefined) return this[0] ? this[0].value : undefined;
169
+ return this.each(function () { this.value = value; });
170
+ };
171
+ $.fn.html = function (value) {
172
+ if (value === undefined) return this[0] ? this[0].innerHTML : undefined;
173
+ // 2026-09-14, real bug found alongside .append()'s: _re.js calls
174
+ // `.html(_rel._RealDOM)` (an array of real Nodes, e.g. for a
175
+ // rendered layout/view) and `.html(_re._RealDOM)` for the view
176
+ // itself — a bare string assignment (`this.innerHTML = value`)
177
+ // coerces an array via toString(), producing garbage text like
178
+ // "[object HTMLDivElement]" instead of actually inserting the
179
+ // rendered content. Route anything non-string through the same
180
+ // append() normalization instead.
181
+ if (typeof value !== 'string') {
182
+ var nodes = _normalizeAppendable([value]);
183
+ return this.each(function () {
184
+ this.innerHTML = '';
185
+ var self = this;
186
+ nodes.forEach(function (n) { self.appendChild(n); });
187
+ });
188
+ }
189
+ return this.each(function () { this.innerHTML = value; });
190
+ };
191
+ $.fn.css = function (name, value) {
192
+ if (typeof name === 'object') {
193
+ return this.each(function () {
194
+ var el = this;
195
+ for (var k in name) el.style[k] = name[k];
196
+ });
197
+ }
198
+ if (value === undefined) return this[0] ? getComputedStyle(this[0])[name] : undefined;
199
+ return this.each(function () { this.style[name] = value; });
200
+ };
201
+ $.fn.addClass = function (cls) {
202
+ var list = cls.split(/\s+/).filter(Boolean);
203
+ return this.each(function () { this.classList.add.apply(this.classList, list); });
204
+ };
205
+ $.fn.removeClass = function (cls) {
206
+ var list = cls.split(/\s+/).filter(Boolean);
207
+ return this.each(function () { this.classList.remove.apply(this.classList, list); });
208
+ };
209
+ $.fn.hasClass = function (cls) { return !!(this[0] && this[0].classList.contains(cls)); };
210
+ $.fn.toggleClass = function (cls, force) {
211
+ var list = cls.split(/\s+/).filter(Boolean);
212
+ return this.each(function () {
213
+ var el = this;
214
+ list.forEach(function (c) { el.classList.toggle(c, force); });
215
+ });
216
+ };
217
+ $.fn.remove = function () {
218
+ return this.each(function () { this.parentNode && this.parentNode.removeChild(this); });
219
+ };
220
+ // Flattens real jQuery's .append()/.prepend() calling convention — any
221
+ // number of arguments, each a string, a real Node, a $-wrapped
222
+ // element, or an array of any of those — into a flat list of real
223
+ // Nodes. Anything else (a plain object, a number, etc.) is silently
224
+ // dropped, matching real jQuery's tolerance for garbage input.
225
+ //
226
+ // 2026-09-14, real bug found testing a real project against a real dev
227
+ // server: this file's old `.append(content)` only ever looked at its
228
+ // FIRST argument and assumed it was a string, a Node, or $-wrapped —
229
+ // `_re.js`'s `el.append(..._re._RealDOM)` (spreading a Node array) hit
230
+ // this whenever _RealDOM had zero or more-than-one entries, and a
231
+ // pre-existing, vestigial `$("body").append({beajs:true, ...})` call
232
+ // in vendor/reconnecting-websocket.js (a plain object, not a Node —
233
+ // dead/legacy code, evidenced by the commented-out line right above
234
+ // it) crashed outright instead of being silently ignored the way real
235
+ // jQuery would have.
236
+ function _normalizeAppendable(args) {
237
+ var out = [];
238
+ function add(item) {
239
+ if (item == null) return;
240
+ if (Array.isArray(item)) { item.forEach(add); return; }
241
+ if (item instanceof $) { for (var i = 0; i < item.length; i++) add(item[i]); return; }
242
+ if (typeof item === 'string') {
243
+ var tpl = document.createElement('template');
244
+ tpl.innerHTML = item;
245
+ while (tpl.content.firstChild) out.push(tpl.content.removeChild(tpl.content.firstChild));
246
+ return;
247
+ }
248
+ if (item instanceof Node) { out.push(item); return; }
249
+ }
250
+ for (var i = 0; i < args.length; i++) add(args[i]);
251
+ return out;
252
+ }
253
+ $.fn.append = function () {
254
+ if (arguments.length === 1 && typeof arguments[0] === 'string') {
255
+ var html = arguments[0];
256
+ return this.each(function () { this.insertAdjacentHTML('beforeend', html); });
257
+ }
258
+ var nodes = _normalizeAppendable(arguments);
259
+ return this.each(function () {
260
+ var self = this;
261
+ nodes.forEach(function (n) { self.appendChild(n); });
262
+ });
263
+ };
264
+ $.fn.prepend = function () {
265
+ if (arguments.length === 1 && typeof arguments[0] === 'string') {
266
+ var html = arguments[0];
267
+ return this.each(function () { this.insertAdjacentHTML('afterbegin', html); });
268
+ }
269
+ var nodes = _normalizeAppendable(arguments);
270
+ return this.each(function () {
271
+ var self = this;
272
+ var ref = self.firstChild;
273
+ nodes.forEach(function (n) { self.insertBefore(n, ref); });
274
+ });
275
+ };
276
+ $.fn.after = function (content) {
277
+ return this.each(function () {
278
+ if (typeof content === 'string') this.insertAdjacentHTML('afterend', content);
279
+ else this.parentNode.insertBefore(content instanceof $ ? content[0] : content, this.nextSibling);
280
+ });
281
+ };
282
+ $.fn.appendTo = function (target) { $(target).append(this); return this; };
283
+ // Real layout metrics (jsdom has no rendering engine, so these will
284
+ // read 0 here — harmless for anything that isn't actually testing
285
+ // pixel measurements, which nothing in this codebase's core does).
286
+ $.fn.width = function () { return this[0] ? this[0].offsetWidth : 0; };
287
+ $.fn.height = function () { return this[0] ? this[0].offsetHeight : 0; };
288
+ $.fn.outerWidth = function (includeMargin) {
289
+ if (!this[0]) return 0;
290
+ var w = this[0].offsetWidth;
291
+ if (includeMargin) {
292
+ var s = getComputedStyle(this[0]);
293
+ w += parseFloat(s.marginLeft || 0) + parseFloat(s.marginRight || 0);
294
+ }
295
+ return w;
296
+ };
297
+ $.fn.outerHeight = function (includeMargin) {
298
+ if (!this[0]) return 0;
299
+ var h = this[0].offsetHeight;
300
+ if (includeMargin) {
301
+ var s = getComputedStyle(this[0]);
302
+ h += parseFloat(s.marginTop || 0) + parseFloat(s.marginBottom || 0);
303
+ }
304
+ return h;
305
+ };
306
+ $.fn.parent = function () { return $(this[0] ? this[0].parentElement : null); };
307
+ $.fn.next = function () { return $(this[0] ? this[0].nextElementSibling : null); };
308
+ $.fn.closest = function (sel) { return $(this[0] ? this[0].closest(sel) : null); };
309
+ $.fn.ready = function (fn) {
310
+ if (document.readyState !== 'loading') fn($);
311
+ else document.addEventListener('DOMContentLoaded', function () { fn($); });
312
+ return this;
313
+ };
314
+ // jQuery's full family of global ajax events — all just named events on
315
+ // document under the hood, registered/fired the same way.
316
+ ['ajaxStart', 'ajaxStop', 'ajaxSend', 'ajaxSuccess', 'ajaxError', 'ajaxComplete'].forEach(function (name) {
317
+ $.fn[name] = function (fn) { return this.on(name, fn); };
318
+ });
319
+
320
+ // ---- Events ----
321
+ // A handful of event names need translating for delegation to actually
322
+ // work — left as-is, these would silently never fire, exactly the class
323
+ // of bug point 6 exists to stop happening. `touch` isn't a real DOM
324
+ // event name at all. `focus`/`blur` are real, but don't bubble, so
325
+ // delegating them from `document`/`body` (lstnrs.js's whole pattern)
326
+ // requires their bubbling equivalents instead — this is what real
327
+ // jQuery does internally for the same reason.
328
+ var EVENT_NAME_MAP = { touch: 'touchstart', focus: 'focusin', blur: 'focusout' };
329
+
330
+ // jQuery lets you `.on()`/`.trigger()` an arbitrary plain object, not
331
+ // just DOM nodes — real jQuery keeps its own event table for that case.
332
+ // Needed here: the vendored WebWorker helper wraps itself (`$(this)`)
333
+ // and uses `.on()`/`.trigger()` purely as a pub/sub bus, never touching
334
+ // the DOM. Bridge non-Node targets to a real EventTarget instead of
335
+ // assuming addEventListener always exists.
336
+ var _eventBuses = typeof WeakMap !== 'undefined' ? new WeakMap() : null;
337
+ function _isNode(x) {
338
+ return x === window || x === document || (typeof Node !== 'undefined' && x instanceof Node);
339
+ }
340
+ // 2026-09-14, real bug found testing a real project end-to-end: the
341
+ // same WebWorker helper ALSO does `$(this.getNativeWorker()).on(
342
+ // "message", ...)` to receive real messages posted FROM the actual
343
+ // worker thread. A native `Worker` isn't a DOM Node, so `_isNode` was
344
+ // false and this got routed through the fake WeakMap-bridged
345
+ // EventTarget below instead of the real Worker instance's own native
346
+ // event system — meaning real `message` events the browser itself
347
+ // dispatches on the Worker never reached it, and the worker's
348
+ // load/start handshake (which is entirely message-driven) silently
349
+ // stalled forever. Any object that's already a genuine EventTarget
350
+ // (Worker, WebSocket, XMLHttpRequest, ...) should be used directly,
351
+ // not bridged.
352
+ function _isRealEventTarget(x) {
353
+ return x != null && typeof x.addEventListener === 'function' && typeof x.dispatchEvent === 'function';
354
+ }
355
+ function _eventTargetFor(x) {
356
+ if (_isNode(x) || _isRealEventTarget(x)) return x;
357
+ if (!_eventBuses.has(x)) _eventBuses.set(x, new EventTarget());
358
+ return _eventBuses.get(x);
359
+ }
360
+
361
+ // 2026-09-16, real bug found and fixed: lstnrs.js delegates every
362
+ // @click/@sort/@drag/... binding via `$("body").on(evt, "[\\@name]",
363
+ // handler)` — 34 call sites, all this exact single-attribute-presence
364
+ // shape. Native `Element.prototype.closest()` (used below for every
365
+ // other selector) matches against the browser's real internal
366
+ // attribute storage, which can never see the `@`-prefixed redirect
367
+ // described in $.fn.hasAttr's comment above — so a delegated `@`-event
368
+ // listener could never fire at all, in any environment, real browser
369
+ // included (confirmed against an actual `lm build` output driven by
370
+ // real Chrome, not just jsdom). Detect that exact selector shape and
371
+ // walk up manually using `getAttribute` (patch-aware) instead of
372
+ // native closest(); every other selector (e.g. lstnrs.js's own
373
+ // "[up] [browse]") is untouched, still native.
374
+ var AT_ATTR_SELECTOR = /^\[\\@([\w-]+)\]$/;
375
+ $.fn.on = function (events, selector, handler) {
376
+ if (typeof selector === 'function') { handler = selector; selector = null; }
377
+ var evts = events.split(/\s+/).filter(Boolean);
378
+ var atMatch = selector && AT_ATTR_SELECTOR.exec(selector);
379
+ var atAttr = atMatch ? '@' + atMatch[1] : null;
380
+ return this.each(function () {
381
+ var node = this;
382
+ var bus = _eventTargetFor(node);
383
+ evts.forEach(function (evt) {
384
+ var real = EVENT_NAME_MAP[evt] || evt;
385
+ bus.addEventListener(real, function (e) {
386
+ if (atAttr) {
387
+ var el = e.target;
388
+ var match = null;
389
+ while (el && el.nodeType === 1) {
390
+ if (el.getAttribute(atAttr) != null) { match = el; break; }
391
+ el = el.parentElement;
392
+ }
393
+ if (match && node.contains(match)) handler.call(match, e);
394
+ } else if (selector && _isNode(node)) {
395
+ var match2 = e.target.closest(selector);
396
+ if (match2 && node.contains(match2)) handler.call(match2, e);
397
+ } else {
398
+ handler.call(node, e);
399
+ }
400
+ });
401
+ });
402
+ });
403
+ };
404
+ // No .live() — 2026-09-13: confirmed every call site in lstnrs.js was
405
+ // internal (never part of the app-facing API), and the file already
406
+ // uses `$("body").on(event, selector, handler)` everywhere else for the
407
+ // exact same pattern. Migrated all 11 sites to that instead of shimming
408
+ // a deprecated jQuery API just to keep two spellings of one thing.
409
+ $.fn.click = function (handler) { return handler ? this.on('click', handler) : this.trigger('click'); };
410
+ $.fn.keydown = function (handler) { return handler ? this.on('keydown', handler) : this.trigger('keydown'); };
411
+ $.fn.trigger = function (type, data) {
412
+ return this.each(function () {
413
+ var bus = _eventTargetFor(this);
414
+ var ev;
415
+ if (type instanceof Event) {
416
+ // 2026-09-14, real bug found testing a real project end-to-
417
+ // end: the vendored WebWorker helper (vendor/webworker-
418
+ // helper.js) builds `$.Event(name)` — a real Event — then
419
+ // assigns custom properties onto it (e.g. `.worker`) and
420
+ // triggers with THAT OBJECT, not a name string. The old
421
+ // code here did `new Event(type, ...)` regardless, so
422
+ // passing an Event object as `type` got silently coerced
423
+ // to garbage like "[object Event]" by the native Event
424
+ // constructor — no error, just an event nothing ever
425
+ // matched, which is why _csswrk (the scoped-CSS worker,
426
+ // gating renderView() itself) never got past its
427
+ // INITIALIZED state: its own start()/load() sequence is
428
+ // entirely driven by trigger()ing exactly this pattern.
429
+ // Dispatch the existing Event as-is so its custom
430
+ // properties survive.
431
+ ev = type;
432
+ } else if (type && typeof type === 'object' && typeof type.type === 'string') {
433
+ ev = new Event(type.type, { bubbles: _isNode(this) });
434
+ for (var k in type) {
435
+ if (k !== 'type' && Object.prototype.hasOwnProperty.call(type, k)) {
436
+ try { ev[k] = type[k]; } catch (e) { /* read-only Event own-property, e.g. `target` */ }
437
+ }
438
+ }
439
+ } else {
440
+ ev = new Event(type, { bubbles: _isNode(this) });
441
+ }
442
+ if (data !== undefined) ev.detail = data;
443
+ bus.dispatchEvent(ev);
444
+ });
445
+ };
446
+ // Real removal needs the original listener reference, which this shim
447
+ // doesn't track (LumenJS's own model is delegation-first — see
448
+ // lumenjs-spec.md §5.6 — so long-lived per-node listeners needing unbind
449
+ // are already the exception, not the pattern). Kept for API completeness.
450
+ $.fn.unbind = function () { return this; };
451
+
452
+ // jQuery's shorthand event methods (2026-09-14, real gap found testing
453
+ // a real project's vendor code end-to-end — `$(el).keyup(...)` threw
454
+ // "not a function", never caught before because nothing had exercised
455
+ // this vendor code path this far). `.eventname(handler)` binds,
456
+ // `.eventname()` triggers — matching real jQuery exactly.
457
+ ["click", "dblclick", "keyup", "keydown", "keypress", "focus", "blur",
458
+ "change", "submit", "focusin", "focusout", "mouseenter", "mouseleave",
459
+ "mouseover", "mouseout", "mousedown", "mouseup", "mousemove",
460
+ "resize", "scroll", "load", "select"].forEach(function (name) {
461
+ $.fn[name] = function (handler) {
462
+ return handler ? this.on(name, handler) : this.trigger(name);
463
+ };
464
+ });
465
+ $.fn.hover = function (enter, leave) {
466
+ this.on('mouseenter', enter);
467
+ this.on('mouseleave', leave || enter);
468
+ return this;
469
+ };
470
+
471
+ global.$ = $;
472
+ global.jQuery = $;
473
+ })(typeof window !== 'undefined' ? window : globalThis);
@@ -0,0 +1,47 @@
1
+ // index.js bootstrap (2026-09-14). index.js no longer has a <script> tag
2
+ // in index.html (see packages/cli's scaffold + dev-server middleware); its
3
+ // top-level vars are compiled server-side into _vt.Global.vars (walk.js /
4
+ // _re.js), and this is what actually fetches and runs that compiled code
5
+ // in dev.
6
+ //
7
+ // Placement (see build/bundle.js): concatenated right after the WHOLE
8
+ // vendor/reconnecting-websocket.js file, which is where `Reactor` comes
9
+ // from (index.js's compiled code calls it as its last line) — but also
10
+ // where real index.js scripts commonly reference other framework globals
11
+ // defined much later in that same file (`cookies`, `session`, ...), so
12
+ // running only after `Reactor` specifically isn't enough (tried that
13
+ // first, 2026-09-14 — see git history). That vendor file's own
14
+ // $(document).ready(...) handler, which needs Reactor() to have already
15
+ // run (it reads appSettings.Base), is guarded to retry instead of
16
+ // assuming this file has already run by the time it fires — see
17
+ // `_lumenReadyHandler` in that file's own comment for why that guarantee
18
+ // doesn't actually hold (jQuery's ready() can fire synchronously, at
19
+ // registration time, before this file gets a chance to run).
20
+ //
21
+ // Uses console.log directly, not `cl` — this runs after _re.js's own
22
+ // `var cl = console.log;`, so `cl` would work too, but there's no reason
23
+ // to depend on that when a bare console.log is just as simple.
24
+ if (typeof _beaTn === 'undefined' || !_beaTn) {
25
+ if (typeof XMLHttpRequest === 'function') {
26
+ try {
27
+ var _idxXhr = new XMLHttpRequest();
28
+ _idxXhr.open('GET', '/index.js', false); // synchronous — see comment above
29
+ _idxXhr.send(null);
30
+ if (_idxXhr.status >= 200 && _idxXhr.status < 300) {
31
+ // Indirect eval (the comma-expression form), not `new
32
+ // Function`: a `function` executes in its OWN fresh scope,
33
+ // so a top-level `function greet(){}` inside it would
34
+ // become a local variable of that wrapper, not a real
35
+ // global — breaking the documented V1 pattern of calling
36
+ // index.js-declared functions straight from view markup
37
+ // (e.g. `onclick="greet()"`, resolved against window).
38
+ // Indirect eval runs in true global scope instead.
39
+ (0, eval)(_idxXhr.responseText);
40
+ } else {
41
+ console.log('Failed to load index.js: HTTP ' + _idxXhr.status);
42
+ }
43
+ } catch (e) {
44
+ console.log('Failed to load index.js: ', e);
45
+ }
46
+ }
47
+ }