openxiangda-cli 2.0.0-alpha.89 → 2.0.0-alpha.91

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 (77) hide show
  1. package/README.md +2 -2
  2. package/bin/run.js +1 -0
  3. package/dist/commands/skill.d.ts +26 -0
  4. package/dist/commands/skill.d.ts.map +1 -0
  5. package/dist/commands/skill.js +43 -0
  6. package/dist/commands/skill.js.map +1 -0
  7. package/dist/create-workspace.d.ts.map +1 -1
  8. package/dist/create-workspace.js +3 -3
  9. package/dist/create-workspace.js.map +1 -1
  10. package/dist/toolchain-capsule.d.ts.map +1 -1
  11. package/dist/toolchain-capsule.js +4 -8
  12. package/dist/toolchain-capsule.js.map +1 -1
  13. package/package.json +6 -7
  14. package/template/AGENTS.md +2 -1
  15. package/template/README.md +3 -3
  16. package/template/apps/server/package.json +1 -1
  17. package/template/apps/server/src/app.module.ts +1 -1
  18. package/template/apps/server/src/main.ts +1 -1
  19. package/template/apps/web/e2e/client-fixture.ts +5 -0
  20. package/template/apps/web/e2e/data-api-live-fixture.tsx +19 -14
  21. package/template/apps/web/e2e/field-protocol-fixture.tsx +87 -20
  22. package/template/apps/web/e2e/field-protocol.spec.ts +86 -0
  23. package/template/apps/web/e2e/resources.spec.ts +20 -181
  24. package/template/apps/web/field-protocol.e2e.html +3 -0
  25. package/template/apps/web/package.json +2 -5
  26. package/template/apps/web/src/main.tsx +12 -60
  27. package/template/apps/web/test/contracts.test.ts +52 -104
  28. package/template/openxiangda.config.ts +1 -1
  29. package/template/package.json +1 -3
  30. package/template/packages/contracts/src/generated.ts +1 -0
  31. package/template/scripts/verify-template-budget.mjs +2 -2
  32. package/template/apps/web/src/AuthoritativeSelector.tsx +0 -661
  33. package/template/apps/web/src/FilePreviewPage.tsx +0 -324
  34. package/template/apps/web/src/RoleSubjectSelect.tsx +0 -128
  35. package/template/apps/web/src/Shell.tsx +0 -445
  36. package/template/apps/web/src/appearance.tsx +0 -147
  37. package/template/apps/web/src/components/platform-fields/AddressField.tsx +0 -338
  38. package/template/apps/web/src/components/platform-fields/AttachmentFileList.tsx +0 -245
  39. package/template/apps/web/src/components/platform-fields/CascadeField.tsx +0 -49
  40. package/template/apps/web/src/components/platform-fields/DateTimeField.tsx +0 -148
  41. package/template/apps/web/src/components/platform-fields/JsonField.tsx +0 -77
  42. package/template/apps/web/src/components/platform-fields/LocationField.tsx +0 -164
  43. package/template/apps/web/src/components/platform-fields/PlatformDirectoryPicker.tsx +0 -673
  44. package/template/apps/web/src/components/platform-fields/ResourceReferenceField.tsx +0 -67
  45. package/template/apps/web/src/components/platform-fields/RichTextField.tsx +0 -196
  46. package/template/apps/web/src/components/platform-fields/SignatureField.tsx +0 -315
  47. package/template/apps/web/src/components/platform-fields/SubtableField.tsx +0 -553
  48. package/template/apps/web/src/components/platform-fields/address-value.ts +0 -80
  49. package/template/apps/web/src/components/platform-fields/cascade-value.ts +0 -66
  50. package/template/apps/web/src/components/platform-fields/directory-value.ts +0 -53
  51. package/template/apps/web/src/components/platform-fields/field-form-codec.ts +0 -98
  52. package/template/apps/web/src/components/platform-fields/location-value.ts +0 -87
  53. package/template/apps/web/src/components/platform-fields/resource-query.ts +0 -131
  54. package/template/apps/web/src/components/platform-fields/rich-text-value.ts +0 -59
  55. package/template/apps/web/src/components/platform-fields/subtable-value.ts +0 -187
  56. package/template/apps/web/src/components/resource/GeneratedResourceCrud.tsx +0 -1184
  57. package/template/apps/web/src/components/resource/ResourceBatchActions.tsx +0 -326
  58. package/template/apps/web/src/components/resource/StandardResourcePages.tsx +0 -392
  59. package/template/apps/web/src/components/resource/SurfaceFields.tsx +0 -915
  60. package/template/apps/web/src/components/resource/generated-resource-definition.ts +0 -18
  61. package/template/apps/web/src/components/resource/resource-import.ts +0 -205
  62. package/template/apps/web/src/data-provider.ts +0 -72
  63. package/template/apps/web/src/platform-client.ts +0 -708
  64. package/template/apps/web/src/runtime-meta.ts +0 -75
  65. package/template/apps/web/src/runtime.tsx +0 -227
  66. package/template/apps/web/src/styles.css +0 -1941
  67. package/template/apps/web/test/address-field.test.ts +0 -66
  68. package/template/apps/web/test/cascade-value.test.ts +0 -68
  69. package/template/apps/web/test/directory-value.test.ts +0 -55
  70. package/template/apps/web/test/field-bounds.test.ts +0 -17
  71. package/template/apps/web/test/field-form-codec.test.ts +0 -85
  72. package/template/apps/web/test/location-field.test.ts +0 -83
  73. package/template/apps/web/test/resource-import.test.ts +0 -143
  74. package/template/apps/web/test/resource-query.test.ts +0 -144
  75. package/template/apps/web/test/rich-json-field.test.ts +0 -53
  76. package/template/apps/web/test/signature-serial-field.test.ts +0 -40
  77. package/template/apps/web/test/subtable-value.test.ts +0 -144
