@james_sww/metaform-asset-distribution 0.1.0 → 0.1.2

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 (112) hide show
  1. package/README.md +8 -6
  2. package/codebase/codebase.json +13 -11
  3. package/codebase/template/.env.example +11 -1
  4. package/codebase/template/.metaform/template-overrides.json +24 -0
  5. package/codebase/template/index.html +1 -1
  6. package/codebase/template/src/components/common/empty-state.tsx +52 -0
  7. package/codebase/template/src/components/common/error-boundary.tsx +2 -2
  8. package/codebase/template/src/components/ui/alert.tsx +48 -50
  9. package/codebase/template/src/hooks/index.ts +2 -2
  10. package/codebase/template/src/integrations/platform.ts +65 -1
  11. package/codebase/template/src/layouts/role-workspace-shell.tsx +136 -48
  12. package/codebase/template/src/lib/http-client.ts +1 -1
  13. package/codebase/template/src/main.tsx +11 -1
  14. package/codebase/template/src/workspace/default-workspace.tsx +1 -1
  15. package/dist/application-source.d.ts +5 -3
  16. package/dist/application-source.js +43 -32
  17. package/dist/cli.js +1 -1
  18. package/dist/contracts.d.ts +6 -1
  19. package/dist/contracts.js +1 -1
  20. package/dist/export-source.js +3 -3
  21. package/dist/prepare.d.ts +1 -1
  22. package/dist/prepare.js +41 -25
  23. package/dist/source-map.d.ts +2 -1
  24. package/dist/source-map.js +10 -8
  25. package/dist/template.d.ts +1 -1
  26. package/dist/template.js +1 -1
  27. package/package.json +2 -2
  28. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/catalog.json +1 -0
  29. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/common-semantic-region-scaffold.json +1 -0
  30. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/components-business-role-tasks-icons.json +1 -0
  31. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/components-business-role-tasks-types.json +1 -0
  32. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/components-common-semantic-region-contract.json +1 -0
  33. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/layouts-role-workspace-account.json +1 -0
  34. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/layouts-role-workspace-icons.json +1 -0
  35. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/layouts-role-workspace-routes.json +1 -0
  36. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/layouts-role-workspace-types.json +1 -0
  37. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/lib-utils.json +1 -0
  38. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/quality-role-task-groups.json +1 -0
  39. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/quality-role-task-overview.json +1 -0
  40. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/role-workspace-shell.json +1 -0
  41. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/styles-styles.json +1 -0
  42. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/styles-theme-tokens.json +1 -0
  43. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-avatar.json +1 -0
  44. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-badge.json +1 -0
  45. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-button.json +1 -0
  46. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-collapsible.json +1 -0
  47. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-dropdown-menu.json +1 -0
  48. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-empty.json +1 -0
  49. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-sheet.json +1 -0
  50. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-skeleton.json +1 -0
  51. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/r/ui-tooltip.json +1 -0
  52. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/registry.json +1 -0
  53. package/registry/47e2405d581daf6aa5433b033351b639e11f9730d174a0563b88e36bc06402f9/release.json +1 -0
  54. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/catalog.json +1 -0
  55. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/common-semantic-region-scaffold.json +1 -0
  56. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/components-business-role-tasks-icons.json +1 -0
  57. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/components-business-role-tasks-types.json +1 -0
  58. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/components-common-semantic-region-contract.json +1 -0
  59. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/layouts-role-workspace-account.json +1 -0
  60. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/layouts-role-workspace-icons.json +1 -0
  61. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/layouts-role-workspace-routes.json +1 -0
  62. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/layouts-role-workspace-types.json +1 -0
  63. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/lib-utils.json +1 -0
  64. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/quality-role-task-groups.json +1 -0
  65. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/quality-role-task-overview.json +1 -0
  66. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/role-workspace-shell.json +1 -0
  67. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/styles-styles.json +1 -0
  68. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/styles-theme-tokens.json +1 -0
  69. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-avatar.json +1 -0
  70. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-badge.json +1 -0
  71. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-button.json +1 -0
  72. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-collapsible.json +1 -0
  73. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-dropdown-menu.json +1 -0
  74. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-empty.json +1 -0
  75. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-sheet.json +1 -0
  76. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-skeleton.json +1 -0
  77. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/r/ui-tooltip.json +1 -0
  78. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/registry.json +1 -0
  79. package/registry/5bce235170d2de6e2c3b0cfe10d0596dd53189c83acb8d3e4256b11606d95a22/release.json +1 -0
  80. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/catalog.json +1 -0
  81. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/common-semantic-region-scaffold.json +1 -0
  82. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/components-business-role-tasks-icons.json +1 -0
  83. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/components-business-role-tasks-types.json +1 -0
  84. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/components-common-semantic-region-contract.json +1 -0
  85. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/lib-utils.json +1 -0
  86. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/quality-role-task-groups.json +1 -0
  87. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/quality-role-task-overview.json +1 -0
  88. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/styles-styles.json +1 -0
  89. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/styles-theme-tokens.json +1 -0
  90. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/ui-badge.json +1 -0
  91. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/ui-button.json +1 -0
  92. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/ui-collapsible.json +1 -0
  93. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/r/ui-empty.json +1 -0
  94. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/registry.json +1 -0
  95. package/registry/ac851c907cc97669042b64a5d30d9bec2017d07e43c8cd933db399f446c5bb86/release.json +1 -0
  96. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/catalog.json +1 -0
  97. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/common-semantic-region-scaffold.json +1 -0
  98. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/components-business-role-tasks-icons.json +1 -0
  99. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/components-business-role-tasks-types.json +1 -0
  100. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/components-common-semantic-region-contract.json +1 -0
  101. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/lib-utils.json +1 -0
  102. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/quality-role-task-groups.json +1 -0
  103. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/quality-role-task-overview.json +1 -0
  104. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/styles-styles.json +1 -0
  105. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/styles-theme-tokens.json +1 -0
  106. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/ui-badge.json +1 -0
  107. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/ui-button.json +1 -0
  108. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/ui-collapsible.json +1 -0
  109. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/r/ui-empty.json +1 -0
  110. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/registry.json +1 -0
  111. package/registry/f5db0c88ee95ccef50d6ced1f08e935c8c56006e337b978e332f0974b18d79b2/release.json +1 -0
  112. package/registry/release.json +1 -1
