@jay-framework/stack-server-runtime 0.10.0 → 0.11.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 +131 -3
- package/dist/index.js +200 -13
- package/package.json +11 -11
package/dist/index.d.ts
CHANGED
|
@@ -21,7 +21,15 @@ interface LoadedPageParts {
|
|
|
21
21
|
/** NPM package names used on this page (for filtering plugin inits) */
|
|
22
22
|
usedPackages: Set<string>;
|
|
23
23
|
}
|
|
24
|
-
|
|
24
|
+
interface LoadPagePartsOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Path to pre-rendered jay-html file to use instead of the original.
|
|
27
|
+
* When provided, this file (with slow-phase bindings resolved) is read.
|
|
28
|
+
* Import resolution still uses the original jay-html's directory.
|
|
29
|
+
*/
|
|
30
|
+
preRenderedPath?: string;
|
|
31
|
+
}
|
|
32
|
+
declare function loadPageParts(vite: ViteDevServer, route: JayRoute, pagesBase: string, projectBase: string, jayRollupConfig: JayRollupConfig, options?: LoadPagePartsOptions): Promise<WithValidations<LoadedPageParts>>;
|
|
25
33
|
|
|
26
34
|
interface SlowlyChangingPhase {
|
|
27
35
|
runSlowlyForPage(pageParams: object, pageProps: PageProps, parts: Array<DevServerPagePart>): Promise<AnySlowlyRenderResult>;
|
|
@@ -183,6 +191,33 @@ declare function executeAction<T = any>(actionName: string, input: unknown): Pro
|
|
|
183
191
|
* @deprecated Use actionRegistry.getCacheHeaders() instead
|
|
184
192
|
*/
|
|
185
193
|
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>;
|
|
186
221
|
|
|
187
222
|
/**
|
|
188
223
|
* Action discovery and auto-registration for Jay Stack.
|
|
@@ -332,6 +367,7 @@ interface PluginInitDiscoveryOptions {
|
|
|
332
367
|
* in `plugin.yaml` via the `init` property.
|
|
333
368
|
*
|
|
334
369
|
* Scans both local plugins (src/plugins/) and NPM plugins (node_modules/).
|
|
370
|
+
* Also discovers transitive plugin dependencies (plugins that depend on other plugins).
|
|
335
371
|
*/
|
|
336
372
|
declare function discoverPluginsWithInit(options: PluginInitDiscoveryOptions): Promise<PluginWithInit[]>;
|
|
337
373
|
/**
|
|
@@ -383,7 +419,20 @@ interface ProjectClientInitInfo {
|
|
|
383
419
|
/** Export name for the JayInit constant (default: 'init') */
|
|
384
420
|
initExport?: string;
|
|
385
421
|
}
|
|
386
|
-
|
|
422
|
+
/**
|
|
423
|
+
* Options for client script generation.
|
|
424
|
+
*/
|
|
425
|
+
interface GenerateClientScriptOptions {
|
|
426
|
+
/** Enable automation integration (default: true in dev mode) */
|
|
427
|
+
enableAutomation?: boolean;
|
|
428
|
+
/**
|
|
429
|
+
* Slow ViewState that was baked into the pre-rendered jay-html.
|
|
430
|
+
* When provided, this is merged with fastViewState for the automation API
|
|
431
|
+
* so that AI/automation tools can see the complete page state.
|
|
432
|
+
*/
|
|
433
|
+
slowViewState?: object;
|
|
434
|
+
}
|
|
435
|
+
declare function generateClientScript(defaultViewState: object, fastCarryForward: object, parts: DevServerPagePart[], jayHtmlPath: string, trackByMap?: TrackByMap, clientInitData?: Record<string, Record<string, any>>, projectInit?: ProjectClientInitInfo, pluginInits?: PluginClientInitInfo[], options?: GenerateClientScriptOptions): string;
|
|
387
436
|
|
|
388
437
|
/**
|
|
389
438
|
* Service registry for Jay Stack server-side dependency injection.
|
|
@@ -549,4 +598,83 @@ declare function getClientInitDataForKey(key: string): Record<string, any>;
|
|
|
549
598
|
*/
|
|
550
599
|
declare function clearClientInitData(): void;
|
|
551
600
|
|
|
552
|
-
|
|
601
|
+
/**
|
|
602
|
+
* Cache entry for pre-rendered jay-html
|
|
603
|
+
*/
|
|
604
|
+
interface SlowRenderCacheEntry {
|
|
605
|
+
/** Path to the pre-rendered jay-html file on disk */
|
|
606
|
+
preRenderedPath: string;
|
|
607
|
+
/** Slow ViewState that was baked into the jay-html */
|
|
608
|
+
slowViewState: object;
|
|
609
|
+
/** CarryForward data from slow rendering (passed to fast phase) */
|
|
610
|
+
carryForward: object;
|
|
611
|
+
/** Timestamp when this entry was created */
|
|
612
|
+
createdAt: number;
|
|
613
|
+
/** Source jay-html path (for debugging) */
|
|
614
|
+
sourcePath: string;
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Cache for pre-rendered jay-html files.
|
|
618
|
+
*
|
|
619
|
+
* This cache stores jay-html content that has been transformed with slow-phase
|
|
620
|
+
* data baked in. The key insight is that since slow ViewState is embedded directly
|
|
621
|
+
* into the jay-html, we don't need to pass it to the client - only fast and
|
|
622
|
+
* interactive ViewState is sent.
|
|
623
|
+
*
|
|
624
|
+
* Pre-rendered files are written to disk so Vite can pick them up and compile them.
|
|
625
|
+
*/
|
|
626
|
+
declare class SlowRenderCache {
|
|
627
|
+
private cache;
|
|
628
|
+
private pathToKeys;
|
|
629
|
+
private readonly cacheDir;
|
|
630
|
+
private readonly pagesRoot;
|
|
631
|
+
/**
|
|
632
|
+
* @param cacheDir - Directory where pre-rendered jay-html files are stored
|
|
633
|
+
* @param pagesRoot - Root directory of the pages (for relative path calculation)
|
|
634
|
+
*/
|
|
635
|
+
constructor(cacheDir: string, pagesRoot: string);
|
|
636
|
+
/**
|
|
637
|
+
* Get a cached pre-rendered jay-html entry
|
|
638
|
+
*/
|
|
639
|
+
get(jayHtmlPath: string, params: Record<string, string>): SlowRenderCacheEntry | undefined;
|
|
640
|
+
/**
|
|
641
|
+
* Store a pre-rendered jay-html entry in the cache.
|
|
642
|
+
* Writes the pre-rendered content to disk and stores metadata in memory.
|
|
643
|
+
*/
|
|
644
|
+
set(jayHtmlPath: string, params: Record<string, string>, preRenderedJayHtml: string, slowViewState: object, carryForward: object): Promise<string>;
|
|
645
|
+
/**
|
|
646
|
+
* Check if a pre-rendered entry exists for the given path and params
|
|
647
|
+
*/
|
|
648
|
+
has(jayHtmlPath: string, params: Record<string, string>): boolean;
|
|
649
|
+
/**
|
|
650
|
+
* Invalidate all cached entries for a given jay-html source path.
|
|
651
|
+
* This is called when the source file changes.
|
|
652
|
+
* Also deletes the cached files from disk.
|
|
653
|
+
*/
|
|
654
|
+
invalidate(jayHtmlPath: string): Promise<void>;
|
|
655
|
+
/**
|
|
656
|
+
* Invalidate all entries that depend on a changed file.
|
|
657
|
+
* The changedPath could be:
|
|
658
|
+
* - A jay-html file itself
|
|
659
|
+
* - A component file (page.ts)
|
|
660
|
+
* - Any other dependency
|
|
661
|
+
*
|
|
662
|
+
* @param changedPath - Absolute path to the changed file
|
|
663
|
+
* @param resolveDependencies - Optional function to resolve which jay-html files depend on the changed file
|
|
664
|
+
*/
|
|
665
|
+
invalidateByDependency(changedPath: string, resolveDependencies?: (changedPath: string) => string[]): Promise<void>;
|
|
666
|
+
/**
|
|
667
|
+
* Clear all cached entries and delete cached files from disk
|
|
668
|
+
*/
|
|
669
|
+
clear(): Promise<void>;
|
|
670
|
+
/**
|
|
671
|
+
* Get the number of cached entries
|
|
672
|
+
*/
|
|
673
|
+
get size(): number;
|
|
674
|
+
/**
|
|
675
|
+
* Get all cached jay-html paths (for debugging/monitoring)
|
|
676
|
+
*/
|
|
677
|
+
getCachedPaths(): string[];
|
|
678
|
+
}
|
|
679
|
+
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import "js-beautify";
|
|
|
17
17
|
import fs$2 from "fs";
|
|
18
18
|
import path$1 from "path";
|
|
19
19
|
import YAML from "yaml";
|
|
20
|
+
import crypto from "node:crypto";
|
|
20
21
|
const serviceRegistry = /* @__PURE__ */ new Map();
|
|
21
22
|
function registerService(marker, service) {
|
|
22
23
|
serviceRegistry.set(marker, service);
|
|
@@ -158,7 +159,9 @@ async function renderFastChangingData(pageParams, pageProps, carryForward, parts
|
|
|
158
159
|
}
|
|
159
160
|
return Promise.resolve(phaseOutput(fastViewState, fastCarryForward));
|
|
160
161
|
}
|
|
161
|
-
function generateClientScript(defaultViewState, fastCarryForward, parts, jayHtmlPath, trackByMap = {}, clientInitData2 = {}, projectInit, pluginInits = []) {
|
|
162
|
+
function generateClientScript(defaultViewState, fastCarryForward, parts, jayHtmlPath, trackByMap = {}, clientInitData2 = {}, projectInit, pluginInits = [], options = {}) {
|
|
163
|
+
const { enableAutomation = true, slowViewState } = options;
|
|
164
|
+
const hasSlowViewState = slowViewState && Object.keys(slowViewState).length > 0;
|
|
162
165
|
const imports = parts.length > 0 ? parts.map((part) => part.clientImport).join("\n") + "\n" : "";
|
|
163
166
|
const compositeParts = parts.length > 0 ? `[
|
|
164
167
|
${parts.map((part) => " " + part.clientPart).join(",\n")}
|
|
@@ -187,6 +190,27 @@ ${parts.map((part) => " " + part.clientPart).join(",\n")}
|
|
|
187
190
|
// Project client initialization
|
|
188
191
|
${projectInitCall}
|
|
189
192
|
` : "";
|
|
193
|
+
const automationImport = enableAutomation ? hasSlowViewState ? `import { wrapWithAutomation, AUTOMATION_CONTEXT } from "@jay-framework/runtime-automation";
|
|
194
|
+
import { registerGlobalContext } from "@jay-framework/runtime";
|
|
195
|
+
import { deepMergeViewStates } from "@jay-framework/view-state-merge";` : `import { wrapWithAutomation, AUTOMATION_CONTEXT } from "@jay-framework/runtime-automation";
|
|
196
|
+
import { registerGlobalContext } from "@jay-framework/runtime";` : "";
|
|
197
|
+
const slowViewStateDecl = enableAutomation && hasSlowViewState ? `const slowViewState = ${JSON.stringify(slowViewState)};` : "";
|
|
198
|
+
const automationWrap = enableAutomation ? hasSlowViewState ? `
|
|
199
|
+
// Wrap with automation for dev tooling
|
|
200
|
+
// Deep merge slow+fast ViewState so automation can see full page state
|
|
201
|
+
const fullViewState = deepMergeViewStates(slowViewState, {...viewState, ...fastCarryForward}, trackByMap);
|
|
202
|
+
const wrapped = wrapWithAutomation(instance, { initialViewState: fullViewState, trackByMap });
|
|
203
|
+
registerGlobalContext(AUTOMATION_CONTEXT, wrapped.automation);
|
|
204
|
+
window.__jay = window.__jay || {};
|
|
205
|
+
window.__jay.automation = wrapped.automation;
|
|
206
|
+
target.appendChild(wrapped.element.dom);` : `
|
|
207
|
+
// Wrap with automation for dev tooling
|
|
208
|
+
const wrapped = wrapWithAutomation(instance);
|
|
209
|
+
registerGlobalContext(AUTOMATION_CONTEXT, wrapped.automation);
|
|
210
|
+
window.__jay = window.__jay || {};
|
|
211
|
+
window.__jay.automation = wrapped.automation;
|
|
212
|
+
target.appendChild(wrapped.element.dom);` : `
|
|
213
|
+
target.appendChild(instance.element.dom);`;
|
|
190
214
|
return `<!doctype html>
|
|
191
215
|
<html lang="en">
|
|
192
216
|
<head>
|
|
@@ -198,10 +222,11 @@ ${parts.map((part) => " " + part.clientPart).join(",\n")}
|
|
|
198
222
|
<div id="target"></div>
|
|
199
223
|
<script type="module">
|
|
200
224
|
import {makeCompositeJayComponent} from "@jay-framework/stack-client-runtime";
|
|
225
|
+
${automationImport}
|
|
201
226
|
${pluginClientInitImports}
|
|
202
227
|
${projectInitImport}
|
|
203
228
|
import { render } from '${jayHtmlPath}';
|
|
204
|
-
${imports}
|
|
229
|
+
${imports}${slowViewStateDecl}
|
|
205
230
|
const viewState = ${JSON.stringify(defaultViewState)};
|
|
206
231
|
const fastCarryForward = ${JSON.stringify(fastCarryForward)};
|
|
207
232
|
const trackByMap = ${JSON.stringify(trackByMap)};
|
|
@@ -210,25 +235,25 @@ ${clientInitExecution}
|
|
|
210
235
|
const pageComp = makeCompositeJayComponent(render, viewState, fastCarryForward, ${compositeParts}, trackByMap)
|
|
211
236
|
|
|
212
237
|
const instance = pageComp({...viewState, ...fastCarryForward})
|
|
213
|
-
|
|
238
|
+
${automationWrap}
|
|
214
239
|
<\/script>
|
|
215
240
|
</body>
|
|
216
241
|
</html>`;
|
|
217
242
|
}
|
|
218
243
|
const require$2 = createRequire(import.meta.url);
|
|
219
|
-
async function loadPageParts(vite, route, pagesBase, projectBase, jayRollupConfig) {
|
|
244
|
+
async function loadPageParts(vite, route, pagesBase, projectBase, jayRollupConfig, options) {
|
|
220
245
|
const exists = await fs$1.access(route.compPath, fs$1.constants.F_OK).then(() => true).catch(() => false);
|
|
221
246
|
const parts = [];
|
|
222
247
|
if (exists) {
|
|
223
248
|
const pageComponent = (await vite.ssrLoadModule(route.compPath)).page;
|
|
224
249
|
parts.push({
|
|
225
250
|
compDefinition: pageComponent,
|
|
226
|
-
// Client import uses client-only code (server code stripped)
|
|
227
251
|
clientImport: `import {page} from '${route.compPath}'`,
|
|
228
|
-
clientPart: `{comp: page.comp, contextMarkers: []}`
|
|
252
|
+
clientPart: `{comp: page.comp, contextMarkers: page.contexts || []}`
|
|
229
253
|
});
|
|
230
254
|
}
|
|
231
|
-
const
|
|
255
|
+
const jayHtmlFilePath = options?.preRenderedPath ?? route.jayHtmlPath;
|
|
256
|
+
const jayHtmlSource = (await fs$1.readFile(jayHtmlFilePath)).toString();
|
|
232
257
|
const fileName = path__default.basename(route.jayHtmlPath);
|
|
233
258
|
const dirName = path__default.dirname(route.jayHtmlPath);
|
|
234
259
|
const jayHtmlWithValidations = await parseJayFile(
|
|
@@ -261,7 +286,7 @@ async function loadPageParts(vite, route, pagesBase, projectBase, jayRollupConfi
|
|
|
261
286
|
key,
|
|
262
287
|
compDefinition,
|
|
263
288
|
clientImport: `import {${name}} from '${clientModuleImport}'`,
|
|
264
|
-
clientPart: `{comp: ${name}.comp, contextMarkers: [], key: '${headlessImport.key}'}`
|
|
289
|
+
clientPart: `{comp: ${name}.comp, contextMarkers: ${name}.contexts || [], key: '${headlessImport.key}'}`
|
|
265
290
|
};
|
|
266
291
|
parts.push(part);
|
|
267
292
|
}
|
|
@@ -420,6 +445,10 @@ async function executeAction(actionName, input) {
|
|
|
420
445
|
function getActionCacheHeaders(actionName) {
|
|
421
446
|
return actionRegistry.getCacheHeaders(actionName);
|
|
422
447
|
}
|
|
448
|
+
async function runAction(action, input) {
|
|
449
|
+
const services = resolveServices(action.services);
|
|
450
|
+
return action.handler(input, ...services);
|
|
451
|
+
}
|
|
423
452
|
var __defProp2 = Object.defineProperty;
|
|
424
453
|
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
425
454
|
var __publicField2 = (obj, key, value) => {
|
|
@@ -747,6 +776,7 @@ const require2 = createRequire$1(import.meta.url);
|
|
|
747
776
|
async function discoverPluginsWithInit(options) {
|
|
748
777
|
const { projectRoot, verbose = false } = options;
|
|
749
778
|
const plugins = [];
|
|
779
|
+
const visitedPackages = /* @__PURE__ */ new Set();
|
|
750
780
|
const localPluginsPath = path.join(projectRoot, "src/plugins");
|
|
751
781
|
if (fs.existsSync(localPluginsPath)) {
|
|
752
782
|
try {
|
|
@@ -772,6 +802,7 @@ async function discoverPluginsWithInit(options) {
|
|
|
772
802
|
initExport: initConfig.export,
|
|
773
803
|
dependencies
|
|
774
804
|
});
|
|
805
|
+
visitedPackages.add(pluginPath);
|
|
775
806
|
if (verbose) {
|
|
776
807
|
console.log(
|
|
777
808
|
`[PluginInit] Found local plugin with init: ${manifest.name || entry.name}`
|
|
@@ -786,11 +817,13 @@ async function discoverPluginsWithInit(options) {
|
|
|
786
817
|
if (fs.existsSync(projectPackageJsonPath)) {
|
|
787
818
|
try {
|
|
788
819
|
const projectPackageJson = JSON.parse(fs.readFileSync(projectPackageJsonPath, "utf-8"));
|
|
789
|
-
const
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
820
|
+
const initialDeps = Object.keys(projectPackageJson.dependencies || {});
|
|
821
|
+
const packagesToCheck = [...initialDeps];
|
|
822
|
+
while (packagesToCheck.length > 0) {
|
|
823
|
+
const depName = packagesToCheck.shift();
|
|
824
|
+
if (visitedPackages.has(depName))
|
|
825
|
+
continue;
|
|
826
|
+
visitedPackages.add(depName);
|
|
794
827
|
let pluginYamlPath;
|
|
795
828
|
try {
|
|
796
829
|
pluginYamlPath = require2.resolve(`${depName}/plugin.yaml`, {
|
|
@@ -819,6 +852,11 @@ async function discoverPluginsWithInit(options) {
|
|
|
819
852
|
if (verbose) {
|
|
820
853
|
console.log(`[PluginInit] Found NPM plugin with init: ${depName}`);
|
|
821
854
|
}
|
|
855
|
+
for (const transitiveDep of dependencies) {
|
|
856
|
+
if (!visitedPackages.has(transitiveDep)) {
|
|
857
|
+
packagesToCheck.push(transitiveDep);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
822
860
|
}
|
|
823
861
|
} catch (error) {
|
|
824
862
|
console.warn(`[PluginInit] Failed to scan NPM plugins: ${error}`);
|
|
@@ -961,9 +999,157 @@ function preparePluginClientInits(plugins) {
|
|
|
961
999
|
};
|
|
962
1000
|
});
|
|
963
1001
|
}
|
|
1002
|
+
function makeCacheKey(jayHtmlPath, params) {
|
|
1003
|
+
const sortedParams = Object.keys(params).sort().reduce(
|
|
1004
|
+
(acc, key) => {
|
|
1005
|
+
acc[key] = params[key];
|
|
1006
|
+
return acc;
|
|
1007
|
+
},
|
|
1008
|
+
{}
|
|
1009
|
+
);
|
|
1010
|
+
return `${jayHtmlPath}:${JSON.stringify(sortedParams)}`;
|
|
1011
|
+
}
|
|
1012
|
+
function hashParams(params) {
|
|
1013
|
+
const sortedParams = Object.keys(params).sort().reduce(
|
|
1014
|
+
(acc, key) => {
|
|
1015
|
+
acc[key] = params[key];
|
|
1016
|
+
return acc;
|
|
1017
|
+
},
|
|
1018
|
+
{}
|
|
1019
|
+
);
|
|
1020
|
+
const json = JSON.stringify(sortedParams);
|
|
1021
|
+
if (json === "{}")
|
|
1022
|
+
return "";
|
|
1023
|
+
return "_" + crypto.createHash("md5").update(json).digest("hex").substring(0, 8);
|
|
1024
|
+
}
|
|
1025
|
+
class SlowRenderCache {
|
|
1026
|
+
/**
|
|
1027
|
+
* @param cacheDir - Directory where pre-rendered jay-html files are stored
|
|
1028
|
+
* @param pagesRoot - Root directory of the pages (for relative path calculation)
|
|
1029
|
+
*/
|
|
1030
|
+
constructor(cacheDir, pagesRoot) {
|
|
1031
|
+
__publicField(this, "cache", /* @__PURE__ */ new Map());
|
|
1032
|
+
__publicField(this, "pathToKeys", /* @__PURE__ */ new Map());
|
|
1033
|
+
__publicField(this, "cacheDir");
|
|
1034
|
+
__publicField(this, "pagesRoot");
|
|
1035
|
+
this.cacheDir = cacheDir;
|
|
1036
|
+
this.pagesRoot = pagesRoot;
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Get a cached pre-rendered jay-html entry
|
|
1040
|
+
*/
|
|
1041
|
+
get(jayHtmlPath, params) {
|
|
1042
|
+
const key = makeCacheKey(jayHtmlPath, params);
|
|
1043
|
+
return this.cache.get(key);
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Store a pre-rendered jay-html entry in the cache.
|
|
1047
|
+
* Writes the pre-rendered content to disk and stores metadata in memory.
|
|
1048
|
+
*/
|
|
1049
|
+
async set(jayHtmlPath, params, preRenderedJayHtml, slowViewState, carryForward) {
|
|
1050
|
+
const key = makeCacheKey(jayHtmlPath, params);
|
|
1051
|
+
const relativePath = path__default.relative(this.pagesRoot, jayHtmlPath);
|
|
1052
|
+
const dir = path__default.dirname(relativePath);
|
|
1053
|
+
const basename = path__default.basename(relativePath, ".jay-html");
|
|
1054
|
+
const paramsHash = hashParams(params);
|
|
1055
|
+
const cacheFileName = `${basename}${paramsHash}.jay-html`;
|
|
1056
|
+
const preRenderedPath = path__default.join(this.cacheDir, dir, cacheFileName);
|
|
1057
|
+
await fs$1.mkdir(path__default.dirname(preRenderedPath), { recursive: true });
|
|
1058
|
+
await fs$1.writeFile(preRenderedPath, preRenderedJayHtml, "utf-8");
|
|
1059
|
+
if (!this.pathToKeys.has(jayHtmlPath)) {
|
|
1060
|
+
this.pathToKeys.set(jayHtmlPath, /* @__PURE__ */ new Set());
|
|
1061
|
+
}
|
|
1062
|
+
this.pathToKeys.get(jayHtmlPath).add(key);
|
|
1063
|
+
const entry = {
|
|
1064
|
+
preRenderedPath,
|
|
1065
|
+
slowViewState,
|
|
1066
|
+
carryForward,
|
|
1067
|
+
createdAt: Date.now(),
|
|
1068
|
+
sourcePath: jayHtmlPath
|
|
1069
|
+
};
|
|
1070
|
+
this.cache.set(key, entry);
|
|
1071
|
+
return preRenderedPath;
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* Check if a pre-rendered entry exists for the given path and params
|
|
1075
|
+
*/
|
|
1076
|
+
has(jayHtmlPath, params) {
|
|
1077
|
+
const key = makeCacheKey(jayHtmlPath, params);
|
|
1078
|
+
return this.cache.has(key);
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Invalidate all cached entries for a given jay-html source path.
|
|
1082
|
+
* This is called when the source file changes.
|
|
1083
|
+
* Also deletes the cached files from disk.
|
|
1084
|
+
*/
|
|
1085
|
+
async invalidate(jayHtmlPath) {
|
|
1086
|
+
const keys = this.pathToKeys.get(jayHtmlPath);
|
|
1087
|
+
if (keys) {
|
|
1088
|
+
for (const key of keys) {
|
|
1089
|
+
const entry = this.cache.get(key);
|
|
1090
|
+
if (entry) {
|
|
1091
|
+
try {
|
|
1092
|
+
await fs$1.unlink(entry.preRenderedPath);
|
|
1093
|
+
} catch {
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
this.cache.delete(key);
|
|
1097
|
+
}
|
|
1098
|
+
this.pathToKeys.delete(jayHtmlPath);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
/**
|
|
1102
|
+
* Invalidate all entries that depend on a changed file.
|
|
1103
|
+
* The changedPath could be:
|
|
1104
|
+
* - A jay-html file itself
|
|
1105
|
+
* - A component file (page.ts)
|
|
1106
|
+
* - Any other dependency
|
|
1107
|
+
*
|
|
1108
|
+
* @param changedPath - Absolute path to the changed file
|
|
1109
|
+
* @param resolveDependencies - Optional function to resolve which jay-html files depend on the changed file
|
|
1110
|
+
*/
|
|
1111
|
+
async invalidateByDependency(changedPath, resolveDependencies) {
|
|
1112
|
+
if (changedPath.endsWith(".jay-html")) {
|
|
1113
|
+
await this.invalidate(changedPath);
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
if (resolveDependencies) {
|
|
1117
|
+
const dependentPaths = resolveDependencies(changedPath);
|
|
1118
|
+
for (const depPath of dependentPaths) {
|
|
1119
|
+
await this.invalidate(depPath);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Clear all cached entries and delete cached files from disk
|
|
1125
|
+
*/
|
|
1126
|
+
async clear() {
|
|
1127
|
+
for (const entry of this.cache.values()) {
|
|
1128
|
+
try {
|
|
1129
|
+
await fs$1.unlink(entry.preRenderedPath);
|
|
1130
|
+
} catch {
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
this.cache.clear();
|
|
1134
|
+
this.pathToKeys.clear();
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Get the number of cached entries
|
|
1138
|
+
*/
|
|
1139
|
+
get size() {
|
|
1140
|
+
return this.cache.size;
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* Get all cached jay-html paths (for debugging/monitoring)
|
|
1144
|
+
*/
|
|
1145
|
+
getCachedPaths() {
|
|
1146
|
+
return Array.from(this.pathToKeys.keys());
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
964
1149
|
export {
|
|
965
1150
|
ActionRegistry,
|
|
966
1151
|
DevSlowlyChangingPhase,
|
|
1152
|
+
SlowRenderCache,
|
|
967
1153
|
actionRegistry,
|
|
968
1154
|
clearActionRegistry,
|
|
969
1155
|
clearClientInitData,
|
|
@@ -992,6 +1178,7 @@ export {
|
|
|
992
1178
|
registerService,
|
|
993
1179
|
renderFastChangingData,
|
|
994
1180
|
resolveServices,
|
|
1181
|
+
runAction,
|
|
995
1182
|
runInitCallbacks,
|
|
996
1183
|
runLoadParams,
|
|
997
1184
|
runShutdownCallbacks,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/stack-server-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.mts",
|
|
@@ -26,18 +26,18 @@
|
|
|
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/runtime": "^0.
|
|
34
|
-
"@jay-framework/stack-route-scanner": "^0.
|
|
35
|
-
"@jay-framework/view-state-merge": "^0.
|
|
29
|
+
"@jay-framework/compiler-jay-html": "^0.11.0",
|
|
30
|
+
"@jay-framework/compiler-shared": "^0.11.0",
|
|
31
|
+
"@jay-framework/component": "^0.11.0",
|
|
32
|
+
"@jay-framework/fullstack-component": "^0.11.0",
|
|
33
|
+
"@jay-framework/runtime": "^0.11.0",
|
|
34
|
+
"@jay-framework/stack-route-scanner": "^0.11.0",
|
|
35
|
+
"@jay-framework/view-state-merge": "^0.11.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@jay-framework/dev-environment": "^0.
|
|
39
|
-
"@jay-framework/jay-cli": "^0.
|
|
40
|
-
"@jay-framework/stack-client-runtime": "^0.
|
|
38
|
+
"@jay-framework/dev-environment": "^0.11.0",
|
|
39
|
+
"@jay-framework/jay-cli": "^0.11.0",
|
|
40
|
+
"@jay-framework/stack-client-runtime": "^0.11.0",
|
|
41
41
|
"@types/express": "^5.0.2",
|
|
42
42
|
"@types/node": "^22.15.21",
|
|
43
43
|
"nodemon": "^3.0.3",
|