@@ -1,75 +0,0 @@
1
- export interface RuntimeMount {
2
- runtimeBase: string;
3
- appCode: string;
4
- environmentKey: 'preproduction' | 'production';
5
- }
6
-
7
- export function readRuntimeMount(
8
- read: (name: string) => string | undefined
9
- ): RuntimeMount | null {
10
- const runtimeBase = read('openxiangda-runtime-base')?.trim();
11
- const appCode = read('openxiangda-app-code')?.trim();
12
- const environment = read('openxiangda-environment')?.trim();
13
- if (!runtimeBase && !appCode && !environment) return null;
14
- if (
15
- !runtimeBase ||
16
- !appCode ||
17
- (environment !== 'preproduction' && environment !== 'production')
18
- ) {
19
- throw new Error('OPENXIANGDA_RUNTIME_META_INVALID');
20
- }
21
- return { runtimeBase, appCode, environmentKey: environment };
22
- }
23
-
24
- export function runtimeMount() {
25
- if (typeof document === 'undefined') return null;
26
- return readRuntimeMount(name =>
27
- document
28
- .querySelector<HTMLMetaElement>(`meta[name="${name}"]`)
29
- ?.content.trim()
30
- );
31
- }
32
-
33
- export function applicationCode() {
34
- return runtimeMount()?.appCode || 'openxiangda-application';
35
- }
36
-
37
- export function resolveApplicationBasename(mount: RuntimeMount | null) {
38
- if (!mount) return undefined;
39
- const pathname = new URL(mount.runtimeBase, 'https://runtime.local').pathname;
40
- const normalized = pathname.replace(/\/+$/, '');
41
- return normalized || '/';
42
- }
43
-
44
- export function applicationBasename() {
45
- return resolveApplicationBasename(runtimeMount());
46
- }
47
-
48
- export function attachmentPreviewPath(resourceCode: string, fileId: string) {
49
- return resolveAttachmentPreviewPath(runtimeMount(), resourceCode, fileId);
50
- }
51
-
52
- export function resolveAttachmentPreviewPath(
53
- mount: RuntimeMount | null,
54
- resourceCode: string,
55
- fileId: string
56
- ) {
57
- const base = resolveApplicationBasename(mount)?.replace(/\/+$/, '') || '';
58
- return `${base}/files/${encodeURIComponent(resourceCode)}/${encodeURIComponent(
59
- fileId
60
- )}/preview`;
61
- }
62
-
63
- export function resolveApplicationApiPath(
64
- mount: RuntimeMount | null,
65
- path: string
66
- ) {
67
- if (!mount) return `/${path.replace(/^\/+/, '')}`;
68
- return `/service/openxiangda-app-api/v2/${encodeURIComponent(
69
- mount.appCode
70
- )}/${encodeURIComponent(mount.environmentKey)}/${path.replace(/^\/+/, '')}`;
71
- }
72
-
73
- export function applicationApiPath(path: string) {
74
- return resolveApplicationApiPath(runtimeMount(), path);
75
- }
@@ -1,227 +0,0 @@
1
- import { Alert, Button, Result, Space, Spin, Typography } from 'antd';
2
- import {
3
- createContext,
4
- useCallback,
5
- useContext,
6
- useEffect,
7
- useMemo,
8
- useState,
9
- } from 'react';
10
- import {
11
- loadRuntimeAuthorization,
12
- subscribeRoleSessionInvalidation,
13
- switchRuntimeRole,
14
- type RuntimeAuthorization,
15
- type RuntimeIdentity,
16
- } from './platform-client';
17
- import { RoleSubjectSelect } from './RoleSubjectSelect';
18
- import { runtimeMount } from './runtime-meta';
19
-
20
- interface RuntimeValue {
21
- identity: RuntimeIdentity;
22
- authorization: RuntimeAuthorization;
23
- identityEpoch: number;
24
- switchingRole: boolean;
25
- hasCapability(code: string): boolean;
26
- switchRole(targetRoleSubjectKey: string): Promise<void>;
27
- }
28
-
29
- const RuntimeContext = createContext<RuntimeValue | null>(null);
30
-
31
- export function RuntimeBoundary({ children }: { children: React.ReactNode }) {
32
- const [authorization, setAuthorization] = useState<RuntimeAuthorization>();
33
- const [error, setError] = useState<Error>();
34
- const [attempt, setAttempt] = useState(0);
35
- const [identityEpoch, setIdentityEpoch] = useState(0);
36
- const [switchingRole, setSwitchingRole] = useState(false);
37
-
38
- useEffect(
39
- () =>
40
- subscribeRoleSessionInvalidation(() => {
41
- setAuthorization(undefined);
42
- setError(undefined);
43
- setAttempt(value => value + 1);
44
- }),
45
- []
46
- );
47
-
48
- useEffect(() => {
49
- let active = true;
50
- setAuthorization(undefined);
51
- setError(undefined);
52
- void loadRuntimeAuthorization().then(
53
- value => {
54
- if (!active) return;
55
- setAuthorization(value);
56
- setIdentityEpoch(epoch => epoch + 1);
57
- },
58
- reason =>
59
- active &&
60
- setError(reason instanceof Error ? reason : new Error(String(reason)))
61
- );
62
- return () => {
63
- active = false;
64
- };
65
- }, [attempt]);
66
-
67
- const switchRole = useCallback(
68
- async (targetRoleSubjectKey: string) => {
69
- if (!authorization?.roleContext) {
70
- throw new Error('OPENXIANGDA_ROLE_SWITCH_UNAVAILABLE');
71
- }
72
- setSwitchingRole(true);
73
- try {
74
- const next = await switchRuntimeRole(
75
- targetRoleSubjectKey,
76
- authorization.roleContext.roleSession?.id || null
77
- );
78
- setAuthorization(next);
79
- setIdentityEpoch(epoch => epoch + 1);
80
- } catch (reason) {
81
- setAuthorization(undefined);
82
- setAttempt(value => value + 1);
83
- throw reason;
84
- } finally {
85
- setSwitchingRole(false);
86
- }
87
- },
88
- [authorization]
89
- );
90
-
91
- const identity = authorization?.identity || null;
92
- const value = useMemo<RuntimeValue | null>(
93
- () =>
94
- identity && authorization
95
- ? {
96
- identity,
97
- authorization,
98
- identityEpoch,
99
- switchingRole,
100
- hasCapability: code =>
101
- identity.isAppSuperAdmin ||
102
- identity.capabilityCodes.includes(code),
103
- switchRole,
104
- }
105
- : null,
106
- [authorization, identity, identityEpoch, switchRole, switchingRole]
107
- );
108
- const production =
109
- identity?.environment.key === 'production' ||
110
- runtimeMount()?.environmentKey === 'production';
111
-
112
- return (
113
- <>
114
- {production && (
115
- <Alert
116
- banner
117
- showIcon
118
- type="error"
119
- data-testid="production-data-warning"
120
- title="正在使用正式数据:所有新增、编辑和删除都会影响生产环境"
121
- />
122
- )}
123
- {error ? (
124
- <Result
125
- status="error"
126
- title="无法读取当前应用角色"
127
- subTitle={error.message}
128
- extra={
129
- <Button onClick={() => setAttempt(value => value + 1)}>
130
- 重试
131
- </Button>
132
- }
133
- />
134
- ) : authorization?.state === 'selection_required' &&
135
- authorization.roleContext ? (
136
- <InitialRoleSelection
137
- authorization={authorization}
138
- switching={switchingRole}
139
- onSelect={switchRole}
140
- />
141
- ) : authorization?.state === 'unassigned' ? (
142
- <Result
143
- status="403"
144
- title="尚未分配应用角色"
145
- subTitle="请联系应用管理员分配当前环境中的应用角色后重试。"
146
- extra={
147
- <Button onClick={() => setAttempt(value => value + 1)}>
148
- 重新检查
149
- </Button>
150
- }
151
- />
152
- ) : value ? (
153
- <RuntimeContext.Provider
154
- key={`${value.identity.identityScope}:${identityEpoch}`}
155
- value={value}
156
- >
157
- {children}
158
- </RuntimeContext.Provider>
159
- ) : (
160
- <div className="oxa-loading">
161
- <Spin description="正在读取当前应用角色与权限" />
162
- </div>
163
- )}
164
- </>
165
- );
166
- }
167
-
168
- function InitialRoleSelection({
169
- authorization,
170
- switching,
171
- onSelect,
172
- }: {
173
- authorization: RuntimeAuthorization;
174
- switching: boolean;
175
- onSelect: (targetRoleSubjectKey: string) => Promise<void>;
176
- }) {
177
- const [selected, setSelected] = useState('');
178
- const [selectionError, setSelectionError] = useState('');
179
- const roleSubjectPage = authorization.roleContext?.roleSubjectPage;
180
- const submit = async () => {
181
- if (!selected) return;
182
- setSelectionError('');
183
- try {
184
- await onSelect(selected);
185
- } catch (reason) {
186
- setSelectionError(
187
- reason instanceof Error ? reason.message : '应用角色切换失败'
188
- );
189
- }
190
- };
191
- return (
192
- <Result
193
- status="info"
194
- title="选择当前应用角色"
195
- subTitle="本次应用访问只使用一个角色;进入应用后仍可从个人菜单切换。"
196
- extra={
197
- <Space align="start" direction="vertical" size="middle">
198
- {roleSubjectPage && (
199
- <RoleSubjectSelect
200
- disabled={switching}
201
- initialPage={roleSubjectPage}
202
- onChange={setSelected}
203
- value={selected}
204
- />
205
- )}
206
- {selectionError && (
207
- <Typography.Text type="danger">{selectionError}</Typography.Text>
208
- )}
209
- <Button
210
- disabled={!selected}
211
- loading={switching}
212
- onClick={() => void submit()}
213
- type="primary"
214
- >
215
- 进入应用
216
- </Button>
217
- </Space>
218
- }
219
- />
220
- );
221
- }
222
-
223
- export function useRuntime() {
224
- const value = useContext(RuntimeContext);
225
- if (!value) throw new Error('OPENXIANGDA_RUNTIME_NOT_READY');
226
- return value;
227
- }