@ramme-io/kernel 1.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.
- package/.turbo/turbo-build.log +5 -0
- package/LICENSE +21 -0
- package/dist/components/AutoForm.d.ts +24 -0
- package/dist/components/AutoForm.d.ts.map +1 -0
- package/dist/components/AutoForm.js +78 -0
- package/dist/components/AutoForm.js.map +1 -0
- package/dist/components/SmartTable.d.ts +14 -0
- package/dist/components/SmartTable.d.ts.map +1 -0
- package/dist/components/SmartTable.js +128 -0
- package/dist/components/SmartTable.js.map +1 -0
- package/dist/config/app.manifest.d.ts +7 -0
- package/dist/config/app.manifest.d.ts.map +1 -0
- package/dist/config/app.manifest.js +8 -0
- package/dist/config/app.manifest.js.map +1 -0
- package/dist/engine/renderers/DynamicBlock.d.ts +9 -0
- package/dist/engine/renderers/DynamicBlock.d.ts.map +1 -0
- package/dist/engine/renderers/DynamicBlock.js +67 -0
- package/dist/engine/renderers/DynamicBlock.js.map +1 -0
- package/dist/engine/renderers/DynamicPage.d.ts +11 -0
- package/dist/engine/renderers/DynamicPage.d.ts.map +1 -0
- package/dist/engine/renderers/DynamicPage.js +91 -0
- package/dist/engine/renderers/DynamicPage.js.map +1 -0
- package/dist/engine/renderers/route-generator.d.ts +16 -0
- package/dist/engine/renderers/route-generator.d.ts.map +1 -0
- package/dist/engine/renderers/route-generator.js +30 -0
- package/dist/engine/renderers/route-generator.js.map +1 -0
- package/dist/engine/renderers/sitemap-entry.d.ts +7 -0
- package/dist/engine/renderers/sitemap-entry.d.ts.map +1 -0
- package/dist/engine/renderers/sitemap-entry.js +2 -0
- package/dist/engine/renderers/sitemap-entry.js.map +1 -0
- package/dist/engine/runtime/ManifestContext.d.ts +115 -0
- package/dist/engine/runtime/ManifestContext.d.ts.map +1 -0
- package/dist/engine/runtime/ManifestContext.js +56 -0
- package/dist/engine/runtime/ManifestContext.js.map +1 -0
- package/dist/engine/runtime/MqttContext.d.ts +14 -0
- package/dist/engine/runtime/MqttContext.d.ts.map +1 -0
- package/dist/engine/runtime/MqttContext.js +70 -0
- package/dist/engine/runtime/MqttContext.js.map +1 -0
- package/dist/engine/runtime/SitemapContext.d.ts +31 -0
- package/dist/engine/runtime/SitemapContext.d.ts.map +1 -0
- package/dist/engine/runtime/SitemapContext.js +54 -0
- package/dist/engine/runtime/SitemapContext.js.map +1 -0
- package/dist/engine/runtime/data-seeder.d.ts +10 -0
- package/dist/engine/runtime/data-seeder.d.ts.map +1 -0
- package/dist/engine/runtime/data-seeder.js +35 -0
- package/dist/engine/runtime/data-seeder.js.map +1 -0
- package/dist/engine/runtime/useAction.d.ts +4 -0
- package/dist/engine/runtime/useAction.d.ts.map +1 -0
- package/dist/engine/runtime/useAction.js +55 -0
- package/dist/engine/runtime/useAction.js.map +1 -0
- package/dist/engine/runtime/useCrudLocalStorage.d.ts +19 -0
- package/dist/engine/runtime/useCrudLocalStorage.d.ts.map +1 -0
- package/dist/engine/runtime/useCrudLocalStorage.js +73 -0
- package/dist/engine/runtime/useCrudLocalStorage.js.map +1 -0
- package/dist/engine/runtime/useDataQuery.d.ts +39 -0
- package/dist/engine/runtime/useDataQuery.d.ts.map +1 -0
- package/dist/engine/runtime/useDataQuery.js +50 -0
- package/dist/engine/runtime/useDataQuery.js.map +1 -0
- package/dist/engine/runtime/useDynamicSitemap.d.ts +9 -0
- package/dist/engine/runtime/useDynamicSitemap.d.ts.map +1 -0
- package/dist/engine/runtime/useDynamicSitemap.js +38 -0
- package/dist/engine/runtime/useDynamicSitemap.js.map +1 -0
- package/dist/engine/runtime/useJustInTimeSeeder.d.ts +11 -0
- package/dist/engine/runtime/useJustInTimeSeeder.d.ts.map +1 -0
- package/dist/engine/runtime/useJustInTimeSeeder.js +88 -0
- package/dist/engine/runtime/useJustInTimeSeeder.js.map +1 -0
- package/dist/engine/runtime/useLiveBridge.d.ts +109 -0
- package/dist/engine/runtime/useLiveBridge.d.ts.map +1 -0
- package/dist/engine/runtime/useLiveBridge.js +21 -0
- package/dist/engine/runtime/useLiveBridge.js.map +1 -0
- package/dist/engine/runtime/useSignal.d.ts +11 -0
- package/dist/engine/runtime/useSignal.d.ts.map +1 -0
- package/dist/engine/runtime/useSignal.js +26 -0
- package/dist/engine/runtime/useSignal.js.map +1 -0
- package/dist/engine/runtime/useSignalStore.d.ts +31 -0
- package/dist/engine/runtime/useSignalStore.d.ts.map +1 -0
- package/dist/engine/runtime/useSignalStore.js +60 -0
- package/dist/engine/runtime/useSignalStore.js.map +1 -0
- package/dist/engine/runtime/useWorkflowEngine.d.ts +4 -0
- package/dist/engine/runtime/useWorkflowEngine.d.ts.map +1 -0
- package/dist/engine/runtime/useWorkflowEngine.js +85 -0
- package/dist/engine/runtime/useWorkflowEngine.js.map +1 -0
- package/dist/engine/types/manifest-types.d.ts +38 -0
- package/dist/engine/types/manifest-types.d.ts.map +1 -0
- package/dist/engine/types/manifest-types.js +5 -0
- package/dist/engine/types/manifest-types.js.map +1 -0
- package/dist/engine/types/sitemap-entry.d.ts +58 -0
- package/dist/engine/types/sitemap-entry.d.ts.map +1 -0
- package/dist/engine/types/sitemap-entry.js +19 -0
- package/dist/engine/types/sitemap-entry.js.map +1 -0
- package/dist/engine/validation/schema.d.ts +383 -0
- package/dist/engine/validation/schema.d.ts.map +1 -0
- package/dist/engine/validation/schema.js +156 -0
- package/dist/engine/validation/schema.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/package.json +36 -0
- package/src/components/AutoForm.tsx +141 -0
- package/src/components/SmartTable.tsx +316 -0
- package/src/config/app.manifest.ts +7 -0
- package/src/engine/renderers/DynamicBlock.tsx +84 -0
- package/src/engine/renderers/DynamicPage.tsx +196 -0
- package/src/engine/renderers/route-generator.tsx +47 -0
- package/src/engine/renderers/sitemap-entry.ts +6 -0
- package/src/engine/runtime/ManifestContext.tsx +81 -0
- package/src/engine/runtime/MqttContext.tsx +94 -0
- package/src/engine/runtime/SitemapContext.tsx +61 -0
- package/src/engine/runtime/data-seeder.ts +39 -0
- package/src/engine/runtime/useAction.ts +64 -0
- package/src/engine/runtime/useCrudLocalStorage.ts +82 -0
- package/src/engine/runtime/useDataQuery.ts +98 -0
- package/src/engine/runtime/useDynamicSitemap.tsx +43 -0
- package/src/engine/runtime/useJustInTimeSeeder.ts +101 -0
- package/src/engine/runtime/useLiveBridge.ts +24 -0
- package/src/engine/runtime/useSignal.ts +40 -0
- package/src/engine/runtime/useSignalStore.ts +94 -0
- package/src/engine/runtime/useWorkflowEngine.ts +89 -0
- package/src/engine/types/manifest-types.ts +45 -0
- package/src/engine/types/sitemap-entry.ts +66 -0
- package/src/engine/validation/schema.ts +189 -0
- package/src/index.ts +27 -0
- package/tsconfig.json +28 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { useSignalStore } from './useSignalStore';
|
|
3
|
+
// ❌ REMOVED: import { appManifest } from '../../config/app.manifest';
|
|
4
|
+
// ✅ ADDED: Live Context
|
|
5
|
+
import { useManifest } from './ManifestContext';
|
|
6
|
+
|
|
7
|
+
export interface SignalState {
|
|
8
|
+
id: string;
|
|
9
|
+
value: any;
|
|
10
|
+
unit?: string;
|
|
11
|
+
min?: number;
|
|
12
|
+
max?: number;
|
|
13
|
+
timestamp?: number;
|
|
14
|
+
status: 'fresh' | 'stale';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const useSignal = (signalId: string): SignalState => {
|
|
18
|
+
const signalData = useSignalStore((state) => state.signals[signalId]);
|
|
19
|
+
|
|
20
|
+
// ✅ 1. Consume Live Manifest
|
|
21
|
+
const appManifest = useManifest();
|
|
22
|
+
|
|
23
|
+
// 2. Get Static Definition from Manifest (Live)
|
|
24
|
+
const signalDef = useMemo(() => {
|
|
25
|
+
return appManifest.domain.signals.find((s) => s.id === signalId);
|
|
26
|
+
}, [signalId, appManifest]); // Re-run if manifest updates
|
|
27
|
+
|
|
28
|
+
const value = signalData?.value ?? signalDef?.defaultValue ?? 0;
|
|
29
|
+
const isStale = signalData ? (Date.now() - signalData.timestamp > 10000) : true;
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
id: signalId,
|
|
33
|
+
value: value,
|
|
34
|
+
unit: signalDef?.unit,
|
|
35
|
+
min: signalDef?.min,
|
|
36
|
+
max: signalDef?.max,
|
|
37
|
+
timestamp: signalData?.timestamp,
|
|
38
|
+
status: isStale ? 'stale' : 'fresh'
|
|
39
|
+
};
|
|
40
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @file useSignalStore.ts
|
|
6
|
+
* @description The "Short-Term Memory" of the application.
|
|
7
|
+
*
|
|
8
|
+
* ARCHITECTURAL ROLE:
|
|
9
|
+
* This Zustand store holds the instantaneous value of every Signal (IoT sensor).
|
|
10
|
+
* It acts as the buffer between the high-speed data stream (MQTT/Simulation)
|
|
11
|
+
* and the UI components.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// --- 1. SIGNAL STORE TYPES ---
|
|
15
|
+
export interface SignalValue {
|
|
16
|
+
value: any;
|
|
17
|
+
timestamp: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface SignalStore {
|
|
21
|
+
signals: Record<string, SignalValue>;
|
|
22
|
+
// Single update (used by UI controls)
|
|
23
|
+
updateSignal: (id: string, value: any) => void;
|
|
24
|
+
// Batch update (used by MQTT/Simulation)
|
|
25
|
+
updateSignals: (updates: Record<string, any>) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Initial State matches your dashboard IDs to prevent "undefined" errors on load
|
|
29
|
+
const initialState = {
|
|
30
|
+
living_room_ac: { value: 72, timestamp: Date.now() },
|
|
31
|
+
living_room_hum: { value: 45, timestamp: Date.now() },
|
|
32
|
+
server_01: { value: 42, timestamp: Date.now() },
|
|
33
|
+
front_door_lock: { value: 'LOCKED', timestamp: Date.now() }
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const useSignalStore = create<SignalStore>((set) => ({
|
|
37
|
+
signals: initialState,
|
|
38
|
+
|
|
39
|
+
updateSignal: (id, value) => set((state) => ({
|
|
40
|
+
signals: {
|
|
41
|
+
...state.signals,
|
|
42
|
+
[id]: { value, timestamp: Date.now() }
|
|
43
|
+
}
|
|
44
|
+
})),
|
|
45
|
+
|
|
46
|
+
updateSignals: (updates) => set((state) => {
|
|
47
|
+
const newSignals = { ...state.signals };
|
|
48
|
+
Object.entries(updates).forEach(([id, val]) => {
|
|
49
|
+
newSignals[id] = { value: val, timestamp: Date.now() };
|
|
50
|
+
});
|
|
51
|
+
return { signals: newSignals };
|
|
52
|
+
})
|
|
53
|
+
}));
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Hook to access specific signals in a component.
|
|
57
|
+
* Usage: const signals = useGeneratedSignals();
|
|
58
|
+
*/
|
|
59
|
+
export const useGeneratedSignals = () => {
|
|
60
|
+
return useSignalStore((state) => state.signals);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// --- 2. SIMULATION ENGINE (The Missing Piece) ---
|
|
64
|
+
/**
|
|
65
|
+
* A hook that generates fake data for testing when no MQTT broker is connected.
|
|
66
|
+
* You can toggle this on/off via the manifest config.
|
|
67
|
+
*/
|
|
68
|
+
export const useSimulation = (isEnabled: boolean = true) => {
|
|
69
|
+
const { updateSignals } = useSignalStore();
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (!isEnabled) return;
|
|
73
|
+
|
|
74
|
+
console.log("[System] Simulation Mode: ON 🎲");
|
|
75
|
+
const interval = setInterval(() => {
|
|
76
|
+
// Simulate random fluctuations
|
|
77
|
+
const updates: Record<string, any> = {};
|
|
78
|
+
|
|
79
|
+
// Randomize values slightly around a baseline
|
|
80
|
+
updates['living_room_ac'] = Number((72 + (Math.random() * 4 - 2)).toFixed(1));
|
|
81
|
+
updates['living_room_hum'] = Number((45 + (Math.random() * 6 - 3)).toFixed(1));
|
|
82
|
+
updates['server_01'] = Math.floor(Math.random() * 100);
|
|
83
|
+
|
|
84
|
+
// Randomly flip the lock status occasionally (1% chance)
|
|
85
|
+
if (Math.random() > 0.99) {
|
|
86
|
+
updates['front_door_lock'] = Math.random() > 0.5 ? 'LOCKED' : 'UNLOCKED';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
updateSignals(updates);
|
|
90
|
+
}, 2000); // Update every 2 seconds
|
|
91
|
+
|
|
92
|
+
return () => clearInterval(interval);
|
|
93
|
+
}, [isEnabled, updateSignals]);
|
|
94
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useToast } from '@ramme-io/ui';
|
|
3
|
+
import { useGeneratedSignals, useSimulation } from './useSignalStore';
|
|
4
|
+
// ❌ REMOVED: import { appManifest } from '../../config/app.manifest';
|
|
5
|
+
// ✅ ADDED: Live Context
|
|
6
|
+
import { useManifest } from './ManifestContext';
|
|
7
|
+
|
|
8
|
+
// ... (Interfaces can remain the same) ...
|
|
9
|
+
interface ActionDefinition { type: string; config: Record<string, any>; }
|
|
10
|
+
interface WorkflowDefinition { id: string; name: string; active: boolean; trigger: { type: string; config: { signalId: string; condition: string; }; }; actions: ActionDefinition[]; }
|
|
11
|
+
|
|
12
|
+
export const useWorkflowEngine = () => {
|
|
13
|
+
const signals = useGeneratedSignals();
|
|
14
|
+
const { addToast } = useToast();
|
|
15
|
+
|
|
16
|
+
// ✅ 1. Consume Live Manifest
|
|
17
|
+
const appManifest = useManifest();
|
|
18
|
+
|
|
19
|
+
// 2. Activate Simulation (Responsive to toggle)
|
|
20
|
+
useSimulation(appManifest.config.mockMode);
|
|
21
|
+
|
|
22
|
+
const executeAction = async (action: ActionDefinition, context: any) => {
|
|
23
|
+
// ... (Same execution logic as before) ...
|
|
24
|
+
console.log(`[Engine] Executing: ${action.type}`, action);
|
|
25
|
+
switch (action.type) {
|
|
26
|
+
case 'send_notification': addToast(action.config.message || 'Notification Sent', 'info'); break;
|
|
27
|
+
case 'update_resource': addToast(`Updating Resource: ${JSON.stringify(action.config)}`, 'success'); break;
|
|
28
|
+
case 'navigate': window.location.href = action.config.path; break;
|
|
29
|
+
case 'agent_task':
|
|
30
|
+
addToast('AI Agent Analyzing...', 'info');
|
|
31
|
+
setTimeout(() => addToast('🤖 Agent: "System Nominal"', 'success', 3000), 1500);
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// 3. Watch Signals & Trigger Workflows (Live)
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (!appManifest.domain?.workflows) return;
|
|
39
|
+
|
|
40
|
+
(appManifest.domain.workflows as unknown as WorkflowDefinition[]).forEach((flow) => {
|
|
41
|
+
if (!flow.active) return;
|
|
42
|
+
|
|
43
|
+
if (flow.trigger.type === 'signal_change') {
|
|
44
|
+
const signalId = flow.trigger.config.signalId;
|
|
45
|
+
const condition = flow.trigger.config.condition;
|
|
46
|
+
|
|
47
|
+
// @ts-ignore
|
|
48
|
+
const signal = signals[signalId];
|
|
49
|
+
|
|
50
|
+
if (signal) {
|
|
51
|
+
const val = signal.value;
|
|
52
|
+
try {
|
|
53
|
+
const isMet = checkCondition(val, condition);
|
|
54
|
+
if (isMet) {
|
|
55
|
+
console.log(`[Engine] Trigger Fired: ${flow.name}`);
|
|
56
|
+
flow.actions.forEach(action => executeAction(action, { signal: val }));
|
|
57
|
+
}
|
|
58
|
+
} catch (e) {}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}, [signals, addToast, appManifest.domain.workflows]); // Added dependency
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
triggerWorkflow: (workflowId: string) => {
|
|
66
|
+
// ✅ 4. Manual Triggers now find new workflows instantly
|
|
67
|
+
const flow = appManifest.domain?.workflows?.find(w => w.id === workflowId);
|
|
68
|
+
if (flow) {
|
|
69
|
+
// @ts-ignore
|
|
70
|
+
flow.actions.forEach(action => executeAction(action, { manual: true }));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const checkCondition = (value: number, condition: string): boolean => {
|
|
77
|
+
if (!condition) return false;
|
|
78
|
+
const parts = condition.trim().split(' ');
|
|
79
|
+
const operator = parts[0];
|
|
80
|
+
const target = parseFloat(parts[1]);
|
|
81
|
+
switch (operator) {
|
|
82
|
+
case '>': return value > target;
|
|
83
|
+
case '<': return value < target;
|
|
84
|
+
case '>=': return value >= target;
|
|
85
|
+
case '<=': return value <= target;
|
|
86
|
+
case '==': return value === target;
|
|
87
|
+
default: return false;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// src/core/manifest-types.ts
|
|
2
|
+
|
|
3
|
+
import { type IconName } from '@ramme-io/ui';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Defines a simple navigation link structure used in menus.
|
|
7
|
+
*/
|
|
8
|
+
export interface ManifestLink {
|
|
9
|
+
id: string;
|
|
10
|
+
title: string;
|
|
11
|
+
path: string;
|
|
12
|
+
icon?: IconName;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The fundamental unit of the UI.
|
|
17
|
+
* Corresponds to a specific React component in the registry.
|
|
18
|
+
*/
|
|
19
|
+
export interface Block {
|
|
20
|
+
id: string;
|
|
21
|
+
type: string; // e.g., 'DeviceCard', 'StatCard', 'LineChart'
|
|
22
|
+
props: Record<string, any>; // Static props (title, default values)
|
|
23
|
+
layout?: {
|
|
24
|
+
colSpan?: number;
|
|
25
|
+
rowSpan?: number;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A horizontal container that groups blocks together.
|
|
31
|
+
* Maps to a CSS Grid or Flex container.
|
|
32
|
+
*/
|
|
33
|
+
export interface PageSection {
|
|
34
|
+
id: string;
|
|
35
|
+
title?: string;
|
|
36
|
+
description?: string;
|
|
37
|
+
layout?: {
|
|
38
|
+
columns?: number; // e.g., 3 (for a 3-column grid)
|
|
39
|
+
variant?: 'grid' | 'stack';
|
|
40
|
+
};
|
|
41
|
+
blocks: Block[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Note: PageDefinition and AppSpecification are exported from schema.ts
|
|
45
|
+
// These types are kept here for backward compatibility but should use schema.ts types instead
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file sitemap-entry.ts
|
|
3
|
+
* @repository ramme-app-starter
|
|
4
|
+
* @description This file defines the canonical TypeScript interface for a single
|
|
5
|
+
* sitemap entry. It is the "schema" or "contract" that governs the entire
|
|
6
|
+
* Sitemap-Driven Architecture.
|
|
7
|
+
*
|
|
8
|
+
* STRATEGIC IMPORTANCE:
|
|
9
|
+
* 1. Designer DX: It provides strict type-safety and autocompletion for any
|
|
10
|
+
* designer editing a `sitemap.ts` file, dramatically lowering the risk of errors.
|
|
11
|
+
* 2. Repo-Linking: It directly imports `IconName` from `@ramme-io/ui`, creating
|
|
12
|
+
* a type-safe link between our starter kit and our component library.
|
|
13
|
+
* 3. AI-Ready: This interface is the precise data structure that the AI Assistant
|
|
14
|
+
* in the (private) `ramme-app-builder` will be trained to generate.
|
|
15
|
+
* The "Architect" phase of the A.D.A.P.T. Framework will result in the
|
|
16
|
+
* AI creating an array of these objects.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// Import the type definition for all available icon names from our open-source
|
|
20
|
+
// UI library. This is a critical integration point.
|
|
21
|
+
import { type IconName } from '@ramme-io/ui';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Defines the required shape for a single entry in any `sitemap.ts` file.
|
|
25
|
+
*/
|
|
26
|
+
export interface SitemapEntry {
|
|
27
|
+
/**
|
|
28
|
+
* A unique string identifier.
|
|
29
|
+
* Used by React as the `key` prop during route generation.
|
|
30
|
+
* e.g., 'dashboard.home'
|
|
31
|
+
*/
|
|
32
|
+
id: string;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The URL path segment for this route.
|
|
36
|
+
* e.g., 'home', 'users', 'settings'
|
|
37
|
+
*/
|
|
38
|
+
path: string;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The human-readable name for the route.
|
|
42
|
+
* Used to populate navigation links, breadcrumbs, and page titles.
|
|
43
|
+
* e.g., 'Home', 'User Management'
|
|
44
|
+
*/
|
|
45
|
+
title: string;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* (Optional) The name of the icon to display in the navigation.
|
|
49
|
+
* This MUST be a valid `IconName` from the `@ramme-io/ui` library.
|
|
50
|
+
* e.g., 'home', 'users', 'settings'
|
|
51
|
+
*/
|
|
52
|
+
icon?: IconName;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The actual React component to render when this route is active.
|
|
56
|
+
* e.g., DashboardPage, SettingsPage
|
|
57
|
+
*/
|
|
58
|
+
component: React.ComponentType;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* (Optional) An array of nested `SitemapEntry` objects.
|
|
62
|
+
* This is the property that enables recursive, nested navigation
|
|
63
|
+
* (e.g., /settings/profile, /settings/billing).
|
|
64
|
+
*/
|
|
65
|
+
children?: SitemapEntry[];
|
|
66
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file schema.ts
|
|
5
|
+
* @description The "Application Constitution".
|
|
6
|
+
*
|
|
7
|
+
* ARCHITECTURAL ROLE:
|
|
8
|
+
* This file uses Zod to define the strict runtime validation rules for the
|
|
9
|
+
* App Manifest. While `manifest-types.ts` handles compile-time TypeScript checks,
|
|
10
|
+
* this file handles runtime validation, ensuring that any JSON loaded into the
|
|
11
|
+
* engine (whether from a file, API, or user input) is structurally sound.
|
|
12
|
+
*
|
|
13
|
+
* LAYERS DEFINED:
|
|
14
|
+
* 1. **SaaS Layer:** Data resources, fields, and tables.
|
|
15
|
+
* 2. **Physical Layer:** IoT signals, sensors, and entities.
|
|
16
|
+
* 3. **Logic Layer:** Workflows, triggers, and automated actions.
|
|
17
|
+
* 4. **Presentation Layer:** Pages, sections, and UI blocks.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
// ------------------------------------------------------------------
|
|
21
|
+
// 1. DATA RESOURCE DEFINITIONS (SaaS Layer)
|
|
22
|
+
// ------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
export const FieldSchema = z.object({
|
|
25
|
+
key: z.string(),
|
|
26
|
+
label: z.string(),
|
|
27
|
+
type: z.enum(['text', 'number', 'currency', 'date', 'boolean', 'status', 'email', 'image', 'textarea']),
|
|
28
|
+
required: z.boolean().optional(),
|
|
29
|
+
description: z.string().optional(),
|
|
30
|
+
defaultValue: z.any().optional(),
|
|
31
|
+
});
|
|
32
|
+
export type FieldDefinition = z.infer<typeof FieldSchema>;
|
|
33
|
+
|
|
34
|
+
export const ResourceSchema = z.object({
|
|
35
|
+
id: z.string(),
|
|
36
|
+
name: z.string(),
|
|
37
|
+
fields: z.array(FieldSchema),
|
|
38
|
+
defaultView: z.enum(['table', 'grid', 'list']).optional(),
|
|
39
|
+
features: z.object({
|
|
40
|
+
searchable: z.boolean().optional(),
|
|
41
|
+
creatable: z.boolean().optional(),
|
|
42
|
+
editable: z.boolean().optional(),
|
|
43
|
+
deletable: z.boolean().optional(),
|
|
44
|
+
exportable: z.boolean().optional(),
|
|
45
|
+
}).optional(),
|
|
46
|
+
});
|
|
47
|
+
export type ResourceDefinition = z.infer<typeof ResourceSchema>;
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
// ------------------------------------------------------------------
|
|
51
|
+
// 2. SIGNAL & IOT DEFINITIONS (Physical Layer)
|
|
52
|
+
// ------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
export const SignalSchema = z.object({
|
|
55
|
+
id: z.string().min(1, "Signal ID is required"),
|
|
56
|
+
label: z.string(),
|
|
57
|
+
description: z.string().optional(),
|
|
58
|
+
kind: z.enum(['sensor', 'actuator', 'setpoint', 'metric', 'status', 'kpi']),
|
|
59
|
+
source: z.enum(['mock', 'mqtt', 'http', 'derived', 'local']),
|
|
60
|
+
|
|
61
|
+
// Connectivity
|
|
62
|
+
topic: z.string().optional(),
|
|
63
|
+
endpoint: z.string().optional(),
|
|
64
|
+
jsonPath: z.string().optional(),
|
|
65
|
+
refreshRate: z.number().optional().default(1000),
|
|
66
|
+
|
|
67
|
+
// Values
|
|
68
|
+
defaultValue: z.any().optional(),
|
|
69
|
+
unit: z.string().optional(),
|
|
70
|
+
min: z.number().optional(),
|
|
71
|
+
max: z.number().optional(),
|
|
72
|
+
});
|
|
73
|
+
export type SignalDefinition = z.infer<typeof SignalSchema>;
|
|
74
|
+
|
|
75
|
+
export const EntitySchema = z.object({
|
|
76
|
+
id: z.string(),
|
|
77
|
+
name: z.string(),
|
|
78
|
+
description: z.string().optional(),
|
|
79
|
+
type: z.string(),
|
|
80
|
+
category: z.string().default('logical'),
|
|
81
|
+
signals: z.array(z.string()),
|
|
82
|
+
ui: z.object({
|
|
83
|
+
icon: z.string().optional(),
|
|
84
|
+
color: z.string().optional(),
|
|
85
|
+
dashboardComponent: z.string().optional(),
|
|
86
|
+
}).optional(),
|
|
87
|
+
});
|
|
88
|
+
export type EntityDefinition = z.infer<typeof EntitySchema>;
|
|
89
|
+
|
|
90
|
+
// ------------------------------------------------------------------
|
|
91
|
+
// ✅ NEW: LOGIC & WORKFLOW DEFINITIONS
|
|
92
|
+
// ------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
export const TriggerSchema = z.object({
|
|
95
|
+
id: z.string(),
|
|
96
|
+
type: z.enum(['signal_change', 'manual_action', 'schedule', 'webhook']),
|
|
97
|
+
config: z.record(z.string(), z.any()),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
export const ActionSchema = z.object({
|
|
101
|
+
id: z.string(),
|
|
102
|
+
type: z.enum([
|
|
103
|
+
'update_resource',
|
|
104
|
+
'send_notification',
|
|
105
|
+
'mqtt_publish',
|
|
106
|
+
'api_call',
|
|
107
|
+
'navigate',
|
|
108
|
+
'agent_task'
|
|
109
|
+
]),
|
|
110
|
+
config: z.record(z.string(), z.any()),
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
export const WorkflowSchema = z.object({
|
|
114
|
+
id: z.string(),
|
|
115
|
+
name: z.string(),
|
|
116
|
+
active: z.boolean().default(true),
|
|
117
|
+
trigger: TriggerSchema,
|
|
118
|
+
actions: z.array(ActionSchema),
|
|
119
|
+
});
|
|
120
|
+
export type WorkflowDefinition = z.infer<typeof WorkflowSchema>;
|
|
121
|
+
export type ActionDefinition = z.infer<typeof ActionSchema>;
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
// ------------------------------------------------------------------
|
|
125
|
+
// 3. UI LAYOUT DEFINITIONS (Presentation Layer)
|
|
126
|
+
// ------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
export const BlockSchema = z.object({
|
|
129
|
+
id: z.string(),
|
|
130
|
+
type: z.string(),
|
|
131
|
+
props: z.record(z.string(), z.any()),
|
|
132
|
+
layout: z.object({
|
|
133
|
+
colSpan: z.number().optional(),
|
|
134
|
+
rowSpan: z.number().optional(),
|
|
135
|
+
}).optional(),
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
export const PageSectionSchema = z.object({
|
|
139
|
+
id: z.string(),
|
|
140
|
+
title: z.string().optional(),
|
|
141
|
+
description: z.string().optional(),
|
|
142
|
+
layout: z.object({
|
|
143
|
+
columns: z.number().optional(),
|
|
144
|
+
variant: z.enum(['grid', 'stack', 'split']).optional()
|
|
145
|
+
}).optional(),
|
|
146
|
+
blocks: z.array(BlockSchema),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
export const PageSchema = z.object({
|
|
150
|
+
id: z.string(),
|
|
151
|
+
slug: z.string(),
|
|
152
|
+
title: z.string(),
|
|
153
|
+
description: z.string().optional(),
|
|
154
|
+
icon: z.string().optional(),
|
|
155
|
+
sections: z.array(PageSectionSchema),
|
|
156
|
+
});
|
|
157
|
+
export type PageDefinition = z.infer<typeof PageSchema>;
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
// MASTER APP SPECIFICATION
|
|
161
|
+
export const AppSpecificationSchema = z.object({
|
|
162
|
+
meta: z.object({
|
|
163
|
+
name: z.string(),
|
|
164
|
+
version: z.string(),
|
|
165
|
+
description: z.string().optional(),
|
|
166
|
+
author: z.string().optional(),
|
|
167
|
+
createdAt: z.string().optional(),
|
|
168
|
+
}),
|
|
169
|
+
|
|
170
|
+
config: z.object({
|
|
171
|
+
theme: z.enum(['light', 'dark', 'system', 'corporate', 'midnight', 'blueprint']).default('system'),
|
|
172
|
+
mockMode: z.boolean().default(true),
|
|
173
|
+
brokerUrl: z.string().optional(),
|
|
174
|
+
}),
|
|
175
|
+
|
|
176
|
+
modules: z.array(z.string()).optional(),
|
|
177
|
+
resources: z.array(ResourceSchema).optional(),
|
|
178
|
+
|
|
179
|
+
domain: z.object({
|
|
180
|
+
signals: z.array(SignalSchema),
|
|
181
|
+
entities: z.array(EntitySchema),
|
|
182
|
+
// ✅ ADDED WORKFLOWS HERE
|
|
183
|
+
workflows: z.array(WorkflowSchema).optional(),
|
|
184
|
+
}),
|
|
185
|
+
|
|
186
|
+
pages: z.array(PageSchema).optional(),
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
export type AppSpecification = z.infer<typeof AppSpecificationSchema>;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Engine exports
|
|
2
|
+
export * from './engine/runtime/ManifestContext';
|
|
3
|
+
export * from './engine/runtime/MqttContext';
|
|
4
|
+
export * from './engine/runtime/SitemapContext';
|
|
5
|
+
export * from './engine/runtime/useAction';
|
|
6
|
+
export * from './engine/runtime/useCrudLocalStorage';
|
|
7
|
+
export * from './engine/runtime/useDataQuery';
|
|
8
|
+
export * from './engine/runtime/useDynamicSitemap';
|
|
9
|
+
export * from './engine/runtime/useJustInTimeSeeder';
|
|
10
|
+
export * from './engine/runtime/useLiveBridge';
|
|
11
|
+
export * from './engine/runtime/useSignal';
|
|
12
|
+
export * from './engine/runtime/useSignalStore';
|
|
13
|
+
export * from './engine/runtime/useWorkflowEngine';
|
|
14
|
+
export * from './engine/runtime/data-seeder';
|
|
15
|
+
|
|
16
|
+
export * from './engine/renderers/DynamicBlock';
|
|
17
|
+
export * from './engine/renderers/DynamicPage';
|
|
18
|
+
export * from './engine/renderers/route-generator';
|
|
19
|
+
|
|
20
|
+
export * from './engine/types/manifest-types';
|
|
21
|
+
export * from './engine/types/sitemap-entry';
|
|
22
|
+
|
|
23
|
+
export * from './engine/validation/schema';
|
|
24
|
+
|
|
25
|
+
// Component exports
|
|
26
|
+
export { SmartTable } from './components/SmartTable';
|
|
27
|
+
export { AutoForm } from './components/AutoForm';
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
/* React Configuration (CRITICAL) */
|
|
4
|
+
"jsx": "react-jsx",
|
|
5
|
+
"types": ["node", "react", "react-dom"],
|
|
6
|
+
|
|
7
|
+
/* Base Options */
|
|
8
|
+
"target": "ES2020",
|
|
9
|
+
"module": "ESNext",
|
|
10
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
|
|
13
|
+
/* Module Resolution */
|
|
14
|
+
"moduleResolution": "bundler",
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"isolatedModules": true,
|
|
17
|
+
"esModuleInterop": true,
|
|
18
|
+
|
|
19
|
+
/* Build Output */
|
|
20
|
+
"outDir": "dist",
|
|
21
|
+
"rootDir": "src",
|
|
22
|
+
"declaration": true,
|
|
23
|
+
"declarationMap": true,
|
|
24
|
+
"sourceMap": true,
|
|
25
|
+
"noEmit": false
|
|
26
|
+
},
|
|
27
|
+
"include": ["src"]
|
|
28
|
+
}
|