@jay-framework/stack-server-runtime 0.15.5 → 0.16.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 +121 -14
- package/dist/index.js +529 -62
- package/package.json +13 -13
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { AnyJayStackComponentDefinition, PageProps, AnySlowlyRenderResult, UrlParams,
|
|
2
|
-
import { JayComponentCore } from '@jay-framework/component';
|
|
1
|
+
import { AnyJayStackComponentDefinition, PageProps, AnySlowlyRenderResult, UrlParams, AnyFastRenderResult, HttpMethod, CacheOptions, JayAction, JayActionDefinition, JayStreamAction, JayStreamActionDefinition, HeadTag, ServiceMarker } from '@jay-framework/fullstack-component';
|
|
3
2
|
import { ViteDevServer } from 'vite';
|
|
4
3
|
import { JayRoute } from '@jay-framework/stack-route-scanner';
|
|
5
4
|
import { WithValidations, JsonSchemaProperty, PluginManifest } from '@jay-framework/compiler-shared';
|
|
@@ -49,6 +48,10 @@ interface LoadedPageParts {
|
|
|
49
48
|
discoveredInstances: DiscoveredHeadlessInstance[];
|
|
50
49
|
/** Discovered forEach <jay:xxx> instances from the jay-html (DL#109) */
|
|
51
50
|
forEachInstances: ForEachHeadlessInstance[];
|
|
51
|
+
/** Absolute paths to linked CSS files (from <link rel="stylesheet">) for dev-server watching */
|
|
52
|
+
linkedCssFiles: string[];
|
|
53
|
+
/** Absolute paths to headfull FS component jay-html files for dev-server watching */
|
|
54
|
+
linkedComponentFiles: string[];
|
|
52
55
|
}
|
|
53
56
|
interface LoadPagePartsOptions {
|
|
54
57
|
/**
|
|
@@ -72,8 +75,11 @@ interface SlowlyChangingPhase {
|
|
|
72
75
|
declare class DevSlowlyChangingPhase implements SlowlyChangingPhase {
|
|
73
76
|
runSlowlyForPage(pageParams: UrlParams, pageProps: PageProps, parts: Array<DevServerPagePart>, discoveredInstances?: DiscoveredHeadlessInstance[], headlessInstanceComponents?: HeadlessInstanceComponent[], jayHtmlPath?: string): Promise<AnySlowlyRenderResult>;
|
|
74
77
|
}
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Run loadParams for all parts (page + keyed headless components).
|
|
80
|
+
* Yields param batches from each part that has loadParams.
|
|
81
|
+
*/
|
|
82
|
+
declare function runLoadParams(parts: DevServerPagePart[]): AsyncGenerator<Record<string, string>[]>;
|
|
77
83
|
|
|
78
84
|
/**
|
|
79
85
|
* Server-side slow render orchestration for headless component instances.
|
|
@@ -97,6 +103,8 @@ interface InstancePhaseData {
|
|
|
97
103
|
}>;
|
|
98
104
|
/** CarryForward per instance (keyed by coordinate path, e.g. "p1/product-card:0") */
|
|
99
105
|
carryForwards: Record<string, object>;
|
|
106
|
+
/** Slow ViewState per instance (keyed by coordinate path) */
|
|
107
|
+
slowViewStates?: Record<string, object>;
|
|
100
108
|
/** ForEach instances that need fast-phase per-item rendering */
|
|
101
109
|
forEachInstances?: ForEachHeadlessInstance[];
|
|
102
110
|
}
|
|
@@ -185,23 +193,43 @@ declare function resolveActionMetadataPath(actionPath: string, pluginDir: string
|
|
|
185
193
|
*/
|
|
186
194
|
|
|
187
195
|
/**
|
|
188
|
-
*
|
|
196
|
+
* Base fields shared by all registered action types.
|
|
189
197
|
*/
|
|
190
|
-
interface
|
|
198
|
+
interface RegisteredActionBase {
|
|
191
199
|
/** Unique action name */
|
|
192
200
|
actionName: string;
|
|
193
201
|
/** HTTP method */
|
|
194
202
|
method: HttpMethod;
|
|
195
|
-
/** Cache options (for GET requests) */
|
|
196
|
-
cacheOptions?: CacheOptions;
|
|
197
203
|
/** Service markers for dependency injection */
|
|
198
204
|
services: any[];
|
|
199
|
-
/** The handler function */
|
|
200
|
-
handler: (input: any, ...services: any[]) => Promise<any>;
|
|
201
205
|
/** Optional metadata from .jay-action file (description, input/output schemas).
|
|
202
206
|
* Actions with metadata are exposed to AI agents; those without are not. */
|
|
203
207
|
metadata?: ActionMetadata;
|
|
204
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* Registered request-response action entry.
|
|
211
|
+
* Uses `isStreaming` as a discriminator for the union.
|
|
212
|
+
*/
|
|
213
|
+
interface RegisteredAction extends RegisteredActionBase {
|
|
214
|
+
/** Discriminator: false or absent for regular actions */
|
|
215
|
+
isStreaming?: false;
|
|
216
|
+
/** Cache options (for GET requests) */
|
|
217
|
+
cacheOptions?: CacheOptions;
|
|
218
|
+
/** The handler function */
|
|
219
|
+
handler: (input: any, ...services: any[]) => Promise<any>;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Registered streaming action entry (DL#129).
|
|
223
|
+
*/
|
|
224
|
+
interface RegisteredStreamAction extends RegisteredActionBase {
|
|
225
|
+
method: 'POST';
|
|
226
|
+
/** Discriminator: true for streaming actions */
|
|
227
|
+
isStreaming: true;
|
|
228
|
+
/** The generator handler function */
|
|
229
|
+
handler: (input: any, ...services: any[]) => AsyncIterable<any>;
|
|
230
|
+
}
|
|
231
|
+
/** Union of all registered action types, discriminated by `isStreaming`. */
|
|
232
|
+
type RegisteredActionEntry = RegisteredAction | RegisteredStreamAction;
|
|
205
233
|
/**
|
|
206
234
|
* Result of executing an action.
|
|
207
235
|
*/
|
|
@@ -252,7 +280,7 @@ declare class ActionRegistry {
|
|
|
252
280
|
* @param actionName - The unique action name
|
|
253
281
|
* @returns The registered action or undefined
|
|
254
282
|
*/
|
|
255
|
-
get(actionName: string):
|
|
283
|
+
get(actionName: string): RegisteredActionEntry | undefined;
|
|
256
284
|
/**
|
|
257
285
|
* Checks if an action is registered.
|
|
258
286
|
*
|
|
@@ -304,6 +332,18 @@ declare class ActionRegistry {
|
|
|
304
332
|
* @returns Cache-Control header value or undefined
|
|
305
333
|
*/
|
|
306
334
|
getCacheHeaders(actionName: string): string | undefined;
|
|
335
|
+
/**
|
|
336
|
+
* Register a streaming action.
|
|
337
|
+
*/
|
|
338
|
+
registerStream<I, C, S extends any[]>(action: JayStreamAction<I, C> & JayStreamActionDefinition<I, C, S>): void;
|
|
339
|
+
/**
|
|
340
|
+
* Check if a registered action is a streaming action.
|
|
341
|
+
*/
|
|
342
|
+
isStreaming(actionName: string): boolean;
|
|
343
|
+
/**
|
|
344
|
+
* Execute a streaming action, returning an async iterable of chunks.
|
|
345
|
+
*/
|
|
346
|
+
executeStream(actionName: string, input: unknown): AsyncGenerator<any>;
|
|
307
347
|
}
|
|
308
348
|
/**
|
|
309
349
|
* Default action registry instance.
|
|
@@ -319,7 +359,7 @@ declare function registerAction<I, O, S extends any[]>(action: JayAction<I, O> &
|
|
|
319
359
|
* Retrieves a registered action by name from the default registry.
|
|
320
360
|
* @deprecated Use actionRegistry.get() instead
|
|
321
361
|
*/
|
|
322
|
-
declare function getRegisteredAction(actionName: string):
|
|
362
|
+
declare function getRegisteredAction(actionName: string): RegisteredActionEntry | undefined;
|
|
323
363
|
/**
|
|
324
364
|
* Checks if an action is registered in the default registry.
|
|
325
365
|
* @deprecated Use actionRegistry.has() instead
|
|
@@ -569,6 +609,8 @@ interface GenerateClientScriptOptions {
|
|
|
569
609
|
* so that AI/automation tools can see the complete page state.
|
|
570
610
|
*/
|
|
571
611
|
slowViewState?: object;
|
|
612
|
+
/** Route pattern (e.g., /products/kitan{/:category}) for freeze entries */
|
|
613
|
+
routePattern?: string;
|
|
572
614
|
}
|
|
573
615
|
/**
|
|
574
616
|
* Shared fragments generated by buildScriptFragments().
|
|
@@ -626,7 +668,23 @@ declare function clearServerElementCache(): void;
|
|
|
626
668
|
* 3. Build hydration script (uses ?jay-hydrate query for hydrate target)
|
|
627
669
|
* 4. Return full HTML page string
|
|
628
670
|
*/
|
|
629
|
-
declare function generateSSRPageHtml(vite: ViteDevServer, jayHtmlContent: string, jayHtmlFilename: string, jayHtmlDir: string, viewState: object, jayHtmlImportPath: string, parts: DevServerPagePart[], carryForward: object, trackByMap: TrackByMap, clientInitData: Record<string, Record<string, any>>, buildFolder: string, projectRoot: string, routeDir: string, tsConfigFilePath?: string, projectInit?: ProjectClientInitInfo, pluginInits?: PluginClientInitInfo[], options?: GenerateClientScriptOptions
|
|
671
|
+
declare function generateSSRPageHtml(vite: ViteDevServer, jayHtmlContent: string, jayHtmlFilename: string, jayHtmlDir: string, viewState: object, jayHtmlImportPath: string, parts: DevServerPagePart[], carryForward: object, trackByMap: TrackByMap, clientInitData: Record<string, Record<string, any>>, buildFolder: string, projectRoot: string, routeDir: string, tsConfigFilePath?: string, projectInit?: ProjectClientInitInfo, pluginInits?: PluginClientInitInfo[], options?: GenerateClientScriptOptions,
|
|
672
|
+
/** Source directory for headfull FS file resolution when jayHtmlDir is pre-rendered */
|
|
673
|
+
sourceDir?: string,
|
|
674
|
+
/** Head tags to inject into <head> during SSR (Design Log #127) */
|
|
675
|
+
headTags?: HeadTag[]): Promise<string>;
|
|
676
|
+
/**
|
|
677
|
+
* Generate a frozen page — pure SSR HTML with no client scripts (DL#127).
|
|
678
|
+
*
|
|
679
|
+
* Uses the same server element module as generateSSRPageHtml, but:
|
|
680
|
+
* - No hydration script
|
|
681
|
+
* - No Vite client
|
|
682
|
+
* - No component runtime
|
|
683
|
+
* - Just rendered HTML + CSS
|
|
684
|
+
*
|
|
685
|
+
* @param format - 'page' for full HTML document, 'fragment' for body-only (shadow DOM)
|
|
686
|
+
*/
|
|
687
|
+
declare function generateFrozenPageHtml(vite: ViteDevServer, jayHtmlContent: string, jayHtmlFilename: string, jayHtmlDir: string, viewState: object, buildFolder: string, projectRoot: string, routeDir: string, tsConfigFilePath?: string, sourceDir?: string, format?: 'page' | 'fragment', freezeName?: string): Promise<string>;
|
|
630
688
|
|
|
631
689
|
/**
|
|
632
690
|
* Service registry for Jay Stack server-side dependency injection.
|
|
@@ -906,10 +964,30 @@ interface ActionIndexEntry {
|
|
|
906
964
|
/** Contract entry within a plugin in plugins-index.yaml */
|
|
907
965
|
interface PluginContractEntry {
|
|
908
966
|
name: string;
|
|
967
|
+
description?: string;
|
|
909
968
|
type: 'static' | 'dynamic';
|
|
910
969
|
path: string;
|
|
911
970
|
metadata?: Record<string, unknown>;
|
|
912
971
|
}
|
|
972
|
+
/** Service entry in plugins-index.yaml (DL#125) */
|
|
973
|
+
interface ServiceIndexEntry {
|
|
974
|
+
name: string;
|
|
975
|
+
marker: string;
|
|
976
|
+
description?: string;
|
|
977
|
+
doc?: string;
|
|
978
|
+
}
|
|
979
|
+
/** Context entry in plugins-index.yaml (DL#125) */
|
|
980
|
+
interface ContextIndexEntry {
|
|
981
|
+
name: string;
|
|
982
|
+
marker: string;
|
|
983
|
+
description?: string;
|
|
984
|
+
doc?: string;
|
|
985
|
+
}
|
|
986
|
+
/** Route entry in plugins-index.yaml (DL#130) */
|
|
987
|
+
interface RouteIndexEntry {
|
|
988
|
+
path: string;
|
|
989
|
+
description?: string;
|
|
990
|
+
}
|
|
913
991
|
/** Entry for plugins-index.yaml (Design Log #85) */
|
|
914
992
|
interface PluginsIndexEntry {
|
|
915
993
|
name: string;
|
|
@@ -917,6 +995,12 @@ interface PluginsIndexEntry {
|
|
|
917
995
|
contracts: PluginContractEntry[];
|
|
918
996
|
/** Actions with .jay-action metadata (exposed to AI agents) */
|
|
919
997
|
actions?: ActionIndexEntry[];
|
|
998
|
+
/** Server-side services provided by this plugin (DL#125) */
|
|
999
|
+
services?: ServiceIndexEntry[];
|
|
1000
|
+
/** Client-side contexts provided by this plugin (DL#125) */
|
|
1001
|
+
contexts?: ContextIndexEntry[];
|
|
1002
|
+
/** Plugin-provided routes (DL#130) */
|
|
1003
|
+
routes?: RouteIndexEntry[];
|
|
920
1004
|
}
|
|
921
1005
|
interface PluginsIndex {
|
|
922
1006
|
plugins: PluginsIndexEntry[];
|
|
@@ -1150,4 +1234,27 @@ declare function executePluginReferences(plugin: PluginWithReferences, options:
|
|
|
1150
1234
|
verbose?: boolean;
|
|
1151
1235
|
}): Promise<PluginReferencesResult>;
|
|
1152
1236
|
|
|
1153
|
-
|
|
1237
|
+
/**
|
|
1238
|
+
* Head tag utilities for SSR head injection (Design Log #127).
|
|
1239
|
+
*
|
|
1240
|
+
* Components declare HeadTag[] via phaseOutput(). The SSR pipeline collects
|
|
1241
|
+
* tags from all sources, deduplicates with last-write-wins + collision warning,
|
|
1242
|
+
* and serializes to HTML for injection into <head>.
|
|
1243
|
+
*/
|
|
1244
|
+
|
|
1245
|
+
/**
|
|
1246
|
+
* Compute a unique identity key for deduplication.
|
|
1247
|
+
* Returns undefined for tags that should always be included (no dedup).
|
|
1248
|
+
*/
|
|
1249
|
+
declare function tagIdentityKey(tag: HeadTag): string | undefined;
|
|
1250
|
+
/**
|
|
1251
|
+
* Merge head tags from multiple sources with last-write-wins.
|
|
1252
|
+
* Warns on collision via logger.
|
|
1253
|
+
*/
|
|
1254
|
+
declare function mergeHeadTags(sources: HeadTag[][]): HeadTag[];
|
|
1255
|
+
/**
|
|
1256
|
+
* Serialize an array of HeadTag objects into an HTML string.
|
|
1257
|
+
*/
|
|
1258
|
+
declare function serializeHeadTags(tags: HeadTag[]): string;
|
|
1259
|
+
|
|
1260
|
+
export { type ActionDiscoveryOptions, type ActionDiscoveryResult, type ActionErrorResponse, type ActionExecutionResult, type ActionIndexEntry, type ActionMetadata, ActionRegistry, type ActionSchema, type ContextIndexEntry, type DevServerPagePart, DevSlowlyChangingPhase, type GenerateClientScriptOptions, type HeadlessInstanceComponent, type InstancePhaseData, type InstanceSlowRenderResult, type LoadedPageParts, type MaterializeContractsOptions, type MaterializeResult, type PluginActionDiscoveryOptions, type PluginClientInitInfo, type PluginContractEntry, 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 RegisteredActionBase, type RegisteredActionEntry, type RegisteredStreamAction, type RouteIndexEntry, type ScannedPlugin, type ScriptFragments, type ServiceIndexEntry, SlowRenderCache, type SlowRenderCacheEntry, type SlowlyChangingPhase, type ViteSSRLoader, actionRegistry, buildAutomationWrap, buildScriptFragments, clearActionRegistry, clearClientInitData, clearLifecycleCallbacks, clearServerElementCache, clearServiceRegistry, discoverAllPluginActions, discoverAndRegisterActions, discoverPluginActions, discoverPluginsWithInit, discoverPluginsWithReferences, discoverPluginsWithSetup, executeAction, executePluginReferences, executePluginServerInits, executePluginSetup, generateClientScript, generateFrozenPageHtml, generatePromiseReconstruction, generateSSRPageHtml, getActionCacheHeaders, getClientInitData, getClientInitDataForKey, getRegisteredAction, getRegisteredActionNames, getService, getServiceRegistry, hasAction, hasService, invalidateServerElementCache, listContracts, loadActionMetadata, loadPageParts, materializeContracts, mergeHeadTags, onInit, onShutdown, parseActionMetadata, preparePluginClientInits, registerAction, registerService, renderFastChangingData, resolveActionMetadataPath, resolveServices, resolveViewStatePromises, runInitCallbacks, runLoadParams, runShutdownCallbacks, scanPlugins, serializeHeadTags, setClientInitData, slowRenderInstances, sortPluginsByDependencies, tagIdentityKey, validateForEachInstances };
|
package/dist/index.js
CHANGED
|
@@ -4,19 +4,19 @@ var __publicField = (obj, key, value) => {
|
|
|
4
4
|
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
5
|
return value;
|
|
6
6
|
};
|
|
7
|
-
import { phaseOutput, isJayAction } from "@jay-framework/fullstack-component";
|
|
7
|
+
import { phaseOutput, isJayAction, isJayStreamAction } from "@jay-framework/fullstack-component";
|
|
8
8
|
import "prettier";
|
|
9
9
|
import "js-beautify";
|
|
10
10
|
import fs$1 from "fs";
|
|
11
11
|
import path$1 from "path";
|
|
12
12
|
import YAML from "yaml";
|
|
13
13
|
import { createRequire } from "module";
|
|
14
|
-
import { parseJayFile, JAY_IMPORT_RESOLVER, generateServerElementFile, discoverHeadlessInstances, parseAction } from "@jay-framework/compiler-jay-html";
|
|
14
|
+
import { parseJayFile, JAY_IMPORT_RESOLVER, generateServerElementFile, injectHeadfullFSTemplates, discoverHeadlessInstances, assignCoordinatesToJayHtml, parseAction } from "@jay-framework/compiler-jay-html";
|
|
15
|
+
import { getLogger } from "@jay-framework/logger";
|
|
15
16
|
import fs$2 from "node:fs/promises";
|
|
16
17
|
import * as path from "node:path";
|
|
17
18
|
import path__default from "node:path";
|
|
18
19
|
import crypto from "node:crypto";
|
|
19
|
-
import { getLogger } from "@jay-framework/logger";
|
|
20
20
|
import * as fs from "node:fs";
|
|
21
21
|
import { createRequire as createRequire$1 } from "node:module";
|
|
22
22
|
const serviceRegistry = /* @__PURE__ */ new Map();
|
|
@@ -87,6 +87,7 @@ class DevSlowlyChangingPhase {
|
|
|
87
87
|
async runSlowlyForPage(pageParams, pageProps, parts, discoveredInstances, headlessInstanceComponents, jayHtmlPath) {
|
|
88
88
|
let slowlyViewState = {};
|
|
89
89
|
let carryForward = {};
|
|
90
|
+
const slowHeadTagSources = [];
|
|
90
91
|
for (const part of parts) {
|
|
91
92
|
const { compDefinition, key, contractInfo } = part;
|
|
92
93
|
if (compDefinition.slowlyRender) {
|
|
@@ -111,6 +112,9 @@ class DevSlowlyChangingPhase {
|
|
|
111
112
|
slowlyViewState[key] = slowlyRenderedPart.rendered;
|
|
112
113
|
carryForward[key] = slowlyRenderedPart.carryForward;
|
|
113
114
|
}
|
|
115
|
+
if (slowlyRenderedPart.headTags) {
|
|
116
|
+
slowHeadTagSources.push(slowlyRenderedPart.headTags);
|
|
117
|
+
}
|
|
114
118
|
} else
|
|
115
119
|
return slowlyRenderedPart;
|
|
116
120
|
}
|
|
@@ -158,6 +162,9 @@ class DevSlowlyChangingPhase {
|
|
|
158
162
|
contract: comp.contract,
|
|
159
163
|
slowViewState: slowResult.rendered
|
|
160
164
|
});
|
|
165
|
+
if (slowResult.headTags) {
|
|
166
|
+
slowHeadTagSources.push(slowResult.headTags);
|
|
167
|
+
}
|
|
161
168
|
}
|
|
162
169
|
}
|
|
163
170
|
}
|
|
@@ -165,13 +172,21 @@ class DevSlowlyChangingPhase {
|
|
|
165
172
|
carryForward.__instanceSlowViewStates = instanceSlowViewStates;
|
|
166
173
|
carryForward.__instanceResolvedData = instanceResolvedData;
|
|
167
174
|
}
|
|
175
|
+
if (slowHeadTagSources.length > 0) {
|
|
176
|
+
carryForward.__slowHeadTags = slowHeadTagSources;
|
|
177
|
+
}
|
|
168
178
|
return phaseOutput(slowlyViewState, carryForward);
|
|
169
179
|
}
|
|
170
180
|
}
|
|
171
|
-
async function runLoadParams(
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
181
|
+
async function* runLoadParams(parts) {
|
|
182
|
+
for (const part of parts) {
|
|
183
|
+
if (part.compDefinition.loadParams) {
|
|
184
|
+
const services = resolveServices(part.compDefinition.services);
|
|
185
|
+
for await (const batch of part.compDefinition.loadParams(services)) {
|
|
186
|
+
yield batch;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
175
190
|
}
|
|
176
191
|
var __defProp2 = Object.defineProperty;
|
|
177
192
|
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -378,6 +393,7 @@ function resolveBinding(binding, item) {
|
|
|
378
393
|
async function renderFastChangingData(pageParams, pageProps, carryForward, parts, instancePhaseData, forEachInstances, headlessInstanceComponents, mergedSlowViewState, query = {}) {
|
|
379
394
|
let fastViewState = {};
|
|
380
395
|
let fastCarryForward = {};
|
|
396
|
+
const fastHeadTagSources = [];
|
|
381
397
|
for (const part of parts) {
|
|
382
398
|
const { compDefinition, key, contractInfo } = part;
|
|
383
399
|
if (compDefinition.fastRender) {
|
|
@@ -392,11 +408,7 @@ async function renderFastChangingData(pageParams, pageProps, carryForward, parts
|
|
|
392
408
|
metadata: contractInfo.metadata
|
|
393
409
|
}
|
|
394
410
|
};
|
|
395
|
-
const fastRenderedPart = await compDefinition.fastRender(
|
|
396
|
-
partProps,
|
|
397
|
-
partSlowlyCarryForward,
|
|
398
|
-
...services
|
|
399
|
-
);
|
|
411
|
+
const fastRenderedPart = compDefinition.slowlyRender ? await compDefinition.fastRender(partProps, partSlowlyCarryForward, ...services) : await compDefinition.fastRender(partProps, ...services);
|
|
400
412
|
if (fastRenderedPart.kind === "PhaseOutput") {
|
|
401
413
|
if (!key) {
|
|
402
414
|
fastViewState = { ...fastViewState, ...fastRenderedPart.rendered };
|
|
@@ -405,6 +417,9 @@ async function renderFastChangingData(pageParams, pageProps, carryForward, parts
|
|
|
405
417
|
fastViewState[key] = fastRenderedPart.rendered;
|
|
406
418
|
fastCarryForward[key] = fastRenderedPart.carryForward;
|
|
407
419
|
}
|
|
420
|
+
if (fastRenderedPart.headTags) {
|
|
421
|
+
fastHeadTagSources.push(fastRenderedPart.headTags);
|
|
422
|
+
}
|
|
408
423
|
} else
|
|
409
424
|
return fastRenderedPart;
|
|
410
425
|
}
|
|
@@ -419,18 +434,25 @@ async function renderFastChangingData(pageParams, pageProps, carryForward, parts
|
|
|
419
434
|
for (const instance of instancePhaseData.discovered) {
|
|
420
435
|
const coordKey = instance.coordinate.join("/");
|
|
421
436
|
const comp = componentByContractName.get(instance.contractName);
|
|
422
|
-
if (!comp
|
|
437
|
+
if (!comp)
|
|
423
438
|
continue;
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
439
|
+
const instanceSlowVS = instancePhaseData.slowViewStates?.[coordKey] ?? carryForward?.__instanceSlowViewStates?.[coordKey];
|
|
440
|
+
if (comp.compDefinition.fastRender) {
|
|
441
|
+
const services = resolveServices(comp.compDefinition.services);
|
|
442
|
+
const cf = instancePhaseData.carryForwards[coordKey];
|
|
443
|
+
const instanceProps = { ...instance.props, query };
|
|
444
|
+
const fastResult = comp.compDefinition.slowlyRender ? await comp.compDefinition.fastRender(instanceProps, cf, ...services) : await comp.compDefinition.fastRender(instanceProps, ...services);
|
|
445
|
+
if (fastResult.kind === "PhaseOutput") {
|
|
446
|
+
instanceViewStates[coordKey] = instanceSlowVS ? { ...instanceSlowVS, ...fastResult.rendered } : fastResult.rendered;
|
|
447
|
+
if (fastResult.carryForward) {
|
|
448
|
+
instanceCarryForwards[coordKey] = fastResult.carryForward;
|
|
449
|
+
}
|
|
450
|
+
if (fastResult.headTags) {
|
|
451
|
+
fastHeadTagSources.push(fastResult.headTags);
|
|
452
|
+
}
|
|
433
453
|
}
|
|
454
|
+
} else {
|
|
455
|
+
instanceViewStates[coordKey] = instanceSlowVS ?? {};
|
|
434
456
|
}
|
|
435
457
|
}
|
|
436
458
|
}
|
|
@@ -491,7 +513,11 @@ async function renderFastChangingData(pageParams, pageProps, carryForward, parts
|
|
|
491
513
|
if (Object.keys(instanceCarryForwards).length > 0) {
|
|
492
514
|
fastCarryForward.__headlessInstances = instanceCarryForwards;
|
|
493
515
|
}
|
|
494
|
-
|
|
516
|
+
const result = phaseOutput(fastViewState, fastCarryForward);
|
|
517
|
+
if (fastHeadTagSources.length > 0) {
|
|
518
|
+
result.headTags = fastHeadTagSources.flat();
|
|
519
|
+
}
|
|
520
|
+
return Promise.resolve(result);
|
|
495
521
|
}
|
|
496
522
|
function generatePromiseReconstruction(outcomes) {
|
|
497
523
|
if (outcomes.length === 0)
|
|
@@ -551,6 +577,80 @@ ${parts.map((part) => " " + part.clientPart).join(",\n")}
|
|
|
551
577
|
slowViewStateDecl
|
|
552
578
|
};
|
|
553
579
|
}
|
|
580
|
+
function buildFreezeScript(routePattern) {
|
|
581
|
+
const routePatternLiteral = routePattern ? `'${routePattern}'` : "undefined";
|
|
582
|
+
return `
|
|
583
|
+
// Page Freeze (DL#127, DL#128 iframe addendum)
|
|
584
|
+
// Sticky embed mode: URL param sets a session cookie so in-iframe navigation preserves it
|
|
585
|
+
if (new URLSearchParams(window.location.search).has('_jay_embed')) {
|
|
586
|
+
document.cookie = '_jay_embed=1;path=/;samesite=lax';
|
|
587
|
+
}
|
|
588
|
+
const __jayEmbedMode = document.cookie.split(';').some(c => c.trim().startsWith('_jay_embed='));
|
|
589
|
+
|
|
590
|
+
async function __jayDoFreeze() {
|
|
591
|
+
const automation = window.__jay?.automation;
|
|
592
|
+
if (!automation) return;
|
|
593
|
+
|
|
594
|
+
// Visual feedback: white flash
|
|
595
|
+
const flash = document.createElement('div');
|
|
596
|
+
flash.style.cssText = 'position:fixed;inset:0;background:white;z-index:999999;opacity:0.8;pointer-events:none;transition:opacity 0.3s';
|
|
597
|
+
document.body.appendChild(flash);
|
|
598
|
+
requestAnimationFrame(() => { flash.style.opacity = '0'; });
|
|
599
|
+
setTimeout(() => flash.remove(), 400);
|
|
600
|
+
|
|
601
|
+
// Audio feedback: camera shutter
|
|
602
|
+
try {
|
|
603
|
+
const ctx = new AudioContext();
|
|
604
|
+
const buf = ctx.createBuffer(1, ctx.sampleRate * 0.15, ctx.sampleRate);
|
|
605
|
+
const data = buf.getChannelData(0);
|
|
606
|
+
for (let i = 0; i < data.length; i++) {
|
|
607
|
+
data[i] = (Math.random() * 2 - 1) * Math.exp(-i / (ctx.sampleRate * 0.02));
|
|
608
|
+
}
|
|
609
|
+
const src = ctx.createBufferSource();
|
|
610
|
+
src.buffer = buf;
|
|
611
|
+
src.connect(ctx.destination);
|
|
612
|
+
src.start();
|
|
613
|
+
} catch {}
|
|
614
|
+
|
|
615
|
+
// Capture and save
|
|
616
|
+
try {
|
|
617
|
+
const state = automation.getPageState();
|
|
618
|
+
const route = window.location.pathname;
|
|
619
|
+
const routePattern = ${routePatternLiteral};
|
|
620
|
+
const resp = await fetch('/_jay/freeze', {
|
|
621
|
+
method: 'POST',
|
|
622
|
+
headers: { 'Content-Type': 'application/json' },
|
|
623
|
+
body: JSON.stringify({ route, routePattern, viewState: state.viewState }),
|
|
624
|
+
});
|
|
625
|
+
const { id } = await resp.json();
|
|
626
|
+
if (__jayEmbedMode) {
|
|
627
|
+
window.parent.postMessage({ type: 'jay:freeze', id, route }, '*');
|
|
628
|
+
} else {
|
|
629
|
+
window.open(route + '?_jay_freeze=' + id, '_blank');
|
|
630
|
+
}
|
|
631
|
+
} catch (err) {
|
|
632
|
+
console.error('[Freeze] Failed:', err);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if (__jayEmbedMode) {
|
|
637
|
+
// Notify parent of the current route on load (DL#128 route addendum)
|
|
638
|
+
window.parent.postMessage({ type: 'jay:route', route: window.location.pathname, routePattern: ${routePatternLiteral} }, '*');
|
|
639
|
+
|
|
640
|
+
// Embed mode: parent triggers freeze via postMessage
|
|
641
|
+
window.addEventListener('message', (e) => {
|
|
642
|
+
if (e.data?.type === 'jay:requestFreeze') __jayDoFreeze();
|
|
643
|
+
});
|
|
644
|
+
} else {
|
|
645
|
+
// Standalone: Alt+S / Option+S keyboard shortcut
|
|
646
|
+
document.addEventListener('keydown', (e) => {
|
|
647
|
+
if (e.altKey && e.code === 'KeyS') {
|
|
648
|
+
e.preventDefault();
|
|
649
|
+
__jayDoFreeze();
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
}`;
|
|
653
|
+
}
|
|
554
654
|
function buildAutomationWrap(options, mode) {
|
|
555
655
|
const { enableAutomation = true, slowViewState } = options;
|
|
556
656
|
const hasSlowViewState = slowViewState && Object.keys(slowViewState).length > 0;
|
|
@@ -561,6 +661,7 @@ function buildAutomationWrap(options, mode) {
|
|
|
561
661
|
}
|
|
562
662
|
const appendLine = appendDom ? `
|
|
563
663
|
target.appendChild(wrapped.element.dom);` : "";
|
|
664
|
+
const freezeScript = buildFreezeScript(options.routePattern);
|
|
564
665
|
if (hasSlowViewState) {
|
|
565
666
|
return `
|
|
566
667
|
// Wrap with automation for dev tooling
|
|
@@ -570,7 +671,8 @@ function buildAutomationWrap(options, mode) {
|
|
|
570
671
|
registerGlobalContext(AUTOMATION_CONTEXT, wrapped.automation);
|
|
571
672
|
window.__jay = window.__jay || {};
|
|
572
673
|
window.__jay.automation = wrapped.automation;
|
|
573
|
-
window.dispatchEvent(new Event('jay:automation-ready'));${appendLine}
|
|
674
|
+
window.dispatchEvent(new Event('jay:automation-ready'));${appendLine}
|
|
675
|
+
${freezeScript}`;
|
|
574
676
|
}
|
|
575
677
|
return `
|
|
576
678
|
// Wrap with automation for dev tooling
|
|
@@ -578,7 +680,8 @@ function buildAutomationWrap(options, mode) {
|
|
|
578
680
|
registerGlobalContext(AUTOMATION_CONTEXT, wrapped.automation);
|
|
579
681
|
window.__jay = window.__jay || {};
|
|
580
682
|
window.__jay.automation = wrapped.automation;
|
|
581
|
-
window.dispatchEvent(new Event('jay:automation-ready'));${appendLine}
|
|
683
|
+
window.dispatchEvent(new Event('jay:automation-ready'));${appendLine}
|
|
684
|
+
${freezeScript}`;
|
|
582
685
|
}
|
|
583
686
|
async function resolveViewStatePromises(viewState) {
|
|
584
687
|
const entries = Object.entries(viewState);
|
|
@@ -635,7 +738,8 @@ async function generateClientScript(defaultViewState, fastCarryForward, parts, j
|
|
|
635
738
|
import { render } from '${jayHtmlPath}';
|
|
636
739
|
${partImports}${slowViewStateDecl}
|
|
637
740
|
const viewState = ${JSON.stringify(defaultViewState)};
|
|
638
|
-
${generatePromiseReconstruction(outcomes)}
|
|
741
|
+
${generatePromiseReconstruction(outcomes)}
|
|
742
|
+
const fastCarryForward = ${JSON.stringify(fastCarryForward)};
|
|
639
743
|
const trackByMap = ${JSON.stringify(trackByMap)};
|
|
640
744
|
${clientInitExecution}
|
|
641
745
|
const target = document.getElementById('target');
|
|
@@ -651,6 +755,65 @@ function asyncSwapScript(id, html) {
|
|
|
651
755
|
const escapedHtml = html.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
652
756
|
return `<script>(function(){var t=document.querySelector('[jay-async="${id}:pending"]');if(t){var d=document.createElement('div');d.innerHTML='${escapedHtml}';t.replaceWith(d.firstChild);}window.__jay&&window.__jay.hydrateAsync&&window.__jay.hydrateAsync('${id}');})()<\/script>`;
|
|
653
757
|
}
|
|
758
|
+
function tagIdentityKey(tag) {
|
|
759
|
+
const t = tag.tag.toLowerCase();
|
|
760
|
+
if (t === "title")
|
|
761
|
+
return "title";
|
|
762
|
+
if (t === "meta") {
|
|
763
|
+
if (tag.attrs?.name)
|
|
764
|
+
return `meta:name:${tag.attrs.name}`;
|
|
765
|
+
if (tag.attrs?.property)
|
|
766
|
+
return `meta:property:${tag.attrs.property}`;
|
|
767
|
+
if (tag.attrs?.charset !== void 0)
|
|
768
|
+
return "meta:charset";
|
|
769
|
+
return void 0;
|
|
770
|
+
}
|
|
771
|
+
if (t === "link") {
|
|
772
|
+
if (tag.attrs?.rel === "canonical")
|
|
773
|
+
return "link:canonical";
|
|
774
|
+
return void 0;
|
|
775
|
+
}
|
|
776
|
+
return void 0;
|
|
777
|
+
}
|
|
778
|
+
function mergeHeadTags(sources) {
|
|
779
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
780
|
+
const result = [];
|
|
781
|
+
for (let si = 0; si < sources.length; si++) {
|
|
782
|
+
for (const tag of sources[si]) {
|
|
783
|
+
const key = tagIdentityKey(tag);
|
|
784
|
+
if (key) {
|
|
785
|
+
const existing = byKey.get(key);
|
|
786
|
+
if (existing && existing.sourceIndex !== si) {
|
|
787
|
+
getLogger().warn(
|
|
788
|
+
`[head-tags] Collision on "${key}" — overwriting with tag from source ${si}`
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
byKey.set(key, { tag, sourceIndex: si });
|
|
792
|
+
} else {
|
|
793
|
+
result.push(tag);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
return [...[...byKey.values()].map((v) => v.tag), ...result];
|
|
798
|
+
}
|
|
799
|
+
function escapeAttr(value) {
|
|
800
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
801
|
+
}
|
|
802
|
+
function escapeHtml(value) {
|
|
803
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
804
|
+
}
|
|
805
|
+
const VOID_ELEMENTS = /* @__PURE__ */ new Set(["meta", "link", "base", "br", "hr", "img", "input"]);
|
|
806
|
+
function serializeHeadTags(tags) {
|
|
807
|
+
return tags.map((tag) => {
|
|
808
|
+
const t = tag.tag.toLowerCase();
|
|
809
|
+
const attrs = tag.attrs ? Object.entries(tag.attrs).map(([k, v]) => ` ${k}="${escapeAttr(v)}"`).join("") : "";
|
|
810
|
+
if (VOID_ELEMENTS.has(t)) {
|
|
811
|
+
return ` <${t}${attrs} />`;
|
|
812
|
+
}
|
|
813
|
+
const children = tag.children ? escapeHtml(tag.children) : "";
|
|
814
|
+
return ` <${t}${attrs}>${children}</${t}>`;
|
|
815
|
+
}).join("\n");
|
|
816
|
+
}
|
|
654
817
|
const serverModuleCache = /* @__PURE__ */ new Map();
|
|
655
818
|
function invalidateServerElementCache(jayHtmlPath) {
|
|
656
819
|
if (serverModuleCache.delete(jayHtmlPath)) {
|
|
@@ -660,7 +823,7 @@ function invalidateServerElementCache(jayHtmlPath) {
|
|
|
660
823
|
function clearServerElementCache() {
|
|
661
824
|
serverModuleCache.clear();
|
|
662
825
|
}
|
|
663
|
-
async function generateSSRPageHtml(vite, jayHtmlContent, jayHtmlFilename, jayHtmlDir, viewState, jayHtmlImportPath, parts, carryForward, trackByMap = {}, clientInitData2 = {}, buildFolder, projectRoot, routeDir, tsConfigFilePath, projectInit, pluginInits = [], options = {}) {
|
|
826
|
+
async function generateSSRPageHtml(vite, jayHtmlContent, jayHtmlFilename, jayHtmlDir, viewState, jayHtmlImportPath, parts, carryForward, trackByMap = {}, clientInitData2 = {}, buildFolder, projectRoot, routeDir, tsConfigFilePath, projectInit, pluginInits = [], options = {}, sourceDir, headTags) {
|
|
664
827
|
const jayHtmlPath = path__default.join(jayHtmlDir, jayHtmlFilename);
|
|
665
828
|
let cached = serverModuleCache.get(jayHtmlPath);
|
|
666
829
|
if (!cached) {
|
|
@@ -672,7 +835,8 @@ async function generateSSRPageHtml(vite, jayHtmlContent, jayHtmlFilename, jayHtm
|
|
|
672
835
|
buildFolder,
|
|
673
836
|
projectRoot,
|
|
674
837
|
routeDir,
|
|
675
|
-
tsConfigFilePath
|
|
838
|
+
tsConfigFilePath,
|
|
839
|
+
sourceDir
|
|
676
840
|
);
|
|
677
841
|
serverModuleCache.set(jayHtmlPath, cached);
|
|
678
842
|
}
|
|
@@ -728,20 +892,97 @@ async function generateSSRPageHtml(vite, jayHtmlContent, jayHtmlFilename, jayHtm
|
|
|
728
892
|
return ` <link rel="${link.rel}" href="${link.href}"${attrs} />`;
|
|
729
893
|
}).join("\n");
|
|
730
894
|
const cssLink = cached.cssHref ? ` <link rel="stylesheet" href="${cached.cssHref}" />` : "";
|
|
731
|
-
const
|
|
895
|
+
const headTagsHtml = headTags && headTags.length > 0 ? serializeHeadTags(headTags) : "";
|
|
896
|
+
const hasCustomTitle = headTags?.some((t) => t.tag.toLowerCase() === "title");
|
|
897
|
+
const titleHtml = hasCustomTitle ? "" : " <title>Vite + TS</title>\n";
|
|
898
|
+
const headExtras = [headLinksHtml, cssLink, headTagsHtml].filter((_) => _).join("\n");
|
|
732
899
|
return `<!doctype html>
|
|
733
900
|
<html lang="en">
|
|
734
901
|
<head>
|
|
735
902
|
<meta charset="UTF-8" />
|
|
736
903
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
737
|
-
|
|
738
|
-
${headExtras ? headExtras + "\n" : ""} </head>
|
|
904
|
+
${titleHtml}${headExtras ? headExtras + "\n" : ""} </head>
|
|
739
905
|
<body>
|
|
740
906
|
<div id="target">${ssrHtml}</div>${asyncScripts}
|
|
741
907
|
${hydrationScript}
|
|
742
908
|
</body>
|
|
743
909
|
</html>`;
|
|
744
910
|
}
|
|
911
|
+
async function generateFrozenPageHtml(vite, jayHtmlContent, jayHtmlFilename, jayHtmlDir, viewState, buildFolder, projectRoot, routeDir, tsConfigFilePath, sourceDir, format = "page", freezeName) {
|
|
912
|
+
const jayHtmlPath = path__default.join(jayHtmlDir, jayHtmlFilename);
|
|
913
|
+
let cached = serverModuleCache.get(jayHtmlPath);
|
|
914
|
+
if (!cached) {
|
|
915
|
+
cached = await compileAndLoadServerElement(
|
|
916
|
+
vite,
|
|
917
|
+
jayHtmlContent,
|
|
918
|
+
jayHtmlFilename,
|
|
919
|
+
jayHtmlDir,
|
|
920
|
+
buildFolder,
|
|
921
|
+
projectRoot,
|
|
922
|
+
routeDir,
|
|
923
|
+
tsConfigFilePath,
|
|
924
|
+
sourceDir
|
|
925
|
+
);
|
|
926
|
+
serverModuleCache.set(jayHtmlPath, cached);
|
|
927
|
+
}
|
|
928
|
+
const htmlChunks = [];
|
|
929
|
+
const ctx = {
|
|
930
|
+
write: (chunk) => {
|
|
931
|
+
htmlChunks.push(chunk);
|
|
932
|
+
},
|
|
933
|
+
onAsync: () => {
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
cached.renderToStream(viewState, ctx);
|
|
937
|
+
const ssrHtml = htmlChunks.join("");
|
|
938
|
+
if (format === "fragment") {
|
|
939
|
+
let inlineCss = "";
|
|
940
|
+
if (cached.cssHref) {
|
|
941
|
+
try {
|
|
942
|
+
const cssPath = cached.cssHref.replace(/^\/@fs/, "").replace(/\?.*$/, "");
|
|
943
|
+
const cssContent = await fs$2.readFile(cssPath, "utf-8");
|
|
944
|
+
inlineCss = `<style>${cssContent}</style>`;
|
|
945
|
+
} catch {
|
|
946
|
+
inlineCss = `<link rel="stylesheet" href="${cached.cssHref}" />`;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
return `${inlineCss}
|
|
950
|
+
${ssrHtml}`;
|
|
951
|
+
}
|
|
952
|
+
const headLinksHtml = cached.headLinks.map((link) => {
|
|
953
|
+
const attrs = Object.entries(link.attributes).map(([k, v]) => ` ${k}="${v}"`).join("");
|
|
954
|
+
return ` <link rel="${link.rel}" href="${link.href}"${attrs} />`;
|
|
955
|
+
}).join("\n");
|
|
956
|
+
const cssLink = cached.cssHref ? ` <link rel="stylesheet" href="${cached.cssHref}" />` : "";
|
|
957
|
+
const headExtras = [headLinksHtml, cssLink].filter((_) => _).join("\n");
|
|
958
|
+
const label = freezeName ? ` — ${freezeName}` : "";
|
|
959
|
+
return `<!doctype html>
|
|
960
|
+
<html lang="en">
|
|
961
|
+
<head>
|
|
962
|
+
<meta charset="UTF-8" />
|
|
963
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
964
|
+
<title>Frozen${label}</title>
|
|
965
|
+
${headExtras ? headExtras + "\n" : ""} <style>
|
|
966
|
+
body::before {
|
|
967
|
+
content: 'FROZEN${label ? `: ${freezeName}` : ""}';
|
|
968
|
+
position: fixed;
|
|
969
|
+
top: 0;
|
|
970
|
+
right: 0;
|
|
971
|
+
background: #1a1a2e;
|
|
972
|
+
color: #e0e0ff;
|
|
973
|
+
padding: 2px 10px;
|
|
974
|
+
font: 11px/1.6 system-ui;
|
|
975
|
+
z-index: 99999;
|
|
976
|
+
border-bottom-left-radius: 4px;
|
|
977
|
+
opacity: 0.8;
|
|
978
|
+
}
|
|
979
|
+
</style>
|
|
980
|
+
</head>
|
|
981
|
+
<body>
|
|
982
|
+
<div id="target">${ssrHtml}</div>
|
|
983
|
+
</body>
|
|
984
|
+
</html>`;
|
|
985
|
+
}
|
|
745
986
|
function rebaseRelativeImports(code, fromDir, toDir) {
|
|
746
987
|
return code.replace(/from "(\.\.\/[^"]+)"/g, (_match, relPath) => {
|
|
747
988
|
const absolutePath = path__default.resolve(fromDir, relPath);
|
|
@@ -752,14 +993,15 @@ function rebaseRelativeImports(code, fromDir, toDir) {
|
|
|
752
993
|
return `from "${newRelPath}"`;
|
|
753
994
|
});
|
|
754
995
|
}
|
|
755
|
-
async function compileAndLoadServerElement(vite, jayHtmlContent, jayHtmlFilename, jayHtmlDir, buildFolder, projectRoot, routeDir, tsConfigFilePath) {
|
|
996
|
+
async function compileAndLoadServerElement(vite, jayHtmlContent, jayHtmlFilename, jayHtmlDir, buildFolder, projectRoot, routeDir, tsConfigFilePath, sourceDir) {
|
|
756
997
|
const jayFile = await parseJayFile(
|
|
757
998
|
jayHtmlContent,
|
|
758
999
|
jayHtmlFilename,
|
|
759
1000
|
jayHtmlDir,
|
|
760
1001
|
{ relativePath: tsConfigFilePath },
|
|
761
1002
|
JAY_IMPORT_RESOLVER,
|
|
762
|
-
projectRoot
|
|
1003
|
+
projectRoot,
|
|
1004
|
+
sourceDir
|
|
763
1005
|
);
|
|
764
1006
|
const parsedJayFile = checkValidationErrors(jayFile);
|
|
765
1007
|
const pageName = jayHtmlFilename.replace(".jay-html", "");
|
|
@@ -785,6 +1027,8 @@ async function compileAndLoadServerElement(vite, jayHtmlContent, jayHtmlFilename
|
|
|
785
1027
|
if (existingModule) {
|
|
786
1028
|
vite.moduleGraph.invalidateModule(existingModule);
|
|
787
1029
|
}
|
|
1030
|
+
const jayHtmlPath = path__default.join(serverElementDir, jayHtmlFilename);
|
|
1031
|
+
invalidateJayHtmlModules(vite, jayHtmlPath);
|
|
788
1032
|
const serverModule = await vite.ssrLoadModule(serverElementPath);
|
|
789
1033
|
let cssHref;
|
|
790
1034
|
if (parsedJayFile.css) {
|
|
@@ -806,6 +1050,34 @@ async function compileAndLoadServerElement(vite, jayHtmlContent, jayHtmlFilename
|
|
|
806
1050
|
cssHref
|
|
807
1051
|
};
|
|
808
1052
|
}
|
|
1053
|
+
function invalidateJayHtmlModules(vite, jayHtmlPath) {
|
|
1054
|
+
let count = 0;
|
|
1055
|
+
const byFile = vite.moduleGraph.getModulesByFile(jayHtmlPath);
|
|
1056
|
+
if (byFile) {
|
|
1057
|
+
for (const mod of byFile) {
|
|
1058
|
+
vite.moduleGraph.invalidateModule(mod);
|
|
1059
|
+
count++;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
const knownIds = [jayHtmlPath + ".ts", jayHtmlPath + JAY_QUERY_HYDRATE + ".ts"];
|
|
1063
|
+
for (const id of knownIds) {
|
|
1064
|
+
const mod = vite.moduleGraph.getModuleById(id);
|
|
1065
|
+
if (mod) {
|
|
1066
|
+
vite.moduleGraph.invalidateModule(mod);
|
|
1067
|
+
count++;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
const idMap = vite.moduleGraph.idToModuleMap;
|
|
1071
|
+
for (const [id, mod] of idMap) {
|
|
1072
|
+
if (id.includes(jayHtmlPath)) {
|
|
1073
|
+
vite.moduleGraph.invalidateModule(mod);
|
|
1074
|
+
count++;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
if (count > 0) {
|
|
1078
|
+
getLogger().info(`[SSR] Invalidated ${count} Vite module(s) for ${jayHtmlPath}`);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
809
1081
|
function generateHydrationScript(defaultViewState, fastCarryForward, parts, jayHtmlPath, trackByMap = {}, clientInitData2 = {}, projectInit, pluginInits = [], options = {}, asyncOutcomes = []) {
|
|
810
1082
|
const {
|
|
811
1083
|
partImports,
|
|
@@ -907,7 +1179,29 @@ async function loadPageParts(vite, route, pagesBase, projectBase, jayRollupConfi
|
|
|
907
1179
|
contract: hi.contract,
|
|
908
1180
|
contractPath: hi.contractPath
|
|
909
1181
|
}));
|
|
910
|
-
const
|
|
1182
|
+
const jayHtmlForDiscovery = injectHeadfullFSTemplates(
|
|
1183
|
+
jayHtmlSource,
|
|
1184
|
+
dirName,
|
|
1185
|
+
JAY_IMPORT_RESOLVER
|
|
1186
|
+
);
|
|
1187
|
+
let discoveryResult;
|
|
1188
|
+
if (headlessInstanceComponents.length > 0) {
|
|
1189
|
+
const firstDiscovery = discoverHeadlessInstances(jayHtmlForDiscovery);
|
|
1190
|
+
const headlessContractNameSet = new Set(
|
|
1191
|
+
jayHtml.headlessImports.map((hi) => hi.contractName)
|
|
1192
|
+
);
|
|
1193
|
+
const jayHtmlWithCoords = assignCoordinatesToJayHtml(
|
|
1194
|
+
firstDiscovery.preRenderedJayHtml,
|
|
1195
|
+
headlessContractNameSet
|
|
1196
|
+
);
|
|
1197
|
+
discoveryResult = discoverHeadlessInstances(jayHtmlWithCoords);
|
|
1198
|
+
} else {
|
|
1199
|
+
discoveryResult = {
|
|
1200
|
+
instances: [],
|
|
1201
|
+
forEachInstances: [],
|
|
1202
|
+
preRenderedJayHtml: jayHtmlSource
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
911
1205
|
return {
|
|
912
1206
|
parts,
|
|
913
1207
|
serverTrackByMap: jayHtml.serverTrackByMap,
|
|
@@ -916,7 +1210,9 @@ async function loadPageParts(vite, route, pagesBase, projectBase, jayRollupConfi
|
|
|
916
1210
|
headlessContracts,
|
|
917
1211
|
headlessInstanceComponents,
|
|
918
1212
|
discoveredInstances: discoveryResult.instances,
|
|
919
|
-
forEachInstances: discoveryResult.forEachInstances
|
|
1213
|
+
forEachInstances: discoveryResult.forEachInstances,
|
|
1214
|
+
linkedCssFiles: jayHtml.linkedCssFiles ?? [],
|
|
1215
|
+
linkedComponentFiles: jayHtml.linkedComponentFiles ?? []
|
|
920
1216
|
};
|
|
921
1217
|
});
|
|
922
1218
|
}
|
|
@@ -931,31 +1227,32 @@ async function slowRenderInstances(discovered, headlessInstanceComponents) {
|
|
|
931
1227
|
const carryForwards = {};
|
|
932
1228
|
for (const instance of discovered) {
|
|
933
1229
|
const comp = componentByContractName.get(instance.contractName);
|
|
934
|
-
if (!comp
|
|
1230
|
+
if (!comp)
|
|
935
1231
|
continue;
|
|
936
|
-
}
|
|
937
1232
|
const contractProps = comp.contract?.props ?? [];
|
|
938
1233
|
const normalizedProps = {};
|
|
939
1234
|
for (const [key, value] of Object.entries(instance.props)) {
|
|
940
1235
|
const match = contractProps.find((p) => p.name.toLowerCase() === key.toLowerCase());
|
|
941
1236
|
normalizedProps[match ? match.name : key] = value;
|
|
942
1237
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
1238
|
+
discoveredForFast.push({
|
|
1239
|
+
contractName: instance.contractName,
|
|
1240
|
+
props: normalizedProps,
|
|
1241
|
+
coordinate: instance.coordinate
|
|
1242
|
+
});
|
|
1243
|
+
if (comp.compDefinition.slowlyRender) {
|
|
1244
|
+
const services = resolveServices(comp.compDefinition.services);
|
|
1245
|
+
const slowResult = await comp.compDefinition.slowlyRender(normalizedProps, ...services);
|
|
1246
|
+
if (slowResult.kind === "PhaseOutput") {
|
|
1247
|
+
const coordKey = instance.coordinate.join("/");
|
|
1248
|
+
resolvedData.push({
|
|
1249
|
+
coordinate: instance.coordinate,
|
|
1250
|
+
contract: comp.contract,
|
|
1251
|
+
slowViewState: slowResult.rendered
|
|
1252
|
+
});
|
|
1253
|
+
slowViewStates[coordKey] = slowResult.rendered;
|
|
1254
|
+
carryForwards[coordKey] = slowResult.carryForward;
|
|
1255
|
+
}
|
|
959
1256
|
}
|
|
960
1257
|
}
|
|
961
1258
|
if (discoveredForFast.length === 0) {
|
|
@@ -964,7 +1261,7 @@ async function slowRenderInstances(discovered, headlessInstanceComponents) {
|
|
|
964
1261
|
return {
|
|
965
1262
|
resolvedData,
|
|
966
1263
|
slowViewStates,
|
|
967
|
-
instancePhaseData: { discovered: discoveredForFast, carryForwards }
|
|
1264
|
+
instancePhaseData: { discovered: discoveredForFast, carryForwards, slowViewStates }
|
|
968
1265
|
};
|
|
969
1266
|
}
|
|
970
1267
|
function validateForEachInstances(forEachInstances, headlessInstanceComponents) {
|
|
@@ -1082,6 +1379,16 @@ class ActionRegistry {
|
|
|
1082
1379
|
}
|
|
1083
1380
|
};
|
|
1084
1381
|
}
|
|
1382
|
+
if (action.isStreaming) {
|
|
1383
|
+
return {
|
|
1384
|
+
success: false,
|
|
1385
|
+
error: {
|
|
1386
|
+
code: "STREAMING_ACTION",
|
|
1387
|
+
message: `Action '${actionName}' is a streaming action — use executeStream() instead`,
|
|
1388
|
+
isActionError: false
|
|
1389
|
+
}
|
|
1390
|
+
};
|
|
1391
|
+
}
|
|
1085
1392
|
try {
|
|
1086
1393
|
const services = resolveServices(action.services);
|
|
1087
1394
|
const result = await action.handler(input, ...services);
|
|
@@ -1135,6 +1442,40 @@ class ActionRegistry {
|
|
|
1135
1442
|
}
|
|
1136
1443
|
return parts.length > 0 ? parts.join(", ") : void 0;
|
|
1137
1444
|
}
|
|
1445
|
+
// --- Streaming actions (DL#129) ---
|
|
1446
|
+
/**
|
|
1447
|
+
* Register a streaming action.
|
|
1448
|
+
*/
|
|
1449
|
+
registerStream(action) {
|
|
1450
|
+
const entry = {
|
|
1451
|
+
actionName: action.actionName,
|
|
1452
|
+
method: "POST",
|
|
1453
|
+
isStreaming: true,
|
|
1454
|
+
services: action.services,
|
|
1455
|
+
handler: action.handler
|
|
1456
|
+
};
|
|
1457
|
+
this.actions.set(action.actionName, entry);
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* Check if a registered action is a streaming action.
|
|
1461
|
+
*/
|
|
1462
|
+
isStreaming(actionName) {
|
|
1463
|
+
const action = this.actions.get(actionName);
|
|
1464
|
+
return !!action?.isStreaming;
|
|
1465
|
+
}
|
|
1466
|
+
/**
|
|
1467
|
+
* Execute a streaming action, returning an async iterable of chunks.
|
|
1468
|
+
*/
|
|
1469
|
+
async *executeStream(actionName, input) {
|
|
1470
|
+
const action = this.actions.get(actionName);
|
|
1471
|
+
if (!action || !action.isStreaming) {
|
|
1472
|
+
throw new Error(`Streaming action '${actionName}' not found`);
|
|
1473
|
+
}
|
|
1474
|
+
const services = resolveServices(action.services);
|
|
1475
|
+
for await (const chunk of action.handler(input, ...services)) {
|
|
1476
|
+
yield chunk;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1138
1479
|
}
|
|
1139
1480
|
const actionRegistry = new ActionRegistry();
|
|
1140
1481
|
function registerAction(action) {
|
|
@@ -1258,6 +1599,15 @@ async function discoverAndRegisterActions(options) {
|
|
|
1258
1599
|
`[Actions] Registered: ${exportValue.actionName}`
|
|
1259
1600
|
);
|
|
1260
1601
|
}
|
|
1602
|
+
} else if (isJayStreamAction(exportValue)) {
|
|
1603
|
+
registry.registerStream(exportValue);
|
|
1604
|
+
result.actionNames.push(exportValue.actionName);
|
|
1605
|
+
result.actionCount++;
|
|
1606
|
+
if (verbose) {
|
|
1607
|
+
getLogger().info(
|
|
1608
|
+
`[Actions] Registered stream: ${exportValue.actionName}`
|
|
1609
|
+
);
|
|
1610
|
+
}
|
|
1261
1611
|
}
|
|
1262
1612
|
}
|
|
1263
1613
|
} catch (error) {
|
|
@@ -1414,6 +1764,13 @@ async function registerNpmPluginActions(packageName, pluginConfig, pluginDir, re
|
|
|
1414
1764
|
if (verbose) {
|
|
1415
1765
|
getLogger().info(`[Actions] Registered NPM plugin action: ${registeredName}`);
|
|
1416
1766
|
}
|
|
1767
|
+
} else if (actionExport && isJayStreamAction(actionExport)) {
|
|
1768
|
+
registry.registerStream(actionExport);
|
|
1769
|
+
const registeredName = actionExport.actionName;
|
|
1770
|
+
registeredActions.push(registeredName);
|
|
1771
|
+
if (verbose) {
|
|
1772
|
+
getLogger().info(`[Actions] Registered NPM plugin stream: ${registeredName}`);
|
|
1773
|
+
}
|
|
1417
1774
|
} else {
|
|
1418
1775
|
getLogger().warn(
|
|
1419
1776
|
`[Actions] NPM plugin "${packageName}" declares action "${actionName}" but it's not exported or not a JayAction`
|
|
@@ -1482,6 +1839,13 @@ async function discoverPluginActions(pluginPath, projectRoot, registry = actionR
|
|
|
1482
1839
|
if (verbose) {
|
|
1483
1840
|
getLogger().info(`[Actions] Registered plugin action: ${registeredName}`);
|
|
1484
1841
|
}
|
|
1842
|
+
} else if (actionExport && isJayStreamAction(actionExport)) {
|
|
1843
|
+
registry.registerStream(actionExport);
|
|
1844
|
+
const registeredName = actionExport.actionName;
|
|
1845
|
+
registeredActions.push(registeredName);
|
|
1846
|
+
if (verbose) {
|
|
1847
|
+
getLogger().info(`[Actions] Registered plugin stream: ${registeredName}`);
|
|
1848
|
+
}
|
|
1485
1849
|
} else {
|
|
1486
1850
|
getLogger().warn(
|
|
1487
1851
|
`[Actions] Plugin "${pluginName}" declares action "${actionName}" but it's not exported or not a JayAction`
|
|
@@ -2153,11 +2517,40 @@ async function materializeContracts(options, services = /* @__PURE__ */ new Map(
|
|
|
2153
2517
|
const { manifest } = plugin;
|
|
2154
2518
|
const pluginRelPath = path.relative(projectRoot, plugin.pluginPath);
|
|
2155
2519
|
if (!pluginsIndexMap.has(plugin.name)) {
|
|
2156
|
-
|
|
2520
|
+
const entry = {
|
|
2157
2521
|
path: "./" + pluginRelPath.replace(/\\/g, "/"),
|
|
2158
2522
|
contracts: [],
|
|
2159
2523
|
actions: []
|
|
2160
|
-
}
|
|
2524
|
+
};
|
|
2525
|
+
if (manifest.services?.length) {
|
|
2526
|
+
entry.services = manifest.services.map((s2) => {
|
|
2527
|
+
const docPath = s2.doc ? "./" + path.relative(projectRoot, path.resolve(plugin.pluginPath, s2.doc)) : void 0;
|
|
2528
|
+
return {
|
|
2529
|
+
name: s2.name,
|
|
2530
|
+
marker: s2.marker,
|
|
2531
|
+
...s2.description && { description: s2.description },
|
|
2532
|
+
...docPath && { doc: docPath }
|
|
2533
|
+
};
|
|
2534
|
+
});
|
|
2535
|
+
}
|
|
2536
|
+
if (manifest.contexts?.length) {
|
|
2537
|
+
entry.contexts = manifest.contexts.map((c) => {
|
|
2538
|
+
const docPath = c.doc ? "./" + path.relative(projectRoot, path.resolve(plugin.pluginPath, c.doc)) : void 0;
|
|
2539
|
+
return {
|
|
2540
|
+
name: c.name,
|
|
2541
|
+
marker: c.marker,
|
|
2542
|
+
...c.description && { description: c.description },
|
|
2543
|
+
...docPath && { doc: docPath }
|
|
2544
|
+
};
|
|
2545
|
+
});
|
|
2546
|
+
}
|
|
2547
|
+
if (manifest.routes?.length) {
|
|
2548
|
+
entry.routes = manifest.routes.map((r) => ({
|
|
2549
|
+
path: r.path,
|
|
2550
|
+
...r.description && { description: r.description }
|
|
2551
|
+
}));
|
|
2552
|
+
}
|
|
2553
|
+
pluginsIndexMap.set(plugin.name, entry);
|
|
2161
2554
|
}
|
|
2162
2555
|
if (!dynamicOnly && manifest.contracts) {
|
|
2163
2556
|
for (const contract of manifest.contracts) {
|
|
@@ -2167,8 +2560,20 @@ async function materializeContracts(options, services = /* @__PURE__ */ new Map(
|
|
|
2167
2560
|
projectRoot
|
|
2168
2561
|
);
|
|
2169
2562
|
const relativePath = path.relative(projectRoot, contractPath);
|
|
2563
|
+
let description = contract.description;
|
|
2564
|
+
if (!description) {
|
|
2565
|
+
try {
|
|
2566
|
+
const contractContent = fs.readFileSync(contractPath, "utf-8");
|
|
2567
|
+
const parsed = YAML.parse(contractContent);
|
|
2568
|
+
if (parsed?.description && typeof parsed.description === "string") {
|
|
2569
|
+
description = parsed.description;
|
|
2570
|
+
}
|
|
2571
|
+
} catch {
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2170
2574
|
pluginsIndexMap.get(plugin.name).contracts.push({
|
|
2171
2575
|
name: contract.name,
|
|
2576
|
+
...description && { description },
|
|
2172
2577
|
type: "static",
|
|
2173
2578
|
path: "./" + relativePath
|
|
2174
2579
|
});
|
|
@@ -2202,8 +2607,17 @@ async function materializeContracts(options, services = /* @__PURE__ */ new Map(
|
|
|
2202
2607
|
const filePath = path.join(pluginOutputDir, fileName);
|
|
2203
2608
|
fs.writeFileSync(filePath, generated.yaml, "utf-8");
|
|
2204
2609
|
const relativePath = path.relative(projectRoot, filePath);
|
|
2610
|
+
let dynDescription;
|
|
2611
|
+
try {
|
|
2612
|
+
const parsedYaml = YAML.parse(generated.yaml);
|
|
2613
|
+
if (parsedYaml?.description && typeof parsedYaml.description === "string") {
|
|
2614
|
+
dynDescription = parsedYaml.description;
|
|
2615
|
+
}
|
|
2616
|
+
} catch {
|
|
2617
|
+
}
|
|
2205
2618
|
const contractEntry = {
|
|
2206
2619
|
name: fullName,
|
|
2620
|
+
...dynDescription && { description: dynDescription },
|
|
2207
2621
|
type: "dynamic",
|
|
2208
2622
|
path: "./" + relativePath,
|
|
2209
2623
|
...generated.metadata && { metadata: generated.metadata }
|
|
@@ -2239,7 +2653,10 @@ async function materializeContracts(options, services = /* @__PURE__ */ new Map(
|
|
|
2239
2653
|
if (!metadata)
|
|
2240
2654
|
continue;
|
|
2241
2655
|
const actionRelPath = path.relative(projectRoot, metadataFilePath);
|
|
2242
|
-
pluginsIndexMap.get(plugin.name)
|
|
2656
|
+
const pluginEntry = pluginsIndexMap.get(plugin.name);
|
|
2657
|
+
if (!pluginEntry.actions)
|
|
2658
|
+
pluginEntry.actions = [];
|
|
2659
|
+
pluginEntry.actions.push({
|
|
2243
2660
|
name: metadata.name,
|
|
2244
2661
|
description: metadata.description,
|
|
2245
2662
|
path: "./" + actionRelPath.replace(/\\/g, "/")
|
|
@@ -2255,7 +2672,10 @@ async function materializeContracts(options, services = /* @__PURE__ */ new Map(
|
|
|
2255
2672
|
name,
|
|
2256
2673
|
path: data.path,
|
|
2257
2674
|
contracts: data.contracts,
|
|
2258
|
-
...data.actions.length > 0 && { actions: data.actions }
|
|
2675
|
+
...data.actions && data.actions.length > 0 && { actions: data.actions },
|
|
2676
|
+
...data.services?.length && { services: data.services },
|
|
2677
|
+
...data.contexts?.length && { contexts: data.contexts },
|
|
2678
|
+
...data.routes?.length && { routes: data.routes }
|
|
2259
2679
|
}))
|
|
2260
2680
|
};
|
|
2261
2681
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
@@ -2286,10 +2706,39 @@ async function listContracts(options) {
|
|
|
2286
2706
|
const { manifest } = plugin;
|
|
2287
2707
|
const pluginRelPath = path.relative(projectRoot, plugin.pluginPath);
|
|
2288
2708
|
if (!pluginsMap.has(plugin.name)) {
|
|
2289
|
-
|
|
2709
|
+
const entry = {
|
|
2290
2710
|
path: "./" + pluginRelPath.replace(/\\/g, "/"),
|
|
2291
2711
|
contracts: []
|
|
2292
|
-
}
|
|
2712
|
+
};
|
|
2713
|
+
if (manifest.services?.length) {
|
|
2714
|
+
entry.services = manifest.services.map((s2) => {
|
|
2715
|
+
const docPath = s2.doc ? "./" + path.relative(projectRoot, path.resolve(plugin.pluginPath, s2.doc)) : void 0;
|
|
2716
|
+
return {
|
|
2717
|
+
name: s2.name,
|
|
2718
|
+
marker: s2.marker,
|
|
2719
|
+
...s2.description && { description: s2.description },
|
|
2720
|
+
...docPath && { doc: docPath }
|
|
2721
|
+
};
|
|
2722
|
+
});
|
|
2723
|
+
}
|
|
2724
|
+
if (manifest.contexts?.length) {
|
|
2725
|
+
entry.contexts = manifest.contexts.map((c) => {
|
|
2726
|
+
const docPath = c.doc ? "./" + path.relative(projectRoot, path.resolve(plugin.pluginPath, c.doc)) : void 0;
|
|
2727
|
+
return {
|
|
2728
|
+
name: c.name,
|
|
2729
|
+
marker: c.marker,
|
|
2730
|
+
...c.description && { description: c.description },
|
|
2731
|
+
...docPath && { doc: docPath }
|
|
2732
|
+
};
|
|
2733
|
+
});
|
|
2734
|
+
}
|
|
2735
|
+
if (manifest.routes?.length) {
|
|
2736
|
+
entry.routes = manifest.routes.map((r) => ({
|
|
2737
|
+
path: r.path,
|
|
2738
|
+
...r.description && { description: r.description }
|
|
2739
|
+
}));
|
|
2740
|
+
}
|
|
2741
|
+
pluginsMap.set(plugin.name, entry);
|
|
2293
2742
|
}
|
|
2294
2743
|
if (!dynamicOnly && manifest.contracts) {
|
|
2295
2744
|
for (const contract of manifest.contracts) {
|
|
@@ -2299,8 +2748,20 @@ async function listContracts(options) {
|
|
|
2299
2748
|
projectRoot
|
|
2300
2749
|
);
|
|
2301
2750
|
const relativePath = path.relative(projectRoot, contractPath);
|
|
2751
|
+
let listDescription = contract.description;
|
|
2752
|
+
if (!listDescription) {
|
|
2753
|
+
try {
|
|
2754
|
+
const contractContent = fs.readFileSync(contractPath, "utf-8");
|
|
2755
|
+
const parsed = YAML.parse(contractContent);
|
|
2756
|
+
if (parsed?.description && typeof parsed.description === "string") {
|
|
2757
|
+
listDescription = parsed.description;
|
|
2758
|
+
}
|
|
2759
|
+
} catch {
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2302
2762
|
pluginsMap.get(plugin.name).contracts.push({
|
|
2303
2763
|
name: contract.name,
|
|
2764
|
+
...listDescription && { description: listDescription },
|
|
2304
2765
|
type: "static",
|
|
2305
2766
|
path: "./" + relativePath
|
|
2306
2767
|
});
|
|
@@ -2321,7 +2782,10 @@ async function listContracts(options) {
|
|
|
2321
2782
|
plugins: Array.from(pluginsMap.entries()).map(([name, data]) => ({
|
|
2322
2783
|
name,
|
|
2323
2784
|
path: data.path,
|
|
2324
|
-
contracts: data.contracts
|
|
2785
|
+
contracts: data.contracts,
|
|
2786
|
+
...data.services?.length && { services: data.services },
|
|
2787
|
+
...data.contexts?.length && { contexts: data.contexts },
|
|
2788
|
+
...data.routes?.length && { routes: data.routes }
|
|
2325
2789
|
}))
|
|
2326
2790
|
};
|
|
2327
2791
|
}
|
|
@@ -2475,6 +2939,7 @@ export {
|
|
|
2475
2939
|
executePluginServerInits,
|
|
2476
2940
|
executePluginSetup,
|
|
2477
2941
|
generateClientScript,
|
|
2942
|
+
generateFrozenPageHtml,
|
|
2478
2943
|
generatePromiseReconstruction,
|
|
2479
2944
|
generateSSRPageHtml,
|
|
2480
2945
|
getActionCacheHeaders,
|
|
@@ -2491,6 +2956,7 @@ export {
|
|
|
2491
2956
|
loadActionMetadata,
|
|
2492
2957
|
loadPageParts,
|
|
2493
2958
|
materializeContracts,
|
|
2959
|
+
mergeHeadTags,
|
|
2494
2960
|
onInit,
|
|
2495
2961
|
onShutdown,
|
|
2496
2962
|
parseActionMetadata,
|
|
@@ -2504,10 +2970,11 @@ export {
|
|
|
2504
2970
|
runInitCallbacks,
|
|
2505
2971
|
runLoadParams,
|
|
2506
2972
|
runShutdownCallbacks,
|
|
2507
|
-
runSlowlyChangingRender,
|
|
2508
2973
|
scanPlugins,
|
|
2974
|
+
serializeHeadTags,
|
|
2509
2975
|
setClientInitData,
|
|
2510
2976
|
slowRenderInstances,
|
|
2511
2977
|
sortPluginsByDependencies,
|
|
2978
|
+
tagIdentityKey,
|
|
2512
2979
|
validateForEachInstances
|
|
2513
2980
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/stack-server-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.mts",
|
|
@@ -26,21 +26,21 @@
|
|
|
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/ssr-runtime": "^0.
|
|
36
|
-
"@jay-framework/stack-route-scanner": "^0.
|
|
37
|
-
"@jay-framework/view-state-merge": "^0.
|
|
29
|
+
"@jay-framework/compiler-jay-html": "^0.16.0",
|
|
30
|
+
"@jay-framework/compiler-shared": "^0.16.0",
|
|
31
|
+
"@jay-framework/component": "^0.16.0",
|
|
32
|
+
"@jay-framework/fullstack-component": "^0.16.0",
|
|
33
|
+
"@jay-framework/logger": "^0.16.0",
|
|
34
|
+
"@jay-framework/runtime": "^0.16.0",
|
|
35
|
+
"@jay-framework/ssr-runtime": "^0.16.0",
|
|
36
|
+
"@jay-framework/stack-route-scanner": "^0.16.0",
|
|
37
|
+
"@jay-framework/view-state-merge": "^0.16.0",
|
|
38
38
|
"yaml": "^2.3.4"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@jay-framework/dev-environment": "^0.
|
|
42
|
-
"@jay-framework/jay-cli": "^0.
|
|
43
|
-
"@jay-framework/stack-client-runtime": "^0.
|
|
41
|
+
"@jay-framework/dev-environment": "^0.16.0",
|
|
42
|
+
"@jay-framework/jay-cli": "^0.16.0",
|
|
43
|
+
"@jay-framework/stack-client-runtime": "^0.16.0",
|
|
44
44
|
"@types/express": "^5.0.2",
|
|
45
45
|
"@types/node": "^22.15.21",
|
|
46
46
|
"nodemon": "^3.0.3",
|