cabloy 5.1.171 → 5.1.173
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/.cabloy-version +1 -1
- package/CHANGELOG.md +18 -0
- package/package.json +2 -2
- package/repo-docs/frontend/component-guide.md +33 -0
- package/repo-docs/frontend/model-resource-cookbook.md +0 -8
- package/repo-docs/frontend/model-resource-usage-guide.md +0 -4
- package/repo-docs/frontend/table-guide.md +26 -0
- package/repo-e2e/specs/cabloy-basic.spec.ts +158 -0
- package/repo-e2e/specs/controller-boundary.spec.ts +261 -0
- package/test-results/.last-run.json +1 -1
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/dto/{{resourceName}}SelectResItem.tsx_ +4 -0
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/dto/{{resourceName}}SelectResItem.tsx_ +4 -0
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-utils/lint/package.json +1 -1
- package/vona/packages-utils/lint/src/oxc/lint.ts +1 -1
- package/vona/packages-utils/lint/src/oxc/lintVue.ts +1 -1
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectResItem.tsx +4 -0
- package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +5 -0
- package/zova/package.original.json +1 -1
- package/zova/packages-cli/cli/package.json +2 -2
- package/zova/packages-cli/cli-set-front/package.json +3 -3
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.bin.buildRest.ts +14 -3
- package/zova/packages-utils/babel-plugin-zova-behavior/package.json +2 -1
- package/zova/packages-utils/babel-plugin-zova-behavior/src/index.ts +21 -8
- package/zova/packages-utils/babel-plugin-zova-behavior/test/behaviorSlots.test.ts +111 -0
- package/zova/packages-utils/zova-jsx/package.json +2 -2
- package/zova/packages-utils/zova-vite/package.json +2 -2
- package/zova/packages-zova/zova/package.json +3 -3
- package/zova/packages-zova/zova-core/package.json +1 -1
- package/zova/packages-zova/zova-core/src/bean/beanBase.ts +1 -1
- package/zova/packages-zova/zova-core/src/bean/beanContainer.ts +95 -39
- package/zova/packages-zova/zova-core/src/bean/resource/error/type.ts +9 -0
- package/zova/packages-zova/zova-core/src/bean/type.ts +18 -0
- package/zova/packages-zova/zova-core/src/components/component.ts +2 -1
- package/zova/packages-zova/zova-core/src/composables/useController.ts +155 -25
- package/zova/packages-zova/zova-core/src/core/component/error.ts +62 -5
- package/zova/packages-zova/zova-core/src/core/context/component.ts +37 -2
- package/zova/packages-zova/zova-core/src/core/context/hooks.ts +13 -2
- package/zova/packages-zova/zova-core/src/core/context/meta.ts +1 -0
- package/zova/packages-zova/zova-core/src/core/context/state.ts +161 -0
- package/zova/packages-zova/zova-core/src/core/sys/config.ts +32 -7
- package/zova/packages-zova/zova-core/src/types/interface/monkey.ts +15 -0
- package/zova/packages-zova/zova-core/src/types/utils/powerPartial.ts +1 -1
- package/zova/pnpm-lock.yaml +69 -54
- package/zova/src/front/config/config/config.ts +11 -0
- package/zova/src/front/config/config/render.tsx +79 -0
- package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/order.ts +0 -1
- package/zova/src/suite/a-demo/modules/demo-basic/src/.metadata/component/controllerBoundaryProbe.ts +31 -0
- package/zova/src/suite/a-demo/modules/demo-basic/src/.metadata/component/controllerBoundaryProbeInline.ts +34 -0
- package/zova/src/suite/a-demo/modules/demo-basic/src/.metadata/component/controllerBoundaryProbeOverride.ts +34 -0
- package/zova/src/suite/a-demo/modules/demo-basic/src/.metadata/index.ts +58 -1
- package/zova/src/suite/a-demo/modules/demo-basic/src/.metadata/page/controllerBoundary.ts +23 -0
- package/zova/src/suite/a-demo/modules/demo-basic/src/component/controllerBoundaryProbe/controller.tsx +36 -0
- package/zova/src/suite/a-demo/modules/demo-basic/src/component/controllerBoundaryProbeInline/controller.tsx +34 -0
- package/zova/src/suite/a-demo/modules/demo-basic/src/component/controllerBoundaryProbeOverride/controller.tsx +46 -0
- package/zova/src/suite/a-demo/modules/demo-basic/src/page/controllerBoundary/controller.tsx +185 -0
- package/zova/src/suite/a-demo/modules/demo-basic/src/routes.ts +6 -0
- package/zova/src/suite/a-home/modules/home-base/src/config/locale/en-us.ts +1 -0
- package/zova/src/suite/a-home/modules/home-base/src/config/locale/zh-cn.ts +1 -0
- package/zova/src/suite/a-training/modules/training-student/src/model/student.ts +0 -3
- package/zova/src/suite/cabloy-basic/modules/basic-button/package.json +58 -0
- package/zova/src/suite/cabloy-basic/modules/basic-button/src/.metadata/component/button.ts +31 -0
- package/zova/src/suite/cabloy-basic/modules/basic-button/src/.metadata/index.ts +122 -0
- package/zova/src/suite/cabloy-basic/modules/basic-button/src/.metadata/this.ts +2 -0
- package/zova/src/suite/cabloy-basic/modules/basic-button/src/bean/behavior.perform.tsx +103 -0
- package/zova/src/suite/cabloy-basic/modules/basic-button/src/component/button/controller.tsx +39 -0
- package/zova/src/suite/cabloy-basic/modules/basic-button/src/index.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-button/tsconfig.build.json +13 -0
- package/zova/src/suite/cabloy-basic/modules/basic-button/tsconfig.json +5 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/package.json +2 -1
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/bean/tableCell.actionDelete.tsx +4 -4
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx +5 -1
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/component/blockPageEntry/controller.tsx +5 -1
- package/zova/src/suite/cabloy-basic/modules/basic-table/package.json +2 -1
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/.metadata/component/actionRefresh.ts +31 -0
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/.metadata/index.ts +13 -0
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/bean/tableCell.actionDelete.tsx +4 -4
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/actionRefresh/controller.tsx +44 -0
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/config/locale/en-us.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/config/locale/zh-cn.ts +1 -0
- package/zova/src/suite/cabloy-basic/package.json +1 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/package.json +1 -1
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/model/layoutprofile.ts +0 -3
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/.metadata/page/resource.ts +6 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +0 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/resource/controller.tsx +5 -1
- package/zova/src/suite-vendor/a-cabloy/package.json +3 -3
- package/zova/src/suite-vendor/a-zova/modules/a-bean/cli/controller/metadata/generateFilePage.ts +3 -1
- package/zova/src/suite-vendor/a-zova/modules/a-bean/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-model/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/bean/bean.model/bean.model.useMutation.ts +1 -11
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/bean/bean.model/bean.model.useQuery.ts +1 -17
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/lib/utils.ts +2 -1
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/types/query.ts +2 -7
- package/zova/src/suite-vendor/a-zova/modules/a-model/test/lib/mutation.types.ts +14 -0
- package/zova/src/suite-vendor/a-zova/modules/a-model/test/lib/queryEnsureLoaded.test.ts +56 -0
- package/zova/src/suite-vendor/a-zova/modules/a-router/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-router/src/monkey.ts +8 -0
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/lib/ssr.ts +85 -1
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/monkey.ts +25 -1
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/types/ssr.ts +15 -1
- package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/package.json +6 -6
package/zova/src/suite-vendor/a-zova/modules/a-bean/cli/controller/metadata/generateFilePage.ts
CHANGED
|
@@ -22,6 +22,7 @@ export async function generateFilePage(
|
|
|
22
22
|
importStyleFirst,
|
|
23
23
|
hasStyleFirst,
|
|
24
24
|
classNameStyleFirst,
|
|
25
|
+
hasComponentOptions,
|
|
25
26
|
} = controllerInfo;
|
|
26
27
|
const { nameSchemaParams, hasSchemaParams, nameSchemaQuery, hasSchemaQuery } = controllerInfo;
|
|
27
28
|
const contentImports: string[] = [];
|
|
@@ -68,7 +69,8 @@ export async function generateFilePage(
|
|
|
68
69
|
}`);
|
|
69
70
|
}
|
|
70
71
|
// page
|
|
71
|
-
const
|
|
72
|
+
const componentOptions = hasComponentOptions ? `${className}.$componentOptions` : undefined;
|
|
73
|
+
const contentPage = `export const ZPage${nameCapitalize} = createZovaComponentPage(${className}, ${hasRenderFirst ? classNameRenderFirst : undefined}, ${hasStyleFirst ? classNameStyleFirst : undefined}${componentOptions ? `, ${componentOptions}` : ''});`;
|
|
72
74
|
// content
|
|
73
75
|
const content = `${contentImports.join('\n')}
|
|
74
76
|
${combineContentRenderAndStyle(controllerInfo, moduleName, className, '', '')}
|
package/zova/src/suite-vendor/a-zova/modules/a-model/src/bean/bean.model/bean.model.useMutation.ts
CHANGED
|
@@ -41,17 +41,7 @@ export class BeanModelUseMutation extends BeanModelUseStateGeneral {
|
|
|
41
41
|
mutationKey = this.self._forceQueryKeyPrefix(mutationKey);
|
|
42
42
|
const mutationHash = hashKey(mutationKey);
|
|
43
43
|
if (!this[SymbolUseMutations][mutationHash]) {
|
|
44
|
-
|
|
45
|
-
if (!cast(mutationOptions).meta?.disableErrorEffect) {
|
|
46
|
-
optionsDefault.onError = (error, variables, context) => {
|
|
47
|
-
let errorInfo = cast(mutationOptions).meta?.errorInfo;
|
|
48
|
-
if (typeof errorInfo === 'function') {
|
|
49
|
-
errorInfo = errorInfo(error, variables, context);
|
|
50
|
-
}
|
|
51
|
-
this.$errorHandler(error, errorInfo ?? 'useMutationData');
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
mutationOptions = Object.assign(optionsDefault, mutationOptions, { mutationKey });
|
|
44
|
+
mutationOptions = Object.assign({}, mutationOptions, { mutationKey });
|
|
55
45
|
this[SymbolUseMutations][mutationHash] = this.$useMutation(mutationOptions, queryClient);
|
|
56
46
|
}
|
|
57
47
|
return this[SymbolUseMutations][mutationHash] as UnwrapNestedRefs<
|
package/zova/src/suite-vendor/a-zova/modules/a-model/src/bean/bean.model/bean.model.useQuery.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { DefaultError, QueryClient, QueryKey, UseQueryOptions } from '@tanstack/vue-query';
|
|
2
2
|
|
|
3
|
-
import { checkErrorJwtExpired } from '@cabloy/utils';
|
|
4
3
|
import { useQuery } from '@tanstack/vue-query';
|
|
5
|
-
import { cast } from 'zova';
|
|
6
4
|
|
|
7
5
|
import type {
|
|
8
6
|
DefinedInitialQueryOptions,
|
|
@@ -45,21 +43,7 @@ export class BeanModelUseQuery extends BeanModelQuery {
|
|
|
45
43
|
$useQuery(options, queryClient) {
|
|
46
44
|
const queryKey = this.self._forceQueryKeyPrefix(options.queryKey);
|
|
47
45
|
const persister = this._createPersister(options.meta?.persister);
|
|
48
|
-
|
|
49
|
-
if (!cast(options).meta?.disableErrorEffect) {
|
|
50
|
-
optionsDefault.throwOnError = (error: Error, query) => {
|
|
51
|
-
let errorInfo = cast(options).meta?.errorInfo;
|
|
52
|
-
if (typeof errorInfo === 'function') {
|
|
53
|
-
errorInfo = errorInfo(error, query);
|
|
54
|
-
}
|
|
55
|
-
if (!error.message.includes('useQuery:') && !checkErrorJwtExpired(error)) {
|
|
56
|
-
error.message = `useQuery: [${queryKey.join(', ')}]: ${error.message}`;
|
|
57
|
-
}
|
|
58
|
-
this.$errorHandler(error, errorInfo ?? 'useQuery');
|
|
59
|
-
return false;
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
options = Object.assign(optionsDefault, options, { queryKey, persister });
|
|
46
|
+
options = Object.assign({}, options, { queryKey, persister });
|
|
63
47
|
// staleTime
|
|
64
48
|
const sync = typeof options.meta?.persister === 'object' && options.meta?.persister?.sync;
|
|
65
49
|
if (sync !== true) {
|
|
@@ -68,7 +68,8 @@ async function _QueryEnsureLoadedInner<T = any>(fn?: TypeQueryEnsureLoadedFn<T>)
|
|
|
68
68
|
if (!fn) return;
|
|
69
69
|
const query = fn();
|
|
70
70
|
if (query && query.data === undefined) {
|
|
71
|
-
await query.suspense();
|
|
71
|
+
const result = await query.suspense();
|
|
72
|
+
if (result.error) throw result.error;
|
|
72
73
|
}
|
|
73
74
|
return query;
|
|
74
75
|
}
|
|
@@ -59,17 +59,12 @@ export interface MyQueryMetaBase extends Record<string, unknown> {
|
|
|
59
59
|
defaultData?: (() => any) | any;
|
|
60
60
|
ssr?: QueryMetaSSR;
|
|
61
61
|
persister?: QueryMetaPersister | boolean;
|
|
62
|
-
disableErrorEffect?: boolean;
|
|
63
62
|
disableSuspenseOnInit?: boolean;
|
|
64
63
|
}
|
|
65
64
|
|
|
66
|
-
export interface MyQueryMeta extends MyQueryMetaBase {
|
|
67
|
-
errorInfo?: ((error, query) => string) | string;
|
|
68
|
-
}
|
|
65
|
+
export interface MyQueryMeta extends MyQueryMetaBase {}
|
|
69
66
|
|
|
70
|
-
export interface MyMutationMeta extends
|
|
71
|
-
errorInfo?: ((error, variables, context) => string) | string;
|
|
72
|
-
}
|
|
67
|
+
export interface MyMutationMeta extends Record<string, unknown> {}
|
|
73
68
|
|
|
74
69
|
export interface QueryMetaSSR {
|
|
75
70
|
/** default is true */
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { MutationObserverOptions } from '@tanstack/vue-query';
|
|
2
|
+
|
|
3
|
+
import type { MyMutationMeta } from '../../src/types/query.ts';
|
|
4
|
+
|
|
5
|
+
const mutationOptions: MutationObserverOptions<number, Error, { id: number }> = {
|
|
6
|
+
mutationKey: ['test:mutation'],
|
|
7
|
+
mutationFn: async variables => variables.id,
|
|
8
|
+
onError: (_error, _variables, _context) => {},
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const mutationMeta: MyMutationMeta = { source: 'test' };
|
|
12
|
+
|
|
13
|
+
void mutationOptions;
|
|
14
|
+
void mutationMeta;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
|
|
4
|
+
import { $QueryEnsureLoaded } from '../../src/lib/utils.js';
|
|
5
|
+
|
|
6
|
+
type QueryResult<T> = {
|
|
7
|
+
data: T | undefined;
|
|
8
|
+
error: Error | null;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function createQuery<T>(data: T | undefined, result: QueryResult<T>) {
|
|
12
|
+
let suspenseCalls = 0;
|
|
13
|
+
const query = {
|
|
14
|
+
data,
|
|
15
|
+
suspense: async () => {
|
|
16
|
+
suspenseCalls++;
|
|
17
|
+
return result;
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
query,
|
|
22
|
+
get suspenseCalls() {
|
|
23
|
+
return suspenseCalls;
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
test('QueryEnsureLoaded throws an initial query error', async () => {
|
|
29
|
+
const error = new Error('Initial query load failed');
|
|
30
|
+
const fixture = createQuery(undefined, { data: undefined, error });
|
|
31
|
+
|
|
32
|
+
await assert.rejects(
|
|
33
|
+
$QueryEnsureLoaded(() => fixture.query as never),
|
|
34
|
+
caught => caught === error,
|
|
35
|
+
);
|
|
36
|
+
assert.equal(fixture.suspenseCalls, 1);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('QueryEnsureLoaded accepts cached data when a background refresh has failed', async () => {
|
|
40
|
+
const error = new Error('Background refresh failed');
|
|
41
|
+
const fixture = createQuery({ value: 'cached' }, { data: { value: 'cached' }, error });
|
|
42
|
+
|
|
43
|
+
const result = await $QueryEnsureLoaded(() => fixture.query as never);
|
|
44
|
+
|
|
45
|
+
assert.equal(result, fixture.query);
|
|
46
|
+
assert.equal(fixture.suspenseCalls, 0);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('QueryEnsureLoaded returns a query after an initial successful load', async () => {
|
|
50
|
+
const fixture = createQuery(undefined, { data: { value: 'loaded' }, error: null });
|
|
51
|
+
|
|
52
|
+
const result = await $QueryEnsureLoaded(() => fixture.query as never);
|
|
53
|
+
|
|
54
|
+
assert.equal(result, fixture.query);
|
|
55
|
+
assert.equal(fixture.suspenseCalls, 1);
|
|
56
|
+
});
|
|
@@ -238,6 +238,9 @@ export class Monkey
|
|
|
238
238
|
private _ssrErrorHandler() {
|
|
239
239
|
if (!process.env.CLIENT) return;
|
|
240
240
|
this.app.meta.event.on('app:errorHandler', (data, next) => {
|
|
241
|
+
if (_isLoadErrorHandledClient(data)) {
|
|
242
|
+
data.load!.disposition = 'handled';
|
|
243
|
+
}
|
|
241
244
|
const err = next();
|
|
242
245
|
if (!err || !(err instanceof Error)) return err;
|
|
243
246
|
return this._errorHandlerDefaultClient(err, data);
|
|
@@ -265,3 +268,8 @@ export class Monkey
|
|
|
265
268
|
return err;
|
|
266
269
|
}
|
|
267
270
|
}
|
|
271
|
+
|
|
272
|
+
function _isLoadErrorHandledClient(data: IErrorHandlerEventData) {
|
|
273
|
+
if (data.load?.kind !== 'controller-load') return false;
|
|
274
|
+
return [301, 302, 401, 600].includes(Number((data.err as ErrorSSR).code));
|
|
275
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ComponentInternalInstance, Ref, VNode } from 'vue';
|
|
2
|
-
import type { Functionable, ILocaleRecord, ZovaContext } from 'zova';
|
|
2
|
+
import type { Functionable, IControllerLoadEvent, ILocaleRecord, ZovaContext } from 'zova';
|
|
3
3
|
|
|
4
4
|
import { RouteLocationNormalizedGeneric, RouteLocationResolvedGeneric } from '@cabloy/vue-router';
|
|
5
5
|
import { includeBooleanAttr, isBooleanAttr, isString, stringifyStyle } from '@vue/shared';
|
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
OnHydratePropHasMismatch,
|
|
15
15
|
OnHydratePropHasMismatchResult,
|
|
16
16
|
SSRContext,
|
|
17
|
+
SSRControllerLoadErrorSnapshot,
|
|
17
18
|
TypeSsrProfile,
|
|
18
19
|
TypeSsrSitePerformAction,
|
|
19
20
|
} from '../types/ssr.js';
|
|
@@ -40,6 +41,7 @@ export class CtxSSR extends BeanSimple {
|
|
|
40
41
|
|
|
41
42
|
private [SymbolHydratingCounter]: number = 0;
|
|
42
43
|
private [SymbolServerContexts]: Set<ZovaContext> = new Set();
|
|
44
|
+
private _controllerLoadSequence: Record<string, number> = {};
|
|
43
45
|
|
|
44
46
|
public metaStore: CtxSSRMetaStore;
|
|
45
47
|
|
|
@@ -132,6 +134,53 @@ export class CtxSSR extends BeanSimple {
|
|
|
132
134
|
}
|
|
133
135
|
}
|
|
134
136
|
|
|
137
|
+
/** @internal */
|
|
138
|
+
public controllerLoad(event: IControllerLoadEvent) {
|
|
139
|
+
if (event.phase === 'prepare') {
|
|
140
|
+
if (process.env.SERVER) {
|
|
141
|
+
event.ctx.meta.state.controllerLoadKey = this._allocateControllerLoadKey(event);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (!process.env.CLIENT || !this.isRuntimeSsrPreHydration) return;
|
|
145
|
+
const key = this._allocateControllerLoadKey(event);
|
|
146
|
+
const snapshot = this.stateDefer.controllerLoad?.[key];
|
|
147
|
+
if (
|
|
148
|
+
!snapshot ||
|
|
149
|
+
snapshot.version !== 1 ||
|
|
150
|
+
snapshot.controllerBeanName !== event.controllerBeanName
|
|
151
|
+
) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
delete this.stateDefer.controllerLoad![key];
|
|
155
|
+
event.replayError = _createControllerLoadError(snapshot);
|
|
156
|
+
event.replayControllerRecorded = snapshot.controllerRecorded;
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (event.phase !== 'fallback' || !process.env.SERVER || !event.error) return;
|
|
160
|
+
const key = event.ctx.meta.state.controllerLoadKey;
|
|
161
|
+
if (!key) return;
|
|
162
|
+
const controllerLoad = (this.stateDefer.controllerLoad ??= {});
|
|
163
|
+
controllerLoad[key] = _createControllerLoadErrorSnapshot(event);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private _allocateControllerLoadKey(event: IControllerLoadEvent) {
|
|
167
|
+
const instance = event.ctx.instance;
|
|
168
|
+
const parts: string[] = [];
|
|
169
|
+
let current: ComponentInternalInstance | null = instance;
|
|
170
|
+
while (current) {
|
|
171
|
+
const componentType = current.type as any;
|
|
172
|
+
const name =
|
|
173
|
+
componentType.__file ?? componentType.name ?? componentType.__name ?? 'anonymous';
|
|
174
|
+
const key = current.vnode.key ?? '';
|
|
175
|
+
parts.unshift(`${name}:${String(key)}`);
|
|
176
|
+
current = current.parent;
|
|
177
|
+
}
|
|
178
|
+
const structuralKey = parts.join('/');
|
|
179
|
+
const occurrence = this._controllerLoadSequence[structuralKey] ?? 0;
|
|
180
|
+
this._controllerLoadSequence[structuralKey] = occurrence + 1;
|
|
181
|
+
return `${structuralKey}#${occurrence}:${event.controllerBeanName ?? ''}`;
|
|
182
|
+
}
|
|
183
|
+
|
|
135
184
|
get profile(): TypeSsrProfile {
|
|
136
185
|
return resolveSsrProfile(this.state.ssrProfile, this.sys.env.SSR_PROFILE);
|
|
137
186
|
}
|
|
@@ -367,3 +416,38 @@ export class CtxSSR extends BeanSimple {
|
|
|
367
416
|
this._serverContextsDispose();
|
|
368
417
|
}
|
|
369
418
|
}
|
|
419
|
+
|
|
420
|
+
function _createControllerLoadErrorSnapshot(
|
|
421
|
+
event: IControllerLoadEvent,
|
|
422
|
+
): SSRControllerLoadErrorSnapshot {
|
|
423
|
+
const error = event.error! as any;
|
|
424
|
+
const snapshot: SSRControllerLoadErrorSnapshot = {
|
|
425
|
+
version: 1,
|
|
426
|
+
controllerBeanName: event.controllerBeanName,
|
|
427
|
+
name: error.name || 'Error',
|
|
428
|
+
message: error.message || String(error),
|
|
429
|
+
controllerRecorded: event.controllerRecorded === true,
|
|
430
|
+
};
|
|
431
|
+
for (const key of ['code', 'status']) {
|
|
432
|
+
if (typeof error[key] === 'number' || typeof error[key] === 'string') {
|
|
433
|
+
snapshot[key] = error[key];
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
for (const key of ['pagePath', 'url']) {
|
|
437
|
+
if (typeof error[key] === 'string') {
|
|
438
|
+
snapshot[key] = error[key];
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return snapshot;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function _createControllerLoadError(snapshot: SSRControllerLoadErrorSnapshot) {
|
|
445
|
+
const error = new Error(snapshot.message);
|
|
446
|
+
error.name = snapshot.name;
|
|
447
|
+
for (const key of ['code', 'status', 'pagePath', 'url']) {
|
|
448
|
+
if (snapshot[key] !== undefined) {
|
|
449
|
+
(error as any)[key] = snapshot[key];
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return error;
|
|
453
|
+
}
|
|
@@ -2,6 +2,7 @@ import type { IModule } from '@cabloy/module-info';
|
|
|
2
2
|
import type {
|
|
3
3
|
BeanBase,
|
|
4
4
|
BeanContainer,
|
|
5
|
+
IControllerLoadEvent,
|
|
5
6
|
IMonkeyAppContextInitialize,
|
|
6
7
|
IMonkeyAppInitialize,
|
|
7
8
|
IMonkeyBeanInit,
|
|
@@ -23,11 +24,22 @@ export class Monkey
|
|
|
23
24
|
{
|
|
24
25
|
appContextInitialize(ctx: ZovaContext): void {
|
|
25
26
|
ctx.meta.$ssr = ctx.app.ctx.meta.$ssr;
|
|
27
|
+
if (process.env.CLIENT) {
|
|
28
|
+
ctx.meta.$ssr.handleDirectOrOnHydrated(() => {
|
|
29
|
+
if (!ctx.disposed) {
|
|
30
|
+
ctx.meta.hooks.invokeHook('hydrated');
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
26
34
|
if (process.env.SERVER) {
|
|
27
35
|
ctx.meta.$ssr._registerServerContext(ctx);
|
|
28
36
|
}
|
|
29
37
|
}
|
|
30
38
|
|
|
39
|
+
controllerLoad(event: IControllerLoadEvent) {
|
|
40
|
+
event.ctx.meta.$ssr.controllerLoad(event);
|
|
41
|
+
}
|
|
42
|
+
|
|
31
43
|
async appInitialize() {
|
|
32
44
|
this.app.meta.event.on('a-router:routerGuards', async (router, next) => {
|
|
33
45
|
if (process.env.CLIENT) {
|
|
@@ -104,8 +116,16 @@ export class Monkey
|
|
|
104
116
|
|
|
105
117
|
private _ssrErrorHandler() {
|
|
106
118
|
if (!process.env.SERVER) return;
|
|
107
|
-
const _eventErrorHandler = this.app.meta.event.on('app:errorHandler', (
|
|
119
|
+
const _eventErrorHandler = this.app.meta.event.on('app:errorHandler', (data, next) => {
|
|
108
120
|
const err = next();
|
|
121
|
+
if (data.load?.kind === 'controller-load') {
|
|
122
|
+
if (_isLoadErrorHandledServer(data.err)) {
|
|
123
|
+
data.load.disposition = 'handled';
|
|
124
|
+
if (!err || !(err instanceof Error)) return err;
|
|
125
|
+
return this._errorHandlerDefaultServer(err);
|
|
126
|
+
}
|
|
127
|
+
return err;
|
|
128
|
+
}
|
|
109
129
|
if (!err || !(err instanceof Error)) return err;
|
|
110
130
|
return this._errorHandlerDefaultServer(err);
|
|
111
131
|
});
|
|
@@ -133,3 +153,7 @@ export class Monkey
|
|
|
133
153
|
return undefined;
|
|
134
154
|
}
|
|
135
155
|
}
|
|
156
|
+
|
|
157
|
+
function _isLoadErrorHandledServer(err: Error) {
|
|
158
|
+
return isNavigationFailure(err) || [301, 302, 401].includes(Number((err as any).code));
|
|
159
|
+
}
|
|
@@ -66,7 +66,21 @@ export interface SSRContextState {
|
|
|
66
66
|
ssrProfile?: TypeSsrProfile;
|
|
67
67
|
ssrProfileOptions?: Readonly<ISsrProfileOptions>;
|
|
68
68
|
}
|
|
69
|
-
export interface
|
|
69
|
+
export interface SSRControllerLoadErrorSnapshot {
|
|
70
|
+
version: 1;
|
|
71
|
+
controllerBeanName?: string;
|
|
72
|
+
name: string;
|
|
73
|
+
message: string;
|
|
74
|
+
code?: number | string;
|
|
75
|
+
status?: number | string;
|
|
76
|
+
pagePath?: string;
|
|
77
|
+
url?: string;
|
|
78
|
+
controllerRecorded: boolean;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface SSRContextStateDefer {
|
|
82
|
+
controllerLoad?: Record<string, SSRControllerLoadErrorSnapshot>;
|
|
83
|
+
}
|
|
70
84
|
|
|
71
85
|
// from: quasar/dist/types/meta.d.ts
|
|
72
86
|
// Cannot use `Record<string, string>` as TS would error out about `template` signature
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zova-module-a-zova",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.105",
|
|
4
4
|
"gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
|
|
5
5
|
"description": "zova",
|
|
6
6
|
"keywords": [
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@cabloy/word-utils": "^2.1.14",
|
|
44
44
|
"defu": "^6.1.7",
|
|
45
45
|
"luxon": "^3.7.2",
|
|
46
|
-
"zova-jsx": "^1.1.
|
|
46
|
+
"zova-jsx": "^1.1.90"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@cabloy/cli": "^3.1.29",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zova-suite-a-zova",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.162",
|
|
4
4
|
"gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
|
|
5
5
|
"description": "zova",
|
|
6
6
|
"license": "MIT",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"zova-module-a-api": "^5.1.22",
|
|
12
12
|
"zova-module-a-app": "^5.1.26",
|
|
13
|
-
"zova-module-a-bean": "^5.1.
|
|
13
|
+
"zova-module-a-bean": "^5.1.36",
|
|
14
14
|
"zova-module-a-behavior": "^5.1.26",
|
|
15
15
|
"zova-module-a-behaviors": "^5.1.22",
|
|
16
16
|
"zova-module-a-boundary": "^5.1.22",
|
|
@@ -21,18 +21,18 @@
|
|
|
21
21
|
"zova-module-a-interceptor": "^5.1.31",
|
|
22
22
|
"zova-module-a-logger": "^5.1.28",
|
|
23
23
|
"zova-module-a-meta": "^5.1.22",
|
|
24
|
-
"zova-module-a-model": "^5.1.
|
|
24
|
+
"zova-module-a-model": "^5.1.37",
|
|
25
25
|
"zova-module-a-openapi": "^5.1.54",
|
|
26
|
-
"zova-module-a-router": "^5.1.
|
|
26
|
+
"zova-module-a-router": "^5.1.38",
|
|
27
27
|
"zova-module-a-routerstack": "^5.1.29",
|
|
28
28
|
"zova-module-a-routertabs": "^5.1.34",
|
|
29
|
-
"zova-module-a-ssr": "^5.1.
|
|
29
|
+
"zova-module-a-ssr": "^5.1.40",
|
|
30
30
|
"zova-module-a-ssrhmr": "^5.1.23",
|
|
31
31
|
"zova-module-a-ssrserver": "^5.1.26",
|
|
32
32
|
"zova-module-a-style": "^5.1.35",
|
|
33
33
|
"zova-module-a-table": "^5.1.43",
|
|
34
34
|
"zova-module-a-zod": "^5.1.37",
|
|
35
|
-
"zova-module-a-zova": "^5.1.
|
|
35
|
+
"zova-module-a-zova": "^5.1.105"
|
|
36
36
|
},
|
|
37
37
|
"title": "a-zova"
|
|
38
38
|
}
|