@object-ui/runner 0.3.0

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.
@@ -0,0 +1,95 @@
1
+ import { AppSchema, PageSchema } from '@object-ui/types';
2
+
3
+ export interface MetadataLoader {
4
+ loadAppConfig(): Promise<AppSchema | null>;
5
+ loadPage(path: string): Promise<PageSchema | null>;
6
+ }
7
+
8
+ /**
9
+ * Strategy A: Local Bundle Loader (Vite Glob)
10
+ * Used during local development via 'pnpm dev:crm'
11
+ */
12
+ export class LocalBundleLoader implements MetadataLoader {
13
+ private appGlob = import.meta.glob('../app-data/app.json');
14
+ private pagesGlob = import.meta.glob('../app-data/pages/**/*.json');
15
+ private rootGlob = import.meta.glob('../app-data/*.json');
16
+
17
+ async loadAppConfig(): Promise<AppSchema | null> {
18
+ const key = '../app-data/app.json';
19
+ if (this.appGlob[key]) {
20
+ const mod: any = await this.appGlob[key]();
21
+ return mod.default || mod;
22
+ }
23
+ return null;
24
+ }
25
+
26
+ async loadPage(path: string): Promise<PageSchema | null> {
27
+ // 1. Normalize Path
28
+ const normalizedPath = path.replace(/^\//, '') || 'index';
29
+
30
+ // 2. Try Exact Match in Pages
31
+ if (await this.tryLoad(`../app-data/pages/${normalizedPath}.json`)) {
32
+ return await this.loadKey(`../app-data/pages/${normalizedPath}.json`);
33
+ }
34
+
35
+ // 3. Try Index Match
36
+ if (await this.tryLoad(`../app-data/pages/${normalizedPath}/index.json`)) {
37
+ return await this.loadKey(`../app-data/pages/${normalizedPath}/index.json`);
38
+ }
39
+
40
+ // 4. Try Root fallback
41
+ if (normalizedPath === 'index' && await this.tryLoad(`../app-data/index.json`)) {
42
+ return await this.loadKey(`../app-data/index.json`);
43
+ }
44
+
45
+ // 5. Dynamic Routing (Basic Mock)
46
+ // TODO: Implement proper glob matching for dynamic routes if needed here,
47
+ // but usually exact paths are enough for this loader demo.
48
+ return null;
49
+ }
50
+
51
+ private async tryLoad(key: string) {
52
+ return !!(this.pagesGlob[key] || this.rootGlob[key]);
53
+ }
54
+
55
+ private async loadKey(key: string) {
56
+ const loader = this.pagesGlob[key] || this.rootGlob[key];
57
+ if (!loader) return null;
58
+ const mod: any = await loader();
59
+ return mod.default || mod;
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Strategy B: Network Loader (Fetch API)
65
+ * Used in production to fetch JSONs from an API endpoint
66
+ */
67
+ export class NetworkLoader implements MetadataLoader {
68
+ private baseUrl: string;
69
+
70
+ constructor(baseUrl: string = '/api') {
71
+ this.baseUrl = baseUrl;
72
+ }
73
+
74
+ async loadAppConfig(): Promise<AppSchema | null> {
75
+ try {
76
+ const res = await fetch(`${this.baseUrl}/app.json`);
77
+ if (!res.ok) return null;
78
+ return await res.json();
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+
84
+ async loadPage(path: string): Promise<PageSchema | null> {
85
+ try {
86
+ // Maps /customers -> /api/pages/customers.json
87
+ const jsonPath = path === '/' ? '/index' : path;
88
+ const res = await fetch(`${this.baseUrl}/pages${jsonPath}.json`);
89
+ if (!res.ok) return null;
90
+ return await res.json();
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
95
+ }
@@ -0,0 +1,34 @@
1
+ import type { DataSource } from '@object-ui/core';
2
+
3
+ /**
4
+ * 模拟数据源 (Mock Adapter)
5
+ * 在真实项目中,你会在这里使用 fetch/axios 调用你的 API。
6
+ */
7
+ export class MockDataSource implements DataSource {
8
+ async find(resource: string, params?: any): Promise<any[]> {
9
+ console.log(`[DataSource] Querying ${resource}`, params);
10
+ return [];
11
+ }
12
+
13
+ async findOne(resource: string, id: string): Promise<any> {
14
+ return null;
15
+ }
16
+
17
+ async create(resource: string, data: any): Promise<any> {
18
+ // 模拟网络请求
19
+ await new Promise(resolve => setTimeout(resolve, 800));
20
+
21
+ console.log(`[DataSource] Created ${resource}:`, data);
22
+ alert(`Success! Created record in "${resource}":\n${JSON.stringify(data, null, 2)}`);
23
+
24
+ return { id: Math.random().toString(), ...data };
25
+ }
26
+
27
+ async update(resource: string, id: string, data: any): Promise<any> {
28
+ return data;
29
+ }
30
+
31
+ async delete(resource: string, id: string): Promise<any> {
32
+ return true;
33
+ }
34
+ }
package/src/main.tsx ADDED
@@ -0,0 +1,10 @@
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom/client'
3
+ import App from './App.tsx'
4
+ import './index.css'
5
+
6
+ ReactDOM.createRoot(document.getElementById('root')!).render(
7
+ <React.StrictMode>
8
+ <App />
9
+ </React.StrictMode>,
10
+ )
@@ -0,0 +1,63 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ export default {
3
+ darkMode: ["class"],
4
+ content: [
5
+ "./index.html",
6
+ "./src/**/*.{ts,tsx}",
7
+
8
+ // ⚠️ 核心配置:
9
+ // 指向 Monorepo 中的包源码,确保 Tailwind 能提取出 Object UI 组件内的 className
10
+ "../../packages/components/src/**/*.{ts,tsx}",
11
+ "../../packages/react/src/**/*.{ts,tsx}",
12
+ "../../packages/plugin-kanban/src/**/*.{ts,tsx}",
13
+ "../../packages/plugin-charts/src/**/*.{ts,tsx}",
14
+ ],
15
+ theme: {
16
+ extend: {
17
+ // 映射 Shadcn UI 变量
18
+ colors: {
19
+ border: "hsl(var(--border))",
20
+ input: "hsl(var(--input))",
21
+ ring: "hsl(var(--ring))",
22
+ background: "hsl(var(--background))",
23
+ foreground: "hsl(var(--foreground))",
24
+ primary: {
25
+ DEFAULT: "hsl(var(--primary))",
26
+ foreground: "hsl(var(--primary-foreground))",
27
+ },
28
+ secondary: {
29
+ DEFAULT: "hsl(var(--secondary))",
30
+ foreground: "hsl(var(--secondary-foreground))",
31
+ },
32
+ destructive: {
33
+ DEFAULT: "hsl(var(--destructive))",
34
+ foreground: "hsl(var(--destructive-foreground))",
35
+ },
36
+ muted: {
37
+ DEFAULT: "hsl(var(--muted))",
38
+ foreground: "hsl(var(--muted-foreground))",
39
+ },
40
+ accent: {
41
+ DEFAULT: "hsl(var(--accent))",
42
+ foreground: "hsl(var(--accent-foreground))",
43
+ },
44
+ popover: {
45
+ DEFAULT: "hsl(var(--popover))",
46
+ foreground: "hsl(var(--popover-foreground))",
47
+ },
48
+ card: {
49
+ DEFAULT: "hsl(var(--card))",
50
+ foreground: "hsl(var(--card-foreground))",
51
+ },
52
+ },
53
+ borderRadius: {
54
+ lg: "var(--radius)",
55
+ md: "calc(var(--radius) - 2px)",
56
+ sm: "calc(var(--radius) - 4px)",
57
+ },
58
+ },
59
+ },
60
+ plugins: [
61
+ require("tailwindcss-animate"),
62
+ ],
63
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import path from 'path'
4
+
5
+ // https://vitejs.dev/config/
6
+ export default defineConfig({
7
+ plugins: [react()],
8
+ resolve: {
9
+ alias: {
10
+ // ⚡️ DX: Fix Shadcn component imports in the monorepo source
11
+ "@/ui": path.resolve(__dirname, "../../packages/components/src/ui"),
12
+ "@/lib/utils": path.resolve(__dirname, "../../packages/components/src/lib/utils"),
13
+ "@/hooks": path.resolve(__dirname, "../../packages/components/src/hooks"),
14
+
15
+ "@": path.resolve(__dirname, "./src"),
16
+ // ⚡️ DX: App Data Symlink
17
+ "@app": path.resolve(__dirname, "./src/app-data"),
18
+
19
+ // ⚡️ DX: Map imports to source code for Hot Module Replacement
20
+ "@object-ui/components": path.resolve(__dirname, "../../packages/components/src"),
21
+ "@object-ui/react": path.resolve(__dirname, "../../packages/react/src"),
22
+ "@object-ui/core": path.resolve(__dirname, "../../packages/core/src"),
23
+ "@object-ui/types": path.resolve(__dirname, "../../packages/types/src"),
24
+ "@object-ui/data-objectql": path.resolve(__dirname, "../../packages/data-objectql/src"),
25
+ "@object-ui/plugin-kanban": path.resolve(__dirname, "../../packages/plugin-kanban/src"),
26
+ "@object-ui/plugin-charts": path.resolve(__dirname, "../../packages/plugin-charts/src"),
27
+ },
28
+ },
29
+ })