@jay-framework/stack-server-runtime 0.15.3 → 0.15.5

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 CHANGED
@@ -539,6 +539,15 @@ interface PluginClientInitInfo {
539
539
  */
540
540
  declare function preparePluginClientInits(plugins: PluginWithInit[]): PluginClientInitInfo[];
541
541
 
542
+ /**
543
+ * Generate JS code to reconstruct Promise.resolve()/Promise.reject() for async ViewState properties.
544
+ * JSON.stringify drops Promise objects, so this emits assignments that recreate them from tracked outcomes.
545
+ */
546
+ declare function generatePromiseReconstruction(outcomes: Array<{
547
+ id: string;
548
+ status: 'resolved' | 'rejected';
549
+ value: any;
550
+ }>): string;
542
551
  /**
543
552
  * Information needed to generate client init script for the project.
544
553
  */
@@ -583,7 +592,21 @@ declare function buildScriptFragments(parts: DevServerPagePart[], clientInitData
583
592
  * @param mode - 'client' appends to DOM; 'hydrate' skips appendChild (DOM already present)
584
593
  */
585
594
  declare function buildAutomationWrap(options: GenerateClientScriptOptions, mode: 'client' | 'hydrate'): string;
586
- 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;
595
+ /**
596
+ * Resolve all top-level Promise values in a ViewState object.
597
+ * Returns the ViewState with Promises replaced by their resolved values,
598
+ * plus a list of outcomes for reconstructing Promise.resolve()/Promise.reject()
599
+ * in the client script.
600
+ */
601
+ declare function resolveViewStatePromises(viewState: object): Promise<{
602
+ resolved: object;
603
+ outcomes: Array<{
604
+ id: string;
605
+ status: 'resolved' | 'rejected';
606
+ value: any;
607
+ }>;
608
+ }>;
609
+ 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): Promise<string>;
587
610
 
588
611
  /**
589
612
  * Invalidate the cached server element module for a jay-html file.
@@ -1127,4 +1150,4 @@ declare function executePluginReferences(plugin: PluginWithReferences, options:
1127
1150
  verbose?: boolean;
1128
1151
  }): Promise<PluginReferencesResult>;
1129
1152
 
1130
- export { type ActionDiscoveryOptions, type ActionDiscoveryResult, type ActionErrorResponse, type ActionExecutionResult, type ActionIndexEntry, type ActionMetadata, ActionRegistry, type ActionSchema, 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 ScannedPlugin, type ScriptFragments, 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, generateSSRPageHtml, getActionCacheHeaders, getClientInitData, getClientInitDataForKey, getRegisteredAction, getRegisteredActionNames, getService, getServiceRegistry, hasAction, hasService, invalidateServerElementCache, listContracts, loadActionMetadata, loadPageParts, materializeContracts, onInit, onShutdown, parseActionMetadata, preparePluginClientInits, registerAction, registerService, renderFastChangingData, resolveActionMetadataPath, resolveServices, runInitCallbacks, runLoadParams, runShutdownCallbacks, runSlowlyChangingRender, scanPlugins, setClientInitData, slowRenderInstances, sortPluginsByDependencies, validateForEachInstances };
1153
+ export { type ActionDiscoveryOptions, type ActionDiscoveryResult, type ActionErrorResponse, type ActionExecutionResult, type ActionIndexEntry, type ActionMetadata, ActionRegistry, type ActionSchema, 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 ScannedPlugin, type ScriptFragments, 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, generatePromiseReconstruction, generateSSRPageHtml, getActionCacheHeaders, getClientInitData, getClientInitDataForKey, getRegisteredAction, getRegisteredActionNames, getService, getServiceRegistry, hasAction, hasService, invalidateServerElementCache, listContracts, loadActionMetadata, loadPageParts, materializeContracts, onInit, onShutdown, parseActionMetadata, preparePluginClientInits, registerAction, registerService, renderFastChangingData, resolveActionMetadataPath, resolveServices, resolveViewStatePromises, runInitCallbacks, runLoadParams, runShutdownCallbacks, runSlowlyChangingRender, scanPlugins, setClientInitData, slowRenderInstances, sortPluginsByDependencies, validateForEachInstances };
package/dist/index.js CHANGED
@@ -15,10 +15,10 @@ import { parseJayFile, JAY_IMPORT_RESOLVER, generateServerElementFile, discoverH
15
15
  import fs$2 from "node:fs/promises";
16
16
  import * as path from "node:path";
17
17
  import path__default from "node:path";
18
+ import crypto from "node:crypto";
18
19
  import { getLogger } from "@jay-framework/logger";
19
20
  import * as fs from "node:fs";
20
21
  import { createRequire as createRequire$1 } from "node:module";
21
- import crypto from "node:crypto";
22
22
  const serviceRegistry = /* @__PURE__ */ new Map();
23
23
  function registerService(marker, service) {
24
24
  serviceRegistry.set(marker, service);
@@ -203,7 +203,7 @@ new JayObjectType("Error", {
203
203
  stack: new JayAtomicType("string")
204
204
  });
205
205
  function isOptionalType(aType) {
206
- return aType.kind === 13;
206
+ return aType.kind === 14;
207
207
  }
208
208
  function isAtomicType(aType) {
209
209
  return aType.kind === 0;
@@ -220,6 +220,9 @@ function isObjectType(aType) {
220
220
  function isArrayType(aType) {
221
221
  return aType.kind === 9;
222
222
  }
223
+ function isRecordType(aType) {
224
+ return aType.kind === 11;
225
+ }
223
226
  function jayTypeToJsonSchema(type) {
224
227
  if (isOptionalType(type)) {
225
228
  return jayTypeToJsonSchema(type.innerType);
@@ -244,6 +247,13 @@ function jayTypeToJsonSchema(type) {
244
247
  }
245
248
  return { type: "array" };
246
249
  }
250
+ if (isRecordType(type)) {
251
+ const valueSchema = jayTypeToJsonSchema(type.itemType);
252
+ if (valueSchema) {
253
+ return { type: "object", additionalProperties: valueSchema };
254
+ }
255
+ return { type: "object" };
256
+ }
247
257
  if (isObjectType(type)) {
248
258
  const properties = {};
249
259
  const required = [];
@@ -483,6 +493,18 @@ async function renderFastChangingData(pageParams, pageProps, carryForward, parts
483
493
  }
484
494
  return Promise.resolve(phaseOutput(fastViewState, fastCarryForward));
485
495
  }
496
+ function generatePromiseReconstruction(outcomes) {
497
+ if (outcomes.length === 0)
498
+ return "";
499
+ return outcomes.map((outcome) => {
500
+ if (outcome.status === "resolved") {
501
+ return ` viewState[${JSON.stringify(outcome.id)}] = Promise.resolve(${JSON.stringify(outcome.value)});`;
502
+ } else {
503
+ const errMsg = outcome.value?.message ?? "Unknown error";
504
+ return ` viewState[${JSON.stringify(outcome.id)}] = Promise.reject(new Error(${JSON.stringify(errMsg)}));`;
505
+ }
506
+ }).join("\n") + "\n";
507
+ }
486
508
  function buildScriptFragments(parts, clientInitData2, projectInit, pluginInits, options) {
487
509
  const { enableAutomation = true, slowViewState } = options;
488
510
  const hasSlowViewState = slowViewState && Object.keys(slowViewState).length > 0;
@@ -558,7 +580,34 @@ function buildAutomationWrap(options, mode) {
558
580
  window.__jay.automation = wrapped.automation;
559
581
  window.dispatchEvent(new Event('jay:automation-ready'));${appendLine}`;
560
582
  }
561
- function generateClientScript(defaultViewState, fastCarryForward, parts, jayHtmlPath, trackByMap = {}, clientInitData2 = {}, projectInit, pluginInits = [], options = {}) {
583
+ async function resolveViewStatePromises(viewState) {
584
+ const entries = Object.entries(viewState);
585
+ const hasPromises = entries.some(([, v]) => v instanceof Promise);
586
+ if (!hasPromises)
587
+ return { resolved: viewState, outcomes: [] };
588
+ const result = { ...viewState };
589
+ const outcomes = [];
590
+ for (const [key, value] of entries) {
591
+ if (value instanceof Promise) {
592
+ try {
593
+ const val = await value;
594
+ result[key] = val;
595
+ outcomes.push({ id: key, status: "resolved", value: val });
596
+ } catch (err) {
597
+ delete result[key];
598
+ outcomes.push({
599
+ id: key,
600
+ status: "rejected",
601
+ value: { message: err?.message ?? String(err) }
602
+ });
603
+ }
604
+ }
605
+ }
606
+ return { resolved: result, outcomes };
607
+ }
608
+ async function generateClientScript(defaultViewState, fastCarryForward, parts, jayHtmlPath, trackByMap = {}, clientInitData2 = {}, projectInit, pluginInits = [], options = {}) {
609
+ const { resolved, outcomes } = await resolveViewStatePromises(defaultViewState);
610
+ defaultViewState = resolved;
562
611
  const {
563
612
  partImports,
564
613
  compositeParts,
@@ -586,7 +635,7 @@ function generateClientScript(defaultViewState, fastCarryForward, parts, jayHtml
586
635
  import { render } from '${jayHtmlPath}';
587
636
  ${partImports}${slowViewStateDecl}
588
637
  const viewState = ${JSON.stringify(defaultViewState)};
589
- const fastCarryForward = ${JSON.stringify(fastCarryForward)};
638
+ ${generatePromiseReconstruction(outcomes)} const fastCarryForward = ${JSON.stringify(fastCarryForward)};
590
639
  const trackByMap = ${JSON.stringify(trackByMap)};
591
640
  ${clientInitExecution}
592
641
  const target = document.getElementById('target');
@@ -598,6 +647,10 @@ ${automationWrap}
598
647
  </body>
599
648
  </html>`;
600
649
  }
650
+ function asyncSwapScript(id, html) {
651
+ const escapedHtml = html.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
652
+ 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
+ }
601
654
  const serverModuleCache = /* @__PURE__ */ new Map();
602
655
  function invalidateServerElementCache(jayHtmlPath) {
603
656
  if (serverModuleCache.delete(jayHtmlPath)) {
@@ -625,6 +678,7 @@ async function generateSSRPageHtml(vite, jayHtmlContent, jayHtmlFilename, jayHtm
625
678
  }
626
679
  const htmlChunks = [];
627
680
  const asyncPromises = [];
681
+ const asyncOutcomes = [];
628
682
  const ctx = {
629
683
  write: (chunk) => {
630
684
  htmlChunks.push(chunk);
@@ -632,14 +686,20 @@ async function generateSSRPageHtml(vite, jayHtmlContent, jayHtmlFilename, jayHtm
632
686
  onAsync: (promise, id, templates) => {
633
687
  const asyncPromise = promise.then(
634
688
  (val) => {
689
+ asyncOutcomes.push({ id, status: "resolved", value: val });
635
690
  if (templates.resolved) {
636
- return templates.resolved(val);
691
+ return asyncSwapScript(id, templates.resolved(val));
637
692
  }
638
693
  return "";
639
694
  },
640
695
  (err) => {
696
+ asyncOutcomes.push({
697
+ id,
698
+ status: "rejected",
699
+ value: { message: err?.message ?? String(err) }
700
+ });
641
701
  if (templates.rejected) {
642
- return templates.rejected(err);
702
+ return asyncSwapScript(id, templates.rejected(err));
643
703
  }
644
704
  return "";
645
705
  }
@@ -660,16 +720,15 @@ async function generateSSRPageHtml(vite, jayHtmlContent, jayHtmlFilename, jayHtm
660
720
  clientInitData2,
661
721
  projectInit,
662
722
  pluginInits,
663
- options
723
+ options,
724
+ asyncOutcomes
664
725
  );
665
726
  const headLinksHtml = cached.headLinks.map((link) => {
666
727
  const attrs = Object.entries(link.attributes).map(([k, v]) => ` ${k}="${v}"`).join("");
667
728
  return ` <link rel="${link.rel}" href="${link.href}"${attrs} />`;
668
729
  }).join("\n");
669
- const inlineCss = cached.css ? ` <style>
670
- ${cached.css}
671
- </style>` : "";
672
- const headExtras = [headLinksHtml, inlineCss].filter((_) => _).join("\n");
730
+ const cssLink = cached.cssHref ? ` <link rel="stylesheet" href="${cached.cssHref}" />` : "";
731
+ const headExtras = [headLinksHtml, cssLink].filter((_) => _).join("\n");
673
732
  return `<!doctype html>
674
733
  <html lang="en">
675
734
  <head>
@@ -727,13 +786,27 @@ async function compileAndLoadServerElement(vite, jayHtmlContent, jayHtmlFilename
727
786
  vite.moduleGraph.invalidateModule(existingModule);
728
787
  }
729
788
  const serverModule = await vite.ssrLoadModule(serverElementPath);
789
+ let cssHref;
790
+ if (parsedJayFile.css) {
791
+ const cssFilename = jayHtmlFilename.replace(".jay-html", ".css");
792
+ const cssPath = path__default.join(serverElementDir, cssFilename);
793
+ await fs$2.writeFile(cssPath, parsedJayFile.css, "utf-8");
794
+ const cssModules = vite.moduleGraph.getModulesByFile(cssPath);
795
+ if (cssModules) {
796
+ for (const mod of cssModules) {
797
+ vite.moduleGraph.invalidateModule(mod);
798
+ }
799
+ }
800
+ const hash = crypto.createHash("md5").update(parsedJayFile.css).digest("hex").slice(0, 8);
801
+ cssHref = "/@fs" + cssPath + "?v=" + hash + "&direct";
802
+ }
730
803
  return {
731
804
  renderToStream: serverModule.renderToStream,
732
805
  headLinks: parsedJayFile.headLinks,
733
- css: parsedJayFile.css
806
+ cssHref
734
807
  };
735
808
  }
736
- function generateHydrationScript(defaultViewState, fastCarryForward, parts, jayHtmlPath, trackByMap = {}, clientInitData2 = {}, projectInit, pluginInits = [], options = {}) {
809
+ function generateHydrationScript(defaultViewState, fastCarryForward, parts, jayHtmlPath, trackByMap = {}, clientInitData2 = {}, projectInit, pluginInits = [], options = {}, asyncOutcomes = []) {
737
810
  const {
738
811
  partImports,
739
812
  compositeParts,
@@ -753,7 +826,7 @@ function generateHydrationScript(defaultViewState, fastCarryForward, parts, jayH
753
826
  import { hydrate } from '${hydrateImportPath}';
754
827
  ${partImports}${slowViewStateDecl}
755
828
  const viewState = ${JSON.stringify(defaultViewState)};
756
- const fastCarryForward = ${JSON.stringify(fastCarryForward)};
829
+ ${generatePromiseReconstruction(asyncOutcomes)} const fastCarryForward = ${JSON.stringify(fastCarryForward)};
757
830
  const trackByMap = ${JSON.stringify(trackByMap)};
758
831
 
759
832
  const target = document.getElementById('target');
@@ -1906,7 +1979,7 @@ class SlowRenderCache {
1906
1979
  try {
1907
1980
  const files = await fs$2.readdir(cacheSubDir);
1908
1981
  for (const file of files) {
1909
- if (file.startsWith(basename) && file.endsWith(".jay-html")) {
1982
+ if (file.startsWith(basename)) {
1910
1983
  try {
1911
1984
  await fs$2.unlink(path__default.join(cacheSubDir, file));
1912
1985
  } catch {
@@ -2402,6 +2475,7 @@ export {
2402
2475
  executePluginServerInits,
2403
2476
  executePluginSetup,
2404
2477
  generateClientScript,
2478
+ generatePromiseReconstruction,
2405
2479
  generateSSRPageHtml,
2406
2480
  getActionCacheHeaders,
2407
2481
  getClientInitData,
@@ -2426,6 +2500,7 @@ export {
2426
2500
  renderFastChangingData,
2427
2501
  resolveActionMetadataPath,
2428
2502
  resolveServices,
2503
+ resolveViewStatePromises,
2429
2504
  runInitCallbacks,
2430
2505
  runLoadParams,
2431
2506
  runShutdownCallbacks,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/stack-server-runtime",
3
- "version": "0.15.3",
3
+ "version": "0.15.5",
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.15.3",
30
- "@jay-framework/compiler-shared": "^0.15.3",
31
- "@jay-framework/component": "^0.15.3",
32
- "@jay-framework/fullstack-component": "^0.15.3",
33
- "@jay-framework/logger": "^0.15.3",
34
- "@jay-framework/runtime": "^0.15.3",
35
- "@jay-framework/ssr-runtime": "^0.15.3",
36
- "@jay-framework/stack-route-scanner": "^0.15.3",
37
- "@jay-framework/view-state-merge": "^0.15.3",
29
+ "@jay-framework/compiler-jay-html": "^0.15.5",
30
+ "@jay-framework/compiler-shared": "^0.15.5",
31
+ "@jay-framework/component": "^0.15.5",
32
+ "@jay-framework/fullstack-component": "^0.15.5",
33
+ "@jay-framework/logger": "^0.15.5",
34
+ "@jay-framework/runtime": "^0.15.5",
35
+ "@jay-framework/ssr-runtime": "^0.15.5",
36
+ "@jay-framework/stack-route-scanner": "^0.15.5",
37
+ "@jay-framework/view-state-merge": "^0.15.5",
38
38
  "yaml": "^2.3.4"
39
39
  },
40
40
  "devDependencies": {
41
- "@jay-framework/dev-environment": "^0.15.3",
42
- "@jay-framework/jay-cli": "^0.15.3",
43
- "@jay-framework/stack-client-runtime": "^0.15.3",
41
+ "@jay-framework/dev-environment": "^0.15.5",
42
+ "@jay-framework/jay-cli": "^0.15.5",
43
+ "@jay-framework/stack-client-runtime": "^0.15.5",
44
44
  "@types/express": "^5.0.2",
45
45
  "@types/node": "^22.15.21",
46
46
  "nodemon": "^3.0.3",