@calmdown/pyxis 1.0.0-rc.2 → 1.0.0-rc.4

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/dist/core-dev.js CHANGED
@@ -1,3 +1,17 @@
1
+ //#region src/support/common.ts
2
+ function invoke(callback) {
3
+ return callback.fn(callback.a0, callback.a1, callback.a2);
4
+ }
5
+ function noop() {}
6
+ function rethrow(errors) {
7
+ if (errors) {
8
+ if (errors.length > 1) throw new AggregateError(errors);
9
+ if (errors.length === 1) throw errors[0];
10
+ }
11
+ }
12
+ const UNIQUE_VALUE = Object.create(null);
13
+
14
+ //#endregion
1
15
  //#region src/dev/ComponentRegistry.ts
2
16
  var ComponentRegistry = class {
3
17
  components = /* @__PURE__ */ new Map();
@@ -5,19 +19,29 @@ var ComponentRegistry = class {
5
19
  on(id, fn) {
6
20
  const entry = this.components.get(id);
7
21
  if (!entry) throw new Error(`no component was registered under "${id}"`);
22
+ fn(entry.component);
8
23
  const listener = {
9
24
  fn,
10
- entry
25
+ entry,
26
+ isActive: true
11
27
  };
12
28
  if (entry.lt) {
13
29
  entry.lt.ln = listener;
14
30
  listener.lp = entry.lt;
15
31
  } else entry.lh = listener;
16
32
  entry.lt = listener;
17
- fn(entry.component);
18
33
  return listener;
19
34
  }
20
35
  off(listener) {
36
+ if (!listener.isActive) return;
37
+ listener.isActive = false;
38
+ if (listener.entry.isDispatching) {
39
+ listener.entry.needsCleanup = true;
40
+ return;
41
+ }
42
+ this.unlink(listener);
43
+ }
44
+ unlink(listener) {
21
45
  const { entry } = listener;
22
46
  if (listener.lp) listener.lp.ln = listener.ln;
23
47
  else if (entry.lh === listener) entry.lh = listener.ln;
@@ -35,7 +59,9 @@ var ComponentRegistry = class {
35
59
  if (!entry) {
36
60
  this.components.set(id, {
37
61
  component,
38
- dirty: false
62
+ dirty: false,
63
+ isDispatching: false,
64
+ needsCleanup: false
39
65
  });
40
66
  return;
41
67
  }
@@ -47,18 +73,40 @@ var ComponentRegistry = class {
47
73
  }
48
74
  }
49
75
  update = () => {
76
+ let errors;
50
77
  this.isPendingUpdate = false;
51
78
  this.components.forEach((entry) => {
52
79
  if (!entry.dirty) return;
80
+ entry.dirty = false;
81
+ entry.isDispatching = true;
82
+ const component = entry.component;
83
+ const last = entry.lt;
53
84
  let current = entry.lh;
54
85
  let next;
55
- while (current) {
56
- next = current.ln;
57
- current.fn(entry.component);
58
- current = next;
86
+ try {
87
+ while (current) {
88
+ if (current.isActive) try {
89
+ current.fn(component);
90
+ } catch (ex) {
91
+ (errors ??= []).push(ex);
92
+ }
93
+ if (current === last) break;
94
+ current = current.ln;
95
+ }
96
+ } finally {
97
+ entry.isDispatching = false;
98
+ if (entry.needsCleanup) {
99
+ current = entry.lh;
100
+ while (current) {
101
+ next = current.ln;
102
+ if (!current.isActive) this.unlink(current);
103
+ current = next;
104
+ }
105
+ entry.needsCleanup = false;
106
+ }
59
107
  }
60
- entry.dirty = false;
61
108
  });
109
+ rethrow(errors);
62
110
  };
63
111
  };
64
112
 
@@ -81,87 +129,30 @@ var StateRegistry = class {
81
129
  };
82
130
 
83
131
  //#endregion
84
- //#region src/dev/PyxisHotReload.ts
85
- globalThis.__PYXIS_HMR__ = {
86
- component: new ComponentRegistry(),
87
- state: new StateRegistry()
88
- };
89
-
90
- //#endregion
91
- //#region src/support/common.ts
92
- function invoke(callback) {
93
- return callback.$fn(callback.$a0, callback.$a1);
94
- }
132
+ //#region src/symbols.ts
133
+ const S_ATOM = Symbol.for("pyxis:atom");
134
+ const S_COMPONENT = Symbol.for("pyxis:component");
135
+ const S_TAG_NAME = Symbol.for("pyxis:tagName");
136
+ const S_DEV_INFO = Symbol.for("pyxis:devInfo");
137
+ const S_RUNTIME = Symbol.for("pyxis:runtime");
95
138
 
96
139
  //#endregion
97
- //#region src/data/Lifecycle.ts
98
- /**
99
- * Registers a callback to run just after the current Component has mounted.
100
- *
101
- * If a teardown callback is returned, it will be run just before the Component unmounts (equivalent
102
- * to adding a separate `unmounted` block).
103
- * @see {@link unmounted}
104
- */
105
- function mounted(block, lifecycle = getLifecycle()) {
106
- onMounted(lifecycle, {
107
- $fn: invokeMountedCallback,
108
- $a0: lifecycle,
109
- $a1: block
110
- });
111
- }
112
- /** @internal */
113
- function onMounted(lifecycle, callback) {
114
- (lifecycle.$onMount ??= []).push(callback);
115
- }
116
- function invokeMountedCallback(lifecycle, block) {
117
- const dispose = block();
118
- if (dispose) onUnmounted(lifecycle, { $fn: dispose });
119
- }
120
- /**
121
- * Registers a callback to run once the current Component is just about to unmount.
122
- * @see {@link mounted}
123
- */
124
- function unmounted(block, lifecycle = getLifecycle()) {
125
- onUnmounted(lifecycle, { $fn: block });
126
- }
127
- /** @internal */
128
- function onUnmounted(lifecycle, callback) {
129
- (lifecycle.$onUnmount ??= []).push(callback);
130
- }
131
- let $currentLifecycle = null;
132
- /**
133
- * Gets the Lifecycle of the calling component.
134
- */
135
- function getLifecycle() {
136
- if (!$currentLifecycle) throw new Error("Cannot get current lifecycle. Are you creating an Atom outside of a Component?");
137
- return $currentLifecycle;
138
- }
139
- /** @internal */
140
- function setLifecycle(lifecycle) {
141
- const previous = $currentLifecycle;
142
- $currentLifecycle = lifecycle;
143
- return previous;
144
- }
145
- function withLifecycle(lifecycle, block, arg) {
146
- const previousLifecycle = $currentLifecycle;
147
- $currentLifecycle = lifecycle;
148
- try {
149
- return block(arg);
150
- } finally {
151
- $currentLifecycle = previousLifecycle;
152
- }
153
- }
140
+ //#region src/Runtime.ts
141
+ const runtime = globalThis[S_RUNTIME] ??= {
142
+ x: {},
143
+ $extIndex: 0,
144
+ $currentLifecycle: null,
145
+ $currentEffect: null,
146
+ $currentContext: null,
147
+ $currentContextNew: false
148
+ };
154
149
 
155
150
  //#endregion
156
151
  //#region src/data/Dependency.ts
157
- function bind(lifecycle, target, block) {
158
- link(lifecycle, target, { $fn: block });
159
- block();
160
- }
161
- /**
162
- * Links a Dependency to an Atom and Lifecycle.
163
- * @internal
164
- */
152
+ function bind(lifecycle, target, dependency) {
153
+ link(lifecycle, target, dependency);
154
+ return invoke(dependency);
155
+ }
165
156
  function link(lifecycle, target, dep) {
166
157
  if (target.$dt) {
167
158
  target.$dt.$an = dep;
@@ -175,14 +166,18 @@ function link(lifecycle, target, dep) {
175
166
  } else lifecycle.$dh = dep;
176
167
  dep.$lifecycle = lifecycle;
177
168
  lifecycle.$dt = dep;
169
+ if (target.$xa && !target.$xn) target.$xn = dep;
170
+ let update;
171
+ do {
172
+ update = target.$notify;
173
+ if (update && update.$se === update.$lifecycle.$scheduler.$epoch) target.$force = true;
174
+ target = target.$upstream;
175
+ } while (target);
178
176
  }
179
- /**
180
- * Unlinks a Dependency from the Atom and Lifecycle it has been linked to.
181
- * @internal
182
- */
183
177
  function unlink(dep) {
184
178
  const target = dep.$target;
185
179
  if (!target) return;
180
+ if (target.$xa && target.$xn === dep) target.$xn = dep.$an;
186
181
  if (dep.$ap) dep.$ap.$an = dep.$an;
187
182
  else if (target.$dh === dep) target.$dh = dep.$an;
188
183
  if (dep.$an) dep.$an.$ap = dep.$ap;
@@ -199,20 +194,17 @@ function unlink(dep) {
199
194
  dep.$lp = null;
200
195
  dep.$ln = null;
201
196
  }
202
- /**
203
- * Unlinks all Dependencies managed by the given Lifecycle.
204
- * @internal
205
- */
206
- function unlinkAll(lifecycle) {
197
+ function teardown(lifecycle) {
207
198
  let dep = lifecycle.$dh;
208
- let atom;
199
+ let target;
209
200
  let next;
210
201
  while (dep) {
211
- atom = dep.$target;
202
+ target = dep.$target;
203
+ if (target.$xa && target.$xn === dep) target.$xn = dep.$an;
212
204
  if (dep.$ap) dep.$ap.$an = dep.$an;
213
- else if (atom.$dh === dep) atom.$dh = dep.$an;
205
+ else if (target.$dh === dep) target.$dh = dep.$an;
214
206
  if (dep.$an) dep.$an.$ap = dep.$ap;
215
- else if (atom.$dt === dep) atom.$dt = dep.$ap;
207
+ else if (target.$dt === dep) target.$dt = dep.$ap;
216
208
  next = dep.$ln;
217
209
  dep.$lifecycle = null;
218
210
  dep.$target = null;
@@ -224,17 +216,141 @@ function unlinkAll(lifecycle) {
224
216
  }
225
217
  lifecycle.$dh = null;
226
218
  lifecycle.$dt = null;
219
+ lifecycle.$life += 1;
220
+ let callbacks;
221
+ if (callbacks = lifecycle.$onMount) callbacks.length = 0;
222
+ if (callbacks = lifecycle.$onUnmount) callbacks.length = 0;
223
+ }
224
+ function notify(list) {
225
+ list.$xa = true;
226
+ list.$xn = list.$dh;
227
+ let errors;
228
+ let current;
229
+ while (current = list.$xn) {
230
+ list.$xn = current.$an;
231
+ try {
232
+ invoke(current);
233
+ } catch (ex) {
234
+ (errors ??= []).push(ex);
235
+ }
236
+ }
237
+ list.$xa = false;
238
+ list.$xn = null;
239
+ rethrow(errors);
240
+ }
241
+
242
+ //#endregion
243
+ //#region src/Component.ts
244
+ /*@__NO_SIDE_EFFECTS__*/
245
+ function component(block) {
246
+ let devId;
247
+ devId = arguments[1];
248
+ devId && runtime.$hmrComponent.upsert(devId, block);
249
+ return (props, hParent, hBefore, nUsedParent, nRealParent, isBatch) => {
250
+ const capturedContext = runtime.$currentContext;
251
+ const capturedContextNew = runtime.$currentContextNew;
252
+ try {
253
+ {
254
+ if (!import.meta.hot || !devId) {
255
+ mountJsx(__DEV__evalComponent(block, props), hParent, hBefore, nUsedParent, nRealParent, isBatch);
256
+ return;
257
+ }
258
+ const hGroup = fork(hParent, hBefore);
259
+ let hMarker = hBefore;
260
+ const unsubscribe = runtime.$hmrComponent.subscribe(devId, (impl) => {
261
+ unmount(hGroup);
262
+ mount({
263
+ ...props,
264
+ [S_COMPONENT]: (() => mountJsx(__DEV__evalComponent(impl, props), hGroup, hMarker, nUsedParent, nRealParent, isBatch))
265
+ }, hGroup, hMarker, nUsedParent, nRealParent, isBatch);
266
+ });
267
+ onceUnmounted(hParent.$ng, { fn: unsubscribe });
268
+ hMarker = insert(hGroup.adapter.marker(`/${devId}`), null, hParent, hBefore, nUsedParent, isBatch, true);
269
+ nUsedParent = nRealParent;
270
+ isBatch = false;
271
+ }
272
+ } finally {
273
+ runtime.$currentContext = capturedContext;
274
+ runtime.$currentContextNew = capturedContextNew;
275
+ }
276
+ };
277
+ }
278
+ function __DEV__evalComponent(block, props) {
279
+ const prevLifecycle = runtime.$componentEvalLifecycle ?? null;
280
+ const prevEffect = runtime.$componentEvalEffect ?? null;
281
+ runtime.$componentEvalLifecycle = runtime.$currentLifecycle;
282
+ runtime.$componentEvalEffect = runtime.$currentEffect;
283
+ runtime.$currentContextNew = false;
284
+ try {
285
+ return block(props);
286
+ } finally {
287
+ runtime.$componentEvalLifecycle = prevLifecycle;
288
+ runtime.$componentEvalEffect = prevEffect;
289
+ }
290
+ }
291
+ function __DEV__assertIsDirectComponentCode(fn) {
292
+ if (!runtime.$componentEvalLifecycle || runtime.$componentEvalLifecycle !== runtime.$currentLifecycle) throw new Error(`"${fn}" can only be used directly within components.`);
293
+ if (runtime.$componentEvalEffect !== runtime.$currentEffect) throw new Error(`"${fn}" cannot be used within effects or derivations.`);
294
+ }
295
+
296
+ //#endregion
297
+ //#region src/data/Lifecycle.ts
298
+ function mounted(block, lifecycle) {
299
+ if (lifecycle === void 0) __DEV__assertIsDirectComponentCode(mounted.name);
300
+ lifecycle ??= getLifecycle();
301
+ onceMounted(lifecycle, {
302
+ fn: invokeMountedCallback,
303
+ a0: lifecycle,
304
+ a1: block
305
+ });
306
+ }
307
+ function onceMounted(lifecycle, callback) {
308
+ (lifecycle.$onMount ??= []).push(callback);
309
+ }
310
+ function invokeMountedCallback(lifecycle, block, arg) {
311
+ const life = lifecycle.$life;
312
+ const dispose = block(arg);
313
+ if (dispose) {
314
+ if (lifecycle.$life > life) {
315
+ dispose();
316
+ return;
317
+ }
318
+ onceUnmounted(lifecycle, { fn: dispose });
319
+ }
320
+ }
321
+ function unmounted(block, lifecycle) {
322
+ if (lifecycle === void 0) __DEV__assertIsDirectComponentCode(unmounted.name);
323
+ onceUnmounted(lifecycle ?? getLifecycle(), { fn: block });
324
+ }
325
+ function onceUnmounted(lifecycle, callback) {
326
+ (lifecycle.$onUnmount ??= []).push(callback);
327
+ }
328
+ function getLifecycle() {
329
+ if (!runtime.$currentLifecycle) throw new Error("Cannot get current lifecycle. Are you creating an Atom outside of a Component?");
330
+ return runtime.$currentLifecycle;
331
+ }
332
+ function withLifecycle(lifecycle, block, a0, a1, a2) {
333
+ const prevLifecycle = runtime.$currentLifecycle;
334
+ runtime.$currentLifecycle = lifecycle;
335
+ try {
336
+ return block(a0, a1, a2);
337
+ } finally {
338
+ runtime.$currentLifecycle = prevLifecycle;
339
+ }
227
340
  }
228
341
 
229
342
  //#endregion
230
343
  //#region src/data/Scheduler.ts
231
- /** @internal */
344
+ /*@__NO_SIDE_EFFECTS__*/
232
345
  function createScheduler(tick) {
233
- let isPending = false;
346
+ const effects = [];
234
347
  const ticks = [];
235
- const tocks = [];
348
+ let tocks = [];
349
+ let tocksParked = [];
350
+ let isPending = false;
236
351
  const scheduler = {
237
352
  $epoch: 1,
353
+ $onEffect: effects,
238
354
  $onTick: ticks,
239
355
  $onTock: tocks,
240
356
  $scheduleTick: () => {
@@ -244,179 +360,227 @@ function createScheduler(tick) {
244
360
  }
245
361
  };
246
362
  const update = () => {
247
- try {
248
- let index = 0;
249
- let callback;
250
- for (; index < ticks.length; index += 1) {
251
- callback = ticks[index];
252
- if (callback.$life === callback.$lifecycle.$life) {
253
- callback.$re = scheduler.$epoch;
363
+ const prevLifecycle = runtime.$currentLifecycle;
364
+ const epoch = scheduler.$epoch;
365
+ let errors;
366
+ let effectIndex = 0;
367
+ let tickIndex = 0;
368
+ let callback;
369
+ do {
370
+ while (tickIndex < ticks.length) {
371
+ callback = ticks[tickIndex++];
372
+ callback.$se = 0;
373
+ if (callback.$life === callback.$lifecycle.$life) try {
374
+ runtime.$currentLifecycle = callback.$lifecycle;
254
375
  invoke(callback);
376
+ } catch (ex) {
377
+ (errors ??= []).push(ex);
255
378
  }
256
379
  }
257
- for (index = 0; index < tocks.length; index += 1) {
258
- callback = tocks[index];
259
- if (callback.$life === callback.$lifecycle.$life) {
260
- callback.$re = scheduler.$epoch;
380
+ if (effectIndex < effects.length) {
381
+ callback = effects[effectIndex++];
382
+ callback.$se = 0;
383
+ if (callback.$life === callback.$lifecycle.$life) try {
384
+ runtime.$currentLifecycle = callback.$lifecycle;
261
385
  invoke(callback);
386
+ } catch (ex) {
387
+ (errors ??= []).push(ex);
262
388
  }
263
389
  }
264
- } finally {
265
- isPending = false;
266
- ticks.length = 0;
267
- tocks.length = 0;
268
- scheduler.$epoch += 1;
390
+ } while (tickIndex < ticks.length || effectIndex < effects.length);
391
+ effects.length = 0;
392
+ ticks.length = 0;
393
+ scheduler.$onTock = tocksParked;
394
+ scheduler.$epoch = epoch + 1;
395
+ const { length } = tocks;
396
+ let tockIndex = 0;
397
+ for (; tockIndex < length; tockIndex += 1) {
398
+ callback = tocks[tockIndex];
399
+ if (callback.$se === epoch) callback.$se = 0;
400
+ if (callback.$life === callback.$lifecycle.$life) try {
401
+ runtime.$currentLifecycle = callback.$lifecycle;
402
+ invoke(callback);
403
+ } catch (ex) {
404
+ (errors ??= []).push(ex);
405
+ }
269
406
  }
407
+ const tmp = tocksParked;
408
+ tocksParked = tocks;
409
+ tocks = tmp;
410
+ tocksParked.length = 0;
411
+ isPending = false;
412
+ if (ticks.length || tocks.length) scheduler.$scheduleTick();
413
+ runtime.$currentLifecycle = prevLifecycle;
414
+ rethrow(errors);
270
415
  };
271
416
  return scheduler;
272
417
  }
273
418
  function schedule(lifecycle, queue, callback) {
274
- const scheduler = lifecycle.$scheduler;
275
- if (callback.$se === scheduler.$epoch) {
276
- if (callback.$re === scheduler.$epoch) throw new Error("Refusing to re-schedule an update after it already executed, as it may cause an infinite loop. Are you mutating an Atom inside an effect that observes it?");
277
- return;
278
- }
279
419
  callback.$lifecycle = lifecycle;
280
420
  callback.$life = lifecycle.$life;
421
+ const scheduler = lifecycle.$scheduler;
422
+ if (callback.$se === scheduler.$epoch) return;
281
423
  callback.$se = scheduler.$epoch;
282
424
  queue.push(callback);
283
425
  scheduler.$scheduleTick();
284
426
  }
285
- /** @internal */
427
+ function scheduleEffect(lifecycle, callback) {
428
+ schedule(lifecycle, lifecycle.$scheduler.$onEffect, callback);
429
+ }
430
+ function tick(block, lifecycle = getLifecycle()) {
431
+ schedule(lifecycle, lifecycle.$scheduler.$onTick, { fn: block });
432
+ }
286
433
  function scheduleTick(lifecycle, callback) {
287
434
  schedule(lifecycle, lifecycle.$scheduler.$onTick, callback);
288
435
  }
289
- /**
290
- * Runs a block of code on the next tick of the scheduler, synchronized with other updates. If a
291
- * tick is not currently pending, a new one is scheduled.
292
- */
293
- function tick(block, lifecycle = getLifecycle()) {
294
- schedule(lifecycle, lifecycle.$scheduler.$onTick, { $fn: block });
436
+ function tock(block, lifecycle = getLifecycle()) {
437
+ schedule(lifecycle, lifecycle.$scheduler.$onTock, { fn: block });
295
438
  }
296
- /** @internal */
297
439
  function scheduleTock(lifecycle, callback) {
298
440
  schedule(lifecycle, lifecycle.$scheduler.$onTock, callback);
299
441
  }
300
- /**
301
- * Runs a block of code after the next tick of the scheduler, once all regular updates finished.
302
- * If a tick is not currently pending, a new one is scheduled.
303
- */
304
- function tock(block, lifecycle = getLifecycle()) {
305
- schedule(lifecycle, lifecycle.$scheduler.$onTock, { $fn: block });
306
- }
307
442
 
308
443
  //#endregion
309
444
  //#region src/data/Effect.ts
310
- /**
311
- * Creates an Effect - a block of logic executed each time any of the Atoms accessed within it
312
- * change. The block is first synchronously executed when the Effect is created.
313
- *
314
- * If a teardown callback is returned, it will be run before the next effect re-run, or on component
315
- * unmount.
316
- */
317
445
  function effect(block, lifecycle = getLifecycle()) {
318
446
  runEffect({
319
447
  $lifecycle: lifecycle,
320
448
  $life: lifecycle.$life,
321
449
  $block: block,
322
- $react: scheduleEffect,
450
+ $react: scheduleReaction$1,
323
451
  $cycle: 0
324
452
  });
325
453
  }
326
- function scheduleEffect(effect, cycle) {
454
+ function scheduleReaction$1(effect, cycle) {
327
455
  if (effect.$cycle > cycle) {
328
- unlink(this);
456
+ unlinkEffectDep(effect, this);
329
457
  return;
330
458
  }
331
- scheduleTick(effect.$lifecycle, effect.$resolve ??= {
332
- $fn: runEffect,
333
- $a0: effect
459
+ scheduleEffect(effect.$lifecycle, effect.$resolve ??= {
460
+ fn: runEffect,
461
+ a0: effect
334
462
  });
335
463
  }
336
464
  function runEffect(effect) {
337
- if (effect.$life !== effect.$lifecycle.$life) return;
338
- effect.$dispose?.();
465
+ const lifecycle = effect.$lifecycle;
466
+ if (effect.$life !== lifecycle.$life) return;
467
+ teardownEffect(effect);
468
+ if (effect.$life !== lifecycle.$life) return;
339
469
  effect.$dispose = resolve(effect);
470
+ if (effect.$life !== lifecycle.$life) {
471
+ teardownEffect(effect);
472
+ return;
473
+ }
340
474
  if (!effect.$tracksUnmount && effect.$dispose) {
341
475
  effect.$tracksUnmount = true;
342
- onUnmounted(effect.$lifecycle, {
343
- $fn: teardownEffect,
344
- $a0: effect
476
+ onceUnmounted(lifecycle, {
477
+ fn: teardownEffect,
478
+ a0: effect
345
479
  });
346
480
  }
347
481
  }
348
482
  function teardownEffect(effect) {
349
- effect.$dispose?.();
483
+ const fn = effect.$dispose;
484
+ if (!fn) return;
485
+ const prevLifecycle = runtime.$currentLifecycle;
486
+ runtime.$currentLifecycle = effect.$lifecycle;
350
487
  effect.$dispose = null;
488
+ try {
489
+ fn();
490
+ } finally {
491
+ runtime.$currentLifecycle = prevLifecycle;
492
+ }
351
493
  }
352
- let $currentEffect = null;
353
- /**
354
- * Resolves an Effect: runs user logic tracking accessed Atoms and updating dependency links.
355
- * Returned value is forwarded.
356
- * @internal
357
- */
358
494
  function resolve(effect) {
359
- effect.$deps ??= /* @__PURE__ */ new WeakMap();
360
- effect.$cycle += 1;
361
- const previousEffect = $currentEffect;
362
- const previousLifecycle = setLifecycle(effect.$lifecycle);
363
- $currentEffect = effect;
495
+ effect.$rc = 0;
496
+ const cycle = effect.$cycle += 1;
497
+ const prevLifecycle = runtime.$currentLifecycle;
498
+ const prevEffect = runtime.$currentEffect;
499
+ const prevTail = effect.$et;
500
+ runtime.$currentLifecycle = effect.$lifecycle;
501
+ runtime.$currentEffect = effect;
364
502
  try {
365
503
  return effect.$block();
366
504
  } finally {
367
- setLifecycle(previousLifecycle);
368
- $currentEffect = previousEffect;
505
+ runtime.$currentLifecycle = prevLifecycle;
506
+ runtime.$currentEffect = prevEffect;
507
+ if (cycle > 1 && effect.$rc > 0 && prevTail) {
508
+ let current = effect.$eh;
509
+ let next;
510
+ while (current) {
511
+ next = current.$en;
512
+ if (current.a1 < cycle) unlinkEffectDep(effect, current);
513
+ if (current === prevTail) break;
514
+ current = next;
515
+ }
516
+ }
369
517
  }
370
518
  }
371
- /**
372
- * Reports an Atom has been accessed. Does nothing if not within an Effect.
373
- * @internal
374
- */
519
+ function unlinkEffectDep(effect, dep) {
520
+ if (dep.$ep) dep.$ep.$en = dep.$en;
521
+ else if (effect.$eh === dep) effect.$eh = dep.$en;
522
+ if (dep.$en) dep.$en.$ep = dep.$ep;
523
+ else if (effect.$et === dep) effect.$et = dep.$ep;
524
+ dep.$ep = null;
525
+ dep.$en = null;
526
+ effect.$deps?.delete(dep.$target);
527
+ unlink(dep);
528
+ }
375
529
  function reportAccess(atom) {
376
- if (!$currentEffect || $currentEffect.$life !== $currentEffect.$lifecycle.$life) return;
377
- let dep = $currentEffect.$deps.get(atom);
530
+ const currentEffect = runtime.$currentEffect;
531
+ if (!currentEffect || currentEffect.$life !== currentEffect.$lifecycle.$life) return;
532
+ const deps = currentEffect.$deps;
533
+ let dep;
534
+ let count = 0;
535
+ if (deps) dep = deps.get(atom);
536
+ else {
537
+ dep = currentEffect.$eh;
538
+ while (dep) {
539
+ count += 1;
540
+ if (dep.$target === atom) break;
541
+ dep = dep.$en;
542
+ }
543
+ }
378
544
  if (dep) {
379
- dep.$a1 = $currentEffect.$cycle;
380
- if (!dep.$lifecycle) link($currentEffect.$lifecycle, atom, dep);
545
+ dep.a1 = currentEffect.$cycle;
546
+ if (!dep.$lifecycle) link(currentEffect.$lifecycle, atom, dep);
381
547
  } else {
382
- link($currentEffect.$lifecycle, atom, dep = {
383
- $fn: $currentEffect.$react,
384
- $a0: $currentEffect,
385
- $a1: $currentEffect.$cycle
548
+ link(currentEffect.$lifecycle, atom, dep = {
549
+ fn: currentEffect.$react,
550
+ a0: currentEffect,
551
+ a1: currentEffect.$cycle
386
552
  });
387
- $currentEffect.$deps.set(atom, dep);
553
+ if (deps) deps.set(atom, dep);
554
+ else if (count >= 5) {
555
+ const map = currentEffect.$deps = /* @__PURE__ */ new WeakMap();
556
+ let current = currentEffect.$eh;
557
+ do
558
+ map.set(current.$target, current);
559
+ while (current = current.$en);
560
+ map.set(atom, dep);
561
+ }
562
+ if (currentEffect.$et) {
563
+ currentEffect.$et.$en = dep;
564
+ dep.$ep = currentEffect.$et;
565
+ } else currentEffect.$eh = dep;
566
+ currentEffect.$et = dep;
567
+ currentEffect.$rc += 1;
388
568
  }
389
569
  }
390
- /**
391
- * Runs a block of code removed from any effects. Accessing atoms within this block will be ignored
392
- * by any effect that may otherwise be observing this code.
393
- */
394
- function noEffect(block) {
395
- const previousEffect = $currentEffect;
396
- $currentEffect = null;
570
+ function noEffect(block, a0, a1, a2) {
571
+ const prevEffect = runtime.$currentEffect;
572
+ runtime.$currentEffect = null;
397
573
  try {
398
- return block();
574
+ return block(a0, a1, a2);
399
575
  } finally {
400
- $currentEffect = previousEffect;
576
+ runtime.$currentEffect = prevEffect;
401
577
  }
402
578
  }
403
- /**
404
- * Asserts that the current code is not running within an effect block.
405
- * Only used in development; In production, this function should be removed by the bundler.
406
- */
407
- function __DEV__assertNotEffect() {
408
- if ($currentEffect && $currentEffect.$lifecycle === getLifecycle() && $currentEffect.$life === $currentEffect.$lifecycle.$life) throw new Error("Attempt to create an Atom inside an effect block.");
409
- }
410
579
 
411
580
  //#endregion
412
581
  //#region src/data/Atom.ts
413
- /**
414
- * Pyxis Atom type guard marker.
415
- */
416
- const S_ATOM = Symbol.for("pyxis:atom");
417
582
  function atomOf(initialValue, lifecycle = getLifecycle()) {
418
- __DEV__assertNotEffect();
419
- if (isAtom(initialValue)) return initialValue;
583
+ if (isAtom(initialValue)) initialValue = initialValue.get();
420
584
  const atom = {
421
585
  [S_ATOM]: true,
422
586
  $value: initialValue,
@@ -424,13 +588,14 @@ function atomOf(initialValue, lifecycle = getLifecycle()) {
424
588
  $lifecycle: lifecycle,
425
589
  $lastValue: initialValue,
426
590
  get: getValue$1,
427
- set: setValue$2
591
+ set: setValue$1
428
592
  };
429
593
  {
430
594
  const devId = arguments[2];
431
595
  atom.$devId = devId;
432
- globalThis.__PYXIS_HMR__.state.restore(lifecycle, devId, (value) => {
596
+ runtime.$hmrState.restore(lifecycle, devId, (value) => {
433
597
  atom.$value = value;
598
+ atom.$lastValue = value;
434
599
  });
435
600
  }
436
601
  return atom;
@@ -438,10 +603,10 @@ function atomOf(initialValue, lifecycle = getLifecycle()) {
438
603
  function getValue$1() {
439
604
  return this.$value;
440
605
  }
441
- function setValue$2(value) {
606
+ function setValue$1(value) {
442
607
  if (Object.is(this.$value, value)) return false;
443
608
  this.$value = value;
444
- globalThis.__PYXIS_HMR__.state.preserve(this.$lifecycle, this.$devId, value);
609
+ runtime.$hmrState.preserve(this.$lifecycle, this.$devId, value);
445
610
  return true;
446
611
  }
447
612
  function isAtom(input) {
@@ -459,96 +624,88 @@ function peek(input) {
459
624
  return input;
460
625
  }
461
626
  function write(input, value, force = false) {
462
- if (isAtom(input)) {
463
- if (input.set(value) || force) {
627
+ if (!isAtom(input)) return input;
628
+ if (input.set(value) || force) {
629
+ if (input.$dh) {
464
630
  input.$force ||= force;
465
- scheduleTick(input.$lifecycle, input.$notify ??= {
466
- $fn: notify$1,
467
- $a0: input
468
- });
469
- }
470
- return input.get();
631
+ scheduleAtomNotification(input);
632
+ } else if (input.$tracksValue) return input.$lastValue = input.get();
471
633
  }
472
- return input;
634
+ return input.get();
473
635
  }
474
636
  function update(input, transform, force = false) {
475
- if (isAtom(input)) {
476
- if (input.set(transform(input.get())) || force) {
637
+ if (!isAtom(input)) return input;
638
+ const value = transform(input.get());
639
+ if (input.set(value) || force) {
640
+ if (input.$dh) {
477
641
  input.$force ||= force;
478
- scheduleTick(input.$lifecycle, input.$notify ??= {
479
- $fn: notify$1,
480
- $a0: input
481
- });
482
- }
483
- return input.get();
484
- }
485
- return input;
486
- }
487
- /**
488
- * Notifies the dependencies of an Atom.
489
- * @internal
490
- */
491
- function notify$1(input) {
492
- if (input.$tracksValue) {
493
- const newValue = input.get();
494
- if (Object.is(input.$lastValue, newValue) && !input.$force) return;
495
- input.$lastValue = newValue;
496
- }
497
- input.$force = false;
498
- let current = input.$dh;
499
- let next;
500
- while (current) {
501
- next = current.$an;
502
- invoke(current);
503
- current = next;
642
+ scheduleAtomNotification(input);
643
+ } else if (input.$tracksValue) return input.$lastValue = input.get();
504
644
  }
645
+ return input.get();
505
646
  }
506
-
507
- //#endregion
508
- //#region src/component/Text.ts
509
- function Text(jsx, hParent, nUsedParent, _nRealParent, nBefore, isBatch) {
510
- const { adapter } = hParent.$ng;
511
- let node = null;
512
- if (isAtom(jsx)) bind(hParent.$ng, jsx, () => {
513
- node = adapter.text(jsx.get()?.toString() ?? "", node);
647
+ function scheduleAtomNotification(atom) {
648
+ scheduleTick(atom.$lifecycle, atom.$notify ??= {
649
+ fn: notifyAtomObservers,
650
+ a0: atom
514
651
  });
515
- else node = adapter.text(jsx.toString(), null);
516
- insert(node, null, hParent, nUsedParent, nBefore, isBatch);
652
+ }
653
+ function notifyAtomObservers(atom) {
654
+ if (atom.$tracksValue) {
655
+ const newValue = atom.get();
656
+ if (!atom.$force && Object.is(atom.$lastValue, newValue)) return;
657
+ atom.$lastValue = newValue;
658
+ }
659
+ atom.$force = false;
660
+ notify(atom);
517
661
  }
518
662
 
519
663
  //#endregion
520
664
  //#region src/Renderer.ts
521
- /** @internal */
522
- const S_COMPONENT = Symbol.for("pyxis:component");
523
- /** @internal */
665
+ /*@__NO_SIDE_EFFECTS__*/
524
666
  function createRenderer(adapter, extensions) {
525
- const hGroup = {
667
+ const hRoot = {
526
668
  adapter,
527
- mounted: false,
669
+ mounted: true,
528
670
  $isGroup: true,
529
- $scheduler: createScheduler(adapter.tick),
671
+ $scheduler: /* @__PURE__ */ createScheduler(adapter.tick),
530
672
  $extensions: extensions,
673
+ $context: null,
674
+ $depth: 0,
531
675
  $life: 1,
532
- $pg: null,
533
676
  $ng: null,
534
- unmount: () => unmount(hGroup),
677
+ $ph: null,
678
+ unmount: destroy,
535
679
  mount: (nRoot, jsx) => {
536
- hGroup.$nn = nRoot;
537
- const isBatch = Boolean(adapter.batch);
538
- const nParent = isBatch ? adapter.batch() : nRoot;
539
- mount(jsx, hGroup, nParent, nRoot, null, isBatch);
540
- if (isBatch) adapter.insert(nParent, nRoot, null);
680
+ const hGroup = fork(hRoot, null, null);
681
+ try {
682
+ willMount(hGroup, () => {
683
+ const isBatch = !!adapter.batch;
684
+ const nParent = isBatch ? adapter.batch() : nRoot;
685
+ mount(jsx, hGroup, null, nParent, nRoot, isBatch);
686
+ if (isBatch) {
687
+ adapter.insert(nParent, nRoot, null);
688
+ flush(hGroup);
689
+ }
690
+ })();
691
+ return hGroup;
692
+ } catch (ex) {
693
+ destroy(hGroup, [ex]);
694
+ }
541
695
  }
542
696
  };
543
- hGroup.$ng = hGroup;
544
- globalThis.__PYXIS_ROOT__ ??= hGroup;
545
- return hGroup;
697
+ hRoot.$ng = hRoot;
698
+ return hRoot;
699
+ }
700
+ function destroy(hGroup, errors) {
701
+ if (!hGroup.$isUnmounting && !deferUnmount(hGroup, true)) {
702
+ errors = doUnmount(hGroup, errors);
703
+ untrack(hGroup);
704
+ hGroup.$ph = null;
705
+ }
706
+ rethrow(errors);
546
707
  }
547
- /**
548
- * Creates a sub-group within the provided MountingGroup. Needed whenever a subtree needs to mount
549
- * or unmount dynamically.
550
- */
551
- function fork(hParent, hBefore = null) {
708
+ function fork(hParent, hBefore = null, context = runtime.$currentContext) {
552
709
  const ng = hParent.$ng;
553
710
  const hGroup = {
554
711
  adapter: ng.adapter,
@@ -556,20 +713,19 @@ function fork(hParent, hBefore = null) {
556
713
  $isGroup: true,
557
714
  $scheduler: ng.$scheduler,
558
715
  $extensions: ng.$extensions,
716
+ $context: context,
717
+ $depth: 0,
559
718
  $life: 1,
560
- $pg: ng,
561
- $ng: null
719
+ $ng: null,
720
+ $ph: hParent
562
721
  };
563
722
  hGroup.$ng = hGroup;
564
723
  hGroup.$nn = hParent.$nn;
565
724
  track(hGroup, hParent, hBefore);
566
725
  return hGroup;
567
726
  }
568
- /**
569
- * Adds a HNode to the hierarchy.
570
- */
571
727
  function track(hNode, hParent, hBefore = null) {
572
- if (hBefore?.$pg === hParent) if (hBefore.$hp) {
728
+ if (hBefore?.$ph === hParent) if (hBefore.$hp) {
573
729
  hNode.$hp = hBefore.$hp;
574
730
  hNode.$hn = hBefore;
575
731
  hBefore.$hp.$hn = hNode;
@@ -587,293 +743,309 @@ function track(hNode, hParent, hBefore = null) {
587
743
  } else hParent.$hh = hNode;
588
744
  hParent.$ht = hNode;
589
745
  }
746
+ hNode.$ph = hParent;
590
747
  }
591
- /**
592
- * Removes a HNode from the tracking hierarchy.
593
- */
594
748
  function untrack(hNode) {
595
749
  if (hNode.$hp) hNode.$hp.$hn = hNode.$hn;
596
- else if (hNode.$pg?.$hh === hNode) hNode.$pg.$hh = hNode.$hn;
750
+ else if (hNode.$ph?.$hh === hNode) hNode.$ph.$hh = hNode.$hn;
597
751
  if (hNode.$hn) hNode.$hn.$hp = hNode.$hp;
598
- else if (hNode.$pg?.$ht === hNode) hNode.$pg.$ht = hNode.$hp;
752
+ else if (hNode.$ph?.$ht === hNode) hNode.$ph.$ht = hNode.$hp;
599
753
  hNode.$hp = null;
600
754
  hNode.$hn = null;
601
755
  }
602
- /**
603
- * Mounts a MountingGroup to the specified location in the node tree. If the group is already
604
- * mounted (i.e. its native nodes are already rendered somewhere), it is moved to the new location
605
- * without re-mounting Pyxis components.
606
- *
607
- * Note that for successfully moving a group within the tree, you should first `untrack` the group,
608
- * then re-`track` it to the new location and only then call `mount` to commit the move.
609
- * @see {@link track}
610
- * @see {@link untrack}
611
- */
612
- function mount(jsx, hGroup, nUsedParent, nRealParent, nBefore, isBatch) {
613
- if (hGroup.mounted) {
614
- reinsertNodes(hGroup, nUsedParent, nBefore);
615
- return;
616
- }
617
- const previousLifecycle = setLifecycle(hGroup);
756
+ function willMount(hGroup, block) {
757
+ return (a0, a1, a2) => {
758
+ const mounts = runtime.$mounts ??= [];
759
+ if (++hGroup.$depth === 1) mounts.push(hGroup);
760
+ let errors;
761
+ try {
762
+ return block(a0, a1, a2);
763
+ } catch (ex) {
764
+ (errors ??= []).push(ex);
765
+ } finally {
766
+ if (--hGroup.$depth === 0) mounts.pop();
767
+ finishMount(errors);
768
+ }
769
+ };
770
+ }
771
+ function mount(jsx, hParent, hBefore, nUsedParent, nRealParent, isBatch) {
772
+ if (hParent.mounted) return;
773
+ const mounts = runtime.$mounts ??= [];
774
+ if (++hParent.$depth === 1) mounts.push(hParent);
775
+ const prevLifecycle = runtime.$currentLifecycle;
776
+ const prevEffect = runtime.$currentEffect;
777
+ const prevContext = runtime.$currentContext;
778
+ const prevContextNew = runtime.$currentContextNew;
779
+ runtime.$currentLifecycle = hParent;
780
+ runtime.$currentContext = hParent.$context;
781
+ runtime.$currentContextNew = false;
782
+ runtime.$currentEffect = null;
783
+ let errors;
618
784
  try {
619
- mountJsx(jsx, hGroup, nUsedParent, nRealParent, nBefore, isBatch);
620
- } finally {
621
- setLifecycle(previousLifecycle);
622
- hGroup.mounted = true;
623
- if (hGroup.$pg?.mounted !== false) notifyMounted(hGroup);
624
- }
625
- }
626
- /**
627
- * Only used by `mount`, do not call directly.
628
- *
629
- * Recursively (dept-first) runs components' `mounted` callbacks.
630
- * @see {@link mount}
631
- * @internal
632
- */
633
- function notifyMounted(hNode) {
785
+ mountJsx(jsx, hParent, hBefore, nUsedParent, nRealParent, isBatch);
786
+ hParent.$canMount = true;
787
+ if (!isBatch) flush(hParent);
788
+ } catch (ex) {
789
+ (errors ??= []).push(ex);
790
+ }
791
+ runtime.$currentLifecycle = prevLifecycle;
792
+ runtime.$currentEffect = prevEffect;
793
+ runtime.$currentContext = prevContext;
794
+ runtime.$currentContextNew = prevContextNew;
795
+ if (--hParent.$depth === 0) mounts.pop();
796
+ if (errors?.length && !deferUnmount(hParent)) errors = doUnmount(hParent, errors);
797
+ finishMount(errors);
798
+ }
799
+ function finishMount(errors) {
800
+ if (!runtime.$mounts.length) {
801
+ const pending = runtime.$unmounts;
802
+ runtime.$unmounts = null;
803
+ if (pending) {
804
+ const { length } = pending;
805
+ let index = 0;
806
+ let request;
807
+ for (; index < length; index += 1) {
808
+ request = pending[index];
809
+ if (request.$life !== request.$group.$life) continue;
810
+ errors = doUnmount(request.$group, errors);
811
+ if (request.$destroy) {
812
+ untrack(request.$group);
813
+ request.$group.$ph = null;
814
+ }
815
+ }
816
+ }
817
+ }
818
+ rethrow(errors);
819
+ }
820
+ function deferUnmount(hGroup, isDestroy = false) {
821
+ const mounts = runtime.$mounts;
822
+ if (!mounts?.length) return false;
823
+ const lifecycle = runtime.$currentLifecycle;
824
+ const { length } = mounts;
825
+ let index = 0;
826
+ let active;
827
+ for (; index < length; index += 1) {
828
+ active = mounts[index];
829
+ if (ancestorContainsNode(hGroup, active) || lifecycle?.$isGroup && ancestorContainsNode(active, lifecycle) && ancestorContainsNode(hGroup, lifecycle)) break;
830
+ }
831
+ if (index === length) return false;
832
+ const pending = runtime.$unmounts ??= [];
833
+ const pendingLength = pending.length;
834
+ let pendingIndex = 0;
835
+ let request;
836
+ for (; pendingIndex < pendingLength; pendingIndex += 1) {
837
+ request = pending[pendingIndex];
838
+ if (request.$group === hGroup && request.$life === hGroup.$life) {
839
+ request.$destroy ||= isDestroy;
840
+ return true;
841
+ }
842
+ }
843
+ pending.push({
844
+ $group: hGroup,
845
+ $life: hGroup.$life,
846
+ $destroy: isDestroy
847
+ });
848
+ return true;
849
+ }
850
+ function ancestorContainsNode(hAncestor, hNode) {
851
+ while (hNode) {
852
+ if (hNode === hAncestor) return true;
853
+ hNode = hNode.$ph;
854
+ }
855
+ return false;
856
+ }
857
+ function flush(hGroup) {
858
+ if (hGroup.$ph?.$ng.mounted !== false) {
859
+ const prevLifecycle = runtime.$currentLifecycle;
860
+ const prevEffect = runtime.$currentEffect;
861
+ runtime.$currentEffect = null;
862
+ const mounts = runtime.$mounts ??= [];
863
+ if (++hGroup.$depth === 1) mounts.push(hGroup);
864
+ const errors = doFlush(hGroup, void 0);
865
+ runtime.$currentLifecycle = prevLifecycle;
866
+ runtime.$currentEffect = prevEffect;
867
+ if (--hGroup.$depth === 0) mounts.pop();
868
+ finishMount(errors);
869
+ }
870
+ }
871
+ function doFlush(hNode, errors) {
634
872
  let current = hNode.$hh;
635
873
  while (current) {
636
- notifyMounted(current);
874
+ errors = doFlush(current, errors);
637
875
  current = current.$hn;
638
876
  }
639
- let callbacks;
640
- if (hNode.$isGroup && hNode.mounted && (callbacks = hNode.$onMount)) {
641
- const previousLifecycle = setLifecycle(hNode);
642
- try {
643
- callbacks.forEach(invoke);
644
- } finally {
877
+ if (hNode.$isGroup && hNode.$canMount && !hNode.mounted) {
878
+ hNode.mounted = true;
879
+ if (hNode.$onMount?.length) {
880
+ const callbacks = hNode.$onMount;
881
+ let index = 0;
882
+ runtime.$currentLifecycle = hNode;
883
+ for (; index < callbacks.length; index += 1) try {
884
+ invoke(callbacks[index]);
885
+ } catch (ex) {
886
+ (errors ??= []).push(ex);
887
+ }
645
888
  callbacks.length = 0;
646
- setLifecycle(previousLifecycle);
647
889
  }
648
890
  }
891
+ return errors;
649
892
  }
650
- /**
651
- * Only used by `mount`, do not call directly.
652
- *
653
- * Re-inserts native nodes of the given group to the specified location in the native tree. Only
654
- * moves the uppermost nodes - any children should move implicitly with them.
655
- * @see {@link mount}
656
- * @internal
657
- */
658
- function reinsertNodes(hGroup, nParent, nBefore) {
659
- const { adapter } = hGroup;
660
- let current = hGroup.$hh;
893
+ function reinsertNodes(hParent, hBefore, nParent) {
894
+ if (!hParent.mounted) return;
895
+ const { adapter } = hParent;
896
+ const nAnchor = hBefore?.$nn ?? null;
897
+ let current = hParent.$hh;
661
898
  while (current) {
662
- if (current.$isNative) adapter.insert(current.$nn, nParent, nBefore);
663
- else reinsertNodes(current, nParent, nBefore);
899
+ if (current.$isNative) adapter.insert(current.$nn, nParent, nAnchor);
900
+ else reinsertNodes(current, hBefore, nParent);
664
901
  current = current.$hn;
665
902
  }
666
903
  }
667
- function unmount(hNode, top = true) {
904
+ function unmount(hGroup, errors) {
905
+ if (deferUnmount(hGroup)) {
906
+ rethrow(errors);
907
+ return;
908
+ }
909
+ errors = doUnmount(hGroup, errors);
910
+ rethrow(errors);
911
+ }
912
+ function doUnmount(hGroup, errors) {
913
+ const prevLifecycle = runtime.$currentLifecycle;
914
+ const prevEffect = runtime.$currentEffect;
915
+ runtime.$currentEffect = null;
916
+ errors = unmountNode(hGroup, errors, true);
917
+ runtime.$currentLifecycle = prevLifecycle;
918
+ runtime.$currentEffect = prevEffect;
919
+ return errors;
920
+ }
921
+ function unmountNode(hNode, errors, top) {
922
+ if (hNode.$isUnmounting) return errors;
923
+ hNode.$isUnmounting = true;
668
924
  if (hNode.$isGroup) {
669
- if (hNode.mounted) {
670
- let callbacks;
671
- if (callbacks = hNode.$onUnmount) {
672
- const previousLifecycle = setLifecycle(hNode);
673
- try {
674
- callbacks.forEach(invoke);
675
- } finally {
676
- callbacks.length = 0;
677
- setLifecycle(previousLifecycle);
678
- }
925
+ if (hNode.$onUnmount?.length) {
926
+ const callbacks = hNode.$onUnmount;
927
+ let index = 0;
928
+ runtime.$currentLifecycle = hNode;
929
+ for (; index < callbacks.length; index += 1) try {
930
+ invoke(callbacks[index]);
931
+ } catch (ex) {
932
+ (errors ??= []).push(ex);
679
933
  }
680
- hNode.mounted = false;
681
- hNode.$life += 1;
682
934
  }
683
- unlinkAll(hNode);
935
+ teardown(hNode);
936
+ hNode.mounted = false;
937
+ hNode.$canMount = false;
684
938
  }
685
939
  const { adapter } = hNode.$ng;
686
940
  let current = hNode.$hh;
687
941
  let next;
688
942
  while (current) {
689
943
  if (current.$isNative && top) {
690
- adapter.remove(current.$nn);
691
- unmount(current, false);
692
- } else unmount(current, top);
944
+ try {
945
+ adapter.remove(current.$nn);
946
+ } catch (ex) {
947
+ (errors ??= []).push(ex);
948
+ }
949
+ errors = unmountNode(current, errors, false);
950
+ } else errors = unmountNode(current, errors, top);
693
951
  next = current.$hn;
694
952
  current.$hp = null;
695
953
  current.$hn = null;
954
+ current.$ph = null;
696
955
  current = next;
697
956
  }
698
957
  hNode.$hh = null;
699
958
  hNode.$ht = null;
959
+ hNode.$isUnmounting = false;
960
+ return errors;
700
961
  }
701
- /**
702
- * Mounts components described by the JsxResult to the specified location in the node tree.
703
- */
704
- function mountJsx(jsx, hParent, nUsedParent, nRealParent, nBefore, isBatch) {
962
+ function mountJsx(jsx, hParent, hBefore, nUsedParent, nRealParent, isBatch) {
705
963
  switch (typeof jsx) {
706
964
  case "object":
707
965
  if (jsx === null) break;
708
966
  if (Array.isArray(jsx)) {
709
- const { length } = jsx;
710
- let index = 0;
711
- for (; index < length; index += 1) mountJsx(jsx[index], hParent, nUsedParent, nRealParent, nBefore, isBatch);
967
+ const text = mountJsxArray(jsx, hParent, hBefore, nUsedParent, nRealParent, isBatch);
968
+ if (text) mountStaticText(text, hParent, hBefore, nUsedParent, isBatch);
712
969
  break;
713
970
  }
714
- if (!isAtom(jsx)) {
715
- jsx[S_COMPONENT]?.(jsx, hParent, nUsedParent, nRealParent, nBefore, isBatch);
716
- break;
717
- }
718
- case "string":
971
+ if (isAtom(jsx)) mountDynamicText(jsx, hParent, hBefore, nUsedParent, isBatch);
972
+ else jsx[S_COMPONENT]?.(jsx, hParent, hBefore, nUsedParent, nRealParent, isBatch);
973
+ break;
974
+ case "string": if (jsx === "") break;
719
975
  case "number":
720
- case "boolean":
721
976
  case "bigint":
722
- Text(jsx, hParent, nUsedParent, nRealParent, nBefore, isBatch);
977
+ mountStaticText("" + jsx, hParent, hBefore, nUsedParent, isBatch);
723
978
  break;
724
979
  }
725
980
  }
726
- /**
727
- * Inserts a native node and adds it to the tracking hierarchy. Necessary to preserve render order.
728
- * Should only be called by component handlers!
729
- */
730
- function insert(nNode, children, hParent, nUsedParent, nBefore, isBatch) {
731
- let hNative = hParent;
732
- if (hNative.$isGroup) {
733
- let hBefore = null;
734
- if (nBefore) {
735
- let current = hParent.$ht;
736
- while (current) {
737
- if (current.$isNative && current.$nn === nBefore) {
738
- hBefore = current;
981
+ function mountJsxArray(jsx, hParent, hBefore, nUsedParent, nRealParent, isBatch, text = "") {
982
+ const { length } = jsx;
983
+ let index = 0;
984
+ let item;
985
+ for (; index < length; index += 1) {
986
+ item = jsx[index];
987
+ switch (typeof item) {
988
+ case "string":
989
+ case "number":
990
+ case "bigint":
991
+ text += item;
992
+ break;
993
+ case "boolean": break;
994
+ default:
995
+ if (item === null || item === void 0) break;
996
+ if (Array.isArray(item)) {
997
+ text = mountJsxArray(item, hParent, hBefore, nUsedParent, nRealParent, isBatch, text);
739
998
  break;
740
999
  }
741
- current = current.$hp;
742
- }
1000
+ if (text) {
1001
+ mountStaticText(text, hParent, hBefore, nUsedParent, isBatch);
1002
+ text = "";
1003
+ }
1004
+ mountJsx(item, hParent, hBefore, nUsedParent, nRealParent, isBatch);
1005
+ break;
743
1006
  }
744
- track(hNative = {
745
- $isNative: true,
746
- $pg: hNative,
747
- $ng: hNative,
748
- $nn: nNode
749
- }, hParent, hBefore);
750
1007
  }
751
- mountJsx(children, hNative, nNode, nNode, null, isBatch);
752
- hParent.$ng.adapter.insert(nNode, nUsedParent, nBefore);
1008
+ return text;
753
1009
  }
754
-
755
- //#endregion
756
- //#region src/component/Fragment.ts
757
- function Fragment(jsx, hParent, nUsedParent, nRealParent, nBefore, isBatch) {
758
- mountJsx(jsx.children, hParent, nUsedParent, nRealParent, nBefore, isBatch);
1010
+ function mountStaticText(text, hParent, hBefore, nUsedParent, isBatch) {
1011
+ insert(hParent.$ng.adapter.text(text, null), null, hParent, hBefore, nUsedParent, isBatch);
759
1012
  }
760
-
761
- //#endregion
762
- //#region src/data/Context.ts
763
- function createContext() {
764
- let $symbol = Symbol();
765
- {
766
- const devId = arguments[0];
767
- $symbol = globalThis.__PYXIS_HMR__.state.restore(createContext, devId) ?? $symbol;
768
- globalThis.__PYXIS_HMR__.state.preserve(createContext, devId, $symbol);
769
- }
770
- return { $symbol };
771
- }
772
- let $currentContainer = null;
773
- let $isNewContainer = false;
774
- /** @internal */
775
- function getContextContainer() {
776
- return $currentContainer;
777
- }
778
- /** @internal */
779
- function setContextContainer(container) {
780
- $currentContainer = container;
781
- $isNewContainer = false;
782
- }
783
- /**
784
- * Gets a consumer Atom for the given Context. This atom will be read-only.
785
- * @see {@link host}
786
- */
787
- function consumerOf(context) {
788
- __DEV__assertNotEffect();
789
- const { $symbol } = context;
790
- let ptr = $currentContainer;
791
- let atom;
792
- while (ptr && !(atom = ptr[$symbol])) ptr = ptr.$parent;
793
- return atom ?? null;
1013
+ function mountDynamicText(atom, hParent, hBefore, nUsedParent, isBatch) {
1014
+ insert(bind(hParent.$ng, atom, {
1015
+ fn: setBoundText,
1016
+ a0: hParent.$ng.adapter,
1017
+ a1: null
1018
+ }), null, hParent, hBefore, nUsedParent, isBatch);
794
1019
  }
795
- function host(context, defaultValue) {
796
- __DEV__assertNotEffect();
797
- if (!$isNewContainer || !$currentContainer) {
798
- $isNewContainer = true;
799
- $currentContainer = { $parent: $currentContainer };
800
- }
801
- const lifecycle = getLifecycle();
802
- {
803
- const devId = arguments[2];
804
- globalThis.__PYXIS_HMR__.state.restore(lifecycle, devId, (value) => {
805
- defaultValue = value;
806
- });
807
- }
808
- const localAtom = {
809
- [S_ATOM]: true,
810
- $lifecycle: lifecycle,
811
- $tracksValue: true,
812
- $value: defaultValue,
813
- get: getLocalValue,
814
- set: setValue$1
815
- };
816
- if (defaultValue === void 0) {
817
- const ancestorAtom = consumerOf(context);
818
- if (ancestorAtom) {
819
- localAtom.get = getAncestorValue;
820
- localAtom.$ancestor = ancestorAtom;
821
- link(lifecycle, ancestorAtom, localAtom.$dep = {
822
- $fn: notify$1,
823
- $a0: localAtom
824
- });
825
- }
826
- }
827
- localAtom.$devId = arguments[2];
828
- if (Object.hasOwn($currentContainer, context.$symbol)) throw new Error("Component declares multiple hosts of the same Context.");
829
- $currentContainer[context.$symbol] = localAtom;
830
- return localAtom;
831
- }
832
- function getAncestorValue() {
833
- return this.$ancestor.get();
1020
+ function setBoundText(adapter, node) {
1021
+ const value = this.$target.get();
1022
+ return this.a1 = adapter.text(value === null || value === void 0 || typeof value === "boolean" ? "" : "" + value, node);
834
1023
  }
835
- function getLocalValue() {
836
- return this.$value;
1024
+ function insert(nNode, children, hParent, hBefore, nUsedParent, isBatch, forceTrack = false) {
1025
+ let hNative = hParent;
1026
+ if (hNative.$isGroup || forceTrack) track(hNative = {
1027
+ $isNative: true,
1028
+ $ng: hParent.$ng,
1029
+ $nn: nNode,
1030
+ $ph: hParent
1031
+ }, hParent, hBefore);
1032
+ mountJsx(children, hNative, null, nNode, nNode, isBatch);
1033
+ hParent.$ng.adapter.insert(nNode, nUsedParent, isBatch ? null : hBefore?.$nn ?? null);
1034
+ return hNative === hParent ? null : hNative;
837
1035
  }
838
- function setValue$1(value) {
839
- let oldValue;
840
- if (this.$ancestor) {
841
- oldValue = this.$ancestor.get();
842
- unlink(this.$dep);
843
- this.$dep = null;
844
- this.$ancestor = null;
845
- this.get = getLocalValue;
846
- } else oldValue = this.$value;
847
- this.$value = value;
848
- globalThis.__PYXIS_HMR__.state.preserve(this.$lifecycle, this.$devId, value);
849
- return !Object.is(oldValue, value);
1036
+
1037
+ //#endregion
1038
+ //#region src/component/Fragment.ts
1039
+ function Fragment(jsx, hParent, hBefore, nUsedParent, nRealParent, isBatch) {
1040
+ mountJsx(jsx.children, hParent, hBefore, nUsedParent, nRealParent, isBatch);
850
1041
  }
851
1042
 
852
1043
  //#endregion
853
1044
  //#region src/data/ListDelta.ts
854
- /** @internal */
855
1045
  const LC_CHANGE = 1;
856
- /** @internal */
857
1046
  const LC_INSERT = 2;
858
- /** @internal */
859
1047
  const LC_REMOVE = 3;
860
- /** @internal */
861
1048
  const LC_CLEAR = 4;
862
- let ChangeKind = /* @__PURE__ */ function(ChangeKind) {
863
- ChangeKind[ChangeKind["Change"] = 1] = "Change";
864
- ChangeKind[ChangeKind["Insert"] = 2] = "Insert";
865
- ChangeKind[ChangeKind["Remove"] = 3] = "Remove";
866
- ChangeKind[ChangeKind["Clear"] = 4] = "Clear";
867
- return ChangeKind;
868
- }({});
869
- /** @internal */
870
- function createDelta() {
871
- return {
872
- changes: [],
873
- lengthChange: 0
874
- };
875
- }
876
- /** @internal */
877
1049
  function itemChanged({ changes: $changes }, at, oldItem, newItem) {
878
1050
  const ci = binarySearch($changes, at, latest);
879
1051
  if (ci < 0) $changes.splice(~ci, 0, {
@@ -900,7 +1072,6 @@ function itemChanged({ changes: $changes }, at, oldItem, newItem) {
900
1072
  }
901
1073
  }
902
1074
  }
903
- /** @internal */
904
1075
  function itemInserted(delta, at, item) {
905
1076
  const { changes: $changes } = delta;
906
1077
  let ci = binarySearch($changes, at, earliest);
@@ -923,16 +1094,18 @@ function itemInserted(delta, at, item) {
923
1094
  });
924
1095
  break;
925
1096
  case 3:
926
- current.kind = 1;
927
- current.newItem = item;
1097
+ if (Object.is(current.oldItem, item)) $changes.splice(ci--, 1);
1098
+ else {
1099
+ current.kind = 1;
1100
+ current.newItem = item;
1101
+ }
928
1102
  break;
929
1103
  }
930
1104
  }
931
1105
  const { length } = $changes;
932
1106
  while (++ci < length) $changes[ci].index += 1;
933
- delta.lengthChange += 1;
1107
+ delta.relSize += 1;
934
1108
  }
935
- /** @internal */
936
1109
  function itemRemoved(delta, at, item) {
937
1110
  const { changes: $changes } = delta;
938
1111
  let ci = binarySearch($changes, at, latest);
@@ -964,29 +1137,29 @@ function itemRemoved(delta, at, item) {
964
1137
  }
965
1138
  const { length } = $changes;
966
1139
  while (++ci < length) $changes[ci].index -= 1;
967
- delta.lengthChange -= 1;
1140
+ delta.relSize -= 1;
968
1141
  }
969
- /** @internal */
970
1142
  function listCleared(delta, count) {
971
- const { changes: $changes } = delta;
972
- $changes.length = 0;
973
- $changes.push({
1143
+ delta.changes.length = 0;
1144
+ delta.relSize -= count;
1145
+ if (delta.relSize === 0) return;
1146
+ delta.changes.push({
974
1147
  kind: 4,
975
1148
  index: -1
976
1149
  });
977
- delta.lengthChange -= count;
978
1150
  }
979
- /** @internal */
980
1151
  function listSynced(delta, oldState, newState, eq) {
981
1152
  let index = 0;
982
1153
  let N = oldState.length;
983
1154
  let M = newState.length;
984
1155
  while (index < N && index < M && eq(oldState[index], newState[index])) index += 1;
985
- if (index === N && index === M) return;
1156
+ if (index === N && index === M) return false;
986
1157
  while (N > index && M > index && eq(oldState[N - 1], newState[M - 1])) {
987
1158
  N -= 1;
988
1159
  M -= 1;
989
1160
  }
1161
+ N -= index;
1162
+ M -= index;
990
1163
  const Z = (Math.min(N, M) + 1) * 2;
991
1164
  const L = N + M;
992
1165
  const state = {
@@ -1012,6 +1185,7 @@ function listSynced(delta, oldState, newState, eq) {
1012
1185
  $oye: -1
1013
1186
  };
1014
1187
  let offset = 0;
1188
+ let hasChanges = false;
1015
1189
  let rs, re, is, ie, r, i;
1016
1190
  do {
1017
1191
  myersDiff(state);
@@ -1026,13 +1200,18 @@ function listSynced(delta, oldState, newState, eq) {
1026
1200
  is = state.$pys;
1027
1201
  ie = state.$pye;
1028
1202
  } else break;
1029
- for (r = rs; r < re; r += 1) itemRemoved(delta, rs + offset, oldState[r]);
1203
+ for (r = rs; r < re; r += 1) {
1204
+ itemRemoved(delta, rs + offset, oldState[r]);
1205
+ hasChanges = true;
1206
+ }
1030
1207
  for (i = is; i < ie; i += 1) {
1031
1208
  itemInserted(delta, rs + offset, newState[i]);
1209
+ hasChanges = true;
1032
1210
  offset += 1;
1033
1211
  }
1034
1212
  offset -= re - rs;
1035
1213
  } while (state.$c < 2);
1214
+ return hasChanges;
1036
1215
  }
1037
1216
  const latest = (changes, index, mid, _min, max) => {
1038
1217
  let i = mid;
@@ -1176,53 +1355,54 @@ function myersDiff(state) {
1176
1355
 
1177
1356
  //#endregion
1178
1357
  //#region src/data/ProxyAtom.ts
1179
- /**
1180
- * Creates a ProxyAtom bound to the provided initial value. If it is an Atom, the proxy will mirror
1181
- * it, otherwise it will be a read-only atom with a static value until rebound.
1182
- */
1183
1358
  function proxyOf(initialValue, lifecycle = getLifecycle()) {
1184
- __DEV__assertNotEffect();
1185
1359
  const self = {
1186
1360
  [S_ATOM]: true,
1187
1361
  $lifecycle: lifecycle,
1188
- use,
1189
- get: getStaticValue
1362
+ use
1190
1363
  };
1191
- self.use(initialValue, false);
1364
+ self.use(initialValue, true);
1192
1365
  return self;
1193
1366
  }
1194
- function use(value, canNotify = true) {
1195
- if (this.$dep) unlink(this.$dep);
1196
- const oldValue = this.get();
1367
+ function use(value, isInit = false) {
1368
+ if (value === this) throw new Error("ProxyAtom cannot proxy itself");
1369
+ const upstream = this.$upstream;
1370
+ if (upstream && upstream === value) return;
1371
+ let shouldNotify = true;
1197
1372
  if (isAtom(value)) {
1198
- this.$bound = value;
1199
- this.$value = null;
1200
- this.get = getBoundValue;
1201
- this.set = setBoundValue;
1373
+ if (!upstream || isInit) {
1374
+ this.$value = null;
1375
+ this.$tracksValue = false;
1376
+ this.$lastValue = null;
1377
+ this.get = getUpstreamValue$1;
1378
+ this.set = setUpstreamValue;
1379
+ } else unlink(this.$dep);
1380
+ this.$upstream = value;
1202
1381
  link(this.$lifecycle, value, this.$dep ??= {
1203
- $fn: notify$1,
1204
- $a0: this
1382
+ fn: scheduleAtomNotification,
1383
+ a0: this
1205
1384
  });
1206
1385
  } else {
1207
- this.$bound = null;
1386
+ if (upstream || isInit) {
1387
+ this.$upstream = null;
1388
+ this.$tracksValue = true;
1389
+ this.$lastValue = UNIQUE_VALUE;
1390
+ this.get = getStaticValue;
1391
+ this.set = setStaticValue;
1392
+ if (!isInit) unlink(this.$dep);
1393
+ } else shouldNotify = !Object.is(this.$value, value);
1208
1394
  this.$value = value;
1209
- this.get = getStaticValue;
1210
- this.set = setStaticValue;
1211
1395
  }
1212
- if (canNotify && !Object.is(oldValue, this.get())) scheduleTick(this.$lifecycle, this.$notify ??= {
1213
- $fn: notify$1,
1214
- $a0: this
1215
- });
1396
+ if (this.$dh) {
1397
+ if (shouldNotify) scheduleAtomNotification(this);
1398
+ } else if (this.$tracksValue) this.$lastValue = value;
1216
1399
  }
1217
- function getBoundValue() {
1218
- return this.$bound.get();
1400
+ function getUpstreamValue$1() {
1401
+ return this.$upstream.get();
1219
1402
  }
1220
- function setBoundValue(value) {
1221
- const atom = this.$bound;
1222
- if (atom.set(value)) scheduleTick(atom.$lifecycle, atom.$notify ??= {
1223
- $fn: notify$1,
1224
- $a0: atom
1225
- });
1403
+ function setUpstreamValue(value) {
1404
+ const atom = this.$upstream;
1405
+ if (atom.set(value) && atom.$dh) scheduleAtomNotification(atom);
1226
1406
  return false;
1227
1407
  }
1228
1408
  function getStaticValue() {
@@ -1231,16 +1411,9 @@ function getStaticValue() {
1231
1411
  function setStaticValue() {
1232
1412
  return false;
1233
1413
  }
1234
- const EMPTY_SOURCE = {};
1235
- /**
1236
- * Copies select keys from a data object into a new object where values are all wrapped in
1237
- * ProxyAtoms and can be updated later.
1238
- * @see {@link updateProxy}
1239
- * @internal
1240
- **/
1241
1414
  function createProxy(lifecycle, data, keys) {
1242
- const source = isObject(data) ? data : EMPTY_SOURCE;
1243
- const proxy = {};
1415
+ const source = isObject(data) ? data : UNIQUE_VALUE;
1416
+ const proxy = Object.create(null);
1244
1417
  const { length } = keys;
1245
1418
  let index = 0;
1246
1419
  let key;
@@ -1251,14 +1424,8 @@ function createProxy(lifecycle, data, keys) {
1251
1424
  proxy.proxied = data;
1252
1425
  return proxy;
1253
1426
  }
1254
- /**
1255
- * Updates the values proxied by a previously created proxy object. The keys array must be the same
1256
- * as was used during creation (or a subset).
1257
- * @see {@link createProxy}
1258
- * @internal
1259
- **/
1260
1427
  function updateProxy(proxy, data, keys) {
1261
- const source = isObject(data) ? data : EMPTY_SOURCE;
1428
+ const source = isObject(data) ? data : UNIQUE_VALUE;
1262
1429
  const { length } = keys;
1263
1430
  proxy.proxied = data;
1264
1431
  let index = 0;
@@ -1272,31 +1439,69 @@ function isObject(value) {
1272
1439
  return value !== null && typeof value === "object";
1273
1440
  }
1274
1441
 
1442
+ //#endregion
1443
+ //#region src/support/template.ts
1444
+ function Template(jsx, hParent, hBefore, nUsedParent, nRealParent, isBatch) {
1445
+ mountJsx(jsx.t?.(jsx.d), hParent, hBefore, nUsedParent, nRealParent, isBatch);
1446
+ }
1447
+ const REUSED_JSX = { [S_COMPONENT]: Template };
1448
+ function mountTemplate(template, data, hParent, hBefore, nUsedParent, nRealParent, isBatch) {
1449
+ REUSED_JSX.t = template;
1450
+ REUSED_JSX.d = data;
1451
+ try {
1452
+ mount(REUSED_JSX, hParent, hBefore, nUsedParent, nRealParent, isBatch);
1453
+ } finally {
1454
+ REUSED_JSX.t = null;
1455
+ REUSED_JSX.d = null;
1456
+ }
1457
+ }
1458
+
1275
1459
  //#endregion
1276
1460
  //#region src/component/Iterator.ts
1277
- function Iterator(jsx, hParent, nUsedParent, nRealParent, nBefore, isBatch) {
1461
+ function Iterator(jsx, hParent, hBefore, nUsedParent, nRealParent, isBatch) {
1278
1462
  const source = jsx.source;
1279
1463
  const proxyKeys = jsx.proxy;
1280
1464
  const isProxy = proxyKeys !== void 0;
1281
1465
  const template = jsx.children[0];
1282
- const hGroup = fork(hParent);
1283
- const context = getContextContainer();
1466
+ const hGroup = fork(hParent, hBefore);
1284
1467
  const { adapter } = hGroup;
1285
- const shouldBatch = Boolean(adapter.batch);
1468
+ const shouldBatch = !!adapter.batch;
1286
1469
  const nListEndMarker = adapter.marker("/Iterator");
1287
- let items;
1288
- let skipDelta = Boolean(source.$delta);
1289
- const onDelta = () => {
1290
- if (skipDelta) {
1291
- skipDelta = false;
1470
+ let renderedItems;
1471
+ let hListEndMarker = null;
1472
+ const renderInitialItems = (initItems, hInitBefore, nInitParent, initIsBatch) => {
1473
+ let nBatchParent = nInitParent;
1474
+ const isLocalBatch = shouldBatch && !initIsBatch && initItems.length > 0;
1475
+ const isAnyBatch = isLocalBatch || initIsBatch;
1476
+ if (isLocalBatch) nBatchParent = adapter.batch();
1477
+ let errors;
1478
+ renderedItems = initItems.map((data) => {
1479
+ const item = createItem(hGroup, hListEndMarker ?? hInitBefore, nBatchParent, isAnyBatch, data, proxyKeys);
1480
+ try {
1481
+ mountTemplate(template, item.$data, item, hInitBefore, nBatchParent, nRealParent, isAnyBatch);
1482
+ } catch (ex) {
1483
+ (errors ??= []).push(ex);
1484
+ }
1485
+ return item;
1486
+ });
1487
+ if (isLocalBatch) {
1488
+ adapter.insert(nBatchParent, nInitParent, hInitBefore?.$nn ?? null);
1489
+ errors = flushItems(renderedItems, 0, renderedItems.length, errors);
1490
+ }
1491
+ rethrow(errors);
1492
+ };
1493
+ source.delta(willMount(hGroup, (delta) => {
1494
+ if (!renderedItems) {
1495
+ renderInitialItems(delta.snapshot, hListEndMarker, nRealParent, false);
1292
1496
  return;
1293
1497
  }
1294
- const { changes, lengthChange } = source.$delta;
1498
+ const { changes, relSize } = delta;
1295
1499
  const cMax = changes.length;
1296
- const iMax = items.length + lengthChange;
1297
- const newItems = new Array(iMax);
1298
- const pending = [];
1299
- let recycled = [];
1500
+ const iMax = renderedItems.length + relSize;
1501
+ let newItems = renderedItems;
1502
+ let errors;
1503
+ let pending;
1504
+ let recycled;
1300
1505
  let item;
1301
1506
  let inserted = 0;
1302
1507
  let ci = 0;
@@ -1307,196 +1512,348 @@ function Iterator(jsx, hParent, nUsedParent, nRealParent, nBefore, isBatch) {
1307
1512
  let tmp;
1308
1513
  let isLocalBatch = false;
1309
1514
  let nBatchParent = nRealParent;
1310
- let nBatchBefore = null;
1515
+ let batchStart = 0;
1311
1516
  for (; ci < cMax; ci += 1) {
1312
1517
  change = changes[ci];
1313
- while (ni < change.index) newItems[ni++] = items[oi++];
1518
+ if (newItems === renderedItems) if (change.kind === 1) oi = ni = change.index;
1519
+ else {
1520
+ newItems = new Array(iMax);
1521
+ oi = ni = 0;
1522
+ }
1523
+ while (ni < change.index) newItems[ni++] = renderedItems[oi++];
1314
1524
  switch (change.kind) {
1315
1525
  case 1:
1316
- item = newItems[ni++] = items[oi++];
1526
+ item = newItems[ni++] = renderedItems[oi++];
1317
1527
  if (isProxy) updateProxy(item.$data, change.newItem, proxyKeys);
1318
1528
  else {
1319
- unmount(item);
1320
- ref = items[oi]?.$marker ?? nListEndMarker;
1321
- insert(item.$marker, null, item, nRealParent, ref, false);
1322
- setContextContainer(context);
1323
- mount(withLifecycle(item, template, item.$data = change.newItem), item, nRealParent, nRealParent, ref, false);
1529
+ try {
1530
+ unmount(item);
1531
+ } catch (ex) {
1532
+ (errors ??= []).push(ex);
1533
+ }
1534
+ ref = renderedItems[oi]?.$marker ?? hListEndMarker;
1535
+ try {
1536
+ mountTemplate(template, item.$data = change.newItem, item, ref, nRealParent, nRealParent, false);
1537
+ } catch (ex) {
1538
+ (errors ??= []).push(ex);
1539
+ }
1324
1540
  }
1325
1541
  break;
1326
1542
  case 2:
1327
- if (!isProxy || inserted < lengthChange) {
1328
- item = newItems[ni++] = fork(hGroup, items[oi]);
1329
- item.$marker = adapter.marker("IteratorItem");
1330
- item.$data = isProxy ? createProxy(item, change.newItem, proxyKeys) : change.newItem;
1543
+ if (!isProxy || inserted < relSize) {
1544
+ ref = renderedItems[oi]?.$marker ?? hListEndMarker;
1545
+ if (!isLocalBatch) {
1546
+ if (shouldBatch) {
1547
+ isLocalBatch = true;
1548
+ batchStart = ni;
1549
+ nBatchParent = adapter.batch();
1550
+ }
1551
+ }
1331
1552
  inserted += 1;
1332
- ref = items[oi]?.$marker ?? nListEndMarker;
1333
- if (!isLocalBatch) if (shouldBatch) {
1334
- isLocalBatch = true;
1335
- nBatchParent = adapter.batch();
1336
- nBatchBefore = null;
1337
- } else nBatchBefore = ref;
1338
- insert(item.$marker, null, item, nBatchParent, nBatchBefore, isLocalBatch);
1339
- setContextContainer(context);
1340
- mount(withLifecycle(item, template, item.$data), item, nBatchParent, nRealParent, nBatchBefore, isLocalBatch);
1341
- if (isLocalBatch && (inserted >= lengthChange || !(tmp = changes[ci + 1]) || tmp.kind !== 2 || tmp.index !== ni)) {
1342
- adapter.insert(nBatchParent, nRealParent, ref);
1553
+ item = newItems[ni++] = createItem(hGroup, renderedItems[oi]?.$marker ?? hListEndMarker, nBatchParent, isLocalBatch, change.newItem, proxyKeys);
1554
+ try {
1555
+ mountTemplate(template, item.$data, item, ref, nBatchParent, nRealParent, isLocalBatch);
1556
+ } catch (ex) {
1557
+ (errors ??= []).push(ex);
1558
+ }
1559
+ if (isLocalBatch && (inserted >= relSize || !(tmp = changes[ci + 1]) || tmp.kind !== 2 || tmp.index !== ni)) {
1560
+ adapter.insert(nBatchParent, nRealParent, ref.$nn);
1561
+ errors = flushItems(newItems, batchStart, ni, errors);
1343
1562
  nBatchParent = nRealParent;
1344
1563
  isLocalBatch = false;
1345
1564
  }
1346
- } else pending.push({
1347
- $index: ni++,
1348
- $item: change.newItem
1349
- });
1565
+ } else {
1566
+ (pending ??= []).push(ci);
1567
+ ni += 1;
1568
+ }
1350
1569
  break;
1351
1570
  case 3:
1352
- item = items[oi++];
1353
- if (isProxy) recycled.push(item);
1354
- else unmount(item);
1355
- untrack(item);
1571
+ item = renderedItems[oi++];
1572
+ if (isProxy) {
1573
+ (recycled ??= []).push(item);
1574
+ untrackItem(item);
1575
+ } else try {
1576
+ destroyItem(item);
1577
+ } catch (ex) {
1578
+ (errors ??= []).push(ex);
1579
+ }
1356
1580
  break;
1357
1581
  case 4:
1358
1582
  if (isProxy) {
1359
- recycled = items;
1360
- recycled.forEach(untrack);
1361
- oi = items.length;
1362
- } else while (oi < items.length) {
1363
- item = items[oi++];
1364
- unmount(item);
1365
- untrack(item);
1583
+ recycled = renderedItems;
1584
+ recycled.forEach(untrackItem);
1585
+ oi = renderedItems.length;
1586
+ } else while (oi < renderedItems.length) {
1587
+ item = renderedItems[oi++];
1588
+ try {
1589
+ destroyItem(item);
1590
+ } catch (ex) {
1591
+ (errors ??= []).push(ex);
1592
+ }
1366
1593
  }
1367
1594
  break;
1368
1595
  }
1369
1596
  }
1370
- while (ni < iMax) newItems[ni++] = items[oi++];
1371
- let pi = pending.length - 1;
1372
- let ri = recycled.length - 1;
1597
+ if (newItems !== renderedItems) while (ni < iMax) newItems[ni++] = renderedItems[oi++];
1598
+ let pi = pending ? pending.length - 1 : -1;
1599
+ let ri = recycled ? recycled.length - 1 : -1;
1373
1600
  while (pi >= 0) {
1374
- tmp = pending[pi--];
1375
- item = newItems[tmp.$index] = recycled[ri--];
1376
- updateProxy(item.$data, tmp.$item, proxyKeys);
1377
- track(item, hGroup, ref = newItems[tmp.$index + 1]);
1378
- setContextContainer(context);
1379
- mount(withLifecycle(item, template, item.$data), item, nRealParent, nRealParent, ref?.$marker ?? nListEndMarker, false);
1601
+ tmp = changes[pending[pi--]];
1602
+ item = newItems[tmp.index] = recycled[ri--];
1603
+ updateProxy(item.$data, tmp.newItem, proxyKeys);
1604
+ ref = newItems[tmp.index + 1];
1605
+ track(item.$marker, hGroup, ref?.$marker ?? hListEndMarker);
1606
+ track(item, hGroup, ref?.$marker ?? hListEndMarker);
1607
+ adapter.insert(item.$marker.$nn, nRealParent, ref?.$marker.$nn ?? nListEndMarker);
1608
+ reinsertNodes(item, ref?.$marker ?? hListEndMarker, nRealParent);
1380
1609
  }
1381
1610
  while (ri >= 0) {
1382
1611
  item = recycled[ri--];
1383
- unmount(item);
1384
- untrack(item);
1385
- }
1386
- items = newItems;
1387
- };
1388
- link(hGroup, source, { $fn: onDelta });
1389
- {
1390
- let nBatchParent = nUsedParent;
1391
- let nBatchBefore = nBefore;
1392
- const isLocalBatch = shouldBatch && !isBatch && source.$items.length > 0;
1393
- if (isLocalBatch) {
1394
- nBatchParent = adapter.batch();
1395
- nBatchBefore = null;
1612
+ try {
1613
+ destroyItem(item);
1614
+ } catch (ex) {
1615
+ (errors ??= []).push(ex);
1616
+ }
1396
1617
  }
1397
- const isAnyBatch = isLocalBatch || isBatch;
1398
- items = source.$items.map((data) => {
1399
- const item = fork(hGroup);
1400
- item.$marker = adapter.marker("IteratorItem");
1401
- item.$data = isProxy ? createProxy(item, data, proxyKeys) : data;
1402
- insert(item.$marker, null, item, nBatchParent, nBatchBefore, isAnyBatch);
1403
- mount(withLifecycle(item, template, item.$data), item, nBatchParent, nRealParent, nBatchBefore, isAnyBatch);
1404
- return item;
1405
- });
1406
- insert(nListEndMarker, null, hGroup, nBatchParent, nBatchBefore, isAnyBatch);
1407
- if (isLocalBatch) adapter.insert(nBatchParent, nUsedParent, nBefore);
1618
+ renderedItems = newItems;
1619
+ rethrow(errors);
1620
+ }), hGroup);
1621
+ if (!source.dirty()) source.stable((snapshot) => {
1622
+ renderInitialItems(snapshot, hBefore, nUsedParent, isBatch);
1623
+ });
1624
+ hListEndMarker = insert(nListEndMarker, null, hGroup, hBefore, nUsedParent, isBatch, true);
1625
+ mount(null, hGroup, hBefore, nUsedParent, nRealParent, isBatch);
1626
+ }
1627
+ function flushItems(items, start, end, errors) {
1628
+ let index = start;
1629
+ for (; index < end; index += 1) try {
1630
+ flush(items[index]);
1631
+ } catch (ex) {
1632
+ (errors ??= []).push(ex);
1633
+ }
1634
+ return errors;
1635
+ }
1636
+ function createItem(hParent, hBefore, nParent, isBatch, data, proxyKeys) {
1637
+ const marker = insert(hParent.adapter.marker("IteratorItem"), null, hParent, hBefore, nParent, isBatch, true);
1638
+ const item = fork(hParent, hBefore, hParent.$context);
1639
+ item.$marker = marker;
1640
+ item.$data = proxyKeys !== void 0 ? createProxy(item, data, proxyKeys) : data;
1641
+ return item;
1642
+ }
1643
+ function untrackItem(item) {
1644
+ untrack(item.$marker);
1645
+ untrack(item);
1646
+ }
1647
+ function destroyItem(item) {
1648
+ try {
1649
+ destroy(item);
1650
+ } finally {
1651
+ const marker = item.$marker;
1652
+ item.adapter.remove(marker.$nn);
1653
+ untrack(marker);
1654
+ marker.$ph = null;
1408
1655
  }
1409
- mount(null, hGroup, nUsedParent, nRealParent, nBefore, isBatch);
1410
1656
  }
1411
1657
 
1412
1658
  //#endregion
1413
1659
  //#region src/component/Native.ts
1414
- const RE_EXT = /^([^:]+?):(.+)$/;
1415
- /** @internal */
1416
- const S_TAG_NAME = Symbol.for("pyxis:tagName");
1417
- function Native(jsx, hParent, nUsedParent, _nRealParent, nBefore, isBatch) {
1660
+ function Native(jsx, hParent, hBefore, nUsedParent, _nRealParent, isBatch) {
1418
1661
  const hGroup = hParent.$ng;
1419
1662
  const { adapter, $extensions } = hGroup;
1420
1663
  const nNode = adapter.element(jsx[S_TAG_NAME]);
1421
1664
  let name;
1422
- let match;
1665
+ let index;
1423
1666
  let value;
1667
+ let target;
1424
1668
  for (name in jsx) {
1425
- match = RE_EXT.exec(name);
1669
+ index = name.indexOf(":");
1426
1670
  value = jsx[name];
1427
- if (match) $extensions[match[1]]?.set(nNode, match[2], value, hGroup);
1428
- else if (name !== "children") if (isAtom(value)) {
1429
- const prop = name;
1430
- const atom = value;
1431
- bind(hGroup, atom, () => {
1432
- adapter.set(nNode, prop, atom.get());
1433
- });
1434
- } else adapter.set(nNode, name, value);
1671
+ if (index === 0) if (target = runtime.x[name]) target.$ext.set(nNode, target.$prop, value, hGroup);
1672
+ else throw new Error(`invalid reserved prop "${name}"`);
1673
+ else if (index > 0) $extensions[name.slice(0, index)]?.set(nNode, name.slice(index + 1), value, hGroup);
1674
+ else if (name !== "children") {
1675
+ if (isAtom(value)) {
1676
+ link(hGroup, value, {
1677
+ fn: setBoundProp,
1678
+ a0: adapter,
1679
+ a1: nNode,
1680
+ a2: name
1681
+ });
1682
+ value = value.get();
1683
+ }
1684
+ adapter.set(nNode, name, value);
1685
+ }
1435
1686
  }
1436
- insert(nNode, jsx.children, hParent, nUsedParent, nBefore, isBatch);
1687
+ insert(nNode, jsx.children, hParent, hBefore, nUsedParent, isBatch);
1688
+ return nNode;
1689
+ }
1690
+ function setBoundProp(adapter, node, prop) {
1691
+ adapter.set(node, prop, this.$target.get());
1437
1692
  }
1438
1693
 
1439
1694
  //#endregion
1440
1695
  //#region src/component/Show.ts
1441
- function Show(jsx, hParent, nUsedParent, nRealParent, nBefore, isBatch) {
1442
- const when = jsx.when;
1696
+ function Show(props, hParent, hBefore, nUsedParent, nRealParent, isBatch) {
1697
+ const when = props.when;
1443
1698
  if (!isAtom(when) && when === false) return;
1444
- const { children, data } = jsx;
1445
- const proxyKeys = jsx.proxy;
1446
- const template = Object.hasOwn(jsx, "data") ? children[0] : () => children;
1699
+ const { children, data } = props;
1700
+ const proxyKeys = props.proxy;
1701
+ const template = Object.hasOwn(props, "data") ? children[0] : () => children;
1447
1702
  let dataOrProxy = data;
1448
1703
  if (proxyKeys) if (isAtom(data)) {
1449
1704
  dataOrProxy = createProxy(hParent.$ng, data.get(), proxyKeys);
1450
- link(hParent.$ng, data, { $fn: () => {
1451
- updateProxy(dataOrProxy, data.get(), proxyKeys);
1452
- } });
1705
+ link(hParent.$ng, data, {
1706
+ fn: updateShowProxy,
1707
+ a0: dataOrProxy,
1708
+ a1: data,
1709
+ a2: proxyKeys
1710
+ });
1453
1711
  } else dataOrProxy = createProxy(hParent.$ng, data, proxyKeys);
1454
1712
  const adapter = hParent.$ng.adapter;
1455
- const shouldBatch = Boolean(adapter.batch);
1713
+ const shouldBatch = !!adapter.batch;
1456
1714
  if (!(isAtom(when) || isAtom(template) || !proxyKeys && isAtom(data))) {
1457
1715
  const isLocalBatch = shouldBatch && !isBatch;
1458
1716
  let nBatchParent = nUsedParent;
1459
- let nBatchBefore = nBefore;
1460
- if (isLocalBatch) {
1461
- nBatchParent = adapter.batch();
1462
- nBatchBefore = null;
1463
- }
1464
- mountJsx(template(dataOrProxy), hParent, nBatchParent, nRealParent, nBatchBefore, isLocalBatch || isBatch);
1465
- if (isLocalBatch) adapter.insert(nBatchParent, nUsedParent, nBefore);
1717
+ if (isLocalBatch) nBatchParent = adapter.batch();
1718
+ mountJsx(template(dataOrProxy), hParent, hBefore, nBatchParent, nRealParent, isLocalBatch || isBatch);
1719
+ if (isLocalBatch) adapter.insert(nBatchParent, nUsedParent, hBefore?.$nn ?? null);
1466
1720
  return;
1467
1721
  }
1468
- const hGroup = fork(hParent);
1469
- const context = getContextContainer();
1470
- let nMarker = nBefore;
1471
- effect(() => {
1722
+ const hGroup = fork(hParent, hBefore);
1723
+ const dispose = () => unmount(hGroup);
1724
+ let hMarker = hBefore;
1725
+ effect(willMount(hGroup, () => {
1472
1726
  if (read(when) === false) return;
1473
1727
  const isLocalBatch = shouldBatch && !isBatch;
1474
1728
  let nBatchParent = nUsedParent;
1475
- let nBatchBefore = nMarker;
1729
+ if (isLocalBatch) nBatchParent = adapter.batch();
1730
+ mountTemplate(read(template), read(dataOrProxy), hGroup, hMarker, nBatchParent, nRealParent, isLocalBatch || isBatch);
1476
1731
  if (isLocalBatch) {
1477
- nBatchParent = adapter.batch();
1478
- nBatchBefore = null;
1732
+ adapter.insert(nBatchParent, nUsedParent, hMarker?.$nn ?? null);
1733
+ try {
1734
+ flush(hGroup);
1735
+ } catch (ex) {
1736
+ unmount(hGroup, [ex]);
1737
+ }
1479
1738
  }
1480
- setContextContainer(context);
1481
- mount(withLifecycle(hGroup, read(template), read(dataOrProxy)), hGroup, nBatchParent, nRealParent, nBatchBefore, isLocalBatch || isBatch);
1482
- if (isLocalBatch) adapter.insert(nBatchParent, nUsedParent, nMarker);
1483
- return () => unmount(hGroup);
1484
- }, hParent.$ng);
1485
- nMarker = adapter.marker("/Show");
1486
- insert(nMarker, null, hParent, nUsedParent, nBefore, isBatch);
1739
+ return dispose;
1740
+ }), hParent.$ng);
1741
+ hMarker = insert(adapter.marker("/Show"), null, hParent, hBefore, nUsedParent, isBatch, true);
1487
1742
  isBatch = false;
1488
1743
  nUsedParent = nRealParent;
1489
1744
  }
1745
+ function updateShowProxy(proxy, source, keys) {
1746
+ updateProxy(proxy, source.get(), keys);
1747
+ }
1748
+
1749
+ //#endregion
1750
+ //#region src/data/Context.ts
1751
+ /*@__NO_SIDE_EFFECTS__*/
1752
+ function contextOf() {
1753
+ {
1754
+ const devId = arguments[0];
1755
+ const $symbol = runtime.$hmrState.restore(contextOf, devId) ?? Symbol();
1756
+ runtime.$hmrState.preserve(contextOf, devId, $symbol);
1757
+ return { $symbol };
1758
+ }
1759
+ }
1760
+ function host(context, defaultValue, lifecycle = getLifecycle()) {
1761
+ __DEV__assertIsDirectComponentCode(host.name);
1762
+ if (!runtime.$currentContextNew || !runtime.$currentContext) {
1763
+ runtime.$currentContextNew = true;
1764
+ runtime.$currentContext = { $parent: runtime.$currentContext };
1765
+ }
1766
+ let isUndefined = defaultValue === void 0;
1767
+ {
1768
+ const devId = arguments[3];
1769
+ runtime.$hmrState.restore(lifecycle, devId, (value) => {
1770
+ defaultValue = value;
1771
+ isUndefined = false;
1772
+ });
1773
+ }
1774
+ const localAtom = {
1775
+ [S_ATOM]: true,
1776
+ $lifecycle: lifecycle,
1777
+ $value: defaultValue,
1778
+ get: getHostedValue,
1779
+ set: setHostedValue
1780
+ };
1781
+ let upstream;
1782
+ if (isUndefined && (upstream = lookupUpstream(context))) {
1783
+ localAtom.get = getUpstreamValue;
1784
+ localAtom.$upstream = upstream;
1785
+ link(lifecycle, upstream, localAtom.$dep = {
1786
+ fn: scheduleAtomNotification,
1787
+ a0: localAtom
1788
+ });
1789
+ } else {
1790
+ localAtom.$tracksValue = true;
1791
+ localAtom.$lastValue = defaultValue;
1792
+ localAtom.$defined = !isUndefined;
1793
+ }
1794
+ localAtom.$devId = arguments[3];
1795
+ if (Object.hasOwn(runtime.$currentContext, context.$symbol)) throw new Error("Component declares multiple hosts of the same Context.");
1796
+ runtime.$currentContext[context.$symbol] = localAtom;
1797
+ return localAtom;
1798
+ }
1799
+ function contextual(context) {
1800
+ __DEV__assertIsDirectComponentCode(contextual.name);
1801
+ const atom = lookupUpstream(context);
1802
+ {
1803
+ if (!atom) throw new Error("No ancestor is hosting this Context.");
1804
+ let consumer = atom.$consumer;
1805
+ if (!consumer) {
1806
+ consumer = atom.$consumer = {
1807
+ [S_ATOM]: true,
1808
+ $lifecycle: atom.$lifecycle,
1809
+ $upstream: atom,
1810
+ get: getUpstreamValue,
1811
+ set: __DEV__setConsumerValue
1812
+ };
1813
+ link(atom.$lifecycle, atom, {
1814
+ fn: scheduleAtomNotification,
1815
+ a0: consumer
1816
+ });
1817
+ }
1818
+ return consumer;
1819
+ }
1820
+ }
1821
+ function lookupUpstream(context) {
1822
+ const { $symbol } = context;
1823
+ let ptr = runtime.$currentContext;
1824
+ let atom;
1825
+ while (ptr && !(atom = ptr[$symbol])) ptr = ptr.$parent;
1826
+ return atom;
1827
+ }
1828
+ function getUpstreamValue() {
1829
+ return this.$upstream.get();
1830
+ }
1831
+ function getHostedValue() {
1832
+ if (!this.$defined) throw new Error("Cannot read Context before it has been initialized.");
1833
+ return this.$value;
1834
+ }
1835
+ function setHostedValue(value) {
1836
+ let didChange = true;
1837
+ if (this.$upstream) {
1838
+ unlink(this.$dep);
1839
+ this.$upstream = null;
1840
+ this.$dep = null;
1841
+ this.$tracksValue = true;
1842
+ this.$lastValue = UNIQUE_VALUE;
1843
+ this.get = getHostedValue;
1844
+ } else didChange = !Object.is(this.$value, value);
1845
+ this.$value = value;
1846
+ this.$defined = true;
1847
+ runtime.$hmrState.preserve(this.$lifecycle, this.$devId, value);
1848
+ return didChange;
1849
+ }
1850
+ function __DEV__setConsumerValue() {
1851
+ throw new Error("Contextual Atoms cannot be written.");
1852
+ }
1490
1853
 
1491
1854
  //#endregion
1492
1855
  //#region src/data/Derivation.ts
1493
- /**
1494
- * Creates a Derivation - an Atom with its value computed from other Atoms. The block runs once
1495
- * eagerly to compute the initial value, then re-runs within scheduler ticks whenever its source
1496
- * Atoms change. Observers are only notified if the new value differs from the previous.
1497
- */
1498
1856
  function derived(block, lifecycle = getLifecycle()) {
1499
- __DEV__assertNotEffect();
1500
1857
  const atom = {
1501
1858
  [S_ATOM]: true,
1502
1859
  $dirty: false,
@@ -1507,7 +1864,7 @@ function derived(block, lifecycle = getLifecycle()) {
1507
1864
  $lastValue: null,
1508
1865
  $cycle: 0,
1509
1866
  $block: block,
1510
- $react: scheduleNotify,
1867
+ $react: scheduleReaction,
1511
1868
  get: getValue,
1512
1869
  set: setValue
1513
1870
  };
@@ -1516,16 +1873,13 @@ function derived(block, lifecycle = getLifecycle()) {
1516
1873
  atom.$lastValue = value;
1517
1874
  return atom;
1518
1875
  }
1519
- function scheduleNotify(derivation, cycle) {
1876
+ function scheduleReaction(derivation, cycle) {
1520
1877
  if (derivation.$life !== derivation.$lifecycle.$life || derivation.$cycle > cycle) {
1521
- unlink(this);
1878
+ unlinkEffectDep(derivation, this);
1522
1879
  return;
1523
1880
  }
1524
1881
  derivation.$dirty = true;
1525
- scheduleTick(derivation.$lifecycle, derivation.$notify ??= {
1526
- $fn: notify$1,
1527
- $a0: derivation
1528
- });
1882
+ scheduleAtomNotification(derivation);
1529
1883
  }
1530
1884
  function getValue() {
1531
1885
  if (this.$dirty && this.$life === this.$lifecycle.$life) {
@@ -1542,22 +1896,25 @@ function setValue() {
1542
1896
  //#region src/data/List.ts
1543
1897
  function listOf(source, lifecycle = getLifecycle()) {
1544
1898
  {
1545
- __DEV__assertNotEffect();
1546
1899
  const devId = arguments[2];
1547
- globalThis.__PYXIS_HMR__.state.restore(lifecycle, devId, (value) => {
1900
+ runtime.$hmrState.restore(lifecycle, devId, (value) => {
1548
1901
  if (Array.isArray(value)) source = value;
1549
1902
  });
1550
1903
  }
1551
1904
  const list = {
1552
- $lifecycle: lifecycle,
1553
1905
  $items: source ? Array.from(source) : [],
1554
- $delta: null,
1906
+ $lifecycle: lifecycle,
1907
+ $committedDelta: null,
1908
+ $pendingDelta: null,
1909
+ $lock: 0,
1555
1910
  [Symbol.iterator]: getIterator,
1556
1911
  size,
1557
1912
  get,
1913
+ dirty,
1558
1914
  delta,
1559
1915
  raw,
1560
1916
  forEach,
1917
+ stable,
1561
1918
  set,
1562
1919
  clear,
1563
1920
  insertAt,
@@ -1571,19 +1928,17 @@ function listOf(source, lifecycle = getLifecycle()) {
1571
1928
  list.$devId = arguments[2];
1572
1929
  return list;
1573
1930
  }
1574
- /**
1575
- * Synchronizes the provided List with the given data source. After this operation, the list will
1576
- * contain an exact copy of the source.
1577
- *
1578
- * Note: This function is separated instead of being a List method since the underlying diff
1579
- * algorithm (Myers) is a relatively large chunk of code which would otherwise always get included
1580
- * into bundled builds. This way, tools like Terser can eliminate the extra code when unused.
1581
- */
1582
- function sync(list, source, eq = defaultEquals) {
1583
- const oldState = list.$items;
1584
- list.$items = source.slice();
1585
- listSynced(list.$delta ??= createDelta(), oldState, source, eq);
1586
- listMutated(list);
1931
+ function sync(list, source, eq = Object.is) {
1932
+ const oldItems = list.$items;
1933
+ const newItems = Array.from(source);
1934
+ list.$items = newItems;
1935
+ __DEV__preserveState(list);
1936
+ if (!list.$dh && !list.$xa) return;
1937
+ const delta = initDelta(list);
1938
+ if (listSynced(delta, oldItems, newItems, eq)) {
1939
+ list.$pendingDelta = delta;
1940
+ scheduleListNotification(list);
1941
+ }
1587
1942
  }
1588
1943
  function size() {
1589
1944
  reportAccess(this);
@@ -1593,16 +1948,35 @@ function get(index) {
1593
1948
  reportAccess(this);
1594
1949
  return this.$items[index];
1595
1950
  }
1596
- function delta() {
1597
- reportAccess(this);
1598
- return this.$delta;
1951
+ function dirty() {
1952
+ if (this.$xa) return true;
1953
+ const delta = this.$pendingDelta;
1954
+ if (!delta) return false;
1955
+ const pending = this.$notify;
1956
+ return (!pending || pending.$life === pending.$lifecycle.$life) && delta.changes.length > 0;
1957
+ }
1958
+ function delta(block, lifecycle = getLifecycle()) {
1959
+ link(lifecycle, this, {
1960
+ fn: invokeDeltaBlock,
1961
+ a0: this,
1962
+ a1: lifecycle,
1963
+ a2: block
1964
+ });
1599
1965
  }
1600
1966
  function raw() {
1601
1967
  return this.$items;
1602
1968
  }
1603
- function forEach(callback, thisArg) {
1969
+ function forEach(callback) {
1604
1970
  reportAccess(this);
1605
- this.$items.forEach(callback, thisArg);
1971
+ this.$items.forEach(callback);
1972
+ }
1973
+ function stable(block) {
1974
+ try {
1975
+ this.$lock += 1;
1976
+ return block(this.$items);
1977
+ } finally {
1978
+ this.$lock = Math.max(this.$lock - 1, 0);
1979
+ }
1606
1980
  }
1607
1981
  function getIterator() {
1608
1982
  reportAccess(this);
@@ -1612,30 +1986,39 @@ function set(index, newItem) {
1612
1986
  assertIndex(this, index);
1613
1987
  const oldItem = this.$items[index];
1614
1988
  if (Object.is(newItem, oldItem)) return;
1989
+ copyOnWrite(this);
1615
1990
  this.$items[index] = newItem;
1616
- if (this.$dh) {
1617
- itemChanged(this.$delta ??= createDelta(), index, oldItem, newItem);
1618
- listMutated(this);
1991
+ __DEV__preserveState(this);
1992
+ if (this.$dh || this.$xa) {
1993
+ itemChanged(this.$pendingDelta = initDelta(this), index, oldItem, newItem);
1994
+ scheduleListNotification(this);
1619
1995
  }
1620
1996
  }
1621
1997
  function clear() {
1622
1998
  const count = this.$items.length;
1623
- this.$items.length = 0;
1624
- if (this.$dh) {
1625
- listCleared(this.$delta ??= createDelta(), count);
1626
- listMutated(this);
1999
+ if (this.$lock > 0) {
2000
+ this.$items = [];
2001
+ this.$lock = 0;
2002
+ } else this.$items.length = 0;
2003
+ __DEV__preserveState(this);
2004
+ if (this.$dh || this.$xa) {
2005
+ listCleared(this.$pendingDelta = initDelta(this), count);
2006
+ scheduleListNotification(this);
1627
2007
  }
1628
2008
  }
1629
2009
  function insertAt(index, item) {
1630
- const { $items } = this;
1631
- if (index === $items.length) $items.push(item);
1632
- else {
2010
+ if (index === this.$items.length) {
2011
+ copyOnWrite(this);
2012
+ this.$items.push(item);
2013
+ } else {
1633
2014
  assertIndex(this, index);
2015
+ copyOnWrite(this);
1634
2016
  this.$items.splice(index, 0, item);
1635
2017
  }
1636
- if (this.$dh) {
1637
- itemInserted(this.$delta ??= createDelta(), index, item);
1638
- listMutated(this);
2018
+ __DEV__preserveState(this);
2019
+ if (this.$dh || this.$xa) {
2020
+ itemInserted(this.$pendingDelta = initDelta(this), index, item);
2021
+ scheduleListNotification(this);
1639
2022
  }
1640
2023
  }
1641
2024
  function insertFirst(item) {
@@ -1652,10 +2035,12 @@ function remove(item) {
1652
2035
  }
1653
2036
  function removeAt(index) {
1654
2037
  assertIndex(this, index);
2038
+ copyOnWrite(this);
1655
2039
  const item = this.$items.splice(index, 1)[0];
1656
- if (this.$dh) {
1657
- itemRemoved(this.$delta ??= createDelta(), index, item);
1658
- listMutated(this);
2040
+ __DEV__preserveState(this);
2041
+ if (this.$dh || this.$xa) {
2042
+ itemRemoved(this.$pendingDelta = initDelta(this), index, item);
2043
+ scheduleListNotification(this);
1659
2044
  }
1660
2045
  return item;
1661
2046
  }
@@ -1667,160 +2052,195 @@ function removeLast() {
1667
2052
  return length > 0 ? this.removeAt(length - 1) : void 0;
1668
2053
  }
1669
2054
  function assertIndex(list, index) {
2055
+ if (!Number.isInteger(index)) throw new TypeError("list index must be an integer");
1670
2056
  if (index >= list.$items.length || index < 0) throw new RangeError("list index out of bounds");
1671
2057
  }
1672
- function defaultEquals(item0, item1) {
1673
- return Object.is(item0, item1);
2058
+ function initDelta(list) {
2059
+ if (list.$pendingDelta) {
2060
+ const pending = list.$notify;
2061
+ if (!pending || pending.$life === pending.$lifecycle.$life) return list.$pendingDelta;
2062
+ }
2063
+ return {
2064
+ changes: [],
2065
+ relSize: 0
2066
+ };
1674
2067
  }
1675
- function listMutated(list) {
1676
- globalThis.__PYXIS_HMR__.state.preserve(list.$lifecycle, list.$devId, list.$items);
2068
+ function copyOnWrite(list) {
2069
+ if (list.$lock > 0) {
2070
+ list.$items = list.$items.slice();
2071
+ list.$lock = 0;
2072
+ }
2073
+ }
2074
+ function scheduleListNotification(list) {
1677
2075
  scheduleTick(list.$lifecycle, list.$notify ??= {
1678
- $fn: notify,
1679
- $a0: list
1680
- });
1681
- scheduleTock(list.$lifecycle, list.$cleanup ??= {
1682
- $fn: cleanup,
1683
- $a0: list
2076
+ fn: notifyListObservers,
2077
+ a0: list
1684
2078
  });
1685
2079
  }
1686
- function notify(list) {
1687
- let current = list.$dh;
1688
- let next;
1689
- while (current) {
1690
- next = current.$an;
1691
- invoke(current);
1692
- current = next;
2080
+ function notifyListObservers(list) {
2081
+ const delta = list.$pendingDelta;
2082
+ list.$pendingDelta = null;
2083
+ if (list.$force || delta.changes.length > 0) {
2084
+ delta.snapshot = list.$items;
2085
+ list.$committedDelta = delta;
2086
+ list.$lock += 1;
2087
+ list.$force = false;
2088
+ try {
2089
+ notify(list);
2090
+ } finally {
2091
+ list.$committedDelta = null;
2092
+ list.$lock = Math.max(list.$lock - 1, 0);
2093
+ }
1693
2094
  }
1694
2095
  }
1695
- function cleanup(list) {
1696
- list.$delta = null;
2096
+ function invokeDeltaBlock(list, lifecycle, block) {
2097
+ withLifecycle(lifecycle, block, list.$committedDelta);
2098
+ }
2099
+ function __DEV__preserveState(list) {
2100
+ runtime.$hmrState.preserve(list.$lifecycle, list.$devId, list.$items);
2101
+ }
2102
+
2103
+ //#endregion
2104
+ //#region src/Adapter.ts
2105
+ /*@__NO_SIDE_EFFECTS__*/
2106
+ function extension(propSetter) {
2107
+ return {
2108
+ $prefix: `:${(++runtime.$extIndex).toString(16)}:`,
2109
+ set: propSetter
2110
+ };
2111
+ }
2112
+ function ext(extension, extensionProps, targetPropsObject = {}) {
2113
+ const prefix = extension.$prefix;
2114
+ if (typeof prefix !== "string") throw new Error("the extension was not created through the `extension()` factory and cannot be used");
2115
+ const routing = runtime.x;
2116
+ let prop;
2117
+ let route;
2118
+ for (prop in extensionProps) {
2119
+ route = prefix + prop;
2120
+ routing[route] ??= {
2121
+ $ext: extension,
2122
+ $prop: prop
2123
+ };
2124
+ targetPropsObject[route] = extensionProps[prop];
2125
+ }
2126
+ return targetPropsObject;
1697
2127
  }
1698
2128
 
1699
2129
  //#endregion
1700
2130
  //#region src/extension/RefExtension.ts
1701
- /**
1702
- * Extension adding direct reference access to any element. Recommended prefix:
1703
- * `"ref"`
1704
- *
1705
- * References can be stored into atoms:
1706
- * ```tsx
1707
- * const wrapperRef = atomOf<HTMLDivElement>();
1708
- * <div ref:atom={wrapperRef} />
1709
- * ```
1710
- * or handled with a custom callback:
1711
- * ```tsx
1712
- * const onWrapperRef = (node: HTMLDivElement) => { ... };
1713
- * <div ref:call={onWrapperRef} />
1714
- * ```
1715
- */
1716
- const RefExtension = { set: (node, kind, value) => {
1717
- let method;
1718
- switch (kind) {
2131
+ const RefExtension = /*@__PURE__*/ extension((node, prop, value, group) => {
2132
+ switch (prop) {
1719
2133
  case "atom":
1720
- if (!isAtom(value)) return;
1721
- method = refAtom;
2134
+ if (isAtom(value)) {
2135
+ onceMounted(group, {
2136
+ fn: write,
2137
+ a0: value,
2138
+ a1: node
2139
+ });
2140
+ onceUnmounted(group, {
2141
+ fn: write,
2142
+ a0: value,
2143
+ a1: null
2144
+ });
2145
+ }
1722
2146
  break;
1723
2147
  case "call":
1724
- if (typeof value !== "function") return;
1725
- method = refCall;
2148
+ if (typeof value === "function") onceMounted(group, {
2149
+ fn: invokeMountedCallback,
2150
+ a0: group,
2151
+ a1: value,
2152
+ a2: node
2153
+ });
1726
2154
  break;
1727
- default: return;
1728
2155
  }
1729
- const lifecycle = getLifecycle();
1730
- onMounted(lifecycle, {
1731
- $fn: method,
1732
- $a0: value,
1733
- $a1: node
1734
- });
1735
- onUnmounted(lifecycle, {
1736
- $fn: method,
1737
- $a0: value,
1738
- $a1: null
1739
- });
1740
- } };
1741
- function refAtom(atom, node) {
1742
- atom.set(node);
1743
- }
1744
- function refCall(setter, node) {
1745
- setter(node);
1746
- }
2156
+ });
1747
2157
 
1748
2158
  //#endregion
1749
- //#region src/support/text.ts
1750
- function tag(access, strings, values) {
2159
+ //#region src/support/atoms.ts
2160
+ function text(strings, ...values) {
1751
2161
  const { length } = values;
1752
- let index = 0;
1753
- let text = strings[0];
1754
- while (index < length) text += access(values[index]) + strings[++index];
1755
- return text;
1756
- }
1757
- /**
1758
- * A template literal tag that automatically wraps each substitution in a `read` call.
1759
- * @see {@link read}
1760
- */
1761
- function reads(strings, ...values) {
1762
- return tag(read, strings, values);
1763
- }
1764
- /**
1765
- * A template literal tag that automatically wraps each substitution in a `peek` call.
1766
- * @see {@link peek}
1767
- */
1768
- function peeks(strings, ...values) {
1769
- return tag(peek, strings, values);
1770
- }
1771
-
1772
- //#endregion
1773
- //#region src/Builder.ts
1774
- function pyxis(adapter) {
1775
- const extensions = {};
1776
- const builder = {
1777
- build: () => createRenderer(adapter, extensions),
1778
- extend: (extensionKey, extension) => {
1779
- extensions[extensionKey] = extension;
1780
- return builder;
2162
+ return derived(() => {
2163
+ let result = strings[0];
2164
+ let index = 0;
2165
+ let value;
2166
+ while (index < length) {
2167
+ value = read(values[index]);
2168
+ try {
2169
+ value = String(value);
2170
+ } catch {
2171
+ value = "[error]";
2172
+ }
2173
+ result += value + strings[++index];
1781
2174
  }
1782
- };
1783
- return builder;
2175
+ return result;
2176
+ });
1784
2177
  }
1785
-
1786
- //#endregion
1787
- //#region src/Component.ts
1788
- function component(block) {
1789
- let devId;
1790
- devId = arguments[1];
1791
- devId && globalThis.__PYXIS_HMR__.component.upsert(devId, block);
1792
- return (jsx, hParent, nUsedParent, nRealParent, nBefore, isBatch) => {
1793
- const context = getContextContainer();
1794
- try {
1795
- {
1796
- if (!import.meta.hot || !devId) {
1797
- setContextContainer(context);
1798
- mountJsx(block(jsx), hParent, nUsedParent, nRealParent, nBefore, isBatch);
1799
- return;
1800
- }
1801
- const hGroup = fork(hParent);
1802
- const nMarker = hGroup.adapter.marker(`/${devId}`);
1803
- insert(nMarker, null, hParent, nUsedParent, nBefore, isBatch);
1804
- unmounted(globalThis.__PYXIS_HMR__.component.subscribe(devId, (impl) => {
1805
- unmount(hGroup);
1806
- setContextContainer(context);
1807
- mount({
1808
- ...jsx,
1809
- [S_COMPONENT]: (() => mountJsx(impl(jsx), hGroup, nUsedParent, nRealParent, nMarker, isBatch))
1810
- }, hGroup, nUsedParent, nRealParent, nMarker, isBatch);
1811
- }));
1812
- nUsedParent = nRealParent;
1813
- isBatch = false;
1814
- }
1815
- } finally {
1816
- setContextContainer(context);
2178
+ /*@__NO_SIDE_EFFECTS__*/
2179
+ function writes(input, value, force) {
2180
+ return isAtom(input) ? () => void write(input, value, force) : noop;
2181
+ }
2182
+ /*@__NO_SIDE_EFFECTS__*/
2183
+ function updates(input, transform, force) {
2184
+ return isAtom(input) ? () => void update(input, transform, force) : noop;
2185
+ }
2186
+ function atomsOf(data, keysOrLifecycle, maybeLifecycle) {
2187
+ let keys;
2188
+ let lifecycle;
2189
+ if (keysOrLifecycle === void 0 || Array.isArray(keysOrLifecycle)) {
2190
+ keys = keysOrLifecycle;
2191
+ lifecycle = maybeLifecycle;
2192
+ } else lifecycle = keysOrLifecycle;
2193
+ lifecycle ??= getLifecycle();
2194
+ const result = Object.create(null);
2195
+ if (keys) {
2196
+ const { length } = keys;
2197
+ let index = 0;
2198
+ let key;
2199
+ for (; index < length; index += 1) {
2200
+ key = keys[index];
2201
+ result[key] = atomOf(data[key], lifecycle);
1817
2202
  }
1818
- };
2203
+ for (key in data) if (result[key] === void 0) result[key] = data[key];
2204
+ } else {
2205
+ let key;
2206
+ for (key in data) result[key] = atomOf(data[key], lifecycle);
2207
+ }
2208
+ return result;
2209
+ }
2210
+ function assign(store, data, keysOrLifecycle, maybeLifecycle) {
2211
+ let keys;
2212
+ let lifecycle;
2213
+ if (keysOrLifecycle === void 0 || Array.isArray(keysOrLifecycle)) {
2214
+ keys = keysOrLifecycle;
2215
+ lifecycle = maybeLifecycle;
2216
+ } else lifecycle = keysOrLifecycle;
2217
+ if (keys) {
2218
+ const { length } = keys;
2219
+ const visited = Object.create(null);
2220
+ let index = 0;
2221
+ let key;
2222
+ for (; index < length; index += 1) {
2223
+ key = keys[index];
2224
+ write(store[key], peek(data[key]));
2225
+ visited[key] = true;
2226
+ }
2227
+ for (key in data) if (!visited[key]) store[key] = data[key];
2228
+ } else {
2229
+ let key;
2230
+ let atom;
2231
+ for (key in store) {
2232
+ atom = store[key];
2233
+ if (isAtom(atom)) write(atom, peek(data[key]));
2234
+ else store[key] = atomOf(data[key], lifecycle ??= getLifecycle());
2235
+ }
2236
+ for (key in data) if (!Object.hasOwn(store, key)) store[key] = atomOf(data[key], lifecycle ??= getLifecycle());
2237
+ }
2238
+ return store;
1819
2239
  }
1820
2240
 
1821
2241
  //#endregion
1822
2242
  //#region src/jsx.ts
1823
- const EMPTY_ARRAY = Object.freeze([]);
2243
+ const EMPTY_ARRAY = [];
1824
2244
  function jsx(componentOrTagName, props, key) {
1825
2245
  const { children } = props;
1826
2246
  props.children = children === void 0 ? EMPTY_ARRAY : Array.isArray(children) ? children : [children];
@@ -1841,5 +2261,26 @@ function jsxs(componentOrTagName, props, key) {
1841
2261
  }
1842
2262
 
1843
2263
  //#endregion
1844
- export { ChangeKind, Fragment, Iterator, Native, RefExtension, Show, atomOf, bind, component, consumerOf, createContext, derived, effect, fork, getLifecycle, host, insert, isAtom, jsx, jsxs, listOf, mount, mountJsx, mounted, noEffect, peek, peeks, proxyOf, pyxis, read, reads, sync, tick, tock, track, unmount, unmounted, untrack, update, withLifecycle, write };
2264
+ //#region src/RendererBuilder.ts
2265
+ /*@__NO_SIDE_EFFECTS__*/
2266
+ function renderer(adapter) {
2267
+ const extensions = {};
2268
+ const builder = {
2269
+ create: () => /* @__PURE__ */ createRenderer(adapter, extensions),
2270
+ extend: (extensionKey, extension) => {
2271
+ if (!/^[a-z_$][a-z0-9_$-]*$/i.test(extensionKey)) throw new Error(`invalid extension key: "${extensionKey}"`);
2272
+ extensions[extensionKey] = extension;
2273
+ return builder;
2274
+ }
2275
+ };
2276
+ return builder;
2277
+ }
2278
+
2279
+ //#endregion
2280
+ //#region src/exports/core-dev.ts
2281
+ runtime.$hmrComponent ??= new ComponentRegistry();
2282
+ runtime.$hmrState ??= new StateRegistry();
2283
+
2284
+ //#endregion
2285
+ export { Fragment, Iterator, LC_CHANGE, LC_CLEAR, LC_INSERT, LC_REMOVE, Native, RefExtension, S_ATOM, S_COMPONENT, S_DEV_INFO, S_TAG_NAME, Show, assign, atomOf, atomsOf, bind, component, contextOf, contextual, derived, destroy, effect, ext, extension, flush, fork, getLifecycle, host, insert, invoke, isAtom, jsx, jsxs, link, listOf, mount, mountJsx, mounted, noEffect, onceMounted, onceUnmounted, peek, proxyOf, read, renderer, scheduleTick, scheduleTock, sync, text, tick, tock, track, unlink, unmount, unmounted, untrack, update, updates, willMount, withLifecycle, write, writes };
1845
2286
  //# sourceMappingURL=core-dev.js.map