@askrjs/askr 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +298 -0
- package/dist/chunk-4CV4JOE5.js +27 -0
- package/dist/chunk-4CV4JOE5.js.map +1 -0
- package/dist/chunk-HIWJVOS4.js +503 -0
- package/dist/chunk-HIWJVOS4.js.map +1 -0
- package/dist/chunk-L7RL4LYV.js +3442 -0
- package/dist/chunk-L7RL4LYV.js.map +1 -0
- package/dist/chunk-UUM5W2RM.js +84 -0
- package/dist/chunk-UUM5W2RM.js.map +1 -0
- package/dist/chunk-YNH3D4KW.js +29 -0
- package/dist/chunk-YNH3D4KW.js.map +1 -0
- package/dist/index.cjs +4733 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +446 -0
- package/dist/index.d.ts +446 -0
- package/dist/index.js +683 -0
- package/dist/index.js.map +1 -0
- package/dist/jsx/jsx-dev-runtime.cjs +40 -0
- package/dist/jsx/jsx-dev-runtime.cjs.map +1 -0
- package/dist/jsx/jsx-dev-runtime.d.cts +16 -0
- package/dist/jsx/jsx-dev-runtime.d.ts +16 -0
- package/dist/jsx/jsx-dev-runtime.js +16 -0
- package/dist/jsx/jsx-dev-runtime.js.map +1 -0
- package/dist/jsx/jsx-runtime.cjs +45 -0
- package/dist/jsx/jsx-runtime.cjs.map +1 -0
- package/dist/jsx/jsx-runtime.d.cts +17 -0
- package/dist/jsx/jsx-runtime.d.ts +17 -0
- package/dist/jsx/jsx-runtime.js +14 -0
- package/dist/jsx/jsx-runtime.js.map +1 -0
- package/dist/navigate-NLQOZQGM.js +16 -0
- package/dist/navigate-NLQOZQGM.js.map +1 -0
- package/dist/route-TVYWYCEJ.js +31 -0
- package/dist/route-TVYWYCEJ.js.map +1 -0
- package/dist/ssr-4ELUFK65.js +24 -0
- package/dist/ssr-4ELUFK65.js.map +1 -0
- package/dist/types-DUDmnzD8.d.cts +38 -0
- package/dist/types-DUDmnzD8.d.ts +38 -0
- package/package.json +83 -0
- package/src/jsx/jsx-dev-runtime.ts +26 -0
- package/src/jsx/jsx-runtime.ts +36 -0
- package/src/jsx/react-jsx-runtime.d.ts +0 -0
- package/src/jsx/types.ts +27 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,4733 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __esm = (fn, res) => function __init() {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
};
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
|
+
|
|
23
|
+
// src/dev/invariant.ts
|
|
24
|
+
function invariant(condition, message, context) {
|
|
25
|
+
if (!condition) {
|
|
26
|
+
const contextStr = context ? "\n" + JSON.stringify(context, null, 2) : "";
|
|
27
|
+
throw new Error(`[Askr Invariant] ${message}${contextStr}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function assertSchedulingPrecondition(condition, violationMessage) {
|
|
31
|
+
invariant(condition, `[Scheduler Precondition] ${violationMessage}`);
|
|
32
|
+
}
|
|
33
|
+
var init_invariant = __esm({
|
|
34
|
+
"src/dev/invariant.ts"() {
|
|
35
|
+
"use strict";
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// src/dev/logger.ts
|
|
40
|
+
function callConsole(method, args) {
|
|
41
|
+
const c = typeof console !== "undefined" ? console : void 0;
|
|
42
|
+
if (!c) return;
|
|
43
|
+
const fn = c[method];
|
|
44
|
+
if (typeof fn === "function") {
|
|
45
|
+
try {
|
|
46
|
+
fn.apply(console, args);
|
|
47
|
+
} catch {
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
var logger;
|
|
52
|
+
var init_logger = __esm({
|
|
53
|
+
"src/dev/logger.ts"() {
|
|
54
|
+
"use strict";
|
|
55
|
+
logger = {
|
|
56
|
+
debug: (...args) => {
|
|
57
|
+
if (process.env.NODE_ENV === "production") return;
|
|
58
|
+
callConsole("debug", args);
|
|
59
|
+
},
|
|
60
|
+
info: (...args) => {
|
|
61
|
+
if (process.env.NODE_ENV === "production") return;
|
|
62
|
+
callConsole("info", args);
|
|
63
|
+
},
|
|
64
|
+
warn: (...args) => {
|
|
65
|
+
if (process.env.NODE_ENV === "production") return;
|
|
66
|
+
callConsole("warn", args);
|
|
67
|
+
},
|
|
68
|
+
error: (...args) => {
|
|
69
|
+
callConsole("error", args);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// src/runtime/scheduler.ts
|
|
76
|
+
function isBulkCommitActive() {
|
|
77
|
+
try {
|
|
78
|
+
const fb = globalThis.__ASKR_FASTLANE;
|
|
79
|
+
return typeof fb?.isBulkCommitActive === "function" ? !!fb.isBulkCommitActive() : false;
|
|
80
|
+
} catch (e) {
|
|
81
|
+
void e;
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function isSchedulerExecuting() {
|
|
86
|
+
return globalScheduler.isExecuting();
|
|
87
|
+
}
|
|
88
|
+
function scheduleEventHandler(handler) {
|
|
89
|
+
return (event) => {
|
|
90
|
+
globalScheduler.setInHandler(true);
|
|
91
|
+
try {
|
|
92
|
+
handler.call(null, event);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
logger.error("[Askr] Event handler error:", error);
|
|
95
|
+
} finally {
|
|
96
|
+
globalScheduler.setInHandler(false);
|
|
97
|
+
const state2 = globalScheduler.getState();
|
|
98
|
+
if ((state2.queueLength ?? 0) > 0 && !state2.running) {
|
|
99
|
+
queueMicrotask(() => {
|
|
100
|
+
try {
|
|
101
|
+
if (!globalScheduler.isExecuting()) globalScheduler.flush();
|
|
102
|
+
} catch (err) {
|
|
103
|
+
setTimeout(() => {
|
|
104
|
+
throw err;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
var MAX_FLUSH_DEPTH, Scheduler, globalScheduler;
|
|
113
|
+
var init_scheduler = __esm({
|
|
114
|
+
"src/runtime/scheduler.ts"() {
|
|
115
|
+
"use strict";
|
|
116
|
+
init_invariant();
|
|
117
|
+
init_logger();
|
|
118
|
+
MAX_FLUSH_DEPTH = 50;
|
|
119
|
+
Scheduler = class {
|
|
120
|
+
constructor() {
|
|
121
|
+
this.q = [];
|
|
122
|
+
this.head = 0;
|
|
123
|
+
this.running = false;
|
|
124
|
+
this.inHandler = false;
|
|
125
|
+
this.depth = 0;
|
|
126
|
+
this.executionDepth = 0;
|
|
127
|
+
// for compat with existing diagnostics
|
|
128
|
+
// Monotonic flush version increments at end of each flush
|
|
129
|
+
this.flushVersion = 0;
|
|
130
|
+
// Best-effort microtask kick scheduling
|
|
131
|
+
this.kickScheduled = false;
|
|
132
|
+
// Escape hatch flag for runWithSyncProgress
|
|
133
|
+
this.allowSyncProgress = false;
|
|
134
|
+
// Waiters waiting for flushVersion >= target
|
|
135
|
+
this.waiters = [];
|
|
136
|
+
// Keep a lightweight taskCount for compatibility/diagnostics
|
|
137
|
+
this.taskCount = 0;
|
|
138
|
+
}
|
|
139
|
+
enqueue(task) {
|
|
140
|
+
assertSchedulingPrecondition(
|
|
141
|
+
typeof task === "function",
|
|
142
|
+
"enqueue() requires a function"
|
|
143
|
+
);
|
|
144
|
+
if (isBulkCommitActive() && !this.allowSyncProgress) {
|
|
145
|
+
if (process.env.NODE_ENV !== "production") {
|
|
146
|
+
throw new Error(
|
|
147
|
+
"[Scheduler] enqueue() during bulk commit (not allowed)"
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
this.q.push(task);
|
|
153
|
+
this.taskCount++;
|
|
154
|
+
if (!this.running && !this.kickScheduled && !this.inHandler && !isBulkCommitActive()) {
|
|
155
|
+
this.kickScheduled = true;
|
|
156
|
+
queueMicrotask(() => {
|
|
157
|
+
this.kickScheduled = false;
|
|
158
|
+
if (this.running) return;
|
|
159
|
+
if (isBulkCommitActive()) return;
|
|
160
|
+
try {
|
|
161
|
+
this.flush();
|
|
162
|
+
} catch (err) {
|
|
163
|
+
setTimeout(() => {
|
|
164
|
+
throw err;
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
flush() {
|
|
171
|
+
invariant(
|
|
172
|
+
!this.running,
|
|
173
|
+
"[Scheduler] flush() called while already running"
|
|
174
|
+
);
|
|
175
|
+
if (process.env.NODE_ENV !== "production") {
|
|
176
|
+
if (isBulkCommitActive() && !this.allowSyncProgress) {
|
|
177
|
+
throw new Error(
|
|
178
|
+
"[Scheduler] flush() started during bulk commit (not allowed)"
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
this.running = true;
|
|
183
|
+
this.depth = 0;
|
|
184
|
+
let fatal = null;
|
|
185
|
+
try {
|
|
186
|
+
while (this.head < this.q.length) {
|
|
187
|
+
this.depth++;
|
|
188
|
+
if (process.env.NODE_ENV !== "production" && this.depth > MAX_FLUSH_DEPTH) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
`[Scheduler] exceeded MAX_FLUSH_DEPTH (${MAX_FLUSH_DEPTH}). Likely infinite update loop.`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
const task = this.q[this.head++];
|
|
194
|
+
try {
|
|
195
|
+
this.executionDepth++;
|
|
196
|
+
task();
|
|
197
|
+
this.executionDepth--;
|
|
198
|
+
} catch (err) {
|
|
199
|
+
if (this.executionDepth > 0) this.executionDepth = 0;
|
|
200
|
+
fatal = err;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
if (this.taskCount > 0) this.taskCount--;
|
|
204
|
+
}
|
|
205
|
+
} finally {
|
|
206
|
+
this.running = false;
|
|
207
|
+
this.depth = 0;
|
|
208
|
+
this.executionDepth = 0;
|
|
209
|
+
if (this.head >= this.q.length) {
|
|
210
|
+
this.q.length = 0;
|
|
211
|
+
this.head = 0;
|
|
212
|
+
} else if (this.head > 0) {
|
|
213
|
+
const remaining = this.q.length - this.head;
|
|
214
|
+
if (this.head > 1024 || this.head > remaining) {
|
|
215
|
+
this.q = this.q.slice(this.head);
|
|
216
|
+
} else {
|
|
217
|
+
for (let i = 0; i < remaining; i++) {
|
|
218
|
+
this.q[i] = this.q[this.head + i];
|
|
219
|
+
}
|
|
220
|
+
this.q.length = remaining;
|
|
221
|
+
}
|
|
222
|
+
this.head = 0;
|
|
223
|
+
}
|
|
224
|
+
this.flushVersion++;
|
|
225
|
+
this.resolveWaiters();
|
|
226
|
+
}
|
|
227
|
+
if (fatal) throw fatal;
|
|
228
|
+
}
|
|
229
|
+
runWithSyncProgress(fn) {
|
|
230
|
+
const prev = this.allowSyncProgress;
|
|
231
|
+
this.allowSyncProgress = true;
|
|
232
|
+
const g = globalThis;
|
|
233
|
+
const origQueueMicrotask = g.queueMicrotask;
|
|
234
|
+
const origSetTimeout = g.setTimeout;
|
|
235
|
+
if (process.env.NODE_ENV !== "production") {
|
|
236
|
+
g.queueMicrotask = () => {
|
|
237
|
+
throw new Error(
|
|
238
|
+
"[Scheduler] queueMicrotask not allowed during runWithSyncProgress"
|
|
239
|
+
);
|
|
240
|
+
};
|
|
241
|
+
g.setTimeout = () => {
|
|
242
|
+
throw new Error(
|
|
243
|
+
"[Scheduler] setTimeout not allowed during runWithSyncProgress"
|
|
244
|
+
);
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
const startVersion = this.flushVersion;
|
|
248
|
+
try {
|
|
249
|
+
const res = fn();
|
|
250
|
+
if (!this.running && this.q.length - this.head > 0) {
|
|
251
|
+
this.flush();
|
|
252
|
+
}
|
|
253
|
+
if (process.env.NODE_ENV !== "production") {
|
|
254
|
+
if (this.q.length - this.head > 0) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
"[Scheduler] tasks remain after runWithSyncProgress flush"
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return res;
|
|
261
|
+
} finally {
|
|
262
|
+
if (process.env.NODE_ENV !== "production") {
|
|
263
|
+
g.queueMicrotask = origQueueMicrotask;
|
|
264
|
+
g.setTimeout = origSetTimeout;
|
|
265
|
+
}
|
|
266
|
+
try {
|
|
267
|
+
if (this.flushVersion === startVersion) {
|
|
268
|
+
this.flushVersion++;
|
|
269
|
+
this.resolveWaiters();
|
|
270
|
+
}
|
|
271
|
+
} catch (e) {
|
|
272
|
+
void e;
|
|
273
|
+
}
|
|
274
|
+
this.allowSyncProgress = prev;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
waitForFlush(targetVersion, timeoutMs = 2e3) {
|
|
278
|
+
const target = typeof targetVersion === "number" ? targetVersion : this.flushVersion + 1;
|
|
279
|
+
if (this.flushVersion >= target) return Promise.resolve();
|
|
280
|
+
return new Promise((resolve, reject) => {
|
|
281
|
+
const timer = setTimeout(() => {
|
|
282
|
+
const diag = {
|
|
283
|
+
flushVersion: this.flushVersion,
|
|
284
|
+
queueLen: this.q.length - this.head,
|
|
285
|
+
running: this.running,
|
|
286
|
+
inHandler: this.inHandler,
|
|
287
|
+
bulk: isBulkCommitActive(),
|
|
288
|
+
globals: {
|
|
289
|
+
__ASKR_LAST_FASTPATH_STATS: globalThis.__ASKR_LAST_FASTPATH_STATS,
|
|
290
|
+
__ASKR_LAST_BULK_TEXT_FASTPATH_STATS: globalThis.__ASKR_LAST_BULK_TEXT_FASTPATH_STATS,
|
|
291
|
+
__ASKR_FASTPATH_COUNTERS: globalThis.__ASKR_FASTPATH_COUNTERS
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
reject(
|
|
295
|
+
new Error(
|
|
296
|
+
`waitForFlush timeout ${timeoutMs}ms: ${JSON.stringify(diag)}`
|
|
297
|
+
)
|
|
298
|
+
);
|
|
299
|
+
}, timeoutMs);
|
|
300
|
+
this.waiters.push({ target, resolve, reject, timer });
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
getState() {
|
|
304
|
+
return {
|
|
305
|
+
queueLength: this.q.length - this.head,
|
|
306
|
+
running: this.running,
|
|
307
|
+
depth: this.depth,
|
|
308
|
+
executionDepth: this.executionDepth,
|
|
309
|
+
taskCount: this.taskCount,
|
|
310
|
+
flushVersion: this.flushVersion,
|
|
311
|
+
// New fields for optional inspection
|
|
312
|
+
inHandler: this.inHandler,
|
|
313
|
+
allowSyncProgress: this.allowSyncProgress
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
setInHandler(v) {
|
|
317
|
+
this.inHandler = v;
|
|
318
|
+
}
|
|
319
|
+
isInHandler() {
|
|
320
|
+
return this.inHandler;
|
|
321
|
+
}
|
|
322
|
+
isExecuting() {
|
|
323
|
+
return this.running || this.executionDepth > 0;
|
|
324
|
+
}
|
|
325
|
+
// Clear pending synchronous tasks (used by fastlane enter/exit)
|
|
326
|
+
clearPendingSyncTasks() {
|
|
327
|
+
const remaining = this.q.length - this.head;
|
|
328
|
+
if (remaining <= 0) return 0;
|
|
329
|
+
if (this.running) {
|
|
330
|
+
this.q.length = this.head;
|
|
331
|
+
this.taskCount = Math.max(0, this.taskCount - remaining);
|
|
332
|
+
queueMicrotask(() => {
|
|
333
|
+
try {
|
|
334
|
+
this.flushVersion++;
|
|
335
|
+
this.resolveWaiters();
|
|
336
|
+
} catch (e) {
|
|
337
|
+
void e;
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
return remaining;
|
|
341
|
+
}
|
|
342
|
+
this.q.length = 0;
|
|
343
|
+
this.head = 0;
|
|
344
|
+
this.taskCount = Math.max(0, this.taskCount - remaining);
|
|
345
|
+
this.flushVersion++;
|
|
346
|
+
this.resolveWaiters();
|
|
347
|
+
return remaining;
|
|
348
|
+
}
|
|
349
|
+
resolveWaiters() {
|
|
350
|
+
if (this.waiters.length === 0) return;
|
|
351
|
+
const ready = [];
|
|
352
|
+
const remaining = [];
|
|
353
|
+
for (const w of this.waiters) {
|
|
354
|
+
if (this.flushVersion >= w.target) {
|
|
355
|
+
if (w.timer) clearTimeout(w.timer);
|
|
356
|
+
ready.push(w.resolve);
|
|
357
|
+
} else {
|
|
358
|
+
remaining.push(w);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
this.waiters = remaining;
|
|
362
|
+
for (const r of ready) r();
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
globalScheduler = new Scheduler();
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// src/runtime/fastlane.ts
|
|
370
|
+
function enterBulkCommit() {
|
|
371
|
+
_bulkCommitActive = true;
|
|
372
|
+
_appliedParents = /* @__PURE__ */ new WeakSet();
|
|
373
|
+
try {
|
|
374
|
+
const cleared = globalScheduler.clearPendingSyncTasks?.() ?? 0;
|
|
375
|
+
if (process.env.NODE_ENV !== "production") {
|
|
376
|
+
const _g = globalThis;
|
|
377
|
+
_g.__ASKR_FASTLANE_CLEARED_TASKS = cleared;
|
|
378
|
+
}
|
|
379
|
+
} catch (err) {
|
|
380
|
+
if (process.env.NODE_ENV !== "production") throw err;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
function exitBulkCommit() {
|
|
384
|
+
_bulkCommitActive = false;
|
|
385
|
+
_appliedParents = null;
|
|
386
|
+
}
|
|
387
|
+
function isBulkCommitActive2() {
|
|
388
|
+
return _bulkCommitActive;
|
|
389
|
+
}
|
|
390
|
+
function markFastPathApplied(parent) {
|
|
391
|
+
if (!_appliedParents) return;
|
|
392
|
+
try {
|
|
393
|
+
_appliedParents.add(parent);
|
|
394
|
+
} catch (e) {
|
|
395
|
+
void e;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function isFastPathApplied(parent) {
|
|
399
|
+
return !!(_appliedParents && _appliedParents.has(parent));
|
|
400
|
+
}
|
|
401
|
+
function classifyUpdate(instance, result) {
|
|
402
|
+
if (!result || typeof result !== "object" || !("type" in result))
|
|
403
|
+
return { useFastPath: false, reason: "not-vnode" };
|
|
404
|
+
const vnode = result;
|
|
405
|
+
if (vnode == null || typeof vnode.type !== "string")
|
|
406
|
+
return { useFastPath: false, reason: "not-intrinsic" };
|
|
407
|
+
const parent = instance.target;
|
|
408
|
+
if (!parent) return { useFastPath: false, reason: "no-root" };
|
|
409
|
+
const firstChild = parent.children[0];
|
|
410
|
+
if (!firstChild) return { useFastPath: false, reason: "no-first-child" };
|
|
411
|
+
if (firstChild.tagName.toLowerCase() !== String(vnode.type).toLowerCase())
|
|
412
|
+
return { useFastPath: false, reason: "root-tag-mismatch" };
|
|
413
|
+
const children = vnode.children || vnode.props?.children;
|
|
414
|
+
if (!Array.isArray(children))
|
|
415
|
+
return { useFastPath: false, reason: "no-children-array" };
|
|
416
|
+
for (let i = 0; i < children.length; i++) {
|
|
417
|
+
const c = children[i];
|
|
418
|
+
if (typeof c === "object" && c !== null && "type" in c && typeof c.type === "function") {
|
|
419
|
+
return { useFastPath: false, reason: "component-child-present" };
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if (instance.mountOperations.length > 0)
|
|
423
|
+
return { useFastPath: false, reason: "pending-mounts" };
|
|
424
|
+
const oldKeyMap = getKeyMapForElement(firstChild);
|
|
425
|
+
const decision = isKeyedReorderFastPathEligible(
|
|
426
|
+
firstChild,
|
|
427
|
+
children,
|
|
428
|
+
oldKeyMap
|
|
429
|
+
);
|
|
430
|
+
if (!decision.useFastPath || decision.totalKeyed < 128)
|
|
431
|
+
return { ...decision, useFastPath: false, reason: "renderer-declined" };
|
|
432
|
+
return { ...decision, useFastPath: true };
|
|
433
|
+
}
|
|
434
|
+
function commitReorderOnly(instance, result) {
|
|
435
|
+
const evaluate2 = globalThis.__ASKR_RENDERER?.evaluate;
|
|
436
|
+
if (typeof evaluate2 !== "function") {
|
|
437
|
+
logger.warn(
|
|
438
|
+
"[Tempo][FASTPATH][DEV] renderer.evaluate not available; declining fast-lane"
|
|
439
|
+
);
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
const schedBefore = process.env.NODE_ENV !== "production" ? globalScheduler.getState() : null;
|
|
443
|
+
enterBulkCommit();
|
|
444
|
+
try {
|
|
445
|
+
globalScheduler.runWithSyncProgress(() => {
|
|
446
|
+
evaluate2(result, instance.target);
|
|
447
|
+
try {
|
|
448
|
+
const comp = (init_component(), __toCommonJS(component_exports));
|
|
449
|
+
if (typeof comp?.finalizeReadSubscriptions === "function") {
|
|
450
|
+
try {
|
|
451
|
+
comp.finalizeReadSubscriptions(instance);
|
|
452
|
+
} catch (e) {
|
|
453
|
+
if (process.env.NODE_ENV !== "production") throw e;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
} catch (e) {
|
|
457
|
+
void e;
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
try {
|
|
461
|
+
const clearedAfter = globalScheduler.clearPendingSyncTasks?.() ?? 0;
|
|
462
|
+
if (process.env.NODE_ENV !== "production") {
|
|
463
|
+
const _g = globalThis;
|
|
464
|
+
_g.__ASKR_FASTLANE_CLEARED_AFTER = clearedAfter;
|
|
465
|
+
}
|
|
466
|
+
} catch (err) {
|
|
467
|
+
if (process.env.NODE_ENV !== "production") throw err;
|
|
468
|
+
}
|
|
469
|
+
if (process.env.NODE_ENV !== "production") {
|
|
470
|
+
const _g = globalThis;
|
|
471
|
+
const commitCount = _g.__ASKR_LAST_FASTPATH_COMMIT_COUNT ?? 0;
|
|
472
|
+
const invariants = {
|
|
473
|
+
commitCount,
|
|
474
|
+
mountOps: instance.mountOperations.length,
|
|
475
|
+
cleanupFns: instance.cleanupFns.length
|
|
476
|
+
};
|
|
477
|
+
_g.__ASKR_LAST_FASTLANE_INVARIANTS = invariants;
|
|
478
|
+
if (commitCount !== 1) {
|
|
479
|
+
throw new Error(
|
|
480
|
+
"Fast-lane invariant violated: expected exactly one DOM commit during reorder-only commit"
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
if (invariants.mountOps > 0) {
|
|
484
|
+
throw new Error(
|
|
485
|
+
"Fast-lane invariant violated: mount operations were registered during bulk commit"
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
if (invariants.cleanupFns > 0) {
|
|
489
|
+
throw new Error(
|
|
490
|
+
"Fast-lane invariant violated: cleanup functions were added during bulk commit"
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
const schedAfter = globalScheduler.getState();
|
|
494
|
+
if (schedBefore && schedAfter && // Only fail if outstanding tasks increased — consuming existing tasks is allowed
|
|
495
|
+
schedAfter.taskCount > schedBefore.taskCount) {
|
|
496
|
+
try {
|
|
497
|
+
console.error(
|
|
498
|
+
"[FASTLANE] schedBefore, schedAfter",
|
|
499
|
+
schedBefore,
|
|
500
|
+
schedAfter
|
|
501
|
+
);
|
|
502
|
+
console.error(
|
|
503
|
+
"[FASTLANE] enqueue logs",
|
|
504
|
+
globalThis.__ASKR_ENQUEUE_LOGS
|
|
505
|
+
);
|
|
506
|
+
} catch (e) {
|
|
507
|
+
void e;
|
|
508
|
+
}
|
|
509
|
+
throw new Error(
|
|
510
|
+
"Fast-lane invariant violated: scheduler enqueued leftover work during bulk commit"
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
let finalState = globalScheduler.getState();
|
|
514
|
+
const executing = globalScheduler.isExecuting();
|
|
515
|
+
const outstandingAfter = Math.max(
|
|
516
|
+
0,
|
|
517
|
+
finalState.taskCount - (executing ? 1 : 0)
|
|
518
|
+
);
|
|
519
|
+
if (outstandingAfter !== 0) {
|
|
520
|
+
if (process.env.NODE_ENV !== "production") {
|
|
521
|
+
let attempts = 0;
|
|
522
|
+
while (attempts < 5) {
|
|
523
|
+
const cleared = globalScheduler.clearPendingSyncTasks?.() ?? 0;
|
|
524
|
+
if (cleared === 0) break;
|
|
525
|
+
attempts++;
|
|
526
|
+
}
|
|
527
|
+
finalState = globalScheduler.getState();
|
|
528
|
+
const outstandingAfter2 = Math.max(
|
|
529
|
+
0,
|
|
530
|
+
finalState.taskCount - (globalScheduler.isExecuting() ? 1 : 0)
|
|
531
|
+
);
|
|
532
|
+
if (outstandingAfter2 !== 0) {
|
|
533
|
+
try {
|
|
534
|
+
const _g2 = globalThis;
|
|
535
|
+
console.error(
|
|
536
|
+
"[FASTLANE] Post-commit enqueue logs:",
|
|
537
|
+
_g2.__ASKR_ENQUEUE_LOGS
|
|
538
|
+
);
|
|
539
|
+
console.error(
|
|
540
|
+
"[FASTLANE] Cleared counts:",
|
|
541
|
+
_g2.__ASKR_FASTLANE_CLEARED_TASKS,
|
|
542
|
+
_g2.__ASKR_FASTLANE_CLEARED_AFTER
|
|
543
|
+
);
|
|
544
|
+
} catch (err) {
|
|
545
|
+
void err;
|
|
546
|
+
}
|
|
547
|
+
throw new Error(
|
|
548
|
+
`Fast-lane invariant violated: scheduler has ${finalState.taskCount} pending task(s) after commit`
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
} else {
|
|
552
|
+
globalScheduler.clearPendingSyncTasks?.();
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return true;
|
|
557
|
+
} finally {
|
|
558
|
+
exitBulkCommit();
|
|
559
|
+
if (process.env.NODE_ENV !== "production") {
|
|
560
|
+
try {
|
|
561
|
+
const _g = globalThis;
|
|
562
|
+
_g.__ASKR_FASTLANE_BULK_FLAG_CHECK = isBulkCommitActive2();
|
|
563
|
+
} catch (e) {
|
|
564
|
+
void e;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
if (process.env.NODE_ENV !== "production") {
|
|
569
|
+
const _g = globalThis;
|
|
570
|
+
if (_g.__ASKR_FASTLANE_BULK_FLAG_CHECK) {
|
|
571
|
+
delete _g.__ASKR_FASTLANE_BULK_FLAG_CHECK;
|
|
572
|
+
throw new Error(
|
|
573
|
+
"Fast-lane invariant violated: bulk commit flag still set after commit"
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
function tryRuntimeFastLaneSync(instance, result) {
|
|
579
|
+
const cls = classifyUpdate(instance, result);
|
|
580
|
+
if (!cls.useFastPath) return false;
|
|
581
|
+
try {
|
|
582
|
+
return commitReorderOnly(instance, result);
|
|
583
|
+
} catch (err) {
|
|
584
|
+
if (process.env.NODE_ENV !== "production") throw err;
|
|
585
|
+
return false;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
var _bulkCommitActive, _appliedParents;
|
|
589
|
+
var init_fastlane = __esm({
|
|
590
|
+
"src/runtime/fastlane.ts"() {
|
|
591
|
+
"use strict";
|
|
592
|
+
init_scheduler();
|
|
593
|
+
init_logger();
|
|
594
|
+
init_dom();
|
|
595
|
+
_bulkCommitActive = false;
|
|
596
|
+
_appliedParents = null;
|
|
597
|
+
if (typeof globalThis !== "undefined") {
|
|
598
|
+
const _g = globalThis;
|
|
599
|
+
_g.__ASKR_FASTLANE = {
|
|
600
|
+
isBulkCommitActive: isBulkCommitActive2,
|
|
601
|
+
enterBulkCommit,
|
|
602
|
+
exitBulkCommit,
|
|
603
|
+
tryRuntimeFastLaneSync,
|
|
604
|
+
markFastPathApplied,
|
|
605
|
+
isFastPathApplied
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
// src/jsx/jsx-runtime.ts
|
|
612
|
+
function jsx(type, props, key) {
|
|
613
|
+
return {
|
|
614
|
+
type,
|
|
615
|
+
props: props || {},
|
|
616
|
+
key
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
function jsxs(type, props, key) {
|
|
620
|
+
return jsx(type, props, key);
|
|
621
|
+
}
|
|
622
|
+
var Fragment;
|
|
623
|
+
var init_jsx_runtime = __esm({
|
|
624
|
+
"src/jsx/jsx-runtime.ts"() {
|
|
625
|
+
"use strict";
|
|
626
|
+
Fragment = /* @__PURE__ */ Symbol.for("@askrjs/askr.Fragment");
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
// src/runtime/context.ts
|
|
631
|
+
function withContext(frame, fn) {
|
|
632
|
+
const oldFrame = currentContextFrame;
|
|
633
|
+
currentContextFrame = frame;
|
|
634
|
+
try {
|
|
635
|
+
return fn();
|
|
636
|
+
} finally {
|
|
637
|
+
currentContextFrame = oldFrame;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
function withAsyncResourceContext(frame, fn) {
|
|
641
|
+
const oldFrame = currentAsyncResourceFrame;
|
|
642
|
+
currentAsyncResourceFrame = frame;
|
|
643
|
+
try {
|
|
644
|
+
return fn();
|
|
645
|
+
} finally {
|
|
646
|
+
currentAsyncResourceFrame = oldFrame;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
function defineContext(defaultValue) {
|
|
650
|
+
const key = /* @__PURE__ */ Symbol("AskrContext");
|
|
651
|
+
return {
|
|
652
|
+
key,
|
|
653
|
+
defaultValue,
|
|
654
|
+
Scope: (props) => {
|
|
655
|
+
return {
|
|
656
|
+
type: ContextScopeComponent,
|
|
657
|
+
props: { key, value: props.value, children: props.children }
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
function readContext(context) {
|
|
663
|
+
const frame = currentContextFrame || currentAsyncResourceFrame;
|
|
664
|
+
if (!frame) {
|
|
665
|
+
throw new Error(
|
|
666
|
+
"readContext() can only be called during component render or async resource execution. Ensure you are calling this from inside your component or resource function."
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
let current2 = frame;
|
|
670
|
+
while (current2) {
|
|
671
|
+
const values = current2.values;
|
|
672
|
+
if (values && values.has(context.key)) {
|
|
673
|
+
return values.get(context.key);
|
|
674
|
+
}
|
|
675
|
+
current2 = current2.parent;
|
|
676
|
+
}
|
|
677
|
+
return context.defaultValue;
|
|
678
|
+
}
|
|
679
|
+
function ContextScopeComponent(props) {
|
|
680
|
+
const key = props["key"];
|
|
681
|
+
const value = props["value"];
|
|
682
|
+
const children = props["children"];
|
|
683
|
+
const instance = getCurrentComponentInstance();
|
|
684
|
+
const parentFrame = (() => {
|
|
685
|
+
if (currentContextFrame) return currentContextFrame;
|
|
686
|
+
if (instance && instance.ownerFrame) return instance.ownerFrame;
|
|
687
|
+
return null;
|
|
688
|
+
})();
|
|
689
|
+
const newFrame = {
|
|
690
|
+
parent: parentFrame,
|
|
691
|
+
values: /* @__PURE__ */ new Map([[key, value]])
|
|
692
|
+
};
|
|
693
|
+
if (Array.isArray(children)) {
|
|
694
|
+
return children.map((child) => {
|
|
695
|
+
if (typeof child === "function") {
|
|
696
|
+
return {
|
|
697
|
+
type: ContextFunctionChildInvoker,
|
|
698
|
+
props: {
|
|
699
|
+
fn: child,
|
|
700
|
+
__frame: newFrame,
|
|
701
|
+
__owner: getCurrentComponentInstance()
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
return markWithFrame(child, newFrame);
|
|
706
|
+
});
|
|
707
|
+
} else if (typeof children === "function") {
|
|
708
|
+
return {
|
|
709
|
+
type: ContextFunctionChildInvoker,
|
|
710
|
+
props: {
|
|
711
|
+
fn: children,
|
|
712
|
+
__frame: newFrame,
|
|
713
|
+
__owner: getCurrentComponentInstance()
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
} else if (children) {
|
|
717
|
+
return markWithFrame(children, newFrame);
|
|
718
|
+
}
|
|
719
|
+
return null;
|
|
720
|
+
}
|
|
721
|
+
function markWithFrame(node, frame) {
|
|
722
|
+
if (typeof node === "object" && node !== null) {
|
|
723
|
+
const obj = node;
|
|
724
|
+
obj[CONTEXT_FRAME_SYMBOL] = frame;
|
|
725
|
+
const children = obj.children;
|
|
726
|
+
if (Array.isArray(children)) {
|
|
727
|
+
for (let i = 0; i < children.length; i++) {
|
|
728
|
+
const child = children[i];
|
|
729
|
+
if (child) {
|
|
730
|
+
children[i] = markWithFrame(child, frame);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
} else if (children) {
|
|
734
|
+
obj.children = markWithFrame(children, frame);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
return node;
|
|
738
|
+
}
|
|
739
|
+
function ContextFunctionChildInvoker(props) {
|
|
740
|
+
const { fn, __frame } = props;
|
|
741
|
+
const res = withContext(__frame, () => fn());
|
|
742
|
+
if (res) return markWithFrame(res, __frame);
|
|
743
|
+
return null;
|
|
744
|
+
}
|
|
745
|
+
function getCurrentContextFrame() {
|
|
746
|
+
return currentContextFrame;
|
|
747
|
+
}
|
|
748
|
+
var CONTEXT_FRAME_SYMBOL, currentContextFrame, currentAsyncResourceFrame;
|
|
749
|
+
var init_context = __esm({
|
|
750
|
+
"src/runtime/context.ts"() {
|
|
751
|
+
"use strict";
|
|
752
|
+
init_component();
|
|
753
|
+
CONTEXT_FRAME_SYMBOL = /* @__PURE__ */ Symbol("__tempoContextFrame__");
|
|
754
|
+
currentContextFrame = null;
|
|
755
|
+
currentAsyncResourceFrame = null;
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
// src/renderer/dom.ts
|
|
760
|
+
function cleanupInstanceIfPresent(node) {
|
|
761
|
+
if (!node) return;
|
|
762
|
+
if (!(node instanceof Element)) return;
|
|
763
|
+
try {
|
|
764
|
+
const inst = node.__ASKR_INSTANCE;
|
|
765
|
+
if (inst) {
|
|
766
|
+
cleanupComponent(inst);
|
|
767
|
+
try {
|
|
768
|
+
delete node.__ASKR_INSTANCE;
|
|
769
|
+
} catch (e) {
|
|
770
|
+
void e;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
} catch (err) {
|
|
774
|
+
void err;
|
|
775
|
+
}
|
|
776
|
+
try {
|
|
777
|
+
const descendants = node.querySelectorAll("*");
|
|
778
|
+
for (const d of Array.from(descendants)) {
|
|
779
|
+
try {
|
|
780
|
+
const inst = d.__ASKR_INSTANCE;
|
|
781
|
+
if (inst) {
|
|
782
|
+
cleanupComponent(inst);
|
|
783
|
+
try {
|
|
784
|
+
delete d.__ASKR_INSTANCE;
|
|
785
|
+
} catch (e) {
|
|
786
|
+
void e;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
} catch (err) {
|
|
790
|
+
void err;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
} catch (err) {
|
|
794
|
+
void err;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
function _isDOMElement(node) {
|
|
798
|
+
return typeof node === "object" && node !== null && "type" in node;
|
|
799
|
+
}
|
|
800
|
+
function getKeyMapForElement(el) {
|
|
801
|
+
return keyedElements.get(el);
|
|
802
|
+
}
|
|
803
|
+
function removeElementListeners(element) {
|
|
804
|
+
const map = elementListeners.get(element);
|
|
805
|
+
if (map) {
|
|
806
|
+
for (const [eventName, entry] of map) {
|
|
807
|
+
element.removeEventListener(eventName, entry.handler);
|
|
808
|
+
}
|
|
809
|
+
elementListeners.delete(element);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
function removeAllListeners(root) {
|
|
813
|
+
if (!root) return;
|
|
814
|
+
removeElementListeners(root);
|
|
815
|
+
const children = root.querySelectorAll("*");
|
|
816
|
+
for (let i = 0; i < children.length; i++) {
|
|
817
|
+
removeElementListeners(children[i]);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
function evaluate(node, target, context) {
|
|
821
|
+
if (!target) return;
|
|
822
|
+
if (context && domRanges.has(context)) {
|
|
823
|
+
const range = domRanges.get(context);
|
|
824
|
+
let current2 = range.start.nextSibling;
|
|
825
|
+
while (current2 && current2 !== range.end) {
|
|
826
|
+
const next = current2.nextSibling;
|
|
827
|
+
current2.remove();
|
|
828
|
+
current2 = next;
|
|
829
|
+
}
|
|
830
|
+
const dom = createDOMNode(node);
|
|
831
|
+
if (dom) {
|
|
832
|
+
target.insertBefore(dom, range.end);
|
|
833
|
+
}
|
|
834
|
+
} else if (context) {
|
|
835
|
+
const start = document.createComment("component-start");
|
|
836
|
+
const end = document.createComment("component-end");
|
|
837
|
+
target.appendChild(start);
|
|
838
|
+
target.appendChild(end);
|
|
839
|
+
domRanges.set(context, { start, end });
|
|
840
|
+
const dom = createDOMNode(node);
|
|
841
|
+
if (dom) {
|
|
842
|
+
target.insertBefore(dom, end);
|
|
843
|
+
}
|
|
844
|
+
} else {
|
|
845
|
+
const vnode = node;
|
|
846
|
+
const firstChild = target.children[0];
|
|
847
|
+
if (firstChild && _isDOMElement(vnode) && typeof vnode.type === "string" && firstChild.tagName.toLowerCase() === vnode.type.toLowerCase()) {
|
|
848
|
+
const vnodeChildren = vnode.children || vnode.props?.children;
|
|
849
|
+
let isSimpleTextVNode = false;
|
|
850
|
+
let textContent;
|
|
851
|
+
if (!Array.isArray(vnodeChildren)) {
|
|
852
|
+
if (typeof vnodeChildren === "string" || typeof vnodeChildren === "number") {
|
|
853
|
+
isSimpleTextVNode = true;
|
|
854
|
+
textContent = String(vnodeChildren);
|
|
855
|
+
}
|
|
856
|
+
} else if (vnodeChildren.length === 1) {
|
|
857
|
+
const child = vnodeChildren[0];
|
|
858
|
+
if (typeof child === "string" || typeof child === "number") {
|
|
859
|
+
isSimpleTextVNode = true;
|
|
860
|
+
textContent = String(child);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
if (isSimpleTextVNode && firstChild.childNodes.length === 1 && firstChild.firstChild?.nodeType === 3) {
|
|
864
|
+
firstChild.firstChild.data = textContent;
|
|
865
|
+
} else {
|
|
866
|
+
if (vnodeChildren) {
|
|
867
|
+
if (Array.isArray(vnodeChildren)) {
|
|
868
|
+
const hasKeys = vnodeChildren.some(
|
|
869
|
+
(child) => typeof child === "object" && child !== null && "key" in child
|
|
870
|
+
);
|
|
871
|
+
if (hasKeys) {
|
|
872
|
+
let oldKeyMap = keyedElements.get(firstChild);
|
|
873
|
+
if (!oldKeyMap) {
|
|
874
|
+
oldKeyMap = /* @__PURE__ */ new Map();
|
|
875
|
+
}
|
|
876
|
+
try {
|
|
877
|
+
if (process.env.ASKR_FORCE_BULK_POSREUSE === "1") {
|
|
878
|
+
try {
|
|
879
|
+
const keyedVnodes = [];
|
|
880
|
+
for (let i = 0; i < vnodeChildren.length; i++) {
|
|
881
|
+
const c = vnodeChildren[i];
|
|
882
|
+
if (_isDOMElement(c) && c.key !== void 0) {
|
|
883
|
+
keyedVnodes.push({
|
|
884
|
+
key: c.key,
|
|
885
|
+
vnode: c
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
if (keyedVnodes.length > 0 && keyedVnodes.length === vnodeChildren.length) {
|
|
890
|
+
logger.warn(
|
|
891
|
+
"[Askr][FASTPATH] forced positional bulk keyed reuse (evaluate-level)"
|
|
892
|
+
);
|
|
893
|
+
const stats = performBulkPositionalKeyedTextUpdate(
|
|
894
|
+
firstChild,
|
|
895
|
+
keyedVnodes
|
|
896
|
+
);
|
|
897
|
+
if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
|
|
898
|
+
try {
|
|
899
|
+
const gl = globalThis;
|
|
900
|
+
gl.__ASKR_LAST_FASTPATH_STATS = stats;
|
|
901
|
+
gl.__ASKR_LAST_FASTPATH_COMMIT_COUNT = 1;
|
|
902
|
+
const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
|
|
903
|
+
counters.bulkKeyedPositionalForced = (counters.bulkKeyedPositionalForced || 0) + 1;
|
|
904
|
+
gl.__ASKR_FASTPATH_COUNTERS = counters;
|
|
905
|
+
} catch (e) {
|
|
906
|
+
void e;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
try {
|
|
910
|
+
const map = /* @__PURE__ */ new Map();
|
|
911
|
+
const children = Array.from(firstChild.children);
|
|
912
|
+
for (let i = 0; i < children.length; i++) {
|
|
913
|
+
const el = children[i];
|
|
914
|
+
const k = el.getAttribute("data-key");
|
|
915
|
+
if (k !== null) {
|
|
916
|
+
map.set(k, el);
|
|
917
|
+
const n = Number(k);
|
|
918
|
+
if (!Number.isNaN(n)) map.set(n, el);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
keyedElements.set(firstChild, map);
|
|
922
|
+
} catch (e) {
|
|
923
|
+
void e;
|
|
924
|
+
}
|
|
925
|
+
} else {
|
|
926
|
+
const newKeyMap = reconcileKeyedChildren(
|
|
927
|
+
firstChild,
|
|
928
|
+
vnodeChildren,
|
|
929
|
+
oldKeyMap
|
|
930
|
+
);
|
|
931
|
+
keyedElements.set(firstChild, newKeyMap);
|
|
932
|
+
}
|
|
933
|
+
} catch (err) {
|
|
934
|
+
logger.warn(
|
|
935
|
+
"[Askr][FASTPATH] forced bulk path failed, falling back",
|
|
936
|
+
err
|
|
937
|
+
);
|
|
938
|
+
const newKeyMap = reconcileKeyedChildren(
|
|
939
|
+
firstChild,
|
|
940
|
+
vnodeChildren,
|
|
941
|
+
oldKeyMap
|
|
942
|
+
);
|
|
943
|
+
keyedElements.set(firstChild, newKeyMap);
|
|
944
|
+
}
|
|
945
|
+
} else {
|
|
946
|
+
const newKeyMap = reconcileKeyedChildren(
|
|
947
|
+
firstChild,
|
|
948
|
+
vnodeChildren,
|
|
949
|
+
oldKeyMap
|
|
950
|
+
);
|
|
951
|
+
keyedElements.set(firstChild, newKeyMap);
|
|
952
|
+
}
|
|
953
|
+
} catch (e) {
|
|
954
|
+
void e;
|
|
955
|
+
const newKeyMap = reconcileKeyedChildren(
|
|
956
|
+
firstChild,
|
|
957
|
+
vnodeChildren,
|
|
958
|
+
oldKeyMap
|
|
959
|
+
);
|
|
960
|
+
keyedElements.set(firstChild, newKeyMap);
|
|
961
|
+
}
|
|
962
|
+
} else {
|
|
963
|
+
if (isBulkTextFastPathEligible(firstChild, vnodeChildren)) {
|
|
964
|
+
const stats = performBulkTextReplace(firstChild, vnodeChildren);
|
|
965
|
+
if (process.env.NODE_ENV !== "production") {
|
|
966
|
+
try {
|
|
967
|
+
const gl = globalThis;
|
|
968
|
+
gl.__ASKR_LAST_BULK_TEXT_FASTPATH_STATS = stats;
|
|
969
|
+
const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
|
|
970
|
+
counters.bulkTextHits = (counters.bulkTextHits || 0) + 1;
|
|
971
|
+
gl.__ASKR_FASTPATH_COUNTERS = counters;
|
|
972
|
+
} catch (e) {
|
|
973
|
+
void e;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
} else {
|
|
977
|
+
if (process.env.NODE_ENV !== "production") {
|
|
978
|
+
try {
|
|
979
|
+
const gl = globalThis;
|
|
980
|
+
const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
|
|
981
|
+
counters.bulkTextMisses = (counters.bulkTextMisses || 0) + 1;
|
|
982
|
+
gl.__ASKR_FASTPATH_COUNTERS = counters;
|
|
983
|
+
} catch (e) {
|
|
984
|
+
void e;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
updateUnkeyedChildren(firstChild, vnodeChildren);
|
|
988
|
+
keyedElements.delete(firstChild);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
} else {
|
|
992
|
+
firstChild.textContent = "";
|
|
993
|
+
const dom = createDOMNode(vnodeChildren);
|
|
994
|
+
if (dom) firstChild.appendChild(dom);
|
|
995
|
+
keyedElements.delete(firstChild);
|
|
996
|
+
}
|
|
997
|
+
} else {
|
|
998
|
+
firstChild.textContent = "";
|
|
999
|
+
keyedElements.delete(firstChild);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
updateElementFromVnode(firstChild, vnode, false);
|
|
1003
|
+
} else {
|
|
1004
|
+
target.textContent = "";
|
|
1005
|
+
if (_isDOMElement(vnode) && typeof vnode.type === "string") {
|
|
1006
|
+
const children = vnode.children;
|
|
1007
|
+
if (Array.isArray(children) && children.some(
|
|
1008
|
+
(child) => typeof child === "object" && child !== null && "key" in child
|
|
1009
|
+
)) {
|
|
1010
|
+
const el = document.createElement(vnode.type);
|
|
1011
|
+
target.appendChild(el);
|
|
1012
|
+
const props = vnode.props || {};
|
|
1013
|
+
for (const [key, value] of Object.entries(props)) {
|
|
1014
|
+
if (key === "children" || key === "key") continue;
|
|
1015
|
+
if (value === void 0 || value === null || value === false)
|
|
1016
|
+
continue;
|
|
1017
|
+
if (key.startsWith("on") && key.length > 2) {
|
|
1018
|
+
const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
|
|
1019
|
+
const wrappedHandler = (event) => {
|
|
1020
|
+
globalScheduler.setInHandler(true);
|
|
1021
|
+
try {
|
|
1022
|
+
value(event);
|
|
1023
|
+
} catch (error) {
|
|
1024
|
+
logger.error("[Askr] Event handler error:", error);
|
|
1025
|
+
} finally {
|
|
1026
|
+
globalScheduler.setInHandler(false);
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
el.addEventListener(eventName, wrappedHandler);
|
|
1030
|
+
if (!elementListeners.has(el)) {
|
|
1031
|
+
elementListeners.set(el, /* @__PURE__ */ new Map());
|
|
1032
|
+
}
|
|
1033
|
+
elementListeners.get(el).set(eventName, {
|
|
1034
|
+
handler: wrappedHandler,
|
|
1035
|
+
original: value
|
|
1036
|
+
});
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
if (key === "class" || key === "className") {
|
|
1040
|
+
el.className = String(value);
|
|
1041
|
+
} else if (key === "value" || key === "checked") {
|
|
1042
|
+
el[key] = value;
|
|
1043
|
+
} else {
|
|
1044
|
+
el.setAttribute(key, String(value));
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
const newKeyMap = reconcileKeyedChildren(el, children, void 0);
|
|
1048
|
+
keyedElements.set(el, newKeyMap);
|
|
1049
|
+
return;
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
const dom = createDOMNode(vnode);
|
|
1054
|
+
if (dom) {
|
|
1055
|
+
target.appendChild(dom);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
function isKeyedReorderFastPathEligible(parent, newChildren, oldKeyMap) {
|
|
1061
|
+
const keyedVnodes = [];
|
|
1062
|
+
for (let i = 0; i < newChildren.length; i++) {
|
|
1063
|
+
const child = newChildren[i];
|
|
1064
|
+
if (_isDOMElement(child) && child.key !== void 0) {
|
|
1065
|
+
keyedVnodes.push({ key: child.key, vnode: child });
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
const totalKeyed = keyedVnodes.length;
|
|
1069
|
+
const newKeyOrder = keyedVnodes.map((kv) => kv.key);
|
|
1070
|
+
const oldKeyOrder = oldKeyMap ? Array.from(oldKeyMap.keys()) : [];
|
|
1071
|
+
let moveCount = 0;
|
|
1072
|
+
for (let i = 0; i < newKeyOrder.length; i++) {
|
|
1073
|
+
const k = newKeyOrder[i];
|
|
1074
|
+
if (i >= oldKeyOrder.length || oldKeyOrder[i] !== k || !oldKeyMap?.has(k)) {
|
|
1075
|
+
moveCount++;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
const FAST_MOVE_THRESHOLD_ABS = 64;
|
|
1079
|
+
const FAST_MOVE_THRESHOLD_REL = 0.1;
|
|
1080
|
+
const cheapMoveTrigger = totalKeyed >= 128 && oldKeyOrder.length > 0 && moveCount > Math.max(
|
|
1081
|
+
FAST_MOVE_THRESHOLD_ABS,
|
|
1082
|
+
Math.floor(totalKeyed * FAST_MOVE_THRESHOLD_REL)
|
|
1083
|
+
);
|
|
1084
|
+
let lisTrigger = false;
|
|
1085
|
+
let lisLen = 0;
|
|
1086
|
+
if (totalKeyed >= 128) {
|
|
1087
|
+
const parentChildren = Array.from(parent.children);
|
|
1088
|
+
const positions = new Array(keyedVnodes.length).fill(-1);
|
|
1089
|
+
for (let i = 0; i < keyedVnodes.length; i++) {
|
|
1090
|
+
const key = keyedVnodes[i].key;
|
|
1091
|
+
const el = oldKeyMap?.get(key);
|
|
1092
|
+
if (el && el.parentElement === parent) {
|
|
1093
|
+
positions[i] = parentChildren.indexOf(el);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
const tails = [];
|
|
1097
|
+
for (let i = 0; i < positions.length; i++) {
|
|
1098
|
+
const pos = positions[i];
|
|
1099
|
+
if (pos === -1) continue;
|
|
1100
|
+
let lo = 0;
|
|
1101
|
+
let hi = tails.length;
|
|
1102
|
+
while (lo < hi) {
|
|
1103
|
+
const mid = lo + hi >> 1;
|
|
1104
|
+
if (tails[mid] < pos) lo = mid + 1;
|
|
1105
|
+
else hi = mid;
|
|
1106
|
+
}
|
|
1107
|
+
if (lo === tails.length) tails.push(pos);
|
|
1108
|
+
else tails[lo] = pos;
|
|
1109
|
+
}
|
|
1110
|
+
lisLen = tails.length;
|
|
1111
|
+
lisTrigger = lisLen < Math.floor(totalKeyed * 0.5);
|
|
1112
|
+
}
|
|
1113
|
+
let hasPropsPresent = false;
|
|
1114
|
+
for (let i = 0; i < keyedVnodes.length; i++) {
|
|
1115
|
+
const vnode = keyedVnodes[i].vnode;
|
|
1116
|
+
if (!_isDOMElement(vnode)) continue;
|
|
1117
|
+
const props = vnode.props || {};
|
|
1118
|
+
for (const k of Object.keys(props)) {
|
|
1119
|
+
if (k === "children" || k === "key") continue;
|
|
1120
|
+
if (k.startsWith("on") && k.length > 2) continue;
|
|
1121
|
+
if (k.startsWith("data-")) continue;
|
|
1122
|
+
hasPropsPresent = true;
|
|
1123
|
+
break;
|
|
1124
|
+
}
|
|
1125
|
+
if (hasPropsPresent) break;
|
|
1126
|
+
}
|
|
1127
|
+
let hasPropChanges = false;
|
|
1128
|
+
for (let i = 0; i < keyedVnodes.length; i++) {
|
|
1129
|
+
const { key, vnode } = keyedVnodes[i];
|
|
1130
|
+
const el = oldKeyMap?.get(key);
|
|
1131
|
+
if (!el || !_isDOMElement(vnode)) continue;
|
|
1132
|
+
const props = vnode.props || {};
|
|
1133
|
+
for (const k of Object.keys(props)) {
|
|
1134
|
+
if (k === "children" || k === "key") continue;
|
|
1135
|
+
if (k.startsWith("on") && k.length > 2) continue;
|
|
1136
|
+
if (k.startsWith("data-")) continue;
|
|
1137
|
+
const v = props[k];
|
|
1138
|
+
try {
|
|
1139
|
+
if (k === "class" || k === "className") {
|
|
1140
|
+
if (el.className !== String(v)) {
|
|
1141
|
+
hasPropChanges = true;
|
|
1142
|
+
break;
|
|
1143
|
+
}
|
|
1144
|
+
} else if (k === "value" || k === "checked") {
|
|
1145
|
+
if (el[k] !== v) {
|
|
1146
|
+
hasPropChanges = true;
|
|
1147
|
+
break;
|
|
1148
|
+
}
|
|
1149
|
+
} else {
|
|
1150
|
+
const attr = el.getAttribute(k);
|
|
1151
|
+
if (v === void 0 || v === null || v === false) {
|
|
1152
|
+
if (attr !== null) {
|
|
1153
|
+
hasPropChanges = true;
|
|
1154
|
+
break;
|
|
1155
|
+
}
|
|
1156
|
+
} else if (String(v) !== attr) {
|
|
1157
|
+
hasPropChanges = true;
|
|
1158
|
+
break;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
} catch {
|
|
1162
|
+
hasPropChanges = true;
|
|
1163
|
+
break;
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
if (hasPropChanges) break;
|
|
1167
|
+
}
|
|
1168
|
+
const useFastPath = (cheapMoveTrigger || lisTrigger) && !hasPropChanges && !hasPropsPresent;
|
|
1169
|
+
return {
|
|
1170
|
+
useFastPath,
|
|
1171
|
+
totalKeyed,
|
|
1172
|
+
moveCount,
|
|
1173
|
+
lisLen,
|
|
1174
|
+
hasPropChanges
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
function reconcileKeyedChildren(parent, newChildren, oldKeyMap) {
|
|
1178
|
+
const newKeyMap = /* @__PURE__ */ new Map();
|
|
1179
|
+
const keyedVnodes = [];
|
|
1180
|
+
const unkeyedVnodes = [];
|
|
1181
|
+
for (let i = 0; i < newChildren.length; i++) {
|
|
1182
|
+
const child = newChildren[i];
|
|
1183
|
+
if (_isDOMElement(child) && child.key !== void 0) {
|
|
1184
|
+
keyedVnodes.push({ key: child.key, vnode: child });
|
|
1185
|
+
} else {
|
|
1186
|
+
unkeyedVnodes.push(child);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
const totalKeyed = keyedVnodes.length;
|
|
1190
|
+
const newKeyOrder = keyedVnodes.map((kv) => kv.key);
|
|
1191
|
+
const oldKeyOrder = oldKeyMap ? Array.from(oldKeyMap.keys()) : [];
|
|
1192
|
+
let moveCount = 0;
|
|
1193
|
+
for (let i = 0; i < newKeyOrder.length; i++) {
|
|
1194
|
+
const k2 = newKeyOrder[i];
|
|
1195
|
+
if (i >= oldKeyOrder.length || oldKeyOrder[i] !== k2 || !oldKeyMap?.has(k2)) {
|
|
1196
|
+
moveCount++;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
let hasPropChanges = false;
|
|
1200
|
+
for (let i = 0; i < keyedVnodes.length; i++) {
|
|
1201
|
+
const { key, vnode } = keyedVnodes[i];
|
|
1202
|
+
const el = oldKeyMap?.get(key);
|
|
1203
|
+
if (!el || !_isDOMElement(vnode)) continue;
|
|
1204
|
+
const props = vnode.props || {};
|
|
1205
|
+
for (const k2 of Object.keys(props)) {
|
|
1206
|
+
if (k2 === "children" || k2 === "key") continue;
|
|
1207
|
+
if (k2.startsWith("on") && k2.length > 2) {
|
|
1208
|
+
continue;
|
|
1209
|
+
}
|
|
1210
|
+
const v = props[k2];
|
|
1211
|
+
try {
|
|
1212
|
+
if (k2 === "class" || k2 === "className") {
|
|
1213
|
+
if (el.className !== String(v)) {
|
|
1214
|
+
logger.warn("[Askr][FASTPATH][DEV] prop mismatch", {
|
|
1215
|
+
key,
|
|
1216
|
+
prop: k2,
|
|
1217
|
+
expected: String(v),
|
|
1218
|
+
actual: el.className
|
|
1219
|
+
});
|
|
1220
|
+
hasPropChanges = true;
|
|
1221
|
+
break;
|
|
1222
|
+
}
|
|
1223
|
+
} else if (k2 === "value" || k2 === "checked") {
|
|
1224
|
+
if (el[k2] !== v) {
|
|
1225
|
+
logger.warn("[Askr][FASTPATH][DEV] prop mismatch", {
|
|
1226
|
+
key,
|
|
1227
|
+
prop: k2,
|
|
1228
|
+
expected: v,
|
|
1229
|
+
actual: el[k2]
|
|
1230
|
+
});
|
|
1231
|
+
hasPropChanges = true;
|
|
1232
|
+
break;
|
|
1233
|
+
}
|
|
1234
|
+
} else {
|
|
1235
|
+
const attr = el.getAttribute(k2);
|
|
1236
|
+
if (v === void 0 || v === null || v === false) {
|
|
1237
|
+
if (attr !== null) {
|
|
1238
|
+
logger.warn(
|
|
1239
|
+
"[Askr][FASTPATH][DEV] prop mismatch (missing attr)",
|
|
1240
|
+
{
|
|
1241
|
+
key,
|
|
1242
|
+
prop: k2,
|
|
1243
|
+
expected: v,
|
|
1244
|
+
actual: attr
|
|
1245
|
+
}
|
|
1246
|
+
);
|
|
1247
|
+
hasPropChanges = true;
|
|
1248
|
+
break;
|
|
1249
|
+
}
|
|
1250
|
+
} else if (String(v) !== attr) {
|
|
1251
|
+
logger.warn("[Askr][FASTPATH][DEV] prop mismatch (attr diff)", {
|
|
1252
|
+
key,
|
|
1253
|
+
prop: k2,
|
|
1254
|
+
expected: String(v),
|
|
1255
|
+
actual: attr
|
|
1256
|
+
});
|
|
1257
|
+
hasPropChanges = true;
|
|
1258
|
+
break;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
} catch {
|
|
1262
|
+
hasPropChanges = true;
|
|
1263
|
+
break;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
if (hasPropChanges) break;
|
|
1267
|
+
}
|
|
1268
|
+
const decision = isKeyedReorderFastPathEligible(
|
|
1269
|
+
parent,
|
|
1270
|
+
newChildren,
|
|
1271
|
+
oldKeyMap
|
|
1272
|
+
);
|
|
1273
|
+
const useFastPath = decision.useFastPath;
|
|
1274
|
+
logger.warn("[Askr][FASTPATH][DEV] decision", decision);
|
|
1275
|
+
try {
|
|
1276
|
+
const hugeThreshold = Number(process.env.ASKR_BULK_HUGE_THRESHOLD) || 2048;
|
|
1277
|
+
if (!useFastPath && keyedVnodes.length >= hugeThreshold) {
|
|
1278
|
+
let allSimple = true;
|
|
1279
|
+
for (let i = 0; i < keyedVnodes.length; i++) {
|
|
1280
|
+
const vnode = keyedVnodes[i].vnode;
|
|
1281
|
+
if (!_isDOMElement(vnode)) {
|
|
1282
|
+
allSimple = false;
|
|
1283
|
+
break;
|
|
1284
|
+
}
|
|
1285
|
+
const dv = vnode;
|
|
1286
|
+
if (typeof dv.type !== "string") {
|
|
1287
|
+
allSimple = false;
|
|
1288
|
+
break;
|
|
1289
|
+
}
|
|
1290
|
+
const ch = dv.children || dv.props?.children;
|
|
1291
|
+
if (ch === void 0) continue;
|
|
1292
|
+
if (Array.isArray(ch)) {
|
|
1293
|
+
if (ch.length !== 1 || typeof ch[0] !== "string" && typeof ch[0] !== "number") {
|
|
1294
|
+
allSimple = false;
|
|
1295
|
+
break;
|
|
1296
|
+
}
|
|
1297
|
+
} else if (typeof ch !== "string" && typeof ch !== "number") {
|
|
1298
|
+
allSimple = false;
|
|
1299
|
+
break;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
if (allSimple) {
|
|
1303
|
+
logger.warn("[Askr][FASTPATH] applying huge-list positional fallback");
|
|
1304
|
+
try {
|
|
1305
|
+
if (isBulkCommitActive2()) markFastPathApplied(parent);
|
|
1306
|
+
} catch (e) {
|
|
1307
|
+
void e;
|
|
1308
|
+
}
|
|
1309
|
+
const stats = performBulkPositionalKeyedTextUpdate(parent, keyedVnodes);
|
|
1310
|
+
if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
|
|
1311
|
+
try {
|
|
1312
|
+
const gl = globalThis;
|
|
1313
|
+
gl.__ASKR_LAST_FASTPATH_STATS = stats;
|
|
1314
|
+
const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
|
|
1315
|
+
counters.bulkKeyedHugeFallback = (counters.bulkKeyedHugeFallback || 0) + 1;
|
|
1316
|
+
gl.__ASKR_FASTPATH_COUNTERS = counters;
|
|
1317
|
+
gl.__ASKR_BULK_DIAG = {
|
|
1318
|
+
phase: "bulk-keyed-huge-fallback",
|
|
1319
|
+
stats
|
|
1320
|
+
};
|
|
1321
|
+
} catch (e) {
|
|
1322
|
+
void e;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
return newKeyMap;
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
} catch (e) {
|
|
1329
|
+
void e;
|
|
1330
|
+
}
|
|
1331
|
+
if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
|
|
1332
|
+
try {
|
|
1333
|
+
const gl = globalThis;
|
|
1334
|
+
gl.__ASKR_BULK_DIAG = {
|
|
1335
|
+
phase: "keyed-decision",
|
|
1336
|
+
decision,
|
|
1337
|
+
totalKeyed,
|
|
1338
|
+
oldKeyMapSize: oldKeyMap?.size ?? 0
|
|
1339
|
+
};
|
|
1340
|
+
} catch (e) {
|
|
1341
|
+
void e;
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
let allSimpleText = false;
|
|
1345
|
+
try {
|
|
1346
|
+
if (keyedVnodes.length >= (Number(process.env.ASKR_BULK_TEXT_THRESHOLD) || 1024)) {
|
|
1347
|
+
let missing = 0;
|
|
1348
|
+
try {
|
|
1349
|
+
const present = /* @__PURE__ */ new Set();
|
|
1350
|
+
const parentChildren2 = Array.from(parent.children);
|
|
1351
|
+
for (let i = 0; i < parentChildren2.length; i++) {
|
|
1352
|
+
const attr = parentChildren2[i].getAttribute("data-key");
|
|
1353
|
+
if (attr !== null) {
|
|
1354
|
+
present.add(attr);
|
|
1355
|
+
const n = Number(attr);
|
|
1356
|
+
if (!Number.isNaN(n)) present.add(n);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
for (let i = 0; i < keyedVnodes.length; i++) {
|
|
1360
|
+
const k2 = keyedVnodes[i].key;
|
|
1361
|
+
if (!present.has(k2)) missing++;
|
|
1362
|
+
}
|
|
1363
|
+
} catch {
|
|
1364
|
+
missing = 0;
|
|
1365
|
+
}
|
|
1366
|
+
allSimpleText = keyedVnodes.length > 0 && keyedVnodes.every(({ vnode }) => {
|
|
1367
|
+
if (!_isDOMElement(vnode)) return false;
|
|
1368
|
+
const dv = vnode;
|
|
1369
|
+
if (typeof dv.type !== "string") return false;
|
|
1370
|
+
const ch = dv.children || dv.props?.children;
|
|
1371
|
+
if (ch === void 0) return true;
|
|
1372
|
+
if (Array.isArray(ch)) {
|
|
1373
|
+
return ch.length === 1 && (typeof ch[0] === "string" || typeof ch[0] === "number");
|
|
1374
|
+
}
|
|
1375
|
+
return typeof ch === "string" || typeof ch === "number";
|
|
1376
|
+
});
|
|
1377
|
+
let hasPropsPresent = false;
|
|
1378
|
+
for (let i = 0; i < keyedVnodes.length; i++) {
|
|
1379
|
+
const vnode = keyedVnodes[i].vnode;
|
|
1380
|
+
if (!_isDOMElement(vnode)) continue;
|
|
1381
|
+
const props = vnode.props || {};
|
|
1382
|
+
for (const k2 of Object.keys(props)) {
|
|
1383
|
+
if (k2 === "children" || k2 === "key") continue;
|
|
1384
|
+
if (k2.startsWith("on") && k2.length > 2) continue;
|
|
1385
|
+
if (k2.startsWith("data-")) continue;
|
|
1386
|
+
hasPropsPresent = true;
|
|
1387
|
+
break;
|
|
1388
|
+
}
|
|
1389
|
+
if (hasPropsPresent) break;
|
|
1390
|
+
}
|
|
1391
|
+
const missingRatio = missing / Math.max(1, keyedVnodes.length);
|
|
1392
|
+
if (missingRatio > 0.5 && allSimpleText && !hasPropsPresent) {
|
|
1393
|
+
logger.warn(
|
|
1394
|
+
"[Askr][FASTPATH] switching to positional bulk keyed fast-path due to missing keys ratio",
|
|
1395
|
+
missingRatio
|
|
1396
|
+
);
|
|
1397
|
+
try {
|
|
1398
|
+
if (isBulkCommitActive2()) markFastPathApplied(parent);
|
|
1399
|
+
} catch (e) {
|
|
1400
|
+
void e;
|
|
1401
|
+
}
|
|
1402
|
+
const stats = performBulkPositionalKeyedTextUpdate(parent, keyedVnodes);
|
|
1403
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1404
|
+
try {
|
|
1405
|
+
const gl = globalThis;
|
|
1406
|
+
gl.__ASKR_LAST_FASTPATH_STATS = stats;
|
|
1407
|
+
try {
|
|
1408
|
+
_reconcilerRecordedParents.add(parent);
|
|
1409
|
+
} catch (e) {
|
|
1410
|
+
void e;
|
|
1411
|
+
}
|
|
1412
|
+
gl.__ASKR_LAST_FASTPATH_COMMIT_COUNT = 1;
|
|
1413
|
+
const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
|
|
1414
|
+
counters.bulkKeyedPositionalHits = (counters.bulkKeyedPositionalHits || 0) + 1;
|
|
1415
|
+
gl.__ASKR_FASTPATH_COUNTERS = counters;
|
|
1416
|
+
} catch (e) {
|
|
1417
|
+
void e;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
return newKeyMap;
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
} catch (e) {
|
|
1424
|
+
void e;
|
|
1425
|
+
}
|
|
1426
|
+
if (!useFastPath && typeof globalThis !== "undefined") {
|
|
1427
|
+
try {
|
|
1428
|
+
let parentFastpathApplied = false;
|
|
1429
|
+
try {
|
|
1430
|
+
parentFastpathApplied = isFastPathApplied(parent);
|
|
1431
|
+
} catch {
|
|
1432
|
+
parentFastpathApplied = false;
|
|
1433
|
+
}
|
|
1434
|
+
if (!parentFastpathApplied) {
|
|
1435
|
+
const gl = globalThis;
|
|
1436
|
+
try {
|
|
1437
|
+
if (_reconcilerRecordedParents.has(parent)) {
|
|
1438
|
+
_reconcilerRecordedParents.delete(parent);
|
|
1439
|
+
} else {
|
|
1440
|
+
delete gl.__ASKR_LAST_FASTPATH_STATS;
|
|
1441
|
+
delete gl.__ASKR_LAST_FASTPATH_REUSED;
|
|
1442
|
+
}
|
|
1443
|
+
} catch {
|
|
1444
|
+
delete gl.__ASKR_LAST_FASTPATH_STATS;
|
|
1445
|
+
delete gl.__ASKR_LAST_FASTPATH_REUSED;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
} catch (e) {
|
|
1449
|
+
void e;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
let _triedPositionalReuse = false;
|
|
1453
|
+
try {
|
|
1454
|
+
const bulkTextThreshold = Number(process.env.ASKR_BULK_TEXT_THRESHOLD) || 1024;
|
|
1455
|
+
if (!useFastPath && keyedVnodes.length >= bulkTextThreshold && allSimpleText && !hasPropChanges) {
|
|
1456
|
+
try {
|
|
1457
|
+
if (isFastPathApplied(parent)) {
|
|
1458
|
+
logger.warn(
|
|
1459
|
+
"[Askr][FASTPATH] fast-path already applied on parent; skipping"
|
|
1460
|
+
);
|
|
1461
|
+
return newKeyMap;
|
|
1462
|
+
}
|
|
1463
|
+
} catch (e) {
|
|
1464
|
+
void e;
|
|
1465
|
+
}
|
|
1466
|
+
let stable = true;
|
|
1467
|
+
try {
|
|
1468
|
+
const parentChildren2 = Array.from(parent.children);
|
|
1469
|
+
if (parentChildren2.length === keyedVnodes.length) {
|
|
1470
|
+
const allSimple = keyedVnodes.every(({ vnode }) => {
|
|
1471
|
+
if (!_isDOMElement(vnode)) return false;
|
|
1472
|
+
const dv = vnode;
|
|
1473
|
+
if (typeof dv.type !== "string") return false;
|
|
1474
|
+
const ch = dv.children || dv.props?.children;
|
|
1475
|
+
if (ch === void 0) return true;
|
|
1476
|
+
if (Array.isArray(ch)) {
|
|
1477
|
+
return ch.length === 1 && (typeof ch[0] === "string" || typeof ch[0] === "number");
|
|
1478
|
+
}
|
|
1479
|
+
return typeof ch === "string" || typeof ch === "number";
|
|
1480
|
+
});
|
|
1481
|
+
if (allSimple || process.env.ASKR_FORCE_BULK_POSREUSE === "1") {
|
|
1482
|
+
logger.warn(
|
|
1483
|
+
"[Askr][FASTPATH] len-match heuristic triggered (positional bulk)"
|
|
1484
|
+
);
|
|
1485
|
+
if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
|
|
1486
|
+
try {
|
|
1487
|
+
const gl = globalThis;
|
|
1488
|
+
gl.__ASKR_BULK_DIAG = {
|
|
1489
|
+
phase: "bulk-keyed-positional-trigger-lenmatch-early",
|
|
1490
|
+
totalKeyed: keyedVnodes.length,
|
|
1491
|
+
allSimple,
|
|
1492
|
+
forced: process.env.ASKR_FORCE_BULK_POSREUSE === "1"
|
|
1493
|
+
};
|
|
1494
|
+
} catch (e) {
|
|
1495
|
+
void e;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
try {
|
|
1499
|
+
if (isBulkCommitActive2()) markFastPathApplied(parent);
|
|
1500
|
+
} catch (e) {
|
|
1501
|
+
void e;
|
|
1502
|
+
}
|
|
1503
|
+
const stats = performBulkPositionalKeyedTextUpdate(
|
|
1504
|
+
parent,
|
|
1505
|
+
keyedVnodes
|
|
1506
|
+
);
|
|
1507
|
+
if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
|
|
1508
|
+
try {
|
|
1509
|
+
const gl = globalThis;
|
|
1510
|
+
gl.__ASKR_LAST_FASTPATH_STATS = stats;
|
|
1511
|
+
gl.__ASKR_BULK_DIAG = {
|
|
1512
|
+
phase: "bulk-keyed-positional-applied",
|
|
1513
|
+
stats
|
|
1514
|
+
};
|
|
1515
|
+
const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
|
|
1516
|
+
counters.bulkKeyedPositionalHits = (counters.bulkKeyedPositionalHits || 0) + 1;
|
|
1517
|
+
if (process.env.ASKR_FORCE_BULK_POSREUSE === "1")
|
|
1518
|
+
counters.bulkKeyedPositionalForced = (counters.bulkKeyedPositionalForced || 0) + 1;
|
|
1519
|
+
gl.__ASKR_FASTPATH_COUNTERS = counters;
|
|
1520
|
+
} catch (e) {
|
|
1521
|
+
void e;
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
return newKeyMap;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
if (parentChildren2.length !== keyedVnodes.length) stable = false;
|
|
1528
|
+
else {
|
|
1529
|
+
let keyMismatches = 0;
|
|
1530
|
+
for (let i = 0; i < keyedVnodes.length; i++) {
|
|
1531
|
+
const k2 = keyedVnodes[i].key;
|
|
1532
|
+
const ch = parentChildren2[i];
|
|
1533
|
+
if (!ch) {
|
|
1534
|
+
stable = false;
|
|
1535
|
+
break;
|
|
1536
|
+
}
|
|
1537
|
+
const attr = ch.getAttribute("data-key");
|
|
1538
|
+
if (attr === null) {
|
|
1539
|
+
stable = false;
|
|
1540
|
+
break;
|
|
1541
|
+
}
|
|
1542
|
+
if (String(k2) !== attr && String(Number(attr)) !== String(k2)) {
|
|
1543
|
+
keyMismatches++;
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
if (stable) {
|
|
1547
|
+
if (keyMismatches === 0) {
|
|
1548
|
+
logger.warn(
|
|
1549
|
+
"[Askr][FASTPATH] applying bulk keyed text fast-path (stable keys)"
|
|
1550
|
+
);
|
|
1551
|
+
if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
|
|
1552
|
+
try {
|
|
1553
|
+
const gl = globalThis;
|
|
1554
|
+
gl.__ASKR_BULK_DIAG = {
|
|
1555
|
+
phase: "bulk-keyed-stable-trigger",
|
|
1556
|
+
totalKeyed: keyedVnodes.length,
|
|
1557
|
+
hasPropChanges
|
|
1558
|
+
};
|
|
1559
|
+
} catch (e) {
|
|
1560
|
+
void e;
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
try {
|
|
1564
|
+
if (isBulkCommitActive2()) markFastPathApplied(parent);
|
|
1565
|
+
} catch (e) {
|
|
1566
|
+
void e;
|
|
1567
|
+
}
|
|
1568
|
+
const stats = performBulkKeyedTextReplace(
|
|
1569
|
+
parent,
|
|
1570
|
+
keyedVnodes,
|
|
1571
|
+
oldKeyMap
|
|
1572
|
+
);
|
|
1573
|
+
if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
|
|
1574
|
+
try {
|
|
1575
|
+
const gl = globalThis;
|
|
1576
|
+
gl.__ASKR_LAST_FASTPATH_STATS = stats;
|
|
1577
|
+
gl.__ASKR_BULK_DIAG = {
|
|
1578
|
+
phase: "bulk-keyed-applied",
|
|
1579
|
+
stats
|
|
1580
|
+
};
|
|
1581
|
+
const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
|
|
1582
|
+
counters.bulkKeyedTextHits = (counters.bulkKeyedTextHits || 0) + 1;
|
|
1583
|
+
gl.__ASKR_FASTPATH_COUNTERS = counters;
|
|
1584
|
+
} catch (e) {
|
|
1585
|
+
void e;
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
return newKeyMap;
|
|
1589
|
+
}
|
|
1590
|
+
if (parentChildren2.length === keyedVnodes.length) {
|
|
1591
|
+
logger.warn(
|
|
1592
|
+
"[Askr][FASTPATH] applying bulk keyed positional text fast-path (len-match)"
|
|
1593
|
+
);
|
|
1594
|
+
if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
|
|
1595
|
+
try {
|
|
1596
|
+
const gl = globalThis;
|
|
1597
|
+
gl.__ASKR_BULK_DIAG = {
|
|
1598
|
+
phase: "bulk-keyed-positional-trigger-lenmatch",
|
|
1599
|
+
totalKeyed: keyedVnodes.length,
|
|
1600
|
+
keyMismatches
|
|
1601
|
+
};
|
|
1602
|
+
} catch (e) {
|
|
1603
|
+
void e;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
try {
|
|
1607
|
+
if (isBulkCommitActive2()) markFastPathApplied(parent);
|
|
1608
|
+
} catch (e) {
|
|
1609
|
+
void e;
|
|
1610
|
+
}
|
|
1611
|
+
const stats = performBulkPositionalKeyedTextUpdate(
|
|
1612
|
+
parent,
|
|
1613
|
+
keyedVnodes
|
|
1614
|
+
);
|
|
1615
|
+
if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
|
|
1616
|
+
try {
|
|
1617
|
+
const gl = globalThis;
|
|
1618
|
+
gl.__ASKR_LAST_FASTPATH_STATS = stats;
|
|
1619
|
+
gl.__ASKR_BULK_DIAG = {
|
|
1620
|
+
phase: "bulk-keyed-positional-applied",
|
|
1621
|
+
stats
|
|
1622
|
+
};
|
|
1623
|
+
const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
|
|
1624
|
+
counters.bulkKeyedPositionalHits = (counters.bulkKeyedPositionalHits || 0) + 1;
|
|
1625
|
+
gl.__ASKR_FASTPATH_COUNTERS = counters;
|
|
1626
|
+
} catch (e) {
|
|
1627
|
+
void e;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
return newKeyMap;
|
|
1631
|
+
}
|
|
1632
|
+
const mismatchRatio = keyMismatches / keyedVnodes.length;
|
|
1633
|
+
const POSITIONAL_THRESHOLD = 0.5;
|
|
1634
|
+
if (mismatchRatio > POSITIONAL_THRESHOLD) {
|
|
1635
|
+
logger.warn(
|
|
1636
|
+
"[Askr][FASTPATH] applying bulk keyed positional text fast-path"
|
|
1637
|
+
);
|
|
1638
|
+
if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
|
|
1639
|
+
try {
|
|
1640
|
+
globalThis.__ASKR_BULK_DIAG = {
|
|
1641
|
+
phase: "bulk-keyed-positional-trigger",
|
|
1642
|
+
totalKeyed: keyedVnodes.length,
|
|
1643
|
+
keyMismatches
|
|
1644
|
+
};
|
|
1645
|
+
} catch (e) {
|
|
1646
|
+
void e;
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
try {
|
|
1650
|
+
if (isBulkCommitActive2()) markFastPathApplied(parent);
|
|
1651
|
+
} catch (e) {
|
|
1652
|
+
void e;
|
|
1653
|
+
}
|
|
1654
|
+
const stats = performBulkPositionalKeyedTextUpdate(
|
|
1655
|
+
parent,
|
|
1656
|
+
keyedVnodes
|
|
1657
|
+
);
|
|
1658
|
+
if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
|
|
1659
|
+
try {
|
|
1660
|
+
const gl = globalThis;
|
|
1661
|
+
gl.__ASKR_LAST_FASTPATH_STATS = stats;
|
|
1662
|
+
gl.__ASKR_BULK_DIAG = {
|
|
1663
|
+
phase: "bulk-keyed-positional-applied",
|
|
1664
|
+
stats
|
|
1665
|
+
};
|
|
1666
|
+
const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
|
|
1667
|
+
counters.bulkKeyedPositionalHits = (counters.bulkKeyedPositionalHits || 0) + 1;
|
|
1668
|
+
gl.__ASKR_FASTPATH_COUNTERS = counters;
|
|
1669
|
+
} catch (e) {
|
|
1670
|
+
void e;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
return newKeyMap;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
} catch {
|
|
1678
|
+
stable = false;
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
} catch (e) {
|
|
1682
|
+
void e;
|
|
1683
|
+
}
|
|
1684
|
+
if (!useFastPath) {
|
|
1685
|
+
const totalKeyed2 = keyedVnodes.length;
|
|
1686
|
+
const candidates = newChildren.filter(
|
|
1687
|
+
(c) => _isDOMElement(c) && typeof c.type === "string"
|
|
1688
|
+
);
|
|
1689
|
+
const smallListPositionalReuseEligible = candidates.length > 0 && candidates.length <= 64 && parent.children.length === candidates.length && candidates.every((vnode) => {
|
|
1690
|
+
const children = vnode.children || vnode.props?.children;
|
|
1691
|
+
if (Array.isArray(children)) {
|
|
1692
|
+
return children.length === 1 && (typeof children[0] === "string" || typeof children[0] === "number");
|
|
1693
|
+
}
|
|
1694
|
+
return children === void 0 || typeof children === "string" || typeof children === "number";
|
|
1695
|
+
});
|
|
1696
|
+
try {
|
|
1697
|
+
if (smallListPositionalReuseEligible) {
|
|
1698
|
+
let eligible = true;
|
|
1699
|
+
for (let i = 0; i < totalKeyed2; i++) {
|
|
1700
|
+
const vnode = keyedVnodes[i].vnode;
|
|
1701
|
+
if (!_isDOMElement(vnode) || typeof vnode.type !== "string") {
|
|
1702
|
+
eligible = false;
|
|
1703
|
+
break;
|
|
1704
|
+
}
|
|
1705
|
+
const children = vnode.children || vnode.props?.children;
|
|
1706
|
+
if (Array.isArray(children)) {
|
|
1707
|
+
if (children.length !== 1) {
|
|
1708
|
+
eligible = false;
|
|
1709
|
+
break;
|
|
1710
|
+
}
|
|
1711
|
+
const c = children[0];
|
|
1712
|
+
if (typeof c !== "string" && typeof c !== "number") {
|
|
1713
|
+
eligible = false;
|
|
1714
|
+
break;
|
|
1715
|
+
}
|
|
1716
|
+
} else if (children !== void 0 && typeof children !== "string" && typeof children !== "number") {
|
|
1717
|
+
eligible = false;
|
|
1718
|
+
break;
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
const anyKeyMatches = !!oldKeyMap && keyedVnodes.some((kv) => oldKeyMap.has(kv.key));
|
|
1722
|
+
if (anyKeyMatches) {
|
|
1723
|
+
eligible = false;
|
|
1724
|
+
}
|
|
1725
|
+
if (eligible || process.env.ASKR_FORCE_POSREUSE === "1") {
|
|
1726
|
+
_triedPositionalReuse = true;
|
|
1727
|
+
if (process.env.ASKR_FORCE_POSREUSE === "1") {
|
|
1728
|
+
logger.warn(
|
|
1729
|
+
"[Askr][POSREUSE][FORCED] forcing positional reuse path for testing"
|
|
1730
|
+
);
|
|
1731
|
+
} else {
|
|
1732
|
+
logger.warn("[Askr][POSREUSE] positional reuse heuristic applied");
|
|
1733
|
+
}
|
|
1734
|
+
const existingChildren = parent.children;
|
|
1735
|
+
for (let i = 0; i < totalKeyed2; i++) {
|
|
1736
|
+
const { key, vnode } = keyedVnodes[i];
|
|
1737
|
+
const current2 = existingChildren[i];
|
|
1738
|
+
if (current2 && _isDOMElement(vnode)) {
|
|
1739
|
+
const vnodeType = vnode.type;
|
|
1740
|
+
if (current2.tagName.toLowerCase() === vnodeType.toLowerCase()) {
|
|
1741
|
+
updateElementFromVnode(current2, vnode);
|
|
1742
|
+
newKeyMap.set(key, current2);
|
|
1743
|
+
continue;
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
const newEl = createDOMNode(vnode);
|
|
1747
|
+
if (newEl instanceof Element) {
|
|
1748
|
+
if (current2) {
|
|
1749
|
+
cleanupInstanceIfPresent(current2);
|
|
1750
|
+
parent.replaceChild(newEl, current2);
|
|
1751
|
+
} else parent.appendChild(newEl);
|
|
1752
|
+
newKeyMap.set(key, newEl);
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
for (const vnode of unkeyedVnodes) {
|
|
1756
|
+
const newEl = createDOMNode(vnode);
|
|
1757
|
+
if (newEl) parent.appendChild(newEl);
|
|
1758
|
+
}
|
|
1759
|
+
return newKeyMap;
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
} catch {
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
if (useFastPath) {
|
|
1766
|
+
if (!isSchedulerExecuting()) {
|
|
1767
|
+
logger.warn(
|
|
1768
|
+
"[Askr][FASTPATH][DEV] Fast-path reconciliation invoked outside scheduler execution"
|
|
1769
|
+
);
|
|
1770
|
+
}
|
|
1771
|
+
let parentChildrenArr;
|
|
1772
|
+
let localOldKeyMap;
|
|
1773
|
+
if (totalKeyed <= 20) {
|
|
1774
|
+
try {
|
|
1775
|
+
const pc = parent.children;
|
|
1776
|
+
parentChildrenArr = new Array(pc.length);
|
|
1777
|
+
for (let i = 0; i < pc.length; i++)
|
|
1778
|
+
parentChildrenArr[i] = pc[i];
|
|
1779
|
+
} catch {
|
|
1780
|
+
parentChildrenArr = void 0;
|
|
1781
|
+
}
|
|
1782
|
+
} else {
|
|
1783
|
+
localOldKeyMap = /* @__PURE__ */ new Map();
|
|
1784
|
+
try {
|
|
1785
|
+
const parentChildren2 = Array.from(parent.children);
|
|
1786
|
+
for (let i = 0; i < parentChildren2.length; i++) {
|
|
1787
|
+
const ch = parentChildren2[i];
|
|
1788
|
+
const k2 = ch.getAttribute("data-key");
|
|
1789
|
+
if (k2 !== null) {
|
|
1790
|
+
localOldKeyMap.set(k2, ch);
|
|
1791
|
+
const n = Number(k2);
|
|
1792
|
+
if (!Number.isNaN(n)) localOldKeyMap.set(n, ch);
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
} catch {
|
|
1796
|
+
localOldKeyMap = void 0;
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
logger.warn(
|
|
1800
|
+
"[Askr][FASTPATH] oldKeyMap size:",
|
|
1801
|
+
oldKeyMap?.size ?? 0,
|
|
1802
|
+
"localOldKeyMap size:",
|
|
1803
|
+
localOldKeyMap?.size
|
|
1804
|
+
);
|
|
1805
|
+
const tLookupStart = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
1806
|
+
const finalNodes = [];
|
|
1807
|
+
let mapLookups = 0;
|
|
1808
|
+
let createdNodes = 0;
|
|
1809
|
+
let reusedCount = 0;
|
|
1810
|
+
for (let i = 0; i < keyedVnodes.length; i++) {
|
|
1811
|
+
const { key, vnode } = keyedVnodes[i];
|
|
1812
|
+
mapLookups++;
|
|
1813
|
+
let el;
|
|
1814
|
+
if (totalKeyed <= 20 && parentChildrenArr) {
|
|
1815
|
+
const ks = String(key);
|
|
1816
|
+
for (let j = 0; j < parentChildrenArr.length; j++) {
|
|
1817
|
+
const ch = parentChildrenArr[j];
|
|
1818
|
+
const k2 = ch.getAttribute("data-key");
|
|
1819
|
+
if (k2 !== null && (k2 === ks || Number(k2) === key)) {
|
|
1820
|
+
el = ch;
|
|
1821
|
+
break;
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
if (!el) el = oldKeyMap?.get(key);
|
|
1825
|
+
} else {
|
|
1826
|
+
el = localOldKeyMap?.get(key) ?? oldKeyMap?.get(key);
|
|
1827
|
+
}
|
|
1828
|
+
if (el) {
|
|
1829
|
+
finalNodes.push(el);
|
|
1830
|
+
reusedCount++;
|
|
1831
|
+
} else {
|
|
1832
|
+
const newEl = createDOMNode(vnode);
|
|
1833
|
+
if (newEl) {
|
|
1834
|
+
finalNodes.push(newEl);
|
|
1835
|
+
createdNodes++;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
for (const vnode of unkeyedVnodes) {
|
|
1840
|
+
const newEl = createDOMNode(vnode);
|
|
1841
|
+
if (newEl) {
|
|
1842
|
+
finalNodes.push(newEl);
|
|
1843
|
+
createdNodes++;
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
const t_lookup = typeof performance !== "undefined" && performance.now ? performance.now() - tLookupStart : 0;
|
|
1847
|
+
if (process.env.ASKR_FASTPATH_GUARD === "1") {
|
|
1848
|
+
const replaceChildrenCount = 0;
|
|
1849
|
+
const otherMutationCount = 0;
|
|
1850
|
+
const orig = {};
|
|
1851
|
+
const elProto = Element.prototype;
|
|
1852
|
+
const nodeProto = Node.prototype;
|
|
1853
|
+
const removeFn = (() => {
|
|
1854
|
+
if (typeof document === "undefined" || typeof document.createElement !== "function")
|
|
1855
|
+
return void 0;
|
|
1856
|
+
try {
|
|
1857
|
+
const el = document.createElement("div");
|
|
1858
|
+
return typeof el.remove === "function" ? el.remove : void 0;
|
|
1859
|
+
} catch {
|
|
1860
|
+
return void 0;
|
|
1861
|
+
}
|
|
1862
|
+
})();
|
|
1863
|
+
if (elProto.replaceChildren)
|
|
1864
|
+
orig.replaceChildren = elProto.replaceChildren;
|
|
1865
|
+
if (nodeProto.appendChild) orig.appendChild = nodeProto.appendChild;
|
|
1866
|
+
if (nodeProto.insertBefore) orig.insertBefore = nodeProto.insertBefore;
|
|
1867
|
+
if (nodeProto.removeChild) orig.removeChild = nodeProto.removeChild;
|
|
1868
|
+
if (nodeProto.replaceChild) orig.replaceChild = nodeProto.replaceChild;
|
|
1869
|
+
if (removeFn) orig.remove = removeFn;
|
|
1870
|
+
let violation = false;
|
|
1871
|
+
try {
|
|
1872
|
+
const fragment = document.createDocumentFragment();
|
|
1873
|
+
for (let i = 0; i < finalNodes.length; i++)
|
|
1874
|
+
fragment.appendChild(finalNodes[i]);
|
|
1875
|
+
let commitCount = 0;
|
|
1876
|
+
commitCount++;
|
|
1877
|
+
parent.replaceChildren(fragment);
|
|
1878
|
+
if (typeof globalThis !== "undefined") {
|
|
1879
|
+
globalThis["__ASKR_LAST_FASTPATH_COMMIT_COUNT"] = commitCount;
|
|
1880
|
+
}
|
|
1881
|
+
} finally {
|
|
1882
|
+
violation = otherMutationCount !== 0 || replaceChildrenCount < 1;
|
|
1883
|
+
}
|
|
1884
|
+
if (violation) {
|
|
1885
|
+
logger.error(
|
|
1886
|
+
"[Askr][DEV] Fast-path structural mutation invariant violated:",
|
|
1887
|
+
{
|
|
1888
|
+
replaceChildrenCount,
|
|
1889
|
+
otherMutationCount
|
|
1890
|
+
}
|
|
1891
|
+
);
|
|
1892
|
+
throw new Error(
|
|
1893
|
+
"Fast-path must perform a single structural replacement (replaceChildren) and no other structural mutations"
|
|
1894
|
+
);
|
|
1895
|
+
}
|
|
1896
|
+
} else {
|
|
1897
|
+
const tFragmentStart = Date.now();
|
|
1898
|
+
const fragment = document.createDocumentFragment();
|
|
1899
|
+
let fragmentAppendCount = 0;
|
|
1900
|
+
for (let i = 0; i < finalNodes.length; i++) {
|
|
1901
|
+
fragment.appendChild(finalNodes[i]);
|
|
1902
|
+
fragmentAppendCount++;
|
|
1903
|
+
}
|
|
1904
|
+
const t_fragment = Date.now() - tFragmentStart;
|
|
1905
|
+
const schedBefore = process.env.NODE_ENV !== "production" ? globalScheduler.getState() : null;
|
|
1906
|
+
const wasExecuting = process.env.NODE_ENV !== "production" ? isSchedulerExecuting() : false;
|
|
1907
|
+
const tCommitStart = Date.now();
|
|
1908
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1909
|
+
}
|
|
1910
|
+
let commitCount = 0;
|
|
1911
|
+
commitCount++;
|
|
1912
|
+
try {
|
|
1913
|
+
const existing = Array.from(parent.childNodes);
|
|
1914
|
+
for (const n of existing) cleanupInstanceIfPresent(n);
|
|
1915
|
+
} catch (e) {
|
|
1916
|
+
void e;
|
|
1917
|
+
}
|
|
1918
|
+
try {
|
|
1919
|
+
const existing = Array.from(parent.childNodes);
|
|
1920
|
+
for (const n of existing) cleanupInstanceIfPresent(n);
|
|
1921
|
+
} catch (e) {
|
|
1922
|
+
void e;
|
|
1923
|
+
}
|
|
1924
|
+
parent.replaceChildren(fragment);
|
|
1925
|
+
if (typeof globalThis !== "undefined") {
|
|
1926
|
+
globalThis["__ASKR_LAST_FASTPATH_COMMIT_COUNT"] = commitCount;
|
|
1927
|
+
}
|
|
1928
|
+
const t_commit = Date.now() - tCommitStart;
|
|
1929
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1930
|
+
const schedAfter = globalScheduler.getState();
|
|
1931
|
+
if (!wasExecuting) {
|
|
1932
|
+
logger.warn(
|
|
1933
|
+
"[Askr][FASTPATH][DEV] Fast-path commit invoked outside scheduler execution"
|
|
1934
|
+
);
|
|
1935
|
+
}
|
|
1936
|
+
if (schedBefore && schedAfter) {
|
|
1937
|
+
if (schedBefore.taskCount !== schedAfter.taskCount) {
|
|
1938
|
+
logger.error(
|
|
1939
|
+
"[Askr][FASTPATH][DEV] Scheduler tasks were enqueued during fast-path commit",
|
|
1940
|
+
{
|
|
1941
|
+
before: schedBefore,
|
|
1942
|
+
after: schedAfter
|
|
1943
|
+
}
|
|
1944
|
+
);
|
|
1945
|
+
throw new Error("Fast-path must not enqueue scheduler tasks");
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
const parentNodes = Array.from(parent.childNodes);
|
|
1949
|
+
if (parentNodes.length !== finalNodes.length) {
|
|
1950
|
+
logger.error("[Askr][FASTPATH][DEV] Parent child count mismatch", {
|
|
1951
|
+
parentCount: parentNodes.length,
|
|
1952
|
+
expected: finalNodes.length
|
|
1953
|
+
});
|
|
1954
|
+
throw new Error(
|
|
1955
|
+
"Fast-path must perform a single structural replacement"
|
|
1956
|
+
);
|
|
1957
|
+
}
|
|
1958
|
+
for (let i = 0; i < finalNodes.length; i++) {
|
|
1959
|
+
if (parentNodes[i] !== finalNodes[i]) {
|
|
1960
|
+
logger.error(
|
|
1961
|
+
"[Askr][FASTPATH][DEV] Final DOM order mismatch at index",
|
|
1962
|
+
i,
|
|
1963
|
+
{
|
|
1964
|
+
expected: finalNodes[i],
|
|
1965
|
+
found: parentNodes[i]
|
|
1966
|
+
}
|
|
1967
|
+
);
|
|
1968
|
+
throw new Error(
|
|
1969
|
+
"Fast-path final DOM order does not match expected nodes"
|
|
1970
|
+
);
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
const tBookkeepingStart = Date.now();
|
|
1975
|
+
for (let i = 0; i < keyedVnodes.length; i++) {
|
|
1976
|
+
const key = keyedVnodes[i].key;
|
|
1977
|
+
const node = finalNodes[i];
|
|
1978
|
+
if (node instanceof Element) newKeyMap.set(key, node);
|
|
1979
|
+
}
|
|
1980
|
+
const t_bookkeeping = Date.now() - tBookkeepingStart;
|
|
1981
|
+
if (process.env.ASKR_FASTPATH_TRACE === "1" || process.env.NODE_ENV !== "production") {
|
|
1982
|
+
const stats = {
|
|
1983
|
+
n: totalKeyed,
|
|
1984
|
+
moves: moveCount,
|
|
1985
|
+
lisLen: 0,
|
|
1986
|
+
t_lookup,
|
|
1987
|
+
t_fragment,
|
|
1988
|
+
t_commit,
|
|
1989
|
+
t_bookkeeping,
|
|
1990
|
+
fragmentAppendCount,
|
|
1991
|
+
mapLookups,
|
|
1992
|
+
createdNodes,
|
|
1993
|
+
reusedCount
|
|
1994
|
+
};
|
|
1995
|
+
if (typeof globalThis !== "undefined") {
|
|
1996
|
+
const _g = globalThis;
|
|
1997
|
+
_g["__ASKR_LAST_FASTPATH_STATS"] = stats;
|
|
1998
|
+
_g["__ASKR_LAST_FASTPATH_REUSED"] = reusedCount > 0;
|
|
1999
|
+
const historyKey = "__ASKR_LAST_FASTPATH_HISTORY";
|
|
2000
|
+
let hist = _g[historyKey];
|
|
2001
|
+
if (!hist) {
|
|
2002
|
+
hist = [];
|
|
2003
|
+
_g[historyKey] = hist;
|
|
2004
|
+
}
|
|
2005
|
+
hist.push(stats);
|
|
2006
|
+
}
|
|
2007
|
+
logger.warn("[Askr][FASTPATH]", JSON.stringify(stats));
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
return newKeyMap;
|
|
2011
|
+
}
|
|
2012
|
+
const parentChildren = Array.from(parent.children);
|
|
2013
|
+
const positions = new Array(keyedVnodes.length).fill(-1);
|
|
2014
|
+
for (let i = 0; i < keyedVnodes.length; i++) {
|
|
2015
|
+
const key = keyedVnodes[i].key;
|
|
2016
|
+
const el = oldKeyMap?.get(key);
|
|
2017
|
+
if (el && el.parentElement === parent) {
|
|
2018
|
+
positions[i] = parentChildren.indexOf(el);
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
const tailsStart = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
2022
|
+
const keepSet = /* @__PURE__ */ new Set();
|
|
2023
|
+
const tails = [];
|
|
2024
|
+
const tailsIdx = [];
|
|
2025
|
+
const prev = new Array(positions.length).fill(-1);
|
|
2026
|
+
for (let i = 0; i < positions.length; i++) {
|
|
2027
|
+
const pos = positions[i];
|
|
2028
|
+
if (pos === -1) continue;
|
|
2029
|
+
let lo = 0;
|
|
2030
|
+
let hi = tails.length;
|
|
2031
|
+
while (lo < hi) {
|
|
2032
|
+
const mid = lo + hi >> 1;
|
|
2033
|
+
if (tails[mid] < pos) lo = mid + 1;
|
|
2034
|
+
else hi = mid;
|
|
2035
|
+
}
|
|
2036
|
+
if (lo === tails.length) {
|
|
2037
|
+
tails.push(pos);
|
|
2038
|
+
tailsIdx.push(i);
|
|
2039
|
+
} else {
|
|
2040
|
+
tails[lo] = pos;
|
|
2041
|
+
tailsIdx[lo] = i;
|
|
2042
|
+
}
|
|
2043
|
+
prev[i] = lo > 0 ? tailsIdx[lo - 1] : -1;
|
|
2044
|
+
}
|
|
2045
|
+
let k = tailsIdx.length ? tailsIdx[tailsIdx.length - 1] : -1;
|
|
2046
|
+
while (k !== -1) {
|
|
2047
|
+
keepSet.add(k);
|
|
2048
|
+
k = prev[k];
|
|
2049
|
+
}
|
|
2050
|
+
const tLIS = (typeof performance !== "undefined" && performance.now ? performance.now() : Date.now()) - tailsStart;
|
|
2051
|
+
if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
|
|
2052
|
+
try {
|
|
2053
|
+
const prev2 = globalThis.__ASKR_BULK_DIAG;
|
|
2054
|
+
globalThis.__ASKR_BULK_DIAG = {
|
|
2055
|
+
phase: "keyed-fallback-lis",
|
|
2056
|
+
positionsFound: positions.filter((p) => p !== -1).length,
|
|
2057
|
+
keepCount: keepSet.size,
|
|
2058
|
+
tLIS,
|
|
2059
|
+
previousDecision: prev2?.decision
|
|
2060
|
+
};
|
|
2061
|
+
} catch (e) {
|
|
2062
|
+
void e;
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
let anchor = parent.firstChild;
|
|
2066
|
+
for (let i = 0; i < keyedVnodes.length; i++) {
|
|
2067
|
+
const { key, vnode } = keyedVnodes[i];
|
|
2068
|
+
const el = oldKeyMap?.get(key);
|
|
2069
|
+
if (el && el.parentElement === parent) {
|
|
2070
|
+
if (keepSet.has(i)) {
|
|
2071
|
+
if (anchor === el) {
|
|
2072
|
+
anchor = el.nextSibling;
|
|
2073
|
+
}
|
|
2074
|
+
updateElementFromVnode(el, vnode);
|
|
2075
|
+
newKeyMap.set(key, el);
|
|
2076
|
+
} else {
|
|
2077
|
+
parent.insertBefore(el, anchor);
|
|
2078
|
+
updateElementFromVnode(el, vnode);
|
|
2079
|
+
newKeyMap.set(key, el);
|
|
2080
|
+
anchor = el.nextSibling;
|
|
2081
|
+
}
|
|
2082
|
+
} else {
|
|
2083
|
+
const newEl = createDOMNode(vnode);
|
|
2084
|
+
if (newEl instanceof Element) {
|
|
2085
|
+
parent.insertBefore(newEl, anchor);
|
|
2086
|
+
newKeyMap.set(key, newEl);
|
|
2087
|
+
anchor = newEl.nextSibling;
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
for (const vnode of unkeyedVnodes) {
|
|
2092
|
+
const newEl = createDOMNode(vnode);
|
|
2093
|
+
if (newEl) {
|
|
2094
|
+
parent.appendChild(newEl);
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
return newKeyMap;
|
|
2098
|
+
}
|
|
2099
|
+
function performBulkKeyedTextReplace(parent, keyedVnodes, oldKeyMap) {
|
|
2100
|
+
const total = keyedVnodes.length;
|
|
2101
|
+
const finalNodes = [];
|
|
2102
|
+
let reused = 0;
|
|
2103
|
+
let created = 0;
|
|
2104
|
+
const t0 = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
2105
|
+
for (let i = 0; i < total; i++) {
|
|
2106
|
+
const { key, vnode } = keyedVnodes[i];
|
|
2107
|
+
const el = oldKeyMap?.get(key);
|
|
2108
|
+
if (el && _isDOMElement(vnode) && typeof vnode.type === "string") {
|
|
2109
|
+
const children = vnode.children || vnode.props?.children;
|
|
2110
|
+
if (typeof children === "string" || typeof children === "number") {
|
|
2111
|
+
if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
|
|
2112
|
+
el.firstChild.data = String(children);
|
|
2113
|
+
} else {
|
|
2114
|
+
el.textContent = String(children);
|
|
2115
|
+
}
|
|
2116
|
+
} else if (Array.isArray(children) && children.length === 1 && (typeof children[0] === "string" || typeof children[0] === "number")) {
|
|
2117
|
+
if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
|
|
2118
|
+
el.firstChild.data = String(children[0]);
|
|
2119
|
+
} else {
|
|
2120
|
+
el.textContent = String(children[0]);
|
|
2121
|
+
}
|
|
2122
|
+
} else {
|
|
2123
|
+
updateElementFromVnode(el, vnode);
|
|
2124
|
+
}
|
|
2125
|
+
finalNodes.push(el);
|
|
2126
|
+
reused++;
|
|
2127
|
+
} else {
|
|
2128
|
+
const dom = createDOMNode(vnode);
|
|
2129
|
+
if (dom) {
|
|
2130
|
+
finalNodes.push(dom);
|
|
2131
|
+
created++;
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
try {
|
|
2136
|
+
const toRemove = Array.from(parent.childNodes).filter(
|
|
2137
|
+
(n) => !finalNodes.includes(n)
|
|
2138
|
+
);
|
|
2139
|
+
for (const n of toRemove) cleanupInstanceIfPresent(n);
|
|
2140
|
+
} catch (e) {
|
|
2141
|
+
void e;
|
|
2142
|
+
}
|
|
2143
|
+
const existingChildren = Array.from(parent.children);
|
|
2144
|
+
const listenerSnapshots = new Array(existingChildren.length).fill(void 0);
|
|
2145
|
+
try {
|
|
2146
|
+
for (let i = 0; i < existingChildren.length; i++) {
|
|
2147
|
+
const ch = existingChildren[i];
|
|
2148
|
+
if (ch) {
|
|
2149
|
+
const map = elementListeners.get(ch);
|
|
2150
|
+
if (map && map.size > 0) {
|
|
2151
|
+
const clone = /* @__PURE__ */ new Map();
|
|
2152
|
+
for (const [k, v] of map) clone.set(k, v);
|
|
2153
|
+
listenerSnapshots[i] = clone;
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
} catch (e) {
|
|
2158
|
+
void e;
|
|
2159
|
+
}
|
|
2160
|
+
const fragment = document.createDocumentFragment();
|
|
2161
|
+
for (let i = 0; i < finalNodes.length; i++)
|
|
2162
|
+
fragment.appendChild(finalNodes[i]);
|
|
2163
|
+
parent.replaceChildren(fragment);
|
|
2164
|
+
try {
|
|
2165
|
+
for (let i = 0; i < finalNodes.length; i++) {
|
|
2166
|
+
const newNode = finalNodes[i];
|
|
2167
|
+
const snapshot = listenerSnapshots[i];
|
|
2168
|
+
if (snapshot && newNode instanceof Element) {
|
|
2169
|
+
for (const [eventName, entry] of snapshot) {
|
|
2170
|
+
const existing = elementListeners.get(newNode)?.get(eventName);
|
|
2171
|
+
if (existing && existing.original === entry.original) continue;
|
|
2172
|
+
newNode.addEventListener(eventName, entry.handler);
|
|
2173
|
+
if (!elementListeners.has(newNode))
|
|
2174
|
+
elementListeners.set(newNode, /* @__PURE__ */ new Map());
|
|
2175
|
+
elementListeners.get(newNode).set(eventName, entry);
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
} catch (e) {
|
|
2180
|
+
void e;
|
|
2181
|
+
}
|
|
2182
|
+
try {
|
|
2183
|
+
keyedElements.delete(parent);
|
|
2184
|
+
} catch (e) {
|
|
2185
|
+
void e;
|
|
2186
|
+
}
|
|
2187
|
+
const t = typeof performance !== "undefined" && performance.now ? performance.now() - t0 : 0;
|
|
2188
|
+
const stats = {
|
|
2189
|
+
n: total,
|
|
2190
|
+
reused,
|
|
2191
|
+
created,
|
|
2192
|
+
t
|
|
2193
|
+
};
|
|
2194
|
+
return stats;
|
|
2195
|
+
}
|
|
2196
|
+
function performBulkPositionalKeyedTextUpdate(parent, keyedVnodes) {
|
|
2197
|
+
const total = keyedVnodes.length;
|
|
2198
|
+
let reused = 0;
|
|
2199
|
+
let updatedKeys = 0;
|
|
2200
|
+
const t0 = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
2201
|
+
for (let i = 0; i < total; i++) {
|
|
2202
|
+
const { key, vnode } = keyedVnodes[i];
|
|
2203
|
+
const ch = parent.children[i];
|
|
2204
|
+
if (ch && _isDOMElement(vnode) && typeof vnode.type === "string") {
|
|
2205
|
+
const vnodeType = vnode.type;
|
|
2206
|
+
if (ch.tagName.toLowerCase() === vnodeType.toLowerCase()) {
|
|
2207
|
+
const children = vnode.children || vnode.props?.children;
|
|
2208
|
+
if (typeof children === "string" || typeof children === "number") {
|
|
2209
|
+
if (ch.childNodes.length === 1 && ch.firstChild?.nodeType === 3) {
|
|
2210
|
+
ch.firstChild.data = String(children);
|
|
2211
|
+
} else {
|
|
2212
|
+
ch.textContent = String(children);
|
|
2213
|
+
}
|
|
2214
|
+
} else if (Array.isArray(children) && children.length === 1 && (typeof children[0] === "string" || typeof children[0] === "number")) {
|
|
2215
|
+
if (ch.childNodes.length === 1 && ch.firstChild?.nodeType === 3) {
|
|
2216
|
+
ch.firstChild.data = String(children[0]);
|
|
2217
|
+
} else {
|
|
2218
|
+
ch.textContent = String(children[0]);
|
|
2219
|
+
}
|
|
2220
|
+
} else {
|
|
2221
|
+
updateElementFromVnode(ch, vnode);
|
|
2222
|
+
}
|
|
2223
|
+
try {
|
|
2224
|
+
ch.setAttribute("data-key", String(key));
|
|
2225
|
+
updatedKeys++;
|
|
2226
|
+
} catch (e) {
|
|
2227
|
+
void e;
|
|
2228
|
+
}
|
|
2229
|
+
reused++;
|
|
2230
|
+
continue;
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
const dom = createDOMNode(vnode);
|
|
2234
|
+
if (dom) {
|
|
2235
|
+
const existing = parent.children[i];
|
|
2236
|
+
if (existing) {
|
|
2237
|
+
cleanupInstanceIfPresent(existing);
|
|
2238
|
+
parent.replaceChild(dom, existing);
|
|
2239
|
+
} else parent.appendChild(dom);
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
const t = typeof performance !== "undefined" && performance.now ? performance.now() - t0 : 0;
|
|
2243
|
+
try {
|
|
2244
|
+
const newKeyMap = /* @__PURE__ */ new Map();
|
|
2245
|
+
for (let i = 0; i < total; i++) {
|
|
2246
|
+
const k = keyedVnodes[i].key;
|
|
2247
|
+
const ch = parent.children[i];
|
|
2248
|
+
if (ch) newKeyMap.set(k, ch);
|
|
2249
|
+
}
|
|
2250
|
+
keyedElements.set(parent, newKeyMap);
|
|
2251
|
+
} catch (e) {
|
|
2252
|
+
void e;
|
|
2253
|
+
}
|
|
2254
|
+
const stats = {
|
|
2255
|
+
n: total,
|
|
2256
|
+
reused,
|
|
2257
|
+
updatedKeys,
|
|
2258
|
+
t
|
|
2259
|
+
};
|
|
2260
|
+
return stats;
|
|
2261
|
+
}
|
|
2262
|
+
function isBulkTextFastPathEligible(parent, newChildren) {
|
|
2263
|
+
const threshold = Number(process.env.ASKR_BULK_TEXT_THRESHOLD) || 1024;
|
|
2264
|
+
const requiredFraction = 0.8;
|
|
2265
|
+
const total = Array.isArray(newChildren) ? newChildren.length : 0;
|
|
2266
|
+
if (total < threshold) {
|
|
2267
|
+
if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
|
|
2268
|
+
try {
|
|
2269
|
+
globalThis.__ASKR_BULK_DIAG = {
|
|
2270
|
+
phase: "bulk-unkeyed-eligible",
|
|
2271
|
+
reason: "too-small",
|
|
2272
|
+
total,
|
|
2273
|
+
threshold
|
|
2274
|
+
};
|
|
2275
|
+
} catch (e) {
|
|
2276
|
+
void e;
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
return false;
|
|
2280
|
+
}
|
|
2281
|
+
let simple = 0;
|
|
2282
|
+
for (let i = 0; i < newChildren.length; i++) {
|
|
2283
|
+
const c = newChildren[i];
|
|
2284
|
+
if (typeof c === "string" || typeof c === "number") {
|
|
2285
|
+
simple++;
|
|
2286
|
+
continue;
|
|
2287
|
+
}
|
|
2288
|
+
if (typeof c === "object" && c !== null && "type" in c) {
|
|
2289
|
+
const dv = c;
|
|
2290
|
+
if (typeof dv.type === "function") {
|
|
2291
|
+
if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
|
|
2292
|
+
try {
|
|
2293
|
+
globalThis.__ASKR_BULK_DIAG = {
|
|
2294
|
+
phase: "bulk-unkeyed-eligible",
|
|
2295
|
+
reason: "component-child",
|
|
2296
|
+
index: i
|
|
2297
|
+
};
|
|
2298
|
+
} catch (e) {
|
|
2299
|
+
void e;
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
return false;
|
|
2303
|
+
}
|
|
2304
|
+
if (typeof dv.type === "string") {
|
|
2305
|
+
const children = dv.children || dv.props?.children;
|
|
2306
|
+
if (!children) {
|
|
2307
|
+
simple++;
|
|
2308
|
+
continue;
|
|
2309
|
+
}
|
|
2310
|
+
if (Array.isArray(children)) {
|
|
2311
|
+
if (children.length === 1 && (typeof children[0] === "string" || typeof children[0] === "number")) {
|
|
2312
|
+
simple++;
|
|
2313
|
+
continue;
|
|
2314
|
+
}
|
|
2315
|
+
} else if (typeof children === "string" || typeof children === "number") {
|
|
2316
|
+
simple++;
|
|
2317
|
+
continue;
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
const fraction = simple / total;
|
|
2323
|
+
const eligible = fraction >= requiredFraction && parent.childNodes.length >= total;
|
|
2324
|
+
if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
|
|
2325
|
+
try {
|
|
2326
|
+
globalThis.__ASKR_BULK_DIAG = {
|
|
2327
|
+
phase: "bulk-unkeyed-eligible",
|
|
2328
|
+
total,
|
|
2329
|
+
simple,
|
|
2330
|
+
fraction,
|
|
2331
|
+
requiredFraction,
|
|
2332
|
+
eligible
|
|
2333
|
+
};
|
|
2334
|
+
} catch (e) {
|
|
2335
|
+
void e;
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
return eligible;
|
|
2339
|
+
}
|
|
2340
|
+
function performBulkTextReplace(parent, newChildren) {
|
|
2341
|
+
const t0 = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
2342
|
+
const existing = Array.from(parent.childNodes);
|
|
2343
|
+
const finalNodes = [];
|
|
2344
|
+
let reused = 0;
|
|
2345
|
+
let created = 0;
|
|
2346
|
+
for (let i = 0; i < newChildren.length; i++) {
|
|
2347
|
+
const vnode = newChildren[i];
|
|
2348
|
+
const existingNode = existing[i];
|
|
2349
|
+
if (typeof vnode === "string" || typeof vnode === "number") {
|
|
2350
|
+
const text = String(vnode);
|
|
2351
|
+
if (existingNode && existingNode.nodeType === 3) {
|
|
2352
|
+
existingNode.data = text;
|
|
2353
|
+
finalNodes.push(existingNode);
|
|
2354
|
+
reused++;
|
|
2355
|
+
} else {
|
|
2356
|
+
finalNodes.push(document.createTextNode(text));
|
|
2357
|
+
created++;
|
|
2358
|
+
}
|
|
2359
|
+
continue;
|
|
2360
|
+
}
|
|
2361
|
+
if (typeof vnode === "object" && vnode !== null && "type" in vnode) {
|
|
2362
|
+
if (typeof vnode.type === "string") {
|
|
2363
|
+
const vtype = vnode.type;
|
|
2364
|
+
if (existingNode && existingNode.nodeType === 1 && existingNode.tagName.toLowerCase() === vtype.toLowerCase()) {
|
|
2365
|
+
const el = existingNode;
|
|
2366
|
+
const children = vnode.children || vnode.props?.children;
|
|
2367
|
+
if (typeof children === "string" || typeof children === "number") {
|
|
2368
|
+
if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
|
|
2369
|
+
el.firstChild.data = String(children);
|
|
2370
|
+
} else {
|
|
2371
|
+
el.textContent = String(children);
|
|
2372
|
+
}
|
|
2373
|
+
} else if (Array.isArray(children) && children.length === 1 && (typeof children[0] === "string" || typeof children[0] === "number")) {
|
|
2374
|
+
if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
|
|
2375
|
+
el.firstChild.data = String(children[0]);
|
|
2376
|
+
} else {
|
|
2377
|
+
el.textContent = String(children[0]);
|
|
2378
|
+
}
|
|
2379
|
+
} else {
|
|
2380
|
+
updateElementFromVnode(el, vnode);
|
|
2381
|
+
}
|
|
2382
|
+
finalNodes.push(el);
|
|
2383
|
+
reused++;
|
|
2384
|
+
continue;
|
|
2385
|
+
}
|
|
2386
|
+
const dom2 = createDOMNode(vnode);
|
|
2387
|
+
if (dom2) {
|
|
2388
|
+
finalNodes.push(dom2);
|
|
2389
|
+
created++;
|
|
2390
|
+
}
|
|
2391
|
+
continue;
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
const dom = createDOMNode(vnode);
|
|
2395
|
+
if (dom) {
|
|
2396
|
+
finalNodes.push(dom);
|
|
2397
|
+
created++;
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
const tBuild = typeof performance !== "undefined" && performance.now ? performance.now() - t0 : 0;
|
|
2401
|
+
try {
|
|
2402
|
+
const toRemove = Array.from(parent.childNodes).filter(
|
|
2403
|
+
(n) => !finalNodes.includes(n)
|
|
2404
|
+
);
|
|
2405
|
+
for (const n of toRemove) cleanupInstanceIfPresent(n);
|
|
2406
|
+
} catch (e) {
|
|
2407
|
+
void e;
|
|
2408
|
+
}
|
|
2409
|
+
const fragStart = Date.now();
|
|
2410
|
+
const fragment = document.createDocumentFragment();
|
|
2411
|
+
for (let i = 0; i < finalNodes.length; i++)
|
|
2412
|
+
fragment.appendChild(finalNodes[i]);
|
|
2413
|
+
parent.replaceChildren(fragment);
|
|
2414
|
+
const tCommit = Date.now() - fragStart;
|
|
2415
|
+
keyedElements.delete(parent);
|
|
2416
|
+
const stats = {
|
|
2417
|
+
n: newChildren.length,
|
|
2418
|
+
reused,
|
|
2419
|
+
created,
|
|
2420
|
+
tBuild,
|
|
2421
|
+
tCommit
|
|
2422
|
+
};
|
|
2423
|
+
return stats;
|
|
2424
|
+
}
|
|
2425
|
+
function updateElementFromVnode(el, vnode, updateChildren = true) {
|
|
2426
|
+
if (!_isDOMElement(vnode)) {
|
|
2427
|
+
return;
|
|
2428
|
+
}
|
|
2429
|
+
const props = vnode.props || {};
|
|
2430
|
+
if (vnode.key !== void 0) {
|
|
2431
|
+
el.setAttribute("data-key", String(vnode.key));
|
|
2432
|
+
}
|
|
2433
|
+
const existingListeners = elementListeners.get(el);
|
|
2434
|
+
const desiredEventNames = /* @__PURE__ */ new Set();
|
|
2435
|
+
for (const key in props) {
|
|
2436
|
+
const value = props[key];
|
|
2437
|
+
if (key === "children" || key === "key") continue;
|
|
2438
|
+
if (value === void 0 || value === null || value === false) {
|
|
2439
|
+
if (key === "class" || key === "className") {
|
|
2440
|
+
el.className = "";
|
|
2441
|
+
} else if (key.startsWith("on") && key.length > 2) {
|
|
2442
|
+
const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
|
|
2443
|
+
if (existingListeners && existingListeners.has(eventName)) {
|
|
2444
|
+
const entry = existingListeners.get(eventName);
|
|
2445
|
+
el.removeEventListener(eventName, entry.handler);
|
|
2446
|
+
existingListeners.delete(eventName);
|
|
2447
|
+
}
|
|
2448
|
+
continue;
|
|
2449
|
+
} else {
|
|
2450
|
+
el.removeAttribute(key);
|
|
2451
|
+
}
|
|
2452
|
+
continue;
|
|
2453
|
+
}
|
|
2454
|
+
if (key === "class" || key === "className") {
|
|
2455
|
+
el.className = String(value);
|
|
2456
|
+
} else if (key === "value" || key === "checked") {
|
|
2457
|
+
el[key] = value;
|
|
2458
|
+
} else if (key.startsWith("on") && key.length > 2) {
|
|
2459
|
+
const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
|
|
2460
|
+
desiredEventNames.add(eventName);
|
|
2461
|
+
const existing = existingListeners?.get(eventName);
|
|
2462
|
+
if (existing && existing.original === value) {
|
|
2463
|
+
continue;
|
|
2464
|
+
}
|
|
2465
|
+
if (existing) {
|
|
2466
|
+
el.removeEventListener(eventName, existing.handler);
|
|
2467
|
+
}
|
|
2468
|
+
const wrappedHandler = (event) => {
|
|
2469
|
+
globalScheduler.setInHandler(true);
|
|
2470
|
+
try {
|
|
2471
|
+
value(event);
|
|
2472
|
+
} catch (error) {
|
|
2473
|
+
logger.error("[Askr] Event handler error:", error);
|
|
2474
|
+
} finally {
|
|
2475
|
+
globalScheduler.setInHandler(false);
|
|
2476
|
+
}
|
|
2477
|
+
};
|
|
2478
|
+
el.addEventListener(eventName, wrappedHandler);
|
|
2479
|
+
if (!elementListeners.has(el)) {
|
|
2480
|
+
elementListeners.set(el, /* @__PURE__ */ new Map());
|
|
2481
|
+
}
|
|
2482
|
+
elementListeners.get(el).set(eventName, {
|
|
2483
|
+
handler: wrappedHandler,
|
|
2484
|
+
original: value
|
|
2485
|
+
});
|
|
2486
|
+
} else {
|
|
2487
|
+
el.setAttribute(key, String(value));
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
if (existingListeners) {
|
|
2491
|
+
for (const eventName of existingListeners.keys()) {
|
|
2492
|
+
const entry = existingListeners.get(eventName);
|
|
2493
|
+
if (!desiredEventNames.has(eventName)) {
|
|
2494
|
+
el.removeEventListener(eventName, entry.handler);
|
|
2495
|
+
existingListeners.delete(eventName);
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
if (existingListeners.size === 0) elementListeners.delete(el);
|
|
2499
|
+
}
|
|
2500
|
+
if (updateChildren) {
|
|
2501
|
+
const children = vnode.children || props.children;
|
|
2502
|
+
updateElementChildren(el, children);
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
function updateElementChildren(el, children) {
|
|
2506
|
+
if (!children) {
|
|
2507
|
+
el.textContent = "";
|
|
2508
|
+
return;
|
|
2509
|
+
}
|
|
2510
|
+
if (!Array.isArray(children) && (typeof children === "string" || typeof children === "number")) {
|
|
2511
|
+
if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
|
|
2512
|
+
el.firstChild.data = String(children);
|
|
2513
|
+
} else {
|
|
2514
|
+
el.textContent = String(children);
|
|
2515
|
+
}
|
|
2516
|
+
return;
|
|
2517
|
+
}
|
|
2518
|
+
if (Array.isArray(children)) {
|
|
2519
|
+
updateUnkeyedChildren(el, children);
|
|
2520
|
+
return;
|
|
2521
|
+
}
|
|
2522
|
+
el.textContent = "";
|
|
2523
|
+
const dom = createDOMNode(children);
|
|
2524
|
+
if (dom) el.appendChild(dom);
|
|
2525
|
+
}
|
|
2526
|
+
function updateUnkeyedChildren(parent, newChildren) {
|
|
2527
|
+
const existing = Array.from(parent.children);
|
|
2528
|
+
if (existing.length === 0 && parent.childNodes.length > 0) {
|
|
2529
|
+
parent.textContent = "";
|
|
2530
|
+
}
|
|
2531
|
+
const max = Math.max(existing.length, newChildren.length);
|
|
2532
|
+
for (let i = 0; i < max; i++) {
|
|
2533
|
+
const current2 = existing[i];
|
|
2534
|
+
const next = newChildren[i];
|
|
2535
|
+
if (next === void 0 && current2) {
|
|
2536
|
+
cleanupInstanceIfPresent(current2);
|
|
2537
|
+
current2.remove();
|
|
2538
|
+
continue;
|
|
2539
|
+
}
|
|
2540
|
+
if (!current2 && next !== void 0) {
|
|
2541
|
+
const dom = createDOMNode(next);
|
|
2542
|
+
if (dom) parent.appendChild(dom);
|
|
2543
|
+
continue;
|
|
2544
|
+
}
|
|
2545
|
+
if (!current2 || next === void 0) continue;
|
|
2546
|
+
if (typeof next === "string" || typeof next === "number") {
|
|
2547
|
+
current2.textContent = String(next);
|
|
2548
|
+
} else if (_isDOMElement(next)) {
|
|
2549
|
+
if (typeof next.type === "string") {
|
|
2550
|
+
if (current2.tagName.toLowerCase() === next.type.toLowerCase()) {
|
|
2551
|
+
updateElementFromVnode(current2, next);
|
|
2552
|
+
} else {
|
|
2553
|
+
const dom = createDOMNode(next);
|
|
2554
|
+
if (dom) {
|
|
2555
|
+
cleanupInstanceIfPresent(current2);
|
|
2556
|
+
parent.replaceChild(dom, current2);
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
} else {
|
|
2560
|
+
const dom = createDOMNode(next);
|
|
2561
|
+
if (dom) {
|
|
2562
|
+
cleanupInstanceIfPresent(current2);
|
|
2563
|
+
parent.replaceChild(dom, current2);
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
} else {
|
|
2567
|
+
const dom = createDOMNode(next);
|
|
2568
|
+
if (dom) {
|
|
2569
|
+
cleanupInstanceIfPresent(current2);
|
|
2570
|
+
parent.replaceChild(dom, current2);
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
function createDOMNode(node) {
|
|
2576
|
+
if (typeof node === "string") {
|
|
2577
|
+
return document.createTextNode(node);
|
|
2578
|
+
}
|
|
2579
|
+
if (typeof node === "number") {
|
|
2580
|
+
return document.createTextNode(String(node));
|
|
2581
|
+
}
|
|
2582
|
+
if (!node) {
|
|
2583
|
+
return null;
|
|
2584
|
+
}
|
|
2585
|
+
if (Array.isArray(node)) {
|
|
2586
|
+
const fragment = document.createDocumentFragment();
|
|
2587
|
+
for (let i = 0; i < node.length; i++) {
|
|
2588
|
+
const dom = createDOMNode(node[i]);
|
|
2589
|
+
if (dom) fragment.appendChild(dom);
|
|
2590
|
+
}
|
|
2591
|
+
return fragment;
|
|
2592
|
+
}
|
|
2593
|
+
if (typeof node === "object" && node !== null && "type" in node) {
|
|
2594
|
+
const type = node.type;
|
|
2595
|
+
const props = node.props || {};
|
|
2596
|
+
if (typeof type === "string") {
|
|
2597
|
+
const el = document.createElement(type);
|
|
2598
|
+
for (const key in props) {
|
|
2599
|
+
const value = props[key];
|
|
2600
|
+
if (key === "children" || key === "key") continue;
|
|
2601
|
+
if (value === void 0 || value === null || value === false) continue;
|
|
2602
|
+
if (key.startsWith("on") && key.length > 2) {
|
|
2603
|
+
const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
|
|
2604
|
+
const wrappedHandler = (event) => {
|
|
2605
|
+
globalScheduler.setInHandler(true);
|
|
2606
|
+
try {
|
|
2607
|
+
value(event);
|
|
2608
|
+
} catch (error) {
|
|
2609
|
+
logger.error("[Askr] Event handler error:", error);
|
|
2610
|
+
} finally {
|
|
2611
|
+
globalScheduler.setInHandler(false);
|
|
2612
|
+
const state2 = globalScheduler.getState();
|
|
2613
|
+
if ((state2.queueLength ?? 0) > 0 && !state2.running) {
|
|
2614
|
+
queueMicrotask(() => {
|
|
2615
|
+
try {
|
|
2616
|
+
if (!globalScheduler.isExecuting()) globalScheduler.flush();
|
|
2617
|
+
} catch (err) {
|
|
2618
|
+
setTimeout(() => {
|
|
2619
|
+
throw err;
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
});
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
};
|
|
2626
|
+
el.addEventListener(eventName, wrappedHandler);
|
|
2627
|
+
if (!elementListeners.has(el)) {
|
|
2628
|
+
elementListeners.set(el, /* @__PURE__ */ new Map());
|
|
2629
|
+
}
|
|
2630
|
+
elementListeners.get(el).set(eventName, {
|
|
2631
|
+
handler: wrappedHandler,
|
|
2632
|
+
original: value
|
|
2633
|
+
});
|
|
2634
|
+
} else if (key === "class" || key === "className") {
|
|
2635
|
+
el.className = String(value);
|
|
2636
|
+
} else if (key === "value" || key === "checked") {
|
|
2637
|
+
el[key] = value;
|
|
2638
|
+
el.setAttribute(key, String(value));
|
|
2639
|
+
} else {
|
|
2640
|
+
el.setAttribute(key, String(value));
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
const vnodeKey = node.key;
|
|
2644
|
+
if (vnodeKey !== void 0) {
|
|
2645
|
+
el.setAttribute("data-key", String(vnodeKey));
|
|
2646
|
+
}
|
|
2647
|
+
const children = props.children || node.children;
|
|
2648
|
+
if (children) {
|
|
2649
|
+
if (Array.isArray(children)) {
|
|
2650
|
+
if (process.env.NODE_ENV !== "production") {
|
|
2651
|
+
let hasElements = false;
|
|
2652
|
+
let hasKeys = false;
|
|
2653
|
+
for (let i = 0; i < children.length; i++) {
|
|
2654
|
+
const item = children[i];
|
|
2655
|
+
if (typeof item === "object" && item !== null && "type" in item) {
|
|
2656
|
+
hasElements = true;
|
|
2657
|
+
const itemProps = item.props || {};
|
|
2658
|
+
if ("key" in itemProps) {
|
|
2659
|
+
hasKeys = true;
|
|
2660
|
+
break;
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
if (hasElements && !hasKeys) {
|
|
2665
|
+
if (typeof console !== "undefined") {
|
|
2666
|
+
logger.warn(
|
|
2667
|
+
'Missing keys on dynamic lists. Each child in a list should have a unique "key" prop.'
|
|
2668
|
+
);
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
for (let i = 0; i < children.length; i++) {
|
|
2673
|
+
const dom = createDOMNode(children[i]);
|
|
2674
|
+
if (dom) el.appendChild(dom);
|
|
2675
|
+
}
|
|
2676
|
+
} else {
|
|
2677
|
+
const dom = createDOMNode(children);
|
|
2678
|
+
if (dom) el.appendChild(dom);
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
return el;
|
|
2682
|
+
}
|
|
2683
|
+
if (typeof type === "function") {
|
|
2684
|
+
const frame = node[CONTEXT_FRAME_SYMBOL];
|
|
2685
|
+
const snapshot = frame || getCurrentContextFrame();
|
|
2686
|
+
const componentFn = type;
|
|
2687
|
+
const isAsync = componentFn.constructor.name === "AsyncFunction";
|
|
2688
|
+
if (isAsync) {
|
|
2689
|
+
throw new Error(
|
|
2690
|
+
"Async components are not supported. Use resource() for async work."
|
|
2691
|
+
);
|
|
2692
|
+
}
|
|
2693
|
+
const vnodeAny = node;
|
|
2694
|
+
let childInstance = vnodeAny.__instance;
|
|
2695
|
+
if (!childInstance) {
|
|
2696
|
+
childInstance = createComponentInstance(
|
|
2697
|
+
`comp-${Math.random().toString(36).slice(2, 7)}`,
|
|
2698
|
+
componentFn,
|
|
2699
|
+
props || {},
|
|
2700
|
+
null
|
|
2701
|
+
);
|
|
2702
|
+
vnodeAny.__instance = childInstance;
|
|
2703
|
+
}
|
|
2704
|
+
if (snapshot) {
|
|
2705
|
+
childInstance.ownerFrame = snapshot;
|
|
2706
|
+
}
|
|
2707
|
+
const result = withContext(
|
|
2708
|
+
snapshot,
|
|
2709
|
+
() => renderComponentInline(childInstance)
|
|
2710
|
+
);
|
|
2711
|
+
if (result instanceof Promise) {
|
|
2712
|
+
throw new Error(
|
|
2713
|
+
"Async components are not supported. Components must return synchronously."
|
|
2714
|
+
);
|
|
2715
|
+
}
|
|
2716
|
+
const dom = withContext(snapshot, () => createDOMNode(result));
|
|
2717
|
+
if (dom instanceof Element) {
|
|
2718
|
+
mountInstanceInline(childInstance, dom);
|
|
2719
|
+
} else {
|
|
2720
|
+
const host = document.createElement("div");
|
|
2721
|
+
mountInstanceInline(childInstance, host);
|
|
2722
|
+
}
|
|
2723
|
+
return dom;
|
|
2724
|
+
}
|
|
2725
|
+
if (typeof type === "symbol" && (type === Fragment || String(type) === "Symbol(Fragment)")) {
|
|
2726
|
+
const fragment = document.createDocumentFragment();
|
|
2727
|
+
const children = props.children || node.children;
|
|
2728
|
+
if (children) {
|
|
2729
|
+
if (Array.isArray(children)) {
|
|
2730
|
+
for (let i = 0; i < children.length; i++) {
|
|
2731
|
+
const dom = createDOMNode(children[i]);
|
|
2732
|
+
if (dom) fragment.appendChild(dom);
|
|
2733
|
+
}
|
|
2734
|
+
} else {
|
|
2735
|
+
const dom = createDOMNode(children);
|
|
2736
|
+
if (dom) fragment.appendChild(dom);
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
return fragment;
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
return null;
|
|
2743
|
+
}
|
|
2744
|
+
var domRanges, elementListeners, keyedElements, _reconcilerRecordedParents;
|
|
2745
|
+
var init_dom = __esm({
|
|
2746
|
+
"src/renderer/dom.ts"() {
|
|
2747
|
+
"use strict";
|
|
2748
|
+
init_scheduler();
|
|
2749
|
+
init_fastlane();
|
|
2750
|
+
init_logger();
|
|
2751
|
+
init_jsx_runtime();
|
|
2752
|
+
init_context();
|
|
2753
|
+
init_component();
|
|
2754
|
+
domRanges = /* @__PURE__ */ new WeakMap();
|
|
2755
|
+
elementListeners = /* @__PURE__ */ new WeakMap();
|
|
2756
|
+
keyedElements = /* @__PURE__ */ new WeakMap();
|
|
2757
|
+
_reconcilerRecordedParents = /* @__PURE__ */ new WeakSet();
|
|
2758
|
+
if (typeof globalThis !== "undefined") {
|
|
2759
|
+
const _g = globalThis;
|
|
2760
|
+
_g.__ASKR_RENDERER = {
|
|
2761
|
+
evaluate,
|
|
2762
|
+
isKeyedReorderFastPathEligible,
|
|
2763
|
+
getKeyMapForElement
|
|
2764
|
+
};
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
});
|
|
2768
|
+
|
|
2769
|
+
// src/runtime/component.ts
|
|
2770
|
+
var component_exports = {};
|
|
2771
|
+
__export(component_exports, {
|
|
2772
|
+
cleanupComponent: () => cleanupComponent,
|
|
2773
|
+
createComponentInstance: () => createComponentInstance,
|
|
2774
|
+
executeComponent: () => executeComponent,
|
|
2775
|
+
finalizeReadSubscriptions: () => finalizeReadSubscriptions,
|
|
2776
|
+
getCurrentComponentInstance: () => getCurrentComponentInstance,
|
|
2777
|
+
getCurrentInstance: () => getCurrentInstance,
|
|
2778
|
+
getNextStateIndex: () => getNextStateIndex,
|
|
2779
|
+
getSignal: () => getSignal,
|
|
2780
|
+
mountComponent: () => mountComponent,
|
|
2781
|
+
mountInstanceInline: () => mountInstanceInline,
|
|
2782
|
+
registerMountOperation: () => registerMountOperation,
|
|
2783
|
+
renderComponentInline: () => renderComponentInline,
|
|
2784
|
+
setCurrentComponentInstance: () => setCurrentComponentInstance
|
|
2785
|
+
});
|
|
2786
|
+
function createComponentInstance(id, fn, props, target) {
|
|
2787
|
+
const instance = {
|
|
2788
|
+
id,
|
|
2789
|
+
fn,
|
|
2790
|
+
props,
|
|
2791
|
+
target,
|
|
2792
|
+
mounted: false,
|
|
2793
|
+
abortController: new AbortController(),
|
|
2794
|
+
// Create per-component
|
|
2795
|
+
stateValues: [],
|
|
2796
|
+
evaluationGeneration: 0,
|
|
2797
|
+
notifyUpdate: null,
|
|
2798
|
+
// Prebound helpers (initialized below) to avoid per-update allocations
|
|
2799
|
+
_pendingFlushTask: void 0,
|
|
2800
|
+
_pendingRunTask: void 0,
|
|
2801
|
+
_enqueueRun: void 0,
|
|
2802
|
+
stateIndexCheck: -1,
|
|
2803
|
+
expectedStateIndices: [],
|
|
2804
|
+
firstRenderComplete: false,
|
|
2805
|
+
mountOperations: [],
|
|
2806
|
+
cleanupFns: [],
|
|
2807
|
+
hasPendingUpdate: false,
|
|
2808
|
+
ownerFrame: null,
|
|
2809
|
+
// Will be set by renderer when vnode is marked
|
|
2810
|
+
ssr: false,
|
|
2811
|
+
isRoot: false,
|
|
2812
|
+
// Render-tracking (for precise state subscriptions)
|
|
2813
|
+
_currentRenderToken: void 0,
|
|
2814
|
+
lastRenderToken: 0,
|
|
2815
|
+
_pendingReadStates: /* @__PURE__ */ new Set(),
|
|
2816
|
+
_lastReadStates: /* @__PURE__ */ new Set()
|
|
2817
|
+
};
|
|
2818
|
+
instance._pendingRunTask = () => {
|
|
2819
|
+
instance.hasPendingUpdate = false;
|
|
2820
|
+
runComponent(instance);
|
|
2821
|
+
};
|
|
2822
|
+
instance._enqueueRun = () => {
|
|
2823
|
+
if (!instance.hasPendingUpdate) {
|
|
2824
|
+
instance.hasPendingUpdate = true;
|
|
2825
|
+
globalScheduler.enqueue(instance._pendingRunTask);
|
|
2826
|
+
}
|
|
2827
|
+
};
|
|
2828
|
+
instance._pendingFlushTask = () => {
|
|
2829
|
+
instance.hasPendingUpdate = false;
|
|
2830
|
+
instance._enqueueRun?.();
|
|
2831
|
+
};
|
|
2832
|
+
return instance;
|
|
2833
|
+
}
|
|
2834
|
+
function getCurrentComponentInstance() {
|
|
2835
|
+
return currentInstance;
|
|
2836
|
+
}
|
|
2837
|
+
function setCurrentComponentInstance(instance) {
|
|
2838
|
+
currentInstance = instance;
|
|
2839
|
+
}
|
|
2840
|
+
function registerMountOperation(operation) {
|
|
2841
|
+
const instance = getCurrentComponentInstance();
|
|
2842
|
+
if (instance) {
|
|
2843
|
+
if (isBulkCommitActive2()) {
|
|
2844
|
+
if (process.env.NODE_ENV !== "production") {
|
|
2845
|
+
throw new Error(
|
|
2846
|
+
"registerMountOperation called during bulk commit fast-lane"
|
|
2847
|
+
);
|
|
2848
|
+
}
|
|
2849
|
+
return;
|
|
2850
|
+
}
|
|
2851
|
+
instance.mountOperations.push(operation);
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
function executeMountOperations(instance) {
|
|
2855
|
+
if (!instance.isRoot) return;
|
|
2856
|
+
for (const operation of instance.mountOperations) {
|
|
2857
|
+
const result = operation();
|
|
2858
|
+
if (result instanceof Promise) {
|
|
2859
|
+
result.then((cleanup) => {
|
|
2860
|
+
if (typeof cleanup === "function") {
|
|
2861
|
+
instance.cleanupFns.push(cleanup);
|
|
2862
|
+
}
|
|
2863
|
+
});
|
|
2864
|
+
} else if (typeof result === "function") {
|
|
2865
|
+
instance.cleanupFns.push(result);
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
instance.mountOperations = [];
|
|
2869
|
+
}
|
|
2870
|
+
function mountInstanceInline(instance, target) {
|
|
2871
|
+
instance.target = target;
|
|
2872
|
+
try {
|
|
2873
|
+
if (target instanceof Element)
|
|
2874
|
+
target.__ASKR_INSTANCE = instance;
|
|
2875
|
+
} catch (err) {
|
|
2876
|
+
void err;
|
|
2877
|
+
}
|
|
2878
|
+
instance.notifyUpdate = instance._enqueueRun;
|
|
2879
|
+
const wasFirstMount = !instance.mounted;
|
|
2880
|
+
instance.mounted = true;
|
|
2881
|
+
if (wasFirstMount && instance.mountOperations.length > 0) {
|
|
2882
|
+
executeMountOperations(instance);
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
function runComponent(instance) {
|
|
2886
|
+
instance.notifyUpdate = instance._enqueueRun;
|
|
2887
|
+
instance._currentRenderToken = ++_globalRenderCounter;
|
|
2888
|
+
instance._pendingReadStates = /* @__PURE__ */ new Set();
|
|
2889
|
+
const domSnapshot = instance.target ? instance.target.innerHTML : "";
|
|
2890
|
+
const result = executeComponentSync(instance);
|
|
2891
|
+
if (result instanceof Promise) {
|
|
2892
|
+
throw new Error(
|
|
2893
|
+
"Async components are not supported. Components must be synchronous."
|
|
2894
|
+
);
|
|
2895
|
+
} else {
|
|
2896
|
+
const fastlaneBridge = globalThis.__ASKR_FASTLANE;
|
|
2897
|
+
try {
|
|
2898
|
+
const used = fastlaneBridge?.tryRuntimeFastLaneSync?.(instance, result);
|
|
2899
|
+
if (used) return;
|
|
2900
|
+
} catch (err) {
|
|
2901
|
+
if (process.env.NODE_ENV !== "production") throw err;
|
|
2902
|
+
}
|
|
2903
|
+
globalScheduler.enqueue(() => {
|
|
2904
|
+
if (instance.target) {
|
|
2905
|
+
try {
|
|
2906
|
+
const wasFirstMount = !instance.mounted;
|
|
2907
|
+
const oldInstance = currentInstance;
|
|
2908
|
+
currentInstance = instance;
|
|
2909
|
+
try {
|
|
2910
|
+
evaluate(result, instance.target);
|
|
2911
|
+
} finally {
|
|
2912
|
+
currentInstance = oldInstance;
|
|
2913
|
+
}
|
|
2914
|
+
finalizeReadSubscriptions(instance);
|
|
2915
|
+
instance.mounted = true;
|
|
2916
|
+
if (wasFirstMount && instance.mountOperations.length > 0) {
|
|
2917
|
+
executeMountOperations(instance);
|
|
2918
|
+
}
|
|
2919
|
+
} catch (renderError) {
|
|
2920
|
+
instance.target.innerHTML = domSnapshot;
|
|
2921
|
+
throw renderError;
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
});
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2927
|
+
function renderComponentInline(instance) {
|
|
2928
|
+
const hadToken = instance._currentRenderToken !== void 0;
|
|
2929
|
+
if (!hadToken) {
|
|
2930
|
+
instance._currentRenderToken = ++_globalRenderCounter;
|
|
2931
|
+
instance._pendingReadStates = /* @__PURE__ */ new Set();
|
|
2932
|
+
}
|
|
2933
|
+
try {
|
|
2934
|
+
const result = executeComponentSync(instance);
|
|
2935
|
+
if (!hadToken) {
|
|
2936
|
+
finalizeReadSubscriptions(instance);
|
|
2937
|
+
}
|
|
2938
|
+
return result;
|
|
2939
|
+
} finally {
|
|
2940
|
+
if (!hadToken) {
|
|
2941
|
+
instance._pendingReadStates = /* @__PURE__ */ new Set();
|
|
2942
|
+
instance._currentRenderToken = void 0;
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
function executeComponentSync(instance) {
|
|
2947
|
+
instance.stateIndexCheck = -1;
|
|
2948
|
+
for (const state2 of instance.stateValues) {
|
|
2949
|
+
if (state2) {
|
|
2950
|
+
state2._hasBeenRead = false;
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
instance._pendingReadStates = /* @__PURE__ */ new Set();
|
|
2954
|
+
currentInstance = instance;
|
|
2955
|
+
stateIndex = 0;
|
|
2956
|
+
try {
|
|
2957
|
+
const renderStartTime = process.env.NODE_ENV !== "production" ? Date.now() : 0;
|
|
2958
|
+
const context = {
|
|
2959
|
+
signal: instance.abortController.signal
|
|
2960
|
+
};
|
|
2961
|
+
const executionFrame = {
|
|
2962
|
+
parent: instance.ownerFrame,
|
|
2963
|
+
values: null
|
|
2964
|
+
};
|
|
2965
|
+
const result = withContext(
|
|
2966
|
+
executionFrame,
|
|
2967
|
+
() => instance.fn(instance.props, context)
|
|
2968
|
+
);
|
|
2969
|
+
if (process.env.NODE_ENV !== "production") {
|
|
2970
|
+
const renderTime = Date.now() - renderStartTime;
|
|
2971
|
+
if (renderTime > 5) {
|
|
2972
|
+
logger.warn(
|
|
2973
|
+
`[askr] Slow render detected: ${renderTime}ms. Consider optimizing component performance.`
|
|
2974
|
+
);
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
if (!instance.firstRenderComplete) {
|
|
2978
|
+
instance.firstRenderComplete = true;
|
|
2979
|
+
}
|
|
2980
|
+
if (process.env.NODE_ENV !== "production") {
|
|
2981
|
+
for (let i = 0; i < instance.stateValues.length; i++) {
|
|
2982
|
+
const state2 = instance.stateValues[i];
|
|
2983
|
+
if (state2 && !state2._hasBeenRead) {
|
|
2984
|
+
logger.warn(
|
|
2985
|
+
`[askr] Unused state variable detected. State should be read during render or removed.`
|
|
2986
|
+
);
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
return result;
|
|
2991
|
+
} finally {
|
|
2992
|
+
currentInstance = null;
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
function executeComponent(instance) {
|
|
2996
|
+
instance.abortController = new AbortController();
|
|
2997
|
+
instance.notifyUpdate = instance._enqueueRun;
|
|
2998
|
+
globalScheduler.enqueue(() => runComponent(instance));
|
|
2999
|
+
}
|
|
3000
|
+
function getCurrentInstance() {
|
|
3001
|
+
return currentInstance;
|
|
3002
|
+
}
|
|
3003
|
+
function getSignal() {
|
|
3004
|
+
if (!currentInstance) {
|
|
3005
|
+
throw new Error(
|
|
3006
|
+
"getSignal() can only be called during component render execution. Ensure you are calling this from inside your component function."
|
|
3007
|
+
);
|
|
3008
|
+
}
|
|
3009
|
+
return currentInstance.abortController.signal;
|
|
3010
|
+
}
|
|
3011
|
+
function finalizeReadSubscriptions(instance) {
|
|
3012
|
+
const newSet = instance._pendingReadStates ?? /* @__PURE__ */ new Set();
|
|
3013
|
+
const oldSet = instance._lastReadStates ?? /* @__PURE__ */ new Set();
|
|
3014
|
+
const token = instance._currentRenderToken;
|
|
3015
|
+
if (token === void 0) return;
|
|
3016
|
+
for (const s of oldSet) {
|
|
3017
|
+
if (!newSet.has(s)) {
|
|
3018
|
+
const readers = s?._readers;
|
|
3019
|
+
if (readers) readers.delete(instance);
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
instance.lastRenderToken = token;
|
|
3023
|
+
for (const s of newSet) {
|
|
3024
|
+
let readers = s?._readers;
|
|
3025
|
+
if (!readers) {
|
|
3026
|
+
readers = /* @__PURE__ */ new Map();
|
|
3027
|
+
s._readers = readers;
|
|
3028
|
+
}
|
|
3029
|
+
readers.set(instance, instance.lastRenderToken ?? 0);
|
|
3030
|
+
}
|
|
3031
|
+
instance._lastReadStates = newSet;
|
|
3032
|
+
instance._pendingReadStates = /* @__PURE__ */ new Set();
|
|
3033
|
+
instance._currentRenderToken = void 0;
|
|
3034
|
+
}
|
|
3035
|
+
function getNextStateIndex() {
|
|
3036
|
+
return stateIndex++;
|
|
3037
|
+
}
|
|
3038
|
+
function mountComponent(instance) {
|
|
3039
|
+
executeComponent(instance);
|
|
3040
|
+
}
|
|
3041
|
+
function cleanupComponent(instance) {
|
|
3042
|
+
for (const cleanup of instance.cleanupFns) {
|
|
3043
|
+
cleanup();
|
|
3044
|
+
}
|
|
3045
|
+
instance.cleanupFns = [];
|
|
3046
|
+
if (instance._lastReadStates) {
|
|
3047
|
+
for (const s of instance._lastReadStates) {
|
|
3048
|
+
const readers = s?._readers;
|
|
3049
|
+
if (readers) readers.delete(instance);
|
|
3050
|
+
}
|
|
3051
|
+
instance._lastReadStates = /* @__PURE__ */ new Set();
|
|
3052
|
+
}
|
|
3053
|
+
instance.abortController.abort();
|
|
3054
|
+
}
|
|
3055
|
+
var currentInstance, stateIndex, _globalRenderCounter;
|
|
3056
|
+
var init_component = __esm({
|
|
3057
|
+
"src/runtime/component.ts"() {
|
|
3058
|
+
"use strict";
|
|
3059
|
+
init_dom();
|
|
3060
|
+
init_scheduler();
|
|
3061
|
+
init_context();
|
|
3062
|
+
init_logger();
|
|
3063
|
+
init_fastlane();
|
|
3064
|
+
currentInstance = null;
|
|
3065
|
+
stateIndex = 0;
|
|
3066
|
+
_globalRenderCounter = 0;
|
|
3067
|
+
}
|
|
3068
|
+
});
|
|
3069
|
+
|
|
3070
|
+
// src/ssr/errors.ts
|
|
3071
|
+
var SSRDataMissingError;
|
|
3072
|
+
var init_errors = __esm({
|
|
3073
|
+
"src/ssr/errors.ts"() {
|
|
3074
|
+
"use strict";
|
|
3075
|
+
SSRDataMissingError = class _SSRDataMissingError extends Error {
|
|
3076
|
+
constructor(message = "Server-side rendering requires all data to be available synchronously. This component attempted to use async data during SSR.") {
|
|
3077
|
+
super(message);
|
|
3078
|
+
this.code = "SSR_DATA_MISSING";
|
|
3079
|
+
this.name = "SSRDataMissingError";
|
|
3080
|
+
Object.setPrototypeOf(this, _SSRDataMissingError.prototype);
|
|
3081
|
+
}
|
|
3082
|
+
};
|
|
3083
|
+
}
|
|
3084
|
+
});
|
|
3085
|
+
|
|
3086
|
+
// src/ssr/context.ts
|
|
3087
|
+
function withSSRContext(ctx, fn) {
|
|
3088
|
+
const prev = current;
|
|
3089
|
+
current = ctx;
|
|
3090
|
+
try {
|
|
3091
|
+
return fn();
|
|
3092
|
+
} finally {
|
|
3093
|
+
current = prev;
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
function createRenderContext(seed = 12345) {
|
|
3097
|
+
const rng = new SeededRNG(seed);
|
|
3098
|
+
return { seed, rng };
|
|
3099
|
+
}
|
|
3100
|
+
function getCurrentSSRContext() {
|
|
3101
|
+
return currentSSRContext;
|
|
3102
|
+
}
|
|
3103
|
+
function runWithSSRContext(ctx, fn) {
|
|
3104
|
+
const prev = currentSSRContext;
|
|
3105
|
+
currentSSRContext = ctx;
|
|
3106
|
+
try {
|
|
3107
|
+
return fn();
|
|
3108
|
+
} finally {
|
|
3109
|
+
currentSSRContext = prev;
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
3112
|
+
function throwSSRDataMissing() {
|
|
3113
|
+
throw new SSRDataMissingError();
|
|
3114
|
+
}
|
|
3115
|
+
var current, SeededRNG, currentSSRContext;
|
|
3116
|
+
var init_context2 = __esm({
|
|
3117
|
+
"src/ssr/context.ts"() {
|
|
3118
|
+
"use strict";
|
|
3119
|
+
init_errors();
|
|
3120
|
+
current = null;
|
|
3121
|
+
SeededRNG = class {
|
|
3122
|
+
constructor(seed = 12345) {
|
|
3123
|
+
this.seed = seed | 0;
|
|
3124
|
+
}
|
|
3125
|
+
reset(seed = 12345) {
|
|
3126
|
+
this.seed = seed | 0;
|
|
3127
|
+
}
|
|
3128
|
+
// Simple LCG, stable and deterministic
|
|
3129
|
+
random() {
|
|
3130
|
+
this.seed = (this.seed * 9301 + 49297) % 233280;
|
|
3131
|
+
return this.seed / 233280;
|
|
3132
|
+
}
|
|
3133
|
+
};
|
|
3134
|
+
currentSSRContext = null;
|
|
3135
|
+
}
|
|
3136
|
+
});
|
|
3137
|
+
|
|
3138
|
+
// src/ssr/data.ts
|
|
3139
|
+
function getCurrentRenderData() {
|
|
3140
|
+
return currentRenderData;
|
|
3141
|
+
}
|
|
3142
|
+
function resetKeyCounter() {
|
|
3143
|
+
keyCounter = 0;
|
|
3144
|
+
}
|
|
3145
|
+
function getNextKey() {
|
|
3146
|
+
return `r:${keyCounter++}`;
|
|
3147
|
+
}
|
|
3148
|
+
function startRenderPhase(data) {
|
|
3149
|
+
currentRenderData = data ?? null;
|
|
3150
|
+
resetKeyCounter();
|
|
3151
|
+
}
|
|
3152
|
+
function stopRenderPhase() {
|
|
3153
|
+
currentRenderData = null;
|
|
3154
|
+
resetKeyCounter();
|
|
3155
|
+
}
|
|
3156
|
+
async function resolvePlan(_plan) {
|
|
3157
|
+
throw new Error(
|
|
3158
|
+
"SSR resolution of prepass plans is removed: SSR is strictly synchronous and does not support resolving async resource plans"
|
|
3159
|
+
);
|
|
3160
|
+
}
|
|
3161
|
+
function collectResources(_opts) {
|
|
3162
|
+
throw new Error(
|
|
3163
|
+
"SSR collection/prepass (collectResources) is removed: SSR is strictly synchronous and does not support prepass collection"
|
|
3164
|
+
);
|
|
3165
|
+
}
|
|
3166
|
+
var keyCounter, currentRenderData, resolveResources;
|
|
3167
|
+
var init_data = __esm({
|
|
3168
|
+
"src/ssr/data.ts"() {
|
|
3169
|
+
"use strict";
|
|
3170
|
+
keyCounter = 0;
|
|
3171
|
+
currentRenderData = null;
|
|
3172
|
+
resolveResources = resolvePlan;
|
|
3173
|
+
}
|
|
3174
|
+
});
|
|
3175
|
+
|
|
3176
|
+
// src/router/match.ts
|
|
3177
|
+
function match(path, pattern) {
|
|
3178
|
+
const normalizedPath = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
|
|
3179
|
+
const normalizedPattern = pattern.endsWith("/") && pattern !== "/" ? pattern.slice(0, -1) : pattern;
|
|
3180
|
+
const pathSegments = normalizedPath.split("/").filter(Boolean);
|
|
3181
|
+
const patternSegments = normalizedPattern.split("/").filter(Boolean);
|
|
3182
|
+
if (patternSegments.length === 1 && patternSegments[0] === "*") {
|
|
3183
|
+
return {
|
|
3184
|
+
matched: true,
|
|
3185
|
+
params: {
|
|
3186
|
+
"*": pathSegments.length > 1 ? normalizedPath : pathSegments[0]
|
|
3187
|
+
}
|
|
3188
|
+
};
|
|
3189
|
+
}
|
|
3190
|
+
if (pathSegments.length !== patternSegments.length) {
|
|
3191
|
+
return { matched: false, params: {} };
|
|
3192
|
+
}
|
|
3193
|
+
const params = {};
|
|
3194
|
+
for (let i = 0; i < patternSegments.length; i++) {
|
|
3195
|
+
const patternSegment = patternSegments[i];
|
|
3196
|
+
const pathSegment = pathSegments[i];
|
|
3197
|
+
if (patternSegment.startsWith("{") && patternSegment.endsWith("}")) {
|
|
3198
|
+
const paramName = patternSegment.slice(1, -1);
|
|
3199
|
+
params[paramName] = decodeURIComponent(pathSegment);
|
|
3200
|
+
} else if (patternSegment === "*") {
|
|
3201
|
+
params["*"] = pathSegment;
|
|
3202
|
+
} else if (patternSegment !== pathSegment) {
|
|
3203
|
+
return { matched: false, params: {} };
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
return { matched: true, params };
|
|
3207
|
+
}
|
|
3208
|
+
var init_match = __esm({
|
|
3209
|
+
"src/router/match.ts"() {
|
|
3210
|
+
"use strict";
|
|
3211
|
+
}
|
|
3212
|
+
});
|
|
3213
|
+
|
|
3214
|
+
// src/router/route.ts
|
|
3215
|
+
var route_exports = {};
|
|
3216
|
+
__export(route_exports, {
|
|
3217
|
+
_lockRouteRegistrationForTests: () => _lockRouteRegistrationForTests,
|
|
3218
|
+
_unlockRouteRegistrationForTests: () => _unlockRouteRegistrationForTests,
|
|
3219
|
+
clearRoutes: () => clearRoutes,
|
|
3220
|
+
getLoadedNamespaces: () => getLoadedNamespaces,
|
|
3221
|
+
getNamespaceRoutes: () => getNamespaceRoutes,
|
|
3222
|
+
getRoutes: () => getRoutes,
|
|
3223
|
+
lockRouteRegistration: () => lockRouteRegistration,
|
|
3224
|
+
registerRoute: () => registerRoute,
|
|
3225
|
+
resolveRoute: () => resolveRoute,
|
|
3226
|
+
route: () => route,
|
|
3227
|
+
setServerLocation: () => setServerLocation,
|
|
3228
|
+
unloadNamespace: () => unloadNamespace
|
|
3229
|
+
});
|
|
3230
|
+
function getDepth(path) {
|
|
3231
|
+
const normalized = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
|
|
3232
|
+
return normalized === "/" ? 0 : normalized.split("/").filter(Boolean).length;
|
|
3233
|
+
}
|
|
3234
|
+
function getSpecificity(path) {
|
|
3235
|
+
const normalized = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
|
|
3236
|
+
if (normalized === "/*") {
|
|
3237
|
+
return 0;
|
|
3238
|
+
}
|
|
3239
|
+
const segments = normalized.split("/").filter(Boolean);
|
|
3240
|
+
let score = 0;
|
|
3241
|
+
for (const segment of segments) {
|
|
3242
|
+
if (segment.startsWith("{") && segment.endsWith("}")) {
|
|
3243
|
+
score += 2;
|
|
3244
|
+
} else if (segment === "*") {
|
|
3245
|
+
score += 1;
|
|
3246
|
+
} else {
|
|
3247
|
+
score += 3;
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
return score;
|
|
3251
|
+
}
|
|
3252
|
+
function setServerLocation(url) {
|
|
3253
|
+
serverLocation = url;
|
|
3254
|
+
}
|
|
3255
|
+
function parseLocation(url) {
|
|
3256
|
+
try {
|
|
3257
|
+
const u = new URL(url, "http://localhost");
|
|
3258
|
+
return { pathname: u.pathname, search: u.search, hash: u.hash };
|
|
3259
|
+
} catch {
|
|
3260
|
+
return { pathname: "/", search: "", hash: "" };
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
3263
|
+
function deepFreeze(obj) {
|
|
3264
|
+
if (obj && typeof obj === "object" && !Object.isFrozen(obj)) {
|
|
3265
|
+
Object.freeze(obj);
|
|
3266
|
+
for (const key of Object.keys(obj)) {
|
|
3267
|
+
const value = obj[key];
|
|
3268
|
+
if (value && typeof value === "object") deepFreeze(value);
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
return obj;
|
|
3272
|
+
}
|
|
3273
|
+
function makeQuery(search) {
|
|
3274
|
+
const usp = new URLSearchParams(search || "");
|
|
3275
|
+
const mapping = /* @__PURE__ */ new Map();
|
|
3276
|
+
for (const [k, v] of usp.entries()) {
|
|
3277
|
+
const existing = mapping.get(k);
|
|
3278
|
+
if (existing) existing.push(v);
|
|
3279
|
+
else mapping.set(k, [v]);
|
|
3280
|
+
}
|
|
3281
|
+
const obj = {
|
|
3282
|
+
get(key) {
|
|
3283
|
+
const arr = mapping.get(key);
|
|
3284
|
+
return arr ? arr[0] : null;
|
|
3285
|
+
},
|
|
3286
|
+
getAll(key) {
|
|
3287
|
+
const arr = mapping.get(key);
|
|
3288
|
+
return arr ? [...arr] : [];
|
|
3289
|
+
},
|
|
3290
|
+
has(key) {
|
|
3291
|
+
return mapping.has(key);
|
|
3292
|
+
},
|
|
3293
|
+
toJSON() {
|
|
3294
|
+
const out = {};
|
|
3295
|
+
for (const [k, arr] of mapping.entries()) {
|
|
3296
|
+
out[k] = arr.length > 1 ? [...arr] : arr[0];
|
|
3297
|
+
}
|
|
3298
|
+
return out;
|
|
3299
|
+
}
|
|
3300
|
+
};
|
|
3301
|
+
return deepFreeze(obj);
|
|
3302
|
+
}
|
|
3303
|
+
function computeMatches(pathname) {
|
|
3304
|
+
const routesList = getRoutes();
|
|
3305
|
+
const matches = [];
|
|
3306
|
+
function getSpecificity2(path) {
|
|
3307
|
+
const normalized = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
|
|
3308
|
+
if (normalized === "/*") return 0;
|
|
3309
|
+
const segments = normalized.split("/").filter(Boolean);
|
|
3310
|
+
let score = 0;
|
|
3311
|
+
for (const segment of segments) {
|
|
3312
|
+
if (segment.startsWith("{") && segment.endsWith("}")) score += 2;
|
|
3313
|
+
else if (segment === "*") score += 1;
|
|
3314
|
+
else score += 3;
|
|
3315
|
+
}
|
|
3316
|
+
return score;
|
|
3317
|
+
}
|
|
3318
|
+
for (const r of routesList) {
|
|
3319
|
+
const result = match(pathname, r.path);
|
|
3320
|
+
if (result.matched) {
|
|
3321
|
+
matches.push({
|
|
3322
|
+
pattern: r.path,
|
|
3323
|
+
params: result.params,
|
|
3324
|
+
name: r.name,
|
|
3325
|
+
namespace: r.namespace,
|
|
3326
|
+
specificity: getSpecificity2(r.path)
|
|
3327
|
+
});
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
matches.sort((a, b) => b.specificity - a.specificity);
|
|
3331
|
+
return matches.map((m) => ({
|
|
3332
|
+
path: m.pattern,
|
|
3333
|
+
params: deepFreeze({ ...m.params }),
|
|
3334
|
+
name: m.name,
|
|
3335
|
+
namespace: m.namespace
|
|
3336
|
+
}));
|
|
3337
|
+
}
|
|
3338
|
+
function lockRouteRegistration() {
|
|
3339
|
+
registrationLocked = true;
|
|
3340
|
+
}
|
|
3341
|
+
function _lockRouteRegistrationForTests() {
|
|
3342
|
+
registrationLocked = true;
|
|
3343
|
+
}
|
|
3344
|
+
function _unlockRouteRegistrationForTests() {
|
|
3345
|
+
registrationLocked = false;
|
|
3346
|
+
}
|
|
3347
|
+
function route(path, handler, namespace) {
|
|
3348
|
+
if (typeof path === "undefined") {
|
|
3349
|
+
const instance = getCurrentComponentInstance();
|
|
3350
|
+
if (!instance) {
|
|
3351
|
+
throw new Error(
|
|
3352
|
+
"route() can only be called during component render execution. Call route() from inside your component function."
|
|
3353
|
+
);
|
|
3354
|
+
}
|
|
3355
|
+
let pathname = "/";
|
|
3356
|
+
let search = "";
|
|
3357
|
+
let hash = "";
|
|
3358
|
+
if (typeof window !== "undefined" && window.location) {
|
|
3359
|
+
pathname = window.location.pathname || "/";
|
|
3360
|
+
search = window.location.search || "";
|
|
3361
|
+
hash = window.location.hash || "";
|
|
3362
|
+
} else if (serverLocation) {
|
|
3363
|
+
const parsed = parseLocation(serverLocation);
|
|
3364
|
+
pathname = parsed.pathname;
|
|
3365
|
+
search = parsed.search;
|
|
3366
|
+
hash = parsed.hash;
|
|
3367
|
+
}
|
|
3368
|
+
const params = deepFreeze({
|
|
3369
|
+
...instance.props || {}
|
|
3370
|
+
});
|
|
3371
|
+
const query = makeQuery(search);
|
|
3372
|
+
const matches = computeMatches(pathname);
|
|
3373
|
+
const snapshot = Object.freeze({
|
|
3374
|
+
path: pathname,
|
|
3375
|
+
params,
|
|
3376
|
+
query,
|
|
3377
|
+
hash: hash || null,
|
|
3378
|
+
matches: Object.freeze(matches)
|
|
3379
|
+
});
|
|
3380
|
+
return snapshot;
|
|
3381
|
+
}
|
|
3382
|
+
const currentInst = getCurrentComponentInstance();
|
|
3383
|
+
if (currentInst && currentInst.ssr) {
|
|
3384
|
+
throw new Error(
|
|
3385
|
+
"route() cannot be called during SSR rendering. Register routes at module load time instead."
|
|
3386
|
+
);
|
|
3387
|
+
}
|
|
3388
|
+
if (registrationLocked) {
|
|
3389
|
+
throw new Error(
|
|
3390
|
+
"Route registration is locked after app startup. Register routes at module load time before calling createApp()."
|
|
3391
|
+
);
|
|
3392
|
+
}
|
|
3393
|
+
if (typeof handler !== "function") {
|
|
3394
|
+
throw new Error(
|
|
3395
|
+
"route(path, handler) requires a function handler that returns a VNode (e.g. () => <Page />). Passing JSX elements or VNodes directly is not supported."
|
|
3396
|
+
);
|
|
3397
|
+
}
|
|
3398
|
+
const routeObj = { path, handler, namespace };
|
|
3399
|
+
routes.push(routeObj);
|
|
3400
|
+
const depth = getDepth(path);
|
|
3401
|
+
let depthRoutes = routesByDepth.get(depth);
|
|
3402
|
+
if (!depthRoutes) {
|
|
3403
|
+
depthRoutes = [];
|
|
3404
|
+
routesByDepth.set(depth, depthRoutes);
|
|
3405
|
+
}
|
|
3406
|
+
depthRoutes.push(routeObj);
|
|
3407
|
+
if (namespace) {
|
|
3408
|
+
namespaces.add(namespace);
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
function getRoutes() {
|
|
3412
|
+
return [...routes];
|
|
3413
|
+
}
|
|
3414
|
+
function getNamespaceRoutes(namespace) {
|
|
3415
|
+
return routes.filter((r) => r.namespace === namespace);
|
|
3416
|
+
}
|
|
3417
|
+
function unloadNamespace(namespace) {
|
|
3418
|
+
const before = routes.length;
|
|
3419
|
+
for (let i = routes.length - 1; i >= 0; i--) {
|
|
3420
|
+
if (routes[i].namespace === namespace) {
|
|
3421
|
+
const removed = routes[i];
|
|
3422
|
+
routes.splice(i, 1);
|
|
3423
|
+
const depth = getDepth(removed.path);
|
|
3424
|
+
const depthRoutes = routesByDepth.get(depth);
|
|
3425
|
+
if (depthRoutes) {
|
|
3426
|
+
const idx = depthRoutes.indexOf(removed);
|
|
3427
|
+
if (idx >= 0) {
|
|
3428
|
+
depthRoutes.splice(idx, 1);
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
namespaces.delete(namespace);
|
|
3434
|
+
return before - routes.length;
|
|
3435
|
+
}
|
|
3436
|
+
function clearRoutes() {
|
|
3437
|
+
routes.length = 0;
|
|
3438
|
+
namespaces.clear();
|
|
3439
|
+
routesByDepth.clear();
|
|
3440
|
+
}
|
|
3441
|
+
function normalizeHandler(handler) {
|
|
3442
|
+
if (handler == null) return void 0;
|
|
3443
|
+
if (typeof handler === "function") {
|
|
3444
|
+
return (params, ctx) => {
|
|
3445
|
+
try {
|
|
3446
|
+
return handler(params, ctx);
|
|
3447
|
+
} catch {
|
|
3448
|
+
return handler(params);
|
|
3449
|
+
}
|
|
3450
|
+
};
|
|
3451
|
+
}
|
|
3452
|
+
return void 0;
|
|
3453
|
+
}
|
|
3454
|
+
function registerRoute(path, handler, ...children) {
|
|
3455
|
+
const isRelative = !path.startsWith("/");
|
|
3456
|
+
const descriptor = {
|
|
3457
|
+
path,
|
|
3458
|
+
handler,
|
|
3459
|
+
children: children.filter(Boolean),
|
|
3460
|
+
_isDescriptor: true
|
|
3461
|
+
};
|
|
3462
|
+
if (!isRelative) {
|
|
3463
|
+
const normalized = normalizeHandler(handler);
|
|
3464
|
+
if (handler != null && !normalized) {
|
|
3465
|
+
throw new Error(
|
|
3466
|
+
"registerRoute(path, handler) requires a function handler. Passing JSX elements or VNodes directly is not supported."
|
|
3467
|
+
);
|
|
3468
|
+
}
|
|
3469
|
+
if (normalized) route(path, normalized);
|
|
3470
|
+
for (const child of descriptor.children || []) {
|
|
3471
|
+
const base = path === "/" ? "" : path.replace(/\/$/, "");
|
|
3472
|
+
const childPath = `${base}/${child.path.replace(/^\//, "")}`.replace(
|
|
3473
|
+
/\/\//g,
|
|
3474
|
+
"/"
|
|
3475
|
+
);
|
|
3476
|
+
if (child.handler) {
|
|
3477
|
+
const childNormalized = normalizeHandler(child.handler);
|
|
3478
|
+
if (!childNormalized) {
|
|
3479
|
+
throw new Error(
|
|
3480
|
+
"registerRoute child handler must be a function. Passing JSX elements directly is not supported."
|
|
3481
|
+
);
|
|
3482
|
+
}
|
|
3483
|
+
if (childNormalized) route(childPath, childNormalized);
|
|
3484
|
+
}
|
|
3485
|
+
if (child.children && child.children.length) {
|
|
3486
|
+
registerRoute(
|
|
3487
|
+
childPath,
|
|
3488
|
+
null,
|
|
3489
|
+
...child.children
|
|
3490
|
+
);
|
|
3491
|
+
}
|
|
3492
|
+
}
|
|
3493
|
+
return descriptor;
|
|
3494
|
+
}
|
|
3495
|
+
return descriptor;
|
|
3496
|
+
}
|
|
3497
|
+
function getLoadedNamespaces() {
|
|
3498
|
+
return Array.from(namespaces);
|
|
3499
|
+
}
|
|
3500
|
+
function resolveRoute(pathname) {
|
|
3501
|
+
const normalized = pathname.endsWith("/") && pathname !== "/" ? pathname.slice(0, -1) : pathname;
|
|
3502
|
+
const depth = normalized === "/" ? 0 : normalized.split("/").filter(Boolean).length;
|
|
3503
|
+
const candidates = [];
|
|
3504
|
+
const depthRoutes = routesByDepth.get(depth);
|
|
3505
|
+
if (depthRoutes) {
|
|
3506
|
+
for (const r of depthRoutes) {
|
|
3507
|
+
const result = match(pathname, r.path);
|
|
3508
|
+
if (result.matched) {
|
|
3509
|
+
candidates.push({
|
|
3510
|
+
route: r,
|
|
3511
|
+
specificity: getSpecificity(r.path),
|
|
3512
|
+
params: result.params
|
|
3513
|
+
});
|
|
3514
|
+
}
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
for (const r of routes) {
|
|
3518
|
+
if (depthRoutes?.includes(r)) continue;
|
|
3519
|
+
const result = match(pathname, r.path);
|
|
3520
|
+
if (result.matched) {
|
|
3521
|
+
candidates.push({
|
|
3522
|
+
route: r,
|
|
3523
|
+
specificity: getSpecificity(r.path),
|
|
3524
|
+
params: result.params
|
|
3525
|
+
});
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
candidates.sort((a, b) => b.specificity - a.specificity);
|
|
3529
|
+
if (candidates.length > 0) {
|
|
3530
|
+
const best = candidates[0];
|
|
3531
|
+
return { handler: best.route.handler, params: best.params };
|
|
3532
|
+
}
|
|
3533
|
+
return null;
|
|
3534
|
+
}
|
|
3535
|
+
var routes, namespaces, routesByDepth, serverLocation, registrationLocked;
|
|
3536
|
+
var init_route = __esm({
|
|
3537
|
+
"src/router/route.ts"() {
|
|
3538
|
+
"use strict";
|
|
3539
|
+
init_match();
|
|
3540
|
+
init_component();
|
|
3541
|
+
routes = [];
|
|
3542
|
+
namespaces = /* @__PURE__ */ new Set();
|
|
3543
|
+
routesByDepth = /* @__PURE__ */ new Map();
|
|
3544
|
+
serverLocation = null;
|
|
3545
|
+
registrationLocked = false;
|
|
3546
|
+
}
|
|
3547
|
+
});
|
|
3548
|
+
|
|
3549
|
+
// src/router/navigate.ts
|
|
3550
|
+
var navigate_exports = {};
|
|
3551
|
+
__export(navigate_exports, {
|
|
3552
|
+
cleanupNavigation: () => cleanupNavigation,
|
|
3553
|
+
initializeNavigation: () => initializeNavigation,
|
|
3554
|
+
navigate: () => navigate,
|
|
3555
|
+
registerAppInstance: () => registerAppInstance
|
|
3556
|
+
});
|
|
3557
|
+
function registerAppInstance(instance, _path) {
|
|
3558
|
+
currentInstance2 = instance;
|
|
3559
|
+
if (process.env.NODE_ENV === "production") {
|
|
3560
|
+
lockRouteRegistration();
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3563
|
+
function navigate(path) {
|
|
3564
|
+
if (typeof window === "undefined") {
|
|
3565
|
+
return;
|
|
3566
|
+
}
|
|
3567
|
+
const resolved = resolveRoute(path);
|
|
3568
|
+
if (!resolved) {
|
|
3569
|
+
if (process.env.NODE_ENV !== "production") {
|
|
3570
|
+
logger.warn(`No route found for path: ${path}`);
|
|
3571
|
+
}
|
|
3572
|
+
return;
|
|
3573
|
+
}
|
|
3574
|
+
window.history.pushState({ path }, "", path);
|
|
3575
|
+
if (currentInstance2) {
|
|
3576
|
+
cleanupComponent(currentInstance2);
|
|
3577
|
+
currentInstance2.fn = resolved.handler;
|
|
3578
|
+
currentInstance2.props = resolved.params;
|
|
3579
|
+
currentInstance2.stateValues = [];
|
|
3580
|
+
currentInstance2.expectedStateIndices = [];
|
|
3581
|
+
currentInstance2.firstRenderComplete = false;
|
|
3582
|
+
currentInstance2.stateIndexCheck = -1;
|
|
3583
|
+
currentInstance2.evaluationGeneration++;
|
|
3584
|
+
currentInstance2.notifyUpdate = null;
|
|
3585
|
+
currentInstance2.abortController = new AbortController();
|
|
3586
|
+
mountComponent(currentInstance2);
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
function handlePopState(_event) {
|
|
3590
|
+
const path = window.location.pathname;
|
|
3591
|
+
if (!currentInstance2) {
|
|
3592
|
+
return;
|
|
3593
|
+
}
|
|
3594
|
+
const resolved = resolveRoute(path);
|
|
3595
|
+
if (resolved) {
|
|
3596
|
+
cleanupComponent(currentInstance2);
|
|
3597
|
+
currentInstance2.fn = resolved.handler;
|
|
3598
|
+
currentInstance2.props = resolved.params;
|
|
3599
|
+
currentInstance2.stateValues = [];
|
|
3600
|
+
currentInstance2.expectedStateIndices = [];
|
|
3601
|
+
currentInstance2.firstRenderComplete = false;
|
|
3602
|
+
currentInstance2.stateIndexCheck = -1;
|
|
3603
|
+
currentInstance2.evaluationGeneration++;
|
|
3604
|
+
currentInstance2.notifyUpdate = null;
|
|
3605
|
+
currentInstance2.abortController = new AbortController();
|
|
3606
|
+
mountComponent(currentInstance2);
|
|
3607
|
+
}
|
|
3608
|
+
}
|
|
3609
|
+
function initializeNavigation() {
|
|
3610
|
+
if (typeof window !== "undefined") {
|
|
3611
|
+
window.addEventListener("popstate", handlePopState);
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
function cleanupNavigation() {
|
|
3615
|
+
if (typeof window !== "undefined") {
|
|
3616
|
+
window.removeEventListener("popstate", handlePopState);
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
var currentInstance2;
|
|
3620
|
+
var init_navigate = __esm({
|
|
3621
|
+
"src/router/navigate.ts"() {
|
|
3622
|
+
"use strict";
|
|
3623
|
+
init_route();
|
|
3624
|
+
init_component();
|
|
3625
|
+
init_logger();
|
|
3626
|
+
currentInstance2 = null;
|
|
3627
|
+
}
|
|
3628
|
+
});
|
|
3629
|
+
|
|
3630
|
+
// src/ssr/sink.ts
|
|
3631
|
+
var StringSink, StreamSink;
|
|
3632
|
+
var init_sink = __esm({
|
|
3633
|
+
"src/ssr/sink.ts"() {
|
|
3634
|
+
"use strict";
|
|
3635
|
+
StringSink = class {
|
|
3636
|
+
constructor() {
|
|
3637
|
+
this.chunks = [];
|
|
3638
|
+
}
|
|
3639
|
+
write(html) {
|
|
3640
|
+
if (html) this.chunks.push(html);
|
|
3641
|
+
}
|
|
3642
|
+
end() {
|
|
3643
|
+
}
|
|
3644
|
+
toString() {
|
|
3645
|
+
return this.chunks.join("");
|
|
3646
|
+
}
|
|
3647
|
+
};
|
|
3648
|
+
StreamSink = class {
|
|
3649
|
+
constructor(onChunk, onComplete) {
|
|
3650
|
+
this.onChunk = onChunk;
|
|
3651
|
+
this.onComplete = onComplete;
|
|
3652
|
+
}
|
|
3653
|
+
write(html) {
|
|
3654
|
+
if (html) this.onChunk(html);
|
|
3655
|
+
}
|
|
3656
|
+
end() {
|
|
3657
|
+
this.onComplete();
|
|
3658
|
+
}
|
|
3659
|
+
};
|
|
3660
|
+
}
|
|
3661
|
+
});
|
|
3662
|
+
|
|
3663
|
+
// src/ssr/render.ts
|
|
3664
|
+
function escapeText(text) {
|
|
3665
|
+
const cached = escapeCache.get(text);
|
|
3666
|
+
if (cached) return cached;
|
|
3667
|
+
const str = String(text);
|
|
3668
|
+
if (!str.includes("&") && !str.includes("<") && !str.includes(">")) {
|
|
3669
|
+
if (escapeCache.size < 256) escapeCache.set(text, str);
|
|
3670
|
+
return str;
|
|
3671
|
+
}
|
|
3672
|
+
const result = str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
3673
|
+
if (escapeCache.size < 256) escapeCache.set(text, result);
|
|
3674
|
+
return result;
|
|
3675
|
+
}
|
|
3676
|
+
function escapeAttr(value) {
|
|
3677
|
+
const str = String(value);
|
|
3678
|
+
if (!str.includes("&") && !str.includes('"') && !str.includes("'") && !str.includes("<") && !str.includes(">")) {
|
|
3679
|
+
return str;
|
|
3680
|
+
}
|
|
3681
|
+
return str.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
3682
|
+
}
|
|
3683
|
+
function styleObjToCss(value) {
|
|
3684
|
+
if (!value || typeof value !== "object") return null;
|
|
3685
|
+
const entries = Object.entries(value);
|
|
3686
|
+
if (entries.length === 0) return "";
|
|
3687
|
+
let out = "";
|
|
3688
|
+
for (const [k, v] of entries) {
|
|
3689
|
+
if (v === null || v === void 0 || v === false) continue;
|
|
3690
|
+
const prop = k.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
3691
|
+
out += `${prop}:${String(v)};`;
|
|
3692
|
+
}
|
|
3693
|
+
return out;
|
|
3694
|
+
}
|
|
3695
|
+
function renderAttrs(props) {
|
|
3696
|
+
if (!props || typeof props !== "object") return "";
|
|
3697
|
+
let result = "";
|
|
3698
|
+
for (const [key, value] of Object.entries(props)) {
|
|
3699
|
+
if (key === "children") continue;
|
|
3700
|
+
if (key.startsWith("on") && key[2] === key[2]?.toUpperCase()) continue;
|
|
3701
|
+
if (key.startsWith("_")) continue;
|
|
3702
|
+
const attrName = key === "class" || key === "className" ? "class" : key;
|
|
3703
|
+
if (attrName === "style") {
|
|
3704
|
+
const css = typeof value === "string" ? value : styleObjToCss(value);
|
|
3705
|
+
if (css === null) continue;
|
|
3706
|
+
if (css === "") continue;
|
|
3707
|
+
result += ` style="${escapeAttr(css)}"`;
|
|
3708
|
+
continue;
|
|
3709
|
+
}
|
|
3710
|
+
if (value === true) {
|
|
3711
|
+
result += ` ${attrName}`;
|
|
3712
|
+
} else if (value === false || value === null || value === void 0) {
|
|
3713
|
+
continue;
|
|
3714
|
+
} else {
|
|
3715
|
+
result += ` ${attrName}="${escapeAttr(String(value))}"`;
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
return result;
|
|
3719
|
+
}
|
|
3720
|
+
function isVNodeLike(x) {
|
|
3721
|
+
return !!x && typeof x === "object" && "type" in x;
|
|
3722
|
+
}
|
|
3723
|
+
function normalizeChildren(node) {
|
|
3724
|
+
const n = node;
|
|
3725
|
+
const direct = Array.isArray(n?.children) ? n?.children : null;
|
|
3726
|
+
const fromProps = n?.props?.children;
|
|
3727
|
+
const raw = direct ?? fromProps;
|
|
3728
|
+
if (raw === null || raw === void 0 || raw === false) return [];
|
|
3729
|
+
if (Array.isArray(raw)) return raw;
|
|
3730
|
+
return [raw];
|
|
3731
|
+
}
|
|
3732
|
+
function renderChildrenToSink(children, sink, ctx) {
|
|
3733
|
+
for (const c of children)
|
|
3734
|
+
renderNodeToSink(
|
|
3735
|
+
c,
|
|
3736
|
+
sink,
|
|
3737
|
+
ctx
|
|
3738
|
+
);
|
|
3739
|
+
}
|
|
3740
|
+
function executeComponent2(type, props, ctx) {
|
|
3741
|
+
const res = type(props ?? {}, { signal: ctx.signal });
|
|
3742
|
+
if (res && typeof res === "object" && "then" in res && typeof res.then === "function") {
|
|
3743
|
+
throwSSRDataMissing();
|
|
3744
|
+
}
|
|
3745
|
+
return res;
|
|
3746
|
+
}
|
|
3747
|
+
function renderNodeToSink(node, sink, ctx) {
|
|
3748
|
+
if (node === null || node === void 0) return;
|
|
3749
|
+
if (typeof node === "string") {
|
|
3750
|
+
sink.write(escapeText(node));
|
|
3751
|
+
return;
|
|
3752
|
+
}
|
|
3753
|
+
if (typeof node === "number") {
|
|
3754
|
+
sink.write(escapeText(String(node)));
|
|
3755
|
+
return;
|
|
3756
|
+
}
|
|
3757
|
+
if (!isVNodeLike(node)) return;
|
|
3758
|
+
const { type, props } = node;
|
|
3759
|
+
if (typeof type === "function") {
|
|
3760
|
+
const out = withSSRContext(
|
|
3761
|
+
ctx,
|
|
3762
|
+
() => executeComponent2(type, props, ctx)
|
|
3763
|
+
);
|
|
3764
|
+
renderNodeToSink(
|
|
3765
|
+
out,
|
|
3766
|
+
sink,
|
|
3767
|
+
ctx
|
|
3768
|
+
);
|
|
3769
|
+
return;
|
|
3770
|
+
}
|
|
3771
|
+
const tag = String(type);
|
|
3772
|
+
const attrs = renderAttrs(props);
|
|
3773
|
+
if (VOID_ELEMENTS.has(tag)) {
|
|
3774
|
+
sink.write(`<${tag}${attrs} />`);
|
|
3775
|
+
return;
|
|
3776
|
+
}
|
|
3777
|
+
sink.write(`<${tag}${attrs}>`);
|
|
3778
|
+
const children = normalizeChildren(node);
|
|
3779
|
+
renderChildrenToSink(children, sink, ctx);
|
|
3780
|
+
sink.write(`</${tag}>`);
|
|
3781
|
+
}
|
|
3782
|
+
var VOID_ELEMENTS, escapeCache;
|
|
3783
|
+
var init_render = __esm({
|
|
3784
|
+
"src/ssr/render.ts"() {
|
|
3785
|
+
"use strict";
|
|
3786
|
+
init_context2();
|
|
3787
|
+
VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
3788
|
+
"area",
|
|
3789
|
+
"base",
|
|
3790
|
+
"br",
|
|
3791
|
+
"col",
|
|
3792
|
+
"embed",
|
|
3793
|
+
"hr",
|
|
3794
|
+
"img",
|
|
3795
|
+
"input",
|
|
3796
|
+
"link",
|
|
3797
|
+
"meta",
|
|
3798
|
+
"param",
|
|
3799
|
+
"source",
|
|
3800
|
+
"track",
|
|
3801
|
+
"wbr"
|
|
3802
|
+
]);
|
|
3803
|
+
escapeCache = /* @__PURE__ */ new Map();
|
|
3804
|
+
}
|
|
3805
|
+
});
|
|
3806
|
+
|
|
3807
|
+
// src/ssr/index.ts
|
|
3808
|
+
var ssr_exports = {};
|
|
3809
|
+
__export(ssr_exports, {
|
|
3810
|
+
SSRDataMissingError: () => SSRDataMissingError,
|
|
3811
|
+
collectResources: () => collectResources,
|
|
3812
|
+
renderToStream: () => renderToStream,
|
|
3813
|
+
renderToString: () => renderToString,
|
|
3814
|
+
renderToStringSync: () => renderToStringSync,
|
|
3815
|
+
renderToStringSyncForUrl: () => renderToStringSyncForUrl,
|
|
3816
|
+
resolvePlan: () => resolvePlan,
|
|
3817
|
+
resolveResources: () => resolveResources
|
|
3818
|
+
});
|
|
3819
|
+
function escapeText2(text) {
|
|
3820
|
+
const cached = escapeCache2.get(text);
|
|
3821
|
+
if (cached) return cached;
|
|
3822
|
+
const str = String(text);
|
|
3823
|
+
if (!str.includes("&") && !str.includes("<") && !str.includes(">")) {
|
|
3824
|
+
escapeCache2.set(text, str);
|
|
3825
|
+
return str;
|
|
3826
|
+
}
|
|
3827
|
+
const result = str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
3828
|
+
if (escapeCache2.size < 256) {
|
|
3829
|
+
escapeCache2.set(text, result);
|
|
3830
|
+
}
|
|
3831
|
+
return result;
|
|
3832
|
+
}
|
|
3833
|
+
function escapeAttr2(value) {
|
|
3834
|
+
const str = String(value);
|
|
3835
|
+
if (!str.includes("&") && !str.includes('"') && !str.includes("'") && !str.includes("<") && !str.includes(">")) {
|
|
3836
|
+
return str;
|
|
3837
|
+
}
|
|
3838
|
+
return str.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
3839
|
+
}
|
|
3840
|
+
function renderAttrs2(props) {
|
|
3841
|
+
if (!props || typeof props !== "object") return "";
|
|
3842
|
+
let result = "";
|
|
3843
|
+
for (const [key, value] of Object.entries(props)) {
|
|
3844
|
+
if (key.startsWith("on") && key[2] === key[2].toUpperCase()) {
|
|
3845
|
+
continue;
|
|
3846
|
+
}
|
|
3847
|
+
if (key.startsWith("_")) {
|
|
3848
|
+
continue;
|
|
3849
|
+
}
|
|
3850
|
+
const attrName = key === "class" || key === "className" ? "class" : key;
|
|
3851
|
+
if (value === true) {
|
|
3852
|
+
result += ` ${attrName}`;
|
|
3853
|
+
} else if (value === false || value === null || value === void 0) {
|
|
3854
|
+
continue;
|
|
3855
|
+
} else {
|
|
3856
|
+
result += ` ${attrName}="${escapeAttr2(String(value))}"`;
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
return result;
|
|
3860
|
+
}
|
|
3861
|
+
function renderChildSync(child, ctx) {
|
|
3862
|
+
if (typeof child === "string") return escapeText2(child);
|
|
3863
|
+
if (typeof child === "number") return escapeText2(String(child));
|
|
3864
|
+
if (child === null || child === void 0 || child === false) return "";
|
|
3865
|
+
if (typeof child === "object" && child !== null && "type" in child) {
|
|
3866
|
+
return renderNodeSync(child, ctx);
|
|
3867
|
+
}
|
|
3868
|
+
return "";
|
|
3869
|
+
}
|
|
3870
|
+
function renderChildrenSync(children, ctx) {
|
|
3871
|
+
if (!children || !Array.isArray(children) || children.length === 0) return "";
|
|
3872
|
+
let result = "";
|
|
3873
|
+
for (const child of children) result += renderChildSync(child, ctx);
|
|
3874
|
+
return result;
|
|
3875
|
+
}
|
|
3876
|
+
function renderNodeSync(node, ctx) {
|
|
3877
|
+
const { type, props } = node;
|
|
3878
|
+
if (typeof type === "function") {
|
|
3879
|
+
const result = executeComponentSync2(type, props, ctx);
|
|
3880
|
+
if (result instanceof Promise) {
|
|
3881
|
+
throwSSRDataMissing();
|
|
3882
|
+
}
|
|
3883
|
+
return renderNodeSync(result, ctx);
|
|
3884
|
+
}
|
|
3885
|
+
const typeStr = type;
|
|
3886
|
+
if (VOID_ELEMENTS2.has(typeStr)) {
|
|
3887
|
+
const attrs2 = renderAttrs2(props);
|
|
3888
|
+
return `<${typeStr}${attrs2} />`;
|
|
3889
|
+
}
|
|
3890
|
+
const attrs = renderAttrs2(props);
|
|
3891
|
+
const children = node.children;
|
|
3892
|
+
const childrenHtml = renderChildrenSync(children, ctx);
|
|
3893
|
+
return `<${typeStr}${attrs}>${childrenHtml}</${typeStr}>`;
|
|
3894
|
+
}
|
|
3895
|
+
function executeComponentSync2(component, props, ctx) {
|
|
3896
|
+
const originalRandom = Math.random;
|
|
3897
|
+
const originalDateNow = Date.now;
|
|
3898
|
+
try {
|
|
3899
|
+
if (process.env.NODE_ENV !== "production") {
|
|
3900
|
+
Math.random = () => {
|
|
3901
|
+
throw new Error(
|
|
3902
|
+
"SSR Strict Purity: Math.random is not allowed during synchronous SSR. Use the provided `ssr` context RNG instead."
|
|
3903
|
+
);
|
|
3904
|
+
};
|
|
3905
|
+
Date.now = () => {
|
|
3906
|
+
throw new Error(
|
|
3907
|
+
"SSR Strict Purity: Date.now is not allowed during synchronous SSR. Pass timestamps explicitly or use deterministic helpers."
|
|
3908
|
+
);
|
|
3909
|
+
};
|
|
3910
|
+
}
|
|
3911
|
+
const prev = getCurrentComponentInstance();
|
|
3912
|
+
const temp = createComponentInstance(
|
|
3913
|
+
"ssr-temp",
|
|
3914
|
+
component,
|
|
3915
|
+
props || {},
|
|
3916
|
+
null
|
|
3917
|
+
);
|
|
3918
|
+
temp.ssr = true;
|
|
3919
|
+
setCurrentComponentInstance(temp);
|
|
3920
|
+
try {
|
|
3921
|
+
return runWithSSRContext(ctx, () => {
|
|
3922
|
+
const result = component(props || {}, { ssr: ctx });
|
|
3923
|
+
if (result instanceof Promise) {
|
|
3924
|
+
throwSSRDataMissing();
|
|
3925
|
+
}
|
|
3926
|
+
return result;
|
|
3927
|
+
});
|
|
3928
|
+
} finally {
|
|
3929
|
+
setCurrentComponentInstance(prev);
|
|
3930
|
+
}
|
|
3931
|
+
} finally {
|
|
3932
|
+
Math.random = originalRandom;
|
|
3933
|
+
Date.now = originalDateNow;
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
function renderToStringSync(component, props, options) {
|
|
3937
|
+
const seed = options?.seed ?? 12345;
|
|
3938
|
+
const ctx = createRenderContext(seed);
|
|
3939
|
+
startRenderPhase(options?.data ?? null);
|
|
3940
|
+
try {
|
|
3941
|
+
const node = executeComponentSync2(component, props || {}, ctx);
|
|
3942
|
+
return renderNodeSync(node, ctx);
|
|
3943
|
+
} finally {
|
|
3944
|
+
stopRenderPhase();
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3947
|
+
function renderToStringSyncForUrl(opts) {
|
|
3948
|
+
const { url, routes: routes2, options } = opts;
|
|
3949
|
+
const {
|
|
3950
|
+
clearRoutes: clearRoutes2,
|
|
3951
|
+
route: route2,
|
|
3952
|
+
setServerLocation: setServerLocation2,
|
|
3953
|
+
lockRouteRegistration: lockRouteRegistration2,
|
|
3954
|
+
resolveRoute: resolveRoute2
|
|
3955
|
+
} = route_exports;
|
|
3956
|
+
clearRoutes2();
|
|
3957
|
+
for (const r of routes2) {
|
|
3958
|
+
route2(r.path, r.handler, r.namespace);
|
|
3959
|
+
}
|
|
3960
|
+
setServerLocation2(url);
|
|
3961
|
+
if (process.env.NODE_ENV === "production") lockRouteRegistration2();
|
|
3962
|
+
const resolved = resolveRoute2(url);
|
|
3963
|
+
if (!resolved)
|
|
3964
|
+
throw new Error(`renderToStringSync: no route found for url: ${url}`);
|
|
3965
|
+
const seed = options?.seed ?? 12345;
|
|
3966
|
+
const ctx = createRenderContext(seed);
|
|
3967
|
+
startRenderPhase(options?.data ?? null);
|
|
3968
|
+
try {
|
|
3969
|
+
const node = executeComponentSync2(
|
|
3970
|
+
resolved.handler,
|
|
3971
|
+
resolved.params || {},
|
|
3972
|
+
ctx
|
|
3973
|
+
);
|
|
3974
|
+
return renderNodeSync(node, ctx);
|
|
3975
|
+
} finally {
|
|
3976
|
+
stopRenderPhase();
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3979
|
+
function renderToString(arg) {
|
|
3980
|
+
if (typeof arg === "function") {
|
|
3981
|
+
return renderToStringSync(
|
|
3982
|
+
arg
|
|
3983
|
+
);
|
|
3984
|
+
}
|
|
3985
|
+
const opts = arg;
|
|
3986
|
+
const sink = new StringSink();
|
|
3987
|
+
renderToSinkInternal({ ...opts, sink });
|
|
3988
|
+
sink.end();
|
|
3989
|
+
return sink.toString();
|
|
3990
|
+
}
|
|
3991
|
+
function renderToStream(opts) {
|
|
3992
|
+
const sink = new StreamSink(opts.onChunk, opts.onComplete);
|
|
3993
|
+
renderToSinkInternal({ ...opts, sink });
|
|
3994
|
+
sink.end();
|
|
3995
|
+
}
|
|
3996
|
+
function renderToSinkInternal(opts) {
|
|
3997
|
+
const { url, routes: routes2, seed = 1, data, sink } = opts;
|
|
3998
|
+
const {
|
|
3999
|
+
clearRoutes: clearRoutes2,
|
|
4000
|
+
route: route2,
|
|
4001
|
+
setServerLocation: setServerLocation2,
|
|
4002
|
+
lockRouteRegistration: lockRouteRegistration2,
|
|
4003
|
+
resolveRoute: resolveRoute2
|
|
4004
|
+
} = route_exports;
|
|
4005
|
+
clearRoutes2();
|
|
4006
|
+
for (const r of routes2) route2(r.path, r.handler, r.namespace);
|
|
4007
|
+
setServerLocation2(url);
|
|
4008
|
+
if (process.env.NODE_ENV === "production") lockRouteRegistration2();
|
|
4009
|
+
const resolved = resolveRoute2(url);
|
|
4010
|
+
if (!resolved) throw new Error(`SSR: no route found for url: ${url}`);
|
|
4011
|
+
const ctx = {
|
|
4012
|
+
url,
|
|
4013
|
+
seed,
|
|
4014
|
+
data,
|
|
4015
|
+
params: resolved.params,
|
|
4016
|
+
signal: void 0
|
|
4017
|
+
};
|
|
4018
|
+
const node = resolved.handler(resolved.params);
|
|
4019
|
+
startRenderPhase(data || null);
|
|
4020
|
+
try {
|
|
4021
|
+
renderNodeToSink(node, sink, ctx);
|
|
4022
|
+
} finally {
|
|
4023
|
+
stopRenderPhase();
|
|
4024
|
+
}
|
|
4025
|
+
}
|
|
4026
|
+
var VOID_ELEMENTS2, escapeCache2;
|
|
4027
|
+
var init_ssr = __esm({
|
|
4028
|
+
"src/ssr/index.ts"() {
|
|
4029
|
+
"use strict";
|
|
4030
|
+
init_route();
|
|
4031
|
+
init_context2();
|
|
4032
|
+
init_component();
|
|
4033
|
+
init_context2();
|
|
4034
|
+
init_sink();
|
|
4035
|
+
init_render();
|
|
4036
|
+
init_data();
|
|
4037
|
+
VOID_ELEMENTS2 = /* @__PURE__ */ new Set([
|
|
4038
|
+
"area",
|
|
4039
|
+
"base",
|
|
4040
|
+
"br",
|
|
4041
|
+
"col",
|
|
4042
|
+
"embed",
|
|
4043
|
+
"hr",
|
|
4044
|
+
"img",
|
|
4045
|
+
"input",
|
|
4046
|
+
"link",
|
|
4047
|
+
"meta",
|
|
4048
|
+
"param",
|
|
4049
|
+
"source",
|
|
4050
|
+
"track",
|
|
4051
|
+
"wbr"
|
|
4052
|
+
]);
|
|
4053
|
+
escapeCache2 = /* @__PURE__ */ new Map();
|
|
4054
|
+
}
|
|
4055
|
+
});
|
|
4056
|
+
|
|
4057
|
+
// src/index.ts
|
|
4058
|
+
var index_exports = {};
|
|
4059
|
+
__export(index_exports, {
|
|
4060
|
+
Fragment: () => Fragment,
|
|
4061
|
+
Link: () => Link,
|
|
4062
|
+
cleanupApp: () => cleanupApp,
|
|
4063
|
+
clearRoutes: () => clearRoutes,
|
|
4064
|
+
collectResources: () => collectResources,
|
|
4065
|
+
createApp: () => createApp,
|
|
4066
|
+
createIsland: () => createIsland,
|
|
4067
|
+
createSPA: () => createSPA,
|
|
4068
|
+
defineContext: () => defineContext,
|
|
4069
|
+
getLoadedNamespaces: () => getLoadedNamespaces,
|
|
4070
|
+
getNamespaceRoutes: () => getNamespaceRoutes,
|
|
4071
|
+
getRoutes: () => getRoutes,
|
|
4072
|
+
getSignal: () => getSignal,
|
|
4073
|
+
hasApp: () => hasApp,
|
|
4074
|
+
hydrateSPA: () => hydrateSPA,
|
|
4075
|
+
jsx: () => jsx,
|
|
4076
|
+
jsxs: () => jsxs,
|
|
4077
|
+
layout: () => layout,
|
|
4078
|
+
navigate: () => navigate,
|
|
4079
|
+
readContext: () => readContext,
|
|
4080
|
+
renderToStream: () => renderToStream,
|
|
4081
|
+
renderToString: () => renderToString,
|
|
4082
|
+
renderToStringSync: () => renderToStringSync,
|
|
4083
|
+
renderToStringSyncForUrl: () => renderToStringSyncForUrl,
|
|
4084
|
+
resolveResources: () => resolveResources,
|
|
4085
|
+
resource: () => resource,
|
|
4086
|
+
route: () => route,
|
|
4087
|
+
scheduleEventHandler: () => scheduleEventHandler,
|
|
4088
|
+
setServerLocation: () => setServerLocation,
|
|
4089
|
+
state: () => state,
|
|
4090
|
+
unloadNamespace: () => unloadNamespace
|
|
4091
|
+
});
|
|
4092
|
+
module.exports = __toCommonJS(index_exports);
|
|
4093
|
+
|
|
4094
|
+
// src/runtime/state.ts
|
|
4095
|
+
init_scheduler();
|
|
4096
|
+
init_component();
|
|
4097
|
+
init_invariant();
|
|
4098
|
+
init_fastlane();
|
|
4099
|
+
function state(initialValue) {
|
|
4100
|
+
const instance = getCurrentInstance();
|
|
4101
|
+
if (!instance) {
|
|
4102
|
+
throw new Error(
|
|
4103
|
+
"state() can only be called during component render execution. Move state() calls to the top level of your component function."
|
|
4104
|
+
);
|
|
4105
|
+
}
|
|
4106
|
+
const index = getNextStateIndex();
|
|
4107
|
+
const stateValues = instance.stateValues;
|
|
4108
|
+
if (index < instance.stateIndexCheck) {
|
|
4109
|
+
throw new Error(
|
|
4110
|
+
`State index violation: state() call at index ${index}, but previously saw index ${instance.stateIndexCheck}. This happens when state() is called conditionally (inside if/for/etc). Move all state() calls to the top level of your component function, before any conditionals.`
|
|
4111
|
+
);
|
|
4112
|
+
}
|
|
4113
|
+
invariant(
|
|
4114
|
+
index >= instance.stateIndexCheck,
|
|
4115
|
+
"[State] State indices must increase monotonically"
|
|
4116
|
+
);
|
|
4117
|
+
instance.stateIndexCheck = index;
|
|
4118
|
+
if (instance.firstRenderComplete) {
|
|
4119
|
+
if (!instance.expectedStateIndices.includes(index)) {
|
|
4120
|
+
throw new Error(
|
|
4121
|
+
`Hook order violation: state() called at index ${index}, but this index was not in the first render's sequence [${instance.expectedStateIndices.join(", ")}]. This usually means state() is inside a conditional or loop. Move all state() calls to the top level of your component function.`
|
|
4122
|
+
);
|
|
4123
|
+
}
|
|
4124
|
+
} else {
|
|
4125
|
+
instance.expectedStateIndices.push(index);
|
|
4126
|
+
}
|
|
4127
|
+
if (stateValues[index]) {
|
|
4128
|
+
const existing = stateValues[index];
|
|
4129
|
+
if (existing._owner !== instance) {
|
|
4130
|
+
throw new Error(
|
|
4131
|
+
`State ownership violation: state() called at index ${index} is owned by a different component instance. State ownership is positional and immutable.`
|
|
4132
|
+
);
|
|
4133
|
+
}
|
|
4134
|
+
return existing;
|
|
4135
|
+
}
|
|
4136
|
+
const cell = createStateCell(initialValue, instance);
|
|
4137
|
+
stateValues[index] = cell;
|
|
4138
|
+
return cell;
|
|
4139
|
+
}
|
|
4140
|
+
function createStateCell(initialValue, instance) {
|
|
4141
|
+
let value = initialValue;
|
|
4142
|
+
const readers = /* @__PURE__ */ new Map();
|
|
4143
|
+
function read() {
|
|
4144
|
+
read._hasBeenRead = true;
|
|
4145
|
+
const inst = getCurrentInstance();
|
|
4146
|
+
if (inst && inst._currentRenderToken !== void 0) {
|
|
4147
|
+
if (!inst._pendingReadStates) inst._pendingReadStates = /* @__PURE__ */ new Set();
|
|
4148
|
+
inst._pendingReadStates.add(read);
|
|
4149
|
+
}
|
|
4150
|
+
return value;
|
|
4151
|
+
}
|
|
4152
|
+
read._readers = readers;
|
|
4153
|
+
read._owner = instance;
|
|
4154
|
+
read.set = (newValue) => {
|
|
4155
|
+
const currentInst = getCurrentInstance();
|
|
4156
|
+
if (currentInst !== null && process.env.NODE_ENV !== "production") {
|
|
4157
|
+
throw new Error(
|
|
4158
|
+
`[Askr] state.set() cannot be called during component render. State mutations during render break the actor model and cause infinite loops. Move state updates to event handlers or use conditional rendering instead.`
|
|
4159
|
+
);
|
|
4160
|
+
}
|
|
4161
|
+
if (currentInst !== null && process.env.NODE_ENV === "production") {
|
|
4162
|
+
return;
|
|
4163
|
+
}
|
|
4164
|
+
if (Object.is(value, newValue)) return;
|
|
4165
|
+
if (isBulkCommitActive2()) {
|
|
4166
|
+
value = newValue;
|
|
4167
|
+
return;
|
|
4168
|
+
}
|
|
4169
|
+
value = newValue;
|
|
4170
|
+
const readersMap = read._readers;
|
|
4171
|
+
if (readersMap) {
|
|
4172
|
+
for (const [subInst, token] of readersMap) {
|
|
4173
|
+
if (subInst.lastRenderToken !== token) continue;
|
|
4174
|
+
if (!subInst.hasPendingUpdate) {
|
|
4175
|
+
subInst.hasPendingUpdate = true;
|
|
4176
|
+
const subTask = subInst._pendingFlushTask;
|
|
4177
|
+
if (subTask) globalScheduler.enqueue(subTask);
|
|
4178
|
+
else
|
|
4179
|
+
globalScheduler.enqueue(() => {
|
|
4180
|
+
subInst.hasPendingUpdate = false;
|
|
4181
|
+
subInst.notifyUpdate?.();
|
|
4182
|
+
});
|
|
4183
|
+
}
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
const readersMapForOwner = readersMap;
|
|
4187
|
+
const ownerRecordedToken = readersMapForOwner?.get(instance);
|
|
4188
|
+
const ownerShouldEnqueue = (
|
|
4189
|
+
// Normal case: owner read this state in last committed render
|
|
4190
|
+
ownerRecordedToken !== void 0 && instance.lastRenderToken === ownerRecordedToken
|
|
4191
|
+
);
|
|
4192
|
+
if (ownerShouldEnqueue && !instance.hasPendingUpdate) {
|
|
4193
|
+
instance.hasPendingUpdate = true;
|
|
4194
|
+
const task = instance._pendingFlushTask;
|
|
4195
|
+
if (task) globalScheduler.enqueue(task);
|
|
4196
|
+
else
|
|
4197
|
+
globalScheduler.enqueue(() => {
|
|
4198
|
+
instance.hasPendingUpdate = false;
|
|
4199
|
+
instance.notifyUpdate?.();
|
|
4200
|
+
});
|
|
4201
|
+
}
|
|
4202
|
+
};
|
|
4203
|
+
return read;
|
|
4204
|
+
}
|
|
4205
|
+
|
|
4206
|
+
// src/index.ts
|
|
4207
|
+
init_component();
|
|
4208
|
+
init_scheduler();
|
|
4209
|
+
init_context();
|
|
4210
|
+
|
|
4211
|
+
// src/runtime/operations.ts
|
|
4212
|
+
init_component();
|
|
4213
|
+
init_context();
|
|
4214
|
+
|
|
4215
|
+
// src/runtime/resource_cell.ts
|
|
4216
|
+
init_context();
|
|
4217
|
+
init_logger();
|
|
4218
|
+
init_context2();
|
|
4219
|
+
var ResourceCell = class {
|
|
4220
|
+
constructor(fn, deps, resourceFrame) {
|
|
4221
|
+
this.value = null;
|
|
4222
|
+
this.pending = true;
|
|
4223
|
+
this.error = null;
|
|
4224
|
+
this.generation = 0;
|
|
4225
|
+
this.controller = null;
|
|
4226
|
+
this.deps = null;
|
|
4227
|
+
this.resourceFrame = null;
|
|
4228
|
+
this.subscribers = /* @__PURE__ */ new Set();
|
|
4229
|
+
this.fn = fn;
|
|
4230
|
+
this.deps = deps ? deps.slice() : null;
|
|
4231
|
+
this.resourceFrame = resourceFrame;
|
|
4232
|
+
this.snapshot = {
|
|
4233
|
+
value: null,
|
|
4234
|
+
pending: true,
|
|
4235
|
+
error: null,
|
|
4236
|
+
refresh: () => this.refresh()
|
|
4237
|
+
};
|
|
4238
|
+
}
|
|
4239
|
+
subscribe(cb) {
|
|
4240
|
+
this.subscribers.add(cb);
|
|
4241
|
+
return () => this.subscribers.delete(cb);
|
|
4242
|
+
}
|
|
4243
|
+
notifySubscribers() {
|
|
4244
|
+
this.snapshot.value = this.value;
|
|
4245
|
+
this.snapshot.pending = this.pending;
|
|
4246
|
+
this.snapshot.error = this.error;
|
|
4247
|
+
for (const cb of this.subscribers) cb();
|
|
4248
|
+
}
|
|
4249
|
+
start(ssr = false, notify = true) {
|
|
4250
|
+
const generation = this.generation;
|
|
4251
|
+
this.controller?.abort();
|
|
4252
|
+
const controller = new AbortController();
|
|
4253
|
+
this.controller = controller;
|
|
4254
|
+
this.pending = true;
|
|
4255
|
+
this.error = null;
|
|
4256
|
+
if (notify) this.notifySubscribers();
|
|
4257
|
+
let result;
|
|
4258
|
+
try {
|
|
4259
|
+
result = withAsyncResourceContext(
|
|
4260
|
+
this.resourceFrame,
|
|
4261
|
+
() => this.fn({ signal: controller.signal })
|
|
4262
|
+
);
|
|
4263
|
+
} catch (err) {
|
|
4264
|
+
this.pending = false;
|
|
4265
|
+
this.error = err;
|
|
4266
|
+
if (notify) this.notifySubscribers();
|
|
4267
|
+
return;
|
|
4268
|
+
}
|
|
4269
|
+
if (!(result instanceof Promise)) {
|
|
4270
|
+
this.value = result;
|
|
4271
|
+
this.pending = false;
|
|
4272
|
+
this.error = null;
|
|
4273
|
+
if (notify) this.notifySubscribers();
|
|
4274
|
+
return;
|
|
4275
|
+
}
|
|
4276
|
+
if (ssr) {
|
|
4277
|
+
throwSSRDataMissing();
|
|
4278
|
+
}
|
|
4279
|
+
result.then((val) => {
|
|
4280
|
+
if (this.generation !== generation) return;
|
|
4281
|
+
if (this.controller !== controller) return;
|
|
4282
|
+
this.value = val;
|
|
4283
|
+
this.pending = false;
|
|
4284
|
+
this.error = null;
|
|
4285
|
+
this.notifySubscribers();
|
|
4286
|
+
}).catch((err) => {
|
|
4287
|
+
if (this.generation !== generation) return;
|
|
4288
|
+
this.pending = false;
|
|
4289
|
+
this.error = err;
|
|
4290
|
+
try {
|
|
4291
|
+
logger.error("[Askr] Async resource error:", err);
|
|
4292
|
+
} catch {
|
|
4293
|
+
}
|
|
4294
|
+
this.notifySubscribers();
|
|
4295
|
+
});
|
|
4296
|
+
}
|
|
4297
|
+
refresh() {
|
|
4298
|
+
this.generation++;
|
|
4299
|
+
this.controller?.abort();
|
|
4300
|
+
this.start();
|
|
4301
|
+
}
|
|
4302
|
+
abort() {
|
|
4303
|
+
this.controller?.abort();
|
|
4304
|
+
}
|
|
4305
|
+
};
|
|
4306
|
+
|
|
4307
|
+
// src/runtime/operations.ts
|
|
4308
|
+
init_context2();
|
|
4309
|
+
init_data();
|
|
4310
|
+
function resource(fn, deps = []) {
|
|
4311
|
+
const instance = getCurrentComponentInstance();
|
|
4312
|
+
const inst = instance;
|
|
4313
|
+
if (!instance) {
|
|
4314
|
+
const renderData2 = getCurrentRenderData();
|
|
4315
|
+
if (renderData2) {
|
|
4316
|
+
const key = getNextKey();
|
|
4317
|
+
if (!(key in renderData2)) {
|
|
4318
|
+
throwSSRDataMissing();
|
|
4319
|
+
}
|
|
4320
|
+
const val = renderData2[key];
|
|
4321
|
+
return {
|
|
4322
|
+
value: val,
|
|
4323
|
+
pending: false,
|
|
4324
|
+
error: null,
|
|
4325
|
+
refresh: () => {
|
|
4326
|
+
}
|
|
4327
|
+
};
|
|
4328
|
+
}
|
|
4329
|
+
const ssrCtx = getCurrentSSRContext();
|
|
4330
|
+
if (ssrCtx) {
|
|
4331
|
+
throwSSRDataMissing();
|
|
4332
|
+
}
|
|
4333
|
+
return {
|
|
4334
|
+
value: null,
|
|
4335
|
+
pending: true,
|
|
4336
|
+
error: null,
|
|
4337
|
+
refresh: () => {
|
|
4338
|
+
}
|
|
4339
|
+
};
|
|
4340
|
+
}
|
|
4341
|
+
const renderData = getCurrentRenderData();
|
|
4342
|
+
if (renderData) {
|
|
4343
|
+
const key = getNextKey();
|
|
4344
|
+
if (!(key in renderData)) {
|
|
4345
|
+
throwSSRDataMissing();
|
|
4346
|
+
}
|
|
4347
|
+
const val = renderData[key];
|
|
4348
|
+
const holder2 = state({
|
|
4349
|
+
cell: void 0,
|
|
4350
|
+
snapshot: {
|
|
4351
|
+
value: val,
|
|
4352
|
+
pending: false,
|
|
4353
|
+
error: null,
|
|
4354
|
+
refresh: () => {
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
});
|
|
4358
|
+
const h2 = holder2();
|
|
4359
|
+
h2.snapshot.value = val;
|
|
4360
|
+
h2.snapshot.pending = false;
|
|
4361
|
+
h2.snapshot.error = null;
|
|
4362
|
+
holder2.set(h2);
|
|
4363
|
+
return h2.snapshot;
|
|
4364
|
+
}
|
|
4365
|
+
const holder = state({
|
|
4366
|
+
cell: void 0,
|
|
4367
|
+
snapshot: {
|
|
4368
|
+
value: null,
|
|
4369
|
+
pending: true,
|
|
4370
|
+
error: null,
|
|
4371
|
+
refresh: () => {
|
|
4372
|
+
}
|
|
4373
|
+
}
|
|
4374
|
+
});
|
|
4375
|
+
const h = holder();
|
|
4376
|
+
if (!h.cell) {
|
|
4377
|
+
const frame = getCurrentContextFrame();
|
|
4378
|
+
const cell2 = new ResourceCell(fn, deps, frame);
|
|
4379
|
+
h.cell = cell2;
|
|
4380
|
+
h.snapshot = cell2.snapshot;
|
|
4381
|
+
const unsubscribe = cell2.subscribe(() => {
|
|
4382
|
+
const cur = holder();
|
|
4383
|
+
cur.snapshot.value = cell2.snapshot.value;
|
|
4384
|
+
cur.snapshot.pending = cell2.snapshot.pending;
|
|
4385
|
+
cur.snapshot.error = cell2.snapshot.error;
|
|
4386
|
+
holder.set(cur);
|
|
4387
|
+
try {
|
|
4388
|
+
inst._enqueueRun?.();
|
|
4389
|
+
} catch {
|
|
4390
|
+
}
|
|
4391
|
+
});
|
|
4392
|
+
inst.cleanupFns.push(() => {
|
|
4393
|
+
unsubscribe();
|
|
4394
|
+
cell2.abort();
|
|
4395
|
+
});
|
|
4396
|
+
try {
|
|
4397
|
+
cell2.start(inst.ssr ?? false, false);
|
|
4398
|
+
if (!cell2.pending) {
|
|
4399
|
+
const cur = holder();
|
|
4400
|
+
cur.snapshot.value = cell2.value;
|
|
4401
|
+
cur.snapshot.pending = cell2.pending;
|
|
4402
|
+
cur.snapshot.error = cell2.error;
|
|
4403
|
+
}
|
|
4404
|
+
} catch (err) {
|
|
4405
|
+
if (err instanceof SSRDataMissingError) throw err;
|
|
4406
|
+
cell2.error = err;
|
|
4407
|
+
cell2.pending = false;
|
|
4408
|
+
const cur = holder();
|
|
4409
|
+
cur.snapshot.value = cell2.value;
|
|
4410
|
+
cur.snapshot.pending = cell2.pending;
|
|
4411
|
+
cur.snapshot.error = cell2.error;
|
|
4412
|
+
}
|
|
4413
|
+
}
|
|
4414
|
+
const cell = h.cell;
|
|
4415
|
+
const depsChanged = !cell.deps || cell.deps.length !== deps.length || cell.deps.some((d, i) => d !== deps[i]);
|
|
4416
|
+
if (depsChanged) {
|
|
4417
|
+
cell.deps = deps.slice();
|
|
4418
|
+
cell.generation++;
|
|
4419
|
+
cell.pending = true;
|
|
4420
|
+
cell.error = null;
|
|
4421
|
+
try {
|
|
4422
|
+
cell.start(inst.ssr ?? false, false);
|
|
4423
|
+
if (!cell.pending) {
|
|
4424
|
+
const cur = holder();
|
|
4425
|
+
cur.snapshot.value = cell.value;
|
|
4426
|
+
cur.snapshot.pending = cell.pending;
|
|
4427
|
+
cur.snapshot.error = cell.error;
|
|
4428
|
+
}
|
|
4429
|
+
} catch (err) {
|
|
4430
|
+
if (err instanceof SSRDataMissingError) throw err;
|
|
4431
|
+
cell.error = err;
|
|
4432
|
+
cell.pending = false;
|
|
4433
|
+
const cur = holder();
|
|
4434
|
+
cur.snapshot.value = cell.value;
|
|
4435
|
+
cur.snapshot.pending = cell.pending;
|
|
4436
|
+
cur.snapshot.error = cell.error;
|
|
4437
|
+
}
|
|
4438
|
+
}
|
|
4439
|
+
return h.snapshot;
|
|
4440
|
+
}
|
|
4441
|
+
|
|
4442
|
+
// src/app/createApp.ts
|
|
4443
|
+
init_component();
|
|
4444
|
+
init_scheduler();
|
|
4445
|
+
init_logger();
|
|
4446
|
+
init_dom();
|
|
4447
|
+
init_navigate();
|
|
4448
|
+
var componentIdCounter = 0;
|
|
4449
|
+
var instancesByRoot = /* @__PURE__ */ new WeakMap();
|
|
4450
|
+
var CLEANUP_SYMBOL = /* @__PURE__ */ Symbol.for("__tempoCleanup__");
|
|
4451
|
+
function createApp(config) {
|
|
4452
|
+
if (!config || typeof config !== "object") {
|
|
4453
|
+
throw new Error("createApp requires a config object");
|
|
4454
|
+
}
|
|
4455
|
+
if ("routes" in config) {
|
|
4456
|
+
throw new Error(
|
|
4457
|
+
"The `createApp` API is removed. Use `createSPA({ root, routes })` for routed apps, or `hydrateSPA({ root, routes })` for SSR hydration."
|
|
4458
|
+
);
|
|
4459
|
+
}
|
|
4460
|
+
const appCfg = config;
|
|
4461
|
+
createIsland({
|
|
4462
|
+
root: appCfg.root,
|
|
4463
|
+
component: appCfg.component
|
|
4464
|
+
});
|
|
4465
|
+
}
|
|
4466
|
+
function attachCleanupForRoot(rootElement, instance) {
|
|
4467
|
+
rootElement[CLEANUP_SYMBOL] = () => {
|
|
4468
|
+
removeAllListeners(rootElement);
|
|
4469
|
+
cleanupComponent(instance);
|
|
4470
|
+
};
|
|
4471
|
+
try {
|
|
4472
|
+
const descriptor = Object.getOwnPropertyDescriptor(rootElement, "innerHTML") || Object.getOwnPropertyDescriptor(
|
|
4473
|
+
Object.getPrototypeOf(rootElement),
|
|
4474
|
+
"innerHTML"
|
|
4475
|
+
) || Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML");
|
|
4476
|
+
if (descriptor && (descriptor.get || descriptor.set)) {
|
|
4477
|
+
Object.defineProperty(rootElement, "innerHTML", {
|
|
4478
|
+
get: descriptor.get ? function() {
|
|
4479
|
+
return descriptor.get.call(this);
|
|
4480
|
+
} : void 0,
|
|
4481
|
+
set: function(value) {
|
|
4482
|
+
if (value === "" && instancesByRoot.get(this) === instance) {
|
|
4483
|
+
removeAllListeners(rootElement);
|
|
4484
|
+
cleanupComponent(instance);
|
|
4485
|
+
}
|
|
4486
|
+
if (descriptor.set) {
|
|
4487
|
+
return descriptor.set.call(this, value);
|
|
4488
|
+
}
|
|
4489
|
+
},
|
|
4490
|
+
configurable: true
|
|
4491
|
+
});
|
|
4492
|
+
}
|
|
4493
|
+
} catch {
|
|
4494
|
+
}
|
|
4495
|
+
}
|
|
4496
|
+
function mountOrUpdate(rootElement, componentFn) {
|
|
4497
|
+
const existingCleanup = rootElement[CLEANUP_SYMBOL];
|
|
4498
|
+
if (existingCleanup) existingCleanup();
|
|
4499
|
+
let instance = instancesByRoot.get(rootElement);
|
|
4500
|
+
if (instance) {
|
|
4501
|
+
removeAllListeners(rootElement);
|
|
4502
|
+
cleanupComponent(instance);
|
|
4503
|
+
instance.fn = componentFn;
|
|
4504
|
+
instance.evaluationGeneration++;
|
|
4505
|
+
instance.mounted = false;
|
|
4506
|
+
instance.expectedStateIndices = [];
|
|
4507
|
+
instance.firstRenderComplete = false;
|
|
4508
|
+
instance.isRoot = true;
|
|
4509
|
+
} else {
|
|
4510
|
+
const componentId = String(++componentIdCounter);
|
|
4511
|
+
instance = createComponentInstance(
|
|
4512
|
+
componentId,
|
|
4513
|
+
componentFn,
|
|
4514
|
+
{},
|
|
4515
|
+
rootElement
|
|
4516
|
+
);
|
|
4517
|
+
instancesByRoot.set(rootElement, instance);
|
|
4518
|
+
instance.isRoot = true;
|
|
4519
|
+
}
|
|
4520
|
+
attachCleanupForRoot(rootElement, instance);
|
|
4521
|
+
mountComponent(instance);
|
|
4522
|
+
globalScheduler.flush();
|
|
4523
|
+
}
|
|
4524
|
+
function createIsland(config) {
|
|
4525
|
+
if (!config || typeof config !== "object") {
|
|
4526
|
+
throw new Error("createIsland requires a config object");
|
|
4527
|
+
}
|
|
4528
|
+
if (typeof config.component !== "function") {
|
|
4529
|
+
throw new Error("createIsland: component must be a function");
|
|
4530
|
+
}
|
|
4531
|
+
const rootElement = typeof config.root === "string" ? document.getElementById(config.root) : config.root;
|
|
4532
|
+
if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
|
|
4533
|
+
if ("routes" in config) {
|
|
4534
|
+
throw new Error(
|
|
4535
|
+
"createIsland does not accept routes; use createSPA for routed apps"
|
|
4536
|
+
);
|
|
4537
|
+
}
|
|
4538
|
+
mountOrUpdate(rootElement, config.component);
|
|
4539
|
+
}
|
|
4540
|
+
async function createSPA(config) {
|
|
4541
|
+
if (!config || typeof config !== "object") {
|
|
4542
|
+
throw new Error("createSPA requires a config object");
|
|
4543
|
+
}
|
|
4544
|
+
if (!Array.isArray(config.routes) || config.routes.length === 0) {
|
|
4545
|
+
throw new Error(
|
|
4546
|
+
"createSPA requires a route table. If you are enhancing existing HTML, use createIsland instead."
|
|
4547
|
+
);
|
|
4548
|
+
}
|
|
4549
|
+
const rootElement = typeof config.root === "string" ? document.getElementById(config.root) : config.root;
|
|
4550
|
+
if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
|
|
4551
|
+
const { clearRoutes: clearRoutes2, route: route2, lockRouteRegistration: lockRouteRegistration2, resolveRoute: resolveRoute2 } = await Promise.resolve().then(() => (init_route(), route_exports));
|
|
4552
|
+
clearRoutes2();
|
|
4553
|
+
for (const r of config.routes) {
|
|
4554
|
+
route2(r.path, r.handler, r.namespace);
|
|
4555
|
+
}
|
|
4556
|
+
if (process.env.NODE_ENV === "production") lockRouteRegistration2();
|
|
4557
|
+
const path = typeof window !== "undefined" ? window.location.pathname : "/";
|
|
4558
|
+
const resolved = resolveRoute2(path);
|
|
4559
|
+
if (!resolved) {
|
|
4560
|
+
if (process.env.NODE_ENV !== "production") {
|
|
4561
|
+
logger.warn(
|
|
4562
|
+
`createSPA: no route found for current path (${path}). Mounting empty placeholder; navigation will activate routes when requested.`
|
|
4563
|
+
);
|
|
4564
|
+
}
|
|
4565
|
+
mountOrUpdate(rootElement, () => ({ type: "div", children: [] }));
|
|
4566
|
+
const instance2 = instancesByRoot.get(rootElement);
|
|
4567
|
+
if (!instance2) throw new Error("Internal error: app instance missing");
|
|
4568
|
+
registerAppInstance(instance2, path);
|
|
4569
|
+
initializeNavigation();
|
|
4570
|
+
return;
|
|
4571
|
+
}
|
|
4572
|
+
mountOrUpdate(rootElement, resolved.handler);
|
|
4573
|
+
const instance = instancesByRoot.get(rootElement);
|
|
4574
|
+
if (!instance) throw new Error("Internal error: app instance missing");
|
|
4575
|
+
registerAppInstance(instance, path);
|
|
4576
|
+
initializeNavigation();
|
|
4577
|
+
}
|
|
4578
|
+
async function hydrateSPA(config) {
|
|
4579
|
+
if (!config || typeof config !== "object") {
|
|
4580
|
+
throw new Error("hydrateSPA requires a config object");
|
|
4581
|
+
}
|
|
4582
|
+
if (!Array.isArray(config.routes) || config.routes.length === 0) {
|
|
4583
|
+
throw new Error(
|
|
4584
|
+
"hydrateSPA requires a route table. If you are enhancing existing HTML, use createIsland instead."
|
|
4585
|
+
);
|
|
4586
|
+
}
|
|
4587
|
+
const rootElement = typeof config.root === "string" ? document.getElementById(config.root) : config.root;
|
|
4588
|
+
if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
|
|
4589
|
+
const serverHTML = rootElement.innerHTML;
|
|
4590
|
+
const {
|
|
4591
|
+
clearRoutes: clearRoutes2,
|
|
4592
|
+
route: route2,
|
|
4593
|
+
setServerLocation: setServerLocation2,
|
|
4594
|
+
lockRouteRegistration: lockRouteRegistration2,
|
|
4595
|
+
resolveRoute: resolveRoute2
|
|
4596
|
+
} = await Promise.resolve().then(() => (init_route(), route_exports));
|
|
4597
|
+
clearRoutes2();
|
|
4598
|
+
for (const r of config.routes) {
|
|
4599
|
+
route2(r.path, r.handler, r.namespace);
|
|
4600
|
+
}
|
|
4601
|
+
const path = typeof window !== "undefined" ? window.location.pathname : "/";
|
|
4602
|
+
setServerLocation2(path);
|
|
4603
|
+
if (process.env.NODE_ENV === "production") lockRouteRegistration2();
|
|
4604
|
+
const resolved = resolveRoute2(path);
|
|
4605
|
+
if (!resolved) {
|
|
4606
|
+
throw new Error(`hydrateSPA: no route found for current path (${path}).`);
|
|
4607
|
+
}
|
|
4608
|
+
const { renderToStringSync: renderToStringSync2 } = await Promise.resolve().then(() => (init_ssr(), ssr_exports));
|
|
4609
|
+
const expectedHTML = renderToStringSync2(() => {
|
|
4610
|
+
const out = resolved.handler(resolved.params);
|
|
4611
|
+
return out ?? {
|
|
4612
|
+
type: "div",
|
|
4613
|
+
children: []
|
|
4614
|
+
};
|
|
4615
|
+
});
|
|
4616
|
+
const serverContainer = document.createElement("div");
|
|
4617
|
+
serverContainer.innerHTML = serverHTML;
|
|
4618
|
+
const expectedContainer = document.createElement("div");
|
|
4619
|
+
expectedContainer.innerHTML = expectedHTML;
|
|
4620
|
+
if (!serverContainer.isEqualNode(expectedContainer)) {
|
|
4621
|
+
throw new Error(
|
|
4622
|
+
"[Askr] Hydration mismatch detected. Server HTML does not match expected server-render output."
|
|
4623
|
+
);
|
|
4624
|
+
}
|
|
4625
|
+
mountOrUpdate(rootElement, resolved.handler);
|
|
4626
|
+
const { registerAppInstance: registerAppInstance2, initializeNavigation: initializeNavigation2 } = await Promise.resolve().then(() => (init_navigate(), navigate_exports));
|
|
4627
|
+
const instance = instancesByRoot.get(rootElement);
|
|
4628
|
+
if (!instance) throw new Error("Internal error: app instance missing");
|
|
4629
|
+
registerAppInstance2(instance, path);
|
|
4630
|
+
initializeNavigation2();
|
|
4631
|
+
}
|
|
4632
|
+
function cleanupApp(root) {
|
|
4633
|
+
const rootElement = typeof root === "string" ? document.getElementById(root) : root;
|
|
4634
|
+
if (!rootElement) return;
|
|
4635
|
+
const cleanupFn = rootElement[CLEANUP_SYMBOL];
|
|
4636
|
+
if (typeof cleanupFn === "function") {
|
|
4637
|
+
cleanupFn();
|
|
4638
|
+
}
|
|
4639
|
+
instancesByRoot.delete(rootElement);
|
|
4640
|
+
}
|
|
4641
|
+
function hasApp(root) {
|
|
4642
|
+
const rootElement = typeof root === "string" ? document.getElementById(root) : root;
|
|
4643
|
+
if (!rootElement) return false;
|
|
4644
|
+
return instancesByRoot.has(rootElement);
|
|
4645
|
+
}
|
|
4646
|
+
|
|
4647
|
+
// src/index.ts
|
|
4648
|
+
init_route();
|
|
4649
|
+
|
|
4650
|
+
// src/router/layouts.ts
|
|
4651
|
+
function layout(Layout) {
|
|
4652
|
+
return (children) => Layout({ children });
|
|
4653
|
+
}
|
|
4654
|
+
|
|
4655
|
+
// src/index.ts
|
|
4656
|
+
init_route();
|
|
4657
|
+
init_navigate();
|
|
4658
|
+
|
|
4659
|
+
// src/components/Link.tsx
|
|
4660
|
+
init_navigate();
|
|
4661
|
+
function Link({ href, children }) {
|
|
4662
|
+
return {
|
|
4663
|
+
type: "a",
|
|
4664
|
+
props: {
|
|
4665
|
+
href,
|
|
4666
|
+
children,
|
|
4667
|
+
onClick: (e) => {
|
|
4668
|
+
const event = e;
|
|
4669
|
+
const button = event.button ?? 0;
|
|
4670
|
+
if (button !== 0 || // not left-click
|
|
4671
|
+
event.ctrlKey || // Ctrl/Cmd+click
|
|
4672
|
+
event.metaKey || // Cmd on Mac
|
|
4673
|
+
event.shiftKey || // Shift+click
|
|
4674
|
+
event.altKey) {
|
|
4675
|
+
return;
|
|
4676
|
+
}
|
|
4677
|
+
event.preventDefault();
|
|
4678
|
+
navigate(href);
|
|
4679
|
+
}
|
|
4680
|
+
}
|
|
4681
|
+
};
|
|
4682
|
+
}
|
|
4683
|
+
|
|
4684
|
+
// src/index.ts
|
|
4685
|
+
init_ssr();
|
|
4686
|
+
init_jsx_runtime();
|
|
4687
|
+
init_route();
|
|
4688
|
+
init_navigate();
|
|
4689
|
+
if (typeof globalThis !== "undefined") {
|
|
4690
|
+
const g = globalThis;
|
|
4691
|
+
if (!g.createApp) g.createApp = createApp;
|
|
4692
|
+
if (!g.createIsland) g.createIsland = createIsland;
|
|
4693
|
+
if (!g.createSPA) g.createSPA = createSPA;
|
|
4694
|
+
if (!g.hydrateSPA) g.hydrateSPA = hydrateSPA;
|
|
4695
|
+
if (!g.route) g.route = route;
|
|
4696
|
+
if (!g.getRoutes) g.getRoutes = getRoutes;
|
|
4697
|
+
if (!g.navigate) g.navigate = navigate;
|
|
4698
|
+
}
|
|
4699
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
4700
|
+
0 && (module.exports = {
|
|
4701
|
+
Fragment,
|
|
4702
|
+
Link,
|
|
4703
|
+
cleanupApp,
|
|
4704
|
+
clearRoutes,
|
|
4705
|
+
collectResources,
|
|
4706
|
+
createApp,
|
|
4707
|
+
createIsland,
|
|
4708
|
+
createSPA,
|
|
4709
|
+
defineContext,
|
|
4710
|
+
getLoadedNamespaces,
|
|
4711
|
+
getNamespaceRoutes,
|
|
4712
|
+
getRoutes,
|
|
4713
|
+
getSignal,
|
|
4714
|
+
hasApp,
|
|
4715
|
+
hydrateSPA,
|
|
4716
|
+
jsx,
|
|
4717
|
+
jsxs,
|
|
4718
|
+
layout,
|
|
4719
|
+
navigate,
|
|
4720
|
+
readContext,
|
|
4721
|
+
renderToStream,
|
|
4722
|
+
renderToString,
|
|
4723
|
+
renderToStringSync,
|
|
4724
|
+
renderToStringSyncForUrl,
|
|
4725
|
+
resolveResources,
|
|
4726
|
+
resource,
|
|
4727
|
+
route,
|
|
4728
|
+
scheduleEventHandler,
|
|
4729
|
+
setServerLocation,
|
|
4730
|
+
state,
|
|
4731
|
+
unloadNamespace
|
|
4732
|
+
});
|
|
4733
|
+
//# sourceMappingURL=index.cjs.map
|