@dooboostore/dom-render 1.0.114 → 1.0.115

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.
@@ -30,9 +30,9 @@ var dooboostoreDomRender = (() => {
30
30
  ));
31
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
32
 
33
- // ../../node_modules/.pnpm/reflect-metadata@0.2.2/node_modules/reflect-metadata/Reflect.js
33
+ // ../../../node_modules/.pnpm/reflect-metadata@0.2.2/node_modules/reflect-metadata/Reflect.js
34
34
  var require_Reflect = __commonJS({
35
- "../../node_modules/.pnpm/reflect-metadata@0.2.2/node_modules/reflect-metadata/Reflect.js"() {
35
+ "../../../node_modules/.pnpm/reflect-metadata@0.2.2/node_modules/reflect-metadata/Reflect.js"() {
36
36
  var Reflect2;
37
37
  (function(Reflect3) {
38
38
  (function(factory) {
@@ -1315,6 +1315,7 @@ var dooboostoreDomRender = (() => {
1315
1315
  Fetcher: () => Fetcher,
1316
1316
  FormValidator: () => FormValidator,
1317
1317
  FunctionUtils: () => FunctionUtils,
1318
+ HashUtils: () => HashUtils,
1318
1319
  HttpFetcher: () => HttpFetcher,
1319
1320
  HttpJsonFetcher: () => HttpJsonFetcher,
1320
1321
  HttpResponseError: () => HttpResponseError,
@@ -1395,6 +1396,7 @@ var dooboostoreDomRender = (() => {
1395
1396
  isCssCommentItem: () => isCssCommentItem,
1396
1397
  isCssDeclarationItem: () => isCssDeclarationItem,
1397
1398
  isCssRuleItem: () => isCssRuleItem,
1399
+ isDefined: () => isDefined,
1398
1400
  isHttpResponseError: () => isHttpResponseError,
1399
1401
  isPoint2DType: () => isPoint2DType,
1400
1402
  isPoint3DType: () => isPoint3DType,
@@ -1440,6 +1442,12 @@ var dooboostoreDomRender = (() => {
1440
1442
  return false;
1441
1443
  }
1442
1444
  };
1445
+ ValidUtils22.isGenerator = (value) => {
1446
+ return value !== null && typeof value === "object" && typeof value.next === "function" && typeof value[Symbol.iterator] === "function";
1447
+ };
1448
+ ValidUtils22.isAsyncGenerator = (value) => {
1449
+ return value !== null && typeof value === "object" && typeof value.next === "function" && typeof value[Symbol.asyncIterator] === "function";
1450
+ };
1443
1451
  ValidUtils22.isFrozen = (obj) => {
1444
1452
  return Object.isFrozen(obj);
1445
1453
  };
@@ -3354,6 +3362,12 @@ var dooboostoreDomRender = (() => {
3354
3362
  this.z = z ?? 0;
3355
3363
  }
3356
3364
  }
3365
+ /**
3366
+ * 성분 대입 (파괴적). 벡터 전달 시 그 성분을 복사.
3367
+ * @example
3368
+ * v.set(1, 2, 3); // v = (1, 2, 3)
3369
+ * v.set(otherVector); // v = other와 동일 성분
3370
+ */
3357
3371
  set(v, y = 0, z = 0) {
3358
3372
  if (v instanceof _Vector || v instanceof Point3D) {
3359
3373
  this.set(v.x || 0, v.y || 0, v.z || 0);
@@ -3364,16 +3378,45 @@ var dooboostoreDomRender = (() => {
3364
3378
  }
3365
3379
  return this;
3366
3380
  }
3381
+ /**
3382
+ * 복사본 반환 (비파괴). 파괴적 메서드와 조합해 원본 보존용.
3383
+ * @example
3384
+ * const s = a.get(); s.add(b); // s = a+b, a는 그대로
3385
+ */
3367
3386
  get() {
3368
3387
  return new _Vector(this.x, this.y, this.z);
3369
3388
  }
3370
- // 피타고라스의 정리는 a의 제곱 더하기 b 의 제곱은 c 의 제곱과 같다는 것을 정의합니다.
3371
- mag() {
3389
+ /**
3390
+ * 노름(norm). 평평한 공간(유클리드 공간)에서의 직선 거리.
3391
+ * - 수식: ‖v‖ = √(x² + y² + z²)
3392
+ * - 2D에서는 피타고라스 정리 그 자체. 3D는 두 번 적용한 것과 같음.
3393
+ * (바닥 대각선 √(x²+y²) → 공간 대각선 √((바닥)²+z²))
3394
+ * - "유클리드"는 평평한 공간의 기하학을 말함 (좌표계는 데카르트의 것, 유클리드는 좌표 없이 기하학을 세움).
3395
+ * 휘어진 공간(구면·쌍곡)에서는 이 공식이 성립하지 않음.
3396
+ * @example
3397
+ * new Vector(3, 4, 0).norm(); // 5
3398
+ * new Vector(6, 8, 0).norm(); // 10
3399
+ */
3400
+ norm() {
3372
3401
  const x = this.x;
3373
3402
  const y = this.y;
3374
3403
  const z = this.z;
3375
3404
  return Math.sqrt(x * x + y * y + z * z);
3376
3405
  }
3406
+ /**
3407
+ * norm()의 별칭 (p5.js 호환).
3408
+ * @example
3409
+ * new Vector(3, 4, 0).mag(); // 5
3410
+ */
3411
+ mag() {
3412
+ return this.norm();
3413
+ }
3414
+ /**
3415
+ * 덧셈 (파괴적). 성분별 합. 기하학적으로 평행사변형의 대각선.
3416
+ * - 수식: a + b = (ax+bx, ay+by, az+bz)
3417
+ * @example
3418
+ * const a = new Vector(4, 2); a.add(new Vector(-2, 3)); // a = (2, 5)
3419
+ */
3377
3420
  add(v, y = 0, z = 0) {
3378
3421
  if (v instanceof _Vector || v instanceof Point3D) {
3379
3422
  this.x += v.x;
@@ -3385,12 +3428,24 @@ var dooboostoreDomRender = (() => {
3385
3428
  this.z += z;
3386
3429
  }
3387
3430
  }
3431
+ /**
3432
+ * 덧셈 결과의 새 벡터 반환 (비파괴).
3433
+ * - 수식: a + b = (ax+bx, ay+by, az+bz)
3434
+ * @example
3435
+ * const s = a.toAdd(a, b); // s = a+b, a·b는 그대로
3436
+ */
3388
3437
  toAdd(v1, v2) {
3389
3438
  return new _Vector(v1.x + v2.x, v1.y + v2.y, v1.x + v2.y);
3390
3439
  }
3391
3440
  // public static add(v1: Vector, v2: Vector): Vector {
3392
3441
  // return new Vector(v1.x + v2.x, v1.y + v2.y, v1.x + v2.y);
3393
3442
  // }
3443
+ /**
3444
+ * 뺄셈 (파괴적). a - b는 "a에서 b를 되돌리는" 벡터.
3445
+ * - 수식: a - b = (ax-bx, ay-by, az-bz)
3446
+ * @example
3447
+ * const a = new Vector(4, 2); a.sub(new Vector(1, 1)); // a = (3, 1)
3448
+ */
3394
3449
  sub(v, y = 0, z = 0) {
3395
3450
  if (v instanceof _Vector || v instanceof Point3D) {
3396
3451
  this.x -= v.x;
@@ -3402,6 +3457,12 @@ var dooboostoreDomRender = (() => {
3402
3457
  this.z -= z;
3403
3458
  }
3404
3459
  }
3460
+ /**
3461
+ * 뺄셈 결과의 새 벡터 반환 (비파괴).
3462
+ * - 수식: a - b = (ax-bx, ay-by, az-bz)
3463
+ * @example
3464
+ * const d = a.toSub(b); // d = a-b, a·b는 그대로
3465
+ */
3405
3466
  toSub(v2) {
3406
3467
  const v1 = this;
3407
3468
  return new _Vector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
@@ -3410,6 +3471,12 @@ var dooboostoreDomRender = (() => {
3410
3471
  // public static sub(v1: Vector, v2: Vector): Vector {
3411
3472
  // return new Vector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
3412
3473
  // }
3474
+ /**
3475
+ * 스칼라배 또는 성분별 곱 (파괴적). 스칼라 k>1이면 늘어나고, 0<k<1이면 줄어듦. k<0이면 반대 방향.
3476
+ * - 수식(스칼라): k·v = (k·x, k·y, k·z)
3477
+ * @example
3478
+ * const v = new Vector(1, 2); v.mult(3); // v = (3, 6)
3479
+ */
3413
3480
  mult(v) {
3414
3481
  if (v instanceof _Vector || v instanceof Point3D) {
3415
3482
  this.x *= v.x;
@@ -3422,6 +3489,12 @@ var dooboostoreDomRender = (() => {
3422
3489
  }
3423
3490
  return this;
3424
3491
  }
3492
+ /**
3493
+ * 성분별 곱 결과의 새 벡터 반환 (비파괴).
3494
+ * - 수식: (ax·bx, ay·by, az·bz)
3495
+ * @example
3496
+ * const m = a.toMult(b); // m = 성분별 곱, a·b는 그대로
3497
+ */
3425
3498
  toMult(v2) {
3426
3499
  const v1 = this;
3427
3500
  return new _Vector(v1.x *= v2.x, v1.y *= v2.y, v1.z *= v2.z);
@@ -3429,6 +3502,12 @@ var dooboostoreDomRender = (() => {
3429
3502
  // public static mult(v1: Vector, v2: Vector) {
3430
3503
  // return new Vector(v1.x *= v2.x, v1.y *= v2.y, v1.z *= v2.z);
3431
3504
  // }
3505
+ /**
3506
+ * 스칼라 나눗셈 또는 성분별 나눗셈 (파괴적). 정규화의 핵심 연산.
3507
+ * - 수식(스칼라): v/k = (x/k, y/k, z/k)
3508
+ * @example
3509
+ * const v = new Vector(6, 8); v.div(2); // v = (3, 4)
3510
+ */
3432
3511
  div(v) {
3433
3512
  if (v instanceof _Vector || v instanceof Point3D) {
3434
3513
  this.x /= v.x;
@@ -3441,6 +3520,12 @@ var dooboostoreDomRender = (() => {
3441
3520
  }
3442
3521
  return this;
3443
3522
  }
3523
+ /**
3524
+ * 스칼라 나눗셈 결과의 새 벡터 반환 (비파괴).
3525
+ * - 수식: v/k = (x/k, y/k, z/k)
3526
+ * @example
3527
+ * const h = v.toDiv(2); // h = v/2, v는 그대로
3528
+ */
3444
3529
  toDiv(d) {
3445
3530
  const v = this;
3446
3531
  const rv = v.get();
@@ -3456,10 +3541,22 @@ var dooboostoreDomRender = (() => {
3456
3541
  // rv.z /= d;
3457
3542
  // return rv;
3458
3543
  // }
3544
+ /**
3545
+ * 두 점(벡터) 사이 거리. 차 벡터의 크기.
3546
+ * - 수식: d = √((dx)² + (dy)² + (dz)²)
3547
+ * @example
3548
+ * new Vector(0, 0).dist(new Vector(3, 4)); // 5
3549
+ */
3459
3550
  dist(v) {
3460
3551
  const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;
3461
3552
  return Math.sqrt(dx * dx + dy * dy + dz * dz);
3462
3553
  }
3554
+ /**
3555
+ * 두 점(벡터) 사이 거리 (toDist는 dist와 동일, 비파괴).
3556
+ * - 수식: d = √((dx)² + (dy)² + (dz)²)
3557
+ * @example
3558
+ * a.toDist(b); // a와 b 사이 거리
3559
+ */
3463
3560
  toDist(v2) {
3464
3561
  const v1 = this;
3465
3562
  const dx = v1.x - v2.x, dy = v1.y - v2.y, dz = v1.z - v2.z;
@@ -3471,36 +3568,90 @@ var dooboostoreDomRender = (() => {
3471
3568
  // dz = v1.z - v2.z;
3472
3569
  // return Math.sqrt(dx * dx + dy * dy + dz * dz);
3473
3570
  // }
3474
- dot(v, y, z) {
3571
+ /**
3572
+ * 내적(dot product). 두 벡터가 같은 방향을 향하는 정도.
3573
+ * - 수식: a·b = ax·bx + ay·by + az·bz = |a||b|cosθ
3574
+ * - θ=90°면 0(직교), 0°면 최대(같은 방향).
3575
+ * - 벡터 전달 시 y·z 생략 가능, 스칼라(x) 전달 시 y·z와 함께 사용.
3576
+ * @example
3577
+ * new Vector(5, 12).dot(new Vector(-5, 12)); // -25+144 = 119
3578
+ * new Vector(1, 0).dot(new Vector(0, 1)); // 0 (직각)
3579
+ * new Vector(1, 2).dot(3, 4); // 1*3+2*4 = 11
3580
+ */
3581
+ dot(v, y = 0, z = 0) {
3475
3582
  if (v instanceof _Vector || v instanceof Point3D) {
3476
3583
  return this.x * v.x + this.y * v.y + this.z * v.z;
3477
3584
  }
3478
3585
  ;
3479
3586
  return this.x * v + this.y * y + this.z * z;
3480
3587
  }
3588
+ /**
3589
+ * 외적(cross product). 두 벡터에 수직인 벡터. 오른손 법칙 방향.
3590
+ * - 수식: a×b = (ay·bz − az·by, az·bx − ax·bz, ax·by − ay·bx)
3591
+ * - 2D에서는 z값 = ax·by − ay·bx (부호 있는 면적, 회전 방향 판정용).
3592
+ * @example
3593
+ * new Vector(1, 0, 0).cross(new Vector(0, 1, 0)); // (0, 0, 1)
3594
+ */
3481
3595
  cross(v) {
3482
3596
  const x = this.x, y = this.y, z = this.z;
3483
3597
  return new _Vector(y * v.z - v.y * z, z * v.x - v.z * x, x * v.y - v.x * y);
3484
3598
  }
3599
+ /**
3600
+ * 정규화 (파괴적). 방향은 그대로, 크기를 1로. 단위벡터 v̂ 생성.
3601
+ * - 수식: v̂ = v / |v|
3602
+ * - 영벡터(|v|=0)는 0으로 나누지 않고 그대로 둠.
3603
+ * @example
3604
+ * const v = new Vector(6, 8); v.normalize(); // v = (0.6, 0.8), |v| = 1
3605
+ */
3485
3606
  normalize() {
3486
- const m = this.mag();
3607
+ const m = this.norm();
3487
3608
  if (m > 0) this.div(m);
3488
3609
  }
3610
+ /**
3611
+ * 크기 상한 (파괴적). 속도가 high를 넘으면 방향 유지한 채 high로 자름. 물리 시뮬레이션 최고속도용.
3612
+ * - 수식: |v| > high → v = v̂ · high
3613
+ * @example
3614
+ * const v = new Vector(6, 8); v.limit(5); // |v|=10 → 5, v = (3, 4)
3615
+ */
3489
3616
  limit(high) {
3490
- if (this.mag() > high) {
3617
+ if (this.norm() > high) {
3491
3618
  this.normalize();
3492
3619
  this.mult(high);
3493
3620
  }
3494
3621
  }
3622
+ /**
3623
+ * 2D 방향각. +x축 기준 반시계 방향 라디안 (-π ~ π).
3624
+ * - 수식: θ = atan2(y, x)
3625
+ * @example
3626
+ * new Vector(1, 1).heading2D(); // π/4 (45°)
3627
+ * new Vector(-1, 0).heading2D(); // π (180°)
3628
+ */
3495
3629
  heading2D() {
3496
3630
  return -Math.atan2(-this.y, this.x);
3497
3631
  }
3632
+ /**
3633
+ * 무작위 방향 단위벡터 반환 (새 Vector, 자신은 변경 안 함).
3634
+ * @example
3635
+ * const r = new Vector().random2D(); // |r| = 1, 방향 랜덤
3636
+ */
3498
3637
  random2D() {
3499
3638
  return this.fromAngle(Math.random() * (2 * Math.PI));
3500
3639
  }
3640
+ /**
3641
+ * 각도→벡터. 각도 θ, 길이 L인 2D 벡터 반환 (새 Vector).
3642
+ * - 수식: (L·cosθ, L·sinθ, 0)
3643
+ * @example
3644
+ * new Vector().fromAngle(Math.PI / 3, 2); // (1, 1.73, 0)
3645
+ */
3501
3646
  fromAngle(angle, length = 1) {
3502
3647
  return new _Vector(length * Math.cos(angle), length * Math.sin(angle), 0);
3503
3648
  }
3649
+ /**
3650
+ * 구면좌표→3D 벡터 (새 Vector 반환, p5.js 컨벤션).
3651
+ * - 수식: (L·sinθ·sinφ, −L·cosθ, L·sinθ·cosφ)
3652
+ * @example
3653
+ * new Vector().fromAngles(Math.PI / 2, Math.PI / 2, 1); // (1, 0, 0)
3654
+ */
3504
3655
  fromAngles(theta, phi, length = 1) {
3505
3656
  const cosPhi = Math.cos(phi);
3506
3657
  const sinPhi = Math.sin(phi);
@@ -3512,18 +3663,43 @@ var dooboostoreDomRender = (() => {
3512
3663
  length * sinTheta * cosPhi
3513
3664
  );
3514
3665
  }
3666
+ /**
3667
+ * 성분 배열 반환. [x, y, z] 순서.
3668
+ * @example
3669
+ * new Vector(1, 2, 3).array(); // [1, 2, 3]
3670
+ */
3515
3671
  array() {
3516
3672
  return [this.x, this.y, this.z];
3517
3673
  }
3674
+ /**
3675
+ * Point3D로 변환 (위치 의미로 바꿀 때).
3676
+ * @example
3677
+ * new Vector(1, 2, 3).toPoint3D(); // Point3D(1, 2, 3)
3678
+ */
3518
3679
  toPoint3D() {
3519
3680
  return new Point3D(this.x, this.y, this.z);
3520
3681
  }
3682
+ /**
3683
+ * Point2D로 변환 (z 버림).
3684
+ * @example
3685
+ * new Vector(1, 2, 3).toPoint2D(); // Point2D(1, 2)
3686
+ */
3521
3687
  toPoint2D() {
3522
3688
  return new Point2D(this.x, this.y);
3523
3689
  }
3690
+ /**
3691
+ * 복사본 반환 (get()과 동일, 비파괴).
3692
+ * @example
3693
+ * const c = v.copy(); // v와 같은 성분의 새 벡터
3694
+ */
3524
3695
  copy() {
3525
3696
  return new _Vector(this.x, this.y, this.z);
3526
3697
  }
3698
+ /**
3699
+ * 문자열 표현. "[x, y, z]" 형식.
3700
+ * @example
3701
+ * new Vector(1, 2).toString(); // "[1, 2, 0]"
3702
+ */
3527
3703
  toString() {
3528
3704
  return "[" + this.x + ", " + this.y + ", " + this.z + "]";
3529
3705
  }
@@ -5716,87 +5892,64 @@ var dooboostoreDomRender = (() => {
5716
5892
  // ../core/dist/esm/promise/AbortablePromise.js
5717
5893
  var AbortablePromise = class _AbortablePromise {
5718
5894
  constructor(executor, signal) {
5719
- this.steps = [];
5720
- this.initialExecutor = executor;
5721
5895
  this.signal = signal;
5896
+ if (typeof executor !== "function") {
5897
+ this.source = Promise.resolve(executor);
5898
+ } else if (signal?.aborted) {
5899
+ this.source = Promise.reject(signal.reason);
5900
+ } else {
5901
+ this.source = Promise.resolve().then(executor);
5902
+ }
5903
+ }
5904
+ /**
5905
+ * source 를 abort 와 경쟁시킨 promise. 실제로 소비될 때(then/toPromise) 한 번만 만든다 —
5906
+ * await 는 then() 이 돌려준 새 인스턴스를 버리므로, 미리 만들면 아무도 안 받는 reject 가 unhandled 로 남는다.
5907
+ */
5908
+ get promise() {
5909
+ if (this.guarded) return this.guarded;
5910
+ const signal = this.signal;
5911
+ if (!signal) return this.guarded = this.source;
5912
+ if (signal.aborted) {
5913
+ this.source.catch(() => {
5914
+ });
5915
+ return this.guarded = Promise.reject(signal.reason);
5916
+ }
5917
+ return this.guarded = new Promise((resolve, reject) => {
5918
+ const cleanup = () => signal.removeEventListener("abort", onAbort);
5919
+ const onAbort = () => reject(signal.reason);
5920
+ signal.addEventListener("abort", onAbort, { once: true });
5921
+ this.source.then(
5922
+ (value) => {
5923
+ cleanup();
5924
+ resolve(value);
5925
+ },
5926
+ (reason) => {
5927
+ cleanup();
5928
+ reject(reason);
5929
+ }
5930
+ );
5931
+ });
5722
5932
  }
5723
5933
  then(onfulfilled, onrejected) {
5724
- const newPromise = new _AbortablePromise(
5725
- this.initialExecutor,
5726
- this.signal
5934
+ const signal = this.signal;
5935
+ const next = this.promise.then(
5936
+ onfulfilled ? (value) => {
5937
+ if (signal?.aborted) throw signal.reason;
5938
+ return onfulfilled(value);
5939
+ } : void 0,
5940
+ onrejected
5727
5941
  );
5728
- newPromise.steps = [...this.steps];
5729
- if (onfulfilled) {
5730
- newPromise.steps.push(onfulfilled);
5731
- }
5732
- if (onrejected) {
5733
- newPromise.errorHandler = onrejected;
5734
- } else if (this.errorHandler) {
5735
- newPromise.errorHandler = this.errorHandler;
5736
- }
5737
- return newPromise;
5942
+ return new _AbortablePromise(next, signal);
5738
5943
  }
5739
5944
  catch(onrejected) {
5740
5945
  return this.then(void 0, onrejected);
5741
5946
  }
5742
5947
  finally(onfinally) {
5743
- return this.then(
5744
- (value) => {
5745
- onfinally?.();
5746
- return value;
5747
- },
5748
- (reason) => {
5749
- onfinally?.();
5750
- throw reason;
5751
- }
5752
- );
5753
- }
5754
- async execute() {
5755
- try {
5756
- if (this.signal?.aborted) {
5757
- throw new Error(this.signal.reason || "Aborted");
5758
- }
5759
- const abortPromise = new Promise((_, reject) => {
5760
- if (this.signal?.aborted) {
5761
- return reject(new Error(this.signal.reason || "Aborted"));
5762
- }
5763
- const callback = (e) => {
5764
- const target = e.target;
5765
- this.signal?.removeEventListener("abort", callback);
5766
- reject(new Error(target.reason || "Aborted"));
5767
- };
5768
- this.signal?.addEventListener("abort", callback, { once: true });
5769
- });
5770
- const pipelineRunner = async () => {
5771
- let lastResult;
5772
- if (typeof this.initialExecutor === "function") {
5773
- if (this.signal?.aborted) {
5774
- throw new Error(this.signal.reason || "Aborted");
5775
- }
5776
- lastResult = await this.initialExecutor();
5777
- } else {
5778
- lastResult = await this.initialExecutor;
5779
- }
5780
- for (let i = 0; i < this.steps.length; i++) {
5781
- if (this.signal?.aborted) {
5782
- throw new Error(this.signal.reason || "Aborted");
5783
- }
5784
- const step = this.steps[i];
5785
- lastResult = await step(lastResult);
5786
- }
5787
- return lastResult;
5788
- };
5789
- return await Promise.race([pipelineRunner(), abortPromise]);
5790
- } catch (error) {
5791
- if (this.errorHandler) {
5792
- return await this.errorHandler(error);
5793
- }
5794
- throw error;
5795
- }
5948
+ return new _AbortablePromise(this.promise.finally(onfinally), this.signal);
5796
5949
  }
5797
- // Convert to native Promise
5950
+ // 네이티브 Promise 로 변환 (그냥 await 해도 된다)
5798
5951
  toPromise() {
5799
- return this.execute();
5952
+ return this.promise;
5800
5953
  }
5801
5954
  };
5802
5955
 
@@ -6669,11 +6822,15 @@ var dooboostoreDomRender = (() => {
6669
6822
  resolve(gdata);
6670
6823
  }).catch(async (e) => {
6671
6824
  this.error(config, pipe, e);
6672
- config?.error?.();
6825
+ config?.error?.(e);
6673
6826
  if (config?.errorTransform) {
6674
6827
  e = await config.errorTransform(e);
6675
6828
  }
6676
- e = await this.errorTransform(e);
6829
+ try {
6830
+ e = await this.errorTransform(e);
6831
+ } catch (ee) {
6832
+ e = ee;
6833
+ }
6677
6834
  reject(e);
6678
6835
  }).finally(() => {
6679
6836
  this.finally(config, pipe);
@@ -6690,41 +6847,30 @@ var dooboostoreDomRender = (() => {
6690
6847
  return data instanceof HttpResponseError;
6691
6848
  };
6692
6849
  var HttpFetcher = class extends Fetcher {
6850
+ // get/post/put/patch/head/delete가 전부 config.config.fetch.method를 세팅하려고 caller가
6851
+ // 넘긴 config(.fetch) 객체를 직접 mutate했다 - 같은 config 객체를 여러 호출이 공유하면
6852
+ // (예: Promise.all로 동시에 get/post) 서로의 method를 덮어쓰는 레이스가 생겼다. caller의
6853
+ // 객체는 절대 건드리지 않고, 얕은 복사본 위에 method만 얹어서 반환한다.
6854
+ withMethod(config, method) {
6855
+ return { ...config, config: { ...config.config, fetch: { ...config.config?.fetch, method } } };
6856
+ }
6693
6857
  get(config) {
6694
- config.config ?? (config.config = {});
6695
- config.config.fetch = config.config?.fetch ?? {};
6696
- config.config.fetch.method = "GET";
6697
- return this.fetch(config);
6858
+ return this.fetch(this.withMethod(config, "GET"));
6698
6859
  }
6699
6860
  post(config) {
6700
- config.config ?? (config.config = {});
6701
- config.config.fetch = config.config?.fetch ?? {};
6702
- config.config.fetch.method = "POST";
6703
- return this.fetch(config);
6861
+ return this.fetch(this.withMethod(config, "POST"));
6704
6862
  }
6705
6863
  patch(config) {
6706
- config.config ?? (config.config = {});
6707
- config.config.fetch = config.config?.fetch ?? {};
6708
- config.config.fetch.method = "PATCH";
6709
- return this.fetch(config);
6864
+ return this.fetch(this.withMethod(config, "PATCH"));
6710
6865
  }
6711
6866
  put(config) {
6712
- config.config ?? (config.config = {});
6713
- config.config.fetch = config.config?.fetch ?? {};
6714
- config.config.fetch.method = "PUT";
6715
- return this.fetch(config);
6867
+ return this.fetch(this.withMethod(config, "PUT"));
6716
6868
  }
6717
6869
  head(config) {
6718
- config.config ?? (config.config = {});
6719
- config.config.fetch = config.config?.fetch ?? {};
6720
- config.config.fetch.method = "HEAD";
6721
- return this.fetch(config);
6870
+ return this.fetch(this.withMethod(config, "HEAD"));
6722
6871
  }
6723
6872
  delete(config) {
6724
- config.config ?? (config.config = {});
6725
- config.config.fetch = config.config?.fetch ?? {};
6726
- config.config.fetch.method = "DELETE";
6727
- return this.fetch(config);
6873
+ return this.fetch(this.withMethod(config, "DELETE"));
6728
6874
  }
6729
6875
  async beforeProxyFetch(config) {
6730
6876
  return config;
@@ -6761,7 +6907,7 @@ var dooboostoreDomRender = (() => {
6761
6907
  }
6762
6908
  } else {
6763
6909
  httpResponseError.body = e;
6764
- httpResponseError.message = e.message;
6910
+ httpResponseError.message = e?.message;
6765
6911
  }
6766
6912
  return httpResponseError;
6767
6913
  }
@@ -6771,7 +6917,7 @@ var dooboostoreDomRender = (() => {
6771
6917
  if (!(target instanceof URL) && typeof target !== "string") {
6772
6918
  const searchParams = ConvertUtils.toURLSearchParams(target.searchParams ?? {});
6773
6919
  try {
6774
- const url = typeof target.url === "string" ? new URL(target.url) : target.url;
6920
+ const url = new URL(target.url);
6775
6921
  searchParams.forEach((value, key) => {
6776
6922
  url.searchParams.append(key, value);
6777
6923
  });
@@ -6786,43 +6932,30 @@ var dooboostoreDomRender = (() => {
6786
6932
  }
6787
6933
  }
6788
6934
  const beforeProxyData = { requestInfo: target, init: config?.fetch };
6789
- let beforeData = config?.beforeProxyFetch ? await config?.beforeProxyFetch(config) : beforeProxyData.requestInfo;
6790
- beforeData = config?.skipGlobalBeforeProxyFetch ? beforeProxyData : await this.beforeProxyFetch(beforeProxyData);
6935
+ let beforeData = config?.beforeProxyFetch ? await config?.beforeProxyFetch(beforeProxyData) : beforeProxyData;
6936
+ beforeData = config?.skipGlobalBeforeProxyFetch ? beforeData : await this.beforeProxyFetch(beforeData);
6791
6937
  target = beforeData.requestInfo;
6792
6938
  if (beforeData.init) {
6793
6939
  config ?? (config = {});
6794
6940
  config.fetch = beforeData.init;
6795
6941
  }
6796
- let abortedTimeout;
6942
+ let requestInit = config?.fetch;
6797
6943
  if (config?.fetch && "timeout" in config.fetch) {
6798
- const abortController = new AbortController();
6799
- abortedTimeout = setTimeout(() => {
6800
- if (config?.fetch?.signal) {
6801
- const inputSignal = config.fetch.signal;
6802
- const listener = () => {
6803
- if (!abortController.signal.aborted) {
6804
- abortController.abort();
6805
- }
6806
- inputSignal.removeEventListener("abort", listener);
6807
- };
6808
- inputSignal.addEventListener("abort", listener);
6809
- }
6810
- if (!abortController.signal.aborted) {
6811
- abortController.abort();
6812
- }
6813
- }, config.fetch.timeout);
6814
- config.fetch.signal = abortController.signal;
6944
+ const timeoutSignal = AbortSignal.timeout(config.fetch.timeout);
6945
+ const signal = config.fetch.signal ? AbortSignal.any([timeoutSignal, config.fetch.signal]) : timeoutSignal;
6946
+ requestInit = { ...config.fetch, signal };
6815
6947
  }
6816
- this.beforeFetch({ target, requestInit: config?.fetch });
6817
- return (config.fetcher ?? fetch)(target, config?.fetch).then(async (it) => {
6818
- this.afterFetch({ target, requestInit: config?.fetch }, it);
6948
+ this.beforeFetch({ target, requestInit });
6949
+ return (config.fetcher ?? fetch)(target, requestInit).then(async (it) => {
6950
+ this.afterFetch({ target, requestInit }, it);
6819
6951
  const afterProxyData = {
6820
- fetch: { target, requestInit: config.fetch },
6952
+ fetch: { target, requestInit },
6821
6953
  config: beforeData,
6822
6954
  response: it
6823
6955
  };
6824
- it = config?.afterProxyFetch ? await config.afterProxyFetch(afterProxyData) : it;
6825
- it = config?.skipGlobalAfterProxyFetch ? it : await this.afterProxyFetch(afterProxyData);
6956
+ afterProxyData.response = config?.afterProxyFetch ? await config.afterProxyFetch(afterProxyData) : it;
6957
+ afterProxyData.response = config?.skipGlobalAfterProxyFetch ? afterProxyData.response : await this.afterProxyFetch(afterProxyData);
6958
+ it = afterProxyData.response;
6826
6959
  config?.fetchResponseAfterCallBack?.(it, config);
6827
6960
  if (!config?.allowedResponseNotOk && !it.ok) {
6828
6961
  throw it;
@@ -6832,30 +6965,31 @@ var dooboostoreDomRender = (() => {
6832
6965
  throw data;
6833
6966
  }
6834
6967
  return it;
6835
- }).finally(() => {
6836
- if (abortedTimeout) {
6837
- clearTimeout(abortedTimeout);
6838
- }
6839
6968
  });
6840
6969
  }
6841
6970
  };
6842
6971
 
6843
6972
  // ../core/dist/esm/fetch/HttpJsonFetcher.js
6844
6973
  var HttpJsonFetcher = class extends HttpFetcher {
6974
+ // config.config/config.config.fetch를 직접 mutate하면(예전처럼) 같은 config 객체를 여러
6975
+ // postJson/patchJson/putJson 호출이 동시에 공유할 때 서로의 headers/body를 덮어쓰는 레이스가
6976
+ // 생긴다(HttpFetcher.get/post의 method 레이스랑 같은 문제). caller의 객체는 안 건드리고,
6977
+ // 얕은 복사본을 만들어서 새 config 객체를 리턴한다.
6845
6978
  updateJsonFetchConfigAndData(config) {
6846
- var _a2, _b2;
6847
6979
  const headers = {
6848
6980
  "Content-Type": "application/json",
6849
6981
  Accept: "application/json, text/plain, */*"
6850
6982
  };
6851
- config.config ?? (config.config = {});
6852
- (_a2 = config.config).fetch ?? (_a2.fetch = {});
6853
- (_b2 = config.config.fetch).headers ?? (_b2.headers = {});
6854
- config.config.fetch.headers = { ...headers, ...config.config.fetch?.headers };
6855
- if (config.config?.fetch?.body && typeof config.config?.fetch?.body !== "string") {
6856
- config.config.fetch.body = JSON.stringify(config.config.fetch.body);
6983
+ const fetchInit = { ...config.config?.fetch };
6984
+ const merged = new Headers(headers);
6985
+ if (fetchInit.headers) {
6986
+ new Headers(fetchInit.headers).forEach((value, key) => merged.set(key, value));
6857
6987
  }
6858
- return config;
6988
+ fetchInit.headers = merged;
6989
+ if (fetchInit.body && typeof fetchInit.body !== "string") {
6990
+ fetchInit.body = JSON.stringify(fetchInit.body);
6991
+ }
6992
+ return { ...config, config: { ...config.config, fetch: fetchInit } };
6859
6993
  }
6860
6994
  get(config) {
6861
6995
  return super.get(config);
@@ -8369,7 +8503,7 @@ var dooboostoreDomRender = (() => {
8369
8503
 
8370
8504
  // ../core/dist/esm/url/UrlUtils.js
8371
8505
  var UrlUtils;
8372
- ((UrlUtils2) => {
8506
+ (function(UrlUtils2) {
8373
8507
  UrlUtils2.origin = (fullurl) => {
8374
8508
  return new URL(fullurl).origin;
8375
8509
  };
@@ -8407,15 +8541,28 @@ var dooboostoreDomRender = (() => {
8407
8541
  });
8408
8542
  return searchParams;
8409
8543
  };
8544
+ UrlUtils2.toAbsoluteUrl = (relative, base) => {
8545
+ return new URL(relative, base);
8546
+ };
8547
+ function toAbsoluteRequest(input, base) {
8548
+ if (input instanceof URL) {
8549
+ return UrlUtils2.toAbsoluteUrl(input.href, base);
8550
+ }
8551
+ if (typeof input === "string") {
8552
+ return UrlUtils2.toAbsoluteUrl(input, base).href;
8553
+ }
8554
+ return new Request(UrlUtils2.toAbsoluteUrl(input.url, base).href, input);
8555
+ }
8556
+ UrlUtils2.toAbsoluteRequest = toAbsoluteRequest;
8410
8557
  UrlUtils2.manipulateSearchParams = (searchParams, options) => {
8411
8558
  if (options?.delete) {
8412
- (0, UrlUtils2.deleteSearchParam)(searchParams, options.delete);
8559
+ UrlUtils2.deleteSearchParam(searchParams, options.delete);
8413
8560
  }
8414
8561
  if (options?.append) {
8415
- (0, UrlUtils2.appendSearchParam)(searchParams, options.append);
8562
+ UrlUtils2.appendSearchParam(searchParams, options.append);
8416
8563
  }
8417
8564
  if (options?.upsert) {
8418
- (0, UrlUtils2.upsertSearchParam)(searchParams, options.upsert);
8565
+ UrlUtils2.upsertSearchParam(searchParams, options.upsert);
8419
8566
  }
8420
8567
  return searchParams;
8421
8568
  };
@@ -8666,12 +8813,13 @@ var dooboostoreDomRender = (() => {
8666
8813
  ReflectUtils2.findMetadata = (metadataKey, target, propertyKey) => {
8667
8814
  if (target == null) return void 0;
8668
8815
  let searchTarget = typeof target === "object" && typeof target !== "function" ? target.constructor : target;
8669
- while (searchTarget && searchTarget !== Object) {
8816
+ const seen = /* @__PURE__ */ new Set();
8817
+ while (searchTarget && searchTarget !== Object && !seen.has(searchTarget)) {
8818
+ seen.add(searchTarget);
8670
8819
  const data = propertyKey ? Reflect.getOwnMetadata(metadataKey, searchTarget, propertyKey) : Reflect.getOwnMetadata(metadataKey, searchTarget);
8671
8820
  if (data !== void 0) return data;
8672
8821
  const proto = Object.getPrototypeOf(searchTarget);
8673
8822
  searchTarget = proto && typeof proto === "function" ? proto : proto?.constructor || null;
8674
- if (searchTarget === target) break;
8675
8823
  }
8676
8824
  return void 0;
8677
8825
  };
@@ -8679,12 +8827,13 @@ var dooboostoreDomRender = (() => {
8679
8827
  const results = [];
8680
8828
  if (target == null) return results;
8681
8829
  let searchTarget = typeof target === "object" && typeof target !== "function" ? target.constructor : target;
8682
- while (searchTarget && searchTarget !== Object) {
8830
+ const seen = /* @__PURE__ */ new Set();
8831
+ while (searchTarget && searchTarget !== Object && !seen.has(searchTarget)) {
8832
+ seen.add(searchTarget);
8683
8833
  const data = propertyKey ? Reflect.getOwnMetadata(metadataKey, searchTarget, propertyKey) : Reflect.getOwnMetadata(metadataKey, searchTarget);
8684
8834
  if (data !== void 0) results.unshift(data);
8685
8835
  const proto = Object.getPrototypeOf(searchTarget);
8686
8836
  searchTarget = proto && typeof proto === "function" ? proto : proto?.constructor || null;
8687
- if (searchTarget === target) break;
8688
8837
  }
8689
8838
  return results;
8690
8839
  };
@@ -10809,6 +10958,23 @@ ${indentStr}}`;
10809
10958
  };
10810
10959
  }
10811
10960
 
10961
+ // ../core/dist/esm/hash/HashUtils.js
10962
+ var HashUtils;
10963
+ ((HashUtils2) => {
10964
+ const core53 = (str, seed) => {
10965
+ let h1 = 3735928559 ^ seed, h2 = 1103547991 ^ seed;
10966
+ for (let i = 0; i < str.length; i++) {
10967
+ const ch = str.charCodeAt(i);
10968
+ h1 = Math.imul(h1 ^ ch, 2654435761);
10969
+ h2 = Math.imul(h2 ^ ch, 1597334677);
10970
+ }
10971
+ h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507) ^ Math.imul(h2 ^ h2 >>> 13, 3266489909);
10972
+ h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507) ^ Math.imul(h1 ^ h1 >>> 13, 3266489909);
10973
+ return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(16).padStart(14, "0");
10974
+ };
10975
+ HashUtils2.hash53 = (str, seed = 0, xyz = 16) => `${core53(str, seed)}${core53(str, seed + 1)}`.slice(0, Math.max(1, xyz));
10976
+ })(HashUtils || (HashUtils = {}));
10977
+
10812
10978
  // ../core/dist/esm/index.js
10813
10979
  var src_default = {
10814
10980
  // Core utilities are exported as named exports above
@@ -10970,7 +11136,7 @@ ${indentStr}}`;
10970
11136
  var ElementApply = class _ElementApply {
10971
11137
  constructor(el, config) {
10972
11138
  this.el = el;
10973
- this.window = this.el.ownerDocument.defaultView;
11139
+ this.window = this.el.ownerDocument.defaultView ?? globalThis;
10974
11140
  this.document = this.el.ownerDocument;
10975
11141
  this.id = config?.id ?? el.id;
10976
11142
  this.variableWrap = config?.variableWrap ?? { start: "@", end: "@" };
@@ -10993,6 +11159,8 @@ ${indentStr}}`;
10993
11159
  let match;
10994
11160
  while ((match = regex.exec(script)) !== null) {
10995
11161
  variables.push(match[1]);
11162
+ const root = match[1].split(/[.[(]/)[0].trim();
11163
+ if (root && root !== match[1]) variables.push(root);
10996
11164
  }
10997
11165
  return variables;
10998
11166
  }
@@ -11097,10 +11265,15 @@ ${indentStr}}`;
11097
11265
  type.variables.forEach((v) => variables.add(v));
11098
11266
  });
11099
11267
  }
11268
+ const bind = config?.bind;
11269
+ const fromBind = (key) => bind != null && typeof key === "string" && !(key in (config?.context ?? {})) && _ElementApply.isUserMember(bind, key);
11100
11270
  const context = new Proxy(config?.context ?? {}, {
11271
+ has(target, key) {
11272
+ return key in target || fromBind(key);
11273
+ },
11101
11274
  get(target, key) {
11102
- const value = target[key];
11103
- return typeof value === "function" ? value.bind(config?.bind ?? target) : value;
11275
+ const value = key in target ? target[key] : fromBind(key) ? bind[key] : void 0;
11276
+ return typeof value === "function" ? value.bind(bind ?? target) : value;
11104
11277
  }
11105
11278
  });
11106
11279
  if (!config?.targetVariableName || config?.targetVariableName && variables.has(config?.targetVariableName)) {
@@ -11125,7 +11298,6 @@ ${indentStr}}`;
11125
11298
  }
11126
11299
  }
11127
11300
  `)(context).bind(it.node);
11128
- console.log("------------------->", it, tit);
11129
11301
  if (tit.type === "attribute" && !config?.exclude?.attribute) {
11130
11302
  it.node.setAttribute(tit.name, data());
11131
11303
  } else if (tit.type === "property" && !config?.exclude?.property) {
@@ -11145,6 +11317,15 @@ ${indentStr}}`;
11145
11317
  }
11146
11318
  }
11147
11319
  }
11320
+ /** key 가 네이티브(DOM/내장) prototype 이 아니라 사용자 클래스나 인스턴스에 정의돼 있으면 true */
11321
+ static isUserMember(obj, key) {
11322
+ for (let p = obj; p; p = Object.getPrototypeOf(p)) {
11323
+ if (!Object.prototype.hasOwnProperty.call(p, key)) continue;
11324
+ const ctor = p === obj ? null : p.constructor;
11325
+ return !(typeof ctor === "function" && /\[native code\]/.test(Function.prototype.toString.call(ctor)));
11326
+ }
11327
+ return false;
11328
+ }
11148
11329
  removeAllEventListener() {
11149
11330
  const m = this.findAllTarget().filter((it) => it.nodeType === "element").flatMap((it) => it.types.map((sit) => ({ ...sit, node: it.node })).filter((it2) => it2.type === "event"));
11150
11331
  m.forEach((tit) => {
@@ -11522,15 +11703,21 @@ ${indentStr}}`;
11522
11703
  const slots = this.findSlots();
11523
11704
  return slots.map((s) => s.nodes());
11524
11705
  }
11525
- append(...args) {
11526
- let targetId;
11527
- let items = [];
11528
- if (typeof args[0] === "string" && args.length > 1) {
11529
- targetId = args[0];
11530
- items = args.slice(1);
11531
- } else {
11532
- items = args;
11706
+ /**
11707
+ * Shared disambiguation for the `(...items)` / `(targetId, ...items)` overload pair.
11708
+ * Items are always Node instances (or strings meant as literal content) coming from
11709
+ * callers; a leading string is therefore always treated as `targetId`, even when no
11710
+ * items follow — this avoids misclassifying `targetId` itself as content when the
11711
+ * caller has zero items to insert (e.g. a decorated method returning undefined).
11712
+ */
11713
+ static parseTargetArgs(args) {
11714
+ if (typeof args[0] === "string") {
11715
+ return { targetId: args[0], items: args.slice(1) };
11533
11716
  }
11717
+ return { targetId: void 0, items: args };
11718
+ }
11719
+ append(...args) {
11720
+ const { targetId, items } = _NodeSlot.parseTargetArgs(args);
11534
11721
  if (!items || items.length === 0) return;
11535
11722
  const slots = this.findSlots(targetId);
11536
11723
  if (!slots || slots.length === 0) return;
@@ -11539,38 +11726,17 @@ ${indentStr}}`;
11539
11726
  }
11540
11727
  }
11541
11728
  appendHtml(...args) {
11542
- let targetId;
11543
- let items = [];
11544
- if (typeof args[0] === "string" && args.length > 1) {
11545
- targetId = args[0];
11546
- items = args.slice(1);
11547
- } else {
11548
- items = args;
11549
- }
11729
+ const { targetId, items } = _NodeSlot.parseTargetArgs(args);
11550
11730
  const slots = this.findSlots(targetId);
11551
11731
  for (const s of slots) s.appendHtml(...items);
11552
11732
  }
11553
11733
  appendText(...args) {
11554
- let targetId;
11555
- let items = [];
11556
- if (typeof args[0] === "string" && args.length > 1) {
11557
- targetId = args[0];
11558
- items = args.slice(1);
11559
- } else {
11560
- items = args;
11561
- }
11734
+ const { targetId, items } = _NodeSlot.parseTargetArgs(args);
11562
11735
  const slots = this.findSlots(targetId);
11563
11736
  for (const s of slots) s.appendText(...items);
11564
11737
  }
11565
11738
  appendCopy(...args) {
11566
- let targetId;
11567
- let items = [];
11568
- if (typeof args[0] === "string" && args.length > 1) {
11569
- targetId = args[0];
11570
- items = args.slice(1);
11571
- } else {
11572
- items = args;
11573
- }
11739
+ const { targetId, items } = _NodeSlot.parseTargetArgs(args);
11574
11740
  if (!items || items.length === 0) return;
11575
11741
  const slots = this.findSlots(targetId);
11576
11742
  if (!slots || slots.length === 0) return;
@@ -11579,14 +11745,7 @@ ${indentStr}}`;
11579
11745
  }
11580
11746
  }
11581
11747
  prepend(...args) {
11582
- let targetId;
11583
- let items = [];
11584
- if (typeof args[0] === "string" && args.length > 1) {
11585
- targetId = args[0];
11586
- items = args.slice(1);
11587
- } else {
11588
- items = args;
11589
- }
11748
+ const { targetId, items } = _NodeSlot.parseTargetArgs(args);
11590
11749
  if (!items || items.length === 0) return;
11591
11750
  const slots = this.findSlots(targetId);
11592
11751
  if (!slots || slots.length === 0) return;
@@ -11595,38 +11754,17 @@ ${indentStr}}`;
11595
11754
  }
11596
11755
  }
11597
11756
  prependHtml(...args) {
11598
- let targetId;
11599
- let items = [];
11600
- if (typeof args[0] === "string" && args.length > 1) {
11601
- targetId = args[0];
11602
- items = args.slice(1);
11603
- } else {
11604
- items = args;
11605
- }
11757
+ const { targetId, items } = _NodeSlot.parseTargetArgs(args);
11606
11758
  const slots = this.findSlots(targetId);
11607
11759
  for (const s of slots) s.prependHtml(...items);
11608
11760
  }
11609
11761
  prependText(...args) {
11610
- let targetId;
11611
- let items = [];
11612
- if (typeof args[0] === "string" && args.length > 1) {
11613
- targetId = args[0];
11614
- items = args.slice(1);
11615
- } else {
11616
- items = args;
11617
- }
11762
+ const { targetId, items } = _NodeSlot.parseTargetArgs(args);
11618
11763
  const slots = this.findSlots(targetId);
11619
11764
  for (const s of slots) s.prependText(...items);
11620
11765
  }
11621
11766
  prependCopy(...args) {
11622
- let targetId;
11623
- let items = [];
11624
- if (typeof args[0] === "string" && args.length > 1) {
11625
- targetId = args[0];
11626
- items = args.slice(1);
11627
- } else {
11628
- items = args;
11629
- }
11767
+ const { targetId, items } = _NodeSlot.parseTargetArgs(args);
11630
11768
  if (!items || items.length === 0) return;
11631
11769
  const slots = this.findSlots(targetId);
11632
11770
  if (!slots || slots.length === 0) return;
@@ -11646,14 +11784,7 @@ ${indentStr}}`;
11646
11784
  }
11647
11785
  }
11648
11786
  replaceChildren(...args) {
11649
- let targetId;
11650
- let items = [];
11651
- if (typeof args[0] === "string" && args.length > 1) {
11652
- targetId = args[0];
11653
- items = args.slice(1);
11654
- } else {
11655
- items = args;
11656
- }
11787
+ const { targetId, items } = _NodeSlot.parseTargetArgs(args);
11657
11788
  const slots = this.findSlots(targetId);
11658
11789
  if (!slots || slots.length === 0) return;
11659
11790
  for (const s of slots) {
@@ -11661,14 +11792,7 @@ ${indentStr}}`;
11661
11792
  }
11662
11793
  }
11663
11794
  replaceChildrenHtml(...args) {
11664
- let targetId;
11665
- let items = [];
11666
- if (typeof args[0] === "string" && args.length > 1) {
11667
- targetId = args[0];
11668
- items = args.slice(1);
11669
- } else {
11670
- items = args;
11671
- }
11795
+ const { targetId, items } = _NodeSlot.parseTargetArgs(args);
11672
11796
  const slots = this.findSlots(targetId);
11673
11797
  if (!slots || slots.length === 0) return;
11674
11798
  for (const s of slots) {
@@ -11676,14 +11800,7 @@ ${indentStr}}`;
11676
11800
  }
11677
11801
  }
11678
11802
  replaceChildrenText(...args) {
11679
- let targetId;
11680
- let items = [];
11681
- if (typeof args[0] === "string" && args.length > 1) {
11682
- targetId = args[0];
11683
- items = args.slice(1);
11684
- } else {
11685
- items = args;
11686
- }
11803
+ const { targetId, items } = _NodeSlot.parseTargetArgs(args);
11687
11804
  const slots = this.findSlots(targetId);
11688
11805
  if (!slots || slots.length === 0) return;
11689
11806
  for (const s of slots) {
@@ -12374,6 +12491,7 @@ ${indentStr}}`;
12374
12491
  var ROUTER_NO_EVENT_MARKER = "__ROUTER_NO_EVENT__";
12375
12492
  var Router = class {
12376
12493
  constructor(config) {
12494
+ this.leaveGuards = /* @__PURE__ */ new Set();
12377
12495
  this._config = config;
12378
12496
  let routeData;
12379
12497
  if (config?.firstUrl) {
@@ -12394,13 +12512,39 @@ ${indentStr}}`;
12394
12512
  this.behaviorSubject = new BehaviorSubject(routeData);
12395
12513
  }
12396
12514
  this.config.window.addEventListener("popstate", (event) => {
12397
- const routeData2 = { ...this.getRouteData(), triggerPoint: "end" };
12398
- this.behaviorSubject.next(routeData2);
12515
+ const publish = () => this.behaviorSubject.next({ ...this.getRouteData(), triggerPoint: "end" });
12516
+ if (!this.leaveGuards.size) return publish();
12517
+ const from = this.value;
12518
+ void this.canLeave(this.getRouteData(), from).then((ok) => {
12519
+ if (ok) publish();
12520
+ else this.config.window.history?.pushState(from.data ?? null, "", from.url);
12521
+ });
12399
12522
  });
12400
12523
  }
12401
12524
  get value() {
12402
12525
  return this.behaviorSubject.getValue();
12403
12526
  }
12527
+ /**
12528
+ * 이동 전 가드 등록. go() 와 뒤로/앞으로 가기(popstate)에 적용된다 (pushState/replaceState 직접 호출은 제외).
12529
+ * 리턴한 함수를 부르면 해제된다.
12530
+ */
12531
+ addLeaveGuard(guard) {
12532
+ this.leaveGuards.add(guard);
12533
+ return () => {
12534
+ this.leaveGuards.delete(guard);
12535
+ };
12536
+ }
12537
+ /** 가드가 하나라도 있는지 (없으면 호출자가 동기로 진행해도 된다) */
12538
+ get hasLeaveGuards() {
12539
+ return this.leaveGuards.size > 0;
12540
+ }
12541
+ /** 등록된 가드를 순서대로 평가. 하나라도 false 면 false */
12542
+ async canLeave(to, from) {
12543
+ for (const guard of [...this.leaveGuards]) {
12544
+ if (await guard(to, from) === false) return false;
12545
+ }
12546
+ return true;
12547
+ }
12404
12548
  get observable() {
12405
12549
  return this.behaviorSubject.asObservable();
12406
12550
  }
@@ -12531,17 +12675,24 @@ ${indentStr}}`;
12531
12675
  if (typeof config === "string") {
12532
12676
  config = { path: config };
12533
12677
  }
12534
- if (config?.replace) {
12535
- this.replace(config.path, config.state);
12536
- } else {
12537
- this.push(config.path, config.state);
12538
- }
12539
- if (config.scrollToTop !== false) {
12540
- try {
12541
- this._config.window?.scrollTo?.(0, 0);
12542
- } catch (e) {
12678
+ const target = config;
12679
+ const navigate = () => {
12680
+ if (target?.replace) {
12681
+ this.replace(target.path, target.state);
12682
+ } else {
12683
+ this.push(target.path, target.state);
12543
12684
  }
12544
- }
12685
+ if (target.scrollToTop !== false) {
12686
+ try {
12687
+ this._config.window?.scrollTo?.(0, 0);
12688
+ } catch (e) {
12689
+ }
12690
+ }
12691
+ };
12692
+ if (!this.leaveGuards.size) return navigate();
12693
+ return this.canLeave(this.getRouteData({ pathOrUrl: this.toUrl(target.path) }), this.value).then((ok) => {
12694
+ if (ok) navigate();
12695
+ });
12545
12696
  }
12546
12697
  toUrl(data) {
12547
12698
  let targetPath;