zova-core 5.1.89 → 5.1.90

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.
Files changed (40) hide show
  1. package/dist/bean/beanContainer.d.ts.map +1 -1
  2. package/dist/bean/resource/error/type.d.ts +8 -0
  3. package/dist/bean/resource/error/type.d.ts.map +1 -1
  4. package/dist/bean/type.d.ts +15 -0
  5. package/dist/bean/type.d.ts.map +1 -1
  6. package/dist/components/component.d.ts +6 -1
  7. package/dist/components/component.d.ts.map +1 -1
  8. package/dist/composables/useController.d.ts.map +1 -1
  9. package/dist/core/component/error.d.ts +7 -0
  10. package/dist/core/component/error.d.ts.map +1 -1
  11. package/dist/core/context/component.d.ts +2 -0
  12. package/dist/core/context/component.d.ts.map +1 -1
  13. package/dist/core/context/hooks.d.ts +3 -2
  14. package/dist/core/context/hooks.d.ts.map +1 -1
  15. package/dist/core/context/meta.d.ts.map +1 -1
  16. package/dist/core/context/state.d.ts +42 -0
  17. package/dist/core/context/state.d.ts.map +1 -1
  18. package/dist/core/sys/config.d.ts +19 -7
  19. package/dist/core/sys/config.d.ts.map +1 -1
  20. package/dist/index.js +477 -126
  21. package/dist/index.js.map +1 -1
  22. package/dist/types/interface/monkey.d.ts +13 -0
  23. package/dist/types/interface/monkey.d.ts.map +1 -1
  24. package/dist/types/utils/powerPartial.d.ts +1 -1
  25. package/dist/types/utils/powerPartial.d.ts.map +1 -1
  26. package/package.json +2 -2
  27. package/src/bean/beanBase.ts +1 -1
  28. package/src/bean/beanContainer.ts +95 -39
  29. package/src/bean/resource/error/type.ts +9 -0
  30. package/src/bean/type.ts +18 -0
  31. package/src/components/component.ts +2 -1
  32. package/src/composables/useController.ts +155 -25
  33. package/src/core/component/error.ts +62 -5
  34. package/src/core/context/component.ts +37 -2
  35. package/src/core/context/hooks.ts +13 -2
  36. package/src/core/context/meta.ts +1 -0
  37. package/src/core/context/state.ts +161 -0
  38. package/src/core/sys/config.ts +32 -7
  39. package/src/types/interface/monkey.ts +15 -0
  40. package/src/types/utils/powerPartial.ts +1 -1
package/dist/index.js CHANGED
@@ -927,7 +927,7 @@ class BeanBase extends BeanBaseSimple {
927
927
  }
928
928
  $controllerMounted(fn) {
929
929
  return this.ctx.util.instanceScope(() => {
930
- return this.ctx.meta.hooks.onMounted(fn);
930
+ return this.ctx.meta.hooks.onMounted(fn, this);
931
931
  });
932
932
  }
