openxiangda-cli 2.0.0-alpha.143 → 2.0.0-alpha.146

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda-cli",
3
- "version": "2.0.0-alpha.143",
3
+ "version": "2.0.0-alpha.146",
4
4
  "description": "Thin application-level CLI for OpenXiangda 2.0.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -21,10 +21,10 @@
21
21
  ],
22
22
  "dependencies": {
23
23
  "@oclif/core": "4.13.3",
24
- "openxiangda-contracts": "2.0.0-alpha.72",
25
- "openxiangda-devkit-core": "2.0.0-alpha.92",
26
- "openxiangda-mcp": "2.0.0-alpha.92",
27
- "openxiangda-skill-kit": "2.0.0-alpha.113"
24
+ "openxiangda-contracts": "2.0.0-alpha.73",
25
+ "openxiangda-devkit-core": "2.0.0-alpha.93",
26
+ "openxiangda-mcp": "2.0.0-alpha.93",
27
+ "openxiangda-skill-kit": "2.0.0-alpha.115"
28
28
  },
29
29
  "devDependencies": {
30
30
  "tsx": "4.23.12",
@@ -3,6 +3,25 @@
3
3
  - Use only the workspace-pinned CLI: `pnpm openxiangda <command>`. Never invoke a bare global `openxiangda` inside a 2.0 task.
4
4
  - `appspec/` is the optional OpenXiangda 2.0 business-intent layer. Before changing observable behavior, read the bounded index with `pnpm openxiangda spec context --json`, then select only the relevant stable ID; use no ChangeSpec for L0 work, one short ChangeSpec for L1, and add permission/rollback/concurrency detail only for L2/L3. Before close, the user—not AI—confirms `currentSpec=merged` or `not-applicable`. AppSpec is advisory and never a release gate. Never read, import or migrate 1.x `openspec/` or SDD.
5
5
  - Use Vite, React Router, Refine Core and Ant Design. Do not add Umi, ProComponents or another admin shell.
6
+ - The platform `openxiangda/react` owns the only admin `ConfigProvider` and the
7
+ `light`/`dark`/`system` appearance preference. Admin pages use Ant Design's
8
+ `defaultAlgorithm`/`darkAlgorithm`, `theme.useToken()`, or generated
9
+ `--oxa-antd-*` CSS variables. Do not add a nested theme provider, theme
10
+ store, palette editor, `--oxa-color-*`/`--oxa-shell-*` aliases, or fixed
11
+ white/black/neutral structural colors. Future palettes are centralized
12
+ platform seed configurations; alpha contracts are replaced directly without
13
+ compatibility aliases. User-end pages are outside this admin visual contract
14
+ and may use Tailwind CSS and their own product design tokens/components, but
15
+ must not mutate the admin provider or Seed.
16
+ - Use the complete Ant Design Seed/Map/Alias/Component model on admin pages:
17
+ platform-owned Seed values feed the selected algorithm; admin code consumes
18
+ generated Alias values for colors, spacing, typography, control dimensions,
19
+ motion, shadows, z-index and breakpoints. Never hand-write Map values or
20
+ introduce an admin token registry. Component tokens are only for
21
+ platform-approved non-color dimensions and typography.
22
+ - The template includes Tailwind CSS v4 for user-end pages. Keep its `@theme`
23
+ and utility classes in the user-end layer, omit Tailwind preflight from the
24
+ admin shell, and do not make user-end utilities depend on `--oxa-antd-*`.
6
25
  - Bind every generated `appRoutes` entry to its local page with `defineApplicationContributions`; desktop `admin` routes stay inside the platform Shell, while `user` routes render without an admin Shell for independent mobile/user experiences. Generated resource CRUD routes are compiler-owned under `/admin/resources/<resourceCode>...` and `/m/admin/resources/<resourceCode>...`; never recreate root resource paths, aliases or redirects. Explicit routes that have the same canonical shape as another explicit or generated route fail compilation, even when dynamic parameter names differ. Use only the typed `toolbar`, `row` and `detail` resource slots for generated resource actions. Declare the complete editable admin menu with `defineAdminNavigation` and its page/group helpers; the Shell renders only generated `adminNavigation` references and permissions only filter them. Do not create another router, menu store, layout, identity provider, permission store or copied CRUD page; route/action access uses capability or `allOf`/`anyOf`, while Data/App API and Workflow authorization remain server-owned.
7
26
  - Application login is optional `frontend.authentication`: existing platform users only, registration rejected, exact desktop `/login` and mobile `/m/login`. Bind generated `authenticationSurfaces` to separate PC/mobile renderers through `defineApplicationContributions`; renderers own only brand visuals and call `ApplicationLoginSurfaceProps`. The platform alone owns passwords, providers, OAuth state/callbacks, secure cookies, current identity and authorization. Never put login in protected `appRoutes`, call a v1 auth route, store tokens, create users/roles, or add another Router/identity provider. Keep QA in generated `platformAuthManifest`, outside the protected route denominator.
8
27
  - External users without platform accounts use only an exact static `surface: 'user'` route declared through `frontend.publicAccess`. Declare the one resource, bounded field sets, required operations, draft limits and named duplicate validations; consume only generated `anonymousPublicAccess` and `createAnonymousPublicClient`. `own.list`/`own.read` mean records submitted by the same platform-issued HttpOnly browser credential, not a verified natural person, and another browser or cleared cookie intentionally loses access. Never create a guest role/user, call the general Native Data API, expose an anonymous upload path, store identity locally or derive ownership from IP, user-agent or fingerprint.
@@ -12,7 +31,7 @@
12
31
  - Add NestJS only for a named business action that needs a cross-resource transaction, an invariant or an external side effect.
13
32
  - Every interactive business action binds `@OpenXiangdaOperation(operation)` and injects `OpenXiangdaBusinessDataApiService`, `OpenXiangdaBusinessNotificationService`, or `OpenXiangdaStandardOperations`. The platform checks the action capability once at App API ingress; the trusted backend then has full Data/managed business-notification access only to its exact application/environment while audit retains the initiating user and action. Do not author `authorizationJSON`, forward user tokens, grant ordinary users `app:notification2:send`, or reapply the user's resource, row and field permissions inside the action.
14
33
  - A NestJS backend declares only `enabled`, `isolation: 'shared' | 'dedicated'` and `resourceProfile: 'light' | 'standard'`. Never put raw Kubernetes resources, replicas, ports or environment maps in application metadata; the platform owns capacity and scaling.
15
- - Use the current logged-in user and the union of that user's application roles. An optional declared Perspective projects only read visibility (pages, rows and fields); create/update/delete, workflows and custom actions continue to authorize against the complete union. Standard Data API calls inherit `X-OpenXiangda-Perspective` automatically. Use `@CurrentPerspective()` only when custom Nest code reads outside the standard Data API, and apply an equivalent read projection explicitly. Business code must not persist a platform Token or authorization result and must not implement a second identity path.
34
+ - Use the current logged-in user and the union of that user's application roles. An app for every logged-in platform user may declare one existing package role through `authz.authenticatedUserRoleCode`; the platform materializes it on first access and unions it with manual and business-projected roles. Do not fabricate a browser fallback role or use a business membership resource for this universal audience. An optional declared Perspective projects only read visibility (pages, rows and fields); create/update/delete, workflows and custom actions continue to authorize against the complete union. Standard Data API calls inherit `X-OpenXiangda-Perspective` automatically. Use `@CurrentPerspective()` only when custom Nest code reads outside the standard Data API, and apply an equivalent read projection explicitly. Business code must not persist a platform Token or authorization result and must not implement a second identity path.
16
35
  - Fields inherit the resource read/create/update capabilities. Use field `access` only to tighten them; arrays are all-of and `false` is explicit deny. There is no `write` fallback.
17
36
  - Field `type` is semantic, never a hand-authored database type. The supported catalog is exactly `text.short`, `text.long`, `text.rich`, `number.integer`, `number.decimal`, `boolean`, `date`, `time`, `datetime`, `date-range`, `datetime-range`, `option.single`, `option.multiple`, `cascade.single`, `cascade.multiple`, `user.single`, `user.multiple`, `department.single`, `department.multiple`, `resource-ref.single`, `resource-ref.multiple`, `file`, `image`, `signature`, `address`, `location`, `json`, `serial-number`, `uuid` and `subtable`. The compiler alone derives PostgreSQL columns, constraints and indexes.
18
37
  - Every `date-range` and `datetime-range` field explicitly declares `rangeBoundary: 'closed' | 'half-open'`. Values remain `{ start, end }`; callers never send or override the boundary. Closed ranges allow `start <= end` and use PostgreSQL `[]`; half-open ranges require `start < end` and use `[)`.
@@ -27,11 +27,18 @@ BrowserRouter、RuntimeBoundary、Refine、generated routes 和后台 Shell
27
27
  `/admin/todos` 与 `/m/todos`,只读取当前用户的消息/待办投影并统一解析目标页面。
28
28
 
29
29
  自定义后台页使用 `OpenXiangdaAdminPage` 作为页面根;Ant Design 组件自动继承平台
30
- `ConfigProvider`,图表等 TypeScript 渲染值读取 `useOpenXiangdaTheme()`,自定义 CSS
31
- 只使用公开的 `--oxa-color-*`、`--oxa-shadow-surface`
32
- `--oxa-radius-surface` 语义变量。不要为后台页新建主题 Provider,也不要把白色、黑色、
33
- 浅灰色写死为页面、卡片、边框或文字颜色。品牌色只能用于品牌和有明确语义的数据分类,
34
- 不能替代结构色。
30
+ `ConfigProvider`,图表等 TypeScript 渲染值读取 `theme.useToken()`,自定义 CSS
31
+ 只使用平台生成的 `--oxa-antd-*` CSS 变量。不要为后台页新建主题 Provider、主题状态
32
+ 或颜色别名,也不要把白色、黑色、浅灰色写死为页面、卡片、边框或文字颜色。品牌色只能
33
+ 用于品牌和有明确语义的数据分类,不能替代结构色。主题只由平台的 Ant Design
34
+ `defaultAlgorithm`/`darkAlgorithm` 生成;Seed 由平台集中维护,Map/Alias、间距、排版、
35
+ 动效、阴影、层级、断点和组件尺寸均从算法令牌读取。未来配色扩展只能修改平台集中维护
36
+ 的 seed,不得在应用内新建 token registry 或任意颜色输入。
37
+
38
+ 用户端页面不属于这份后台主题合同,可以使用 Tailwind CSS 和自己的产品组件/设计令牌;
39
+ 用户端不应依赖 `--oxa-antd-*`,也不能修改后台 Provider 或平台 Seed。
40
+ 模板已预装 Tailwind CSS v4 与 Vite 插件;在用户端页面直接使用 utility class,
41
+ 并在 `apps/web/src/user-end/tailwind.css` 的 `@theme` 中集中维护用户端产品令牌。
35
42
 
36
43
  登录声明位于 `frontend.authentication`,只允许现有平台用户并拒绝注册。桌面 `/login`
37
44
  与移动 `/m/login` 是独立生成的 `authenticationSurfaces`,不计入受保护的
@@ -15,7 +15,7 @@
15
15
  "@nestjs/common": "11.1.29",
16
16
  "@nestjs/core": "11.1.29",
17
17
  "@nestjs/platform-fastify": "11.1.29",
18
- "openxiangda": "2.0.0-alpha.62",
18
+ "openxiangda": "2.0.0-alpha.65",
19
19
  "reflect-metadata": "0.2.2",
20
20
  "rxjs": "7.8.2"
21
21
  },
