@askrjs/askr 0.0.2 → 0.0.3

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.
@@ -1,11 +1,60 @@
1
1
  import {
2
- Fragment,
3
- init_jsx_runtime
4
- } from "./chunk-KR6HG7HF.js";
2
+ Fragment as Fragment2
3
+ } from "./chunk-SALJX5PZ.js";
5
4
  import {
6
- __esm,
5
+ Fragment,
7
6
  __export
8
- } from "./chunk-QECQ2TF6.js";
7
+ } from "./chunk-JHOGWTAW.js";
8
+
9
+ // src/router/route.ts
10
+ var route_exports = {};
11
+ __export(route_exports, {
12
+ _lockRouteRegistrationForTests: () => _lockRouteRegistrationForTests,
13
+ _unlockRouteRegistrationForTests: () => _unlockRouteRegistrationForTests,
14
+ clearRoutes: () => clearRoutes,
15
+ getLoadedNamespaces: () => getLoadedNamespaces,
16
+ getNamespaceRoutes: () => getNamespaceRoutes,
17
+ getRoutes: () => getRoutes,
18
+ lockRouteRegistration: () => lockRouteRegistration,
19
+ registerRoute: () => registerRoute,
20
+ resolveRoute: () => resolveRoute,
21
+ route: () => route,
22
+ setServerLocation: () => setServerLocation,
23
+ unloadNamespace: () => unloadNamespace
24
+ });
25
+
26
+ // src/router/match.ts
27
+ function match(path, pattern) {
28
+ const normalizedPath = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
29
+ const normalizedPattern = pattern.endsWith("/") && pattern !== "/" ? pattern.slice(0, -1) : pattern;
30
+ const pathSegments = normalizedPath.split("/").filter(Boolean);
31
+ const patternSegments = normalizedPattern.split("/").filter(Boolean);
32
+ if (patternSegments.length === 1 && patternSegments[0] === "*") {
33
+ return {
34
+ matched: true,
35
+ params: {
36
+ "*": pathSegments.length > 1 ? normalizedPath : pathSegments[0]
37
+ }
38
+ };
39
+ }
40
+ if (pathSegments.length !== patternSegments.length) {
41
+ return { matched: false, params: {} };
42
+ }
43
+ const params = {};
44
+ for (let i = 0; i < patternSegments.length; i++) {
45
+ const patternSegment = patternSegments[i];
46
+ const pathSegment = pathSegments[i];
47
+ if (patternSegment.startsWith("{") && patternSegment.endsWith("}")) {
48
+ const paramName = patternSegment.slice(1, -1);
49
+ params[paramName] = decodeURIComponent(pathSegment);
50
+ } else if (patternSegment === "*") {
51
+ params["*"] = pathSegment;
52
+ } else if (patternSegment !== pathSegment) {
53
+ return { matched: false, params: {} };
54
+ }
55
+ }
56
+ return { matched: true, params };
57
+ }
9
58
 
10
59
  // src/dev/invariant.ts
