@marifold/core 0.60.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/LICENSE +674 -0
- package/dist/agent/AgentEvents.d.ts +106 -0
- package/dist/agent/AgentEvents.d.ts.map +1 -0
- package/dist/agent/AgentEvents.js +3 -0
- package/dist/agent/AgentEvents.js.map +1 -0
- package/dist/agent/AgentHistory.d.ts +27 -0
- package/dist/agent/AgentHistory.d.ts.map +1 -0
- package/dist/agent/AgentHistory.js +42 -0
- package/dist/agent/AgentHistory.js.map +1 -0
- package/dist/agent/AgentRunner.d.ts +149 -0
- package/dist/agent/AgentRunner.d.ts.map +1 -0
- package/dist/agent/AgentRunner.js +727 -0
- package/dist/agent/AgentRunner.js.map +1 -0
- package/dist/agent/ApprovalPolicy.d.ts +71 -0
- package/dist/agent/ApprovalPolicy.d.ts.map +1 -0
- package/dist/agent/ApprovalPolicy.js +29 -0
- package/dist/agent/ApprovalPolicy.js.map +1 -0
- package/dist/agent/ControlBlockTools.d.ts +9 -0
- package/dist/agent/ControlBlockTools.d.ts.map +1 -0
- package/dist/agent/ControlBlockTools.js +49 -0
- package/dist/agent/ControlBlockTools.js.map +1 -0
- package/dist/agent/RunWorkspace.d.ts +92 -0
- package/dist/agent/RunWorkspace.d.ts.map +1 -0
- package/dist/agent/RunWorkspace.js +328 -0
- package/dist/agent/RunWorkspace.js.map +1 -0
- package/dist/agent/ScopedProcess.d.ts +35 -0
- package/dist/agent/ScopedProcess.d.ts.map +1 -0
- package/dist/agent/ScopedProcess.js +280 -0
- package/dist/agent/ScopedProcess.js.map +1 -0
- package/dist/agent/ToolRegistry.d.ts +80 -0
- package/dist/agent/ToolRegistry.d.ts.map +1 -0
- package/dist/agent/ToolRegistry.js +45 -0
- package/dist/agent/ToolRegistry.js.map +1 -0
- package/dist/agent/UserInput.d.ts +59 -0
- package/dist/agent/UserInput.d.ts.map +1 -0
- package/dist/agent/UserInput.js +203 -0
- package/dist/agent/UserInput.js.map +1 -0
- package/dist/agent/tools/AskUserTool.d.ts +71 -0
- package/dist/agent/tools/AskUserTool.d.ts.map +1 -0
- package/dist/agent/tools/AskUserTool.js +70 -0
- package/dist/agent/tools/AskUserTool.js.map +1 -0
- package/dist/agent/tools/DelegateTool.d.ts +49 -0
- package/dist/agent/tools/DelegateTool.d.ts.map +1 -0
- package/dist/agent/tools/DelegateTool.js +62 -0
- package/dist/agent/tools/DelegateTool.js.map +1 -0
- package/dist/agent/tools/InspectAttachmentTool.d.ts +25 -0
- package/dist/agent/tools/InspectAttachmentTool.d.ts.map +1 -0
- package/dist/agent/tools/InspectAttachmentTool.js +129 -0
- package/dist/agent/tools/InspectAttachmentTool.js.map +1 -0
- package/dist/agent/tools/PythonPackageTool.d.ts +26 -0
- package/dist/agent/tools/PythonPackageTool.d.ts.map +1 -0
- package/dist/agent/tools/PythonPackageTool.js +105 -0
- package/dist/agent/tools/PythonPackageTool.js.map +1 -0
- package/dist/agent/tools/ReadFileTool.d.ts +24 -0
- package/dist/agent/tools/ReadFileTool.d.ts.map +1 -0
- package/dist/agent/tools/ReadFileTool.js +112 -0
- package/dist/agent/tools/ReadFileTool.js.map +1 -0
- package/dist/agent/tools/ShellExecTool.d.ts +23 -0
- package/dist/agent/tools/ShellExecTool.d.ts.map +1 -0
- package/dist/agent/tools/ShellExecTool.js +75 -0
- package/dist/agent/tools/ShellExecTool.js.map +1 -0
- package/dist/agent/tools/WebSearchTool.d.ts +26 -0
- package/dist/agent/tools/WebSearchTool.d.ts.map +1 -0
- package/dist/agent/tools/WebSearchTool.js +52 -0
- package/dist/agent/tools/WebSearchTool.js.map +1 -0
- package/dist/agent/tools/WriteFileTool.d.ts +30 -0
- package/dist/agent/tools/WriteFileTool.d.ts.map +1 -0
- package/dist/agent/tools/WriteFileTool.js +155 -0
- package/dist/agent/tools/WriteFileTool.js.map +1 -0
- package/dist/app/AppActionResolver.d.ts +15 -0
- package/dist/app/AppActionResolver.d.ts.map +1 -0
- package/dist/app/AppActionResolver.js +98 -0
- package/dist/app/AppActionResolver.js.map +1 -0
- package/dist/app/AppSchema.d.ts +83 -0
- package/dist/app/AppSchema.d.ts.map +1 -0
- package/dist/app/AppSchema.js +17 -0
- package/dist/app/AppSchema.js.map +1 -0
- package/dist/app/AppStore.d.ts +14 -0
- package/dist/app/AppStore.d.ts.map +1 -0
- package/dist/app/AppStore.js +146 -0
- package/dist/app/AppStore.js.map +1 -0
- package/dist/app/AppValidator.d.ts +10 -0
- package/dist/app/AppValidator.d.ts.map +1 -0
- package/dist/app/AppValidator.js +503 -0
- package/dist/app/AppValidator.js.map +1 -0
- package/dist/app/SkillAppCompiler.d.ts +5 -0
- package/dist/app/SkillAppCompiler.d.ts.map +1 -0
- package/dist/app/SkillAppCompiler.js +632 -0
- package/dist/app/SkillAppCompiler.js.map +1 -0
- package/dist/app/SkillAppDsl.d.ts +93 -0
- package/dist/app/SkillAppDsl.d.ts.map +1 -0
- package/dist/app/SkillAppDsl.js +72 -0
- package/dist/app/SkillAppDsl.js.map +1 -0
- package/dist/app/SkillAppInstanceRegistry.d.ts +25 -0
- package/dist/app/SkillAppInstanceRegistry.d.ts.map +1 -0
- package/dist/app/SkillAppInstanceRegistry.js +218 -0
- package/dist/app/SkillAppInstanceRegistry.js.map +1 -0
- package/dist/app/SkillAppResolver.d.ts +12 -0
- package/dist/app/SkillAppResolver.d.ts.map +1 -0
- package/dist/app/SkillAppResolver.js +89 -0
- package/dist/app/SkillAppResolver.js.map +1 -0
- package/dist/app/SkillAppSchema.d.ts +116 -0
- package/dist/app/SkillAppSchema.d.ts.map +1 -0
- package/dist/app/SkillAppSchema.js +5 -0
- package/dist/app/SkillAppSchema.js.map +1 -0
- package/dist/channels/TelegramBridge.d.ts +103 -0
- package/dist/channels/TelegramBridge.d.ts.map +1 -0
- package/dist/channels/TelegramBridge.js +565 -0
- package/dist/channels/TelegramBridge.js.map +1 -0
- package/dist/channels/respond.d.ts +46 -0
- package/dist/channels/respond.d.ts.map +1 -0
- package/dist/channels/respond.js +41 -0
- package/dist/channels/respond.js.map +1 -0
- package/dist/config/ChatGptTokenRefresh.d.ts +18 -0
- package/dist/config/ChatGptTokenRefresh.d.ts.map +1 -0
- package/dist/config/ChatGptTokenRefresh.js +63 -0
- package/dist/config/ChatGptTokenRefresh.js.map +1 -0
- package/dist/config/ConfigBackup.d.ts +20 -0
- package/dist/config/ConfigBackup.d.ts.map +1 -0
- package/dist/config/ConfigBackup.js +197 -0
- package/dist/config/ConfigBackup.js.map +1 -0
- package/dist/config/ConfigLoader.d.ts +28 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -0
- package/dist/config/ConfigLoader.js +335 -0
- package/dist/config/ConfigLoader.js.map +1 -0
- package/dist/config/ConfigManager.d.ts +55 -0
- package/dist/config/ConfigManager.d.ts.map +1 -0
- package/dist/config/ConfigManager.js +707 -0
- package/dist/config/ConfigManager.js.map +1 -0
- package/dist/config/ConfigSchema.d.ts +231 -0
- package/dist/config/ConfigSchema.d.ts.map +1 -0
- package/dist/config/ConfigSchema.js +15 -0
- package/dist/config/ConfigSchema.js.map +1 -0
- package/dist/config/GitHubCopilotAuth.d.ts +7 -0
- package/dist/config/GitHubCopilotAuth.d.ts.map +1 -0
- package/dist/config/GitHubCopilotAuth.js +40 -0
- package/dist/config/GitHubCopilotAuth.js.map +1 -0
- package/dist/config/MarifoldOpenAICompatProvider.d.ts +39 -0
- package/dist/config/MarifoldOpenAICompatProvider.d.ts.map +1 -0
- package/dist/config/MarifoldOpenAICompatProvider.js +158 -0
- package/dist/config/MarifoldOpenAICompatProvider.js.map +1 -0
- package/dist/config/OpenAICompatUrls.d.ts +7 -0
- package/dist/config/OpenAICompatUrls.d.ts.map +1 -0
- package/dist/config/OpenAICompatUrls.js +47 -0
- package/dist/config/OpenAICompatUrls.js.map +1 -0
- package/dist/config/ProviderFactory.d.ts +12 -0
- package/dist/config/ProviderFactory.d.ts.map +1 -0
- package/dist/config/ProviderFactory.js +61 -0
- package/dist/config/ProviderFactory.js.map +1 -0
- package/dist/config/ProviderInspector.d.ts +46 -0
- package/dist/config/ProviderInspector.d.ts.map +1 -0
- package/dist/config/ProviderInspector.js +356 -0
- package/dist/config/ProviderInspector.js.map +1 -0
- package/dist/config/ProviderRegistry.d.ts +19 -0
- package/dist/config/ProviderRegistry.d.ts.map +1 -0
- package/dist/config/ProviderRegistry.js +301 -0
- package/dist/config/ProviderRegistry.js.map +1 -0
- package/dist/config/XaiTokenRefresh.d.ts +15 -0
- package/dist/config/XaiTokenRefresh.d.ts.map +1 -0
- package/dist/config/XaiTokenRefresh.js +73 -0
- package/dist/config/XaiTokenRefresh.js.map +1 -0
- package/dist/errors/MarifoldError.d.ts +34 -0
- package/dist/errors/MarifoldError.d.ts.map +1 -0
- package/dist/errors/MarifoldError.js +100 -0
- package/dist/errors/MarifoldError.js.map +1 -0
- package/dist/images/ImageOptimizer.d.ts +34 -0
- package/dist/images/ImageOptimizer.d.ts.map +1 -0
- package/dist/images/ImageOptimizer.js +225 -0
- package/dist/images/ImageOptimizer.js.map +1 -0
- package/dist/index.d.ts +96 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +193 -0
- package/dist/index.js.map +1 -0
- package/dist/memory/MemoryControls.d.ts +26 -0
- package/dist/memory/MemoryControls.d.ts.map +1 -0
- package/dist/memory/MemoryControls.js +388 -0
- package/dist/memory/MemoryControls.js.map +1 -0
- package/dist/memory/MemoryStore.d.ts +122 -0
- package/dist/memory/MemoryStore.d.ts.map +1 -0
- package/dist/memory/MemoryStore.js +1096 -0
- package/dist/memory/MemoryStore.js.map +1 -0
- package/dist/profiles/ProfileManager.d.ts +139 -0
- package/dist/profiles/ProfileManager.d.ts.map +1 -0
- package/dist/profiles/ProfileManager.js +475 -0
- package/dist/profiles/ProfileManager.js.map +1 -0
- package/dist/profiles/ProfileResolver.d.ts +18 -0
- package/dist/profiles/ProfileResolver.d.ts.map +1 -0
- package/dist/profiles/ProfileResolver.js +280 -0
- package/dist/profiles/ProfileResolver.js.map +1 -0
- package/dist/runs/RunRegistry.d.ts +140 -0
- package/dist/runs/RunRegistry.d.ts.map +1 -0
- package/dist/runs/RunRegistry.js +432 -0
- package/dist/runs/RunRegistry.js.map +1 -0
- package/dist/runtime/MarifoldRuntime.d.ts +244 -0
- package/dist/runtime/MarifoldRuntime.d.ts.map +1 -0
- package/dist/runtime/MarifoldRuntime.js +1099 -0
- package/dist/runtime/MarifoldRuntime.js.map +1 -0
- package/dist/runtime/MarifoldTypes.d.ts +70 -0
- package/dist/runtime/MarifoldTypes.d.ts.map +1 -0
- package/dist/runtime/MarifoldTypes.js +3 -0
- package/dist/runtime/MarifoldTypes.js.map +1 -0
- package/dist/schedule/ScheduleStore.d.ts +60 -0
- package/dist/schedule/ScheduleStore.d.ts.map +1 -0
- package/dist/schedule/ScheduleStore.js +212 -0
- package/dist/schedule/ScheduleStore.js.map +1 -0
- package/dist/schedule/Scheduler.d.ts +28 -0
- package/dist/schedule/Scheduler.d.ts.map +1 -0
- package/dist/schedule/Scheduler.js +65 -0
- package/dist/schedule/Scheduler.js.map +1 -0
- package/dist/search/DuckDuckGoBackend.d.ts +18 -0
- package/dist/search/DuckDuckGoBackend.d.ts.map +1 -0
- package/dist/search/DuckDuckGoBackend.js +37 -0
- package/dist/search/DuckDuckGoBackend.js.map +1 -0
- package/dist/search/FirecrawlBackend.d.ts +28 -0
- package/dist/search/FirecrawlBackend.d.ts.map +1 -0
- package/dist/search/FirecrawlBackend.js +76 -0
- package/dist/search/FirecrawlBackend.js.map +1 -0
- package/dist/search/SearchBackend.d.ts +18 -0
- package/dist/search/SearchBackend.d.ts.map +1 -0
- package/dist/search/SearchBackend.js +24 -0
- package/dist/search/SearchBackend.js.map +1 -0
- package/dist/search/createSearchBackend.d.ts +16 -0
- package/dist/search/createSearchBackend.d.ts.map +1 -0
- package/dist/search/createSearchBackend.js +30 -0
- package/dist/search/createSearchBackend.js.map +1 -0
- package/dist/sessions/ResponseMetrics.d.ts +17 -0
- package/dist/sessions/ResponseMetrics.d.ts.map +1 -0
- package/dist/sessions/ResponseMetrics.js +3 -0
- package/dist/sessions/ResponseMetrics.js.map +1 -0
- package/dist/sessions/SessionResolver.d.ts +135 -0
- package/dist/sessions/SessionResolver.d.ts.map +1 -0
- package/dist/sessions/SessionResolver.js +1173 -0
- package/dist/sessions/SessionResolver.js.map +1 -0
- package/dist/skill/BuiltInSkillManager.d.ts +11 -0
- package/dist/skill/BuiltInSkillManager.d.ts.map +1 -0
- package/dist/skill/BuiltInSkillManager.js +98 -0
- package/dist/skill/BuiltInSkillManager.js.map +1 -0
- package/dist/skill/SkillInvocation.d.ts +30 -0
- package/dist/skill/SkillInvocation.d.ts.map +1 -0
- package/dist/skill/SkillInvocation.js +135 -0
- package/dist/skill/SkillInvocation.js.map +1 -0
- package/dist/skill/SkillSchema.d.ts +37 -0
- package/dist/skill/SkillSchema.d.ts.map +1 -0
- package/dist/skill/SkillSchema.js +26 -0
- package/dist/skill/SkillSchema.js.map +1 -0
- package/dist/skill/SkillStore.d.ts +41 -0
- package/dist/skill/SkillStore.d.ts.map +1 -0
- package/dist/skill/SkillStore.js +184 -0
- package/dist/skill/SkillStore.js.map +1 -0
- package/dist/skill/SkillTemplater.d.ts +19 -0
- package/dist/skill/SkillTemplater.d.ts.map +1 -0
- package/dist/skill/SkillTemplater.js +36 -0
- package/dist/skill/SkillTemplater.js.map +1 -0
- package/dist/skill/SkillValidator.d.ts +10 -0
- package/dist/skill/SkillValidator.d.ts.map +1 -0
- package/dist/skill/SkillValidator.js +131 -0
- package/dist/skill/SkillValidator.js.map +1 -0
- package/dist/skill/SkillVars.d.ts +10 -0
- package/dist/skill/SkillVars.d.ts.map +1 -0
- package/dist/skill/SkillVars.js +65 -0
- package/dist/skill/SkillVars.js.map +1 -0
- package/dist/skill/index.d.ts +10 -0
- package/dist/skill/index.d.ts.map +1 -0
- package/dist/skill/index.js +22 -0
- package/dist/skill/index.js.map +1 -0
- package/dist/skillapp/SkillAppActionResolver.d.ts +13 -0
- package/dist/skillapp/SkillAppActionResolver.d.ts.map +1 -0
- package/dist/skillapp/SkillAppActionResolver.js +95 -0
- package/dist/skillapp/SkillAppActionResolver.js.map +1 -0
- package/dist/skillapp/SkillAppSchema.d.ts +79 -0
- package/dist/skillapp/SkillAppSchema.d.ts.map +1 -0
- package/dist/skillapp/SkillAppSchema.js +20 -0
- package/dist/skillapp/SkillAppSchema.js.map +1 -0
- package/dist/skillapp/SkillAppStore.d.ts +11 -0
- package/dist/skillapp/SkillAppStore.d.ts.map +1 -0
- package/dist/skillapp/SkillAppStore.js +98 -0
- package/dist/skillapp/SkillAppStore.js.map +1 -0
- package/dist/skillapp/SkillAppValidator.d.ts +10 -0
- package/dist/skillapp/SkillAppValidator.d.ts.map +1 -0
- package/dist/skillapp/SkillAppValidator.js +522 -0
- package/dist/skillapp/SkillAppValidator.js.map +1 -0
- package/dist/tasks/TaskStore.d.ts +110 -0
- package/dist/tasks/TaskStore.d.ts.map +1 -0
- package/dist/tasks/TaskStore.js +371 -0
- package/dist/tasks/TaskStore.js.map +1 -0
- package/dist/util/fetchRetry.d.ts +11 -0
- package/dist/util/fetchRetry.d.ts.map +1 -0
- package/dist/util/fetchRetry.js +48 -0
- package/dist/util/fetchRetry.js.map +1 -0
- package/dist/util/idToken.d.ts +12 -0
- package/dist/util/idToken.d.ts.map +1 -0
- package/dist/util/idToken.js +32 -0
- package/dist/util/idToken.js.map +1 -0
- package/dist/util/proxy.d.ts +18 -0
- package/dist/util/proxy.d.ts.map +1 -0
- package/dist/util/proxy.js +28 -0
- package/dist/util/proxy.js.map +1 -0
- package/dist/workspace/Workspace.d.ts +9 -0
- package/dist/workspace/Workspace.d.ts.map +1 -0
- package/dist/workspace/Workspace.js +55 -0
- package/dist/workspace/Workspace.js.map +1 -0
- package/dist/workspace/WorkspaceInitializer.d.ts +40 -0
- package/dist/workspace/WorkspaceInitializer.d.ts.map +1 -0
- package/dist/workspace/WorkspaceInitializer.js +212 -0
- package/dist/workspace/WorkspaceInitializer.js.map +1 -0
- package/dist/workspace/WorkspacePaths.d.ts +11 -0
- package/dist/workspace/WorkspacePaths.d.ts.map +1 -0
- package/dist/workspace/WorkspacePaths.js +82 -0
- package/dist/workspace/WorkspacePaths.js.map +1 -0
- package/package.json +36 -0
|
@@ -0,0 +1,632 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.compileSkillApp = compileSkillApp;
|
|
37
|
+
const ts = __importStar(require("typescript-compiler"));
|
|
38
|
+
const MarifoldError_1 = require("../errors/MarifoldError");
|
|
39
|
+
const SkillAppSchema_1 = require("./SkillAppSchema");
|
|
40
|
+
const MAX_SOURCE_BYTES = 128 * 1024;
|
|
41
|
+
const SAFE_APP_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
42
|
+
const SAFE_LOCAL_NAME = /^[a-zA-Z][a-zA-Z0-9_]*$/;
|
|
43
|
+
const SAFE_SKILL_NAME = /^[a-z0-9][a-z0-9_-]*$/;
|
|
44
|
+
const SAFE_PROVIDER_NAME = /^[a-z0-9][a-z0-9_-]*$/;
|
|
45
|
+
const ALLOWED_BUILDERS = new Set([
|
|
46
|
+
'App', 'Button', 'Column', 'Row', 'Select', 'Spacer', 'State', 'Textarea',
|
|
47
|
+
'TextResult', 'defineSkillApp', 'registerModel', 'registerSkill', 'trigger', 'useSkill',
|
|
48
|
+
]);
|
|
49
|
+
/** Compile the restricted TypeScript authoring syntax into renderer-neutral data.
|
|
50
|
+
* The source is inspected as an AST and is never evaluated or imported. */
|
|
51
|
+
function compileSkillApp(source, sourcePath = 'skillapp.ts') {
|
|
52
|
+
if (Buffer.byteLength(source, 'utf8') > MAX_SOURCE_BYTES) {
|
|
53
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`SkillApp source exceeds ${MAX_SOURCE_BYTES} bytes.`, sourcePath);
|
|
54
|
+
}
|
|
55
|
+
const sourceFile = ts.createSourceFile(sourcePath, source.replace(/^/, ''), ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
56
|
+
const parseDiagnostics = sourceFile.parseDiagnostics;
|
|
57
|
+
if (parseDiagnostics.length > 0) {
|
|
58
|
+
const diagnostic = parseDiagnostics[0];
|
|
59
|
+
throw invalidAt(sourceFile, diagnostic.start, ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'), sourcePath);
|
|
60
|
+
}
|
|
61
|
+
const state = {
|
|
62
|
+
sourceFile,
|
|
63
|
+
imports: new Map(),
|
|
64
|
+
values: new Map(),
|
|
65
|
+
states: [],
|
|
66
|
+
models: [],
|
|
67
|
+
skills: [],
|
|
68
|
+
operations: [],
|
|
69
|
+
triggers: [],
|
|
70
|
+
};
|
|
71
|
+
for (const statement of sourceFile.statements) {
|
|
72
|
+
if (ts.isImportDeclaration(statement)) {
|
|
73
|
+
parseImport(statement, state, sourcePath);
|
|
74
|
+
}
|
|
75
|
+
else if (ts.isVariableStatement(statement)) {
|
|
76
|
+
parseVariables(statement, state, sourcePath);
|
|
77
|
+
}
|
|
78
|
+
else if (ts.isExpressionStatement(statement)) {
|
|
79
|
+
const expression = unwrap(statement.expression);
|
|
80
|
+
if (!ts.isCallExpression(expression) || builderName(expression.expression, state) !== 'trigger') {
|
|
81
|
+
throw invalidAt(sourceFile, statement.pos, 'Only a top-level trigger(...) call is allowed as an expression.', sourcePath);
|
|
82
|
+
}
|
|
83
|
+
evaluateCall(expression, state, sourcePath);
|
|
84
|
+
}
|
|
85
|
+
else if (ts.isExportAssignment(statement) && !statement.isExportEquals) {
|
|
86
|
+
if (state.template)
|
|
87
|
+
throw invalidAt(sourceFile, statement.pos, 'Only one default export is allowed.', sourcePath);
|
|
88
|
+
const value = evaluateExpression(statement.expression, state, sourcePath);
|
|
89
|
+
const tagged = requireTagged(value, 'skillapp', statement.expression, state, sourcePath);
|
|
90
|
+
state.template = {
|
|
91
|
+
app: requireObject(tagged.app, 'defineSkillApp app', statement.expression, state, sourcePath),
|
|
92
|
+
ui: requireTagged(tagged.ui, 'component', statement.expression, state, sourcePath),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
else if (statement.getText(sourceFile).trim() !== '') {
|
|
96
|
+
throw invalidAt(sourceFile, statement.pos, 'SkillApp templates allow imports, const declarations, trigger(...), and one default export only.', sourcePath);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (!state.template)
|
|
100
|
+
throw MarifoldError_1.MarifoldError.appInvalid('SkillApp must export default defineSkillApp({...}).', sourcePath);
|
|
101
|
+
const app = normalizeAppInfo(state.template.app, state, sourcePath);
|
|
102
|
+
const layoutRoot = normalizeComponent(state.template.ui, state, sourcePath);
|
|
103
|
+
if (layoutRoot.component !== 'app') {
|
|
104
|
+
throw MarifoldError_1.MarifoldError.appInvalid('defineSkillApp.ui must be App([...]).', sourcePath);
|
|
105
|
+
}
|
|
106
|
+
validateReferences(state, layoutRoot.children ?? [], sourcePath);
|
|
107
|
+
return {
|
|
108
|
+
schema: SkillAppSchema_1.SKILL_APP_SCHEMA,
|
|
109
|
+
app,
|
|
110
|
+
states: state.states,
|
|
111
|
+
models: state.models,
|
|
112
|
+
skills: state.skills,
|
|
113
|
+
operations: state.operations,
|
|
114
|
+
triggers: state.triggers,
|
|
115
|
+
layout: layoutRoot.children ?? [],
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function parseImport(node, state, sourcePath) {
|
|
119
|
+
if (!ts.isStringLiteral(node.moduleSpecifier) || node.moduleSpecifier.text !== '@marifold/core') {
|
|
120
|
+
throw invalidAt(state.sourceFile, node.pos, 'SkillApp may import builders only from "@marifold/core".', sourcePath);
|
|
121
|
+
}
|
|
122
|
+
const clause = node.importClause;
|
|
123
|
+
if (!clause || clause.name || !clause.namedBindings || !ts.isNamedImports(clause.namedBindings)) {
|
|
124
|
+
throw invalidAt(state.sourceFile, node.pos, 'Use named SkillApp builder imports.', sourcePath);
|
|
125
|
+
}
|
|
126
|
+
for (const element of clause.namedBindings.elements) {
|
|
127
|
+
const imported = element.propertyName?.text ?? element.name.text;
|
|
128
|
+
if (!ALLOWED_BUILDERS.has(imported)) {
|
|
129
|
+
throw invalidAt(state.sourceFile, element.pos, `'${imported}' is not an allowed SkillApp builder.`, sourcePath);
|
|
130
|
+
}
|
|
131
|
+
state.imports.set(element.name.text, imported);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function parseVariables(node, state, sourcePath) {
|
|
135
|
+
if ((node.declarationList.flags & ts.NodeFlags.Const) === 0) {
|
|
136
|
+
throw invalidAt(state.sourceFile, node.pos, 'SkillApp declarations must use const.', sourcePath);
|
|
137
|
+
}
|
|
138
|
+
for (const declaration of node.declarationList.declarations) {
|
|
139
|
+
if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
|
|
140
|
+
throw invalidAt(state.sourceFile, declaration.pos, 'SkillApp const declarations require a simple name and initializer.', sourcePath);
|
|
141
|
+
}
|
|
142
|
+
const name = declaration.name.text;
|
|
143
|
+
if (!SAFE_LOCAL_NAME.test(name) || state.values.has(name)) {
|
|
144
|
+
throw invalidAt(state.sourceFile, declaration.pos, `Invalid or duplicate declaration '${name}'.`, sourcePath);
|
|
145
|
+
}
|
|
146
|
+
const value = evaluateExpression(declaration.initializer, state, sourcePath, name);
|
|
147
|
+
if (isTagged(value))
|
|
148
|
+
value.name = name;
|
|
149
|
+
state.values.set(name, value);
|
|
150
|
+
registerDeclaration(name, value, declaration, state, sourcePath);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function registerDeclaration(name, value, node, state, sourcePath) {
|
|
154
|
+
if (!isTagged(value))
|
|
155
|
+
return;
|
|
156
|
+
if (value.__kind === 'state') {
|
|
157
|
+
state.states.push({ name, initial: requireString(value.initial, 'State initial value', node, state, sourcePath) });
|
|
158
|
+
}
|
|
159
|
+
else if (value.__kind === 'model') {
|
|
160
|
+
const id = requireString(value.id, 'model id', node, state, sourcePath);
|
|
161
|
+
const separator = id.indexOf('/');
|
|
162
|
+
if (separator <= 0 || separator === id.length - 1) {
|
|
163
|
+
throw invalidAt(state.sourceFile, node.pos, `Model '${id}' must use provider/model format.`, sourcePath);
|
|
164
|
+
}
|
|
165
|
+
const provider = id.slice(0, separator);
|
|
166
|
+
if (!SAFE_PROVIDER_NAME.test(provider)) {
|
|
167
|
+
throw invalidAt(state.sourceFile, node.pos, `Invalid model provider '${provider}'.`, sourcePath);
|
|
168
|
+
}
|
|
169
|
+
const model = id.slice(separator + 1);
|
|
170
|
+
if (model.trim() !== model || model.length === 0) {
|
|
171
|
+
throw invalidAt(state.sourceFile, node.pos, `Invalid model id '${model}'.`, sourcePath);
|
|
172
|
+
}
|
|
173
|
+
state.models.push({
|
|
174
|
+
name,
|
|
175
|
+
provider,
|
|
176
|
+
model,
|
|
177
|
+
think: requireBoolean(value.think, 'model think', node, state, sourcePath),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
else if (value.__kind === 'skill') {
|
|
181
|
+
const skillName = requireString(value.skillName, 'skill name', node, state, sourcePath);
|
|
182
|
+
if (!SAFE_SKILL_NAME.test(skillName)) {
|
|
183
|
+
throw invalidAt(state.sourceFile, node.pos, `Invalid app-local skill name '${skillName}'.`, sourcePath);
|
|
184
|
+
}
|
|
185
|
+
const result = requireTagged(value.result, 'text_result', node, state, sourcePath);
|
|
186
|
+
state.skills.push({
|
|
187
|
+
name: skillName,
|
|
188
|
+
result: { kind: 'text', trim: requireBoolean(result.trim, 'result trim', node, state, sourcePath) },
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
else if (value.__kind === 'operation') {
|
|
192
|
+
state.operations.push({
|
|
193
|
+
name,
|
|
194
|
+
model: requireNamedReference(value.model, 'model', node, state, sourcePath),
|
|
195
|
+
skill: requireNamedReference(value.skill, 'skill', node, state, sourcePath, true),
|
|
196
|
+
parameters: Object.fromEntries(Object.entries(requireObject(value.parameters, 'operation parameters', node, state, sourcePath)).map(([parameter, reference]) => [
|
|
197
|
+
parameter,
|
|
198
|
+
requireNamedReference(reference, 'state', node, state, sourcePath),
|
|
199
|
+
])),
|
|
200
|
+
requiredInputs: [],
|
|
201
|
+
output: requireNamedReference(value.output, 'state', node, state, sourcePath),
|
|
202
|
+
execution: { memory: false, history: false, profileContext: false },
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function evaluateExpression(rawNode, state, sourcePath, declarationName) {
|
|
207
|
+
const node = unwrap(rawNode);
|
|
208
|
+
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
|
|
209
|
+
return node.text;
|
|
210
|
+
if (ts.isNumericLiteral(node))
|
|
211
|
+
return Number(node.text);
|
|
212
|
+
if (node.kind === ts.SyntaxKind.TrueKeyword)
|
|
213
|
+
return true;
|
|
214
|
+
if (node.kind === ts.SyntaxKind.FalseKeyword)
|
|
215
|
+
return false;
|
|
216
|
+
if (node.kind === ts.SyntaxKind.NullKeyword)
|
|
217
|
+
return null;
|
|
218
|
+
if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.MinusToken && ts.isNumericLiteral(node.operand)) {
|
|
219
|
+
return -Number(node.operand.text);
|
|
220
|
+
}
|
|
221
|
+
if (ts.isIdentifier(node)) {
|
|
222
|
+
const value = state.values.get(node.text);
|
|
223
|
+
if (value === undefined)
|
|
224
|
+
throw invalidAt(state.sourceFile, node.pos, `Unknown declaration '${node.text}'.`, sourcePath);
|
|
225
|
+
return value;
|
|
226
|
+
}
|
|
227
|
+
if (ts.isArrayLiteralExpression(node)) {
|
|
228
|
+
return node.elements.map(element => {
|
|
229
|
+
if (ts.isSpreadElement(element))
|
|
230
|
+
throw invalidAt(state.sourceFile, element.pos, 'Array spreads are not allowed.', sourcePath);
|
|
231
|
+
return evaluateExpression(element, state, sourcePath);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
235
|
+
const result = Object.create(null);
|
|
236
|
+
for (const property of node.properties) {
|
|
237
|
+
if (ts.isPropertyAssignment(property)) {
|
|
238
|
+
const name = propertyName(property.name, state, sourcePath);
|
|
239
|
+
if (Object.hasOwn(result, name)) {
|
|
240
|
+
throw invalidAt(state.sourceFile, property.pos, `Duplicate object property '${name}'.`, sourcePath);
|
|
241
|
+
}
|
|
242
|
+
result[name] = evaluateExpression(property.initializer, state, sourcePath);
|
|
243
|
+
}
|
|
244
|
+
else if (ts.isShorthandPropertyAssignment(property)) {
|
|
245
|
+
if (Object.hasOwn(result, property.name.text)) {
|
|
246
|
+
throw invalidAt(state.sourceFile, property.pos, `Duplicate object property '${property.name.text}'.`, sourcePath);
|
|
247
|
+
}
|
|
248
|
+
const value = state.values.get(property.name.text);
|
|
249
|
+
if (value === undefined)
|
|
250
|
+
throw invalidAt(state.sourceFile, property.pos, `Unknown declaration '${property.name.text}'.`, sourcePath);
|
|
251
|
+
result[property.name.text] = value;
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
throw invalidAt(state.sourceFile, property.pos, 'Object methods, accessors, and spreads are not allowed.', sourcePath);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return result;
|
|
258
|
+
}
|
|
259
|
+
if (ts.isCallExpression(node))
|
|
260
|
+
return evaluateCall(node, state, sourcePath, declarationName);
|
|
261
|
+
throw invalidAt(state.sourceFile, node.pos, `Unsupported expression '${ts.SyntaxKind[node.kind]}'.`, sourcePath);
|
|
262
|
+
}
|
|
263
|
+
function evaluateCall(node, state, sourcePath, _declarationName) {
|
|
264
|
+
const name = builderName(node.expression, state);
|
|
265
|
+
if (!name)
|
|
266
|
+
throw invalidAt(state.sourceFile, node.pos, 'Only imported SkillApp builders may be called.', sourcePath);
|
|
267
|
+
const args = node.arguments.map(argument => evaluateExpression(argument, state, sourcePath));
|
|
268
|
+
switch (name) {
|
|
269
|
+
case 'State':
|
|
270
|
+
exactArgs(name, args, 1, node, state, sourcePath);
|
|
271
|
+
return { __kind: 'state', initial: requireString(args[0], 'State initial value', node, state, sourcePath) };
|
|
272
|
+
case 'registerModel': {
|
|
273
|
+
argsRange(name, args, 1, 2, node, state, sourcePath);
|
|
274
|
+
const options = args[1] === undefined ? {} : requireObject(args[1], 'registerModel options', node, state, sourcePath);
|
|
275
|
+
rejectUnknown(options, ['think'], name, node, state, sourcePath);
|
|
276
|
+
return {
|
|
277
|
+
__kind: 'model',
|
|
278
|
+
id: requireString(args[0], 'model id', node, state, sourcePath),
|
|
279
|
+
think: options.think === undefined ? false : requireBoolean(options.think, 'think', node, state, sourcePath),
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
case 'TextResult': {
|
|
283
|
+
argsRange(name, args, 0, 1, node, state, sourcePath);
|
|
284
|
+
const options = args[0] === undefined ? {} : requireObject(args[0], 'TextResult options', node, state, sourcePath);
|
|
285
|
+
rejectUnknown(options, ['trim'], name, node, state, sourcePath);
|
|
286
|
+
return {
|
|
287
|
+
__kind: 'text_result',
|
|
288
|
+
trim: options.trim === undefined ? false : requireBoolean(options.trim, 'trim', node, state, sourcePath),
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
case 'registerSkill': {
|
|
292
|
+
exactArgs(name, args, 2, node, state, sourcePath);
|
|
293
|
+
const options = requireObject(args[1], 'registerSkill options', node, state, sourcePath);
|
|
294
|
+
rejectUnknown(options, ['result'], name, node, state, sourcePath);
|
|
295
|
+
return {
|
|
296
|
+
__kind: 'skill',
|
|
297
|
+
skillName: requireString(args[0], 'skill name', node, state, sourcePath),
|
|
298
|
+
result: requireTagged(options.result, 'text_result', node, state, sourcePath),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
case 'useSkill': {
|
|
302
|
+
exactArgs(name, args, 3, node, state, sourcePath);
|
|
303
|
+
const options = requireObject(args[2], 'useSkill options', node, state, sourcePath);
|
|
304
|
+
rejectUnknown(options, ['parameters', 'output', 'memory', 'history', 'profileContext'], name, node, state, sourcePath);
|
|
305
|
+
for (const key of ['memory', 'history', 'profileContext']) {
|
|
306
|
+
if (options[key] !== false)
|
|
307
|
+
throw invalidAt(state.sourceFile, node.pos, `useSkill.${key} must be false in v1.`, sourcePath);
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
__kind: 'operation',
|
|
311
|
+
model: requireTagged(args[0], 'model', node, state, sourcePath),
|
|
312
|
+
skill: requireTagged(args[1], 'skill', node, state, sourcePath),
|
|
313
|
+
parameters: requireObject(options.parameters, 'useSkill parameters', node, state, sourcePath),
|
|
314
|
+
output: requireTagged(options.output, 'state', node, state, sourcePath),
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
case 'trigger': {
|
|
318
|
+
exactArgs(name, args, 2, node, state, sourcePath);
|
|
319
|
+
const operation = requireTagged(args[0], 'operation', node, state, sourcePath);
|
|
320
|
+
const options = requireObject(args[1], 'trigger options', node, state, sourcePath);
|
|
321
|
+
rejectUnknown(options, ['onChange', 'debounce', 'concurrency'], name, node, state, sourcePath);
|
|
322
|
+
const onChange = requireArray(options.onChange, 'trigger onChange', node, state, sourcePath)
|
|
323
|
+
.map(value => requireNamedReference(value, 'state', node, state, sourcePath));
|
|
324
|
+
if (onChange.length === 0)
|
|
325
|
+
throw invalidAt(state.sourceFile, node.pos, 'trigger.onChange cannot be empty.', sourcePath);
|
|
326
|
+
const debounce = options.debounce === undefined ? 0 : requireNonNegativeInteger(options.debounce, 'debounce', node, state, sourcePath);
|
|
327
|
+
if (debounce > 60_000)
|
|
328
|
+
throw invalidAt(state.sourceFile, node.pos, 'trigger.debounce cannot exceed 60000 ms.', sourcePath);
|
|
329
|
+
const concurrency = options.concurrency === undefined
|
|
330
|
+
? 'latest'
|
|
331
|
+
: requireString(options.concurrency, 'concurrency', node, state, sourcePath);
|
|
332
|
+
if (concurrency !== 'latest')
|
|
333
|
+
throw invalidAt(state.sourceFile, node.pos, 'trigger.concurrency must be "latest" in v1.', sourcePath);
|
|
334
|
+
state.triggers.push({
|
|
335
|
+
operation: requireName(operation, 'operation', node, state, sourcePath),
|
|
336
|
+
onChange,
|
|
337
|
+
debounce,
|
|
338
|
+
concurrency,
|
|
339
|
+
});
|
|
340
|
+
return { __kind: 'trigger' };
|
|
341
|
+
}
|
|
342
|
+
case 'defineSkillApp': {
|
|
343
|
+
exactArgs(name, args, 1, node, state, sourcePath);
|
|
344
|
+
const template = requireObject(args[0], 'defineSkillApp template', node, state, sourcePath);
|
|
345
|
+
rejectUnknown(template, ['app', 'ui'], name, node, state, sourcePath);
|
|
346
|
+
return {
|
|
347
|
+
__kind: 'skillapp',
|
|
348
|
+
app: requireObject(template.app, 'app metadata', node, state, sourcePath),
|
|
349
|
+
ui: requireTagged(template.ui, 'component', node, state, sourcePath),
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
case 'App':
|
|
353
|
+
case 'Column':
|
|
354
|
+
case 'Row': {
|
|
355
|
+
argsRange(name, args, 1, 2, node, state, sourcePath);
|
|
356
|
+
const children = requireArray(args[0], `${name} children`, node, state, sourcePath);
|
|
357
|
+
children.forEach(value => requireTagged(value, 'component', node, state, sourcePath));
|
|
358
|
+
const options = args[1] === undefined ? {} : requireObject(args[1], `${name} options`, node, state, sourcePath);
|
|
359
|
+
const allowed = name === 'Row' ? ['gap', 'responsive'] : name === 'Column' ? ['gap'] : [];
|
|
360
|
+
rejectUnknown(options, allowed, name, node, state, sourcePath);
|
|
361
|
+
return { __kind: 'component', component: name.toLowerCase(), children, options };
|
|
362
|
+
}
|
|
363
|
+
case 'Spacer':
|
|
364
|
+
exactArgs(name, args, 0, node, state, sourcePath);
|
|
365
|
+
return { __kind: 'component', component: 'spacer', options: {} };
|
|
366
|
+
case 'Textarea': {
|
|
367
|
+
argsRange(name, args, 2, 3, node, state, sourcePath);
|
|
368
|
+
const options = args[2] === undefined ? {} : requireObject(args[2], 'Textarea options', node, state, sourcePath);
|
|
369
|
+
rejectUnknown(options, ['showLabel', 'grow', 'editable', 'copyable', 'placeholder'], name, node, state, sourcePath);
|
|
370
|
+
return {
|
|
371
|
+
__kind: 'component', component: 'textarea', label: requireNonEmptyString(args[0], 'Textarea label', node, state, sourcePath),
|
|
372
|
+
bind: requireTagged(args[1], 'state', node, state, sourcePath), options,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
case 'Select': {
|
|
376
|
+
exactArgs(name, args, 3, node, state, sourcePath);
|
|
377
|
+
const options = requireObject(args[2], 'Select options', node, state, sourcePath);
|
|
378
|
+
rejectUnknown(options, ['options', 'showLabel', 'grow'], name, node, state, sourcePath);
|
|
379
|
+
const choices = requireArray(options.options, 'Select options.options', node, state, sourcePath)
|
|
380
|
+
.map(value => requireNonEmptyString(value, 'Select option', node, state, sourcePath));
|
|
381
|
+
if (choices.length === 0)
|
|
382
|
+
throw invalidAt(state.sourceFile, node.pos, 'Select options cannot be empty.', sourcePath);
|
|
383
|
+
return {
|
|
384
|
+
__kind: 'component', component: 'select', label: requireNonEmptyString(args[0], 'Select label', node, state, sourcePath),
|
|
385
|
+
bind: requireTagged(args[1], 'state', node, state, sourcePath), options: { ...options, options: choices },
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
case 'Button': {
|
|
389
|
+
exactArgs(name, args, 2, node, state, sourcePath);
|
|
390
|
+
const options = requireObject(args[1], 'Button options', node, state, sourcePath);
|
|
391
|
+
rejectUnknown(options, ['trigger', 'emphasis'], name, node, state, sourcePath);
|
|
392
|
+
return {
|
|
393
|
+
__kind: 'component', component: 'button', label: requireNonEmptyString(args[0], 'Button label', node, state, sourcePath),
|
|
394
|
+
operation: requireTagged(options.trigger, 'operation', node, state, sourcePath), options,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
throw invalidAt(state.sourceFile, node.pos, `Unsupported builder '${name}'.`, sourcePath);
|
|
399
|
+
}
|
|
400
|
+
function normalizeComponent(value, state, sourcePath, depth = 0) {
|
|
401
|
+
if (depth > 4)
|
|
402
|
+
throw MarifoldError_1.MarifoldError.appInvalid('SkillApp layout depth cannot exceed four.', sourcePath);
|
|
403
|
+
const component = requireString(value.component, 'component name', state.sourceFile, state, sourcePath);
|
|
404
|
+
const options = requireObject(value.options ?? {}, `${component} options`, state.sourceFile, state, sourcePath);
|
|
405
|
+
const item = { component };
|
|
406
|
+
if (value.children) {
|
|
407
|
+
item.children = requireArray(value.children, `${component} children`, state.sourceFile, state, sourcePath)
|
|
408
|
+
.map(child => normalizeComponent(requireTagged(child, 'component', state.sourceFile, state, sourcePath), state, sourcePath, depth + 1));
|
|
409
|
+
}
|
|
410
|
+
if (value.label !== undefined)
|
|
411
|
+
item.label = requireString(value.label, `${component} label`, state.sourceFile, state, sourcePath);
|
|
412
|
+
if (value.bind !== undefined)
|
|
413
|
+
item.bind = requireNamedReference(value.bind, 'state', state.sourceFile, state, sourcePath);
|
|
414
|
+
if (value.operation !== undefined)
|
|
415
|
+
item.trigger = requireName(requireTagged(value.operation, 'operation', state.sourceFile, state, sourcePath), 'operation', state.sourceFile, state, sourcePath);
|
|
416
|
+
copyBoolean(options, 'showLabel', item, state, sourcePath);
|
|
417
|
+
copyBoolean(options, 'grow', item, state, sourcePath);
|
|
418
|
+
copyBoolean(options, 'editable', item, state, sourcePath);
|
|
419
|
+
copyBoolean(options, 'copyable', item, state, sourcePath);
|
|
420
|
+
copyString(options, 'placeholder', item, state, sourcePath);
|
|
421
|
+
copyString(options, 'gap', item, state, sourcePath);
|
|
422
|
+
copyString(options, 'responsive', item, state, sourcePath);
|
|
423
|
+
copyString(options, 'emphasis', item, state, sourcePath);
|
|
424
|
+
if (options.options !== undefined) {
|
|
425
|
+
item.options = requireArray(options.options, 'Select options', state.sourceFile, state, sourcePath)
|
|
426
|
+
.map(choice => requireString(choice, 'Select option', state.sourceFile, state, sourcePath));
|
|
427
|
+
}
|
|
428
|
+
if (item.component === 'textarea' && item.editable === undefined)
|
|
429
|
+
item.editable = true;
|
|
430
|
+
if (item.component === 'button' && item.emphasis === undefined)
|
|
431
|
+
item.emphasis = 'primary';
|
|
432
|
+
if (item.gap !== undefined && !['none', 'small', 'medium', 'large'].includes(item.gap)) {
|
|
433
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Invalid layout gap '${item.gap}'.`, sourcePath);
|
|
434
|
+
}
|
|
435
|
+
if (item.responsive !== undefined && item.responsive !== 'stack') {
|
|
436
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Invalid responsive behavior '${item.responsive}'.`, sourcePath);
|
|
437
|
+
}
|
|
438
|
+
if (item.emphasis !== undefined && !['primary', 'secondary'].includes(item.emphasis)) {
|
|
439
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Invalid button emphasis '${item.emphasis}'.`, sourcePath);
|
|
440
|
+
}
|
|
441
|
+
if (item.options && new Set(item.options).size !== item.options.length) {
|
|
442
|
+
throw MarifoldError_1.MarifoldError.appInvalid('Select options must be unique.', sourcePath);
|
|
443
|
+
}
|
|
444
|
+
return item;
|
|
445
|
+
}
|
|
446
|
+
function validateReferences(state, layout, sourcePath) {
|
|
447
|
+
const stateNames = new Set(state.states.map(item => item.name));
|
|
448
|
+
const modelNames = new Set(state.models.map(item => item.name));
|
|
449
|
+
const skillNames = new Set(state.skills.map(item => item.name));
|
|
450
|
+
const operationNames = new Set(state.operations.map(item => item.name));
|
|
451
|
+
assertUnique(state.models.map(item => item.name), 'model declarations', sourcePath);
|
|
452
|
+
assertUnique(state.skills.map(item => item.name), 'registered Skills', sourcePath);
|
|
453
|
+
assertUnique(state.operations.map(item => item.name), 'operations', sourcePath);
|
|
454
|
+
if (state.states.length === 0)
|
|
455
|
+
throw MarifoldError_1.MarifoldError.appInvalid('SkillApp must declare at least one State.', sourcePath);
|
|
456
|
+
if (state.operations.length === 0)
|
|
457
|
+
throw MarifoldError_1.MarifoldError.appInvalid('SkillApp must declare at least one useSkill operation.', sourcePath);
|
|
458
|
+
for (const operation of state.operations) {
|
|
459
|
+
if (!modelNames.has(operation.model))
|
|
460
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Operation '${operation.name}' references missing model '${operation.model}'.`, sourcePath);
|
|
461
|
+
if (!skillNames.has(operation.skill))
|
|
462
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Operation '${operation.name}' references missing skill '${operation.skill}'.`, sourcePath);
|
|
463
|
+
if (!stateNames.has(operation.output))
|
|
464
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Operation '${operation.name}' references missing output state '${operation.output}'.`, sourcePath);
|
|
465
|
+
for (const name of Object.values(operation.parameters)) {
|
|
466
|
+
if (!stateNames.has(name))
|
|
467
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Operation '${operation.name}' references missing state '${name}'.`, sourcePath);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
const layoutItems = flatten(layout);
|
|
471
|
+
if (layoutItems.length > 100)
|
|
472
|
+
throw MarifoldError_1.MarifoldError.appInvalid('SkillApp layout cannot exceed 100 components.', sourcePath);
|
|
473
|
+
for (const item of layoutItems) {
|
|
474
|
+
if (item.component === 'app')
|
|
475
|
+
throw MarifoldError_1.MarifoldError.appInvalid('App(...) can only be the root UI component.', sourcePath);
|
|
476
|
+
if (item.bind && !stateNames.has(item.bind))
|
|
477
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Layout references missing state '${item.bind}'.`, sourcePath);
|
|
478
|
+
if (item.trigger && !operationNames.has(item.trigger))
|
|
479
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Button references missing operation '${item.trigger}'.`, sourcePath);
|
|
480
|
+
if (item.component === 'select' && item.bind) {
|
|
481
|
+
const initial = state.states.find(candidate => candidate.name === item.bind)?.initial;
|
|
482
|
+
if (initial !== undefined && !(item.options ?? []).includes(initial)) {
|
|
483
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Select state '${item.bind}' initial value must be one of its options.`, sourcePath);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
for (const triggerDefinition of state.triggers) {
|
|
488
|
+
if (!operationNames.has(triggerDefinition.operation))
|
|
489
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Trigger references missing operation '${triggerDefinition.operation}'.`, sourcePath);
|
|
490
|
+
for (const name of triggerDefinition.onChange) {
|
|
491
|
+
if (!stateNames.has(name))
|
|
492
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Trigger references missing state '${name}'.`, sourcePath);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
const outputStates = new Set(state.operations.map(operation => operation.output));
|
|
496
|
+
for (const operation of state.operations) {
|
|
497
|
+
for (const input of Object.values(operation.parameters)) {
|
|
498
|
+
if (outputStates.has(input))
|
|
499
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Operation '${operation.name}' cannot use output state '${input}' as a parameter in v1.`, sourcePath);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
for (const item of layoutItems) {
|
|
503
|
+
if (!item.bind || !outputStates.has(item.bind))
|
|
504
|
+
continue;
|
|
505
|
+
if (item.component === 'select') {
|
|
506
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Output state '${item.bind}' cannot bind to Select.`, sourcePath);
|
|
507
|
+
}
|
|
508
|
+
if (item.component === 'textarea' && item.editable !== false) {
|
|
509
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Output state '${item.bind}' must use Textarea(..., { editable: false }).`, sourcePath);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
for (const triggerDefinition of state.triggers) {
|
|
513
|
+
const outputDependency = triggerDefinition.onChange.find(name => outputStates.has(name));
|
|
514
|
+
if (outputDependency) {
|
|
515
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Trigger cannot watch output state '${outputDependency}'.`, sourcePath);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
function assertUnique(values, label, sourcePath) {
|
|
520
|
+
const seen = new Set();
|
|
521
|
+
for (const value of values) {
|
|
522
|
+
if (seen.has(value))
|
|
523
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Duplicate ${label}: '${value}'.`, sourcePath);
|
|
524
|
+
seen.add(value);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
function normalizeAppInfo(raw, state, sourcePath) {
|
|
528
|
+
rejectUnknown(raw, ['name', 'title', 'version', 'description'], 'app metadata', state.sourceFile, state, sourcePath);
|
|
529
|
+
const name = requireNonEmptyString(raw.name, 'app.name', state.sourceFile, state, sourcePath);
|
|
530
|
+
if (!SAFE_APP_NAME.test(name))
|
|
531
|
+
throw MarifoldError_1.MarifoldError.appInvalid(`Invalid App name '${name}'. Use kebab-case.`, sourcePath);
|
|
532
|
+
return {
|
|
533
|
+
name,
|
|
534
|
+
title: requireNonEmptyString(raw.title, 'app.title', state.sourceFile, state, sourcePath),
|
|
535
|
+
...(raw.version !== undefined ? { version: requireNonEmptyString(raw.version, 'app.version', state.sourceFile, state, sourcePath) } : {}),
|
|
536
|
+
...(raw.description !== undefined ? { description: requireString(raw.description, 'app.description', state.sourceFile, state, sourcePath) } : {}),
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
function flatten(items) {
|
|
540
|
+
return items.flatMap(item => [item, ...flatten(item.children ?? [])]);
|
|
541
|
+
}
|
|
542
|
+
function unwrap(node) {
|
|
543
|
+
let current = node;
|
|
544
|
+
while (ts.isParenthesizedExpression(current) || ts.isAsExpression(current) || ts.isSatisfiesExpression(current) || ts.isTypeAssertionExpression(current)) {
|
|
545
|
+
current = current.expression;
|
|
546
|
+
}
|
|
547
|
+
return current;
|
|
548
|
+
}
|
|
549
|
+
function builderName(expression, state) {
|
|
550
|
+
return ts.isIdentifier(expression) ? state.imports.get(expression.text) : undefined;
|
|
551
|
+
}
|
|
552
|
+
function propertyName(name, state, sourcePath) {
|
|
553
|
+
if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name))
|
|
554
|
+
return name.text;
|
|
555
|
+
throw invalidAt(state.sourceFile, name.pos, 'Computed property names are not allowed.', sourcePath);
|
|
556
|
+
}
|
|
557
|
+
function isTagged(value) {
|
|
558
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value) && '__kind' in value;
|
|
559
|
+
}
|
|
560
|
+
function requireTagged(value, kind, node, state, sourcePath) {
|
|
561
|
+
if (isTagged(value) && value.__kind === kind)
|
|
562
|
+
return value;
|
|
563
|
+
throw invalidAt(state.sourceFile, node.pos, `Expected ${kind} reference.`, sourcePath);
|
|
564
|
+
}
|
|
565
|
+
function requireObject(value, label, node, state, sourcePath) {
|
|
566
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value) && !isTagged(value))
|
|
567
|
+
return value;
|
|
568
|
+
throw invalidAt(state.sourceFile, node.pos, `Expected ${label} to be an object.`, sourcePath);
|
|
569
|
+
}
|
|
570
|
+
function requireArray(value, label, node, state, sourcePath) {
|
|
571
|
+
if (Array.isArray(value))
|
|
572
|
+
return value;
|
|
573
|
+
throw invalidAt(state.sourceFile, node.pos, `Expected ${label} to be an array.`, sourcePath);
|
|
574
|
+
}
|
|
575
|
+
function requireString(value, label, node, state, sourcePath) {
|
|
576
|
+
if (typeof value === 'string')
|
|
577
|
+
return value;
|
|
578
|
+
throw invalidAt(state.sourceFile, node.pos, `Expected ${label} to be a string.`, sourcePath);
|
|
579
|
+
}
|
|
580
|
+
function requireNonEmptyString(value, label, node, state, sourcePath) {
|
|
581
|
+
const text = requireString(value, label, node, state, sourcePath).trim();
|
|
582
|
+
if (!text)
|
|
583
|
+
throw invalidAt(state.sourceFile, node.pos, `${label} cannot be empty.`, sourcePath);
|
|
584
|
+
return text;
|
|
585
|
+
}
|
|
586
|
+
function requireBoolean(value, label, node, state, sourcePath) {
|
|
587
|
+
if (typeof value === 'boolean')
|
|
588
|
+
return value;
|
|
589
|
+
throw invalidAt(state.sourceFile, node.pos, `Expected ${label} to be a boolean.`, sourcePath);
|
|
590
|
+
}
|
|
591
|
+
function requireNonNegativeInteger(value, label, node, state, sourcePath) {
|
|
592
|
+
if (typeof value === 'number' && Number.isInteger(value) && value >= 0)
|
|
593
|
+
return value;
|
|
594
|
+
throw invalidAt(state.sourceFile, node.pos, `Expected ${label} to be a non-negative integer.`, sourcePath);
|
|
595
|
+
}
|
|
596
|
+
function requireName(value, kind, node, state, sourcePath) {
|
|
597
|
+
if (value.name)
|
|
598
|
+
return value.name;
|
|
599
|
+
throw invalidAt(state.sourceFile, node.pos, `The ${kind} reference must first be assigned to a const.`, sourcePath);
|
|
600
|
+
}
|
|
601
|
+
function requireNamedReference(value, kind, node, state, sourcePath, skillUsesRegisteredName = false) {
|
|
602
|
+
const tagged = requireTagged(value, kind, node, state, sourcePath);
|
|
603
|
+
if (skillUsesRegisteredName)
|
|
604
|
+
return requireString(tagged.skillName, 'registered skill name', node, state, sourcePath);
|
|
605
|
+
return requireName(tagged, kind, node, state, sourcePath);
|
|
606
|
+
}
|
|
607
|
+
function exactArgs(name, args, count, node, state, sourcePath) {
|
|
608
|
+
argsRange(name, args, count, count, node, state, sourcePath);
|
|
609
|
+
}
|
|
610
|
+
function argsRange(name, args, min, max, node, state, sourcePath) {
|
|
611
|
+
if (args.length < min || args.length > max) {
|
|
612
|
+
throw invalidAt(state.sourceFile, node.pos, `${name} expects ${min === max ? String(min) : `${min}-${max}`} argument(s).`, sourcePath);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
function rejectUnknown(object, allowed, label, node, state, sourcePath) {
|
|
616
|
+
const unknown = Object.keys(object).filter(key => !allowed.includes(key));
|
|
617
|
+
if (unknown.length > 0)
|
|
618
|
+
throw invalidAt(state.sourceFile, node.pos, `${label} does not support: ${unknown.join(', ')}.`, sourcePath);
|
|
619
|
+
}
|
|
620
|
+
function copyBoolean(source, key, target, state, sourcePath) {
|
|
621
|
+
if (source[key] !== undefined)
|
|
622
|
+
target[key] = requireBoolean(source[key], key, state.sourceFile, state, sourcePath);
|
|
623
|
+
}
|
|
624
|
+
function copyString(source, key, target, state, sourcePath) {
|
|
625
|
+
if (source[key] !== undefined)
|
|
626
|
+
target[key] = requireString(source[key], key, state.sourceFile, state, sourcePath);
|
|
627
|
+
}
|
|
628
|
+
function invalidAt(sourceFile, position, message, sourcePath) {
|
|
629
|
+
const location = sourceFile.getLineAndCharacterOfPosition(Math.max(0, position ?? 0));
|
|
630
|
+
return MarifoldError_1.MarifoldError.appInvalid(`${message} (${location.line + 1}:${location.character + 1})`, sourcePath);
|
|
631
|
+
}
|
|
632
|
+
//# sourceMappingURL=SkillAppCompiler.js.map
|