cabloy 5.1.190 → 5.1.192
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/.github/workflows/agent-governance.yml +39 -0
- package/.github/workflows/docs-pages.yml +1 -0
- package/.gitignore +3 -0
- package/CHANGELOG.md +17 -0
- package/README.md +2 -2
- package/package.json +11 -5
- package/repo-agent-governance/adapters/claude/contract-loop-gate.ts +199 -0
- package/{.claude → repo-agent-governance}/commands/release.md +20 -20
- package/repo-agent-governance/managed-assets.json +887 -0
- package/repo-agent-governance/manifest.json +56 -0
- package/{CLAUDE.md → repo-agent-governance/policies/repository.md} +4 -2
- package/repo-agent-governance/scripts/governance.mjs +570 -0
- package/repo-agent-governance/scripts/pack-check.mjs +43 -0
- package/{.claude → repo-agent-governance}/skills/cabloy-contract-loop/references/contract-loop-map.md +4 -4
- package/{.claude → repo-agent-governance}/skills/cabloy-master-detail/SKILL.md +1 -1
- package/{.claude → repo-agent-governance}/skills/cabloy-module-removal/SKILL.md +1 -1
- package/{.claude → repo-agent-governance}/skills/cabloy-spec-execution/SKILL.md +1 -1
- package/{.claude → repo-agent-governance}/skills/cabloy-spec-execution/references/execution-protocol.md +8 -8
- package/{.claude → repo-agent-governance}/skills/cabloy-spec-execution/references/status-and-evidence.md +9 -9
- package/{.claude → repo-agent-governance}/skills/cabloy-spec-generation/SKILL.md +2 -2
- package/{.claude → repo-agent-governance}/skills/cabloy-spec-generation/references/repo-aware-discovery.md +7 -7
- package/{.claude → repo-agent-governance}/skills/cabloy-spec-generation/references/repo-specs-document-set.md +30 -30
- package/{.claude → repo-agent-governance}/skills/cabloy-spec-generation/references/traceability-and-status-rules.md +9 -9
- package/{.claude → repo-agent-governance}/skills/cabloy-workflow/SKILL.md +7 -8
- package/{.claude → repo-agent-governance}/skills/cabloy-worktree-environment/SKILL.md +9 -9
- package/{.claude → repo-agent-governance}/skills/cabloy-zova-source-reading/SKILL.md +5 -5
- package/repo-agent-governance/tests/governance.test.mjs +295 -0
- package/repo-agent-governance/tools/contract-loop/check.mjs +71 -0
- package/repo-agent-governance/tools/contract-loop/core.mjs +189 -0
- package/repo-agent-governance/tools/spec-charts/generate-implementation-charts.mjs +858 -0
- package/{.claude/skills/cabloy-spec-generation/scripts → repo-agent-governance/tools/spec-charts}/generate-implementation-charts.test.mjs +95 -20
- package/repo-docs/.vitepress/config.mjs +2 -0
- package/repo-docs/ai/agent-governance.md +80 -0
- package/repo-docs/ai/cli-to-skill-map.md +3 -3
- package/repo-docs/ai/docs-skills-rules-mapping.md +27 -26
- package/repo-docs/ai/edition-consistency-checklist.md +8 -6
- package/repo-docs/ai/future-skill-roadmap.md +2 -2
- package/repo-docs/ai/introduction.md +7 -6
- package/repo-docs/ai/repo-guidance.md +3 -2
- package/repo-docs/ai/rules-and-config.md +23 -15
- package/repo-docs/ai/skills.md +7 -7
- package/repo-docs/ai/verification.md +2 -1
- package/repo-docs/demo.md +12 -0
- package/repo-docs/fullstack/contract-loop-playbook.md +1 -1
- package/repo-docs/fullstack/quickstart.md +1 -1
- package/repo-docs/index.md +3 -0
- package/repo-docs/reference/package-map.md +3 -2
- package/repo-docs/reference/repo-scripts.md +17 -2
- package/scripts/bootstrapAgentGovernance.mjs +81 -0
- package/scripts/init.ts +1 -0
- package/scripts/release.ts +15 -0
- package/scripts/upgrade.ts +66 -7
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/pnpm-lock.yaml +3 -3
- package/zova/src/suite/a-home/modules/home-passport/src/model/passport.ts +2 -7
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/bean/tableCell.actionOperationsRow.tsx +19 -13
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/blockDetails/controller.tsx +11 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/blockToolbarBulk/controller.tsx +7 -4
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/component/formFieldDetails/controller.tsx +14 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/lib/detailsPermissions.ts +65 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/lib/index.ts +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/lib/utils.ts +3 -3
- package/zova/src/suite/cabloy-basic/modules/basic-details/src/types/details.ts +6 -1
- package/zova/src/suite/cabloy-basic/modules/basic-details/test/lib/detailsPermissions.test.ts +125 -0
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/permissions.ts +2 -8
- package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/controller.tsx +2 -1
- package/zova/src/suite-vendor/a-zova/package.json +3 -3
- package/.claude/hooks/contract-loop-gate.ts +0 -421
- package/.claude/scheduled_tasks.lock +0 -1
- package/.claude/skills/cabloy-spec-generation/scripts/generate-implementation-charts.mjs +0 -385
- /package/{.claude → repo-agent-governance/adapters/claude}/settings.json +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-backend-scaffold/SKILL.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-backend-scaffold/evals/evals.json +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-backend-scaffold/references/backend-thread-map.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-backend-scaffold/references/follow-up-checklist.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-contract-loop/SKILL.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-contract-loop/evals/evals.json +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-contract-loop/references/resource-custom-state-pattern.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-contract-loop/references/verification-checklist.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-domain-planning/SKILL.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-frontend-scaffold/SKILL.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-frontend-scaffold/evals/evals.json +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-frontend-scaffold/references/follow-up-checklist.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-frontend-scaffold/references/frontend-thread-map.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-resource-field-update/SKILL.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-resource-field-update/evals/evals.json +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-resource-field-update/references/custom-renderer-demo-checklist.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-resource-field-update/references/field-update-decision-tree.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-resource-field-update/references/follow-up-checklist.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-resource-field-update/references/verification-checklist.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-spec-execution/evals/evals.json +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-spec-generation/evals/evals.json +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-workflow/evals/evals.json +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-workflow/references/cli-strategy.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-workflow/references/edition-detection.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-worktree-environment/evals/evals.json +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-zova-source-reading/references/analysis-modes.md +0 -0
- /package/{.claude → repo-agent-governance}/skills/cabloy-zova-source-reading/references/core-reading-paths.md +0 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IPermissionHintGeneral,
|
|
3
|
+
IResourceRenderDetailsActionBulkOptionsAction,
|
|
4
|
+
IResourceRenderDetailsActionRowOptionsAction,
|
|
5
|
+
TypeFormScene,
|
|
6
|
+
TypeOpenapiPermissions,
|
|
7
|
+
} from 'zova-module-a-openapi';
|
|
8
|
+
|
|
9
|
+
import type { TypeDetailsCheckPermission } from '../types/details.js';
|
|
10
|
+
|
|
11
|
+
import { checkFormScene } from './utils.js';
|
|
12
|
+
|
|
13
|
+
export interface IDetailsPermissionAction {
|
|
14
|
+
name?: string;
|
|
15
|
+
options?: { permission?: IPermissionHintGeneral };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function createDetailsPermissionChecker(
|
|
19
|
+
getPermissions: () => TypeOpenapiPermissions | undefined,
|
|
20
|
+
getCurrentData: () => Record<string, unknown> | undefined,
|
|
21
|
+
checkPermission: (
|
|
22
|
+
permissions: TypeOpenapiPermissions | undefined,
|
|
23
|
+
actionName?: string,
|
|
24
|
+
permissionHint?: IPermissionHintGeneral,
|
|
25
|
+
currentData?: Record<string, unknown>,
|
|
26
|
+
) => boolean,
|
|
27
|
+
): TypeDetailsCheckPermission {
|
|
28
|
+
return (actionName, permissionHint) => {
|
|
29
|
+
return checkPermission(getPermissions(), actionName, permissionHint, getCurrentData());
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function isDetailsActionAllowed(
|
|
34
|
+
formScene: TypeFormScene,
|
|
35
|
+
checkPermission: TypeDetailsCheckPermission,
|
|
36
|
+
action: IDetailsPermissionAction,
|
|
37
|
+
): boolean {
|
|
38
|
+
const permissionHint = action.options?.permission;
|
|
39
|
+
return checkFormScene(formScene, permissionHint) && checkPermission(action.name, permissionHint);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function filterDetailsActions<TAction extends IDetailsPermissionAction>(
|
|
43
|
+
formScene: TypeFormScene,
|
|
44
|
+
checkPermission: TypeDetailsCheckPermission,
|
|
45
|
+
actions: TAction[] | undefined,
|
|
46
|
+
): TAction[] {
|
|
47
|
+
if (!actions || actions.length === 0) return [];
|
|
48
|
+
return actions.filter(action => isDetailsActionAllowed(formScene, checkPermission, action));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function filterDetailsBulkActions(
|
|
52
|
+
formScene: TypeFormScene,
|
|
53
|
+
checkPermission: TypeDetailsCheckPermission,
|
|
54
|
+
actions: IResourceRenderDetailsActionBulkOptionsAction[] | undefined,
|
|
55
|
+
) {
|
|
56
|
+
return filterDetailsActions(formScene, checkPermission, actions);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function filterDetailsRowActions(
|
|
60
|
+
formScene: TypeFormScene,
|
|
61
|
+
checkPermission: TypeDetailsCheckPermission,
|
|
62
|
+
actions: IResourceRenderDetailsActionRowOptionsAction[] | undefined,
|
|
63
|
+
) {
|
|
64
|
+
return filterDetailsActions(formScene, checkPermission, actions);
|
|
65
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { IPermissionHintGeneral, TypeFormScene } from 'zova-module-a-openapi';
|
|
2
2
|
|
|
3
|
-
export function
|
|
3
|
+
export function checkFormScene(
|
|
4
4
|
formScene: TypeFormScene,
|
|
5
|
-
permissionHint?:
|
|
5
|
+
permissionHint?: IPermissionHintGeneral,
|
|
6
6
|
): boolean {
|
|
7
7
|
const formSceneHint = permissionHint?.formScene;
|
|
8
8
|
if (!formSceneHint) return true;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import 'zova-module-a-table';
|
|
2
2
|
import 'zova-module-a-openapi';
|
|
3
|
-
import type { IDetailsScope } from 'zova-module-a-openapi';
|
|
3
|
+
import type { IDetailsScope, IPermissionHintGeneral } from 'zova-module-a-openapi';
|
|
4
4
|
|
|
5
5
|
import { ControllerBlockDetails } from '../component/blockDetails/controller.jsx';
|
|
6
6
|
|
|
7
|
+
export type TypeDetailsCheckPermission = (
|
|
8
|
+
actionName?: string,
|
|
9
|
+
permissionHint?: IPermissionHintGeneral,
|
|
10
|
+
) => boolean;
|
|
11
|
+
|
|
7
12
|
declare module 'zova-module-a-openapi' {
|
|
8
13
|
export interface IJsxRenderContextDetails<TData extends {} = {}> {
|
|
9
14
|
$$details: ControllerBlockDetails<TData>;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { IPermissionHintGeneral, TypeOpenapiPermissions } from 'zova-module-a-openapi';
|
|
2
|
+
|
|
3
|
+
import assert from 'node:assert/strict';
|
|
4
|
+
import test from 'node:test';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
createDetailsPermissionChecker,
|
|
8
|
+
filterDetailsBulkActions,
|
|
9
|
+
filterDetailsRowActions,
|
|
10
|
+
} from '../../src/lib/detailsPermissions.js';
|
|
11
|
+
|
|
12
|
+
test('details permission checker lazily reads the current enclosing Resource projection', () => {
|
|
13
|
+
let permissions: TypeOpenapiPermissions | undefined = true;
|
|
14
|
+
let currentData: Record<string, unknown> = { departmentId: 7 };
|
|
15
|
+
const calls: unknown[][] = [];
|
|
16
|
+
const checkPermission = createDetailsPermissionChecker(
|
|
17
|
+
() => permissions,
|
|
18
|
+
() => currentData,
|
|
19
|
+
(...args) => {
|
|
20
|
+
calls.push(args);
|
|
21
|
+
return true;
|
|
22
|
+
},
|
|
23
|
+
);
|
|
24
|
+
const permissionHint: IPermissionHintGeneral = { actionInherit: 'view' };
|
|
25
|
+
|
|
26
|
+
const firstPermissions = permissions;
|
|
27
|
+
const firstCurrentData = currentData;
|
|
28
|
+
assert.equal(checkPermission('grantSystemAdmin', permissionHint), true);
|
|
29
|
+
permissions = false;
|
|
30
|
+
currentData = { departmentId: 8 };
|
|
31
|
+
assert.equal(checkPermission('grantSystemAdmin', permissionHint), true);
|
|
32
|
+
|
|
33
|
+
assert.equal(calls.length, 2);
|
|
34
|
+
assert.strictEqual(calls[0][0], firstPermissions);
|
|
35
|
+
assert.strictEqual(calls[1][0], permissions);
|
|
36
|
+
assert.strictEqual(calls[0][3], firstCurrentData);
|
|
37
|
+
assert.strictEqual(calls[1][3], currentData);
|
|
38
|
+
assert.deepEqual(
|
|
39
|
+
calls.map(call => call.slice(1, 3)),
|
|
40
|
+
[
|
|
41
|
+
['grantSystemAdmin', permissionHint],
|
|
42
|
+
['grantSystemAdmin', permissionHint],
|
|
43
|
+
],
|
|
44
|
+
);
|
|
45
|
+
assert.equal(
|
|
46
|
+
calls.every(call => call.length === 4),
|
|
47
|
+
true,
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('bulk details actions apply form-scene filtering before the enclosing Resource checker', () => {
|
|
52
|
+
const calls: Array<[string | undefined, IPermissionHintGeneral | undefined]> = [];
|
|
53
|
+
const grantHint = { actionInherit: 'view', formScene: ['view'] };
|
|
54
|
+
const actions = [
|
|
55
|
+
{
|
|
56
|
+
name: 'hiddenInView',
|
|
57
|
+
render: 'test:hiddenInView',
|
|
58
|
+
options: { permission: { formScene: ['edit'] } },
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'denied',
|
|
62
|
+
render: 'test:denied',
|
|
63
|
+
options: { permission: { formScene: ['view'] } },
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'grantSystemAdmin',
|
|
67
|
+
render: 'test:grantSystemAdmin',
|
|
68
|
+
options: { permission: grantHint },
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
const allowed = filterDetailsBulkActions(
|
|
73
|
+
'view',
|
|
74
|
+
(actionName, permissionHint) => {
|
|
75
|
+
calls.push([actionName, permissionHint]);
|
|
76
|
+
return actionName === 'grantSystemAdmin';
|
|
77
|
+
},
|
|
78
|
+
actions as any,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
assert.deepEqual(
|
|
82
|
+
allowed.map(action => action.name),
|
|
83
|
+
['grantSystemAdmin'],
|
|
84
|
+
);
|
|
85
|
+
assert.deepEqual(
|
|
86
|
+
calls.map(([actionName]) => actionName),
|
|
87
|
+
['denied', 'grantSystemAdmin'],
|
|
88
|
+
);
|
|
89
|
+
assert.strictEqual(calls[1][1], grantHint);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('row details actions use the same enclosing Resource checker and form-scene gate', () => {
|
|
93
|
+
const calls: string[] = [];
|
|
94
|
+
const actions = [
|
|
95
|
+
{
|
|
96
|
+
name: 'hiddenInView',
|
|
97
|
+
render: 'test:hiddenInView',
|
|
98
|
+
options: { permission: { formScene: ['edit'] } },
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'allowed',
|
|
102
|
+
render: 'test:allowed',
|
|
103
|
+
options: { permission: { public: true } },
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'denied',
|
|
107
|
+
render: 'test:denied',
|
|
108
|
+
},
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
const allowed = filterDetailsRowActions(
|
|
112
|
+
'view',
|
|
113
|
+
actionName => {
|
|
114
|
+
calls.push(actionName!);
|
|
115
|
+
return actionName === 'allowed';
|
|
116
|
+
},
|
|
117
|
+
actions as any,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
assert.deepEqual(
|
|
121
|
+
allowed.map(action => action.name),
|
|
122
|
+
['allowed'],
|
|
123
|
+
);
|
|
124
|
+
assert.deepEqual(calls, ['allowed', 'denied']);
|
|
125
|
+
});
|
|
@@ -52,12 +52,6 @@ export interface IPermissionHintTableActionBulk {
|
|
|
52
52
|
public?: boolean;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
export interface IPermissionHintDetailsActionRow {
|
|
56
|
-
formScene?: TypeFormScene | TypeFormScene[];
|
|
57
|
-
}
|
|
55
|
+
export interface IPermissionHintDetailsActionRow extends IPermissionHintGeneral {}
|
|
58
56
|
|
|
59
|
-
export interface IPermissionHintDetailsActionBulk {
|
|
60
|
-
// actionInherit?: keyof IResourceDetailsActionBulkNameRecord;
|
|
61
|
-
// public?: boolean;
|
|
62
|
-
formScene?: TypeFormScene | TypeFormScene[];
|
|
63
|
-
}
|
|
57
|
+
export interface IPermissionHintDetailsActionBulk extends IPermissionHintGeneral {}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
IResourceDetailsActionRowOptionsBase,
|
|
2
3
|
IResourceTableActionRowOptionsBase,
|
|
3
4
|
ISchemaObjectExtensionField,
|
|
4
5
|
ISchemaObjectExtensionFieldRest,
|
|
@@ -466,7 +467,7 @@ export class ControllerTable<TData extends {} = {}> extends BeanControllerTableB
|
|
|
466
467
|
|
|
467
468
|
public cellRender(
|
|
468
469
|
render: TypeTableCellRenderComponent,
|
|
469
|
-
columnProps: IResourceTableActionRowOptionsBase,
|
|
470
|
+
columnProps: IResourceTableActionRowOptionsBase | IResourceDetailsActionRowOptionsBase,
|
|
470
471
|
renderContext: IJsxRenderContextTableCell,
|
|
471
472
|
) {
|
|
472
473
|
// render
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zova-suite-a-zova",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.166",
|
|
4
4
|
"gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
|
|
5
5
|
"description": "zova",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"zova-module-a-logger": "^5.1.28",
|
|
22
22
|
"zova-module-a-meta": "^5.1.22",
|
|
23
23
|
"zova-module-a-model": "^5.1.37",
|
|
24
|
-
"zova-module-a-openapi": "^5.1.
|
|
24
|
+
"zova-module-a-openapi": "^5.1.55",
|
|
25
25
|
"zova-module-a-router": "^5.1.41",
|
|
26
26
|
"zova-module-a-routerstack": "^5.1.29",
|
|
27
27
|
"zova-module-a-routertabs": "^5.1.34",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"zova-module-a-ssrhmr": "^5.1.23",
|
|
30
30
|
"zova-module-a-ssrserver": "^5.1.26",
|
|
31
31
|
"zova-module-a-style": "^5.1.35",
|
|
32
|
-
"zova-module-a-table": "^5.1.
|
|
32
|
+
"zova-module-a-table": "^5.1.44",
|
|
33
33
|
"zova-module-a-zod": "^5.1.37",
|
|
34
34
|
"zova-module-a-zova": "^5.1.108"
|
|
35
35
|
},
|