@jay-framework/stack-server-runtime 0.11.0 → 0.12.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.
Files changed (3) hide show
  1. package/dist/index.d.ts +337 -29
  2. package/dist/index.js +672 -89
  3. package/package.json +13 -11
package/dist/index.d.ts CHANGED
@@ -2,15 +2,34 @@ 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 } from '@jay-framework/compiler-shared';
5
+ import { WithValidations, PluginManifest } from '@jay-framework/compiler-shared';
6
+ import { Contract, HeadlessContractInfo, DiscoveredHeadlessInstance } from '@jay-framework/compiler-jay-html';
6
7
  import { JayRollupConfig } from '@jay-framework/rollup-plugin';
7
8
  import { TrackByMap } from '@jay-framework/view-state-merge';
9
+ import { Coordinate } from '@jay-framework/runtime';
8
10
 
9
11
  interface DevServerPagePart {
10
12
  compDefinition: AnyJayStackComponentDefinition;
11
13
  key?: string;
12
14
  clientImport: string;
13
15
  clientPart: string;
16
+ /** Contract metadata for dynamic contract components */
17
+ contractInfo?: {
18
+ contractName: string;
19
+ metadata?: Record<string, unknown>;
20
+ };
21
+ }
22
+ /**
23
+ * Instance-only headless component (no key attribute).
24
+ * Used for server-side phase orchestration of `<jay:xxx>` instances.
25
+ */
26
+ interface HeadlessInstanceComponent {
27
+ /** Contract name from the script tag (e.g., "product-card") */
28
+ contractName: string;
29
+ /** Component definition for calling slowlyRender/fastRender */
30
+ compDefinition: AnyJayStackComponentDefinition;
31
+ /** Parsed contract (for phase detection in slow render Pass 2) */
32
+ contract: Contract;
14
33
  }
