@jay-framework/stack-server-runtime 0.12.0 → 0.13.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/dist/index.d.ts +97 -5
- package/dist/index.js +305 -20
- package/package.json +12 -12
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,10 @@ import { AnyJayStackComponentDefinition, PageProps, AnySlowlyRenderResult, UrlPa
|
|
|
2
2
|
import { JayComponentCore } from '@jay-framework/component';
|
|
3
3
|
import { ViteDevServer } from 'vite';
|
|
4
4
|
import { JayRoute } from '@jay-framework/stack-route-scanner';
|
|
5
|
-
import { WithValidations, PluginManifest } from '@jay-framework/compiler-shared';
|
|
6
|
-
|
|
5
|
+
import { WithValidations, JsonSchemaProperty, PluginManifest } from '@jay-framework/compiler-shared';
|
|
6
|
+
export { JsonSchemaProperty } from '@jay-framework/compiler-shared';
|
|
7
|
+
import { Contract, HeadlessContractInfo, ForEachHeadlessInstance, DiscoveredHeadlessInstance } from '@jay-framework/compiler-jay-html';
|
|
8
|
+
export { ForEachHeadlessInstance } from '@jay-framework/compiler-jay-html';
|
|
7
9
|
import { JayRollupConfig } from '@jay-framework/rollup-plugin';
|
|
8
10
|
import { TrackByMap } from '@jay-framework/view-state-merge';
|
|
9
11
|
import { Coordinate } from '@jay-framework/runtime';
|
|
@@ -67,6 +69,50 @@ declare function runSlowlyChangingRender<Refs extends object, SlowVS extends obj
|
|
|
67
69
|
|
|
68
70
|
declare function renderFastChangingData(pageParams: object, pageProps: PageProps, carryForward: object, parts: Array<DevServerPagePart>): Promise<AnyFastRenderResult>;
|
|
69
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Action metadata loaded from .jay-action files.
|
|
74
|
+
*
|
|
75
|
+
* Uses the compiler's parseAction to parse .jay-action YAML into JayType,
|
|
76
|
+
* then converts JayType → JSON Schema for consumers (AI agent tool builders).
|
|
77
|
+
*
|
|
78
|
+
* Actions without .jay-action files are not exposed to AI agents.
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Input schema for an action (JSON Schema object type).
|
|
83
|
+
*/
|
|
84
|
+
interface ActionSchema {
|
|
85
|
+
type: 'object';
|
|
86
|
+
properties: Record<string, JsonSchemaProperty>;
|
|
87
|
+
required?: string[];
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Metadata loaded from a .jay-action file.
|
|
91
|
+
*/
|
|
92
|
+
interface ActionMetadata {
|
|
93
|
+
/** Action name (must match the export name in plugin code) */
|
|
94
|
+
name: string;
|
|
95
|
+
/** Human-readable description of what the action does */
|
|
96
|
+
description: string;
|
|
97
|
+
/** JSON Schema for the action's input parameters */
|
|
98
|
+
inputSchema: ActionSchema;
|
|
99
|
+
/** JSON Schema for the action's output (optional) */
|
|
100
|
+
outputSchema?: JsonSchemaProperty;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Parses a .jay-action YAML string into ActionMetadata.
|
|
104
|
+
* Uses the compiler's parseAction to produce JayType, then converts to JSON Schema.
|
|
105
|
+
*/
|
|
106
|
+
declare function parseActionMetadata(yamlContent: string, fileName: string): ActionMetadata | null;
|
|
107
|
+
/**
|
|
108
|
+
* Loads action metadata from a .jay-action file on disk.
|
|
109
|
+
*/
|
|
110
|
+
declare function loadActionMetadata(filePath: string): ActionMetadata | null;
|
|
111
|
+
/**
|
|
112
|
+
* Resolves the absolute path of a .jay-action file relative to a plugin directory.
|
|
113
|
+
*/
|
|
114
|
+
declare function resolveActionMetadataPath(actionPath: string, pluginDir: string): string;
|
|
115
|
+
|
|
70
116
|
/**
|
|
71
117
|
* Action registry for Jay Stack server-side action handling.
|
|
72
118
|
*
|
|
@@ -88,6 +134,9 @@ interface RegisteredAction {
|
|
|
88
134
|
services: any[];
|
|
89
135
|
/** The handler function */
|
|
90
136
|
handler: (input: any, ...services: any[]) => Promise<any>;
|
|
137
|
+
/** Optional metadata from .jay-action file (description, input/output schemas).
|
|
138
|
+
* Actions with metadata are exposed to AI agents; those without are not. */
|
|
139
|
+
metadata?: ActionMetadata;
|
|
91
140
|
}
|
|
92
141
|
/**
|
|
93
142
|
* Result of executing an action.
|
|
@@ -153,6 +202,24 @@ declare class ActionRegistry {
|
|
|
153
202
|
* @returns Array of registered action names
|
|
154
203
|
*/
|
|
155
204
|
getNames(): string[];
|
|
205
|
+
/**
|
|
206
|
+
* Attaches metadata from a .jay-action file to a registered action.
|
|
207
|
+
* Called during action discovery when a plugin declares action metadata.
|
|
208
|
+
*
|
|
209
|
+
* @param actionName - The action name
|
|
210
|
+
* @param metadata - Parsed ActionMetadata from .jay-action file
|
|
211
|
+
*/
|
|
212
|
+
setMetadata(actionName: string, metadata: ActionMetadata): void;
|
|
213
|
+
/**
|
|
214
|
+
* Gets all registered actions that have .jay-action metadata.
|
|
215
|
+
* These are the actions that should be exposed to AI agents.
|
|
216
|
+
*
|
|
217
|
+
* @returns Array of { actionName, metadata } for actions with metadata
|
|
218
|
+
*/
|
|
219
|
+
getActionsWithMetadata(): Array<{
|
|
220
|
+
actionName: string;
|
|
221
|
+
metadata: ActionMetadata;
|
|
222
|
+
}>;
|
|
156
223
|
/**
|
|
157
224
|
* Clears all registered actions.
|
|
158
225
|
*/
|
|
@@ -346,6 +413,8 @@ interface PluginWithInit {
|
|
|
346
413
|
initExport: string;
|
|
347
414
|
/** Dependencies from package.json (for ordering) */
|
|
348
415
|
dependencies: string[];
|
|
416
|
+
/** When true, plugin is loaded on every page regardless of usage in jay-html */
|
|
417
|
+
global: boolean;
|
|
349
418
|
}
|
|
350
419
|
/**
|
|
351
420
|
* Options for plugin init discovery.
|
|
@@ -383,7 +452,7 @@ declare function sortPluginsByDependencies(plugins: PluginWithInit[]): PluginWit
|
|
|
383
452
|
* @param viteServer - Vite server for SSR module loading (optional)
|
|
384
453
|
* @param verbose - Whether to log progress
|
|
385
454
|
*/
|
|
386
|
-
declare function executePluginServerInits(plugins: PluginWithInit[], viteServer?: ViteSSRLoader, verbose?: boolean): Promise<
|
|
455
|
+
declare function executePluginServerInits(plugins: PluginWithInit[], viteServer?: ViteSSRLoader, verbose?: boolean): Promise<Map<string, Error>>;
|
|
387
456
|
/**
|
|
388
457
|
* Information needed to generate client init script for a plugin.
|
|
389
458
|
*/
|
|
@@ -621,6 +690,8 @@ interface InstancePhaseData {
|
|
|
621
690
|
}>;
|
|
622
691
|
/** CarryForward per instance (keyed by coordinate path, e.g. "p1/product-card:0") */
|
|
623
692
|
carryForwards: Record<string, object>;
|
|
693
|
+
/** ForEach instances that need fast-phase per-item rendering */
|
|
694
|
+
forEachInstances?: ForEachHeadlessInstance[];
|
|
624
695
|
}
|
|
625
696
|
/**
|
|
626
697
|
* Result of running slowlyRender for all discovered headless instances.
|
|
@@ -643,6 +714,15 @@ interface InstanceSlowRenderResult {
|
|
|
643
714
|
* Shared between preRenderJayHtml (pre-render path) and handleDirectRequest (direct path).
|
|
644
715
|
*/
|
|
645
716
|
declare function slowRenderInstances(discovered: DiscoveredHeadlessInstance[], headlessInstanceComponents: HeadlessInstanceComponent[]): Promise<InstanceSlowRenderResult | undefined>;
|
|
717
|
+
/**
|
|
718
|
+
* Validate that forEach headless instances do not have a slow phase.
|
|
719
|
+
*
|
|
720
|
+
* Components with slowlyRender cannot be used inside forEach because
|
|
721
|
+
* forEach items are only known at request time, after slow rendering completes.
|
|
722
|
+
*
|
|
723
|
+
* @returns Array of validation error messages (empty if all valid)
|
|
724
|
+
*/
|
|
725
|
+
declare function validateForEachInstances(forEachInstances: ForEachHeadlessInstance[], headlessInstanceComponents: HeadlessInstanceComponent[]): string[];
|
|
646
726
|
|
|
647
727
|
/**
|
|
648
728
|
* Cache entry for pre-rendered jay-html
|
|
@@ -744,6 +824,13 @@ interface ContractsIndex {
|
|
|
744
824
|
jay_stack_version: string;
|
|
745
825
|
contracts: ContractIndexEntry[];
|
|
746
826
|
}
|
|
827
|
+
/** Action metadata entry in plugins-index.yaml */
|
|
828
|
+
interface ActionIndexEntry {
|
|
829
|
+
name: string;
|
|
830
|
+
description: string;
|
|
831
|
+
/** Path to the .jay-action file (relative to project root) */
|
|
832
|
+
path: string;
|
|
833
|
+
}
|
|
747
834
|
/** Entry for plugins-index.yaml (Design Log #85) */
|
|
748
835
|
interface PluginsIndexEntry {
|
|
749
836
|
name: string;
|
|
@@ -753,6 +840,8 @@ interface PluginsIndexEntry {
|
|
|
753
840
|
type: 'static' | 'dynamic';
|
|
754
841
|
path: string;
|
|
755
842
|
}>;
|
|
843
|
+
/** Actions with .jay-action metadata (exposed to AI agents) */
|
|
844
|
+
actions?: ActionIndexEntry[];
|
|
756
845
|
}
|
|
757
846
|
interface PluginsIndex {
|
|
758
847
|
materialized_at: string;
|
|
@@ -887,7 +976,7 @@ interface PluginSetupResult {
|
|
|
887
976
|
type PluginSetupHandler = (context: PluginSetupContext) => Promise<PluginSetupResult>;
|
|
888
977
|
/**
|
|
889
978
|
* Context passed to a plugin's references handler.
|
|
890
|
-
* Services
|
|
979
|
+
* Services may or may not be initialized — check initError if your handler needs them.
|
|
891
980
|
*/
|
|
892
981
|
interface PluginReferencesContext {
|
|
893
982
|
/** Plugin name (from plugin.yaml) */
|
|
@@ -898,6 +987,8 @@ interface PluginReferencesContext {
|
|
|
898
987
|
referencesDir: string;
|
|
899
988
|
/** Registered services */
|
|
900
989
|
services: Map<symbol, unknown>;
|
|
990
|
+
/** Present if this plugin's server init failed */
|
|
991
|
+
initError?: Error;
|
|
901
992
|
/** Whether --force flag was passed */
|
|
902
993
|
force: boolean;
|
|
903
994
|
}
|
|
@@ -981,8 +1072,9 @@ declare function executePluginSetup(plugin: PluginWithSetup, options: {
|
|
|
981
1072
|
declare function executePluginReferences(plugin: PluginWithReferences, options: {
|
|
982
1073
|
projectRoot: string;
|
|
983
1074
|
force: boolean;
|
|
1075
|
+
initError?: Error;
|
|
984
1076
|
viteServer?: ViteSSRLoader;
|
|
985
1077
|
verbose?: boolean;
|
|
986
1078
|
}): Promise<PluginReferencesResult>;
|
|
987
1079
|
|
|
988
|
-
export { type ActionDiscoveryOptions, type ActionDiscoveryResult, type ActionErrorResponse, type ActionExecutionResult, ActionRegistry, type ContractIndexEntry, type ContractsIndex, type DevServerPagePart, DevSlowlyChangingPhase, type GenerateClientScriptOptions, type HeadlessInstanceComponent, type InstancePhaseData, type InstanceSlowRenderResult, type LoadedPageParts, type MaterializeContractsOptions, type MaterializeResult, type PluginActionDiscoveryOptions, type PluginClientInitInfo, type PluginInitDiscoveryOptions, type PluginReferencesContext, type PluginReferencesHandler, type PluginReferencesResult, type PluginScanOptions, type PluginSetupContext, type PluginSetupHandler, type PluginSetupResult, type PluginWithInit, type PluginWithReferences, type PluginWithSetup, type PluginsIndex, type PluginsIndexEntry, type ProjectClientInitInfo, type RegisteredAction, type ScannedPlugin, SlowRenderCache, type SlowRenderCacheEntry, type SlowlyChangingPhase, type ViteSSRLoader, actionRegistry, clearActionRegistry, clearClientInitData, clearLifecycleCallbacks, clearServiceRegistry, discoverAllPluginActions, discoverAndRegisterActions, discoverPluginActions, discoverPluginsWithInit, discoverPluginsWithReferences, discoverPluginsWithSetup, executeAction, executePluginReferences, executePluginServerInits, executePluginSetup, generateClientScript, getActionCacheHeaders, getClientInitData, getClientInitDataForKey, getRegisteredAction, getRegisteredActionNames, getService, getServiceRegistry, hasAction, hasService, listContracts, loadPageParts, materializeContracts, onInit, onShutdown, preparePluginClientInits, registerAction, registerService, renderFastChangingData, resolveServices, runInitCallbacks, runLoadParams, runShutdownCallbacks, runSlowlyChangingRender, scanPlugins, setClientInitData, slowRenderInstances, sortPluginsByDependencies };
|
|
1080
|
+
export { type ActionDiscoveryOptions, type ActionDiscoveryResult, type ActionErrorResponse, type ActionExecutionResult, type ActionIndexEntry, type ActionMetadata, ActionRegistry, type ActionSchema, type ContractIndexEntry, type ContractsIndex, type DevServerPagePart, DevSlowlyChangingPhase, type GenerateClientScriptOptions, type HeadlessInstanceComponent, type InstancePhaseData, type InstanceSlowRenderResult, type LoadedPageParts, type MaterializeContractsOptions, type MaterializeResult, type PluginActionDiscoveryOptions, type PluginClientInitInfo, type PluginInitDiscoveryOptions, type PluginReferencesContext, type PluginReferencesHandler, type PluginReferencesResult, type PluginScanOptions, type PluginSetupContext, type PluginSetupHandler, type PluginSetupResult, type PluginWithInit, type PluginWithReferences, type PluginWithSetup, type PluginsIndex, type PluginsIndexEntry, type ProjectClientInitInfo, type RegisteredAction, type ScannedPlugin, SlowRenderCache, type SlowRenderCacheEntry, type SlowlyChangingPhase, type ViteSSRLoader, actionRegistry, clearActionRegistry, clearClientInitData, clearLifecycleCallbacks, clearServiceRegistry, discoverAllPluginActions, discoverAndRegisterActions, discoverPluginActions, discoverPluginsWithInit, discoverPluginsWithReferences, discoverPluginsWithSetup, executeAction, executePluginReferences, executePluginServerInits, executePluginSetup, generateClientScript, getActionCacheHeaders, getClientInitData, getClientInitDataForKey, getRegisteredAction, getRegisteredActionNames, getService, getServiceRegistry, hasAction, hasService, listContracts, loadActionMetadata, loadPageParts, materializeContracts, onInit, onShutdown, parseActionMetadata, preparePluginClientInits, registerAction, registerService, renderFastChangingData, resolveActionMetadataPath, resolveServices, runInitCallbacks, runLoadParams, runShutdownCallbacks, runSlowlyChangingRender, scanPlugins, setClientInitData, slowRenderInstances, sortPluginsByDependencies, validateForEachInstances };
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { notFound, partialRender, phaseOutput, isJayAction } from "@jay-framewor
|
|
|
8
8
|
import fs$1 from "node:fs/promises";
|
|
9
9
|
import * as path from "node:path";
|
|
10
10
|
import path__default from "node:path";
|
|
11
|
-
import { parseJayFile, JAY_IMPORT_RESOLVER } from "@jay-framework/compiler-jay-html";
|
|
11
|
+
import { parseJayFile, JAY_IMPORT_RESOLVER, parseAction } from "@jay-framework/compiler-jay-html";
|
|
12
12
|
import { createRequire } from "module";
|
|
13
13
|
import * as fs from "node:fs";
|
|
14
14
|
import { createRequire as createRequire$1 } from "node:module";
|
|
@@ -389,6 +389,22 @@ async function slowRenderInstances(discovered, headlessInstanceComponents) {
|
|
|
389
389
|
instancePhaseData: { discovered: discoveredForFast, carryForwards }
|
|
390
390
|
};
|
|
391
391
|
}
|
|
392
|
+
function validateForEachInstances(forEachInstances, headlessInstanceComponents) {
|
|
393
|
+
const componentByContractName = /* @__PURE__ */ new Map();
|
|
394
|
+
for (const comp of headlessInstanceComponents) {
|
|
395
|
+
componentByContractName.set(comp.contractName, comp);
|
|
396
|
+
}
|
|
397
|
+
const validations = [];
|
|
398
|
+
for (const instance of forEachInstances) {
|
|
399
|
+
const comp = componentByContractName.get(instance.contractName);
|
|
400
|
+
if (comp?.compDefinition.slowlyRender) {
|
|
401
|
+
validations.push(
|
|
402
|
+
`<jay:${instance.contractName}> inside forEach has a slow rendering phase. Headless components with slow phases cannot be used inside forEach because forEach items are only known at request time, after slow rendering completes. Use slowForEach instead, or remove the slow phase from the component.`
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return validations;
|
|
407
|
+
}
|
|
392
408
|
class ActionRegistry {
|
|
393
409
|
constructor() {
|
|
394
410
|
__publicField(this, "actions", /* @__PURE__ */ new Map());
|
|
@@ -434,6 +450,34 @@ class ActionRegistry {
|
|
|
434
450
|
getNames() {
|
|
435
451
|
return Array.from(this.actions.keys());
|
|
436
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* Attaches metadata from a .jay-action file to a registered action.
|
|
455
|
+
* Called during action discovery when a plugin declares action metadata.
|
|
456
|
+
*
|
|
457
|
+
* @param actionName - The action name
|
|
458
|
+
* @param metadata - Parsed ActionMetadata from .jay-action file
|
|
459
|
+
*/
|
|
460
|
+
setMetadata(actionName, metadata) {
|
|
461
|
+
const action = this.actions.get(actionName);
|
|
462
|
+
if (action) {
|
|
463
|
+
action.metadata = metadata;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Gets all registered actions that have .jay-action metadata.
|
|
468
|
+
* These are the actions that should be exposed to AI agents.
|
|
469
|
+
*
|
|
470
|
+
* @returns Array of { actionName, metadata } for actions with metadata
|
|
471
|
+
*/
|
|
472
|
+
getActionsWithMetadata() {
|
|
473
|
+
const result = [];
|
|
474
|
+
for (const action of this.actions.values()) {
|
|
475
|
+
if (action.metadata) {
|
|
476
|
+
result.push({ actionName: action.actionName, metadata: action.metadata });
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return result;
|
|
480
|
+
}
|
|
437
481
|
/**
|
|
438
482
|
* Clears all registered actions.
|
|
439
483
|
*/
|
|
@@ -565,6 +609,69 @@ new JayObjectType("Error", {
|
|
|
565
609
|
name: new JayAtomicType("string"),
|
|
566
610
|
stack: new JayAtomicType("string")
|
|
567
611
|
});
|
|
612
|
+
function isOptionalType(aType) {
|
|
613
|
+
return aType.kind === 13;
|
|
614
|
+
}
|
|
615
|
+
function isAtomicType(aType) {
|
|
616
|
+
return aType.kind === 0;
|
|
617
|
+
}
|
|
618
|
+
function isEnumType(aType) {
|
|
619
|
+
return aType.kind === 2;
|
|
620
|
+
}
|
|
621
|
+
function isImportedType(aType) {
|
|
622
|
+
return aType.kind === 4;
|
|
623
|
+
}
|
|
624
|
+
function isObjectType(aType) {
|
|
625
|
+
return aType.kind === 8;
|
|
626
|
+
}
|
|
627
|
+
function isArrayType(aType) {
|
|
628
|
+
return aType.kind === 9;
|
|
629
|
+
}
|
|
630
|
+
function jayTypeToJsonSchema(type) {
|
|
631
|
+
if (isOptionalType(type)) {
|
|
632
|
+
return jayTypeToJsonSchema(type.innerType);
|
|
633
|
+
}
|
|
634
|
+
if (isAtomicType(type)) {
|
|
635
|
+
const name = type.name.toLowerCase();
|
|
636
|
+
if (name === "string" || name === "number" || name === "boolean") {
|
|
637
|
+
return { type: name };
|
|
638
|
+
}
|
|
639
|
+
return { type: "string" };
|
|
640
|
+
}
|
|
641
|
+
if (isEnumType(type)) {
|
|
642
|
+
return { type: "string", enum: type.values };
|
|
643
|
+
}
|
|
644
|
+
if (isImportedType(type)) {
|
|
645
|
+
return { type: "object", description: `Contract: ${type.name}` };
|
|
646
|
+
}
|
|
647
|
+
if (isArrayType(type)) {
|
|
648
|
+
const itemSchema = jayTypeToJsonSchema(type.itemType);
|
|
649
|
+
if (itemSchema) {
|
|
650
|
+
return { type: "array", items: itemSchema };
|
|
651
|
+
}
|
|
652
|
+
return { type: "array" };
|
|
653
|
+
}
|
|
654
|
+
if (isObjectType(type)) {
|
|
655
|
+
const properties = {};
|
|
656
|
+
const required = [];
|
|
657
|
+
for (const [key, propType] of Object.entries(type.props)) {
|
|
658
|
+
const isOpt = isOptionalType(propType);
|
|
659
|
+
const schema = jayTypeToJsonSchema(propType);
|
|
660
|
+
if (schema) {
|
|
661
|
+
properties[key] = schema;
|
|
662
|
+
if (!isOpt) {
|
|
663
|
+
required.push(key);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return {
|
|
668
|
+
type: "object",
|
|
669
|
+
properties,
|
|
670
|
+
...required.length > 0 && { required }
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
return null;
|
|
674
|
+
}
|
|
568
675
|
var RuntimeMode = /* @__PURE__ */ ((RuntimeMode2) => {
|
|
569
676
|
RuntimeMode2["MainTrusted"] = "mainTrusted";
|
|
570
677
|
RuntimeMode2["MainSandbox"] = "mainSandbox";
|
|
@@ -614,6 +721,12 @@ const JAY_QUERY_PREFIX = "?jay-";
|
|
|
614
721
|
}
|
|
615
722
|
];
|
|
616
723
|
createRequire(import.meta.url);
|
|
724
|
+
function normalizeActionEntry(entry) {
|
|
725
|
+
if (typeof entry === "string") {
|
|
726
|
+
return { name: entry };
|
|
727
|
+
}
|
|
728
|
+
return { name: entry.name, action: entry.action };
|
|
729
|
+
}
|
|
617
730
|
function loadPluginManifest(pluginDir) {
|
|
618
731
|
const pluginYamlPath = path$1.join(pluginDir, "plugin.yaml");
|
|
619
732
|
if (!fs$2.existsSync(pluginYamlPath)) {
|
|
@@ -632,6 +745,62 @@ new Proxy(e, {
|
|
|
632
745
|
return t[r];
|
|
633
746
|
}
|
|
634
747
|
});
|
|
748
|
+
function parseActionMetadata(yamlContent, fileName) {
|
|
749
|
+
try {
|
|
750
|
+
const parsed = parseAction(yamlContent, fileName);
|
|
751
|
+
if (parsed.validations.length > 0) {
|
|
752
|
+
getLogger().warn(
|
|
753
|
+
`[ActionMetadata] ${fileName}: validation errors: ${parsed.validations.join(", ")}`
|
|
754
|
+
);
|
|
755
|
+
return null;
|
|
756
|
+
}
|
|
757
|
+
if (!parsed.val) {
|
|
758
|
+
getLogger().warn(`[ActionMetadata] ${fileName}: parsing returned no result`);
|
|
759
|
+
return null;
|
|
760
|
+
}
|
|
761
|
+
const action = parsed.val;
|
|
762
|
+
const inputJsonSchema = jayTypeToJsonSchema(action.inputType);
|
|
763
|
+
let inputSchema;
|
|
764
|
+
if (inputJsonSchema && inputJsonSchema.type === "object") {
|
|
765
|
+
inputSchema = {
|
|
766
|
+
type: "object",
|
|
767
|
+
properties: inputJsonSchema.properties || {},
|
|
768
|
+
...inputJsonSchema.required && inputJsonSchema.required.length > 0 && { required: inputJsonSchema.required }
|
|
769
|
+
};
|
|
770
|
+
} else {
|
|
771
|
+
inputSchema = { type: "object", properties: {} };
|
|
772
|
+
}
|
|
773
|
+
const metadata = {
|
|
774
|
+
name: action.name,
|
|
775
|
+
description: action.description,
|
|
776
|
+
inputSchema
|
|
777
|
+
};
|
|
778
|
+
if (action.outputType) {
|
|
779
|
+
const outputJsonSchema = jayTypeToJsonSchema(action.outputType);
|
|
780
|
+
if (outputJsonSchema) {
|
|
781
|
+
metadata.outputSchema = outputJsonSchema;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return metadata;
|
|
785
|
+
} catch (error) {
|
|
786
|
+
getLogger().error(
|
|
787
|
+
`[ActionMetadata] Failed to parse ${fileName}: ${error instanceof Error ? error.message : error}`
|
|
788
|
+
);
|
|
789
|
+
return null;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
function loadActionMetadata(filePath) {
|
|
793
|
+
if (!fs.existsSync(filePath)) {
|
|
794
|
+
getLogger().warn(`[ActionMetadata] File not found: ${filePath}`);
|
|
795
|
+
return null;
|
|
796
|
+
}
|
|
797
|
+
const yamlContent = fs.readFileSync(filePath, "utf-8");
|
|
798
|
+
const fileName = path.basename(filePath);
|
|
799
|
+
return parseActionMetadata(yamlContent, fileName);
|
|
800
|
+
}
|
|
801
|
+
function resolveActionMetadataPath(actionPath, pluginDir) {
|
|
802
|
+
return path.resolve(pluginDir, actionPath);
|
|
803
|
+
}
|
|
635
804
|
const require$2 = createRequire$1(import.meta.url);
|
|
636
805
|
async function discoverAndRegisterActions(options) {
|
|
637
806
|
const {
|
|
@@ -753,6 +922,7 @@ async function discoverNpmPluginActions(projectRoot, registry, verbose, viteServ
|
|
|
753
922
|
const actions = await registerNpmPluginActions(
|
|
754
923
|
packageName,
|
|
755
924
|
pluginConfig,
|
|
925
|
+
pluginDir,
|
|
756
926
|
registry,
|
|
757
927
|
verbose,
|
|
758
928
|
viteServer
|
|
@@ -776,7 +946,25 @@ function tryResolvePluginYaml(packageName, projectRoot) {
|
|
|
776
946
|
return null;
|
|
777
947
|
}
|
|
778
948
|
}
|
|
779
|
-
|
|
949
|
+
function resolveNpmActionMetadataPath(actionPath, packageName, pluginDir) {
|
|
950
|
+
if (!actionPath.startsWith(".")) {
|
|
951
|
+
try {
|
|
952
|
+
return require$2.resolve(`${packageName}/${actionPath}`, {
|
|
953
|
+
paths: [pluginDir]
|
|
954
|
+
});
|
|
955
|
+
} catch {
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
const resolved = resolveActionMetadataPath(actionPath, pluginDir);
|
|
959
|
+
if (fs.existsSync(resolved)) {
|
|
960
|
+
return resolved;
|
|
961
|
+
}
|
|
962
|
+
getLogger().warn(
|
|
963
|
+
`[Actions] Could not resolve .jay-action file "${actionPath}" for package "${packageName}"`
|
|
964
|
+
);
|
|
965
|
+
return null;
|
|
966
|
+
}
|
|
967
|
+
async function registerNpmPluginActions(packageName, pluginConfig, pluginDir, registry, verbose, viteServer) {
|
|
780
968
|
const registeredActions = [];
|
|
781
969
|
try {
|
|
782
970
|
let pluginModule;
|
|
@@ -785,15 +973,33 @@ async function registerNpmPluginActions(packageName, pluginConfig, registry, ver
|
|
|
785
973
|
} else {
|
|
786
974
|
pluginModule = await import(packageName);
|
|
787
975
|
}
|
|
788
|
-
for (const
|
|
976
|
+
for (const entry of pluginConfig.actions) {
|
|
977
|
+
const { name: actionName, action: actionPath } = normalizeActionEntry(entry);
|
|
789
978
|
const actionExport = pluginModule[actionName];
|
|
790
979
|
if (actionExport && isJayAction(actionExport)) {
|
|
791
980
|
registry.register(actionExport);
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
981
|
+
const registeredName = actionExport.actionName;
|
|
982
|
+
registeredActions.push(registeredName);
|
|
983
|
+
if (actionPath) {
|
|
984
|
+
const metadataFilePath = resolveNpmActionMetadataPath(
|
|
985
|
+
actionPath,
|
|
986
|
+
packageName,
|
|
987
|
+
pluginDir
|
|
796
988
|
);
|
|
989
|
+
if (metadataFilePath) {
|
|
990
|
+
const metadata = loadActionMetadata(metadataFilePath);
|
|
991
|
+
if (metadata) {
|
|
992
|
+
registry.setMetadata(registeredName, metadata);
|
|
993
|
+
if (verbose) {
|
|
994
|
+
getLogger().info(
|
|
995
|
+
`[Actions] Loaded metadata for "${registeredName}" from ${actionPath}`
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
if (verbose) {
|
|
1002
|
+
getLogger().info(`[Actions] Registered NPM plugin action: ${registeredName}`);
|
|
797
1003
|
}
|
|
798
1004
|
} else {
|
|
799
1005
|
getLogger().warn(
|
|
@@ -841,15 +1047,27 @@ async function discoverPluginActions(pluginPath, projectRoot, registry = actionR
|
|
|
841
1047
|
} else {
|
|
842
1048
|
pluginModule = await import(modulePath);
|
|
843
1049
|
}
|
|
844
|
-
for (const
|
|
1050
|
+
for (const entry of pluginConfig.actions) {
|
|
1051
|
+
const { name: actionName, action: actionPath } = normalizeActionEntry(entry);
|
|
845
1052
|
const actionExport = pluginModule[actionName];
|
|
846
1053
|
if (actionExport && isJayAction(actionExport)) {
|
|
847
1054
|
registry.register(actionExport);
|
|
848
|
-
|
|
1055
|
+
const registeredName = actionExport.actionName;
|
|
1056
|
+
registeredActions.push(registeredName);
|
|
1057
|
+
if (actionPath) {
|
|
1058
|
+
const metadataFilePath = resolveActionMetadataPath(actionPath, pluginPath);
|
|
1059
|
+
const metadata = loadActionMetadata(metadataFilePath);
|
|
1060
|
+
if (metadata) {
|
|
1061
|
+
registry.setMetadata(registeredName, metadata);
|
|
1062
|
+
if (verbose) {
|
|
1063
|
+
getLogger().info(
|
|
1064
|
+
`[Actions] Loaded metadata for "${registeredName}" from ${actionPath}`
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
849
1069
|
if (verbose) {
|
|
850
|
-
getLogger().info(
|
|
851
|
-
`[Actions] Registered plugin action: ${actionExport.actionName}`
|
|
852
|
-
);
|
|
1070
|
+
getLogger().info(`[Actions] Registered plugin action: ${registeredName}`);
|
|
853
1071
|
}
|
|
854
1072
|
} else {
|
|
855
1073
|
getLogger().warn(
|
|
@@ -999,7 +1217,8 @@ async function discoverPluginsWithInit(options) {
|
|
|
999
1217
|
isLocal: scanned.isLocal,
|
|
1000
1218
|
initModule: initConfig.module,
|
|
1001
1219
|
initExport: initConfig.export,
|
|
1002
|
-
dependencies: scanned.dependencies
|
|
1220
|
+
dependencies: scanned.dependencies,
|
|
1221
|
+
global: scanned.manifest.global === true
|
|
1003
1222
|
});
|
|
1004
1223
|
if (verbose) {
|
|
1005
1224
|
getLogger().info(`[PluginInit] Found plugin with init: ${scanned.name}`);
|
|
@@ -1073,6 +1292,7 @@ function sortPluginsByDependencies(plugins) {
|
|
|
1073
1292
|
return sorted;
|
|
1074
1293
|
}
|
|
1075
1294
|
async function executePluginServerInits(plugins, viteServer, verbose = false) {
|
|
1295
|
+
const initErrors = /* @__PURE__ */ new Map();
|
|
1076
1296
|
for (const plugin of plugins) {
|
|
1077
1297
|
try {
|
|
1078
1298
|
let modulePath;
|
|
@@ -1106,11 +1326,14 @@ async function executePluginServerInits(plugins, viteServer, verbose = false) {
|
|
|
1106
1326
|
}
|
|
1107
1327
|
}
|
|
1108
1328
|
} catch (error) {
|
|
1329
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
1330
|
+
initErrors.set(plugin.name, err);
|
|
1109
1331
|
getLogger().error(
|
|
1110
1332
|
`[PluginInit] Failed to execute server init for "${plugin.name}": ${error}`
|
|
1111
1333
|
);
|
|
1112
1334
|
}
|
|
1113
1335
|
}
|
|
1336
|
+
return initErrors;
|
|
1114
1337
|
}
|
|
1115
1338
|
function preparePluginClientInits(plugins) {
|
|
1116
1339
|
return plugins.map((plugin) => {
|
|
@@ -1361,8 +1584,7 @@ async function executeDynamicGenerator(plugin, config, projectRoot, services, ve
|
|
|
1361
1584
|
if (verbose) {
|
|
1362
1585
|
getLogger().info(` Executing generator...`);
|
|
1363
1586
|
}
|
|
1364
|
-
|
|
1365
|
-
return result;
|
|
1587
|
+
return await generator.generate(...resolvedServices);
|
|
1366
1588
|
}
|
|
1367
1589
|
function resolveStaticContractPath(plugin, contractSpec, projectRoot) {
|
|
1368
1590
|
const { pluginPath, isLocal, packageName } = plugin;
|
|
@@ -1384,6 +1606,25 @@ function resolveStaticContractPath(plugin, contractSpec, projectRoot) {
|
|
|
1384
1606
|
return path.join(pluginPath, contractSpec);
|
|
1385
1607
|
}
|
|
1386
1608
|
}
|
|
1609
|
+
function resolveActionFilePath(actionPath, packageName, pluginPath, isLocal, projectRoot) {
|
|
1610
|
+
if (!isLocal && !actionPath.startsWith(".")) {
|
|
1611
|
+
try {
|
|
1612
|
+
return require2.resolve(`${packageName}/${actionPath}`, {
|
|
1613
|
+
paths: [projectRoot]
|
|
1614
|
+
});
|
|
1615
|
+
} catch {
|
|
1616
|
+
const possiblePaths = [
|
|
1617
|
+
path.join(pluginPath, "dist", actionPath),
|
|
1618
|
+
path.join(pluginPath, "lib", actionPath),
|
|
1619
|
+
path.join(pluginPath, actionPath)
|
|
1620
|
+
];
|
|
1621
|
+
const found = possiblePaths.find((p) => fs.existsSync(p));
|
|
1622
|
+
return found || null;
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
const resolved = resolveActionMetadataPath(actionPath, pluginPath);
|
|
1626
|
+
return fs.existsSync(resolved) ? resolved : null;
|
|
1627
|
+
}
|
|
1387
1628
|
function toKebabCase(str) {
|
|
1388
1629
|
return str.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "");
|
|
1389
1630
|
}
|
|
@@ -1449,7 +1690,8 @@ async function materializeContracts(options, services = /* @__PURE__ */ new Map(
|
|
|
1449
1690
|
if (!pluginsIndexMap.has(plugin.name)) {
|
|
1450
1691
|
pluginsIndexMap.set(plugin.name, {
|
|
1451
1692
|
path: "./" + pluginRelPath.replace(/\\/g, "/"),
|
|
1452
|
-
contracts: []
|
|
1693
|
+
contracts: [],
|
|
1694
|
+
actions: []
|
|
1453
1695
|
});
|
|
1454
1696
|
}
|
|
1455
1697
|
pluginsIndexMap.get(plugin.name).contracts.push({
|
|
@@ -1499,7 +1741,8 @@ async function materializeContracts(options, services = /* @__PURE__ */ new Map(
|
|
|
1499
1741
|
if (!pluginsIndexMap.has(plugin.name)) {
|
|
1500
1742
|
pluginsIndexMap.set(plugin.name, {
|
|
1501
1743
|
path: "./" + pluginRelPath.replace(/\\/g, "/"),
|
|
1502
|
-
contracts: []
|
|
1744
|
+
contracts: [],
|
|
1745
|
+
actions: []
|
|
1503
1746
|
});
|
|
1504
1747
|
}
|
|
1505
1748
|
pluginsIndexMap.get(plugin.name).contracts.push({
|
|
@@ -1518,6 +1761,42 @@ async function materializeContracts(options, services = /* @__PURE__ */ new Map(
|
|
|
1518
1761
|
}
|
|
1519
1762
|
}
|
|
1520
1763
|
}
|
|
1764
|
+
if (manifest.actions && Array.isArray(manifest.actions)) {
|
|
1765
|
+
for (const entry of manifest.actions) {
|
|
1766
|
+
const { name: actionName, action: actionPath } = normalizeActionEntry(entry);
|
|
1767
|
+
if (!actionPath)
|
|
1768
|
+
continue;
|
|
1769
|
+
const metadataFilePath = resolveActionFilePath(
|
|
1770
|
+
actionPath,
|
|
1771
|
+
plugin.packageName,
|
|
1772
|
+
plugin.pluginPath,
|
|
1773
|
+
plugin.isLocal,
|
|
1774
|
+
projectRoot
|
|
1775
|
+
);
|
|
1776
|
+
if (!metadataFilePath)
|
|
1777
|
+
continue;
|
|
1778
|
+
const metadata = loadActionMetadata(metadataFilePath);
|
|
1779
|
+
if (!metadata)
|
|
1780
|
+
continue;
|
|
1781
|
+
const pluginRelPath = path.relative(projectRoot, plugin.pluginPath);
|
|
1782
|
+
if (!pluginsIndexMap.has(plugin.name)) {
|
|
1783
|
+
pluginsIndexMap.set(plugin.name, {
|
|
1784
|
+
path: "./" + pluginRelPath.replace(/\\/g, "/"),
|
|
1785
|
+
contracts: [],
|
|
1786
|
+
actions: []
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1789
|
+
const actionRelPath = path.relative(projectRoot, metadataFilePath);
|
|
1790
|
+
pluginsIndexMap.get(plugin.name).actions.push({
|
|
1791
|
+
name: metadata.name,
|
|
1792
|
+
description: metadata.description,
|
|
1793
|
+
path: "./" + actionRelPath.replace(/\\/g, "/")
|
|
1794
|
+
});
|
|
1795
|
+
if (verbose) {
|
|
1796
|
+
getLogger().info(` 🔧 Action: ${metadata.name} (${actionPath})`);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1521
1800
|
}
|
|
1522
1801
|
const index = {
|
|
1523
1802
|
materialized_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -1533,7 +1812,8 @@ async function materializeContracts(options, services = /* @__PURE__ */ new Map(
|
|
|
1533
1812
|
plugins: Array.from(pluginsIndexMap.entries()).map(([name, data]) => ({
|
|
1534
1813
|
name,
|
|
1535
1814
|
path: data.path,
|
|
1536
|
-
contracts: data.contracts
|
|
1815
|
+
contracts: data.contracts,
|
|
1816
|
+
...data.actions.length > 0 && { actions: data.actions }
|
|
1537
1817
|
}))
|
|
1538
1818
|
};
|
|
1539
1819
|
const pluginsIndexPath = path.join(outputDir, "plugins-index.yaml");
|
|
@@ -1673,13 +1953,14 @@ async function executePluginSetup(plugin, options) {
|
|
|
1673
1953
|
return handler(context);
|
|
1674
1954
|
}
|
|
1675
1955
|
async function executePluginReferences(plugin, options) {
|
|
1676
|
-
const { projectRoot, force, viteServer } = options;
|
|
1956
|
+
const { projectRoot, force, initError, viteServer } = options;
|
|
1677
1957
|
const referencesDir = path.join(projectRoot, "agent-kit", "references", plugin.name);
|
|
1678
1958
|
const context = {
|
|
1679
1959
|
pluginName: plugin.name,
|
|
1680
1960
|
projectRoot,
|
|
1681
1961
|
referencesDir,
|
|
1682
1962
|
services: getServiceRegistry(),
|
|
1963
|
+
initError,
|
|
1683
1964
|
force
|
|
1684
1965
|
};
|
|
1685
1966
|
const handler = await loadHandler(
|
|
@@ -1751,14 +2032,17 @@ export {
|
|
|
1751
2032
|
hasAction,
|
|
1752
2033
|
hasService,
|
|
1753
2034
|
listContracts,
|
|
2035
|
+
loadActionMetadata,
|
|
1754
2036
|
loadPageParts,
|
|
1755
2037
|
materializeContracts,
|
|
1756
2038
|
onInit,
|
|
1757
2039
|
onShutdown,
|
|
2040
|
+
parseActionMetadata,
|
|
1758
2041
|
preparePluginClientInits,
|
|
1759
2042
|
registerAction,
|
|
1760
2043
|
registerService,
|
|
1761
2044
|
renderFastChangingData,
|
|
2045
|
+
resolveActionMetadataPath,
|
|
1762
2046
|
resolveServices,
|
|
1763
2047
|
runInitCallbacks,
|
|
1764
2048
|
runLoadParams,
|
|
@@ -1767,5 +2051,6 @@ export {
|
|
|
1767
2051
|
scanPlugins,
|
|
1768
2052
|
setClientInitData,
|
|
1769
2053
|
slowRenderInstances,
|
|
1770
|
-
sortPluginsByDependencies
|
|
2054
|
+
sortPluginsByDependencies,
|
|
2055
|
+
validateForEachInstances
|
|
1771
2056
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/stack-server-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.mts",
|
|
@@ -26,20 +26,20 @@
|
|
|
26
26
|
"test:watch": "vitest"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@jay-framework/compiler-jay-html": "^0.
|
|
30
|
-
"@jay-framework/compiler-shared": "^0.
|
|
31
|
-
"@jay-framework/component": "^0.
|
|
32
|
-
"@jay-framework/fullstack-component": "^0.
|
|
33
|
-
"@jay-framework/logger": "^0.
|
|
34
|
-
"@jay-framework/runtime": "^0.
|
|
35
|
-
"@jay-framework/stack-route-scanner": "^0.
|
|
36
|
-
"@jay-framework/view-state-merge": "^0.
|
|
29
|
+
"@jay-framework/compiler-jay-html": "^0.13.0",
|
|
30
|
+
"@jay-framework/compiler-shared": "^0.13.0",
|
|
31
|
+
"@jay-framework/component": "^0.13.0",
|
|
32
|
+
"@jay-framework/fullstack-component": "^0.13.0",
|
|
33
|
+
"@jay-framework/logger": "^0.13.0",
|
|
34
|
+
"@jay-framework/runtime": "^0.13.0",
|
|
35
|
+
"@jay-framework/stack-route-scanner": "^0.13.0",
|
|
36
|
+
"@jay-framework/view-state-merge": "^0.13.0",
|
|
37
37
|
"yaml": "^2.3.4"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@jay-framework/dev-environment": "^0.
|
|
41
|
-
"@jay-framework/jay-cli": "^0.
|
|
42
|
-
"@jay-framework/stack-client-runtime": "^0.
|
|
40
|
+
"@jay-framework/dev-environment": "^0.13.0",
|
|
41
|
+
"@jay-framework/jay-cli": "^0.13.0",
|
|
42
|
+
"@jay-framework/stack-client-runtime": "^0.13.0",
|
|
43
43
|
"@types/express": "^5.0.2",
|
|
44
44
|
"@types/node": "^22.15.21",
|
|
45
45
|
"nodemon": "^3.0.3",
|