@@ -5,7 +5,6 @@ import {
5
5
  defineApplicationContributions,
6
6
  OpenXiangdaAdminPage,
7
7
  OpenXiangdaApplication,
8
- useOpenXiangdaTheme,
9
8
  } from 'openxiangda/react';
10
9
  import type { DataFieldSurface, DataResourceSurface } from 'openxiangda/core';
11
10
  import 'openxiangda/react/styles.css';
@@ -198,12 +197,9 @@ const resourceDefinitions = Object.fromEntries(
198
197
  );
199
198
 
200
199
  function ContentOperationsPage() {
201
- const { effectiveAppearance, tokens } = useOpenXiangdaTheme();
202
200
  return (
203
201
  <OpenXiangdaAdminPage
204
202
  className="theme-contract-page"
205
- data-primary={tokens.primary}
206
- data-theme={effectiveAppearance}
207
203
  data-testid="custom-admin-page"
208
204
  >
209
205
  <Typography.Title level={2}>内容业务操作</Typography.Title>
@@ -197,7 +197,7 @@ async function mockPlatform(
197
197
  body: JSON.stringify({
198
198
  code: 200,
199
199
  data: {
200
- schemaVersion: 'openxiangda.native-data-page/v2',
200
+ schemaVersion: 'openxiangda.data-page/v2',
201
201
  resourceCode,
202
202
  appVersionId: 'app-version-1',
203
203
  environmentHeadRevision: 1,
@@ -301,18 +301,44 @@ test('custom admin pages inherit tokens and follow live system appearance', asyn
301
301
  const customPage = page.getByTestId('custom-admin-page');
302
302
  const customPanel = customPage.locator('.theme-contract-panel');
303
303
  await expect(page.locator('html')).toHaveAttribute('data-oxa-theme', 'dark');
304
- await expect(customPage).toHaveAttribute('data-theme', 'dark');
305
- await expect(customPage).toHaveAttribute('data-primary', '#447bdc');
306
- await expect(customPage).toHaveCSS('background-color', 'rgb(11, 21, 36)');
307
- await expect(customPanel).toHaveCSS('background-color', 'rgb(17, 29, 46)');
308
- await expect(customPanel).toHaveCSS('color', 'rgb(215, 224, 236)');
309
- await expect(customPanel).toHaveCSS('border-color', 'rgb(38, 55, 79)');
304
+ const readThemeColors = () =>
305
+ page.evaluate(() => {
306
+ const root = document.documentElement;
307
+ const host = document.querySelector('[data-testid="custom-admin-page"]') ?? root;
308
+ const probe = document.createElement('div');
309
+ probe.style.cssText = [
310
+ 'position:fixed',
311
+ 'width:1px',
312
+ 'height:1px',
313
+ 'background:var(--oxa-antd-color-bg-layout)',
314
+ 'color:var(--oxa-antd-color-bg-container)',
315
+ 'border:1px solid var(--oxa-antd-color-border-secondary)',
316
+ 'outline:1px solid var(--oxa-antd-color-text)',
317
+ ].join(';');
318
+ host.appendChild(probe);
319
+ const styles = getComputedStyle(probe);
320
+ const result = {
321
+ canvas: styles.backgroundColor,
322
+ surface: styles.color,
323
+ border: styles.borderTopColor,
324
+ text: styles.outlineColor,
325
+ };
326
+ probe.remove();
327
+ return result;
328
+ });
329
+ const darkColors = await readThemeColors();
330
+ await expect(customPage).toHaveCSS('background-color', darkColors.canvas);
331
+ await expect(customPanel).toHaveCSS('background-color', darkColors.surface);
332
+ await expect(customPanel).toHaveCSS('color', darkColors.text);
333
+ await expect(customPanel).toHaveCSS('border-color', darkColors.border);
310
334
 
311
335
  await page.emulateMedia({ colorScheme: 'light' });
312
336
  await expect(page.locator('html')).toHaveAttribute('data-oxa-theme', 'light');
313
- await expect(customPage).toHaveAttribute('data-theme', 'light');
314
- await expect(customPage).toHaveCSS('background-color', 'rgb(245, 245, 245)');
315
- await expect(customPanel).toHaveCSS('background-color', 'rgb(255, 255, 255)');
337
+ const lightColors = await readThemeColors();
338
+ await expect(customPage).toHaveCSS('background-color', lightColors.canvas);
339
+ await expect(customPanel).toHaveCSS('background-color', lightColors.surface);
340
+ await expect(customPanel).toHaveCSS('color', lightColors.text);
341
+ await expect(customPanel).toHaveCSS('border-color', lightColors.border);
316
342
  });
317
343
 
318
344
  test('opens the stable admin entry under the preproduction gateway mount', async ({ page }) => {
@@ -6,10 +6,10 @@
6
6
  }
7
7
 
8
8
  .theme-contract-panel {
9
- border: 1px solid var(--oxa-color-border);
10
- border-radius: var(--oxa-radius-surface);
11
- background: var(--oxa-color-surface);
9
+ border: 1px solid var(--oxa-antd-color-border-secondary);
10
+ border-radius: var(--oxa-antd-border-radius);
11
+ background: var(--oxa-antd-color-bg-container);
12
12
  padding: 24px;
13
- color: var(--oxa-color-text);
14
- box-shadow: var(--oxa-shadow-surface);
13
+ color: var(--oxa-antd-color-text);
14
+ box-shadow: var(--oxa-antd-box-shadow-tertiary);
15
15
  }
@@ -14,17 +14,19 @@
14
14
  "@ant-design/icons": "6.2.3",
15
15
  "@refinedev/core": "5.0.12",
16
16
  "antd": "6.4.3",
17
- "openxiangda": "2.0.0-alpha.62",
17
+ "openxiangda": "2.0.0-alpha.65",
18
18
  "react": "19.2.8",
19
19
  "react-dom": "19.2.8",
20
20
  "react-router-dom": "7.8.2"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@playwright/test": "1.62.1",
24
+ "@tailwindcss/vite": "4.3.3",
24
25
  "@types/node": "24.13.3",
25
26
  "@types/react": "19.2.14",
26
27
  "@types/react-dom": "19.2.3",
27
28
  "@vitejs/plugin-react": "4.7.0",
29
+ "tailwindcss": "4.3.3",
28
30
  "tsx": "4.23.12",
29
31
  "typescript": "5.9.3",
30
32
  "vite": "7.1.3"
@@ -20,6 +20,7 @@ import {
20
20
  resourceDefinitions,
21
21
  workflowDefinitions,
22
22
  } from '../../../packages/contracts/src/generated.js';
23
+ import './user-end/tailwind.css';
23
24
  import 'openxiangda/react/styles.css';
24
25
 
25
26
  const ApplicationHome = () => <main>OpenXiangda 应用</main>;
@@ -0,0 +1,17 @@
1
+ /* User-end Tailwind foundation. Keep its reset and tokens separate from admin. */
2
+ @import "tailwindcss/theme.css";
3
+ @import "tailwindcss/utilities.css";
4
+
5
+ @source "../";
6
+
7
+ @theme {
8
+ --font-sans: Inter, "PingFang SC", "Microsoft YaHei", sans-serif;
9
+ --font-display: Inter, "PingFang SC", "Microsoft YaHei", sans-serif;
10
+ --color-user-brand-500: oklch(58.5% 0.205 256);
11
+ --color-user-brand-600: oklch(51.5% 0.224 256);
12
+ --color-user-surface: oklch(99% 0.002 256);
13
+ --color-user-canvas: oklch(97.5% 0.006 256);
14
+ --color-user-ink: oklch(25% 0.025 256);
15
+ --color-user-muted: oklch(50% 0.025 256);
16
+ --radius-user-card: 1rem;
17
+ }
@@ -1,4 +1,5 @@
1
1
  import react from '@vitejs/plugin-react';
2
+ import tailwindcss from '@tailwindcss/vite';
2
3
  import { defineConfig } from 'vite';
3
4
 
4
5
  const connectedProxy =
@@ -6,7 +7,7 @@ const connectedProxy =
6
7
  const port = Number(process.env.OPENXIANGDA_WEB_PORT || 5173);
7
8
 
8
9
  export default defineConfig({
9
- plugins: [react()],
10
+ plugins: [react(), tailwindcss()],
10
11
  base: './',
11
12
  optimizeDeps: {
12
13
  entries: [
@@ -23,7 +23,7 @@
23
23
  "build": "pnpm --recursive build"
24
24
  },
25
25
  "devDependencies": {
26
- "openxiangda": "2.0.0-alpha.62",
26
+ "openxiangda": "2.0.0-alpha.65",
27
27
  "typescript": "5.9.3"
28
28
  }
29
29
  }
@@ -1,5 +1,5 @@
1
1
  // Generated by OpenXiangda 2.0 Native compiler. Do not edit by hand.
2
- export const compilerContractVersion = "native-3" as const;
2
+ export const compilerContractVersion = "native-4" as const;
3
3
  export const appCode = "openxiangda-application" as const;
4
4
  export const appName = "OpenXiangda 应用" as const;
5
5
  export const appPerspectives = [] as const;