11
60
  function invariant(condition, message, context) {
@@ -17,11 +66,6 @@ function invariant(condition, message, context) {
17
66
  function assertSchedulingPrecondition(condition, violationMessage) {
18
67
  invariant(condition, `[Scheduler Precondition] ${violationMessage}`);
19
68
  }
20
- var init_invariant = __esm({
21
- "src/dev/invariant.ts"() {
22
- "use strict";
23
- }
24
- });
25
69
 
26
70
  // src/dev/logger.ts
27
71
  function callConsole(method, args) {
@@ -35,31 +79,26 @@ function callConsole(method, args) {
35
79
  }
36
80
  }
37
81
  }
38
- var logger;
39
- var init_logger = __esm({
40
- "src/dev/logger.ts"() {
41
- "use strict";
42
- logger = {
43
- debug: (...args) => {
44
- if (process.env.NODE_ENV === "production") return;
45
- callConsole("debug", args);
46
- },
47
- info: (...args) => {
48
- if (process.env.NODE_ENV === "production") return;
49
- callConsole("info", args);
50
- },
51
- warn: (...args) => {
52
- if (process.env.NODE_ENV === "production") return;
53
- callConsole("warn", args);
54
- },
55
- error: (...args) => {
56
- callConsole("error", args);
57
- }
58
- };
82
+ var logger = {
83
+ debug: (...args) => {
84
+ if (process.env.NODE_ENV === "production") return;
85
+ callConsole("debug", args);
86
+ },
87
+ info: (...args) => {
88
+ if (process.env.NODE_ENV === "production") return;
89
+ callConsole("info", args);
90
+ },
91
+ warn: (...args) => {
92
+ if (process.env.NODE_ENV === "production") return;
93
+ callConsole("warn", args);
94
+ },
95
+ error: (...args) => {
96
+ callConsole("error", args);
59
97
  }
60
- });
98
+ };
61
99
 
62
100
  // src/runtime/scheduler.ts
101
+ var MAX_FLUSH_DEPTH = 50;
63
102
  function isBulkCommitActive() {
64
103
  try {
65
104
  const fb = globalThis.__ASKR_FASTLANE;
@@ -69,6 +108,250 @@ function isBulkCommitActive() {
69
108
  return false;
70
109
  }
71
110
  }
111
+ var Scheduler = class {
112
+ constructor() {
113
+ this.q = [];
114
+ this.head = 0;
115
+ this.running = false;
116
+ this.inHandler = false;
117
+ this.depth = 0;
118
+ this.executionDepth = 0;
119
+ // for compat with existing diagnostics
120
+ // Monotonic flush version increments at end of each flush
121
+ this.flushVersion = 0;
122
+ // Best-effort microtask kick scheduling
123
+ this.kickScheduled = false;
124
+ // Escape hatch flag for runWithSyncProgress
125
+ this.allowSyncProgress = false;
126
+ // Waiters waiting for flushVersion >= target
127
+ this.waiters = [];
128
+ // Keep a lightweight taskCount for compatibility/diagnostics
129
+ this.taskCount = 0;
130
+ }
131
+ enqueue(task) {
132
+ assertSchedulingPrecondition(
133
+ typeof task === "function",
134
+ "enqueue() requires a function"
135
+ );
136
+ if (isBulkCommitActive() && !this.allowSyncProgress) {
137
+ if (process.env.NODE_ENV !== "production") {
138
+ throw new Error(
139
+ "[Scheduler] enqueue() during bulk commit (not allowed)"
140
+ );
141
+ }
142
+ return;
143
+ }
144
+ this.q.push(task);
145
+ this.taskCount++;
146
+ if (!this.running && !this.kickScheduled && !this.inHandler && !isBulkCommitActive()) {
147
+ this.kickScheduled = true;
148
+ queueMicrotask(() => {
149
+ this.kickScheduled = false;
150
+ if (this.running) return;
151
+ if (isBulkCommitActive()) return;
152
+ try {
153
+ this.flush();
154
+ } catch (err) {
155
+ setTimeout(() => {
156
+ throw err;
157
+ });
158
+ }
159
+ });
160
+ }
161
+ }
162
+ flush() {
163
+ invariant(
164
+ !this.running,
165
+ "[Scheduler] flush() called while already running"
166
+ );
167
+ if (process.env.NODE_ENV !== "production") {
168
+ if (isBulkCommitActive() && !this.allowSyncProgress) {
169
+ throw new Error(
170
+ "[Scheduler] flush() started during bulk commit (not allowed)"
171
+ );
172
+ }
173
+ }
174
+ this.running = true;
175
+ this.depth = 0;
176
+ let fatal = null;
177
+ try {
178
+ while (this.head < this.q.length) {
179
+ this.depth++;
180
+ if (process.env.NODE_ENV !== "production" && this.depth > MAX_FLUSH_DEPTH) {
181
+ throw new Error(
182
+ `[Scheduler] exceeded MAX_FLUSH_DEPTH (${MAX_FLUSH_DEPTH}). Likely infinite update loop.`
183
+ );
184
+ }
185
+ const task = this.q[this.head++];
186
+ try {
187
+ this.executionDepth++;
188
+ task();
189
+ this.executionDepth--;
190
+ } catch (err) {
191
+ if (this.executionDepth > 0) this.executionDepth = 0;
192
+ fatal = err;
193
+ break;
194
+ }
195
+ if (this.taskCount > 0) this.taskCount--;
196
+ }
197
+ } finally {
198
+ this.running = false;
199
+ this.depth = 0;
200
+ this.executionDepth = 0;
201
+ if (this.head >= this.q.length) {
202
+ this.q.length = 0;
203
+ this.head = 0;
204
+ } else if (this.head > 0) {
205
+ const remaining = this.q.length - this.head;
206
+ if (this.head > 1024 || this.head > remaining) {
207
+ this.q = this.q.slice(this.head);
208
+ } else {
209
+ for (let i = 0; i < remaining; i++) {
210
+ this.q[i] = this.q[this.head + i];
211
+ }
212
+ this.q.length = remaining;
213
+ }
214
+ this.head = 0;
215
+ }
216
+ this.flushVersion++;
217
+ this.resolveWaiters();
218
+ }
219
+ if (fatal) throw fatal;
220
+ }
221
+ runWithSyncProgress(fn) {
222
+ const prev = this.allowSyncProgress;
223
+ this.allowSyncProgress = true;
224
+ const g = globalThis;
225
+ const origQueueMicrotask = g.queueMicrotask;
226
+ const origSetTimeout = g.setTimeout;
227
+ if (process.env.NODE_ENV !== "production") {
228
+ g.queueMicrotask = () => {
229
+ throw new Error(
230
+ "[Scheduler] queueMicrotask not allowed during runWithSyncProgress"
231
+ );
232
+ };
233
+ g.setTimeout = () => {
234
+ throw new Error(
235
+ "[Scheduler] setTimeout not allowed during runWithSyncProgress"
236
+ );
237
+ };
238
+ }
239
+ const startVersion = this.flushVersion;
240
+ try {
241
+ const res = fn();
242
+ if (!this.running && this.q.length - this.head > 0) {
243
+ this.flush();
244
+ }
245
+ if (process.env.NODE_ENV !== "production") {
246
+ if (this.q.length - this.head > 0) {
247
+ throw new Error(
248
+ "[Scheduler] tasks remain after runWithSyncProgress flush"
249
+ );
250
+ }
251
+ }
252
+ return res;
253
+ } finally {
254
+ if (process.env.NODE_ENV !== "production") {
255
+ g.queueMicrotask = origQueueMicrotask;
256
+ g.setTimeout = origSetTimeout;
257
+ }
258
+ try {
259
+ if (this.flushVersion === startVersion) {
260
+ this.flushVersion++;
261
+ this.resolveWaiters();
262
+ }
263
+ } catch (e) {
264
+ void e;
265
+ }
266
+ this.allowSyncProgress = prev;
267
+ }
268
+ }
269
+ waitForFlush(targetVersion, timeoutMs = 2e3) {
270
+ const target = typeof targetVersion === "number" ? targetVersion : this.flushVersion + 1;
271
+ if (this.flushVersion >= target) return Promise.resolve();
272
+ return new Promise((resolve, reject) => {
273
+ const timer = setTimeout(() => {
274
+ const ns = globalThis.__ASKR__ || {};
275
+ const diag = {
276
+ flushVersion: this.flushVersion,
277
+ queueLen: this.q.length - this.head,
278
+ running: this.running,
279
+ inHandler: this.inHandler,
280
+ bulk: isBulkCommitActive(),
281
+ namespace: ns
282
+ };
283
+ reject(
284
+ new Error(
285
+ `waitForFlush timeout ${timeoutMs}ms: ${JSON.stringify(diag)}`
286
+ )
287
+ );
288
+ }, timeoutMs);
289
+ this.waiters.push({ target, resolve, reject, timer });
290
+ });
291
+ }
292
+ getState() {
293
+ return {
294
+ queueLength: this.q.length - this.head,
295
+ running: this.running,
296
+ depth: this.depth,
297
+ executionDepth: this.executionDepth,
298
+ taskCount: this.taskCount,
299
+ flushVersion: this.flushVersion,
300
+ // New fields for optional inspection
301
+ inHandler: this.inHandler,
302
+ allowSyncProgress: this.allowSyncProgress
303
+ };
304
+ }
305
+ setInHandler(v) {
306
+ this.inHandler = v;
307
+ }
308
+ isInHandler() {
309
+ return this.inHandler;
310
+ }
311
+ isExecuting() {
312
+ return this.running || this.executionDepth > 0;
313
+ }
314
+ // Clear pending synchronous tasks (used by fastlane enter/exit)
315
+ clearPendingSyncTasks() {
316
+ const remaining = this.q.length - this.head;
317
+ if (remaining <= 0) return 0;
318
+ if (this.running) {
319
+ this.q.length = this.head;
320
+ this.taskCount = Math.max(0, this.taskCount - remaining);
321
+ queueMicrotask(() => {
322
+ try {
323
+ this.flushVersion++;
324
+ this.resolveWaiters();
325
+ } catch (e) {
326
+ void e;
327
+ }
328
+ });
329
+ return remaining;
330
+ }
331
+ this.q.length = 0;
332
+ this.head = 0;
333
+ this.taskCount = Math.max(0, this.taskCount - remaining);
334
+ this.flushVersion++;
335
+ this.resolveWaiters();
336
+ return remaining;
337
+ }
338
+ resolveWaiters() {
339
+ if (this.waiters.length === 0) return;
340
+ const ready = [];
341
+ const remaining = [];
342
+ for (const w of this.waiters) {
343
+ if (this.flushVersion >= w.target) {
344
+ if (w.timer) clearTimeout(w.timer);
345
+ ready.push(w.resolve);
346
+ } else {
347
+ remaining.push(w);
348
+ }
349
+ }
350
+ this.waiters = remaining;
351
+ for (const r of ready) r();
352
+ }
353
+ };
354
+ var globalScheduler = new Scheduler();
72
355
  function isSchedulerExecuting() {
73
356
  return globalScheduler.isExecuting();
74
357
  }
@@ -96,261 +379,11 @@ function scheduleEventHandler(handler) {
96
379
  }
97
380
  };
98
381
  }
99
- var MAX_FLUSH_DEPTH, Scheduler, globalScheduler;
100
- var init_scheduler = __esm({
101
- "src/runtime/scheduler.ts"() {
102
- "use strict";
103
- init_invariant();
104
- init_logger();
105
- MAX_FLUSH_DEPTH = 50;
106
- Scheduler = class {
107
- constructor() {
108
- this.q = [];
109
- this.head = 0;
110
- this.running = false;
111
- this.inHandler = false;
112
- this.depth = 0;
113
- this.executionDepth = 0;
114
- // for compat with existing diagnostics
115
- // Monotonic flush version increments at end of each flush
116
- this.flushVersion = 0;
117
- // Best-effort microtask kick scheduling
118
- this.kickScheduled = false;
119
- // Escape hatch flag for runWithSyncProgress
120
- this.allowSyncProgress = false;
121
- // Waiters waiting for flushVersion >= target
122
- this.waiters = [];
123
- // Keep a lightweight taskCount for compatibility/diagnostics
124
- this.taskCount = 0;
125
- }
126
- enqueue(task) {
127
- assertSchedulingPrecondition(
128
- typeof task === "function",
129
- "enqueue() requires a function"
130
- );
131
- if (isBulkCommitActive() && !this.allowSyncProgress) {
132
- if (process.env.NODE_ENV !== "production") {
133
- throw new Error(
134
- "[Scheduler] enqueue() during bulk commit (not allowed)"
135
- );
136
- }
137
- return;
138
- }
139
- this.q.push(task);
140
- this.taskCount++;
141
- if (!this.running && !this.kickScheduled && !this.inHandler && !isBulkCommitActive()) {
142
- this.kickScheduled = true;
143
- queueMicrotask(() => {
144
- this.kickScheduled = false;
145
- if (this.running) return;
146
- if (isBulkCommitActive()) return;
147
- try {
148
- this.flush();
149
- } catch (err) {
150
- setTimeout(() => {
151
- throw err;
152
- });
153
- }
154
- });
155
- }
156
- }
157
- flush() {
158
- invariant(
159
- !this.running,
160
- "[Scheduler] flush() called while already running"
161
- );
162
- if (process.env.NODE_ENV !== "production") {
163
- if (isBulkCommitActive() && !this.allowSyncProgress) {
164
- throw new Error(
165
- "[Scheduler] flush() started during bulk commit (not allowed)"
166
- );
167
- }
168
- }
169
- this.running = true;
170
- this.depth = 0;
171
- let fatal = null;
172
- try {
173
- while (this.head < this.q.length) {
174
- this.depth++;
175
- if (process.env.NODE_ENV !== "production" && this.depth > MAX_FLUSH_DEPTH) {
176
- throw new Error(
177
- `[Scheduler] exceeded MAX_FLUSH_DEPTH (${MAX_FLUSH_DEPTH}). Likely infinite update loop.`
178
- );
179
- }
180
- const task = this.q[this.head++];
181
- try {
182
- this.executionDepth++;
183
- task();
184
- this.executionDepth--;
185
- } catch (err) {
186
- if (this.executionDepth > 0) this.executionDepth = 0;
187
- fatal = err;
188
- break;
189
- }
190
- if (this.taskCount > 0) this.taskCount--;
191
- }
192
- } finally {
193
- this.running = false;
194
- this.depth = 0;
195
- this.executionDepth = 0;
196
- if (this.head >= this.q.length) {
197
- this.q.length = 0;
198
- this.head = 0;
199
- } else if (this.head > 0) {
200
- const remaining = this.q.length - this.head;
201
- if (this.head > 1024 || this.head > remaining) {
202
- this.q = this.q.slice(this.head);
203
- } else {
204
- for (let i = 0; i < remaining; i++) {
205
- this.q[i] = this.q[this.head + i];
206
- }
207
- this.q.length = remaining;
208
- }
209
- this.head = 0;
210
- }
211
- this.flushVersion++;
212
- this.resolveWaiters();
213
- }
214
- if (fatal) throw fatal;
215
- }
216
- runWithSyncProgress(fn) {
217
- const prev = this.allowSyncProgress;
218
- this.allowSyncProgress = true;
219
- const g = globalThis;
220
- const origQueueMicrotask = g.queueMicrotask;
221
- const origSetTimeout = g.setTimeout;
222
- if (process.env.NODE_ENV !== "production") {
223
- g.queueMicrotask = () => {
224
- throw new Error(
225
- "[Scheduler] queueMicrotask not allowed during runWithSyncProgress"
226
- );
227
- };
228
- g.setTimeout = () => {
229
- throw new Error(
230
- "[Scheduler] setTimeout not allowed during runWithSyncProgress"
231
- );
232
- };
233
- }
234
- const startVersion = this.flushVersion;
235
- try {
236
- const res = fn();
237
- if (!this.running && this.q.length - this.head > 0) {
238
- this.flush();
239
- }
240
- if (process.env.NODE_ENV !== "production") {
241
- if (this.q.length - this.head > 0) {
242
- throw new Error(
243
- "[Scheduler] tasks remain after runWithSyncProgress flush"
244
- );
245
- }
246
- }
247
- return res;
248
- } finally {
249
- if (process.env.NODE_ENV !== "production") {
250
- g.queueMicrotask = origQueueMicrotask;
251
- g.setTimeout = origSetTimeout;
252
- }
253
- try {
254
- if (this.flushVersion === startVersion) {
255
- this.flushVersion++;
256
- this.resolveWaiters();
257
- }
258
- } catch (e) {
259
- void e;
260
- }
261
- this.allowSyncProgress = prev;
262
- }
263
- }
264
- waitForFlush(targetVersion, timeoutMs = 2e3) {
265
- const target = typeof targetVersion === "number" ? targetVersion : this.flushVersion + 1;
266
- if (this.flushVersion >= target) return Promise.resolve();
267
- return new Promise((resolve, reject) => {
268
- const timer = setTimeout(() => {
269
- const ns = globalThis.__ASKR__ || {};
270
- const diag = {
271
- flushVersion: this.flushVersion,
272
- queueLen: this.q.length - this.head,
273
- running: this.running,
274
- inHandler: this.inHandler,
275
- bulk: isBulkCommitActive(),
276
- namespace: ns
277
- };
278
- reject(
279
- new Error(
280
- `waitForFlush timeout ${timeoutMs}ms: ${JSON.stringify(diag)}`
281
- )
282
- );
283
- }, timeoutMs);
284
- this.waiters.push({ target, resolve, reject, timer });
285
- });
286
- }
287
- getState() {
288
- return {
289
- queueLength: this.q.length - this.head,
290
- running: this.running,
291
- depth: this.depth,
292
- executionDepth: this.executionDepth,
293
- taskCount: this.taskCount,
294
- flushVersion: this.flushVersion,
295
- // New fields for optional inspection
296
- inHandler: this.inHandler,
297
- allowSyncProgress: this.allowSyncProgress
298
- };
299
- }
300
- setInHandler(v) {
301
- this.inHandler = v;
302
- }
303
- isInHandler() {
304
- return this.inHandler;
305
- }
306
- isExecuting() {
307
- return this.running || this.executionDepth > 0;
308
- }
309
- // Clear pending synchronous tasks (used by fastlane enter/exit)
310
- clearPendingSyncTasks() {
311
- const remaining = this.q.length - this.head;
312
- if (remaining <= 0) return 0;
313
- if (this.running) {
314
- this.q.length = this.head;
315
- this.taskCount = Math.max(0, this.taskCount - remaining);
316
- queueMicrotask(() => {
317
- try {
318
- this.flushVersion++;
319
- this.resolveWaiters();
320
- } catch (e) {
321
- void e;
322
- }
323
- });
324
- return remaining;
325
- }
326
- this.q.length = 0;
327
- this.head = 0;
328
- this.taskCount = Math.max(0, this.taskCount - remaining);
329
- this.flushVersion++;
330
- this.resolveWaiters();
331
- return remaining;
332
- }
333
- resolveWaiters() {
334
- if (this.waiters.length === 0) return;
335
- const ready = [];
336
- const remaining = [];
337
- for (const w of this.waiters) {
338
- if (this.flushVersion >= w.target) {
339
- if (w.timer) clearTimeout(w.timer);
340
- ready.push(w.resolve);
341
- } else {
342
- remaining.push(w);
343
- }
344
- }
345
- this.waiters = remaining;
346
- for (const r of ready) r();
347
- }
348
- };
349
- globalScheduler = new Scheduler();
350
- }
351
- });
352
382
 
353
383
  // src/runtime/context.ts
384
+ var CONTEXT_FRAME_SYMBOL = /* @__PURE__ */ Symbol("__tempoContextFrame__");
385
+ var currentContextFrame = null;
386
+ var currentAsyncResourceFrame = null;
354
387
  function withContext(frame, fn) {
355
388
  const oldFrame = currentContextFrame;
356
389
  currentContextFrame = frame;
@@ -469,16 +502,6 @@ function ContextFunctionChildInvoker(props) {
469
502
  function getCurrentContextFrame() {
470
503
  return currentContextFrame;
471
504
  }
472
- var CONTEXT_FRAME_SYMBOL, currentContextFrame, currentAsyncResourceFrame;
473
- var init_context = __esm({
474
- "src/runtime/context.ts"() {
475
- "use strict";
476
- init_component();
477
- CONTEXT_FRAME_SYMBOL = /* @__PURE__ */ Symbol("__tempoContextFrame__");
478
- currentContextFrame = null;
479
- currentAsyncResourceFrame = null;
480
- }
481
- });
482
505
 
483
506
  // src/renderer/diag/index.ts
484
507
  function getDiagMap() {
@@ -536,13 +559,51 @@ function __ASKR_incCounter(key) {
536
559
  void e;
537
560
  }
538
561
  }
539
- var init_diag = __esm({
540
- "src/renderer/diag/index.ts"() {
541
- "use strict";
562
+
563
+ // src/runtime/dev-namespace.ts
564
+ function getDevNamespace() {
565
+ if (process.env.NODE_ENV === "production") return {};
566
+ try {
567
+ const g = globalThis;
568
+ if (!g.__ASKR__) g.__ASKR__ = {};
569
+ return g.__ASKR__;
570
+ } catch {
571
+ return {};
542
572
  }
543
- });
573
+ }
574
+ function setDevValue(key, value) {
575
+ if (process.env.NODE_ENV === "production") return;
576
+ try {
577
+ getDevNamespace()[key] = value;
578
+ } catch {
579
+ }
580
+ }
581
+ function getDevValue(key) {
582
+ if (process.env.NODE_ENV === "production") return void 0;
583
+ try {
584
+ return getDevNamespace()[key];
585
+ } catch {
586
+ return void 0;
587
+ }
588
+ }
544
589
 
545
590
  // src/runtime/fastlane-shared.ts
591
+ var _bulkCommitActive = false;
592
+ var _appliedParents = null;
593
+ function enterBulkCommit() {
594
+ _bulkCommitActive = true;
595
+ _appliedParents = /* @__PURE__ */ new WeakSet();
596
+ try {
597
+ const cleared = globalScheduler.clearPendingSyncTasks?.() ?? 0;
598
+ setDevValue("__ASKR_FASTLANE_CLEARED_TASKS", cleared);
599
+ } catch (err) {
600
+ if (process.env.NODE_ENV !== "production") throw err;
601
+ }
602
+ }
603
+ function exitBulkCommit() {
604
+ _bulkCommitActive = false;
605
+ _appliedParents = null;
606
+ }
546
607
  function isBulkCommitActive2() {
547
608
  return _bulkCommitActive;
548
609
  }
@@ -554,95 +615,62 @@ function markFastPathApplied(parent) {
554
615
  void e;
555
616
  }
556
617
  }
557
- var _bulkCommitActive, _appliedParents;
558
- var init_fastlane_shared = __esm({
559
- "src/runtime/fastlane-shared.ts"() {
560
- "use strict";
561
- _bulkCommitActive = false;
562
- _appliedParents = null;
563
- }
564
- });
618
+ function isFastPathApplied(parent) {
619
+ return !!(_appliedParents && _appliedParents.has(parent));
620
+ }
565
621
 
566
622
  // src/renderer/types.ts
567
623
  function _isDOMElement(node) {
568
624
  return typeof node === "object" && node !== null && "type" in node;
569
625
  }
570
- var init_types = __esm({
571
- "src/renderer/types.ts"() {
572
- "use strict";
573
- }
574
- });
575
626
 
576
627
  // src/renderer/cleanup.ts
577
- function cleanupInstanceIfPresent(node, opts) {
578
- if (!node) return;
579
- if (!(node instanceof Element)) return;
580
- const errors = [];
628
+ function cleanupSingleInstance(node, errors, strict) {
629
+ const inst = node.__ASKR_INSTANCE;
630
+ if (!inst) return;
581
631
  try {
582
- const inst = node.__ASKR_INSTANCE;
583
- if (inst) {
584
- try {
585
- cleanupComponent(inst);
586
- } catch (err) {
587
- if (opts?.strict) errors.push(err);
588
- else if (process.env.NODE_ENV !== "production")
589
- logger.warn("[Askr] cleanupComponent failed:", err);
590
- }
591
- try {
592
- delete node.__ASKR_INSTANCE;
593
- } catch (e) {
594
- if (opts?.strict) errors.push(e);
595
- else void e;
596
- }
597
- }
632
+ cleanupComponent(inst);
598
633
  } catch (err) {
599
- if (opts?.strict) errors.push(err);
600
- else if (process.env.NODE_ENV !== "production") {
601
- logger.warn("[Askr] cleanupInstanceIfPresent failed:", err);
602
- }
634
+ if (strict) errors.push(err);
635
+ else logger.warn("[Askr] cleanupComponent failed:", err);
636
+ }
637
+ try {
638
+ delete node.__ASKR_INSTANCE;
639
+ } catch (e) {
640
+ if (strict) errors.push(e);
641
+ }
642
+ }
643
+ function cleanupInstanceIfPresent(node, opts) {
644
+ if (!node || !(node instanceof Element)) return;
645
+ const errors = [];
646
+ const strict = opts?.strict ?? false;
647
+ try {
648
+ cleanupSingleInstance(node, errors, strict);
649
+ } catch (err) {
650
+ if (strict) errors.push(err);
651
+ else logger.warn("[Askr] cleanupInstanceIfPresent failed:", err);
603
652
  }
604
653
  try {
605
654
  const descendants = node.querySelectorAll("*");
606
655
  for (const d of Array.from(descendants)) {
607
656
  try {
608
- const inst = d.__ASKR_INSTANCE;
609
- if (inst) {
610
- try {
611
- cleanupComponent(inst);
612
- } catch (err) {
613
- if (opts?.strict) errors.push(err);
614
- else if (process.env.NODE_ENV !== "production") {
615
- logger.warn(
616
- "[Askr] cleanupInstanceIfPresent descendant cleanup failed:",
617
- err
618
- );
619
- }
620
- }
621
- try {
622
- delete d.__ASKR_INSTANCE;
623
- } catch (e) {
624
- if (opts?.strict) errors.push(e);
625
- else void e;
626
- }
627
- }
657
+ cleanupSingleInstance(d, errors, strict);
628
658
  } catch (err) {
629
- if (opts?.strict) errors.push(err);
630
- else if (process.env.NODE_ENV !== "production") {
659
+ if (strict) errors.push(err);
660
+ else
631
661
  logger.warn(
632
662
  "[Askr] cleanupInstanceIfPresent descendant cleanup failed:",
633
663
  err
634
664
  );
635
- }
636
665
  }
637
666
  }
638
667
  } catch (err) {
639
- if (opts?.strict) errors.push(err);
640
- else if (process.env.NODE_ENV !== "production") {
668
+ if (strict) errors.push(err);
669
+ else
641
670
  logger.warn(
642
671
  "[Askr] cleanupInstanceIfPresent descendant query failed:",
643
672
  err
644
673
  );
645
- }
646
674
  }
647
675
  if (errors.length > 0) {
648
676
  throw new AggregateError(errors, "cleanupInstanceIfPresent failed");
@@ -651,6 +679,7 @@ function cleanupInstanceIfPresent(node, opts) {
651
679
  function cleanupInstancesUnder(node, opts) {
652
680
  cleanupInstanceIfPresent(node, opts);
653
681
  }
682
+ var elementListeners = /* @__PURE__ */ new WeakMap();
654
683
  function removeElementListeners(element) {
655
684
  const map = elementListeners.get(element);
656
685
  if (map) {
@@ -670,34 +699,217 @@ function removeAllListeners(root) {
670
699
  removeElementListeners(children[i]);
671
700
  }
672
701
  }
673
- var elementListeners;
674
- var init_cleanup = __esm({
675
- "src/renderer/cleanup.ts"() {
676
- "use strict";
677
- init_component();
678
- init_logger();
679
- elementListeners = /* @__PURE__ */ new WeakMap();
702
+
703
+ // src/renderer/utils.ts
704
+ function parseEventName(propName) {
705
+ if (!propName.startsWith("on") || propName.length <= 2) return null;
706
+ return propName.slice(2).charAt(0).toLowerCase() + propName.slice(3).toLowerCase();
707
+ }
708
+ function getPassiveOptions(eventName) {
709
+ if (eventName === "wheel" || eventName === "scroll" || eventName.startsWith("touch")) {
710
+ return { passive: true };
680
711
  }
681
- });
712
+ return void 0;
713
+ }
714
+ function createWrappedHandler(handler, flushAfter = false) {
715
+ return (event) => {
716
+ globalScheduler.setInHandler(true);
717
+ try {
718
+ handler(event);
719
+ } catch (error) {
720
+ logger.error("[Askr] Event handler error:", error);
721
+ } finally {
722
+ globalScheduler.setInHandler(false);
723
+ if (flushAfter) {
724
+ const state = globalScheduler.getState();
725
+ if ((state.queueLength ?? 0) > 0 && !state.running) {
726
+ queueMicrotask(() => {
727
+ try {
728
+ if (!globalScheduler.isExecuting()) globalScheduler.flush();
729
+ } catch (err) {
730
+ queueMicrotask(() => {
731
+ throw err;
732
+ });
733
+ }
734
+ });
735
+ }
736
+ }
737
+ }
738
+ };
739
+ }
740
+ function isSkippedProp(key) {
741
+ return key === "children" || key === "key";
742
+ }
743
+ function isIgnoredForPropChanges(key) {
744
+ if (key === "children" || key === "key") return true;
745
+ if (key.startsWith("on") && key.length > 2) return true;
746
+ if (key.startsWith("data-")) return true;
747
+ return false;
748
+ }
749
+ function hasPropChanged(el, key, value) {
750
+ try {
751
+ if (key === "class" || key === "className") {
752
+ return el.className !== String(value);
753
+ }
754
+ if (key === "value" || key === "checked") {
755
+ return el[key] !== value;
756
+ }
757
+ const attr = el.getAttribute(key);
758
+ if (value === void 0 || value === null || value === false) {
759
+ return attr !== null;
760
+ }
761
+ return String(value) !== attr;
762
+ } catch {
763
+ return true;
764
+ }
765
+ }
766
+ function hasNonTrivialProps(props) {
767
+ for (const k of Object.keys(props)) {
768
+ if (isIgnoredForPropChanges(k)) continue;
769
+ return true;
770
+ }
771
+ return false;
772
+ }
773
+ function checkPropChanges(el, props) {
774
+ for (const k of Object.keys(props)) {
775
+ if (isIgnoredForPropChanges(k)) continue;
776
+ if (hasPropChanged(el, k, props[k])) {
777
+ return true;
778
+ }
779
+ }
780
+ return false;
781
+ }
782
+ function extractKey(vnode) {
783
+ if (typeof vnode !== "object" || vnode === null) return void 0;
784
+ const obj = vnode;
785
+ const rawKey = obj.key ?? obj.props?.key;
786
+ if (rawKey === void 0) return void 0;
787
+ return typeof rawKey === "symbol" ? String(rawKey) : rawKey;
788
+ }
789
+ function buildKeyMapFromChildren(parent) {
790
+ const map = /* @__PURE__ */ new Map();
791
+ const children = Array.from(parent.children);
792
+ for (const ch of children) {
793
+ const k = ch.getAttribute("data-key");
794
+ if (k !== null) {
795
+ map.set(k, ch);
796
+ const n = Number(k);
797
+ if (!Number.isNaN(n)) map.set(n, ch);
798
+ }
799
+ }
800
+ return map;
801
+ }
802
+ function recordDOMReplace(source) {
803
+ try {
804
+ __ASKR_incCounter("__DOM_REPLACE_COUNT");
805
+ __ASKR_set(`__LAST_DOM_REPLACE_STACK_${source}`, new Error().stack);
806
+ } catch {
807
+ }
808
+ }
809
+ function recordFastPathStats(stats, counterName) {
810
+ try {
811
+ __ASKR_set("__LAST_FASTPATH_STATS", stats);
812
+ __ASKR_set("__LAST_FASTPATH_COMMIT_COUNT", 1);
813
+ if (counterName) {
814
+ __ASKR_incCounter(counterName);
815
+ }
816
+ } catch {
817
+ }
818
+ }
819
+ function logFastPathDebug(message, indexOrData, data) {
820
+ if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
821
+ if (data !== void 0) {
822
+ logger.warn(`[Askr][FASTPATH] ${message}`, indexOrData, data);
823
+ } else if (indexOrData !== void 0) {
824
+ logger.warn(`[Askr][FASTPATH] ${message}`, indexOrData);
825
+ } else {
826
+ logger.warn(`[Askr][FASTPATH] ${message}`);
827
+ }
828
+ }
829
+ }
830
+ function now() {
831
+ return typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
832
+ }
682
833
 
683
834
  // src/renderer/keyed.ts
835
+ var keyedElements = /* @__PURE__ */ new WeakMap();
684
836
  function getKeyMapForElement(el) {
685
837
  return keyedElements.get(el);
686
838
  }
687
- function isKeyedReorderFastPathEligible(parent, newChildren, oldKeyMap) {
688
- const keyedVnodes = [];
689
- for (let i = 0; i < newChildren.length; i++) {
690
- const child = newChildren[i];
691
- if (typeof child === "object" && child !== null && "type" in child) {
692
- const childObj = child;
693
- if (childObj.key !== void 0) {
694
- keyedVnodes.push({
695
- key: childObj.key,
696
- vnode: child
697
- });
839
+ function populateKeyMapForElement(parent) {
840
+ try {
841
+ if (keyedElements.has(parent)) return;
842
+ let domMap = buildKeyMapFromChildren(parent);
843
+ if (domMap.size === 0) {
844
+ domMap = /* @__PURE__ */ new Map();
845
+ const children = Array.from(parent.children);
846
+ for (const ch of children) {
847
+ const text = (ch.textContent || "").trim();
848
+ if (text) {
849
+ domMap.set(text, ch);
850
+ const n = Number(text);
851
+ if (!Number.isNaN(n)) domMap.set(n, ch);
852
+ }
853
+ }
854
+ }
855
+ if (domMap.size > 0) keyedElements.set(parent, domMap);
856
+ } catch {
857
+ }
858
+ }
859
+ var _reconcilerRecordedParents = /* @__PURE__ */ new WeakSet();
860
+ function extractKeyedVnodes(newChildren) {
861
+ const result = [];
862
+ for (const child of newChildren) {
863
+ const key = extractKey(child);
864
+ if (key !== void 0) {
865
+ result.push({ key, vnode: child });
866
+ }
867
+ }
868
+ return result;
869
+ }
870
+ function computeLISLength(positions) {
871
+ const tails = [];
872
+ for (const pos of positions) {
873
+ if (pos === -1) continue;
874
+ let lo = 0;
875
+ let hi = tails.length;
876
+ while (lo < hi) {
877
+ const mid = lo + hi >> 1;
878
+ if (tails[mid] < pos) lo = mid + 1;
879
+ else hi = mid;
880
+ }
881
+ if (lo === tails.length) tails.push(pos);
882
+ else tails[lo] = pos;
883
+ }
884
+ return tails.length;
885
+ }
886
+ function checkVnodesHaveProps(keyedVnodes) {
887
+ for (const { vnode } of keyedVnodes) {
888
+ if (typeof vnode !== "object" || vnode === null) continue;
889
+ const vnodeObj = vnode;
890
+ if (vnodeObj.props && hasNonTrivialProps(vnodeObj.props)) {
891
+ return true;
892
+ }
893
+ }
894
+ return false;
895
+ }
896
+ function checkVnodePropChanges(keyedVnodes, oldKeyMap) {
897
+ for (const { key, vnode } of keyedVnodes) {
898
+ const el = oldKeyMap?.get(key);
899
+ if (!el || typeof vnode !== "object" || vnode === null) continue;
900
+ const vnodeObj = vnode;
901
+ const props = vnodeObj.props || {};
902
+ for (const k of Object.keys(props)) {
903
+ if (isIgnoredForPropChanges(k)) continue;
904
+ if (hasPropChanged(el, k, props[k])) {
905
+ return true;
698
906
  }
699
907
  }
700
908
  }
909
+ return false;
910
+ }
911
+ function isKeyedReorderFastPathEligible(parent, newChildren, oldKeyMap) {
912
+ const keyedVnodes = extractKeyedVnodes(newChildren);
701
913
  const totalKeyed = keyedVnodes.length;
702
914
  const newKeyOrder = keyedVnodes.map((kv) => kv.key);
703
915
  const oldKeyOrder = oldKeyMap ? Array.from(oldKeyMap.keys()) : [];
@@ -718,89 +930,15 @@ function isKeyedReorderFastPathEligible(parent, newChildren, oldKeyMap) {
718
930
  let lisLen = 0;
719
931
  if (totalKeyed >= 128) {
720
932
  const parentChildren = Array.from(parent.children);
721
- const positions = new Array(keyedVnodes.length).fill(-1);
722
- for (let i = 0; i < keyedVnodes.length; i++) {
723
- const key = keyedVnodes[i].key;
933
+ const positions = keyedVnodes.map(({ key }) => {
724
934
  const el = oldKeyMap?.get(key);
725
- if (el && el.parentElement === parent) {
726
- positions[i] = parentChildren.indexOf(el);
727
- }
728
- }
729
- const tails = [];
730
- for (let i = 0; i < positions.length; i++) {
731
- const pos = positions[i];
732
- if (pos === -1) continue;
733
- let lo = 0;
734
- let hi = tails.length;
735
- while (lo < hi) {
736
- const mid = lo + hi >> 1;
737
- if (tails[mid] < pos) lo = mid + 1;
738
- else hi = mid;
739
- }
740
- if (lo === tails.length) tails.push(pos);
741
- else tails[lo] = pos;
742
- }
743
- lisLen = tails.length;
935
+ return el?.parentElement === parent ? parentChildren.indexOf(el) : -1;
936
+ });
937
+ lisLen = computeLISLength(positions);
744
938
  lisTrigger = lisLen < Math.floor(totalKeyed * 0.5);
745
939
  }
746
- let hasPropsPresent = false;
747
- for (let i = 0; i < keyedVnodes.length; i++) {
748
- const vnode = keyedVnodes[i].vnode;
749
- if (typeof vnode !== "object" || vnode === null) continue;
750
- const vnodeObj = vnode;
751
- const props = vnodeObj.props || {};
752
- for (const k of Object.keys(props)) {
753
- if (k === "children" || k === "key") continue;
754
- if (k.startsWith("on") && k.length > 2) continue;
755
- if (k.startsWith("data-")) continue;
756
- hasPropsPresent = true;
757
- break;
758
- }
759
- if (hasPropsPresent) break;
760
- }
761
- let hasPropChanges = false;
762
- for (let i = 0; i < keyedVnodes.length; i++) {
763
- const { key, vnode } = keyedVnodes[i];
764
- const el = oldKeyMap?.get(key);
765
- if (!el || typeof vnode !== "object" || vnode === null) continue;
766
- const vnodeObj = vnode;
767
- const props = vnodeObj.props || {};
768
- for (const k of Object.keys(props)) {
769
- if (k === "children" || k === "key") continue;
770
- if (k.startsWith("on") && k.length > 2) continue;
771
- if (k.startsWith("data-")) continue;
772
- const v = props[k];
773
- try {
774
- if (k === "class" || k === "className") {
775
- if (el.className !== String(v)) {
776
- hasPropChanges = true;
777
- break;
778
- }
779
- } else if (k === "value" || k === "checked") {
780
- if (el[k] !== v) {
781
- hasPropChanges = true;
782
- break;
783
- }
784
- } else {
785
- const attr = el.getAttribute(k);
786
- if (v === void 0 || v === null || v === false) {
787
- if (attr !== null) {
788
- hasPropChanges = true;
789
- break;
790
- }
791
- } else if (String(v) !== attr) {
792
- hasPropChanges = true;
793
- break;
794
- }
795
- }
796
- } catch (e) {
797
- hasPropChanges = true;
798
- void e;
799
- break;
800
- }
801
- }
802
- if (hasPropChanges) break;
803
- }
940
+ const hasPropsPresent = checkVnodesHaveProps(keyedVnodes);
941
+ const hasPropChanges = checkVnodePropChanges(keyedVnodes, oldKeyMap);
804
942
  const useFastPath = (cheapMoveTrigger || lisTrigger) && !hasPropChanges && !hasPropsPresent;
805
943
  return {
806
944
  useFastPath,
@@ -810,23 +948,103 @@ function isKeyedReorderFastPathEligible(parent, newChildren, oldKeyMap) {
810
948
  hasPropChanges
811
949
  };
812
950
  }
813
- var keyedElements, _reconcilerRecordedParents;
814
- var init_keyed = __esm({
815
- "src/renderer/keyed.ts"() {
816
- "use strict";
817
- keyedElements = /* @__PURE__ */ new WeakMap();
818
- _reconcilerRecordedParents = /* @__PURE__ */ new WeakSet();
819
- }
820
- });
821
951
 
822
952
  // src/renderer/dom.ts
953
+ var IS_DOM_AVAILABLE = typeof document !== "undefined";
954
+ function addTrackedListener(el, eventName, handler) {
955
+ const wrappedHandler = createWrappedHandler(handler, true);
956
+ const options = getPassiveOptions(eventName);
957
+ if (options !== void 0) {
958
+ el.addEventListener(eventName, wrappedHandler, options);
959
+ } else {
960
+ el.addEventListener(eventName, wrappedHandler);
961
+ }
962
+ if (!elementListeners.has(el)) {
963
+ elementListeners.set(el, /* @__PURE__ */ new Map());
964
+ }
965
+ elementListeners.get(el).set(eventName, {
966
+ handler: wrappedHandler,
967
+ original: handler,
968
+ options
969
+ });
970
+ }
971
+ function applyPropsToElement(el, props, tagName) {
972
+ for (const key in props) {
973
+ const value = props[key];
974
+ if (isSkippedProp(key)) continue;
975
+ if (value === void 0 || value === null || value === false) continue;
976
+ const eventName = parseEventName(key);
977
+ if (eventName) {
978
+ addTrackedListener(el, eventName, value);
979
+ continue;
980
+ }
981
+ if (key === "class" || key === "className") {
982
+ el.className = String(value);
983
+ } else if (key === "value" || key === "checked") {
984
+ applyFormControlProp(el, key, value, tagName);
985
+ } else {
986
+ el.setAttribute(key, String(value));
987
+ }
988
+ }
989
+ }
990
+ function applyFormControlProp(el, key, value, tagName) {
991
+ const tag = tagName.toLowerCase();
992
+ if (key === "value") {
993
+ if (tag === "input" || tag === "textarea" || tag === "select") {
994
+ el.value = String(value);
995
+ el.setAttribute("value", String(value));
996
+ } else {
997
+ el.setAttribute("value", String(value));
998
+ }
999
+ } else if (key === "checked") {
1000
+ if (tag === "input") {
1001
+ el.checked = Boolean(value);
1002
+ el.setAttribute("checked", String(Boolean(value)));
1003
+ } else {
1004
+ el.setAttribute("checked", String(Boolean(value)));
1005
+ }
1006
+ }
1007
+ }
1008
+ function materializeKey(el, vnode, props) {
1009
+ const vnodeKey = vnode.key ?? props?.key;
1010
+ if (vnodeKey !== void 0) {
1011
+ el.setAttribute("data-key", String(vnodeKey));
1012
+ }
1013
+ }
1014
+ function warnMissingKeys(children) {
1015
+ if (process.env.NODE_ENV === "production") return;
1016
+ let hasElements = false;
1017
+ let hasKeys = false;
1018
+ for (const item of children) {
1019
+ if (typeof item === "object" && item !== null && "type" in item) {
1020
+ hasElements = true;
1021
+ const rawKey = item.key ?? item.props?.key;
1022
+ if (rawKey !== void 0) {
1023
+ hasKeys = true;
1024
+ break;
1025
+ }
1026
+ }
1027
+ }
1028
+ if (hasElements && !hasKeys) {
1029
+ try {
1030
+ const inst = getCurrentInstance();
1031
+ const name = inst?.fn?.name || "<anonymous>";
1032
+ logger.warn(
1033
+ `Missing keys on dynamic lists in ${name}. Each child in a list should have a unique "key" prop.`
1034
+ );
1035
+ } catch {
1036
+ logger.warn(
1037
+ 'Missing keys on dynamic lists. Each child in a list should have a unique "key" prop.'
1038
+ );
1039
+ }
1040
+ }
1041
+ }
823
1042
  function createDOMNode(node) {
824
1043
  if (!IS_DOM_AVAILABLE) {
825
1044
  if (process.env.NODE_ENV !== "production") {
826
1045
  try {
827
1046
  logger.warn("[Askr] createDOMNode called in non-DOM environment");
828
- } catch (e) {
829
- void e;
1047
+ } catch {
830
1048
  }
831
1049
  }
832
1050
  return null;
@@ -842,8 +1060,8 @@ function createDOMNode(node) {
842
1060
  }
843
1061
  if (Array.isArray(node)) {
844
1062
  const fragment = document.createDocumentFragment();
845
- for (let i = 0; i < node.length; i++) {
846
- const dom = createDOMNode(node[i]);
1063
+ for (const child of node) {
1064
+ const dom = createDOMNode(child);
847
1065
  if (dom) fragment.appendChild(dom);
848
1066
  }
849
1067
  return fragment;
@@ -852,212 +1070,124 @@ function createDOMNode(node) {
852
1070
  const type = node.type;
853
1071
  const props = node.props || {};
854
1072
  if (typeof type === "string") {
855
- const el = document.createElement(type);
856
- for (const key in props) {
857
- const value = props[key];
858
- if (key === "children" || key === "key") continue;
859
- if (value === void 0 || value === null || value === false) continue;
860
- if (key.startsWith("on") && key.length > 2) {
861
- const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
862
- const wrappedHandler = (event) => {
863
- globalScheduler.setInHandler(true);
864
- try {
865
- value(event);
866
- } catch (error) {
867
- logger.error("[Askr] Event handler error:", error);
868
- } finally {
869
- globalScheduler.setInHandler(false);
870
- const state = globalScheduler.getState();
871
- if ((state.queueLength ?? 0) > 0 && !state.running) {
872
- queueMicrotask(() => {
873
- try {
874
- if (!globalScheduler.isExecuting()) globalScheduler.flush();
875
- } catch (err) {
876
- queueMicrotask(() => {
877
- throw err;
878
- });
879
- }
880
- });
881
- }
882
- }
883
- };
884
- const options = eventName === "wheel" || eventName === "scroll" || eventName.startsWith("touch") ? { passive: true } : void 0;
885
- if (options !== void 0)
886
- el.addEventListener(eventName, wrappedHandler, options);
887
- else el.addEventListener(eventName, wrappedHandler);
888
- if (!elementListeners.has(el)) {
889
- elementListeners.set(el, /* @__PURE__ */ new Map());
890
- }
891
- elementListeners.get(el).set(eventName, {
892
- handler: wrappedHandler,
893
- original: value,
894
- options
895
- });
896
- } else if (key === "class" || key === "className") {
897
- el.className = String(value);
898
- } else if (key === "value" || key === "checked") {
899
- const tag = type.toLowerCase();
900
- if (key === "value") {
901
- if (tag === "input" || tag === "textarea" || tag === "select") {
902
- el.value = String(value);
903
- el.setAttribute("value", String(value));
904
- } else {
905
- el.setAttribute("value", String(value));
906
- }
907
- } else {
908
- if (tag === "input") {
909
- el.checked = Boolean(value);
910
- el.setAttribute("checked", String(Boolean(value)));
911
- } else {
912
- el.setAttribute("checked", String(Boolean(value)));
913
- }
914
- }
915
- } else {
916
- el.setAttribute(key, String(value));
917
- }
918
- }
919
- const vnodeKey = node.key ?? props?.key;
920
- if (vnodeKey !== void 0) {
921
- el.setAttribute("data-key", String(vnodeKey));
922
- }
923
- const children = props.children || node.children;
924
- if (children) {
925
- if (Array.isArray(children)) {
926
- if (process.env.NODE_ENV !== "production") {
927
- let hasElements = false;
928
- let hasKeys = false;
929
- for (let i = 0; i < children.length; i++) {
930
- const item = children[i];
931
- if (typeof item === "object" && item !== null && "type" in item) {
932
- hasElements = true;
933
- const itemProps = item.props || {};
934
- if ("key" in itemProps) {
935
- hasKeys = true;
936
- break;
937
- }
938
- }
939
- }
940
- if (hasElements && !hasKeys) {
941
- if (typeof console !== "undefined") {
942
- try {
943
- const inst = getCurrentInstance();
944
- const name = inst?.fn?.name || "<anonymous>";
945
- logger.warn(
946
- `Missing keys on dynamic lists in ${name}. Each child in a list should have a unique "key" prop.`
947
- );
948
- } catch (e) {
949
- logger.warn(
950
- 'Missing keys on dynamic lists. Each child in a list should have a unique "key" prop.'
951
- );
952
- void e;
953
- }
954
- }
955
- }
956
- }
957
- for (let i = 0; i < children.length; i++) {
958
- const dom = createDOMNode(children[i]);
959
- if (dom) el.appendChild(dom);
960
- }
961
- } else {
962
- const dom = createDOMNode(children);
963
- if (dom) el.appendChild(dom);
964
- }
965
- }
966
- return el;
1073
+ return createIntrinsicElement(node, type, props);
967
1074
  }
968
1075
  if (typeof type === "function") {
969
- const frame = node[CONTEXT_FRAME_SYMBOL];
970
- const snapshot = frame || getCurrentContextFrame();
971
- const componentFn = type;
972
- const isAsync = componentFn.constructor.name === "AsyncFunction";
973
- if (isAsync) {
974
- throw new Error(
975
- "Async components are not supported. Use resource() for async work."
976
- );
977
- }
978
- const vnodeAny = node;
979
- let childInstance = vnodeAny.__instance;
980
- if (!childInstance) {
981
- childInstance = createComponentInstance(
982
- `comp-${Math.random().toString(36).slice(2, 7)}`,
983
- componentFn,
984
- props || {},
985
- null
986
- );
987
- vnodeAny.__instance = childInstance;
988
- }
989
- if (snapshot) {
990
- childInstance.ownerFrame = snapshot;
991
- }
992
- const result = withContext(
993
- snapshot,
994
- () => renderComponentInline(childInstance)
995
- );
996
- if (result instanceof Promise) {
997
- throw new Error(
998
- "Async components are not supported. Components must return synchronously."
999
- );
1076
+ return createComponentElement(node, type, props);
1077
+ }
1078
+ if (typeof type === "symbol" && (type === Fragment2 || String(type) === "Symbol(Fragment)")) {
1079
+ return createFragmentElement(node, props);
1080
+ }
1081
+ }
1082
+ return null;
1083
+ }
1084
+ function createIntrinsicElement(node, type, props) {
1085
+ const el = document.createElement(type);
1086
+ materializeKey(el, node, props);
1087
+ applyPropsToElement(el, props, type);
1088
+ const children = props.children || node.children;
1089
+ if (children) {
1090
+ if (Array.isArray(children)) {
1091
+ warnMissingKeys(children);
1092
+ for (const child of children) {
1093
+ const dom = createDOMNode(child);
1094
+ if (dom) el.appendChild(dom);
1000
1095
  }
1001
- const dom = withContext(snapshot, () => createDOMNode(result));
1002
- if (dom instanceof Element) {
1003
- mountInstanceInline(childInstance, dom);
1004
- return dom;
1005
- }
1006
- const host = document.createElement("div");
1007
- if (dom instanceof DocumentFragment) {
1008
- host.appendChild(dom);
1009
- } else if (dom) {
1010
- host.appendChild(dom);
1011
- }
1012
- mountInstanceInline(childInstance, host);
1013
- return host;
1014
- }
1015
- if (typeof type === "symbol" && (type === Fragment || String(type) === "Symbol(Fragment)")) {
1016
- const fragment = document.createDocumentFragment();
1017
- const children = props.children || node.children;
1018
- if (children) {
1019
- if (Array.isArray(children)) {
1020
- for (let i = 0; i < children.length; i++) {
1021
- const dom = createDOMNode(children[i]);
1022
- if (dom) fragment.appendChild(dom);
1023
- }
1024
- } else {
1025
- const dom = createDOMNode(children);
1026
- if (dom) fragment.appendChild(dom);
1027
- }
1096
+ } else {
1097
+ const dom = createDOMNode(children);
1098
+ if (dom) el.appendChild(dom);
1099
+ }
1100
+ }
1101
+ return el;
1102
+ }
1103
+ function createComponentElement(node, type, props) {
1104
+ const frame = node[CONTEXT_FRAME_SYMBOL];
1105
+ const snapshot = frame || getCurrentContextFrame();
1106
+ const componentFn = type;
1107
+ const isAsync = componentFn.constructor.name === "AsyncFunction";
1108
+ if (isAsync) {
1109
+ throw new Error(
1110
+ "Async components are not supported. Use resource() for async work."
1111
+ );
1112
+ }
1113
+ let childInstance = node.__instance;
1114
+ if (!childInstance) {
1115
+ childInstance = createComponentInstance(
1116
+ `comp-${Math.random().toString(36).slice(2, 7)}`,
1117
+ componentFn,
1118
+ props || {},
1119
+ null
1120
+ );
1121
+ node.__instance = childInstance;
1122
+ }
1123
+ if (snapshot) {
1124
+ childInstance.ownerFrame = snapshot;
1125
+ }
1126
+ const result = withContext(
1127
+ snapshot,
1128
+ () => renderComponentInline(childInstance)
1129
+ );
1130
+ if (result instanceof Promise) {
1131
+ throw new Error(
1132
+ "Async components are not supported. Components must return synchronously."
1133
+ );
1134
+ }
1135
+ const dom = withContext(snapshot, () => createDOMNode(result));
1136
+ if (dom instanceof Element) {
1137
+ mountInstanceInline(childInstance, dom);
1138
+ return dom;
1139
+ }
1140
+ if (!dom) {
1141
+ const placeholder = document.createComment("");
1142
+ childInstance._placeholder = placeholder;
1143
+ childInstance.mounted = true;
1144
+ childInstance.notifyUpdate = childInstance._enqueueRun;
1145
+ return placeholder;
1146
+ }
1147
+ const host = document.createElement("div");
1148
+ host.appendChild(dom);
1149
+ mountInstanceInline(childInstance, host);
1150
+ return host;
1151
+ }
1152
+ function createFragmentElement(node, props) {
1153
+ const fragment = document.createDocumentFragment();
1154
+ const children = props.children || node.children;
1155
+ if (children) {
1156
+ if (Array.isArray(children)) {
1157
+ for (const child of children) {
1158
+ const dom = createDOMNode(child);
1159
+ if (dom) fragment.appendChild(dom);
1028
1160
  }
1029
- return fragment;
1161
+ } else {
1162
+ const dom = createDOMNode(children);
1163
+ if (dom) fragment.appendChild(dom);
1030
1164
  }
1031
1165
  }
1032
- return null;
1166
+ return fragment;
1033
1167
  }
1034
1168
  function updateElementFromVnode(el, vnode, updateChildren = true) {
1035
1169
  if (!_isDOMElement(vnode)) {
1036
1170
  return;
1037
1171
  }
1038
1172
  const props = vnode.props || {};
1039
- const vnodeKey = vnode.key ?? vnode.props?.key;
1040
- if (vnodeKey !== void 0) {
1041
- el.setAttribute("data-key", String(vnodeKey));
1042
- }
1173
+ materializeKey(el, vnode, props);
1043
1174
  const existingListeners = elementListeners.get(el);
1044
1175
  const desiredEventNames = /* @__PURE__ */ new Set();
1045
1176
  for (const key in props) {
1046
1177
  const value = props[key];
1047
- if (key === "children" || key === "key") continue;
1178
+ if (isSkippedProp(key)) continue;
1179
+ const eventName = parseEventName(key);
1048
1180
  if (value === void 0 || value === null || value === false) {
1049
1181
  if (key === "class" || key === "className") {
1050
1182
  el.className = "";
1051
- } else if (key.startsWith("on") && key.length > 2) {
1052
- const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
1053
- if (existingListeners && existingListeners.has(eventName)) {
1054
- const entry = existingListeners.get(eventName);
1055
- if (entry.options !== void 0)
1056
- el.removeEventListener(eventName, entry.handler, entry.options);
1057
- else el.removeEventListener(eventName, entry.handler);
1058
- existingListeners.delete(eventName);
1183
+ } else if (eventName && existingListeners?.has(eventName)) {
1184
+ const entry = existingListeners.get(eventName);
1185
+ if (entry.options !== void 0) {
1186
+ el.removeEventListener(eventName, entry.handler, entry.options);
1187
+ } else {
1188
+ el.removeEventListener(eventName, entry.handler);
1059
1189
  }
1060
- continue;
1190
+ existingListeners.delete(eventName);
1061
1191
  } else {
1062
1192
  el.removeAttribute(key);
1063
1193
  }
@@ -1067,8 +1197,7 @@ function updateElementFromVnode(el, vnode, updateChildren = true) {
1067
1197
  el.className = String(value);
1068
1198
  } else if (key === "value" || key === "checked") {
1069
1199
  el[key] = value;
1070
- } else if (key.startsWith("on") && key.length > 2) {
1071
- const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
1200
+ } else if (eventName) {
1072
1201
  desiredEventNames.add(eventName);
1073
1202
  const existing = existingListeners?.get(eventName);
1074
1203
  if (existing && existing.original === value) {
@@ -1077,20 +1206,16 @@ function updateElementFromVnode(el, vnode, updateChildren = true) {
1077
1206
  if (existing) {
1078
1207
  el.removeEventListener(eventName, existing.handler);
1079
1208
  }
1080
- const wrappedHandler = (event) => {
1081
- globalScheduler.setInHandler(true);
1082
- try {
1083
- value(event);
1084
- } catch (error) {
1085
- logger.error("[Askr] Event handler error:", error);
1086
- } finally {
1087
- globalScheduler.setInHandler(false);
1088
- }
1089
- };
1090
- const options = eventName === "wheel" || eventName === "scroll" || eventName.startsWith("touch") ? { passive: true } : void 0;
1091
- if (options !== void 0)
1209
+ const wrappedHandler = createWrappedHandler(
1210
+ value,
1211
+ false
1212
+ );
1213
+ const options = getPassiveOptions(eventName);
1214
+ if (options !== void 0) {
1092
1215
  el.addEventListener(eventName, wrappedHandler, options);
1093
- else el.addEventListener(eventName, wrappedHandler);
1216
+ } else {
1217
+ el.addEventListener(eventName, wrappedHandler);
1218
+ }
1094
1219
  if (!elementListeners.has(el)) {
1095
1220
  elementListeners.set(el, /* @__PURE__ */ new Map());
1096
1221
  }
@@ -1105,8 +1230,8 @@ function updateElementFromVnode(el, vnode, updateChildren = true) {
1105
1230
  }
1106
1231
  if (existingListeners) {
1107
1232
  for (const eventName of existingListeners.keys()) {
1108
- const entry = existingListeners.get(eventName);
1109
1233
  if (!desiredEventNames.has(eventName)) {
1234
+ const entry = existingListeners.get(eventName);
1110
1235
  el.removeEventListener(eventName, entry.handler);
1111
1236
  existingListeners.delete(eventName);
1112
1237
  }
@@ -1141,6 +1266,14 @@ function updateElementChildren(el, children) {
1141
1266
  }
1142
1267
  function updateUnkeyedChildren(parent, newChildren) {
1143
1268
  const existing = Array.from(parent.children);
1269
+ if (newChildren.length === 1 && existing.length === 0 && parent.childNodes.length === 1) {
1270
+ const firstNewChild = newChildren[0];
1271
+ const firstExisting = parent.firstChild;
1272
+ if ((typeof firstNewChild === "string" || typeof firstNewChild === "number") && firstExisting?.nodeType === 3) {
1273
+ firstExisting.data = String(firstNewChild);
1274
+ return;
1275
+ }
1276
+ }
1144
1277
  if (existing.length === 0 && parent.childNodes.length > 0) {
1145
1278
  parent.textContent = "";
1146
1279
  }
@@ -1195,7 +1328,7 @@ function performBulkPositionalKeyedTextUpdate(parent, keyedVnodes) {
1195
1328
  const total = keyedVnodes.length;
1196
1329
  let reused = 0;
1197
1330
  let updatedKeys = 0;
1198
- const t0 = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
1331
+ const t0 = now();
1199
1332
  for (let i = 0; i < total; i++) {
1200
1333
  const { key, vnode } = keyedVnodes[i];
1201
1334
  const ch = parent.children[i];
@@ -1203,202 +1336,209 @@ function performBulkPositionalKeyedTextUpdate(parent, keyedVnodes) {
1203
1336
  const vnodeType = vnode.type;
1204
1337
  if (ch.tagName.toLowerCase() === vnodeType.toLowerCase()) {
1205
1338
  const children = vnode.children || vnode.props?.children;
1206
- try {
1207
- if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
1208
- logger.warn("[Askr][FASTPATH] positional idx", i, {
1209
- chTag: ch.tagName.toLowerCase(),
1210
- vnodeType,
1211
- chChildNodes: ch.childNodes.length,
1212
- childrenType: Array.isArray(children) ? "array" : typeof children
1213
- });
1214
- }
1215
- } catch (e) {
1216
- void e;
1217
- }
1218
- if (typeof children === "string" || typeof children === "number") {
1219
- if (ch.childNodes.length === 1 && ch.firstChild?.nodeType === 3) {
1220
- ch.firstChild.data = String(children);
1221
- } else {
1222
- ch.textContent = String(children);
1223
- }
1224
- } else if (Array.isArray(children) && children.length === 1 && (typeof children[0] === "string" || typeof children[0] === "number")) {
1225
- if (ch.childNodes.length === 1 && ch.firstChild?.nodeType === 3) {
1226
- ch.firstChild.data = String(children[0]);
1227
- } else {
1228
- ch.textContent = String(children[0]);
1229
- }
1230
- } else {
1231
- updateElementFromVnode(ch, vnode);
1232
- }
1233
- try {
1234
- ch.setAttribute("data-key", String(key));
1235
- updatedKeys++;
1236
- } catch (e) {
1237
- void e;
1238
- }
1339
+ logFastPathDebug("positional idx", i, {
1340
+ chTag: ch.tagName.toLowerCase(),
1341
+ vnodeType,
1342
+ chChildNodes: ch.childNodes.length,
1343
+ childrenType: Array.isArray(children) ? "array" : typeof children
1344
+ });
1345
+ updateTextContent(ch, children);
1346
+ setDataKey(ch, key, () => updatedKeys++);
1239
1347
  reused++;
1240
1348
  continue;
1241
1349
  } else {
1242
- try {
1243
- if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
1244
- logger.warn("[Askr][FASTPATH] positional tag mismatch", i, {
1245
- chTag: ch.tagName.toLowerCase(),
1246
- vnodeType
1247
- });
1248
- }
1249
- } catch (e) {
1250
- void e;
1251
- }
1350
+ logFastPathDebug("positional tag mismatch", i, {
1351
+ chTag: ch.tagName.toLowerCase(),
1352
+ vnodeType
1353
+ });
1252
1354
  }
1253
1355
  } else {
1254
- try {
1255
- if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
1256
- logger.warn("[Askr][FASTPATH] positional missing or invalid", i, {
1257
- ch: !!ch
1258
- });
1259
- }
1260
- } catch (e) {
1261
- void e;
1262
- }
1356
+ logFastPathDebug("positional missing or invalid", i, { ch: !!ch });
1263
1357
  }
1264
- const dom = createDOMNode(vnode);
1265
- if (dom) {
1266
- const existing = parent.children[i];
1267
- if (existing) {
1268
- cleanupInstanceIfPresent(existing);
1269
- parent.replaceChild(dom, existing);
1270
- } else parent.appendChild(dom);
1358
+ replaceNodeAtPosition(parent, i, vnode);
1359
+ }
1360
+ const t = now() - t0;
1361
+ updateKeyedElementsMap(parent, keyedVnodes);
1362
+ const stats = { n: total, reused, updatedKeys, t };
1363
+ recordFastPathStats(stats, "bulkKeyedPositionalHits");
1364
+ return stats;
1365
+ }
1366
+ function updateTextContent(el, children) {
1367
+ if (typeof children === "string" || typeof children === "number") {
1368
+ setTextNodeData(el, String(children));
1369
+ } else if (Array.isArray(children) && children.length === 1 && (typeof children[0] === "string" || typeof children[0] === "number")) {
1370
+ setTextNodeData(el, String(children[0]));
1371
+ } else {
1372
+ updateElementFromVnode(el, el);
1373
+ }
1374
+ }
1375
+ function setTextNodeData(el, text) {
1376
+ if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
1377
+ el.firstChild.data = text;
1378
+ } else {
1379
+ el.textContent = text;
1380
+ }
1381
+ }
1382
+ function setDataKey(el, key, onSet) {
1383
+ try {
1384
+ el.setAttribute("data-key", String(key));
1385
+ onSet();
1386
+ } catch {
1387
+ }
1388
+ }
1389
+ function replaceNodeAtPosition(parent, index, vnode) {
1390
+ const dom = createDOMNode(vnode);
1391
+ if (dom) {
1392
+ const existing = parent.children[index];
1393
+ if (existing) {
1394
+ cleanupInstanceIfPresent(existing);
1395
+ parent.replaceChild(dom, existing);
1396
+ } else {
1397
+ parent.appendChild(dom);
1271
1398
  }
1272
1399
  }
1273
- const t = typeof performance !== "undefined" && performance.now ? performance.now() - t0 : 0;
1400
+ }
1401
+ function updateKeyedElementsMap(parent, keyedVnodes) {
1274
1402
  try {
1275
1403
  const newKeyMap = /* @__PURE__ */ new Map();
1276
- for (let i = 0; i < total; i++) {
1404
+ for (let i = 0; i < keyedVnodes.length; i++) {
1277
1405
  const k = keyedVnodes[i].key;
1278
1406
  const ch = parent.children[i];
1279
1407
  if (ch) newKeyMap.set(k, ch);
1280
1408
  }
1281
1409
  keyedElements.set(parent, newKeyMap);
1282
- } catch (e) {
1283
- void e;
1284
- }
1285
- const stats = { n: total, reused, updatedKeys, t };
1286
- try {
1287
- if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
1288
- logger.warn("[Askr][FASTPATH] bulk positional stats", stats);
1289
- }
1290
- __ASKR_set("__LAST_FASTPATH_STATS", stats);
1291
- __ASKR_set("__LAST_FASTPATH_COMMIT_COUNT", 1);
1292
- __ASKR_incCounter("bulkKeyedPositionalHits");
1293
- } catch (e) {
1294
- void e;
1410
+ } catch {
1295
1411
  }
1296
- return stats;
1297
1412
  }
1298
1413
  function performBulkTextReplace(parent, newChildren) {
1299
- const t0 = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
1414
+ const t0 = now();
1300
1415
  const existing = Array.from(parent.childNodes);
1301
1416
  const finalNodes = [];
1302
1417
  let reused = 0;
1303
1418
  let created = 0;
1304
1419
  for (let i = 0; i < newChildren.length; i++) {
1305
- const vnode = newChildren[i];
1306
- const existingNode = existing[i];
1307
- if (typeof vnode === "string" || typeof vnode === "number") {
1308
- const text = String(vnode);
1309
- if (existingNode && existingNode.nodeType === 3) {
1310
- existingNode.data = text;
1311
- finalNodes.push(existingNode);
1312
- reused++;
1313
- } else {
1314
- finalNodes.push(document.createTextNode(text));
1315
- created++;
1316
- }
1317
- continue;
1318
- }
1319
- if (typeof vnode === "object" && vnode !== null && "type" in vnode) {
1320
- const vnodeObj = vnode;
1321
- if (typeof vnodeObj.type === "string") {
1322
- const tag = vnodeObj.type;
1323
- if (existingNode && existingNode.nodeType === 1 && existingNode.tagName.toLowerCase() === tag.toLowerCase()) {
1324
- updateElementFromVnode(existingNode, vnode);
1325
- finalNodes.push(existingNode);
1326
- reused++;
1327
- continue;
1328
- }
1329
- }
1330
- const dom = createDOMNode(vnode);
1331
- if (dom) {
1332
- finalNodes.push(dom);
1333
- created++;
1334
- continue;
1335
- }
1420
+ const result = processChildNode(newChildren[i], existing[i], finalNodes);
1421
+ if (result === "reused") reused++;
1422
+ else if (result === "created") created++;
1423
+ }
1424
+ const tBuild = now() - t0;
1425
+ cleanupRemovedNodes(parent, finalNodes);
1426
+ const tCommit = commitBulkReplace(parent, finalNodes);
1427
+ keyedElements.delete(parent);
1428
+ const stats = {
1429
+ n: newChildren.length,
1430
+ reused,
1431
+ created,
1432
+ tBuild,
1433
+ tCommit
1434
+ };
1435
+ recordBulkTextStats(stats);
1436
+ return stats;
1437
+ }
1438
+ function processChildNode(vnode, existingNode, finalNodes) {
1439
+ if (typeof vnode === "string" || typeof vnode === "number") {
1440
+ return processTextVnode(String(vnode), existingNode, finalNodes);
1441
+ }
1442
+ if (typeof vnode === "object" && vnode !== null && "type" in vnode) {
1443
+ return processElementVnode(vnode, existingNode, finalNodes);
1444
+ }
1445
+ return "skipped";
1446
+ }
1447
+ function processTextVnode(text, existingNode, finalNodes) {
1448
+ if (existingNode && existingNode.nodeType === 3) {
1449
+ existingNode.data = text;
1450
+ finalNodes.push(existingNode);
1451
+ return "reused";
1452
+ }
1453
+ finalNodes.push(document.createTextNode(text));
1454
+ return "created";
1455
+ }
1456
+ function processElementVnode(vnode, existingNode, finalNodes) {
1457
+ const vnodeObj = vnode;
1458
+ if (typeof vnodeObj.type === "string") {
1459
+ const tag = vnodeObj.type;
1460
+ if (existingNode && existingNode.nodeType === 1 && existingNode.tagName.toLowerCase() === tag.toLowerCase()) {
1461
+ updateElementFromVnode(existingNode, vnode);
1462
+ finalNodes.push(existingNode);
1463
+ return "reused";
1336
1464
  }
1337
1465
  }
1338
- const tBuild = (typeof performance !== "undefined" && performance.now ? performance.now() : Date.now()) - t0;
1466
+ const dom = createDOMNode(vnode);
1467
+ if (dom) {
1468
+ finalNodes.push(dom);
1469
+ return "created";
1470
+ }
1471
+ return "skipped";
1472
+ }
1473
+ function cleanupRemovedNodes(parent, keepNodes) {
1339
1474
  try {
1340
1475
  const toRemove = Array.from(parent.childNodes).filter(
1341
- (n) => !finalNodes.includes(n)
1476
+ (n) => !keepNodes.includes(n)
1342
1477
  );
1343
1478
  for (const n of toRemove) {
1344
1479
  if (n instanceof Element) removeAllListeners(n);
1345
1480
  cleanupInstanceIfPresent(n);
1346
1481
  }
1347
- } catch (e) {
1348
- void e;
1482
+ } catch {
1349
1483
  }
1484
+ }
1485
+ function commitBulkReplace(parent, nodes) {
1350
1486
  const fragStart = Date.now();
1351
1487
  const fragment = document.createDocumentFragment();
1352
- for (let i = 0; i < finalNodes.length; i++)
1353
- fragment.appendChild(finalNodes[i]);
1354
- try {
1355
- __ASKR_incCounter("__DOM_REPLACE_COUNT");
1356
- __ASKR_set("__LAST_DOM_REPLACE_STACK_DOM", new Error().stack);
1357
- } catch (e) {
1358
- void e;
1488
+ for (let i = 0; i < nodes.length; i++) {
1489
+ fragment.appendChild(nodes[i]);
1359
1490
  }
1491
+ recordDOMReplace("bulk-text-replace");
1360
1492
  parent.replaceChildren(fragment);
1361
- const tCommit = Date.now() - fragStart;
1362
- keyedElements.delete(parent);
1363
- const stats = {
1364
- n: newChildren.length,
1365
- reused,
1366
- created,
1367
- tBuild,
1368
- tCommit
1369
- };
1493
+ return Date.now() - fragStart;
1494
+ }
1495
+ function recordBulkTextStats(stats) {
1370
1496
  try {
1371
1497
  __ASKR_set("__LAST_BULK_TEXT_FASTPATH_STATS", stats);
1372
1498
  __ASKR_set("__LAST_FASTPATH_STATS", stats);
1373
1499
  __ASKR_set("__LAST_FASTPATH_COMMIT_COUNT", 1);
1374
1500
  __ASKR_incCounter("bulkTextFastpathHits");
1375
- } catch (e) {
1376
- void e;
1501
+ } catch {
1377
1502
  }
1378
- return stats;
1379
1503
  }
1380
1504
  function isBulkTextFastPathEligible(parent, newChildren) {
1381
1505
  const threshold = Number(process.env.ASKR_BULK_TEXT_THRESHOLD) || 1024;
1382
1506
  const requiredFraction = 0.8;
1383
1507
  const total = Array.isArray(newChildren) ? newChildren.length : 0;
1384
1508
  if (total < threshold) {
1385
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1386
- try {
1387
- __ASKR_set("__BULK_DIAG", {
1388
- phase: "bulk-unkeyed-eligible",
1389
- reason: "too-small",
1390
- total,
1391
- threshold
1392
- });
1393
- } catch (e) {
1394
- void e;
1395
- }
1396
- }
1509
+ recordBulkDiag({
1510
+ phase: "bulk-unkeyed-eligible",
1511
+ reason: "too-small",
1512
+ total,
1513
+ threshold
1514
+ });
1515
+ return false;
1516
+ }
1517
+ const result = countSimpleChildren(newChildren);
1518
+ if (result.componentFound !== void 0) {
1519
+ recordBulkDiag({
1520
+ phase: "bulk-unkeyed-eligible",
1521
+ reason: "component-child",
1522
+ index: result.componentFound
1523
+ });
1397
1524
  return false;
1398
1525
  }
1526
+ const fraction = result.simple / total;
1527
+ const eligible = fraction >= requiredFraction && parent.childNodes.length >= total;
1528
+ recordBulkDiag({
1529
+ phase: "bulk-unkeyed-eligible",
1530
+ total,
1531
+ simple: result.simple,
1532
+ fraction,
1533
+ requiredFraction,
1534
+ eligible
1535
+ });
1536
+ return eligible;
1537
+ }
1538
+ function countSimpleChildren(children) {
1399
1539
  let simple = 0;
1400
- for (let i = 0; i < newChildren.length; i++) {
1401
- const c = newChildren[i];
1540
+ for (let i = 0; i < children.length; i++) {
1541
+ const c = children[i];
1402
1542
  if (typeof c === "string" || typeof c === "number") {
1403
1543
  simple++;
1404
1544
  continue;
@@ -1406,71 +1546,34 @@ function isBulkTextFastPathEligible(parent, newChildren) {
1406
1546
  if (typeof c === "object" && c !== null && "type" in c) {
1407
1547
  const dv = c;
1408
1548
  if (typeof dv.type === "function") {
1409
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1410
- try {
1411
- __ASKR_set("__BULK_DIAG", {
1412
- phase: "bulk-unkeyed-eligible",
1413
- reason: "component-child",
1414
- index: i
1415
- });
1416
- } catch (e) {
1417
- void e;
1418
- }
1419
- }
1420
- return false;
1549
+ return { simple, componentFound: i };
1421
1550
  }
1422
- if (typeof dv.type === "string") {
1423
- const children = dv.children || dv.props?.children;
1424
- if (!children) {
1425
- simple++;
1426
- continue;
1427
- }
1428
- if (Array.isArray(children)) {
1429
- if (children.length === 1 && (typeof children[0] === "string" || typeof children[0] === "number")) {
1430
- simple++;
1431
- continue;
1432
- }
1433
- } else if (typeof children === "string" || typeof children === "number") {
1434
- simple++;
1435
- continue;
1436
- }
1551
+ if (typeof dv.type === "string" && isSimpleElement(dv)) {
1552
+ simple++;
1437
1553
  }
1438
1554
  }
1439
1555
  }
1440
- const fraction = simple / total;
1441
- const eligible = fraction >= requiredFraction && parent.childNodes.length >= total;
1556
+ return { simple };
1557
+ }
1558
+ function isSimpleElement(dv) {
1559
+ const children = dv.children || dv.props?.children;
1560
+ if (!children) return true;
1561
+ if (typeof children === "string" || typeof children === "number") {
1562
+ return true;
1563
+ }
1564
+ if (Array.isArray(children) && children.length === 1 && (typeof children[0] === "string" || typeof children[0] === "number")) {
1565
+ return true;
1566
+ }
1567
+ return false;
1568
+ }
1569
+ function recordBulkDiag(data) {
1442
1570
  if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1443
1571
  try {
1444
- __ASKR_set("__BULK_DIAG", {
1445
- phase: "bulk-unkeyed-eligible",
1446
- total,
1447
- simple,
1448
- fraction,
1449
- requiredFraction,
1450
- eligible
1451
- });
1452
- } catch (e) {
1453
- void e;
1572
+ __ASKR_set("__BULK_DIAG", data);
1573
+ } catch {
1454
1574
  }
1455
1575
  }
1456
- return eligible;
1457
1576
  }
1458
- var IS_DOM_AVAILABLE;
1459
- var init_dom = __esm({
1460
- "src/renderer/dom.ts"() {
1461
- "use strict";
1462
- init_scheduler();
1463
- init_logger();
1464
- init_jsx_runtime();
1465
- init_context();
1466
- init_component();
1467
- init_cleanup();
1468
- init_diag();
1469
- init_types();
1470
- init_keyed();
1471
- IS_DOM_AVAILABLE = typeof document !== "undefined";
1472
- }
1473
- });
1474
1577
 
1475
1578
  // src/renderer/fastpath.ts
1476
1579
  function applyRendererFastPath(parent, keyedVnodes, oldKeyMap, unkeyedVnodes) {
@@ -1635,320 +1738,571 @@ function applyRendererFastPath(parent, keyedVnodes, oldKeyMap, unkeyedVnodes) {
1635
1738
  return null;
1636
1739
  }
1637
1740
  }
1638
- var init_fastpath = __esm({
1639
- "src/renderer/fastpath.ts"() {
1640
- "use strict";
1641
- init_dom();
1642
- init_keyed();
1643
- init_logger();
1644
- init_cleanup();
1645
- init_diag();
1646
- init_scheduler();
1647
- init_fastlane_shared();
1648
- }
1649
- });
1650
1741
 
1651
1742
  // src/renderer/reconcile.ts
1652
1743
  function reconcileKeyedChildren(parent, newChildren, oldKeyMap) {
1653
- const newKeyMap = /* @__PURE__ */ new Map();
1744
+ logReconcileDebug(newChildren);
1745
+ const { keyedVnodes, unkeyedVnodes } = partitionChildren(newChildren);
1746
+ const fastPathResult = tryFastPaths(
1747
+ parent,
1748
+ newChildren,
1749
+ keyedVnodes,
1750
+ unkeyedVnodes,
1751
+ oldKeyMap
1752
+ );
1753
+ if (fastPathResult) return fastPathResult;
1754
+ return performFullReconciliation(parent, newChildren, keyedVnodes, oldKeyMap);
1755
+ }
1756
+ function logReconcileDebug(newChildren) {
1757
+ if (process.env.NODE_ENV !== "production") {
1758
+ try {
1759
+ logger.warn(
1760
+ "[Askr][RECONCILE] reconcileKeyedChildren newChildren sample",
1761
+ {
1762
+ sample: newChildren && newChildren.length && newChildren[0] || null,
1763
+ len: newChildren.length
1764
+ }
1765
+ );
1766
+ } catch {
1767
+ }
1768
+ }
1769
+ }
1770
+ function partitionChildren(newChildren) {
1654
1771
  const keyedVnodes = [];
1655
1772
  const unkeyedVnodes = [];
1656
1773
  for (let i = 0; i < newChildren.length; i++) {
1657
1774
  const child = newChildren[i];
1658
- if (typeof child === "object" && child !== null && "type" in child) {
1659
- const childObj = child;
1660
- const rawKey = childObj.key ?? childObj.props?.key;
1661
- if (rawKey !== void 0) {
1662
- const key = typeof rawKey === "symbol" ? String(rawKey) : rawKey;
1663
- keyedVnodes.push({ key, vnode: child });
1664
- } else {
1665
- unkeyedVnodes.push(child);
1666
- }
1775
+ const key = extractKey(child);
1776
+ if (key !== void 0) {
1777
+ keyedVnodes.push({ key, vnode: child });
1667
1778
  } else {
1668
1779
  unkeyedVnodes.push(child);
1669
1780
  }
1670
1781
  }
1782
+ return { keyedVnodes, unkeyedVnodes };
1783
+ }
1784
+ function tryFastPaths(parent, newChildren, keyedVnodes, unkeyedVnodes, oldKeyMap) {
1671
1785
  try {
1672
- const decision = isKeyedReorderFastPathEligible(
1786
+ const rendererResult = tryRendererFastPath(
1673
1787
  parent,
1674
1788
  newChildren,
1789
+ keyedVnodes,
1790
+ unkeyedVnodes,
1675
1791
  oldKeyMap
1676
1792
  );
1677
- if (decision.useFastPath && keyedVnodes.length >= 128 || // If we're executing inside a runtime bulk commit (fastlane), prefer the
1678
- // renderer fast-path to ensure the single-commit invariant is preserved.
1679
- isBulkCommitActive2()) {
1680
- try {
1681
- const map = applyRendererFastPath(
1682
- parent,
1683
- keyedVnodes,
1684
- oldKeyMap,
1685
- unkeyedVnodes
1686
- );
1687
- if (map) {
1688
- try {
1689
- keyedElements.set(parent, map);
1690
- } catch (e) {
1691
- void e;
1692
- }
1693
- return map;
1694
- }
1695
- } catch (e) {
1696
- void e;
1793
+ if (rendererResult) return rendererResult;
1794
+ const positionalResult = tryPositionalBulkUpdate(parent, keyedVnodes);
1795
+ if (positionalResult) return positionalResult;
1796
+ } catch {
1797
+ }
1798
+ return null;
1799
+ }
1800
+ function tryRendererFastPath(parent, newChildren, keyedVnodes, unkeyedVnodes, oldKeyMap) {
1801
+ const decision = isKeyedReorderFastPathEligible(
1802
+ parent,
1803
+ newChildren,
1804
+ oldKeyMap
1805
+ );
1806
+ if (decision.useFastPath && keyedVnodes.length >= 128 || isBulkCommitActive2()) {
1807
+ try {
1808
+ const map = applyRendererFastPath(
1809
+ parent,
1810
+ keyedVnodes,
1811
+ oldKeyMap,
1812
+ unkeyedVnodes
1813
+ );
1814
+ if (map) {
1815
+ keyedElements.set(parent, map);
1816
+ return map;
1817
+ }
1818
+ } catch {
1819
+ }
1820
+ }
1821
+ return null;
1822
+ }
1823
+ function tryPositionalBulkUpdate(parent, keyedVnodes) {
1824
+ const total = keyedVnodes.length;
1825
+ if (total < 10) return null;
1826
+ const matchCount = countPositionalMatches(parent, keyedVnodes);
1827
+ logPositionalCheck(total, matchCount, parent.children.length);
1828
+ if (matchCount / total < 0.9) return null;
1829
+ if (hasPositionalPropChanges(parent, keyedVnodes)) return null;
1830
+ try {
1831
+ const stats = performBulkPositionalKeyedTextUpdate(parent, keyedVnodes);
1832
+ recordFastPathStats(stats, "bulkKeyedPositionalHits");
1833
+ rebuildKeyedMap(parent);
1834
+ return keyedElements.get(parent);
1835
+ } catch {
1836
+ return null;
1837
+ }
1838
+ }
1839
+ function countPositionalMatches(parent, keyedVnodes) {
1840
+ let matchCount = 0;
1841
+ try {
1842
+ for (let i = 0; i < keyedVnodes.length; i++) {
1843
+ const vnode = keyedVnodes[i].vnode;
1844
+ if (!vnode || typeof vnode !== "object" || typeof vnode.type !== "string")
1845
+ continue;
1846
+ const el = parent.children[i];
1847
+ if (!el) continue;
1848
+ if (el.tagName.toLowerCase() === String(vnode.type).toLowerCase()) {
1849
+ matchCount++;
1697
1850
  }
1698
1851
  }
1852
+ } catch {
1853
+ }
1854
+ return matchCount;
1855
+ }
1856
+ function logPositionalCheck(total, matchCount, parentChildren) {
1857
+ if (process.env.NODE_ENV !== "production") {
1699
1858
  try {
1700
- const total = keyedVnodes.length;
1701
- if (total >= 10) {
1702
- let matchCount = 0;
1703
- try {
1704
- for (let i = 0; i < total; i++) {
1705
- const vnode = keyedVnodes[i].vnode;
1706
- if (!vnode || typeof vnode !== "object" || typeof vnode.type !== "string")
1707
- continue;
1708
- const el = parent.children[i];
1709
- if (!el) continue;
1710
- if (el.tagName.toLowerCase() === String(vnode.type).toLowerCase())
1711
- matchCount++;
1712
- }
1713
- } catch (e) {
1714
- void e;
1715
- }
1716
- if (matchCount / total >= 0.9) {
1717
- let hasPropChanges = false;
1718
- try {
1719
- for (let i = 0; i < total; i++) {
1720
- const vnode = keyedVnodes[i].vnode;
1721
- const el = parent.children[i];
1722
- if (!el || !vnode || typeof vnode !== "object") continue;
1723
- const props = vnode.props || {};
1724
- for (const k of Object.keys(props)) {
1725
- if (k === "children" || k === "key") continue;
1726
- if (k.startsWith("on") && k.length > 2) continue;
1727
- if (k.startsWith("data-")) continue;
1728
- const v = props[k];
1729
- try {
1730
- if (k === "class" || k === "className") {
1731
- if (el.className !== String(v)) {
1732
- hasPropChanges = true;
1733
- break;
1734
- }
1735
- } else if (k === "value" || k === "checked") {
1736
- if (el[k] !== v) {
1737
- hasPropChanges = true;
1738
- break;
1739
- }
1740
- } else {
1741
- const attr = el.getAttribute(k);
1742
- if (v === void 0 || v === null || v === false) {
1743
- if (attr !== null) {
1744
- hasPropChanges = true;
1745
- break;
1746
- }
1747
- } else if (String(v) !== attr) {
1748
- hasPropChanges = true;
1749
- break;
1750
- }
1751
- }
1752
- } catch (e) {
1753
- hasPropChanges = true;
1754
- void e;
1755
- break;
1756
- }
1757
- }
1758
- if (hasPropChanges) break;
1759
- }
1760
- } catch (e) {
1761
- void e;
1762
- }
1763
- if (hasPropChanges) {
1764
- } else {
1765
- try {
1766
- const stats = performBulkPositionalKeyedTextUpdate(
1767
- parent,
1768
- keyedVnodes
1769
- );
1770
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1771
- try {
1772
- __ASKR_set("__LAST_FASTPATH_STATS", stats);
1773
- __ASKR_set("__LAST_FASTPATH_COMMIT_COUNT", 1);
1774
- __ASKR_incCounter("bulkKeyedPositionalHits");
1775
- } catch (e) {
1776
- void e;
1777
- }
1778
- }
1779
- try {
1780
- const map = /* @__PURE__ */ new Map();
1781
- const children = Array.from(parent.children);
1782
- for (let i = 0; i < children.length; i++) {
1783
- const el = children[i];
1784
- const k = el.getAttribute("data-key");
1785
- if (k !== null) {
1786
- map.set(k, el);
1787
- const n = Number(k);
1788
- if (!Number.isNaN(n)) map.set(n, el);
1789
- }
1790
- }
1791
- keyedElements.set(parent, map);
1792
- } catch (e) {
1793
- void e;
1794
- }
1795
- return keyedElements.get(parent);
1796
- } catch (e) {
1797
- void e;
1798
- }
1799
- }
1800
- }
1859
+ logger.warn("[Askr][FASTPATH] positional check", {
1860
+ total,
1861
+ matchCount,
1862
+ parentChildren
1863
+ });
1864
+ } catch {
1865
+ }
1866
+ }
1867
+ }
1868
+ function hasPositionalPropChanges(parent, keyedVnodes) {
1869
+ try {
1870
+ for (let i = 0; i < keyedVnodes.length; i++) {
1871
+ const vnode = keyedVnodes[i].vnode;
1872
+ const el = parent.children[i];
1873
+ if (!el || !vnode || typeof vnode !== "object") continue;
1874
+ if (checkPropChanges(el, vnode.props || {})) {
1875
+ return true;
1801
1876
  }
1802
- } catch (e) {
1803
- void e;
1804
1877
  }
1805
- } catch (e) {
1806
- void e;
1878
+ } catch {
1879
+ return true;
1880
+ }
1881
+ return false;
1882
+ }
1883
+ function rebuildKeyedMap(parent) {
1884
+ try {
1885
+ const map = /* @__PURE__ */ new Map();
1886
+ const children = Array.from(parent.children);
1887
+ for (let i = 0; i < children.length; i++) {
1888
+ const el = children[i];
1889
+ const k = el.getAttribute("data-key");
1890
+ if (k !== null) {
1891
+ map.set(k, el);
1892
+ const n = Number(k);
1893
+ if (!Number.isNaN(n)) map.set(n, el);
1894
+ }
1895
+ }
1896
+ keyedElements.set(parent, map);
1897
+ } catch {
1898
+ }
1899
+ }
1900
+ function performFullReconciliation(parent, newChildren, keyedVnodes, oldKeyMap) {
1901
+ const newKeyMap = /* @__PURE__ */ new Map();
1902
+ const finalNodes = [];
1903
+ const usedOldEls = /* @__PURE__ */ new WeakSet();
1904
+ const resolveOldElOnce = createOldElResolver(parent, oldKeyMap, usedOldEls);
1905
+ for (let i = 0; i < newChildren.length; i++) {
1906
+ const child = newChildren[i];
1907
+ const node = reconcileSingleChild(
1908
+ child,
1909
+ i,
1910
+ parent,
1911
+ resolveOldElOnce,
1912
+ usedOldEls,
1913
+ newKeyMap
1914
+ );
1915
+ if (node) finalNodes.push(node);
1916
+ }
1917
+ if (typeof document === "undefined") return newKeyMap;
1918
+ commitReconciliation(parent, finalNodes);
1919
+ keyedElements.delete(parent);
1920
+ return newKeyMap;
1921
+ }
1922
+ function createOldElResolver(parent, oldKeyMap, usedOldEls) {
1923
+ return (k) => {
1924
+ if (!oldKeyMap) return void 0;
1925
+ const direct = oldKeyMap.get(k);
1926
+ if (direct && !usedOldEls.has(direct)) {
1927
+ usedOldEls.add(direct);
1928
+ return direct;
1929
+ }
1930
+ const s = String(k);
1931
+ const byString = oldKeyMap.get(s);
1932
+ if (byString && !usedOldEls.has(byString)) {
1933
+ usedOldEls.add(byString);
1934
+ return byString;
1935
+ }
1936
+ const n = Number(s);
1937
+ if (!Number.isNaN(n)) {
1938
+ const byNum = oldKeyMap.get(n);
1939
+ if (byNum && !usedOldEls.has(byNum)) {
1940
+ usedOldEls.add(byNum);
1941
+ return byNum;
1942
+ }
1943
+ }
1944
+ return scanForElementByKey(parent, k, s, usedOldEls);
1945
+ };
1946
+ }
1947
+ function scanForElementByKey(parent, k, keyStr, usedOldEls) {
1948
+ try {
1949
+ const children = Array.from(parent.children);
1950
+ for (const ch of children) {
1951
+ if (usedOldEls.has(ch)) continue;
1952
+ const attr = ch.getAttribute("data-key");
1953
+ if (attr === keyStr) {
1954
+ usedOldEls.add(ch);
1955
+ return ch;
1956
+ }
1957
+ const numAttr = Number(attr);
1958
+ if (!Number.isNaN(numAttr) && numAttr === k) {
1959
+ usedOldEls.add(ch);
1960
+ return ch;
1961
+ }
1962
+ }
1963
+ } catch {
1964
+ }
1965
+ return void 0;
1966
+ }
1967
+ function reconcileSingleChild(child, index, parent, resolveOldElOnce, usedOldEls, newKeyMap) {
1968
+ const key = extractKey(child);
1969
+ if (key !== void 0) {
1970
+ return reconcileKeyedChild(child, key, parent, resolveOldElOnce, newKeyMap);
1971
+ }
1972
+ return reconcileUnkeyedChild(child, index, parent, usedOldEls);
1973
+ }
1974
+ function reconcileKeyedChild(child, key, parent, resolveOldElOnce, newKeyMap) {
1975
+ const el = resolveOldElOnce(key);
1976
+ if (el && el.parentElement === parent) {
1977
+ updateElementFromVnode(el, child);
1978
+ newKeyMap.set(key, el);
1979
+ return el;
1980
+ }
1981
+ const dom = createDOMNode(child);
1982
+ if (dom) {
1983
+ if (dom instanceof Element) newKeyMap.set(key, dom);
1984
+ return dom;
1985
+ }
1986
+ return null;
1987
+ }
1988
+ function reconcileUnkeyedChild(child, index, parent, usedOldEls) {
1989
+ try {
1990
+ const existing = parent.children[index];
1991
+ if (existing && (typeof child === "string" || typeof child === "number") && existing.nodeType === 1) {
1992
+ existing.textContent = String(child);
1993
+ usedOldEls.add(existing);
1994
+ return existing;
1995
+ }
1996
+ if (canReuseElement(existing, child)) {
1997
+ updateElementFromVnode(existing, child);
1998
+ usedOldEls.add(existing);
1999
+ return existing;
2000
+ }
2001
+ const avail = findAvailableUnkeyedElement(parent, usedOldEls);
2002
+ if (avail) {
2003
+ const reuseResult = tryReuseElement(avail, child, usedOldEls);
2004
+ if (reuseResult) return reuseResult;
2005
+ }
2006
+ } catch {
2007
+ }
2008
+ const dom = createDOMNode(child);
2009
+ return dom;
2010
+ }
2011
+ function canReuseElement(existing, child) {
2012
+ if (!existing) return false;
2013
+ if (typeof child !== "object" || child === null || !("type" in child))
2014
+ return false;
2015
+ const childObj = child;
2016
+ const hasNoKey = existing.getAttribute("data-key") === null || existing.getAttribute("data-key") === void 0;
2017
+ return hasNoKey && typeof childObj.type === "string" && existing.tagName.toLowerCase() === String(childObj.type).toLowerCase();
2018
+ }
2019
+ function findAvailableUnkeyedElement(parent, usedOldEls) {
2020
+ return Array.from(parent.children).find(
2021
+ (ch) => !usedOldEls.has(ch) && ch.getAttribute("data-key") === null
2022
+ );
2023
+ }
2024
+ function tryReuseElement(avail, child, usedOldEls) {
2025
+ if (typeof child === "string" || typeof child === "number") {
2026
+ avail.textContent = String(child);
2027
+ usedOldEls.add(avail);
2028
+ return avail;
2029
+ }
2030
+ if (typeof child === "object" && child !== null && "type" in child) {
2031
+ const childObj = child;
2032
+ if (typeof childObj.type === "string" && avail.tagName.toLowerCase() === String(childObj.type).toLowerCase()) {
2033
+ updateElementFromVnode(avail, child);
2034
+ usedOldEls.add(avail);
2035
+ return avail;
2036
+ }
2037
+ }
2038
+ return null;
2039
+ }
2040
+ function commitReconciliation(parent, finalNodes) {
2041
+ const fragment = document.createDocumentFragment();
2042
+ for (let i = 0; i < finalNodes.length; i++) {
2043
+ fragment.appendChild(finalNodes[i]);
2044
+ }
2045
+ try {
2046
+ const existing = Array.from(parent.childNodes);
2047
+ for (const n of existing) {
2048
+ if (n instanceof Element) removeAllListeners(n);
2049
+ cleanupInstanceIfPresent(n);
2050
+ }
2051
+ } catch {
2052
+ }
2053
+ recordDOMReplace("reconcile");
2054
+ parent.replaceChildren(fragment);
2055
+ }
2056
+
2057
+ // src/renderer/evaluate.ts
2058
+ var domRanges = /* @__PURE__ */ new WeakMap();
2059
+ function checkSimpleText(vnodeChildren) {
2060
+ if (!Array.isArray(vnodeChildren)) {
2061
+ if (typeof vnodeChildren === "string" || typeof vnodeChildren === "number") {
2062
+ return { isSimple: true, text: String(vnodeChildren) };
2063
+ }
2064
+ } else if (vnodeChildren.length === 1) {
2065
+ const child = vnodeChildren[0];
2066
+ if (typeof child === "string" || typeof child === "number") {
2067
+ return { isSimple: true, text: String(child) };
2068
+ }
2069
+ }
2070
+ return { isSimple: false };
2071
+ }
2072
+ function tryUpdateTextInPlace(element, text) {
2073
+ if (element.childNodes.length === 1 && element.firstChild?.nodeType === 3) {
2074
+ element.firstChild.data = text;
2075
+ return true;
2076
+ }
2077
+ return false;
2078
+ }
2079
+ function buildKeyMapFromDOM(parent) {
2080
+ const keyMap = /* @__PURE__ */ new Map();
2081
+ const children = Array.from(parent.children);
2082
+ for (const child of children) {
2083
+ const k = child.getAttribute("data-key");
2084
+ if (k !== null) {
2085
+ keyMap.set(k, child);
2086
+ const n = Number(k);
2087
+ if (!Number.isNaN(n)) keyMap.set(n, child);
2088
+ }
2089
+ }
2090
+ return keyMap;
2091
+ }
2092
+ function getOrBuildKeyMap(parent) {
2093
+ let keyMap = keyedElements.get(parent);
2094
+ if (!keyMap) {
2095
+ keyMap = buildKeyMapFromDOM(parent);
2096
+ if (keyMap.size > 0) {
2097
+ keyedElements.set(parent, keyMap);
2098
+ }
2099
+ }
2100
+ return keyMap.size > 0 ? keyMap : void 0;
2101
+ }
2102
+ function hasKeyedChildren(children) {
2103
+ return children.some(
2104
+ (child) => typeof child === "object" && child !== null && "key" in child
2105
+ );
2106
+ }
2107
+ function trackBulkTextStats(stats) {
2108
+ if (process.env.NODE_ENV !== "production") {
2109
+ try {
2110
+ __ASKR_set("__LAST_BULK_TEXT_FASTPATH_STATS", stats);
2111
+ __ASKR_incCounter("bulkTextHits");
2112
+ } catch {
2113
+ }
2114
+ }
2115
+ }
2116
+ function trackBulkTextMiss() {
2117
+ if (process.env.NODE_ENV !== "production") {
2118
+ try {
2119
+ __ASKR_incCounter("bulkTextMisses");
2120
+ } catch {
2121
+ }
1807
2122
  }
1808
- const finalNodes = [];
1809
- const usedOldEls = /* @__PURE__ */ new WeakSet();
1810
- const resolveOldElOnce = (k) => {
1811
- if (!oldKeyMap) return void 0;
1812
- const direct = oldKeyMap.get(k);
1813
- if (direct && !usedOldEls.has(direct)) {
1814
- usedOldEls.add(direct);
1815
- return direct;
2123
+ }
2124
+ function reconcileKeyed(parent, children, oldKeyMap) {
2125
+ if (process.env.ASKR_FORCE_BULK_POSREUSE === "1") {
2126
+ const result = tryForcedBulkKeyedPath(parent, children);
2127
+ if (result) return;
2128
+ }
2129
+ const newKeyMap = reconcileKeyedChildren(parent, children, oldKeyMap);
2130
+ keyedElements.set(parent, newKeyMap);
2131
+ }
2132
+ function tryForcedBulkKeyedPath(parent, children) {
2133
+ try {
2134
+ const keyedVnodes = [];
2135
+ for (const child of children) {
2136
+ if (_isDOMElement(child) && child.key !== void 0) {
2137
+ keyedVnodes.push({
2138
+ key: child.key,
2139
+ vnode: child
2140
+ });
2141
+ }
1816
2142
  }
1817
- const s = String(k);
1818
- const byString = oldKeyMap.get(s);
1819
- if (byString && !usedOldEls.has(byString)) {
1820
- usedOldEls.add(byString);
1821
- return byString;
2143
+ if (keyedVnodes.length === 0 || keyedVnodes.length !== children.length) {
2144
+ return false;
1822
2145
  }
1823
- const n = Number(String(k));
1824
- if (!Number.isNaN(n)) {
1825
- const byNum = oldKeyMap.get(n);
1826
- if (byNum && !usedOldEls.has(byNum)) {
1827
- usedOldEls.add(byNum);
1828
- return byNum;
2146
+ if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
2147
+ logger.warn(
2148
+ "[Askr][FASTPATH] forced positional bulk keyed reuse (evaluate-level)"
2149
+ );
2150
+ }
2151
+ const stats = performBulkPositionalKeyedTextUpdate(parent, keyedVnodes);
2152
+ if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
2153
+ try {
2154
+ __ASKR_set("__LAST_FASTPATH_STATS", stats);
2155
+ __ASKR_set("__LAST_FASTPATH_COMMIT_COUNT", 1);
2156
+ __ASKR_incCounter("bulkKeyedPositionalForced");
2157
+ } catch {
1829
2158
  }
1830
2159
  }
2160
+ const newMap = buildKeyMapFromDOM(parent);
2161
+ keyedElements.set(parent, newMap);
2162
+ return true;
2163
+ } catch (err) {
2164
+ if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
2165
+ logger.warn(
2166
+ "[Askr][FASTPATH] forced bulk path failed, falling back",
2167
+ err
2168
+ );
2169
+ }
2170
+ return false;
2171
+ }
2172
+ }
2173
+ function reconcileUnkeyed(parent, children) {
2174
+ if (isBulkTextFastPathEligible(parent, children)) {
2175
+ const stats = performBulkTextReplace(parent, children);
2176
+ trackBulkTextStats(stats);
2177
+ } else {
2178
+ trackBulkTextMiss();
2179
+ updateUnkeyedChildren(parent, children);
2180
+ }
2181
+ keyedElements.delete(parent);
2182
+ }
2183
+ function updateElementChildren2(element, vnodeChildren) {
2184
+ if (!vnodeChildren) {
2185
+ element.textContent = "";
2186
+ keyedElements.delete(element);
2187
+ return;
2188
+ }
2189
+ if (!Array.isArray(vnodeChildren)) {
2190
+ element.textContent = "";
2191
+ const dom = createDOMNode(vnodeChildren);
2192
+ if (dom) element.appendChild(dom);
2193
+ keyedElements.delete(element);
2194
+ return;
2195
+ }
2196
+ if (hasKeyedChildren(vnodeChildren)) {
2197
+ const oldKeyMap = getOrBuildKeyMap(element);
1831
2198
  try {
1832
- const children = Array.from(parent.children);
1833
- for (const ch of children) {
1834
- if (usedOldEls.has(ch)) continue;
1835
- const attr = ch.getAttribute("data-key");
1836
- if (attr === s) {
1837
- usedOldEls.add(ch);
1838
- return ch;
1839
- }
1840
- const numAttr = Number(attr);
1841
- if (!Number.isNaN(numAttr) && numAttr === k) {
1842
- usedOldEls.add(ch);
1843
- return ch;
1844
- }
1845
- }
1846
- } catch (e) {
1847
- void e;
2199
+ reconcileKeyed(element, vnodeChildren, oldKeyMap);
2200
+ } catch {
2201
+ const newKeyMap = reconcileKeyedChildren(
2202
+ element,
2203
+ vnodeChildren,
2204
+ oldKeyMap
2205
+ );
2206
+ keyedElements.set(element, newKeyMap);
1848
2207
  }
1849
- return void 0;
1850
- };
1851
- for (let i = 0; i < newChildren.length; i++) {
1852
- const child = newChildren[i];
1853
- if (typeof child === "object" && child !== null && "type" in child) {
1854
- const childObj = child;
1855
- const rawKey = childObj.key ?? childObj.props?.key;
1856
- if (rawKey !== void 0) {
1857
- const key = typeof rawKey === "symbol" ? String(rawKey) : rawKey;
1858
- const el = resolveOldElOnce(key);
1859
- if (el && el.parentElement === parent) {
1860
- updateElementFromVnode(el, child);
1861
- finalNodes.push(el);
1862
- newKeyMap.set(key, el);
1863
- continue;
1864
- }
1865
- const dom2 = createDOMNode(child);
1866
- if (dom2) {
1867
- finalNodes.push(dom2);
1868
- if (dom2 instanceof Element) newKeyMap.set(key, dom2);
1869
- }
1870
- continue;
2208
+ } else {
2209
+ reconcileUnkeyed(element, vnodeChildren);
2210
+ }
2211
+ }
2212
+ function smartUpdateElement(element, vnode) {
2213
+ const vnodeChildren = vnode.children || vnode.props?.children;
2214
+ const textCheck = checkSimpleText(vnodeChildren);
2215
+ if (textCheck.isSimple && tryUpdateTextInPlace(element, textCheck.text)) {
2216
+ } else {
2217
+ updateElementChildren2(element, vnodeChildren);
2218
+ }
2219
+ updateElementFromVnode(element, vnode, false);
2220
+ }
2221
+ function processFragmentChildren(target, childArray) {
2222
+ const existingChildren = Array.from(target.children);
2223
+ for (let i = 0; i < childArray.length; i++) {
2224
+ const childVnode = childArray[i];
2225
+ const existingNode = existingChildren[i];
2226
+ if (existingNode && _isDOMElement(childVnode) && typeof childVnode.type === "string" && existingNode.tagName.toLowerCase() === childVnode.type.toLowerCase()) {
2227
+ smartUpdateElement(existingNode, childVnode);
2228
+ continue;
2229
+ }
2230
+ const newDom = createDOMNode(childVnode);
2231
+ if (newDom) {
2232
+ if (existingNode) {
2233
+ target.replaceChild(newDom, existingNode);
2234
+ } else {
2235
+ target.appendChild(newDom);
1871
2236
  }
1872
2237
  }
2238
+ }
2239
+ while (target.children.length > childArray.length) {
2240
+ target.removeChild(target.lastChild);
2241
+ }
2242
+ }
2243
+ function createWrappedEventHandler(handler) {
2244
+ return (event) => {
2245
+ globalScheduler.setInHandler(true);
1873
2246
  try {
1874
- const existing = parent.children[i];
1875
- if (existing && (typeof child === "string" || typeof child === "number") && existing.nodeType === 1) {
1876
- existing.textContent = String(child);
1877
- finalNodes.push(existing);
1878
- usedOldEls.add(existing);
1879
- continue;
1880
- }
1881
- if (existing && typeof child === "object" && child !== null && "type" in child && (existing.getAttribute("data-key") === null || existing.getAttribute("data-key") === void 0) && typeof child.type === "string" && existing.tagName.toLowerCase() === String(child.type).toLowerCase()) {
1882
- updateElementFromVnode(existing, child);
1883
- finalNodes.push(existing);
1884
- usedOldEls.add(existing);
1885
- continue;
2247
+ handler(event);
2248
+ } catch (error) {
2249
+ logger.error("[Askr] Event handler error:", error);
2250
+ } finally {
2251
+ globalScheduler.setInHandler(false);
2252
+ }
2253
+ };
2254
+ }
2255
+ function applyPropsToElement2(el, props) {
2256
+ for (const [key, value] of Object.entries(props)) {
2257
+ if (key === "children" || key === "key") continue;
2258
+ if (value === void 0 || value === null || value === false) continue;
2259
+ if (key.startsWith("on") && key.length > 2) {
2260
+ const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
2261
+ const wrappedHandler = createWrappedEventHandler(value);
2262
+ const options = eventName === "wheel" || eventName === "scroll" || eventName.startsWith("touch") ? { passive: true } : void 0;
2263
+ if (options !== void 0) {
2264
+ el.addEventListener(eventName, wrappedHandler, options);
2265
+ } else {
2266
+ el.addEventListener(eventName, wrappedHandler);
1886
2267
  }
1887
- try {
1888
- const avail = Array.from(parent.children).find(
1889
- (ch) => !usedOldEls.has(ch) && ch.getAttribute("data-key") === null
1890
- );
1891
- if (avail) {
1892
- if (typeof child === "string" || typeof child === "number") {
1893
- avail.textContent = String(child);
1894
- } else if (typeof child === "object" && child !== null && "type" in child && typeof child.type === "string" && avail.tagName.toLowerCase() === String(child.type).toLowerCase()) {
1895
- updateElementFromVnode(avail, child);
1896
- } else {
1897
- const dom2 = createDOMNode(child);
1898
- if (dom2) {
1899
- finalNodes.push(dom2);
1900
- continue;
1901
- }
1902
- }
1903
- usedOldEls.add(avail);
1904
- finalNodes.push(avail);
1905
- continue;
1906
- }
1907
- } catch (e) {
1908
- void e;
2268
+ if (!elementListeners.has(el)) {
2269
+ elementListeners.set(el, /* @__PURE__ */ new Map());
1909
2270
  }
1910
- } catch (e) {
1911
- void e;
2271
+ elementListeners.get(el).set(eventName, {
2272
+ handler: wrappedHandler,
2273
+ original: value,
2274
+ options
2275
+ });
2276
+ continue;
1912
2277
  }
1913
- const dom = createDOMNode(child);
1914
- if (dom) finalNodes.push(dom);
1915
- }
1916
- if (typeof document === "undefined") return newKeyMap;
1917
- const fragment = document.createDocumentFragment();
1918
- for (let i = 0; i < finalNodes.length; i++)
1919
- fragment.appendChild(finalNodes[i]);
1920
- try {
1921
- const existing = Array.from(parent.childNodes);
1922
- for (const n of existing) {
1923
- if (n instanceof Element) removeAllListeners(n);
1924
- cleanupInstanceIfPresent(n);
2278
+ if (key === "class" || key === "className") {
2279
+ el.className = String(value);
2280
+ } else if (key === "value" || key === "checked") {
2281
+ el[key] = value;
2282
+ } else {
2283
+ el.setAttribute(key, String(value));
1925
2284
  }
1926
- } catch (e) {
1927
- void e;
1928
- }
1929
- try {
1930
- __ASKR_incCounter("__DOM_REPLACE_COUNT");
1931
- __ASKR_set("__LAST_DOM_REPLACE_STACK_RECONCILE", new Error().stack);
1932
- } catch (e) {
1933
- void e;
1934
2285
  }
1935
- parent.replaceChildren(fragment);
1936
- keyedElements.delete(parent);
1937
- return newKeyMap;
1938
2286
  }
1939
- var init_reconcile = __esm({
1940
- "src/renderer/reconcile.ts"() {
1941
- "use strict";
1942
- init_dom();
1943
- init_keyed();
1944
- init_cleanup();
1945
- init_fastlane_shared();
1946
- init_diag();
1947
- init_fastpath();
2287
+ function tryFirstRenderKeyedChildren(target, vnode) {
2288
+ const children = vnode.children;
2289
+ if (!Array.isArray(children) || !hasKeyedChildren(children)) {
2290
+ return false;
1948
2291
  }
1949
- });
1950
-
1951
- // src/renderer/evaluate.ts
2292
+ const el = document.createElement(vnode.type);
2293
+ target.appendChild(el);
2294
+ applyPropsToElement2(el, vnode.props || {});
2295
+ const newKeyMap = reconcileKeyedChildren(el, children, void 0);
2296
+ keyedElements.set(el, newKeyMap);
2297
+ return true;
2298
+ }
2299
+ function isFragment(vnode) {
2300
+ return _isDOMElement(vnode) && typeof vnode.type === "symbol" && (vnode.type === Fragment || String(vnode.type) === "Symbol(askr.fragment)");
2301
+ }
2302
+ function getFragmentChildren(vnode) {
2303
+ const fragmentChildren = vnode.props?.children || vnode.children || [];
2304
+ return Array.isArray(fragmentChildren) ? fragmentChildren : [fragmentChildren];
2305
+ }
1952
2306
  function evaluate(node, target, context) {
1953
2307
  if (!target) return;
1954
2308
  if (typeof document === "undefined") {
@@ -1984,228 +2338,23 @@ function evaluate(node, target, context) {
1984
2338
  target.insertBefore(dom, end);
1985
2339
  }
1986
2340
  } else {
1987
- const vnode = node;
1988
- const firstChild = target.children[0];
1989
- if (firstChild && _isDOMElement(vnode) && typeof vnode.type === "string" && firstChild.tagName.toLowerCase() === vnode.type.toLowerCase()) {
1990
- const vnodeChildren = vnode.children || vnode.props?.children;
1991
- let isSimpleTextVNode = false;
1992
- let textContent;
1993
- if (!Array.isArray(vnodeChildren)) {
1994
- if (typeof vnodeChildren === "string" || typeof vnodeChildren === "number") {
1995
- isSimpleTextVNode = true;
1996
- textContent = String(vnodeChildren);
1997
- }
1998
- } else if (vnodeChildren.length === 1) {
1999
- const child = vnodeChildren[0];
2000
- if (typeof child === "string" || typeof child === "number") {
2001
- isSimpleTextVNode = true;
2002
- textContent = String(child);
2003
- }
2004
- }
2005
- if (isSimpleTextVNode && firstChild.childNodes.length === 1 && firstChild.firstChild?.nodeType === 3) {
2006
- firstChild.firstChild.data = textContent;
2341
+ let vnode = node;
2342
+ if (isFragment(vnode)) {
2343
+ const childArray = getFragmentChildren(vnode);
2344
+ if (childArray.length === 1 && _isDOMElement(childArray[0]) && typeof childArray[0].type === "string") {
2345
+ vnode = childArray[0];
2007
2346
  } else {
2008
- if (vnodeChildren) {
2009
- if (Array.isArray(vnodeChildren)) {
2010
- const hasKeys = vnodeChildren.some(
2011
- (child) => typeof child === "object" && child !== null && "key" in child
2012
- );
2013
- if (hasKeys) {
2014
- let oldKeyMap = keyedElements.get(firstChild);
2015
- if (!oldKeyMap) {
2016
- oldKeyMap = /* @__PURE__ */ new Map();
2017
- try {
2018
- const children = Array.from(firstChild.children);
2019
- for (let i = 0; i < children.length; i++) {
2020
- const ch = children[i];
2021
- const k = ch.getAttribute("data-key");
2022
- if (k !== null) {
2023
- oldKeyMap.set(k, ch);
2024
- const n = Number(k);
2025
- if (!Number.isNaN(n)) oldKeyMap.set(n, ch);
2026
- }
2027
- }
2028
- if (oldKeyMap.size > 0)
2029
- keyedElements.set(firstChild, oldKeyMap);
2030
- } catch (e) {
2031
- void e;
2032
- }
2033
- }
2034
- try {
2035
- if (process.env.ASKR_FORCE_BULK_POSREUSE === "1") {
2036
- try {
2037
- const keyedVnodes = [];
2038
- for (let i = 0; i < vnodeChildren.length; i++) {
2039
- const c = vnodeChildren[i];
2040
- if (_isDOMElement(c) && c.key !== void 0) {
2041
- keyedVnodes.push({
2042
- key: c.key,
2043
- vnode: c
2044
- });
2045
- }
2046
- }
2047
- if (keyedVnodes.length > 0 && keyedVnodes.length === vnodeChildren.length) {
2048
- if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
2049
- logger.warn(
2050
- "[Askr][FASTPATH] forced positional bulk keyed reuse (evaluate-level)"
2051
- );
2052
- }
2053
- const stats = performBulkPositionalKeyedTextUpdate(
2054
- firstChild,
2055
- keyedVnodes
2056
- );
2057
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
2058
- try {
2059
- __ASKR_set("__LAST_FASTPATH_STATS", stats);
2060
- __ASKR_set("__LAST_FASTPATH_COMMIT_COUNT", 1);
2061
- __ASKR_incCounter("bulkKeyedPositionalForced");
2062
- } catch (e) {
2063
- void e;
2064
- }
2065
- }
2066
- try {
2067
- const map = /* @__PURE__ */ new Map();
2068
- const children = Array.from(firstChild.children);
2069
- for (let i = 0; i < children.length; i++) {
2070
- const el = children[i];
2071
- const k = el.getAttribute("data-key");
2072
- if (k !== null) {
2073
- map.set(k, el);
2074
- const n = Number(k);
2075
- if (!Number.isNaN(n)) map.set(n, el);
2076
- }
2077
- }
2078
- keyedElements.set(firstChild, map);
2079
- } catch (e) {
2080
- void e;
2081
- }
2082
- } else {
2083
- const newKeyMap = reconcileKeyedChildren(
2084
- firstChild,
2085
- vnodeChildren,
2086
- oldKeyMap
2087
- );
2088
- keyedElements.set(firstChild, newKeyMap);
2089
- }
2090
- } catch (err) {
2091
- if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
2092
- logger.warn(
2093
- "[Askr][FASTPATH] forced bulk path failed, falling back",
2094
- err
2095
- );
2096
- }
2097
- const newKeyMap = reconcileKeyedChildren(
2098
- firstChild,
2099
- vnodeChildren,
2100
- oldKeyMap
2101
- );
2102
- keyedElements.set(firstChild, newKeyMap);
2103
- }
2104
- } else {
2105
- const newKeyMap = reconcileKeyedChildren(
2106
- firstChild,
2107
- vnodeChildren,
2108
- oldKeyMap
2109
- );
2110
- keyedElements.set(firstChild, newKeyMap);
2111
- }
2112
- } catch (e) {
2113
- void e;
2114
- const newKeyMap = reconcileKeyedChildren(
2115
- firstChild,
2116
- vnodeChildren,
2117
- oldKeyMap
2118
- );
2119
- keyedElements.set(firstChild, newKeyMap);
2120
- }
2121
- } else {
2122
- if (isBulkTextFastPathEligible(firstChild, vnodeChildren)) {
2123
- const stats = performBulkTextReplace(firstChild, vnodeChildren);
2124
- if (process.env.NODE_ENV !== "production") {
2125
- try {
2126
- __ASKR_set("__LAST_BULK_TEXT_FASTPATH_STATS", stats);
2127
- __ASKR_incCounter("bulkTextHits");
2128
- } catch (e) {
2129
- void e;
2130
- }
2131
- }
2132
- } else {
2133
- if (process.env.NODE_ENV !== "production") {
2134
- try {
2135
- __ASKR_incCounter("bulkTextMisses");
2136
- } catch (e) {
2137
- void e;
2138
- }
2139
- }
2140
- updateUnkeyedChildren(firstChild, vnodeChildren);
2141
- keyedElements.delete(firstChild);
2142
- }
2143
- }
2144
- } else {
2145
- firstChild.textContent = "";
2146
- const dom = createDOMNode(vnodeChildren);
2147
- if (dom) firstChild.appendChild(dom);
2148
- keyedElements.delete(firstChild);
2149
- }
2150
- } else {
2151
- firstChild.textContent = "";
2152
- keyedElements.delete(firstChild);
2153
- }
2347
+ processFragmentChildren(target, childArray);
2348
+ return;
2154
2349
  }
2155
- updateElementFromVnode(firstChild, vnode, false);
2350
+ }
2351
+ const firstChild = target.children[0];
2352
+ if (firstChild && _isDOMElement(vnode) && typeof vnode.type === "string" && firstChild.tagName.toLowerCase() === vnode.type.toLowerCase()) {
2353
+ smartUpdateElement(firstChild, vnode);
2156
2354
  } else {
2157
2355
  target.textContent = "";
2158
- if (_isDOMElement(vnode) && typeof vnode.type === "string") {
2159
- const children = vnode.children;
2160
- if (Array.isArray(children) && children.some(
2161
- (child) => typeof child === "object" && child !== null && "key" in child
2162
- )) {
2163
- const el = document.createElement(vnode.type);
2164
- target.appendChild(el);
2165
- const props = vnode.props || {};
2166
- for (const [key, value] of Object.entries(props)) {
2167
- if (key === "children" || key === "key") continue;
2168
- if (value === void 0 || value === null || value === false)
2169
- continue;
2170
- if (key.startsWith("on") && key.length > 2) {
2171
- const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
2172
- const wrappedHandler = (event) => {
2173
- globalScheduler.setInHandler(true);
2174
- try {
2175
- value(event);
2176
- } catch (error) {
2177
- logger.error("[Askr] Event handler error:", error);
2178
- } finally {
2179
- globalScheduler.setInHandler(false);
2180
- }
2181
- };
2182
- const options = eventName === "wheel" || eventName === "scroll" || eventName.startsWith("touch") ? { passive: true } : void 0;
2183
- if (options !== void 0)
2184
- el.addEventListener(eventName, wrappedHandler, options);
2185
- else el.addEventListener(eventName, wrappedHandler);
2186
- if (!elementListeners.has(el)) {
2187
- elementListeners.set(el, /* @__PURE__ */ new Map());
2188
- }
2189
- elementListeners.get(el).set(eventName, {
2190
- handler: wrappedHandler,
2191
- original: value,
2192
- options
2193
- });
2194
- continue;
2195
- }
2196
- if (key === "class" || key === "className") {
2197
- el.className = String(value);
2198
- } else if (key === "value" || key === "checked") {
2199
- el[key] = value;
2200
- } else {
2201
- el.setAttribute(key, String(value));
2202
- }
2203
- }
2204
- const newKeyMap = reconcileKeyedChildren(el, children, void 0);
2205
- keyedElements.set(el, newKeyMap);
2206
- return;
2207
- return;
2208
- }
2356
+ if (_isDOMElement(vnode) && typeof vnode.type === "string" && tryFirstRenderKeyedChildren(target, vnode)) {
2357
+ return;
2209
2358
  }
2210
2359
  const dom = createDOMNode(vnode);
2211
2360
  if (dom) {
@@ -2214,43 +2363,16 @@ function evaluate(node, target, context) {
2214
2363
  }
2215
2364
  }
2216
2365
  }
2217
- var domRanges;
2218
- var init_evaluate = __esm({
2219
- "src/renderer/evaluate.ts"() {
2220
- "use strict";
2221
- init_scheduler();
2222
- init_logger();
2223
- init_cleanup();
2224
- init_keyed();
2225
- init_reconcile();
2226
- init_types();
2227
- init_dom();
2228
- init_diag();
2229
- domRanges = /* @__PURE__ */ new WeakMap();
2230
- }
2231
- });
2232
2366
 
2233
2367
  // src/renderer/index.ts
2234
- var init_renderer = __esm({
2235
- "src/renderer/index.ts"() {
2236
- "use strict";
2237
- init_types();
2238
- init_cleanup();
2239
- init_keyed();
2240
- init_dom();
2241
- init_evaluate();
2242
- init_evaluate();
2243
- init_keyed();
2244
- if (typeof globalThis !== "undefined") {
2245
- const _g = globalThis;
2246
- _g.__ASKR_RENDERER = {
2247
- evaluate,
2248
- isKeyedReorderFastPathEligible,
2249
- getKeyMapForElement
2250
- };
2251
- }
2252
- }
2253
- });
2368
+ if (typeof globalThis !== "undefined") {
2369
+ const _g = globalThis;
2370
+ _g.__ASKR_RENDERER = {
2371
+ evaluate,
2372
+ isKeyedReorderFastPathEligible,
2373
+ getKeyMapForElement
2374
+ };
2375
+ }
2254
2376
 
2255
2377
  // src/runtime/component.ts
2256
2378
  function createComponentInstance(id, fn, props, target) {
@@ -2302,6 +2424,8 @@ function createComponentInstance(id, fn, props, target) {
2302
2424
  };
2303
2425
  return instance;
2304
2426
  }
2427
+ var currentInstance = null;
2428
+ var stateIndex = 0;
2305
2429
  function getCurrentComponentInstance() {
2306
2430
  return currentInstance;
2307
2431
  }
@@ -2353,6 +2477,7 @@ function mountInstanceInline(instance, target) {
2353
2477
  executeMountOperations(instance);
2354
2478
  }
2355
2479
  }
2480
+ var _globalRenderCounter = 0;
2356
2481
  function runComponent(instance) {
2357
2482
  instance.notifyUpdate = instance._enqueueRun;
2358
2483
  instance._currentRenderToken = ++_globalRenderCounter;
@@ -2372,6 +2497,34 @@ function runComponent(instance) {
2372
2497
  if (process.env.NODE_ENV !== "production") throw err;
2373
2498
  }
2374
2499
  globalScheduler.enqueue(() => {
2500
+ if (!instance.target && instance._placeholder) {
2501
+ if (result === null || result === void 0) {
2502
+ finalizeReadSubscriptions(instance);
2503
+ return;
2504
+ }
2505
+ const placeholder = instance._placeholder;
2506
+ const parent = placeholder.parentNode;
2507
+ if (!parent) {
2508
+ logger.warn(
2509
+ "[Askr] placeholder no longer in DOM, cannot render component"
2510
+ );
2511
+ return;
2512
+ }
2513
+ const host = document.createElement("div");
2514
+ const oldInstance = currentInstance;
2515
+ currentInstance = instance;
2516
+ try {
2517
+ evaluate(result, host);
2518
+ parent.replaceChild(host, placeholder);
2519
+ instance.target = host;
2520
+ instance._placeholder = void 0;
2521
+ host.__ASKR_INSTANCE = instance;
2522
+ finalizeReadSubscriptions(instance);
2523
+ } finally {
2524
+ currentInstance = oldInstance;
2525
+ }
2526
+ return;
2527
+ }
2375
2528
  if (instance.target) {
2376
2529
  let oldChildren = [];
2377
2530
  try {
@@ -2454,6 +2607,8 @@ function runComponent(instance) {
2454
2607
  }
2455
2608
  function renderComponentInline(instance) {
2456
2609
  const hadToken = instance._currentRenderToken !== void 0;
2610
+ const prevToken = instance._currentRenderToken;
2611
+ const prevPendingReads = instance._pendingReadStates;
2457
2612
  if (!hadToken) {
2458
2613
  instance._currentRenderToken = ++_globalRenderCounter;
2459
2614
  instance._pendingReadStates = /* @__PURE__ */ new Set();
@@ -2465,10 +2620,8 @@ function renderComponentInline(instance) {
2465
2620
  }
2466
2621
  return result;
2467
2622
  } finally {
2468
- if (!hadToken) {
2469
- instance._pendingReadStates = /* @__PURE__ */ new Set();
2470
- instance._currentRenderToken = void 0;
2471
- }
2623
+ instance._currentRenderToken = prevToken;
2624
+ instance._pendingReadStates = prevPendingReads ?? /* @__PURE__ */ new Set();
2472
2625
  }
2473
2626
  }
2474
2627
  function executeComponentSync(instance) {
@@ -2599,75 +2752,11 @@ function cleanupComponent(instance) {
2599
2752
  instance._lastReadStates = /* @__PURE__ */ new Set();
2600
2753
  }
2601
2754
  instance.abortController.abort();
2602
- }
2603
- var currentInstance, stateIndex, _globalRenderCounter;
2604
- var init_component = __esm({
2605
- "src/runtime/component.ts"() {
2606
- "use strict";
2607
- init_scheduler();
2608
- init_context();
2609
- init_logger();
2610
- init_diag();
2611
- init_fastlane_shared();
2612
- init_renderer();
2613
- currentInstance = null;
2614
- stateIndex = 0;
2615
- _globalRenderCounter = 0;
2616
- }
2617
- });
2618
-
2619
- // src/router/route.ts
2620
- var route_exports = {};
2621
- __export(route_exports, {
2622
- _lockRouteRegistrationForTests: () => _lockRouteRegistrationForTests,
2623
- _unlockRouteRegistrationForTests: () => _unlockRouteRegistrationForTests,
2624
- clearRoutes: () => clearRoutes,
2625
- getLoadedNamespaces: () => getLoadedNamespaces,
2626
- getNamespaceRoutes: () => getNamespaceRoutes,
2627
- getRoutes: () => getRoutes,
2628
- lockRouteRegistration: () => lockRouteRegistration,
2629
- registerRoute: () => registerRoute,
2630
- resolveRoute: () => resolveRoute,
2631
- route: () => route,
2632
- setServerLocation: () => setServerLocation,
2633
- unloadNamespace: () => unloadNamespace
2634
- });
2635
-
2636
- // src/router/match.ts
2637
- function match(path, pattern) {
2638
- const normalizedPath = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
2639
- const normalizedPattern = pattern.endsWith("/") && pattern !== "/" ? pattern.slice(0, -1) : pattern;
2640
- const pathSegments = normalizedPath.split("/").filter(Boolean);
2641
- const patternSegments = normalizedPattern.split("/").filter(Boolean);
2642
- if (patternSegments.length === 1 && patternSegments[0] === "*") {
2643
- return {
2644
- matched: true,
2645
- params: {
2646
- "*": pathSegments.length > 1 ? normalizedPath : pathSegments[0]
2647
- }
2648
- };
2649
- }
2650
- if (pathSegments.length !== patternSegments.length) {
2651
- return { matched: false, params: {} };
2652
- }
2653
- const params = {};
2654
- for (let i = 0; i < patternSegments.length; i++) {
2655
- const patternSegment = patternSegments[i];
2656
- const pathSegment = pathSegments[i];
2657
- if (patternSegment.startsWith("{") && patternSegment.endsWith("}")) {
2658
- const paramName = patternSegment.slice(1, -1);
2659
- params[paramName] = decodeURIComponent(pathSegment);
2660
- } else if (patternSegment === "*") {
2661
- params["*"] = pathSegment;
2662
- } else if (patternSegment !== pathSegment) {
2663
- return { matched: false, params: {} };
2664
- }
2665
- }
2666
- return { matched: true, params };
2755
+ instance.notifyUpdate = null;
2756
+ instance.mounted = false;
2667
2757
  }
2668
2758
 
2669
2759
  // src/router/route.ts
2670
- init_component();
2671
2760
  var routes = [];
2672
2761
  var namespaces = /* @__PURE__ */ new Set();
2673
2762
  var routesByDepth = /* @__PURE__ */ new Map();
@@ -2981,31 +3070,34 @@ function resolveRoute(pathname) {
2981
3070
 
2982
3071
  export {
2983
3072
  invariant,
2984
- init_invariant,
2985
3073
  logger,
2986
- init_logger,
2987
3074
  globalScheduler,
2988
3075
  scheduleEventHandler,
2989
- init_scheduler,
2990
3076
  withAsyncResourceContext,
2991
3077
  defineContext,
2992
3078
  readContext,
2993
3079
  getCurrentContextFrame,
2994
- init_context,
3080
+ setDevValue,
3081
+ getDevValue,
3082
+ enterBulkCommit,
3083
+ exitBulkCommit,
2995
3084
  isBulkCommitActive2 as isBulkCommitActive,
2996
- init_fastlane_shared,
3085
+ markFastPathApplied,
3086
+ isFastPathApplied,
2997
3087
  removeAllListeners,
2998
- init_renderer,
3088
+ getKeyMapForElement,
3089
+ populateKeyMapForElement,
3090
+ isKeyedReorderFastPathEligible,
2999
3091
  createComponentInstance,
3000
3092
  getCurrentComponentInstance,
3001
3093
  setCurrentComponentInstance,
3002
3094
  registerMountOperation,
3003
3095
  getCurrentInstance,
3004
3096
  getSignal,
3097
+ finalizeReadSubscriptions,
3005
3098
  getNextStateIndex,
3006
3099
  mountComponent,
3007
3100
  cleanupComponent,
3008
- init_component,
3009
3101
  setServerLocation,
3010
3102
  lockRouteRegistration,
3011
3103
  _lockRouteRegistrationForTests,
@@ -3020,4 +3112,4 @@ export {
3020
3112
  resolveRoute,
3021
3113
  route_exports
3022
3114
  };
3023
- //# sourceMappingURL=chunk-MIPES65F.js.map
3115
+ //# sourceMappingURL=chunk-2ONGHQ7Z.js.map