15
34
  interface LoadedPageParts {
16
35
  parts: DevServerPagePart[];
@@ -20,6 +39,10 @@ interface LoadedPageParts {
20
39
  clientTrackByMap?: Record<string, string>;
21
40
  /** NPM package names used on this page (for filtering plugin inits) */
22
41
  usedPackages: Set<string>;
42
+ /** Headless contracts for slow rendering (already loaded by parseJayFile) */
43
+ headlessContracts: HeadlessContractInfo[];
44
+ /** Instance-only headless components (no key) for server-side phase orchestration */
45
+ headlessInstanceComponents: HeadlessInstanceComponent[];
23
46
  }
24
47
  interface LoadPagePartsOptions {
25
48
  /**
@@ -191,33 +214,6 @@ declare function executeAction<T = any>(actionName: string, input: unknown): Pro
191
214
  * @deprecated Use actionRegistry.getCacheHeaders() instead
192
215
  */
193
216
  declare function getActionCacheHeaders(actionName: string): string | undefined;
194
- /**
195
- * Executes an action directly with proper service injection.
196
- * Use this when calling actions from backend code (e.g., render phases).
197
- *
198
- * Unlike calling the action directly (which bypasses service injection),
199
- * this function resolves services and calls the handler correctly.
200
- *
201
- * @param action - The JayAction to execute (with definition metadata)
202
- * @param input - The input data for the action
203
- * @returns The action result (throws on error)
204
- *
205
- * @example
206
- * ```typescript
207
- * // In a render phase
208
- * import { runAction } from '@jay-framework/stack-server-runtime';
209
- * import { searchProducts } from '../actions/stores-actions';
210
- *
211
- * async function renderFastChanging(props, slowCarryForward, wixStores) {
212
- * // ✅ Correct - services are injected
213
- * const result = await runAction(searchProducts, { query: '', pageSize: 12 });
214
- *
215
- * // ❌ Wrong - services are NOT injected (passes empty array)
216
- * // const result = await searchProducts({ query: '', pageSize: 12 });
217
- * }
218
- * ```
219
- */
220
- declare function runAction<I, O>(action: JayAction<I, O> & JayActionDefinition<I, O, any[]>, input: I): Promise<O>;
221
217
 
222
218
  /**
223
219
  * Action discovery and auto-registration for Jay Stack.
@@ -489,6 +485,11 @@ declare function hasService<ServiceType>(marker: ServiceMarker<ServiceType>): bo
489
485
  * Internal API used by dev-server during hot reload.
490
486
  */
491
487
  declare function clearServiceRegistry(): void;
488
+ /**
489
+ * Returns the internal service registry map.
490
+ * Internal API used by contract materializer to pass services to dynamic generators.
491
+ */
492
+ declare function getServiceRegistry(): Map<symbol, any>;
492
493
  /**
493
494
  * Resolves an array of service markers to their registered instances.
494
495
  * Used by the runtime to inject services into render functions.
@@ -598,6 +599,51 @@ declare function getClientInitDataForKey(key: string): Record<string, any>;
598
599
  */
599
600
  declare function clearClientInitData(): void;
600
601
 
602
+ /**
603
+ * Server-side slow render orchestration for headless component instances.
604
+ *
605
+ * Given discovered instances (from discoverHeadlessInstances) and their
606
+ * component definitions, runs slowlyRender for each instance and collects
607
+ * the results for downstream consumers (pre-render pipeline, direct mode, fast phase).
608
+ */
609
+
610
+ /**
611
+ * Data needed by the fast phase to render headless instances.
612
+ * Stored in carryForward.__instances so the fast phase can access it
613
+ * (both in pre-render and cached flows).
614
+ */
615
+ interface InstancePhaseData {
616
+ /** Discovered instances with their props and coordinates */
617
+ discovered: Array<{
618
+ contractName: string;
619
+ props: Record<string, string>;
620
+ coordinate: Coordinate;
621
+ }>;
622
+ /** CarryForward per instance (keyed by coordinate path, e.g. "p1/product-card:0") */
623
+ carryForwards: Record<string, object>;
624
+ }
625
+ /**
626
+ * Result of running slowlyRender for all discovered headless instances.
627
+ */
628
+ interface InstanceSlowRenderResult {
629
+ /** Resolved data for each instance (for resolveHeadlessInstances pass 2) */
630
+ resolvedData: Array<{
631
+ coordinate: Coordinate;
632
+ contract: Contract;
633
+ slowViewState: Record<string, unknown>;
634
+ }>;
635
+ /** Per-instance slow ViewState keyed by coordinate (for direct mode merge) */
636
+ slowViewStates: Record<string, object>;
637
+ /** Phase data for the fast render phase */
638
+ instancePhaseData: InstancePhaseData;
639
+ }
640
+ /**
641
+ * Run slowlyRender for each discovered headless instance.
642
+ *
643
+ * Shared between preRenderJayHtml (pre-render path) and handleDirectRequest (direct path).
644
+ */
645
+ declare function slowRenderInstances(discovered: DiscoveredHeadlessInstance[], headlessInstanceComponents: HeadlessInstanceComponent[]): Promise<InstanceSlowRenderResult | undefined>;
646
+
601
647
  /**
602
648
  * Cache entry for pre-rendered jay-html
603
649
  */
@@ -677,4 +723,266 @@ declare class SlowRenderCache {
677
723
  getCachedPaths(): string[];
678
724
  }
679
725
 
680
- export { type ActionDiscoveryOptions, type ActionDiscoveryResult, type ActionErrorResponse, type ActionExecutionResult, ActionRegistry, type DevServerPagePart, DevSlowlyChangingPhase, type GenerateClientScriptOptions, type LoadedPageParts, type PluginActionDiscoveryOptions, type PluginClientInitInfo, type PluginInitDiscoveryOptions, type PluginWithInit, type ProjectClientInitInfo, type RegisteredAction, SlowRenderCache, type SlowRenderCacheEntry, type SlowlyChangingPhase, type ViteSSRLoader, actionRegistry, clearActionRegistry, clearClientInitData, clearLifecycleCallbacks, clearServiceRegistry, discoverAllPluginActions, discoverAndRegisterActions, discoverPluginActions, discoverPluginsWithInit, executeAction, executePluginServerInits, generateClientScript, getActionCacheHeaders, getClientInitData, getClientInitDataForKey, getRegisteredAction, getRegisteredActionNames, getService, hasAction, hasService, loadPageParts, onInit, onShutdown, preparePluginClientInits, registerAction, registerService, renderFastChangingData, resolveServices, runAction, runInitCallbacks, runLoadParams, runShutdownCallbacks, runSlowlyChangingRender, setClientInitData, sortPluginsByDependencies };
726
+ /**
727
+ * Contract Materializer
728
+ *
729
+ * Materializes dynamic contracts to disk for agent discovery.
730
+ * Also creates an index file listing both static and dynamic contracts.
731
+ *
732
+ * @see Design Log #80 - Materializing Dynamic Contracts for Agentic Page Generation
733
+ */
734
+
735
+ interface ContractIndexEntry {
736
+ plugin: string;
737
+ name: string;
738
+ type: 'static' | 'dynamic';
739
+ path: string;
740
+ metadata?: Record<string, unknown>;
741
+ }
742
+ interface ContractsIndex {
743
+ materialized_at: string;
744
+ jay_stack_version: string;
745
+ contracts: ContractIndexEntry[];
746
+ }
747
+ /** Entry for plugins-index.yaml (Design Log #85) */
748
+ interface PluginsIndexEntry {
749
+ name: string;
750
+ path: string;
751
+ contracts: Array<{
752
+ name: string;
753
+ type: 'static' | 'dynamic';
754
+ path: string;
755
+ }>;
756
+ }
757
+ interface PluginsIndex {
758
+ materialized_at: string;
759
+ jay_stack_version: string;
760
+ plugins: PluginsIndexEntry[];
761
+ }
762
+ interface MaterializeContractsOptions {
763
+ projectRoot: string;
764
+ outputDir?: string;
765
+ force?: boolean;
766
+ dynamicOnly?: boolean;
767
+ pluginFilter?: string;
768
+ verbose?: boolean;
769
+ /** Optional Vite server for TypeScript support */
770
+ viteServer?: ViteSSRLoader;
771
+ }
772
+ interface MaterializeResult {
773
+ index: ContractsIndex;
774
+ staticCount: number;
775
+ dynamicCount: number;
776
+ outputDir: string;
777
+ }
778
+ /**
779
+ * Materializes all contracts (static references + dynamic generated) to disk.
780
+ *
781
+ * @param options - Materialization options
782
+ * @param services - Map of service markers to service instances (from init.ts)
783
+ */
784
+ declare function materializeContracts(options: MaterializeContractsOptions, services?: Map<symbol, unknown>): Promise<MaterializeResult>;
785
+ /**
786
+ * Lists contracts without writing files (for --list mode)
787
+ */
788
+ declare function listContracts(options: MaterializeContractsOptions): Promise<ContractsIndex>;
789
+
790
+ /**
791
+ * Plugin Scanner
792
+ *
793
+ * Shared utility for scanning Jay plugins in a project.
794
+ * Used by both plugin-init-discovery and contract-materializer.
795
+ */
796
+
797
+ /**
798
+ * Basic plugin information from scanning.
799
+ */
800
+ interface ScannedPlugin {
801
+ /** Plugin name (from plugin.yaml or directory/package name) */
802
+ name: string;
803
+ /** Full path to plugin directory */
804
+ pluginPath: string;
805
+ /** Package name for NPM plugins, or directory name for local plugins */
806
+ packageName: string;
807
+ /** Whether this is a local plugin (src/plugins/) or NPM package */
808
+ isLocal: boolean;
809
+ /** Parsed plugin.yaml manifest */
810
+ manifest: PluginManifest;
811
+ /** Dependencies from package.json (for ordering) */
812
+ dependencies: string[];
813
+ }
814
+ /**
815
+ * Options for plugin scanning.
816
+ */
817
+ interface PluginScanOptions {
818
+ /** Project root directory */
819
+ projectRoot: string;
820
+ /** Whether to log discovery progress */
821
+ verbose?: boolean;
822
+ /** Whether to include dev dependencies (default: false) */
823
+ includeDevDeps?: boolean;
824
+ /** Whether to discover transitive plugin dependencies (default: false) */
825
+ discoverTransitive?: boolean;
826
+ }
827
+ /**
828
+ * Scans for all Jay plugins in a project.
829
+ *
830
+ * Scans both local plugins (src/plugins/) and NPM plugins (node_modules/).
831
+ * Returns basic plugin information that can be used by different consumers
832
+ * (init discovery, contract materialization, etc.)
833
+ *
834
+ * @param options - Scanning options
835
+ * @returns Map of package/directory name to plugin info
836
+ */
837
+ declare function scanPlugins(options: PluginScanOptions): Promise<Map<string, ScannedPlugin>>;
838
+
839
+ /**
840
+ * Plugin Setup and References (Design Log #87)
841
+ *
842
+ * Two separate concerns:
843
+ * - **Setup** (jay-stack setup): Config creation + credential/service validation
844
+ * - **References** (jay-stack agent-kit): Generate discovery data using live services
845
+ *
846
+ * Setup flow:
847
+ * 1. Scan plugins for `setup.handler` in plugin.yaml
848
+ * 2. Run init for all plugins (dependency-ordered)
849
+ * 3. For each target plugin: load setup handler → call it → report result
850
+ *
851
+ * References flow (called by agent-kit after materializing contracts):
852
+ * 1. Scan plugins for `setup.references` in plugin.yaml
853
+ * 2. Services are already initialized (agent-kit does this for contract materialization)
854
+ * 3. For each plugin: load references handler → call it → report result
855
+ */
856
+
857
+ /**
858
+ * Context passed to a plugin's setup handler.
859
+ * Setup handles config creation and service validation only.
860
+ */
861
+ interface PluginSetupContext {
862
+ /** Plugin name (from plugin.yaml) */
863
+ pluginName: string;
864
+ /** Project root directory */
865
+ projectRoot: string;
866
+ /** Config directory path (from .jay configBase, defaults to ./config) */
867
+ configDir: string;
868
+ /** Registered services (may be empty if init failed) */
869
+ services: Map<symbol, unknown>;
870
+ /** Present if plugin init failed */
871
+ initError?: Error;
872
+ /** Whether --force flag was passed */
873
+ force: boolean;
874
+ }
875
+ /**
876
+ * Result returned by a plugin's setup handler.
877
+ */
878
+ interface PluginSetupResult {
879
+ /** Overall status */
880
+ status: 'configured' | 'needs-config' | 'error';
881
+ /** Config files created (relative to project root) */
882
+ configCreated?: string[];
883
+ /** Human-readable status message */
884
+ message?: string;
885
+ }
886
+ /** A plugin's setup handler function signature. */
887
+ type PluginSetupHandler = (context: PluginSetupContext) => Promise<PluginSetupResult>;
888
+ /**
889
+ * Context passed to a plugin's references handler.
890
+ * Services are guaranteed to be initialized (agent-kit already does this).
891
+ */
892
+ interface PluginReferencesContext {
893
+ /** Plugin name (from plugin.yaml) */
894
+ pluginName: string;
895
+ /** Project root directory */
896
+ projectRoot: string;
897
+ /** Directory for this plugin's reference data (agent-kit/references/<plugin>/) */
898
+ referencesDir: string;
899
+ /** Registered services */
900
+ services: Map<symbol, unknown>;
901
+ /** Whether --force flag was passed */
902
+ force: boolean;
903
+ }
904
+ /**
905
+ * Result returned by a plugin's references handler.
906
+ */
907
+ interface PluginReferencesResult {
908
+ /** Reference files created (relative to project root) */
909
+ referencesCreated: string[];
910
+ /** Human-readable status message */
911
+ message?: string;
912
+ }
913
+ /** A plugin's references handler function signature. */
914
+ type PluginReferencesHandler = (context: PluginReferencesContext) => Promise<PluginReferencesResult>;
915
+ /**
916
+ * Information about a discovered plugin with a setup handler.
917
+ */
918
+ interface PluginWithSetup {
919
+ /** Plugin name from plugin.yaml */
920
+ name: string;
921
+ /** Plugin path (directory containing plugin.yaml) */
922
+ pluginPath: string;
923
+ /** Package name for NPM plugins, or path for local plugins */
924
+ packageName: string;
925
+ /** Whether this is a local plugin */
926
+ isLocal: boolean;
927
+ /** Setup handler export name or relative path */
928
+ setupHandler: string;
929
+ /** Setup description from plugin.yaml */
930
+ setupDescription?: string;
931
+ /** Dependencies from package.json (for ordering) */
932
+ dependencies: string[];
933
+ }
934
+ /**
935
+ * Information about a discovered plugin with a references handler.
936
+ */
937
+ interface PluginWithReferences {
938
+ /** Plugin name from plugin.yaml */
939
+ name: string;
940
+ /** Plugin path (directory containing plugin.yaml) */
941
+ pluginPath: string;
942
+ /** Package name for NPM plugins, or path for local plugins */
943
+ packageName: string;
944
+ /** Whether this is a local plugin */
945
+ isLocal: boolean;
946
+ /** References handler export name */
947
+ referencesHandler: string;
948
+ /** Dependencies from package.json (for ordering) */
949
+ dependencies: string[];
950
+ }
951
+ /**
952
+ * Discovers all plugins that have a `setup.handler` in plugin.yaml.
953
+ */
954
+ declare function discoverPluginsWithSetup(options: {
955
+ projectRoot: string;
956
+ verbose?: boolean;
957
+ pluginFilter?: string;
958
+ }): Promise<PluginWithSetup[]>;
959
+ /**
960
+ * Discovers all plugins that have a `setup.references` in plugin.yaml.
961
+ */
962
+ declare function discoverPluginsWithReferences(options: {
963
+ projectRoot: string;
964
+ verbose?: boolean;
965
+ pluginFilter?: string;
966
+ }): Promise<PluginWithReferences[]>;
967
+ /**
968
+ * Loads and executes a plugin's setup handler.
969
+ */
970
+ declare function executePluginSetup(plugin: PluginWithSetup, options: {
971
+ projectRoot: string;
972
+ configDir: string;
973
+ force: boolean;
974
+ initError?: Error;
975
+ viteServer?: ViteSSRLoader;
976
+ verbose?: boolean;
977
+ }): Promise<PluginSetupResult>;
978
+ /**
979
+ * Loads and executes a plugin's references handler.
980
+ */
981
+ declare function executePluginReferences(plugin: PluginWithReferences, options: {
982
+ projectRoot: string;
983
+ force: boolean;
984
+ viteServer?: ViteSSRLoader;
985
+ verbose?: boolean;
986
+ }): Promise<PluginReferencesResult>;
987
+
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 };