@@ -228,7 +228,7 @@ export class HttpClient {
228
228
 
229
229
  // 创建默认实例
230
230
  export const httpClient = new HttpClient({
231
- baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
231
+ baseURL: (import.meta as { env?: { VITE_API_BASE_URL?: string } }).env?.VITE_API_BASE_URL || '/api',
232
232
  onRequest: async (config) => {
233
233
  // 自动添加认证 token
234
234
  const token = localStorage.getItem('auth_token');
@@ -1,6 +1,16 @@
1
1
  import { createRoot } from "react-dom/client";
2
+ import { Toaster } from "sonner";
2
3
 
4
+ import { ErrorBoundary } from "./components/common/error-boundary";
5
+ import { ToastProvider } from "./contexts/toast-context";
3
6
  import { DefaultWorkspace } from "./workspace/default-workspace";
4
7
  import "./styles/index.css";
5
8
 
6
- createRoot(document.getElementById("root")!).render(<DefaultWorkspace />);
9
+ createRoot(document.getElementById("root")!).render(
10
+ <ErrorBoundary>
11
+ <ToastProvider>
12
+ <DefaultWorkspace />
13
+ <Toaster position="top-right" richColors closeButton />
14
+ </ToastProvider>
15
+ </ErrorBoundary>,
16
+ );
@@ -23,7 +23,7 @@ export function DefaultWorkspace() {
23
23
  }}
24
24
  slots={{
25
25
  content: (
26
- <div className="mx-auto w-full max-w-6xl space-y-6 p-4 md:p-8">
26
+ <div className="mx-auto w-full max-w-6xl space-y-6">
27
27
  <h1 className="text-xl font-semibold text-foreground">
28
28
  {contract.title}
29
29
  </h1>
@@ -1,3 +1,5 @@
1
- import type { ApplicationDesign } from './application-design.js';
2
- import { type RegistryRelease } from './contracts.js';
3
- export declare function generateApplicationFiles(design: ApplicationDesign, release: RegistryRelease): Record<string, string>;
1
+ import type { ApplicationDesign } from "./application-design.js";
2
+ import { type RegistryRelease } from "./contracts.js";
3
+ export declare function generateApplicationFiles(design: ApplicationDesign, release: RegistryRelease, options?: {
4
+ accent?: string;
5
+ }): Record<string, string>;
@@ -1,64 +1,75 @@
1
- import { posix } from 'node:path';
2
- import { DistributionError } from './contracts.js';
1
+ import { posix } from "node:path";
2
+ import { DistributionError } from "./contracts.js";
3
3
  const literal = (value) => JSON.stringify(value, null, 2)
4
- .replaceAll('<', '\\u003c')
5
- .replaceAll('\u2028', '\\u2028')
6
- .replaceAll('\u2029', '\\u2029');
4
+ .replaceAll("<", "\\u003c")
5
+ .replaceAll("\u2028", "\\u2028")
6
+ .replaceAll("\u2029", "\\u2029");
7
7
  function modulePath(from, to) {
8
- let p = posix.relative(posix.dirname(from), to).replace(/\.tsx?$/, '');
9
- if (!p.startsWith('.'))
10
- p = './' + p;
8
+ let p = posix.relative(posix.dirname(from), to).replace(/\.tsx?$/, "");
9
+ if (!p.startsWith("."))
10
+ p = "./" + p;
11
11
  return JSON.stringify(p);
12
12
  }
13
- function propsSource(value, events, path = '') {
13
+ function propsSource(value, events, path = "") {
14
14
  if (events.has(path))
15
15
  return `(...args: unknown[]) => onAction(${literal(events.get(path))}, args[0])`;
16
16
  if (Array.isArray(value))
17
- return ('[' +
18
- value.map((x, i) => propsSource(x, events, path ? path + '.' + i : String(i))).join(',') +
19
- ']');
20
- if (value && typeof value === 'object') {
17
+ return ("[" +
18
+ value
19
+ .map((x, i) => propsSource(x, events, path ? path + "." + i : String(i)))
20
+ .join(",") +
21
+ "]");
22
+ if (value && typeof value === "object") {
21
23
  const keys = new Set(Object.keys(value));
22
24
  for (const eventPath of events.keys())
23
- if (!path || eventPath.startsWith(path + '.'))
24
- keys.add(eventPath.slice(path ? path.length + 1 : 0).split('.')[0]);
25
- return ('{' +
25
+ if (!path || eventPath.startsWith(path + "."))
26
+ keys.add(eventPath.slice(path ? path.length + 1 : 0).split(".")[0]);
27
+ return ("{" +
26
28
  [...keys]
27
- .map((k) => `${literal(k)}:${propsSource(Object.hasOwn(value, k) ? value[k] : {}, events, path ? path + '.' + k : k)}`)
28
- .join(',') +
29
- '}');
29
+ .map((k) => `${literal(k)}:${propsSource(Object.hasOwn(value, k) ? value[k] : {}, events, path ? path + "." + k : k)}`)
30
+ .join(",") +
31
+ "}");
30
32
  }
31
33
  return literal(value);
32
34
  }
33
- export function generateApplicationFiles(design, release) {
35
+ export function generateApplicationFiles(design, release, options = {}) {
34
36
  const files = {};
35
- const scaffold = 'src/components/common/semantic-region/semantic-region-scaffold.tsx';
36
- const types = 'src/components/common/semantic-region/contract.ts';
37
+ const accent = options.accent && /^[a-z][a-z0-9-]*$/.test(options.accent)
38
+ ? ` data-accent="${options.accent}"`
39
+ : "";
40
+ const scaffold = "src/components/common/semantic-region/semantic-region-scaffold.tsx";
41
+ const types = "src/components/common/semantic-region/contract.ts";
37
42
  const targets = [];
38
43
  for (const role of design.roles) {
39
44
  const pages = design.pages.filter((p) => p.roleKey === role.roleKey);
40
45
  const regions = design.regions.filter((r) => pages.some((p) => p.id === r.contract.pageId));
41
46
  const root = `src/features/${role.roleKey}`;
42
47
  const entry = `${role.roleKey}/index.html`;
43
- targets.push({ roleKey: role.roleKey, entry, path: `/${role.roleKey}/`, featuresRoot: root });
48
+ targets.push({
49
+ roleKey: role.roleKey,
50
+ entry,
51
+ path: `/${role.roleKey}/`,
52
+ featuresRoot: root,
53
+ });
44
54
  files[entry] =
45
- `<!doctype html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>${design.name.replace(/[<>&"']/g, '')}</title></head><body><div id="root"></div><script type="module" src="/${root}/main.tsx"></script></body></html>\n`;
55
+ `<!doctype html><html lang="zh-CN"${accent}><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>${design.name.replace(/[<>&"']/g, "")}</title></head><body><div id="root"></div><script type="module" src="/${root}/main.tsx"></script></body></html>\n`;
46
56
  for (const r of regions) {
47
57
  const componentPath = `${root}/regions/${r.contract.id}.tsx`;
48
58
  const contractPath = `${root}/regions/${r.contract.id}.contract.ts`;
49
59
  files[contractPath] =
50
60
  `import type { SemanticRegionContract } from ${modulePath(contractPath, types)};\nexport const contract: SemanticRegionContract = ${literal(r.contract)};\n`;
51
61
  const common = `import { contract } from './${r.contract.id}.contract';\nexport { contract };\nexport interface RegionProps { onAction(id: string, context?: unknown): void; context?: unknown }\n`;
52
- if (r.implementation.kind === 'scaffold') {
62
+ if (r.implementation.kind === "scaffold") {
53
63
  files[componentPath] =
54
64
  `import { SemanticRegionScaffold } from ${modulePath(componentPath, scaffold)};\n${common}export function Region({ onAction }: RegionProps) { return <SemanticRegionScaffold contract={contract} commands={{ onAction: (action) => onAction(action.id) }} />; }\n`;
55
65
  }
56
66
  else {
57
67
  const impl = r.implementation;
58
68
  const item = release.items.find((a) => a.assetId === impl.asset.id && a.version === impl.asset.version);
59
- const source = item.files.find((f) => f.sourcePath === item.contractRef) ?? item.files[0];
69
+ const source = item.files.find((f) => f.sourcePath === item.contractRef) ??
70
+ item.files[0];
60
71
  if (!source)
61
- throw new DistributionError('asset_entry_missing', [item.assetId]);
72
+ throw new DistributionError("asset_entry_missing", [item.assetId]);
62
73
  files[componentPath] =
63
74
  `import { ${impl.exportName} } from ${modulePath(componentPath, source.targetPath)};\n${common}export function Region({ onAction }: RegionProps) { return <section data-region-id={contract.id} data-region-implementation="asset"><${impl.exportName} {...${propsSource(impl.props, new Map(impl.events.map((e) => [e.propPath, e.actionId])))}} /></section>; }\n`;
64
75
  }
@@ -70,11 +81,11 @@ export function generateApplicationFiles(design, release) {
70
81
  import { RoleWorkspaceShell } from '../../layouts/role-workspace-shell';
71
82
  import { SemanticRegionScaffold } from '../../components/common/semantic-region/semantic-region-scaffold';
72
83
  import { activeWorkspaceRoute, resolveWorkspacePage, workspaceRouteContract, type WorkspaceRouteMap } from '../../workspace/routes';
73
- ${regions.map((r, i) => `import { Region as Region${i}, contract as contract${i} } from './regions/${r.contract.id}';`).join('\n')}
84
+ ${regions.map((r, i) => `import { Region as Region${i}, contract as contract${i} } from './regions/${r.contract.id}';`).join("\n")}
74
85
  const pages = ${literal(pages)};
75
86
  const workspaceRoutes: WorkspaceRouteMap = ${literal(role.workspaceRoutes ?? {})};
76
87
  const mappedPages = new Set(Object.values(workspaceRoutes));
77
- const regions = { ${regions.map((r, i) => `${literal(r.contract.id)}: { View: Region${i}, contract: contract${i} }`).join(',')} };
88
+ const regions = { ${regions.map((r, i) => `${literal(r.contract.id)}: { View: Region${i}, contract: contract${i} }`).join(",")} };
78
89
  export function Workspace() {
79
90
  const [state, setState] = useState<{pageId:string; contexts:Record<string,unknown>}>({pageId:${literal(role.entryPageId)},contexts:{}});
80
91
  const [feedback, setFeedback] = useState('');
@@ -97,10 +108,10 @@ export function Workspace() {
97
108
  }
98
109
  return <RoleWorkspaceShell model={{productName:${literal(design.name)},workspaceTitle:${literal(role.label)},actorLabel:'未连接登录服务',roleLabel:${literal(role.label)},themes:pages.filter((p) => p.menu !== false && !mappedPages.has(p.id)).map((p) => ({id:p.id,label:p.title,availability:'implemented' as const})),activeThemeId:page ? activeWorkspaceRoute(page.id,workspaceRoutes) : state.pageId}}
99
110
  commands={{onSelectTheme:selectPage}}
100
- slots={{content:<div className="mx-auto w-full max-w-6xl space-y-6 p-4 md:p-8"><header className="space-y-2"><p className="text-sm text-muted-foreground">应用设计底稿 · 真实业务数据尚未接入</p><h1 className="text-2xl font-semibold text-foreground">{page?.title ?? placeholder?.title ?? '待确认页面'}</h1></header>{feedback ? <p role="status" className="text-sm text-muted-foreground">{feedback}</p> : null}{page ? page.regionIds.map((id) => {const View = regions[id as keyof typeof regions].View; return <View key={id} context={state.contexts[page.id]} onAction={(actionId,context) => onAction(id,actionId,context)} />;}) : placeholder ? <SemanticRegionScaffold contract={placeholder} /> : <p>页面映射待确认</p>}</div>}} />;
111
+ slots={{content:<div className="mx-auto w-full max-w-6xl space-y-6"><header className="space-y-2"><p className="text-sm text-muted-foreground">应用设计底稿 · 真实业务数据尚未接入</p><h1 className="text-2xl font-semibold text-foreground">{page?.title ?? placeholder?.title ?? '待确认页面'}</h1></header>{feedback ? <p role="status" className="text-sm text-muted-foreground">{feedback}</p> : null}{page ? page.regionIds.map((id) => {const View = regions[id as keyof typeof regions].View; return <View key={id} context={state.contexts[page.id]} onAction={(actionId,context) => onAction(id,actionId,context)} />;}) : placeholder ? <SemanticRegionScaffold contract={placeholder} /> : <p>页面映射待确认</p>}</div>}} />;
101
112
  }\n`;
102
113
  }
103
- files['role-targets.json'] = literal(targets) + '\n';
104
- files['docs/application-design.json'] = literal(design) + '\n';
114
+ files["role-targets.json"] = literal(targets) + "\n";
115
+ files["docs/application-design.json"] = literal(design) + "\n";
105
116
  return files;
106
117
  }
package/dist/cli.js CHANGED
@@ -96,7 +96,7 @@ export async function runCLI(argv, io) {
96
96
  else if (cmd === 'init')
97
97
  output = await initProject({
98
98
  projectRoot: root,
99
- templateVersion: options.get('template') ?? '0.1.0',
99
+ templateVersion: options.get('template'),
100
100
  codebaseRoot: options.get('codebase'),
101
101
  });
102
102
  else if (cmd === 'reconcile')
@@ -14,7 +14,7 @@ export type SourceFile = {
14
14
  contentBase64: string;
15
15
  sha256: string;
16
16
  bytes: number;
17
- kind: 'source' | 'style' | 'resource' | 'license';
17
+ kind: "source" | "style" | "resource" | "license";
18
18
  };
19
19
  export type SourceItem = {
20
20
  assetId: string;
@@ -35,6 +35,11 @@ export type RegistryRelease = {
35
35
  id: string;
36
36
  version: string;
37
37
  sourceCommit: string;
38
+ sourceBaseline?: {
39
+ sourcesDigest: string;
40
+ contentDigest: string;
41
+ dirty?: boolean;
42
+ };
38
43
  digest: string;
39
44
  items: SourceItem[];
40
45
  catalog?: CatalogSnapshot;
package/dist/contracts.js CHANGED
@@ -2,7 +2,7 @@ export class DistributionError extends Error {
2
2
  code;
3
3
  paths;
4
4
  constructor(code, paths = []) {
5
- super(code + (paths.length ? ': ' + paths.join(', ') : ''));
5
+ super(code + (paths.length ? ": " + paths.join(", ") : ""));
6
6
  this.code = code;
7
7
  this.paths = paths;
8
8
  }
@@ -111,7 +111,7 @@ export async function exportAssets(input) {
111
111
  const imports = new Map(), dependencies = new Set();
112
112
  function classify(specifier) {
113
113
  if (specifier.startsWith('@metaform/design-system/'))
114
- return ('frontend/packages/design-system/src/' +
114
+ return ('vendor/design-system/src/' +
115
115
  specifier.slice('@metaform/design-system/'.length));
116
116
  if (specifier.startsWith('.'))
117
117
  return posix.normalize(posix.join(posix.dirname(source), specifier));
@@ -165,7 +165,7 @@ export async function exportAssets(input) {
165
165
  else
166
166
  text = rewriteModule(text, source, (s) => imports.get(s) ?? s);
167
167
  if (SEEDS[id]) {
168
- const style = await collect('frontend/packages/design-system/src/styles.css');
168
+ const style = await collect('vendor/design-system/src/styles.css');
169
169
  dependencies.add(style);
170
170
  let cssImport = posix.relative(posix.dirname(target), 'src/styles/styles.css');
171
171
  if (!cssImport.startsWith('.'))
@@ -183,6 +183,6 @@ export async function exportAssets(input) {
183
183
  throw new DistributionError('asset_version', [ref.id]);
184
184
  await collect(SEEDS[ref.id].source);
185
185
  }
186
- await collect('frontend/packages/design-system/src/styles.css');
186
+ await collect('vendor/design-system/src/styles.css');
187
187
  return [...items.values()].sort((a, b) => a.assetId.localeCompare(b.assetId));
188
188
  }
package/dist/prepare.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type InstallPlan, type RegistryRelease } from './contracts.js';
1
+ import { type InstallPlan, type RegistryRelease } from "./contracts.js";
2
2
  export declare function planPrepare(input: {
3
3
  projectRoot: string;
4
4
  release: RegistryRelease;
package/dist/prepare.js CHANGED
@@ -1,29 +1,37 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { join } from 'node:path';
3
- import { validateApplicationDesign } from './application-design.js';
4
- import { generateApplicationFiles } from './application-source.js';
5
- import { planAdd } from './planner.js';
6
- import { projectFiles } from './provenance.js';
7
- import { canonical, sha256, sourceFile, validateTargetPath } from './validate.js';
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { validateApplicationDesign } from "./application-design.js";
4
+ import { generateApplicationFiles } from "./application-source.js";
5
+ import { planAdd } from "./planner.js";
6
+ import { projectFiles } from "./provenance.js";
7
+ import { canonical, sha256, sourceFile, validateTargetPath, } from "./validate.js";
8
8
  export async function planPrepare(input) {
9
9
  const { projectRoot: root, release } = input;
10
10
  const design = validateApplicationDesign(input.design, release);
11
- const scaffold = release.items.find((a) => a.assetId === 'common.semantic-region-scaffold');
11
+ const scaffold = release.items.find((a) => a.assetId === "common.semantic-region-scaffold");
12
12
  const refs = new Map();
13
- refs.set('common.semantic-region-scaffold', {
14
- id: 'common.semantic-region-scaffold',
15
- version: scaffold?.version ?? '',
13
+ refs.set("common.semantic-region-scaffold", {
14
+ id: "common.semantic-region-scaffold",
15
+ version: scaffold?.version ?? "",
16
16
  });
17
17
  for (const r of design.regions)
18
- if (r.implementation.kind === 'asset')
18
+ if (r.implementation.kind === "asset")
19
19
  refs.set(r.implementation.asset.id, r.implementation.asset);
20
- const plan = await planAdd({ projectRoot: root, release, assets: [...refs.values()] });
20
+ const plan = await planAdd({
21
+ projectRoot: root,
22
+ release,
23
+ assets: [...refs.values()],
24
+ });
21
25
  const existing = await projectFiles(root);
22
- const baselinePath = '.metaform/application-baselines.json';
26
+ const baselinePath = ".metaform/application-baselines.json";
23
27
  let previous = { files: {} };
24
28
  if (existing[baselinePath])
25
- previous = JSON.parse(await readFile(validateTargetPath(root, baselinePath), 'utf8'));
26
- const generated = generateApplicationFiles(design, release);
29
+ previous = JSON.parse(await readFile(validateTargetPath(root, baselinePath), "utf8"));
30
+ const entryHtml = existing["index.html"]
31
+ ? await readFile(join(root, "index.html"), "utf8")
32
+ : "";
33
+ const accent = entryHtml.match(/data-accent=["']([a-z][a-z0-9-]*)["']/)?.[1];
34
+ const generated = generateApplicationFiles(design, release, { accent });
27
35
  const hashes = {};
28
36
  for (const [path, content] of Object.entries(generated)) {
29
37
  validateTargetPath(root, path);
@@ -34,35 +42,43 @@ export async function planPrepare(input) {
34
42
  if (existing[path] &&
35
43
  existing[path] !== hash &&
36
44
  existing[path] !== previous.files[path] &&
37
- !(path === 'role-targets.json' &&
45
+ !(path === "role-targets.json" &&
38
46
  !existing[baselinePath] &&
39
- (await readFile(join(root, path), 'utf8')).trim() === '[]')) {
40
- plan.conflicts.push({ code: 'application_file_conflict', path, assetIds: [] });
47
+ (await readFile(join(root, path), "utf8")).trim() === "[]")) {
48
+ plan.conflicts.push({
49
+ code: "application_file_conflict",
50
+ path,
51
+ assetIds: [],
52
+ });
41
53
  continue;
42
54
  }
43
55
  if (existing[path] !== hash)
44
56
  plan.changes.push({
45
57
  path,
46
58
  beforeSha256: existing[path] ?? null,
47
- after: sourceFile('application-design/' + path, path, content),
59
+ after: sourceFile("application-design/" + path, path, content),
48
60
  });
49
61
  }
50
62
  for (const path of Object.keys(previous.files))
51
63
  if (!(path in hashes))
52
- plan.conflicts.push({ code: 'application_removal_requires_review', path, assetIds: [] });
64
+ plan.conflicts.push({
65
+ code: "application_removal_requires_review",
66
+ path,
67
+ assetIds: [],
68
+ });
53
69
  const metadata = JSON.stringify({
54
70
  schemaVersion: 1,
55
- phase: 'before-s7',
71
+ phase: "before-s7",
56
72
  designDigest: sha256(canonical(design)),
57
73
  registryDigest: release.digest,
58
74
  files: hashes,
59
- }, null, 2) + '\n';
75
+ }, null, 2) + "\n";
60
76
  if (existing[baselinePath] !== sha256(metadata))
61
77
  plan.changes.push({
62
78
  path: baselinePath,
63
79
  beforeSha256: existing[baselinePath] ?? null,
64
- after: sourceFile('application-design/baseline', baselinePath, metadata),
80
+ after: sourceFile("application-design/baseline", baselinePath, metadata),
65
81
  });
66
- plan.planDigest = sha256(canonical({ ...plan, planDigest: '' }));
82
+ plan.planDigest = sha256(canonical({ ...plan, planDigest: "" }));
67
83
  return plan;
68
84
  }
@@ -1,8 +1,9 @@
1
1
  export declare const VERSION = "0.1.0";
2
+ export declare const DISTRIBUTED_ASSET_IDS: readonly ["common.semantic-region-scaffold", "quality.role-task-overview", "quality.role-task-groups"];
2
3
  export declare const SEEDS: Record<string, {
3
4
  source: string;
4
5
  target: string;
5
6
  exports: string[];
6
7
  }>;
7
8
  export declare const NPM_VERSIONS: Record<string, string>;
8
- export declare const SOURCE_ROOTS: readonly [readonly ["frontend/asset-library/platform/src/platform/", "src/layouts/"], readonly ["frontend/asset-library/layout/src/semantic-region/", "src/components/common/semantic-region/"], readonly ["frontend/packages/design-system/src/", "src/"], readonly ["frontend/asset-library/business/quality/src/role-tasks/", "src/components/business/role-tasks/"]];
9
+ export declare const SOURCE_ROOTS: readonly [readonly ["platform/src/platform/", "src/layouts/"], readonly ["layout/src/semantic-region/", "src/components/common/semantic-region/"], readonly ["vendor/design-system/src/", "src/"], readonly ["business/quality/src/role-tasks/", "src/components/business/role-tasks/"]];
@@ -1,22 +1,24 @@
1
1
  export const VERSION = '0.1.0';
2
+ // 外壳只随基础模板交付;保留旧导出入口供兼容验证。
3
+ export const DISTRIBUTED_ASSET_IDS = ['common.semantic-region-scaffold', 'quality.role-task-overview', 'quality.role-task-groups'];
2
4
  export const SEEDS = {
3
5
  'common.semantic-region-scaffold': {
4
- source: 'frontend/asset-library/layout/src/semantic-region/semantic-region-scaffold.tsx',
6
+ source: 'layout/src/semantic-region/semantic-region-scaffold.tsx',
5
7
  target: 'src/components/common/semantic-region/semantic-region-scaffold.tsx',
6
8
  exports: ['SemanticRegionScaffold'],
7
9
  },
8
10
  'role-workspace-shell': {
9
- source: 'frontend/asset-library/platform/src/platform/role-workspace-shell.tsx',
11
+ source: 'platform/src/platform/role-workspace-shell.tsx',
10
12
  target: 'src/layouts/role-workspace-shell.tsx',
11
13
  exports: ['RoleWorkspaceShell'],
12
14
  },
13
15
  'quality.role-task-overview': {
14
- source: 'frontend/asset-library/business/quality/src/role-tasks/role-task-overview.tsx',
16
+ source: 'business/quality/src/role-tasks/role-task-overview.tsx',
15
17
  target: 'src/components/business/role-tasks/role-task-overview.tsx',
16
18
  exports: ['RoleTaskOverview'],
17
19
  },
18
20
  'quality.role-task-groups': {
19
- source: 'frontend/asset-library/business/quality/src/role-tasks/role-task-groups.tsx',
21
+ source: 'business/quality/src/role-tasks/role-task-groups.tsx',
20
22
  target: 'src/components/business/role-tasks/role-task-groups.tsx',
21
23
  exports: ['RoleTaskGroups'],
22
24
  },
@@ -32,11 +34,11 @@ export const NPM_VERSIONS = {
32
34
  'tw-animate-css': '1.4.0',
33
35
  };
34
36
  export const SOURCE_ROOTS = [
35
- ['frontend/asset-library/platform/src/platform/', 'src/layouts/'],
36
- ['frontend/asset-library/layout/src/semantic-region/', 'src/components/common/semantic-region/'],
37
- ['frontend/packages/design-system/src/', 'src/'],
37
+ ['platform/src/platform/', 'src/layouts/'],
38
+ ['layout/src/semantic-region/', 'src/components/common/semantic-region/'],
39
+ ['vendor/design-system/src/', 'src/'],
38
40
  [
39
- 'frontend/asset-library/business/quality/src/role-tasks/',
41
+ 'business/quality/src/role-tasks/',
40
42
  'src/components/business/role-tasks/',
41
43
  ],
42
44
  ];
@@ -2,7 +2,7 @@ import { type RegistryRelease } from './contracts.js';
2
2
  export declare function initProject(input: {
3
3
  projectRoot: string;
4
4
  release?: RegistryRelease;
5
- templateVersion: string;
5
+ templateVersion?: string;
6
6
  codebaseRoot?: string;
7
7
  }): Promise<{
8
8
  codebase: {
package/dist/template.js CHANGED
@@ -22,7 +22,7 @@ export async function initProject(input) {
22
22
  Array.isArray(manifest.files) ||
23
23
  typeof manifest.files !== 'object')
24
24
  throw new DistributionError('invalid_codebase');
25
- if (manifest.version !== input.templateVersion)
25
+ if (input.templateVersion && manifest.version !== input.templateVersion)
26
26
  throw new DistributionError('template_version');
27
27
  const template = validateTargetPath(packageRoot, 'template');
28
28
  const actual = await projectFiles(template);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@james_sww/metaform-asset-distribution",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "metaform-assets": "./dist/bin.js"
@@ -33,7 +33,7 @@
33
33
  "access": "public"
34
34
  },
35
35
  "scripts": {
36
- "test": "node scripts/prepare-codebase.mjs && node scripts/prepare-contracts.mjs && vitest run",
36
+ "test": "node scripts/prepare-codebase.mjs && node scripts/prepare-contracts.mjs && vitest run --maxWorkers=1 --no-file-parallelism",
37
37
  "typecheck": "node scripts/prepare-contracts.mjs && tsc --noEmit",
38
38
  "build": "node scripts/prepare-contracts.mjs && tsc -p tsconfig.build.json",
39
39
  "test:standalone": "node tests/run-standalone.mjs",