933
933
  $customRef(factory) {
@@ -1187,26 +1187,64 @@ class BeanContainer {
1187
1187
  /** @internal */
1188
1188
  dispose() {
1189
1189
  const beanInstances = this[SymbolBeanContainerInstances];
1190
- for (const prop in beanInstances) {
1191
- if (prop.startsWith('$$')) continue;
1192
- const beanInstance = cast(beanInstances[prop]);
1193
- if (beanInstance && !(beanInstance instanceof BeanAopBase) && beanInstance.__dispose__) {
1194
- if (this.containerType === 'sys') {
1195
- this.sys.meta.module._monkeyModuleSync(false, 'beanDispose', undefined, this, beanInstance);
1196
- beanInstance.__dispose__();
1197
- this.sys.meta.module._monkeyModuleSync(false, 'beanDisposed', undefined, this, beanInstance);
1198
- } else {
1199
- this.app.meta.module._monkeyModuleSync(false, 'beanDispose', undefined, this, beanInstance);
1190
+ let error;
1191
+ try {
1192
+ for (const prop in beanInstances) {
1193
+ if (prop.startsWith('$$')) continue;
1194
+ const beanInstance = cast(beanInstances[prop]);
1195
+ if (!beanInstance || beanInstance instanceof BeanAopBase || !beanInstance.__dispose__) continue;
1196
+ try {
1197
+ if (this.containerType === 'sys') {
1198
+ this.sys.meta.module._monkeyModuleSync(false, 'beanDispose', undefined, this, beanInstance);
1199
+ beanInstance.__dispose__();
1200
+ this.sys.meta.module._monkeyModuleSync(false, 'beanDisposed', undefined, this, beanInstance);
1201
+ } else {
1202
+ this.app.meta.module._monkeyModuleSync(false, 'beanDispose', undefined, this, beanInstance);
1203
+ this.runWithInstanceScopeOrAppContext(() => {
1204
+ return beanInstance.__dispose__();
1205
+ });
1206
+ this.app.meta.module._monkeyModuleSync(false, 'beanDisposed', undefined, this, beanInstance);
1207
+ }
1208
+ } catch (err) {
1209
+ error ??= err;
1210
+ }
1211
+ }
1212
+ } finally {
1213
+ this[SymbolBeanContainerInstances] = shallowReactive({});
1214
+ this[SymbolBeanContainerParent] = undefined;
1215
+ this[SymbolGetBeanSelectorInnerPromises] = {};
1216
+ }
1217
+ // eslint-disable no-throw-literal
1218
+ if (error) throw error;
1219
+ }
1220
+
1221
+ /** @internal */
1222
+ retryReset(ownedBeans) {
1223
+ const beanInstances = this[SymbolBeanContainerInstances];
1224
+ let error;
1225
+ try {
1226
+ for (const prop in beanInstances) {
1227
+ const beanInstance = cast(beanInstances[prop]);
1228
+ if (!ownedBeans.has(beanInstance)) continue;
1229
+ if (!beanInstance || beanInstance instanceof BeanAopBase || !beanInstance.__dispose__) continue;
1230
+ try {
1200
1231
  this.runWithInstanceScopeOrAppContext(() => {
1201
1232
  return beanInstance.__dispose__();
1202
1233
  });
1203
- this.app.meta.module._monkeyModuleSync(false, 'beanDisposed', undefined, this, beanInstance);
1234
+ } catch (err) {
1235
+ error ??= err;
1236
+ }
1237
+ }
1238
+ } finally {
1239
+ for (const prop in beanInstances) {
1240
+ if (ownedBeans.has(cast(beanInstances[prop]))) {
1241
+ delete beanInstances[prop];
1204
1242
  }
1205
1243
  }
1244
+ this[SymbolGetBeanSelectorInnerPromises] = {};
1206
1245
  }
1207
- this[SymbolBeanContainerInstances] = shallowReactive({});
1208
- this[SymbolBeanContainerParent] = undefined;
1209
- this[SymbolGetBeanSelectorInnerPromises] = {};
1246
+ // eslint-disable no-throw-literal
1247
+ if (error) throw error;
1210
1248
  }
1211
1249
  get containerType() {
1212
1250
  if (!this.ctx) return 'sys';
@@ -1348,7 +1386,7 @@ class BeanContainer {
1348
1386
  return this[SymbolBeanContainerInstances][key];
1349
1387
  }
1350
1388
  async _getBeanSelectorInnerPromise(recordProp, fullName, markReactive, withSelector, ...args) {
1351
- return await this._newBeanInner(true, recordProp, null, fullName, markReactive, withSelector, ...args);
1389
+ return await this._newBeanInner(true, recordProp, null, fullName, markReactive, withSelector, undefined, ...args);
1352
1390
  }
1353
1391
  _newBeanSimple(A, markReactive, ...args) {
1354
1392
  // prepare
@@ -1362,7 +1400,7 @@ class BeanContainer {
1362
1400
  }
1363
1401
  // async _newBean<T>(beanFullName: string, markReactive?: boolean, ...args): Promise<T>;
1364
1402
  async _newBean(beanFullName, markReactive, ...args) {
1365
- return await this._newBeanInner(false, null, null, beanFullName, markReactive, false, ...args);
1403
+ return await this._newBeanInner(false, null, null, beanFullName, markReactive, false, undefined, ...args);
1366
1404
  }
1367
1405
  // async _newBeanSelector<T>(beanFullName: string, markReactive?: boolean, selector?: string, ...args): Promise<T>;
1368
1406
  async _newBeanSelector(beanFullName, markReactive, selector, ...args) {
@@ -1370,13 +1408,13 @@ class BeanContainer {
1370
1408
  }
1371
1409
 
1372
1410
  /** @internal */
1373
- async _newBeanInner(record, recordProp, controllerData, beanFullName, markReactive, withSelector, ...args) {
1411
+ async _newBeanInner(record, recordProp, controllerData, beanFullName, markReactive, withSelector, onBeanRecorded, ...args) {
1374
1412
  // bean options
1375
1413
  const beanOptions = await this._getBeanOptionsForce(beanFullName);
1376
1414
  if (!beanOptions) {
1377
1415
  // class
1378
1416
  if (typeof beanFullName === 'function' && isClass(beanFullName)) {
1379
- return await this._createBeanInstance(record, recordProp, controllerData, undefined, beanFullName, args, false, markReactive, withSelector);
1417
+ return await this._createBeanInstance(record, recordProp, controllerData, undefined, beanFullName, args, false, markReactive, withSelector, onBeanRecorded);
1380
1418
  }
1381
1419
  // throw new Error(`bean not found: ${beanFullName}`);
1382
1420
  return null;
@@ -1384,7 +1422,7 @@ class BeanContainer {
1384
1422
  // beanFullName
1385
1423
  return await this._createBeanInstance(record, recordProp, controllerData, beanOptions.beanFullName, beanOptions.beanClass, args, cast(beanOptions.scene) === 'aop',
1386
1424
  // default is true: same as inject prop
1387
- markReactive ?? beanOptions.markReactive ?? true, withSelector);
1425
+ markReactive ?? beanOptions.markReactive ?? true, withSelector, onBeanRecorded);
1388
1426
  }
1389
1427
  _getBeanFullNameByComposableOrClassSync(beanFullName) {
1390
1428
  // bean options
@@ -1424,7 +1462,7 @@ class BeanContainer {
1424
1462
  // get
1425
1463
  return appResource.getBean(beanFullName);
1426
1464
  }
1427
- async _createBeanInstance(record, recordProp, controllerData, beanFullName, beanClass, args, aop, markReactive, withSelector) {
1465
+ async _createBeanInstance(record, recordProp, controllerData, beanFullName, beanClass, args, aop, markReactive, withSelector, onBeanRecorded) {
1428
1466
  // prepare
1429
1467
  const beanInstance = await this._prepareBeanInstance(beanFullName, beanClass, args, markReactive, aop);
1430
1468
  // special for controller
@@ -1444,6 +1482,7 @@ class BeanContainer {
1444
1482
  if (recordProp) {
1445
1483
  this.__recordProp(recordProp, fullName, beanInstance, true);
1446
1484
  }
1485
+ onBeanRecorded?.(beanInstance);
1447
1486
  }
1448
1487
  // init
1449
1488
  await this._initBeanInstance(beanFullName, beanInstance, args);
@@ -1640,7 +1679,7 @@ class BeanContainer {
1640
1679
  targetInstance = await this._getBeanSelectorInner(true, recordProp, targetBeanFullName, markReactive, selectorInfo.withSelector, ...selectorInfo.args);
1641
1680
  } else if (injectionScope === 'new') {
1642
1681
  // not record prop
1643
- targetInstance = await this._newBeanInner(false, null, null, targetBeanFullName, markReactive, selectorInfo.withSelector, ...selectorInfo.args);
1682
+ targetInstance = await this._newBeanInner(false, null, null, targetBeanFullName, markReactive, selectorInfo.withSelector, undefined, ...selectorInfo.args);
1644
1683
  }
1645
1684
  return targetInstance;
1646
1685
  }
@@ -2448,7 +2487,8 @@ function configDefault(env) {
2448
2487
  flavor: env.META_FLAVOR,
2449
2488
  mode: env.META_MODE,
2450
2489
  appMode: env.META_APP_MODE
2451
- }
2490
+ },
2491
+ boundary: {}
2452
2492
  };
2453
2493
  return config;
2454
2494
  }
@@ -3476,6 +3516,112 @@ const ClientOnly = defineComponent({
3476
3516
  }
3477
3517
  });
3478
3518
 
3519
+ class AppError extends ErrorClass {
3520
+ /** @internal */
3521
+ async initialize() {
3522
+ await super.initialize();
3523
+ // errorHandler
3524
+ this.app.vue.config.errorHandler = (err, instance, info) => {
3525
+ return this._handleError(err, instance, info);
3526
+ };
3527
+ // unhandledrejection
3528
+ if (process.env.CLIENT) {
3529
+ window.addEventListener('unhandledrejection', event => {
3530
+ event.preventDefault();
3531
+ this._handleUnhandledError(event.reason, 'unhandledrejection');
3532
+ return false;
3533
+ });
3534
+ window.addEventListener('error', event => {
3535
+ event.preventDefault();
3536
+ this._handleUnhandledError(event.error, 'unhandlederror');
3537
+ return false;
3538
+ });
3539
+ }
3540
+ }
3541
+
3542
+ /** @internal */
3543
+ createScopeError(moduleScope, errorCode) {
3544
+ const self = this;
3545
+ return {
3546
+ throw: (...args) => {
3547
+ return self.throw(moduleScope, errorCode, ...args);
3548
+ },
3549
+ parseFail: (...args) => {
3550
+ return self.parseFail(moduleScope, errorCode, ...args);
3551
+ }
3552
+ };
3553
+ }
3554
+ handleLoadError(originalError, ctx, instance) {
3555
+ const err = normalizeError(originalError);
3556
+ const load = {
3557
+ kind: 'controller-load',
3558
+ ctx,
3559
+ originalError,
3560
+ disposition: 'fallback'
3561
+ };
3562
+ this._handleError(err, instance, 'useController:load', load);
3563
+ return {
3564
+ err,
3565
+ disposition: load.disposition
3566
+ };
3567
+ }
3568
+ _handleUnhandledError(error, infoDefault) {
3569
+ if (error instanceof Error) {
3570
+ const errorInfo = error[SymbolErrorInstanceInfo];
3571
+ if (errorInfo) {
3572
+ delete error[SymbolErrorInstanceInfo];
3573
+ }
3574
+ // should not catch error
3575
+ this.app.vue.config.errorHandler(error, errorInfo?.instance, errorInfo?.info || infoDefault);
3576
+ }
3577
+ }
3578
+ _handleError(err, instance, info, load) {
3579
+ if (!this.app) {
3580
+ // means destroyed
3581
+ console.error(err);
3582
+ return;
3583
+ }
3584
+ const err2 = this.app.meta.event.emitSync('app:errorHandler', {
3585
+ err: err,
3586
+ instance,
3587
+ info,
3588
+ load
3589
+ }, data => {
3590
+ return data.err;
3591
+ });
3592
+ // only log error in client
3593
+ if (process.env.CLIENT) {
3594
+ if (!err2 || !(err2 instanceof Error)) return err2;
3595
+ console.error(err2);
3596
+ }
3597
+ return err2;
3598
+ }
3599
+ }
3600
+ function normalizeError(error) {
3601
+ if (error instanceof Error) return error;
3602
+ const normalized = new Error(getErrorMessage(error));
3603
+ if (error && (typeof error === 'object' || typeof error === 'function')) {
3604
+ for (const key of ['code', 'status', 'pagePath', 'url']) {
3605
+ const descriptor = Object.getOwnPropertyDescriptor(error, key);
3606
+ if (!descriptor || !('value' in descriptor)) continue;
3607
+ const value = descriptor.value;
3608
+ if ((key === 'code' || key === 'status') && (typeof value === 'number' || typeof value === 'string')) {
3609
+ normalized[key] = value;
3610
+ }
3611
+ if ((key === 'pagePath' || key === 'url') && typeof value === 'string') {
3612
+ normalized[key] = value;
3613
+ }
3614
+ }
3615
+ }
3616
+ return normalized;
3617
+ }
3618
+ function getErrorMessage(error) {
3619
+ if (error === null || error === undefined || typeof error === 'string' || typeof error === 'number' || typeof error === 'boolean' || typeof error === 'bigint' || typeof error === 'symbol') {
3620
+ return String(error);
3621
+ }
3622
+ return 'Unknown error';
3623
+ }
3624
+
3479
3625
  const SymbolTypeSSRRenderOriginal = Symbol('SymbolTypeSSRRenderOriginal');
3480
3626
  const SymbolTypeSSRRenderResetCount = Symbol('SymbolTypeSSRRenderResetCount');
3481
3627
  class CtxComponent extends BeanSimple {
@@ -3494,8 +3640,12 @@ class CtxComponent extends BeanSimple {
3494
3640
  const self = this;
3495
3641
  instance[renderMethod] = function (...args) {
3496
3642
  if (instance.isUnmounted) return;
3497
- if (!self.ctx.meta.state.inited.state) {
3498
- return self._bean_render_original.call(this, ...args);
3643
+ const loadStatus = self.ctx.meta.state.loadStatus;
3644
+ if (loadStatus === 'loading') {
3645
+ return self.ctx.meta.state.loadLoadingVisible ? self._renderLoading() : null;
3646
+ }
3647
+ if (loadStatus === 'error') {
3648
+ return self._renderError(self.ctx.meta.state.loadError);
3499
3649
  }
3500
3650
  const render = self._getRender();
3501
3651
  if (!render) {
@@ -3549,6 +3699,28 @@ class CtxComponent extends BeanSimple {
3549
3699
  this._renderPatched = false;
3550
3700
  this._ssrRenderReset = false;
3551
3701
  }
3702
+ _renderLoading() {
3703
+ const renderMode = this.ctx.meta.state.loadRenderMode;
3704
+ const controller = this.ctx.meta.state.loadController;
3705
+ if (controller?.renderLoading) {
3706
+ return controller.renderLoading(renderMode);
3707
+ }
3708
+ const renderLoading = this.sys.config.boundary.renderLoading;
3709
+ return renderLoading?.(this.ctx, renderMode) ?? null;
3710
+ }
3711
+ _renderError(error) {
3712
+ const renderMode = this.ctx.meta.state.loadRenderMode;
3713
+ const retry = this.ctx.meta.state.loadRetry;
3714
+ const controller = this.ctx.meta.state.loadController;
3715
+ if (controller?.renderError) {
3716
+ return controller.renderError(error, renderMode, retry);
3717
+ }
3718
+ const renderError = this.sys.config.boundary.renderError;
3719
+ if (renderError) {
3720
+ return renderError(this.ctx, error, renderMode, retry);
3721
+ }
3722
+ return _renderErrorDefault(error);
3723
+ }
3552
3724
  _getRender() {
3553
3725
  const render = this.bean._getBeanSyncOnly(BeanControllerIdentifier);
3554
3726
  if (!render) return;
@@ -3557,6 +3729,12 @@ class CtxComponent extends BeanSimple {
3557
3729
  return this.bean._getBeanSyncOnly(BeanRenderIdentifier);
3558
3730
  }
3559
3731
  }
3732
+ function _renderErrorDefault(error) {
3733
+ if (error instanceof Error) {
3734
+ return error.message;
3735
+ }
3736
+ return String(error);
3737
+ }
3560
3738
 
3561
3739
  const SymbolHooksFns = Symbol('SymbolHooksFns');
3562
3740
  const SymbolHooksState = Symbol('SymbolHooksState');
@@ -3573,9 +3751,19 @@ class CtxHooks extends BeanSimple {
3573
3751
  onCreated(fn) {
3574
3752
  this._onHook('created', fn);
3575
3753
  }
3576
- onMounted(fn) {
3754
+ onMounted(fn, owner) {
3755
+ if (owner && !this.ctx.meta.state.isLoadBeanActive(owner)) return;
3577
3756
  this._onHook('mounted', fn);
3578
3757
  }
3758
+
3759
+ /** @internal */
3760
+ onHydrated(fn) {
3761
+ this._onHook('hydrated', fn);
3762
+ }
3763
+ clearMounted() {
3764
+ if (this[SymbolHooksState].mounted) return;
3765
+ this[SymbolHooksFns].mounted = undefined;
3766
+ }
3579
3767
  _onHook(type, fn) {
3580
3768
  if (this[SymbolHooksState][type]) {
3581
3769
  this.ctx.util.instanceScope(fn);
@@ -3605,17 +3793,142 @@ class CtxState extends BeanSimple {
3605
3793
  super(...args);
3606
3794
  this._id = void 0;
3607
3795
  this._inited = void 0;
3796
+ this._loadStatus = 'loading';
3797
+ this._loadRenderMode = 'block';
3798
+ this._loadRetryEnabled = false;
3799
+ this._loadRetryReady = false;
3800
+ this._loadRetry = void 0;
3801
+ this._loadAttemptId = 0;
3802
+ this._loadBeanAttempts = new WeakMap();
3803
+ this._loadError = void 0;
3804
+ this._loadController = void 0;
3805
+ /** @internal SSR hydration protocol key. */
3806
+ this.controllerLoadKey = void 0;
3807
+ this._loadLoadingVisible = false;
3808
+ this._loadLoadingTimer = void 0;
3608
3809
  }
3609
3810
  __init__() {
3610
3811
  this._id = ++__id;
3611
3812
  this._inited = StateLock.create();
3612
3813
  }
3814
+
3815
+ /** @internal */
3816
+ dispose() {
3817
+ this._clearLoadLoadingTimer();
3818
+ }
3613
3819
  get id() {
3614
3820
  return this._id;
3615
3821
  }
3616
3822
  get inited() {
3617
3823
  return this._inited;
3618
3824
  }
3825
+ get loadStatus() {
3826
+ return this._loadStatus;
3827
+ }
3828
+ get loadRenderMode() {
3829
+ return this._loadRenderMode;
3830
+ }
3831
+ get loadRetry() {
3832
+ if (!this.isLoadRetryAvailable) return undefined;
3833
+ return this._loadRetry;
3834
+ }
3835
+ get isLoadRetryAvailable() {
3836
+ return this._loadRetryEnabled && this._loadRetryReady && this._loadStatus === 'error' && !this.ctx.disposed && !!this._loadRetry;
3837
+ }
3838
+ get loadAttemptId() {
3839
+ return this._loadAttemptId;
3840
+ }
3841
+ get loadError() {
3842
+ return this._loadError;
3843
+ }
3844
+ get loadController() {
3845
+ return this._loadController;
3846
+ }
3847
+ get loadLoadingVisible() {
3848
+ return this._loadLoadingVisible;
3849
+ }
3850
+ setLoadController(controller) {
3851
+ this._loadController = controller;
3852
+ }
3853
+ setLoadRenderMode(renderMode) {
3854
+ this._loadRenderMode = renderMode === 'inline' ? 'inline' : 'block';
3855
+ }
3856
+ setLoadRetryEnabled(retryEnabled) {
3857
+ this._loadRetryEnabled = retryEnabled === true;
3858
+ }
3859
+ setLoadRetry(retry) {
3860
+ this._loadRetry = retry;
3861
+ }
3862
+ setLoadRetryReady() {
3863
+ this._loadRetryReady = true;
3864
+ }
3865
+ beginLoadAttempt(delay) {
3866
+ this._clearLoadLoadingTimer();
3867
+ this._loadAttemptId++;
3868
+ this._loadStatus = 'loading';
3869
+ this._loadError = undefined;
3870
+ this._loadController = undefined;
3871
+ this._loadLoadingVisible = false;
3872
+ if (delay !== undefined) {
3873
+ this.startLoadLoading(delay);
3874
+ }
3875
+ return this._loadAttemptId;
3876
+ }
3877
+ isLoadAttemptActive(attemptId) {
3878
+ return !this.ctx.disposed && this._loadAttemptId === attemptId;
3879
+ }
3880
+ setLoadBeanAttempt(bean, attemptId) {
3881
+ this._loadBeanAttempts.set(bean, attemptId);
3882
+ }
3883
+ getLoadBeanAttempt(bean) {
3884
+ return this._loadBeanAttempts.get(bean);
3885
+ }
3886
+ isLoadBeanActive(bean) {
3887
+ const attemptId = this._loadBeanAttempts.get(bean);
3888
+ return attemptId === undefined || this.isLoadAttemptActive(attemptId);
3889
+ }
3890
+ startLoadLoading(delay) {
3891
+ this._clearLoadLoadingTimer();
3892
+ if (delay === 0) {
3893
+ this._loadLoadingVisible = true;
3894
+ return;
3895
+ }
3896
+ this._loadLoadingTimer = setTimeout(() => {
3897
+ this._loadLoadingTimer = undefined;
3898
+ if (this.ctx.disposed || this._loadStatus !== 'loading') return;
3899
+ this._loadLoadingVisible = true;
3900
+ }, delay);
3901
+ }
3902
+
3903
+ /**
3904
+ * Resolves the Controller-load terminal lock consumed by the custom SSR hydration runtime.
3905
+ * A settled load is not necessarily a successful load: error and handled control-flow paths
3906
+ * must also allow the rendered boundary subtree to hydrate.
3907
+ */
3908
+ settleLoad() {
3909
+ this._inited.touch();
3910
+ }
3911
+ setLoadReady() {
3912
+ this._clearLoadLoadingTimer();
3913
+ this._loadError = undefined;
3914
+ this._loadStatus = 'ready';
3915
+ this.settleLoad();
3916
+ }
3917
+ setLoadError(error) {
3918
+ this._clearLoadLoadingTimer();
3919
+ this._loadError = error;
3920
+ this._loadStatus = 'error';
3921
+ this.settleLoad();
3922
+ }
3923
+ setLoadHandled() {
3924
+ this._clearLoadLoadingTimer();
3925
+ this.settleLoad();
3926
+ }
3927
+ _clearLoadLoadingTimer() {
3928
+ if (this._loadLoadingTimer === undefined) return;
3929
+ clearTimeout(this._loadLoadingTimer);
3930
+ this._loadLoadingTimer = undefined;
3931
+ }
3619
3932
  }
3620
3933
 
3621
3934
  class CtxMeta extends BeanSimple {
@@ -3638,6 +3951,7 @@ class CtxMeta extends BeanSimple {
3638
3951
 
3639
3952
  /** @internal */
3640
3953
  dispose() {
3954
+ this.state.dispose();
3641
3955
  this.component.dispose();
3642
3956
  this.hooks.dispose();
3643
3957
  }
@@ -3758,6 +4072,7 @@ function useController(controllerBeanFullName, renderBeanFullName, styleBeanFull
3758
4072
  async function _useController(controllerData, controllerBeanFullName, renderBeanFullName, styleBeanFullName) {
3759
4073
  // ctx
3760
4074
  const ctx = new ZovaContext(getCurrentInstance());
4075
+ setLoadBoundaryOptions(controllerBeanFullName);
3761
4076
  // ctx: monkey
3762
4077
  if (ctx.app) {
3763
4078
  ctx.app.meta.module._monkeyModuleSync(true, 'appContextInitialize', undefined, ctx);
@@ -3768,54 +4083,159 @@ async function _useController(controllerData, controllerBeanFullName, renderBean
3768
4083
  if (ctx.app) {
3769
4084
  ctx.app.meta.module._monkeyModuleSync(true, 'controllerDataPrepare', undefined, controllerData, ctx);
3770
4085
  }
3771
- if (process.env.CLIENT) {
3772
- // dispose
3773
- onBeforeUnmount(() => {
3774
- if (ctx.disposed) return;
3775
- // undefined better than null
3776
- setControllerRef(ctx, false);
3777
- if (ctx.bean !== ctx.app.bean) {
3778
- ctx.bean.dispose();
3779
- }
3780
- });
3781
- onUnmounted(() => {
3782
- ctx.dispose();
3783
- });
4086
+ let retryPromise;
4087
+ let loadBeans = new Set();
4088
+ function setLoadBoundaryOptions(controllerBeanFullName) {
4089
+ const boundary = controllerBeanFullName.$componentOptions?.boundary;
4090
+ ctx.meta.state.setLoadRenderMode(boundary?.renderMode);
4091
+ ctx.meta.state.setLoadRetryEnabled(boundary?.retry);
4092
+ }
4093
+ function isAttemptActive(attemptId) {
4094
+ return ctx.meta.state.isLoadAttemptActive(attemptId);
3784
4095
  }
3785
- async function __load() {
4096
+ function getLoadLoadingDelay() {
4097
+ const delay = controllerBeanFullName.$componentOptions?.boundary?.loading?.delay ?? sys.config.boundary.loading?.delay;
4098
+ const delayNumber = Number(delay);
4099
+ return Number.isFinite(delayNumber) && delayNumber > 0 ? delayNumber : 0;
4100
+ }
4101
+ async function __loadBeans(attemptId) {
3786
4102
  // controller
3787
- if (ctx.disposed) return;
3788
- await ctx.bean._newBeanInner(true, BeanControllerIdentifier, controllerData, controllerBeanFullName, true, false);
4103
+ if (!isAttemptActive(attemptId)) return false;
4104
+ await ctx.bean._newBeanInner(true, BeanControllerIdentifier, controllerData, controllerBeanFullName, true, false, controller => {
4105
+ loadBeans.add(controller);
4106
+ ctx.meta.state.setLoadBeanAttempt(controller, attemptId);
4107
+ if (!isAttemptActive(attemptId)) return;
4108
+ ctx.meta.state.setLoadController(controller);
4109
+ setLoadBoundaryOptions(controller.constructor);
4110
+ });
4111
+ if (!isAttemptActive(attemptId)) return false;
4112
+ // style
3789
4113
  if (styleBeanFullName) {
3790
- if (ctx.disposed) return;
3791
- await ctx.bean._newBeanInner(true, BeanStyleIdentifier, undefined, styleBeanFullName, true, false);
4114
+ await ctx.bean._newBeanInner(true, BeanStyleIdentifier, undefined, styleBeanFullName, true, false, style => {
4115
+ loadBeans.add(style);
4116
+ ctx.meta.state.setLoadBeanAttempt(style, attemptId);
4117
+ });
3792
4118
  }
4119
+ if (!isAttemptActive(attemptId)) return false;
4120
+ // render
3793
4121
  if (renderBeanFullName) {
3794
- if (ctx.disposed) return;
3795
- await ctx.bean._newBeanInner(true, BeanRenderIdentifier, undefined, renderBeanFullName, true, false);
4122
+ await ctx.bean._newBeanInner(true, BeanRenderIdentifier, undefined, renderBeanFullName, true, false, render => {
4123
+ loadBeans.add(render);
4124
+ ctx.meta.state.setLoadBeanAttempt(render, attemptId);
4125
+ });
3796
4126
  }
3797
- // must touch inited on server/client, force router.use effect
3798
- if (ctx.disposed) return;
3799
- ctx.meta.state.inited.touch();
4127
+ return isAttemptActive(attemptId);
4128
+ }
4129
+ async function __load(attemptId) {
4130
+ if (!(await __loadBeans(attemptId))) return;
4131
+ if (!isAttemptActive(attemptId)) return;
4132
+ ctx.meta.state.setLoadReady();
3800
4133
  if (process.env.CLIENT) {
3801
4134
  ctx.util.instanceScope(() => {
3802
4135
  queuePostFlushCb(() => {
4136
+ if (!isAttemptActive(attemptId)) return;
3803
4137
  setControllerRef(ctx, true);
3804
4138
  ctx.meta.hooks.invokeHook('mounted');
3805
4139
  });
3806
4140
  });
3807
4141
  }
3808
4142
  }
4143
+ async function runLoadAttempt(attemptId, beforeLoad) {
4144
+ try {
4145
+ await beforeLoad?.();
4146
+ if (!isAttemptActive(attemptId)) return;
4147
+ await __load(attemptId);
4148
+ } catch (err) {
4149
+ if (!isAttemptActive(attemptId)) return;
4150
+ const result = ctx.app?.meta.error.handleLoadError(err, ctx, ctx.instance) ?? {
4151
+ err: normalizeError(err),
4152
+ disposition: 'fallback'
4153
+ };
4154
+ if (!isAttemptActive(attemptId)) return;
4155
+ if (result.disposition === 'handled') {
4156
+ ctx.meta.state.setLoadHandled();
4157
+ return;
4158
+ }
4159
+ ctx.app?.meta.module._monkeyModuleSync(true, 'controllerLoad', undefined, {
4160
+ phase: 'fallback',
4161
+ ctx,
4162
+ controllerBeanFullName,
4163
+ controllerBeanName: typeof controllerBeanFullName === 'string' ? controllerBeanFullName : controllerBeanFullName.name,
4164
+ error: result.err,
4165
+ controllerRecorded: !!ctx.meta.state.loadController
4166
+ });
4167
+ if (!isAttemptActive(attemptId)) return;
4168
+ ctx.meta.state.setLoadError(result.err);
4169
+ }
4170
+ }
4171
+ function retry() {
4172
+ if (!ctx.meta.state.isLoadRetryAvailable) return Promise.resolve();
4173
+ if (retryPromise) return retryPromise;
4174
+ const promise = (async () => {
4175
+ const attemptId = ctx.meta.state.beginLoadAttempt(getLoadLoadingDelay());
4176
+ const previousLoadBeans = loadBeans;
4177
+ loadBeans = new Set();
4178
+ ctx.meta.hooks.clearMounted();
4179
+ setControllerRef(ctx, false);
4180
+ await runLoadAttempt(attemptId, () => {
4181
+ ctx.bean.retryReset(previousLoadBeans);
4182
+ });
4183
+ })();
4184
+ retryPromise = promise;
4185
+ void promise.finally(() => {
4186
+ if (retryPromise === promise) {
4187
+ retryPromise = undefined;
4188
+ }
4189
+ });
4190
+ return promise;
4191
+ }
4192
+ ctx.meta.state.setLoadRetry(retry);
4193
+ if (process.env.CLIENT) {
4194
+ ctx.meta.hooks.onHydrated(() => {
4195
+ if (!ctx.disposed) {
4196
+ ctx.meta.state.setLoadRetryReady();
4197
+ }
4198
+ });
4199
+ // dispose
4200
+ onBeforeUnmount(() => {
4201
+ if (ctx.disposed) return;
4202
+ // undefined better than null
4203
+ setControllerRef(ctx, false);
4204
+ if (ctx.bean !== ctx.app.bean) {
4205
+ ctx.bean.dispose();
4206
+ }
4207
+ });
4208
+ onUnmounted(() => {
4209
+ ctx.dispose();
4210
+ });
4211
+ }
3809
4212
 
3810
4213
  // load
3811
4214
  ctx.meta.hooks.onCreated(async () => {
3812
4215
  if (ctx.disposed) return;
3813
- try {
3814
- return await __load();
3815
- } catch (err) {
3816
- if (ctx.disposed) return;
3817
- throw err;
4216
+ const loadEvent = {
4217
+ phase: 'prepare',
4218
+ ctx,
4219
+ controllerBeanFullName,
4220
+ controllerBeanName: typeof controllerBeanFullName === 'string' ? controllerBeanFullName : controllerBeanFullName.name
4221
+ };
4222
+ ctx.app?.meta.module._monkeyModuleSync(true, 'controllerLoad', undefined, loadEvent);
4223
+ if (loadEvent.replayError) {
4224
+ const attemptId = ctx.meta.state.beginLoadAttempt();
4225
+ if (loadEvent.replayControllerRecorded) {
4226
+ try {
4227
+ await __loadBeans(attemptId);
4228
+ } catch {
4229
+ // The server's fallback decision is authoritative during initial hydration.
4230
+ }
4231
+ }
4232
+ if (!ctx.disposed) {
4233
+ ctx.meta.state.setLoadError(loadEvent.replayError);
4234
+ }
4235
+ return;
3818
4236
  }
4237
+ const attemptId = ctx.meta.state.beginLoadAttempt(getLoadLoadingDelay());
4238
+ await runLoadAttempt(attemptId);
3819
4239
  });
3820
4240
  if (process.env.SERVER) {
3821
4241
  onServerPrefetch(() => {
@@ -3836,11 +4256,11 @@ function setControllerRef(ctx, on) {
3836
4256
  }
3837
4257
  }
3838
4258
 
3839
- function createZovaComponentPage(controller, render, style) {
4259
+ function createZovaComponentPage(controller, render, style, componentOptions) {
3840
4260
  return defineComponent(() => {
3841
4261
  useControllerPage(controller, render, style);
3842
4262
  return () => {};
3843
- });
4263
+ }, prepareComponentOptions(componentOptions));
3844
4264
  }
3845
4265
  function prepareComponentOptions(componentOptions) {
3846
4266
  return Object.assign({
@@ -3882,75 +4302,6 @@ class AppComponent extends BeanSimple {
3882
4302
  }
3883
4303
  }
3884
4304
 
3885
- class AppError extends ErrorClass {
3886
- /** @internal */
3887
- async initialize() {
3888
- await super.initialize();
3889
- // errorHandler
3890
- this.app.vue.config.errorHandler = (err, instance, info) => {
3891
- return this._handleError(err, instance, info);
3892
- };
3893
- // unhandledrejection
3894
- if (process.env.CLIENT) {
3895
- window.addEventListener('unhandledrejection', event => {
3896
- event.preventDefault();
3897
- this._handleUnhandledError(event.reason, 'unhandledrejection');
3898
- return false;
3899
- });
3900
- window.addEventListener('error', event => {
3901
- event.preventDefault();
3902
- this._handleUnhandledError(event.error, 'unhandlederror');
3903
- return false;
3904
- });
3905
- }
3906
- }
3907
-
3908
- /** @internal */
3909
- createScopeError(moduleScope, errorCode) {
3910
- const self = this;
3911
- return {
3912
- throw: (...args) => {
3913
- return self.throw(moduleScope, errorCode, ...args);
3914
- },
3915
- parseFail: (...args) => {
3916
- return self.parseFail(moduleScope, errorCode, ...args);
3917
- }
3918
- };
3919
- }
3920
- _handleUnhandledError(error, infoDefault) {
3921
- if (error instanceof Error) {
3922
- const errorInfo = error[SymbolErrorInstanceInfo];
3923
- if (errorInfo) {
3924
- delete error[SymbolErrorInstanceInfo];
3925
- }
3926
- // should not catch error
3927
- this.app.vue.config.errorHandler(error, errorInfo?.instance, errorInfo?.info || infoDefault);
3928
- }
3929
- }
3930
- _handleError(err, instance, info) {
3931
- if (!this.app) {
3932
- // means destroyed
3933
- console.error(err);
3934
- return;
3935
- }
3936
- const err2 = this.app.meta.event.emitSync('app:errorHandler', {
3937
- err: err,
3938
- instance,
3939
- info
3940
- }, data => {
3941
- return data.err;
3942
- });
3943
- // only log error in client
3944
- if (process.env.CLIENT) {
3945
- if (!err2 || !(err2 instanceof Error)) return err2;
3946
- if (!info || !['useMutationData'].includes(info)) {
3947
- console.error(err2);
3948
- }
3949
- }
3950
- return err2;
3951
- }
3952
- }
3953
-
3954
4305
  class AppModule extends BeanSimple {
3955
4306
  constructor(...args) {
3956
4307
  super(...args);
@@ -4437,5 +4788,5 @@ function $protocolKey(key) {
4437
4788
  return key;
4438
4789
  }
4439
4790
 
4440
- export { $getBeanName, $protocolKey, AppError, AppLocale, AppMeta, AppMetadata, AppModule, AppResource, BeanAopBase, BeanAopMethodBase, BeanBase, BeanBaseSimple, BeanContainer, BeanControllerBase, BeanControllerIdentifier, BeanControllerLike, BeanControllerPageBase, BeanInfo, BeanRenderBase, BeanRenderIdentifier, BeanRenderLike, BeanScopeBase, BeanScopeError, BeanScopeLocale, BeanScopeUtil, BeanSimple, BeanStyleBase, BeanStyleIdentifier, ClientOnly, CtxComponent, CtxMeta, CtxState, CtxUtil, DecoratorBeanFullName, ErrorClass, LocaleModuleNameSeparator, LocalePattern, MixinClass, OmitClass, PartialClass, PickClass, PluginBean, PluginZova, Preload, ProxyDisable, StateLock, SymbolBeanContainerInstances, SymbolBeanFullName, SymbolBeanInstanceKey, SymbolController, SymbolControllerRefDisable, SymbolDecoratorBeanInfo, SymbolDecoratorPreload, SymbolDecoratorProxyDisable, SymbolDecoratorUse, SymbolDecoratorVirtual, SymbolDecoratorVueElements, SymbolErrorInstanceInfo, SymbolInited, SymbolInstalled, SymbolMappedClassMetadataKeys, SymbolModuleBelong, SymbolModuleName, SysComponent, SysError, SysLocale, SysLogger, SysMeta, SysModule, SysUtil, Use, UseScope, Virtual, ZovaApplication, ZovaContext, ZovaSys, __prepareInjectSelectorInfo, appMetadata, appResource, beanFullNameFromOnionName, bootstrap, cast, configDefault, constantDefault, convertToUnit, copyMetadataOfClasses, copyProperties, copyPropertiesOfClasses, createBeanDecorator, createVueDecorator, createZovaComponentAsync, createZovaComponentPage, deepEqual, deepExtend, disposeInstance, errorsInternal, formatLoggerConsole, formatLoggerFilter, functionNoop, getLoggerFilterLevel, getMappedClassMetadataKeys, isHttpUrl, isUuid, localeDefault, modelGet, modelSet, objectAssignReactive, onionNameFromBeanFullName, polyfillDispose, prepareComponentOptions, registerMappedClassMetadataKey, setLoggerFilterLevel, ssr_load_local, sys, throwErrorComponentUnmounted, useApp, useComputed, useContext, useController, useControllerPage, usePrepareArg, usePrepareArgs, uuid };
4791
+ export { $getBeanName, $protocolKey, AppError, AppLocale, AppMeta, AppMetadata, AppModule, AppResource, BeanAopBase, BeanAopMethodBase, BeanBase, BeanBaseSimple, BeanContainer, BeanControllerBase, BeanControllerIdentifier, BeanControllerLike, BeanControllerPageBase, BeanInfo, BeanRenderBase, BeanRenderIdentifier, BeanRenderLike, BeanScopeBase, BeanScopeError, BeanScopeLocale, BeanScopeUtil, BeanSimple, BeanStyleBase, BeanStyleIdentifier, ClientOnly, CtxComponent, CtxMeta, CtxState, CtxUtil, DecoratorBeanFullName, ErrorClass, LocaleModuleNameSeparator, LocalePattern, MixinClass, OmitClass, PartialClass, PickClass, PluginBean, PluginZova, Preload, ProxyDisable, StateLock, SymbolBeanContainerInstances, SymbolBeanFullName, SymbolBeanInstanceKey, SymbolController, SymbolControllerRefDisable, SymbolDecoratorBeanInfo, SymbolDecoratorPreload, SymbolDecoratorProxyDisable, SymbolDecoratorUse, SymbolDecoratorVirtual, SymbolDecoratorVueElements, SymbolErrorInstanceInfo, SymbolInited, SymbolInstalled, SymbolMappedClassMetadataKeys, SymbolModuleBelong, SymbolModuleName, SysComponent, SysError, SysLocale, SysLogger, SysMeta, SysModule, SysUtil, Use, UseScope, Virtual, ZovaApplication, ZovaContext, ZovaSys, __prepareInjectSelectorInfo, appMetadata, appResource, beanFullNameFromOnionName, bootstrap, cast, configDefault, constantDefault, convertToUnit, copyMetadataOfClasses, copyProperties, copyPropertiesOfClasses, createBeanDecorator, createVueDecorator, createZovaComponentAsync, createZovaComponentPage, deepEqual, deepExtend, disposeInstance, errorsInternal, formatLoggerConsole, formatLoggerFilter, functionNoop, getLoggerFilterLevel, getMappedClassMetadataKeys, isHttpUrl, isUuid, localeDefault, modelGet, modelSet, normalizeError, objectAssignReactive, onionNameFromBeanFullName, polyfillDispose, prepareComponentOptions, registerMappedClassMetadataKey, setLoggerFilterLevel, ssr_load_local, sys, throwErrorComponentUnmounted, useApp, useComputed, useContext, useController, useControllerPage, usePrepareArg, usePrepareArgs, uuid };
4441
4792
  //# sourceMappingURL=index.js.map