@jay-framework/dev-server 0.24.0 → 0.24.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +2 -1
  2. package/dist/index.js +1914 -45
  3. package/package.json +15 -14
package/dist/index.js CHANGED
@@ -7,31 +7,33 @@ import { createRequire } from "module";
7
7
  import "@jay-framework/compiler-shared";
8
8
  import * as path from "node:path";
9
9
  import path__default from "node:path";
10
- import { injectHeadfullFSTemplates, JAY_IMPORT_RESOLVER, parseContract, slowRenderTransform, assignCoordinatesToJayHtml, discoverHeadlessInstances } from "@jay-framework/compiler-jay-html";
10
+ import { parseJayFile, JAY_IMPORT_RESOLVER, injectHeadfullFSTemplates, discoverHeadlessInstances, assignCoordinatesToJayHtml, parseAction, generateServerElementFile, parseContract, slowRenderTransform } from "@jay-framework/compiler-jay-html";
11
11
  import { getLogger, getDevLogger } from "@jay-framework/logger";
12
12
  import { createRequire as createRequire$1 } from "node:module";
13
- import * as fsSync from "node:fs";
14
- import fsSync__default from "node:fs";
13
+ import * as fs from "node:fs";
14
+ import fs__default$1 from "node:fs";
15
15
  import { scanRoutes, createRoute, routeToExpressRoute } from "@jay-framework/stack-route-scanner";
16
- import { discoverPluginsWithInit, sortPluginsByDependencies, executePluginServerInits, runInitCallbacks, discoverAndRegisterActions, actionRegistry, discoverAllPluginActions, runShutdownCallbacks, clearLifecycleCallbacks, clearServiceRegistry, clearClientInitData, loadPageParts, runLoadParams, DevSlowlyChangingPhase, SlowRenderCache, preparePluginClientInits, registerService, getServiceRegistry, materializeContracts, scanPlugins, clearServerElementCache, parseCookies, generateFrozenPageHtml, renderFastChangingData, generateClientScript, getClientInitData, mergeHeadTags, generateSSRPageHtml, slowRenderInstances, normalizeAndResolveInstanceProps } from "@jay-framework/stack-server-runtime";
17
- import * as fs from "node:fs/promises";
16
+ import { actionRegistry, scanPlugins, serializeHeadTags, mergeHeadTags, discoverPluginsWithInit, sortPluginsByDependencies, executePluginServerInits, runInitCallbacks, runShutdownCallbacks, clearLifecycleCallbacks, clearServiceRegistry, clearClientInitData, runLoadParams, DevSlowlyChangingPhase, preparePluginClientInits, registerService, getServiceRegistry, parseCookies, renderFastChangingData, getClientInitData, slowRenderInstances, normalizeAndResolveInstanceProps } from "@jay-framework/stack-server-runtime";
17
+ import * as fs$1 from "node:fs/promises";
18
18
  import fs__default from "node:fs/promises";
19
+ import jsBeautify from "js-beautify";
20
+ import fs$2 from "fs";
21
+ import path$1 from "path";
22
+ import YAML from "yaml";
23
+ import crypto, { randomUUID } from "node:crypto";
24
+ import { isJayAction, isJayStreamAction, createJayService } from "@jay-framework/fullstack-component";
19
25
  import { pathToFileURL } from "node:url";
20
26
  import Busboy from "busboy";
21
- import fs$1 from "fs";
22
- import path$1 from "path";
23
- import crypto from "crypto";
24
- import { randomUUID } from "node:crypto";
25
- import { createJayService } from "@jay-framework/fullstack-component";
26
- const s$1 = createRequire(import.meta.url), e$1 = s$1("typescript"), c$1 = new Proxy(e$1, {
27
+ import crypto$1 from "crypto";
28
+ const s$2 = createRequire(import.meta.url), e$2 = s$2("typescript"), c$1 = new Proxy(e$2, {
27
29
  get(t, r) {
28
30
  return t[r];
29
31
  }
30
32
  });
31
- var __defProp2 = Object.defineProperty;
32
- var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
33
- var __publicField2 = (obj, key, value) => __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
34
- const s = createRequire(import.meta.url), e = s("typescript"), c = new Proxy(e, {
33
+ var __defProp$1 = Object.defineProperty;
34
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
35
+ var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
36
+ const s$1 = createRequire(import.meta.url), e$1 = s$1("typescript"), c = new Proxy(e$1, {
35
37
  get(t, r) {
36
38
  return t[r];
37
39
  }
@@ -180,7 +182,7 @@ function tsBindingNameToVariable(binding, accessedFrom, assignedFrom, propertyNa
180
182
  }
181
183
  class NameBindingResolver {
182
184
  constructor(parentNameResolver) {
183
- __publicField2(this, "variables", /* @__PURE__ */ new Map());
185
+ __publicField$1(this, "variables", /* @__PURE__ */ new Map());
184
186
  this.parentNameResolver = parentNameResolver;
185
187
  }
186
188
  addVariable(name, variable) {
@@ -455,7 +457,7 @@ class GlobalResolvedType {
455
457
  }
456
458
  class SourceFileBindingResolver {
457
459
  constructor(sourceFile) {
458
- __publicField2(this, "nameBindingResolvers", /* @__PURE__ */ new Map());
460
+ __publicField$1(this, "nameBindingResolvers", /* @__PURE__ */ new Map());
459
461
  this.nameBindingResolvers.set(sourceFile, new NameBindingResolver());
460
462
  const nbResolversQueue = [
461
463
  this.nameBindingResolvers.get(sourceFile)
@@ -1244,7 +1246,7 @@ function createImportChainTracker(options = {}) {
1244
1246
  }
1245
1247
  };
1246
1248
  }
1247
- const require$1 = createRequire$1(import.meta.url);
1249
+ const require$1$1 = createRequire$1(import.meta.url);
1248
1250
  function createDefaultPluginDetector() {
1249
1251
  const cache = /* @__PURE__ */ new Map();
1250
1252
  return {
@@ -1255,9 +1257,9 @@ function createDefaultPluginDetector() {
1255
1257
  }
1256
1258
  let result = false;
1257
1259
  try {
1258
- require$1.resolve(`${packageName}/plugin.yaml`, { paths: [projectRoot] });
1260
+ require$1$1.resolve(`${packageName}/plugin.yaml`, { paths: [projectRoot] });
1259
1261
  try {
1260
- require$1.resolve(`${packageName}/client`, { paths: [projectRoot] });
1262
+ require$1$1.resolve(`${packageName}/client`, { paths: [projectRoot] });
1261
1263
  result = true;
1262
1264
  } catch {
1263
1265
  result = false;
@@ -1428,16 +1430,16 @@ function jayStackCompiler(options = {}) {
1428
1430
  const importerDir = path.dirname(importer);
1429
1431
  let resolvedPath = path.resolve(importerDir, source);
1430
1432
  if (!resolvedPath.endsWith(".ts") && !resolvedPath.endsWith(".js")) {
1431
- if (fsSync.existsSync(resolvedPath + ".ts")) {
1433
+ if (fs.existsSync(resolvedPath + ".ts")) {
1432
1434
  resolvedPath += ".ts";
1433
- } else if (fsSync.existsSync(resolvedPath + ".js")) {
1435
+ } else if (fs.existsSync(resolvedPath + ".js")) {
1434
1436
  resolvedPath += ".js";
1435
1437
  } else {
1436
1438
  return null;
1437
1439
  }
1438
- } else if (resolvedPath.endsWith(".js") && !fsSync.existsSync(resolvedPath)) {
1440
+ } else if (resolvedPath.endsWith(".js") && !fs.existsSync(resolvedPath)) {
1439
1441
  const tsPath = resolvedPath.slice(0, -3) + ".ts";
1440
- if (fsSync.existsSync(tsPath)) {
1442
+ if (fs.existsSync(tsPath)) {
1441
1443
  resolvedPath = tsPath;
1442
1444
  } else {
1443
1445
  return null;
@@ -1452,7 +1454,7 @@ function jayStackCompiler(options = {}) {
1452
1454
  const actualPath = id.slice("\0jay-action:".length);
1453
1455
  let code;
1454
1456
  try {
1455
- code = await fsSync.promises.readFile(actualPath, "utf-8");
1457
+ code = await fs.promises.readFile(actualPath, "utf-8");
1456
1458
  } catch (err) {
1457
1459
  getLogger().error(
1458
1460
  `[action-transform] Could not read ${actualPath}: ${err}`
@@ -1610,6 +1612,1873 @@ async function createViteForCli(options) {
1610
1612
  });
1611
1613
  return vite;
1612
1614
  }
1615
+ var __defProp2 = Object.defineProperty;
1616
+ var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1617
+ var __publicField2 = (obj, key, value) => __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
1618
+ const require$3 = createRequire(import.meta.url);
1619
+ async function loadPageParts(vite, route, pagesBase, projectBase, jayRollupConfig, options) {
1620
+ const exists = await fs__default.access(route.compPath, fs__default.constants.F_OK).then(() => true).catch(() => false);
1621
+ const parts = [];
1622
+ const validations = [];
1623
+ if (exists) {
1624
+ const exportName = route.componentExport || "page";
1625
+ const pageComponent = (await vite.ssrLoadModule(route.compPath))[exportName];
1626
+ if (!pageComponent) {
1627
+ validations.push(
1628
+ `${route.compPath} does not export "${exportName}". The file exists but the expected export is missing — remove the file or add the export.`
1629
+ );
1630
+ } else {
1631
+ const clientImportPath = route.componentExport ? route.compPath.replace(/index\.js$/, "index.client.js") : route.compPath;
1632
+ parts.push({
1633
+ compDefinition: pageComponent,
1634
+ clientImport: `import {${exportName}} from '${clientImportPath}'`,
1635
+ clientPart: `{comp: ${exportName}.comp, contextMarkers: ${exportName}.contexts || []}`
1636
+ });
1637
+ }
1638
+ }
1639
+ const jayHtmlFilePath = options?.preRenderedPath ?? route.jayHtmlPath;
1640
+ const jayHtmlSource = options?.preRenderedContent ?? (await fs__default.readFile(jayHtmlFilePath)).toString();
1641
+ const fileName = path__default.basename(route.jayHtmlPath);
1642
+ const dirName = path__default.dirname(route.jayHtmlPath);
1643
+ const jayHtmlWithValidations = await parseJayFile(
1644
+ jayHtmlSource,
1645
+ fileName,
1646
+ dirName,
1647
+ {
1648
+ relativePath: jayRollupConfig.tsConfigFilePath
1649
+ },
1650
+ JAY_IMPORT_RESOLVER,
1651
+ projectBase
1652
+ );
1653
+ jayHtmlWithValidations.validations.push(...validations);
1654
+ return jayHtmlWithValidations.mapAsync(async (jayHtml) => {
1655
+ const usedPackages = /* @__PURE__ */ new Set();
1656
+ const headlessInstanceComponents = [];
1657
+ for await (const headlessImport of jayHtml.headlessImports) {
1658
+ const module = headlessImport.codeLink.module;
1659
+ const name = headlessImport.codeLink.names[0].name;
1660
+ const isLocalModule = module[0] === "." || module[0] === "/";
1661
+ let compDefinition;
1662
+ let clientModuleImport;
1663
+ let isNpmPackage;
1664
+ if (headlessImport.structural) {
1665
+ continue;
1666
+ } else {
1667
+ const modulePath = isLocalModule ? path__default.resolve(dirName, module) : require$3.resolve(module, { paths: require$3.resolve.paths(dirName) });
1668
+ compDefinition = (await vite.ssrLoadModule(modulePath))[name];
1669
+ const moduleImport = isLocalModule ? path__default.resolve(dirName, module) : module;
1670
+ isNpmPackage = !isLocalModule;
1671
+ clientModuleImport = isNpmPackage ? `${moduleImport}/client` : `${moduleImport}`;
1672
+ }
1673
+ if (isNpmPackage) {
1674
+ const packageName = module.startsWith("@") ? module.split("/").slice(0, 2).join("/") : module.split("/")[0];
1675
+ usedPackages.add(packageName);
1676
+ }
1677
+ if (headlessImport.key) {
1678
+ const key = headlessImport.key;
1679
+ const hasClientComp = !!compDefinition?.fastRender || !!compDefinition?.hasInteractive;
1680
+ const part = {
1681
+ key,
1682
+ compDefinition,
1683
+ clientImport: hasClientComp ? `import {${name}} from '${clientModuleImport}'` : "",
1684
+ clientPart: hasClientComp ? `{comp: ${name}.comp, contextMarkers: ${name}.contexts || [], key: '${key}'}` : "",
1685
+ contractInfo: headlessImport.contract ? {
1686
+ contractName: headlessImport.contract.name,
1687
+ metadata: headlessImport.metadata
1688
+ } : void 0,
1689
+ headlessProps: headlessImport.headlessProps
1690
+ };
1691
+ parts.push(part);
1692
+ }
1693
+ if (!headlessImport.key && headlessImport.contract) {
1694
+ headlessInstanceComponents.push({
1695
+ contractName: headlessImport.contractName,
1696
+ compDefinition,
1697
+ contract: headlessImport.contract
1698
+ });
1699
+ }
1700
+ }
1701
+ const headlessContracts = jayHtml.headlessImports.filter((hi) => hi.contract !== void 0 && hi.key !== void 0).map((hi) => ({
1702
+ key: hi.key,
1703
+ contract: hi.contract,
1704
+ contractPath: hi.contractPath
1705
+ }));
1706
+ const jayHtmlForDiscovery = injectHeadfullFSTemplates(
1707
+ jayHtmlSource,
1708
+ dirName,
1709
+ JAY_IMPORT_RESOLVER
1710
+ );
1711
+ let discoveryResult;
1712
+ if (headlessInstanceComponents.length > 0) {
1713
+ const firstDiscovery = discoverHeadlessInstances(jayHtmlForDiscovery);
1714
+ const headlessContractNameSet = new Set(
1715
+ jayHtml.headlessImports.map((hi) => hi.contractName)
1716
+ );
1717
+ const jayHtmlWithCoords = assignCoordinatesToJayHtml(
1718
+ firstDiscovery.preRenderedJayHtml,
1719
+ headlessContractNameSet
1720
+ );
1721
+ discoveryResult = discoverHeadlessInstances(jayHtmlWithCoords);
1722
+ } else {
1723
+ discoveryResult = {
1724
+ instances: [],
1725
+ forEachInstances: [],
1726
+ preRenderedJayHtml: jayHtmlSource
1727
+ };
1728
+ }
1729
+ return {
1730
+ parts,
1731
+ serverTrackByMap: jayHtml.serverTrackByMap,
1732
+ clientTrackByMap: jayHtml.clientTrackByMap,
1733
+ usedPackages,
1734
+ headlessContracts,
1735
+ headlessInstanceComponents,
1736
+ discoveredInstances: discoveryResult.instances,
1737
+ forEachInstances: discoveryResult.forEachInstances,
1738
+ linkedCssFiles: jayHtml.linkedCssFiles ?? [],
1739
+ linkedComponentFiles: jayHtml.linkedComponentFiles ?? [],
1740
+ scripts: jayHtml.scripts
1741
+ };
1742
+ });
1743
+ }
1744
+ var __defProp22 = Object.defineProperty;
1745
+ var __defNormalProp22 = (obj, key, value) => key in obj ? __defProp22(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1746
+ var __publicField22 = (obj, key, value) => __defNormalProp22(obj, key + "", value);
1747
+ class JayAtomicType {
1748
+ constructor(name) {
1749
+ __publicField22(this, "kind", 0);
1750
+ this.name = name;
1751
+ }
1752
+ }
1753
+ new JayAtomicType("string");
1754
+ new JayAtomicType("string");
1755
+ new JayAtomicType("number");
1756
+ new JayAtomicType("boolean");
1757
+ new JayAtomicType("Date");
1758
+ new JayAtomicType("file");
1759
+ new JayAtomicType("Unknown");
1760
+ class JayObjectType {
1761
+ constructor(name, props) {
1762
+ __publicField22(this, "kind", 8);
1763
+ this.name = name;
1764
+ this.props = props;
1765
+ }
1766
+ }
1767
+ new JayObjectType("Error", {
1768
+ message: new JayAtomicType("string"),
1769
+ name: new JayAtomicType("string"),
1770
+ stack: new JayAtomicType("string")
1771
+ });
1772
+ function isOptionalType(aType) {
1773
+ return aType.kind === 14;
1774
+ }
1775
+ function isAtomicType(aType) {
1776
+ return aType.kind === 0;
1777
+ }
1778
+ function isEnumType(aType) {
1779
+ return aType.kind === 2;
1780
+ }
1781
+ function isImportedType(aType) {
1782
+ return aType.kind === 4;
1783
+ }
1784
+ function isObjectType(aType) {
1785
+ return aType.kind === 8;
1786
+ }
1787
+ function isArrayType(aType) {
1788
+ return aType.kind === 9;
1789
+ }
1790
+ function isRecordType(aType) {
1791
+ return aType.kind === 11;
1792
+ }
1793
+ function jayTypeToJsonSchema(type) {
1794
+ if (isOptionalType(type)) {
1795
+ return jayTypeToJsonSchema(type.innerType);
1796
+ }
1797
+ if (isAtomicType(type)) {
1798
+ const name = type.name.toLowerCase();
1799
+ if (name === "string" || name === "number" || name === "boolean") {
1800
+ return { type: name };
1801
+ }
1802
+ if (name === "file") {
1803
+ return { type: "string", description: "Binary file upload (JayFile)" };
1804
+ }
1805
+ return { type: "string" };
1806
+ }
1807
+ if (isEnumType(type)) {
1808
+ return { type: "string", enum: type.values };
1809
+ }
1810
+ if (isImportedType(type)) {
1811
+ return { type: "object", description: `Contract: ${type.name}` };
1812
+ }
1813
+ if (isArrayType(type)) {
1814
+ const itemSchema = jayTypeToJsonSchema(type.itemType);
1815
+ if (itemSchema) {
1816
+ return { type: "array", items: itemSchema };
1817
+ }
1818
+ return { type: "array" };
1819
+ }
1820
+ if (isRecordType(type)) {
1821
+ const valueSchema = jayTypeToJsonSchema(type.itemType);
1822
+ if (valueSchema) {
1823
+ return { type: "object", additionalProperties: valueSchema };
1824
+ }
1825
+ return { type: "object" };
1826
+ }
1827
+ if (isObjectType(type)) {
1828
+ const properties = {};
1829
+ const required = [];
1830
+ for (const [key, propType] of Object.entries(type.props)) {
1831
+ const isOpt = isOptionalType(propType);
1832
+ const schema = jayTypeToJsonSchema(propType);
1833
+ if (schema) {
1834
+ properties[key] = schema;
1835
+ if (!isOpt) {
1836
+ required.push(key);
1837
+ }
1838
+ }
1839
+ }
1840
+ return {
1841
+ type: "object",
1842
+ properties,
1843
+ ...required.length > 0 && { required }
1844
+ };
1845
+ }
1846
+ return null;
1847
+ }
1848
+ var RuntimeMode = /* @__PURE__ */ ((RuntimeMode2) => {
1849
+ RuntimeMode2["MainTrusted"] = "mainTrusted";
1850
+ RuntimeMode2["MainSandbox"] = "mainSandbox";
1851
+ RuntimeMode2["WorkerTrusted"] = "workerTrusted";
1852
+ RuntimeMode2["WorkerSandbox"] = "workerSandbox";
1853
+ return RuntimeMode2;
1854
+ })(RuntimeMode || {});
1855
+ const TS_EXTENSION = ".ts";
1856
+ const JAY_QUERY_PREFIX = "?jay-";
1857
+ const JAY_QUERY_HYDRATE = `${JAY_QUERY_PREFIX}hydrate`;
1858
+ [
1859
+ // Hydrate target
1860
+ {
1861
+ pattern: JAY_QUERY_HYDRATE,
1862
+ buildEnv: "client",
1863
+ isHydrate: true
1864
+ },
1865
+ // Build environments
1866
+ {
1867
+ pattern: `${JAY_QUERY_PREFIX}${"client"}`,
1868
+ buildEnv: "client"
1869
+ /* Client */
1870
+ },
1871
+ {
1872
+ pattern: `${JAY_QUERY_PREFIX}${"server"}`,
1873
+ buildEnv: "server"
1874
+ /* Server */
1875
+ },
1876
+ // Runtime modes (with .ts suffix)
1877
+ {
1878
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.MainSandbox}${TS_EXTENSION}`,
1879
+ runtimeMode: RuntimeMode.MainSandbox
1880
+ },
1881
+ {
1882
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.WorkerTrusted}${TS_EXTENSION}`,
1883
+ runtimeMode: RuntimeMode.WorkerTrusted
1884
+ },
1885
+ {
1886
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.WorkerSandbox}${TS_EXTENSION}`,
1887
+ runtimeMode: RuntimeMode.WorkerSandbox
1888
+ },
1889
+ // Runtime modes (without .ts suffix)
1890
+ {
1891
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.MainSandbox}`,
1892
+ runtimeMode: RuntimeMode.MainSandbox
1893
+ },
1894
+ {
1895
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.WorkerTrusted}`,
1896
+ runtimeMode: RuntimeMode.WorkerTrusted
1897
+ },
1898
+ {
1899
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.WorkerSandbox}`,
1900
+ runtimeMode: RuntimeMode.WorkerSandbox
1901
+ }
1902
+ ];
1903
+ function checkValidationErrors(withValidations) {
1904
+ const { validations } = withValidations;
1905
+ if (validations.length > 0) {
1906
+ throw new Error(validations.join("\n"));
1907
+ }
1908
+ return withValidations.val;
1909
+ }
1910
+ const { html: htmlBeautify } = jsBeautify;
1911
+ createRequire(import.meta.url);
1912
+ function normalizeActionEntry(entry) {
1913
+ if (typeof entry === "string") {
1914
+ return { name: entry };
1915
+ }
1916
+ return { name: entry.name, action: entry.action };
1917
+ }
1918
+ function loadPluginManifest(pluginDir) {
1919
+ const pluginYamlPath = path$1.join(pluginDir, "plugin.yaml");
1920
+ if (!fs$2.existsSync(pluginYamlPath)) {
1921
+ return null;
1922
+ }
1923
+ try {
1924
+ const yamlContent = fs$2.readFileSync(pluginYamlPath, "utf-8");
1925
+ return YAML.parse(yamlContent);
1926
+ } catch (error) {
1927
+ return null;
1928
+ }
1929
+ }
1930
+ const s = createRequire(import.meta.url), e = s("typescript");
1931
+ new Proxy(e, {
1932
+ get(t, r) {
1933
+ return t[r];
1934
+ }
1935
+ });
1936
+ function asyncSwapScript(id, html) {
1937
+ const escapedHtml = html.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
1938
+ 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>`;
1939
+ }
1940
+ function generatePromiseReconstruction(outcomes) {
1941
+ if (outcomes.length === 0) return "";
1942
+ return outcomes.map((outcome) => {
1943
+ if (outcome.status === "resolved") {
1944
+ return ` viewState[${JSON.stringify(outcome.id)}] = Promise.resolve(${JSON.stringify(outcome.value)});`;
1945
+ } else {
1946
+ const errMsg = outcome.value?.message ?? "Unknown error";
1947
+ return ` viewState[${JSON.stringify(outcome.id)}] = Promise.reject(new Error(${JSON.stringify(errMsg)}));`;
1948
+ }
1949
+ }).join("\n") + "\n";
1950
+ }
1951
+ function buildScriptFragments(parts, clientInitData, projectInit, pluginInits, options) {
1952
+ const { enableAutomation = true, slowViewState } = options;
1953
+ const hasSlowViewState = slowViewState && Object.keys(slowViewState).length > 0;
1954
+ const clientParts = parts.filter((part) => part.clientPart);
1955
+ const partImports = clientParts.length > 0 ? clientParts.map((part) => part.clientImport).join("\n") + "\n" : "";
1956
+ const compositeParts = clientParts.length > 0 ? `[
1957
+ ${clientParts.map((part) => " " + part.clientPart).join(",\n")}
1958
+ ]` : "[]";
1959
+ const hasClientInit = projectInit || pluginInits.length > 0;
1960
+ const pluginClientInitImports = pluginInits.map((plugin, idx) => {
1961
+ return `import { ${plugin.initExport} as jayInit${idx} } from "${plugin.importPath}";`;
1962
+ }).join("\n ");
1963
+ const projectInitImport = projectInit ? `import { ${projectInit.initExport || "init"} as projectJayInit } from "${projectInit.importPath}";` : "";
1964
+ const pluginClientInitCalls = pluginInits.map((plugin, idx) => {
1965
+ const pluginData = clientInitData[plugin.name] || {};
1966
+ return `if (typeof jayInit${idx}._clientInit === 'function') {
1967
+ console.log('[DevServer] Running client init: ${plugin.name}');
1968
+ await jayInit${idx}._clientInit(${JSON.stringify(pluginData)});
1969
+ }`;
1970
+ }).join("\n ");
1971
+ const projectInitCall = projectInit ? `if (typeof projectJayInit._clientInit === 'function') {
1972
+ console.log('[DevServer] Running client init: project');
1973
+ const projectData = ${JSON.stringify(clientInitData["project"] || {})};
1974
+ await projectJayInit._clientInit(projectData);
1975
+ }` : "";
1976
+ const clientInitExecution = hasClientInit ? `
1977
+ // Plugin client initialization (in dependency order)
1978
+ ${pluginClientInitCalls}
1979
+
1980
+ // Project client initialization
1981
+ ${projectInitCall}
1982
+ ` : "";
1983
+ const automationImport = enableAutomation ? hasSlowViewState ? `import { wrapWithAutomation, AUTOMATION_CONTEXT } from "@jay-framework/runtime-automation";
1984
+ import { registerGlobalContext } from "@jay-framework/runtime";
1985
+ import { deepMergeViewStates } from "@jay-framework/view-state-merge";` : `import { wrapWithAutomation, AUTOMATION_CONTEXT } from "@jay-framework/runtime-automation";
1986
+ import { registerGlobalContext } from "@jay-framework/runtime";` : "";
1987
+ const slowViewStateDecl = enableAutomation && hasSlowViewState ? `const slowViewState = ${JSON.stringify(slowViewState)};` : "";
1988
+ return {
1989
+ partImports,
1990
+ compositeParts,
1991
+ pluginClientInitImports,
1992
+ projectInitImport,
1993
+ clientInitExecution,
1994
+ automationImport,
1995
+ slowViewStateDecl
1996
+ };
1997
+ }
1998
+ function buildFreezeScript(routePattern) {
1999
+ const routePatternLiteral = routePattern ? `'${routePattern}'` : "undefined";
2000
+ return `
2001
+ // Page Freeze (DL#127, DL#128 iframe addendum)
2002
+ // Sticky embed mode: URL param sets a session cookie so in-iframe navigation preserves it
2003
+ if (new URLSearchParams(window.location.search).has('_jay_embed')) {
2004
+ document.cookie = '_jay_embed=1;path=/;samesite=lax';
2005
+ }
2006
+ const __jayEmbedMode = document.cookie.split(';').some(c => c.trim().startsWith('_jay_embed='));
2007
+
2008
+ async function __jayDoFreeze() {
2009
+ const automation = window.__jay?.automation;
2010
+ if (!automation) return;
2011
+
2012
+ // Visual feedback: white flash
2013
+ const flash = document.createElement('div');
2014
+ flash.style.cssText = 'position:fixed;inset:0;background:white;z-index:999999;opacity:0.8;pointer-events:none;transition:opacity 0.3s';
2015
+ document.body.appendChild(flash);
2016
+ requestAnimationFrame(() => { flash.style.opacity = '0'; });
2017
+ setTimeout(() => flash.remove(), 400);
2018
+
2019
+ // Audio feedback: camera shutter
2020
+ try {
2021
+ const ctx = new AudioContext();
2022
+ const buf = ctx.createBuffer(1, ctx.sampleRate * 0.15, ctx.sampleRate);
2023
+ const data = buf.getChannelData(0);
2024
+ for (let i = 0; i < data.length; i++) {
2025
+ data[i] = (Math.random() * 2 - 1) * Math.exp(-i / (ctx.sampleRate * 0.02));
2026
+ }
2027
+ const src = ctx.createBufferSource();
2028
+ src.buffer = buf;
2029
+ src.connect(ctx.destination);
2030
+ src.start();
2031
+ } catch {}
2032
+
2033
+ // Capture and save
2034
+ try {
2035
+ const state = automation.getPageState();
2036
+ const route = window.location.pathname;
2037
+ const routePattern = ${routePatternLiteral};
2038
+ const resp = await fetch('/_jay/freeze', {
2039
+ method: 'POST',
2040
+ headers: { 'Content-Type': 'application/json' },
2041
+ body: JSON.stringify({ route, routePattern, viewState: state.viewState }),
2042
+ });
2043
+ const { id } = await resp.json();
2044
+ if (__jayEmbedMode) {
2045
+ window.parent.postMessage({ type: 'jay:freeze', id, route }, '*');
2046
+ } else {
2047
+ window.open(route + '?_jay_freeze=' + id, '_blank');
2048
+ }
2049
+ } catch (err) {
2050
+ console.error('[Freeze] Failed:', err);
2051
+ }
2052
+ }
2053
+
2054
+ if (__jayEmbedMode) {
2055
+ // Notify parent of the current route on load (DL#128 route addendum)
2056
+ window.parent.postMessage({ type: 'jay:route', route: window.location.pathname, routePattern: ${routePatternLiteral} }, '*');
2057
+
2058
+ // Embed mode: parent triggers freeze via postMessage
2059
+ window.addEventListener('message', (e) => {
2060
+ if (e.data?.type === 'jay:requestFreeze') __jayDoFreeze();
2061
+ });
2062
+ } else {
2063
+ // Standalone: Alt+S / Option+S keyboard shortcut
2064
+ document.addEventListener('keydown', (e) => {
2065
+ if (e.altKey && e.code === 'KeyS') {
2066
+ e.preventDefault();
2067
+ __jayDoFreeze();
2068
+ }
2069
+ });
2070
+ }
2071
+
2072
+ // Targeted page reload: only reload if this page matches the changed route prefix
2073
+ if (import.meta.hot) {
2074
+ import.meta.hot.on('jay:page-reload', (data) => {
2075
+ const prefix = data.routePrefix;
2076
+ const pathname = window.location.pathname;
2077
+ if (pathname === prefix || pathname.startsWith(prefix + '/')) {
2078
+ window.location.reload();
2079
+ }
2080
+ });
2081
+ }`;
2082
+ }
2083
+ function buildAutomationWrap(options, mode) {
2084
+ const { enableAutomation = true, slowViewState } = options;
2085
+ const hasSlowViewState = slowViewState && Object.keys(slowViewState).length > 0;
2086
+ const appendDom = mode === "client";
2087
+ if (!enableAutomation) {
2088
+ return appendDom ? `
2089
+ target.appendChild(instance.element.dom);` : "";
2090
+ }
2091
+ const appendLine = appendDom ? `
2092
+ target.appendChild(wrapped.element.dom);` : "";
2093
+ const freezeScript = buildFreezeScript(options.routePattern);
2094
+ if (hasSlowViewState) {
2095
+ return `
2096
+ // Wrap with automation for dev tooling
2097
+ // Deep merge slow+fast ViewState so automation can see full page state
2098
+ const fullViewState = deepMergeViewStates(slowViewState, {...viewState, ...fastCarryForward}, trackByMap);
2099
+ const wrapped = wrapWithAutomation(instance, { initialViewState: fullViewState, trackByMap });
2100
+ registerGlobalContext(AUTOMATION_CONTEXT, wrapped.automation);
2101
+ window.__jay = window.__jay || {};
2102
+ window.__jay.automation = wrapped.automation;
2103
+ window.dispatchEvent(new Event('jay:automation-ready'));${appendLine}
2104
+ ${freezeScript}`;
2105
+ }
2106
+ return `
2107
+ // Wrap with automation for dev tooling
2108
+ const wrapped = wrapWithAutomation(instance);
2109
+ registerGlobalContext(AUTOMATION_CONTEXT, wrapped.automation);
2110
+ window.__jay = window.__jay || {};
2111
+ window.__jay.automation = wrapped.automation;
2112
+ window.dispatchEvent(new Event('jay:automation-ready'));${appendLine}
2113
+ ${freezeScript}`;
2114
+ }
2115
+ async function resolveViewStatePromises(viewState) {
2116
+ const entries = Object.entries(viewState);
2117
+ const hasPromises = entries.some(([, v]) => v instanceof Promise);
2118
+ if (!hasPromises) return { resolved: viewState, outcomes: [] };
2119
+ const result = { ...viewState };
2120
+ const outcomes = [];
2121
+ for (const [key, value] of entries) {
2122
+ if (value instanceof Promise) {
2123
+ try {
2124
+ const val = await value;
2125
+ result[key] = val;
2126
+ outcomes.push({ id: key, status: "resolved", value: val });
2127
+ } catch (err) {
2128
+ delete result[key];
2129
+ outcomes.push({
2130
+ id: key,
2131
+ status: "rejected",
2132
+ value: { message: err?.message ?? String(err) }
2133
+ });
2134
+ }
2135
+ }
2136
+ }
2137
+ return { resolved: result, outcomes };
2138
+ }
2139
+ async function generateClientScript(defaultViewState, fastCarryForward, parts, jayHtmlPath, trackByMap = {}, clientInitData = {}, projectInit, pluginInits = [], options = {}) {
2140
+ const { resolved, outcomes } = await resolveViewStatePromises(defaultViewState);
2141
+ defaultViewState = resolved;
2142
+ const {
2143
+ partImports,
2144
+ compositeParts,
2145
+ pluginClientInitImports,
2146
+ projectInitImport,
2147
+ clientInitExecution,
2148
+ automationImport,
2149
+ slowViewStateDecl
2150
+ } = buildScriptFragments(parts, clientInitData, projectInit, pluginInits, options);
2151
+ const automationWrap = buildAutomationWrap(options, "client");
2152
+ return `<!doctype html>
2153
+ <html lang="en">
2154
+ <head>
2155
+ <meta charset="UTF-8" />
2156
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
2157
+ <title>Vite + TS</title>
2158
+ </head>
2159
+ <body>
2160
+ <div id="target"></div>
2161
+ <script type="module">
2162
+ import {makeCompositeJayComponent} from "@jay-framework/stack-client-runtime";
2163
+ ${automationImport}
2164
+ ${pluginClientInitImports}
2165
+ ${projectInitImport}
2166
+ import { render } from '${jayHtmlPath}';
2167
+ ${partImports}${slowViewStateDecl}
2168
+ const viewState = ${JSON.stringify(defaultViewState)};
2169
+ ${generatePromiseReconstruction(outcomes)}
2170
+ const fastCarryForward = ${JSON.stringify(fastCarryForward)};
2171
+ const trackByMap = ${JSON.stringify(trackByMap)};
2172
+ ${clientInitExecution}
2173
+ const target = document.getElementById('target');
2174
+ const pageComp = makeCompositeJayComponent(render, viewState, fastCarryForward, ${compositeParts}, trackByMap)
2175
+
2176
+ const instance = pageComp({/* placeholder for page props */})
2177
+ ${automationWrap}
2178
+ <\/script>
2179
+ </body>
2180
+ </html>`;
2181
+ }
2182
+ const serverModuleCache = /* @__PURE__ */ new Map();
2183
+ function clearServerElementCache() {
2184
+ serverModuleCache.clear();
2185
+ }
2186
+ function getByPath(obj, dotPath) {
2187
+ const segments = dotPath.split(".");
2188
+ let current = obj;
2189
+ for (const seg of segments) {
2190
+ if (current == null || typeof current !== "object") return void 0;
2191
+ current = current[seg];
2192
+ }
2193
+ return current;
2194
+ }
2195
+ function resolveParts(parts, viewState) {
2196
+ return parts.map((p) => {
2197
+ if (p.kind === "static") return p.value;
2198
+ if (!viewState) return `{${p.value}}`;
2199
+ const resolved = getByPath(viewState, p.value);
2200
+ return resolved !== void 0 && resolved !== null ? String(resolved) : `{${p.value}}`;
2201
+ }).join("");
2202
+ }
2203
+ function headMetaToHeadTags(headMeta, viewState) {
2204
+ if (!headMeta) return [];
2205
+ const tags = [];
2206
+ if (headMeta.title) {
2207
+ tags.push({ tag: "title", children: resolveParts(headMeta.title, viewState) });
2208
+ }
2209
+ for (const m of headMeta.meta) {
2210
+ const attrs = {};
2211
+ if (m.name) attrs.name = m.name;
2212
+ if (m.property) attrs.property = m.property;
2213
+ attrs.content = resolveParts(m.content, viewState);
2214
+ tags.push({ tag: "meta", attrs });
2215
+ }
2216
+ for (const l of headMeta.links) {
2217
+ if (l.rel === "stylesheet" || l.rel === "import") continue;
2218
+ const attrs = { rel: l.rel };
2219
+ attrs.href = resolveParts(l.href, viewState);
2220
+ for (const [k, v] of Object.entries(l)) {
2221
+ if (k !== "rel" && k !== "href" && typeof v === "string") attrs[k] = v;
2222
+ }
2223
+ tags.push({ tag: "link", attrs });
2224
+ }
2225
+ return tags;
2226
+ }
2227
+ async function generateSSRPageHtml(vite, jayHtmlContent, jayHtmlFilename, jayHtmlDir, viewState, jayHtmlImportPath, parts, carryForward, trackByMap = {}, clientInitData = {}, buildFolder, projectRoot, routeDir, tsConfigFilePath, projectInit, pluginInits = [], options = {}, sourceDir, headTags, scripts) {
2228
+ const jayHtmlPath = path__default.join(jayHtmlDir, jayHtmlFilename);
2229
+ let cached = serverModuleCache.get(jayHtmlPath);
2230
+ if (!cached) {
2231
+ cached = await compileAndLoadServerElement(
2232
+ vite,
2233
+ jayHtmlContent,
2234
+ jayHtmlFilename,
2235
+ jayHtmlDir,
2236
+ buildFolder,
2237
+ projectRoot,
2238
+ routeDir,
2239
+ tsConfigFilePath,
2240
+ sourceDir
2241
+ );
2242
+ serverModuleCache.set(jayHtmlPath, cached);
2243
+ }
2244
+ const htmlChunks = [];
2245
+ const asyncPromises = [];
2246
+ const asyncOutcomes = [];
2247
+ const ctx = {
2248
+ write: (chunk) => {
2249
+ htmlChunks.push(chunk);
2250
+ },
2251
+ onAsync: (promise, id, templates) => {
2252
+ const asyncPromise = promise.then(
2253
+ (val) => {
2254
+ asyncOutcomes.push({ id, status: "resolved", value: val });
2255
+ if (templates.resolved) {
2256
+ return asyncSwapScript(id, templates.resolved(val));
2257
+ }
2258
+ return "";
2259
+ },
2260
+ (err) => {
2261
+ asyncOutcomes.push({
2262
+ id,
2263
+ status: "rejected",
2264
+ value: { message: err?.message ?? String(err) }
2265
+ });
2266
+ if (templates.rejected) {
2267
+ return asyncSwapScript(id, templates.rejected(err));
2268
+ }
2269
+ return "";
2270
+ }
2271
+ );
2272
+ asyncPromises.push(asyncPromise);
2273
+ }
2274
+ };
2275
+ cached.renderToStream(viewState, ctx);
2276
+ const asyncResults = await Promise.all(asyncPromises);
2277
+ const ssrHtml = htmlChunks.join("");
2278
+ const asyncScripts = asyncResults.filter((r) => r !== "").join("");
2279
+ const hydrationScript = generateHydrationScript(
2280
+ viewState,
2281
+ carryForward,
2282
+ parts,
2283
+ jayHtmlImportPath,
2284
+ trackByMap,
2285
+ clientInitData,
2286
+ projectInit,
2287
+ pluginInits,
2288
+ options,
2289
+ asyncOutcomes
2290
+ );
2291
+ const cssLink = cached.cssHref ? ` <link rel="stylesheet" href="${cached.cssHref}" />` : "";
2292
+ const tagSources = [];
2293
+ if (headTags && headTags.length > 0) tagSources.push(headTags);
2294
+ const headMetaTags = headMetaToHeadTags(cached.headMeta, viewState);
2295
+ if (headMetaTags.length > 0) tagSources.push(headMetaTags);
2296
+ const mergedTags = mergeHeadTags(tagSources);
2297
+ const hasCustomTitle = mergedTags.some((t) => t.tag.toLowerCase() === "title");
2298
+ const titleTag = hasCustomTitle ? "" : " <title>Vite + TS</title>\n";
2299
+ const headTagsHtml = mergedTags.length > 0 ? serializeHeadTags(mergedTags) : "";
2300
+ const headScriptsHtml = serializeScripts(scripts, "head");
2301
+ const bodyScriptsHtml = serializeScripts(scripts, "body");
2302
+ const headExtras = [cssLink, headTagsHtml, headScriptsHtml].filter((_) => _).join("\n");
2303
+ return `<!doctype html>
2304
+ <html lang="en">
2305
+ <head>
2306
+ <meta charset="UTF-8" />
2307
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
2308
+ ${titleTag}${headExtras ? headExtras + "\n" : ""} </head>
2309
+ <body>
2310
+ <div id="target">${ssrHtml}</div>${asyncScripts}
2311
+ ${hydrationScript}${bodyScriptsHtml ? "\n " + bodyScriptsHtml : ""}
2312
+ </body>
2313
+ </html>`;
2314
+ }
2315
+ async function generateFrozenPageHtml(vite, jayHtmlContent, jayHtmlFilename, jayHtmlDir, viewState, buildFolder, projectRoot, routeDir, tsConfigFilePath, sourceDir, format = "page", freezeName) {
2316
+ const jayHtmlPath = path__default.join(jayHtmlDir, jayHtmlFilename);
2317
+ let cached = serverModuleCache.get(jayHtmlPath);
2318
+ if (!cached) {
2319
+ cached = await compileAndLoadServerElement(
2320
+ vite,
2321
+ jayHtmlContent,
2322
+ jayHtmlFilename,
2323
+ jayHtmlDir,
2324
+ buildFolder,
2325
+ projectRoot,
2326
+ routeDir,
2327
+ tsConfigFilePath,
2328
+ sourceDir
2329
+ );
2330
+ serverModuleCache.set(jayHtmlPath, cached);
2331
+ }
2332
+ const htmlChunks = [];
2333
+ const ctx = {
2334
+ write: (chunk) => {
2335
+ htmlChunks.push(chunk);
2336
+ },
2337
+ onAsync: () => {
2338
+ }
2339
+ };
2340
+ cached.renderToStream(viewState, ctx);
2341
+ const ssrHtml = htmlChunks.join("");
2342
+ if (format === "fragment") {
2343
+ let inlineCss = "";
2344
+ if (cached.cssHref) {
2345
+ try {
2346
+ const cssPath = cached.cssHref.replace(/^\/@fs/, "").replace(/\?.*$/, "");
2347
+ const cssContent = await fs__default.readFile(cssPath, "utf-8");
2348
+ inlineCss = `<style>${cssContent}</style>`;
2349
+ } catch {
2350
+ inlineCss = `<link rel="stylesheet" href="${cached.cssHref}" />`;
2351
+ }
2352
+ }
2353
+ return `${inlineCss}
2354
+ ${ssrHtml}`;
2355
+ }
2356
+ const frozenHeadTags = headMetaToHeadTags(cached.headMeta, viewState);
2357
+ const frozenHeadTagsHtml = frozenHeadTags.length > 0 ? serializeHeadTags(frozenHeadTags) : "";
2358
+ const cssLink = cached.cssHref ? ` <link rel="stylesheet" href="${cached.cssHref}" />` : "";
2359
+ const headExtras = [cssLink, frozenHeadTagsHtml].filter((_) => _).join("\n");
2360
+ const label = freezeName ? ` — ${freezeName}` : "";
2361
+ return `<!doctype html>
2362
+ <html lang="en">
2363
+ <head>
2364
+ <meta charset="UTF-8" />
2365
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
2366
+ <title>Frozen${label}</title>
2367
+ ${headExtras ? headExtras + "\n" : ""} <style>
2368
+ body::before {
2369
+ content: 'FROZEN${label ? `: ${freezeName}` : ""}';
2370
+ position: fixed;
2371
+ top: 0;
2372
+ right: 0;
2373
+ background: #1a1a2e;
2374
+ color: #e0e0ff;
2375
+ padding: 2px 10px;
2376
+ font: 11px/1.6 system-ui;
2377
+ z-index: 99999;
2378
+ border-bottom-left-radius: 4px;
2379
+ opacity: 0.8;
2380
+ }
2381
+ </style>
2382
+ </head>
2383
+ <body>
2384
+ <div id="target">${ssrHtml}</div>
2385
+ </body>
2386
+ </html>`;
2387
+ }
2388
+ function serializeScripts(scripts, position) {
2389
+ if (!scripts) return "";
2390
+ const filtered = scripts.filter((s2) => s2.position === position);
2391
+ if (filtered.length === 0) return "";
2392
+ return filtered.map((s2) => {
2393
+ const attrs = Object.entries(s2.attributes).map(([k, v]) => v === "" ? k : `${k}="${v}"`).join(" ");
2394
+ const attrStr = attrs ? " " + attrs : "";
2395
+ if (s2.src) {
2396
+ return ` <script src="${s2.src}"${attrStr}><\/script>`;
2397
+ }
2398
+ return ` <script${attrStr}>${s2.inline}<\/script>`;
2399
+ }).join("\n");
2400
+ }
2401
+ function rebaseRelativeImports(code, fromDir, toDir) {
2402
+ return code.replace(/from "(\.\.\/[^"]+)"/g, (_match, relPath) => {
2403
+ const absolutePath = path__default.resolve(fromDir, relPath);
2404
+ let newRelPath = path__default.relative(toDir, absolutePath);
2405
+ if (!newRelPath.startsWith(".")) {
2406
+ newRelPath = "./" + newRelPath;
2407
+ }
2408
+ return `from "${newRelPath}"`;
2409
+ });
2410
+ }
2411
+ async function compileAndLoadServerElement(vite, jayHtmlContent, jayHtmlFilename, jayHtmlDir, buildFolder, projectRoot, routeDir, tsConfigFilePath, sourceDir) {
2412
+ const jayFile = await parseJayFile(
2413
+ jayHtmlContent,
2414
+ jayHtmlFilename,
2415
+ jayHtmlDir,
2416
+ { relativePath: tsConfigFilePath },
2417
+ JAY_IMPORT_RESOLVER,
2418
+ projectRoot,
2419
+ sourceDir
2420
+ );
2421
+ const parsedJayFile = checkValidationErrors(jayFile);
2422
+ const pageName = jayHtmlFilename.replace(".jay-html", "");
2423
+ const debugCoordinatePreprocessPath = path__default.join(
2424
+ buildFolder,
2425
+ "debug",
2426
+ routeDir,
2427
+ `${pageName}.coordinate-preprocess.jay-html`
2428
+ );
2429
+ const serverElementOptions = {
2430
+ debugCoordinatePreprocessPath
2431
+ };
2432
+ const serverElementCode = checkValidationErrors(
2433
+ generateServerElementFile(parsedJayFile, serverElementOptions)
2434
+ );
2435
+ const serverElementDir = path__default.join(buildFolder, "pre-rendered", routeDir);
2436
+ await fs__default.mkdir(serverElementDir, { recursive: true });
2437
+ const adjustedCode = rebaseRelativeImports(serverElementCode, jayHtmlDir, serverElementDir);
2438
+ const serverElementFilename = jayHtmlFilename.replace(".jay-html", ".server-element.ts");
2439
+ const serverElementPath = path__default.join(serverElementDir, serverElementFilename);
2440
+ await fs__default.writeFile(serverElementPath, adjustedCode, "utf-8");
2441
+ const existingModule = vite.moduleGraph.getModuleById(serverElementPath);
2442
+ if (existingModule) {
2443
+ vite.moduleGraph.invalidateModule(existingModule);
2444
+ }
2445
+ const jayHtmlPath = path__default.join(serverElementDir, jayHtmlFilename);
2446
+ invalidateJayHtmlModules(vite, jayHtmlPath);
2447
+ const serverModule = await vite.ssrLoadModule(serverElementPath);
2448
+ let cssHref;
2449
+ if (parsedJayFile.css) {
2450
+ const cssFilename = jayHtmlFilename.replace(".jay-html", ".css");
2451
+ const cssPath = path__default.join(serverElementDir, cssFilename);
2452
+ await fs__default.writeFile(cssPath, parsedJayFile.css, "utf-8");
2453
+ const cssModules = vite.moduleGraph.getModulesByFile(cssPath);
2454
+ if (cssModules) {
2455
+ for (const mod of cssModules) {
2456
+ vite.moduleGraph.invalidateModule(mod);
2457
+ }
2458
+ }
2459
+ const hash = crypto.createHash("md5").update(parsedJayFile.css).digest("hex").slice(0, 8);
2460
+ cssHref = "/@fs" + cssPath + "?v=" + hash + "&direct";
2461
+ }
2462
+ return {
2463
+ renderToStream: serverModule.renderToStream,
2464
+ headLinks: parsedJayFile.headLinks,
2465
+ headMeta: parsedJayFile.headMeta,
2466
+ cssHref
2467
+ };
2468
+ }
2469
+ function invalidateJayHtmlModules(vite, jayHtmlPath) {
2470
+ let count = 0;
2471
+ const byFile = vite.moduleGraph.getModulesByFile(jayHtmlPath);
2472
+ if (byFile) {
2473
+ for (const mod of byFile) {
2474
+ vite.moduleGraph.invalidateModule(mod);
2475
+ count++;
2476
+ }
2477
+ }
2478
+ const knownIds = [jayHtmlPath + ".ts", jayHtmlPath + JAY_QUERY_HYDRATE + ".ts"];
2479
+ for (const id of knownIds) {
2480
+ const mod = vite.moduleGraph.getModuleById(id);
2481
+ if (mod) {
2482
+ vite.moduleGraph.invalidateModule(mod);
2483
+ count++;
2484
+ }
2485
+ }
2486
+ const idMap = vite.moduleGraph.idToModuleMap;
2487
+ for (const [id, mod] of idMap) {
2488
+ if (id.includes(jayHtmlPath)) {
2489
+ vite.moduleGraph.invalidateModule(mod);
2490
+ count++;
2491
+ }
2492
+ }
2493
+ if (count > 0) {
2494
+ getLogger().info(`[SSR] Invalidated ${count} Vite module(s) for ${jayHtmlPath}`);
2495
+ }
2496
+ }
2497
+ function generateHydrationScript(defaultViewState, fastCarryForward, parts, jayHtmlPath, trackByMap = {}, clientInitData = {}, projectInit, pluginInits = [], options = {}, asyncOutcomes = []) {
2498
+ const {
2499
+ partImports,
2500
+ compositeParts,
2501
+ pluginClientInitImports,
2502
+ projectInitImport,
2503
+ clientInitExecution,
2504
+ automationImport,
2505
+ slowViewStateDecl
2506
+ } = buildScriptFragments(parts, clientInitData, projectInit, pluginInits, options);
2507
+ const automationWrap = buildAutomationWrap(options, "hydrate");
2508
+ const hydrateImportPath = `${jayHtmlPath}${JAY_QUERY_HYDRATE}`;
2509
+ return `<script type="module">
2510
+ import {hydrateCompositeJayComponent} from "@jay-framework/stack-client-runtime";
2511
+ ${automationImport}
2512
+ ${pluginClientInitImports}
2513
+ ${projectInitImport}
2514
+ import { hydrate } from '${hydrateImportPath}';
2515
+ ${partImports}${slowViewStateDecl}
2516
+ const viewState = ${JSON.stringify(defaultViewState)};
2517
+ ${generatePromiseReconstruction(asyncOutcomes)} const fastCarryForward = ${JSON.stringify(fastCarryForward)};
2518
+ const trackByMap = ${JSON.stringify(trackByMap)};
2519
+
2520
+ const target = document.getElementById('target');
2521
+ const rootElement = target.firstElementChild;
2522
+ const pageComp = hydrateCompositeJayComponent(hydrate, viewState, fastCarryForward, ${compositeParts}, trackByMap, rootElement);
2523
+ ${clientInitExecution}
2524
+ const instance = pageComp({/* placeholder for page props */});
2525
+ ${automationWrap}
2526
+ <\/script>`;
2527
+ }
2528
+ function parseActionMetadata(yamlContent, fileName) {
2529
+ try {
2530
+ const parsed = parseAction(yamlContent, fileName);
2531
+ if (parsed.validations.length > 0) {
2532
+ getLogger().warn(
2533
+ `[ActionMetadata] ${fileName}: validation errors: ${parsed.validations.join(", ")}`
2534
+ );
2535
+ return null;
2536
+ }
2537
+ if (!parsed.val) {
2538
+ getLogger().warn(`[ActionMetadata] ${fileName}: parsing returned no result`);
2539
+ return null;
2540
+ }
2541
+ const action = parsed.val;
2542
+ const inputJsonSchema = jayTypeToJsonSchema(action.inputType);
2543
+ let inputSchema;
2544
+ if (inputJsonSchema && inputJsonSchema.type === "object") {
2545
+ inputSchema = {
2546
+ type: "object",
2547
+ properties: inputJsonSchema.properties || {},
2548
+ ...inputJsonSchema.required && inputJsonSchema.required.length > 0 && { required: inputJsonSchema.required }
2549
+ };
2550
+ } else {
2551
+ inputSchema = { type: "object", properties: {} };
2552
+ }
2553
+ const metadata = {
2554
+ name: action.name,
2555
+ description: action.description,
2556
+ inputSchema
2557
+ };
2558
+ if (action.outputType) {
2559
+ const outputJsonSchema = jayTypeToJsonSchema(action.outputType);
2560
+ if (outputJsonSchema) {
2561
+ metadata.outputSchema = outputJsonSchema;
2562
+ }
2563
+ }
2564
+ return metadata;
2565
+ } catch (error) {
2566
+ getLogger().error(
2567
+ `[ActionMetadata] Failed to parse ${fileName}: ${error instanceof Error ? error.message : error}`
2568
+ );
2569
+ return null;
2570
+ }
2571
+ }
2572
+ function loadActionMetadata(filePath) {
2573
+ if (!fs.existsSync(filePath)) {
2574
+ getLogger().warn(`[ActionMetadata] File not found: ${filePath}`);
2575
+ return null;
2576
+ }
2577
+ const yamlContent = fs.readFileSync(filePath, "utf-8");
2578
+ const fileName = path.basename(filePath);
2579
+ return parseActionMetadata(yamlContent, fileName);
2580
+ }
2581
+ function resolveActionMetadataPath(actionPath, pluginDir) {
2582
+ return path.resolve(pluginDir, actionPath);
2583
+ }
2584
+ const require$2 = createRequire$1(import.meta.url);
2585
+ async function discoverAndRegisterActions(options) {
2586
+ const {
2587
+ projectRoot,
2588
+ actionsDir = "src/actions",
2589
+ registry = actionRegistry,
2590
+ verbose = false,
2591
+ viteServer
2592
+ } = options;
2593
+ const result = {
2594
+ actionCount: 0,
2595
+ actionNames: [],
2596
+ scannedFiles: []
2597
+ };
2598
+ const actionsPath = path.resolve(projectRoot, actionsDir);
2599
+ if (!fs.existsSync(actionsPath)) {
2600
+ if (verbose) {
2601
+ getLogger().info(`[Actions] No actions directory found at ${actionsPath}`);
2602
+ }
2603
+ return result;
2604
+ }
2605
+ const actionFiles = await findActionFiles(actionsPath);
2606
+ if (verbose) {
2607
+ getLogger().info(`[Actions] Found ${actionFiles.length} action file(s)`);
2608
+ }
2609
+ for (const filePath of actionFiles) {
2610
+ result.scannedFiles.push(filePath);
2611
+ try {
2612
+ let module;
2613
+ if (viteServer) {
2614
+ module = await viteServer.ssrLoadModule(filePath);
2615
+ } else {
2616
+ module = await import(filePath);
2617
+ }
2618
+ for (const [exportName, exportValue] of Object.entries(module)) {
2619
+ if (isJayAction(exportValue)) {
2620
+ registry.register(exportValue);
2621
+ result.actionNames.push(exportValue.actionName);
2622
+ result.actionCount++;
2623
+ if (verbose) {
2624
+ getLogger().info(
2625
+ `[Actions] Registered: ${exportValue.actionName}`
2626
+ );
2627
+ }
2628
+ } else if (isJayStreamAction(exportValue)) {
2629
+ registry.registerStream(exportValue);
2630
+ result.actionNames.push(exportValue.actionName);
2631
+ result.actionCount++;
2632
+ if (verbose) {
2633
+ getLogger().info(
2634
+ `[Actions] Registered stream: ${exportValue.actionName}`
2635
+ );
2636
+ }
2637
+ }
2638
+ }
2639
+ } catch (error) {
2640
+ getLogger().error(`[Actions] Failed to import ${filePath}: ${error}`);
2641
+ }
2642
+ }
2643
+ return result;
2644
+ }
2645
+ async function findActionFiles(dir) {
2646
+ const files = [];
2647
+ const entries = await fs.promises.readdir(dir, { withFileTypes: true });
2648
+ for (const entry of entries) {
2649
+ const fullPath = path.join(dir, entry.name);
2650
+ if (entry.isDirectory()) {
2651
+ const subFiles = await findActionFiles(fullPath);
2652
+ files.push(...subFiles);
2653
+ } else if (entry.isFile() && entry.name.endsWith(".actions.ts")) {
2654
+ files.push(fullPath);
2655
+ }
2656
+ }
2657
+ return files;
2658
+ }
2659
+ async function discoverAllPluginActions(options) {
2660
+ const { projectRoot, registry = actionRegistry, verbose = false, viteServer } = options;
2661
+ const allActions = [];
2662
+ const localPluginsPath = path.join(projectRoot, "src/plugins");
2663
+ if (fs.existsSync(localPluginsPath)) {
2664
+ const pluginDirs = await fs.promises.readdir(localPluginsPath, { withFileTypes: true });
2665
+ for (const entry of pluginDirs) {
2666
+ if (entry.isDirectory()) {
2667
+ const pluginPath = path.join(localPluginsPath, entry.name);
2668
+ const actions = await discoverPluginActions(
2669
+ pluginPath,
2670
+ projectRoot,
2671
+ registry,
2672
+ verbose,
2673
+ viteServer
2674
+ );
2675
+ allActions.push(...actions);
2676
+ }
2677
+ }
2678
+ }
2679
+ const npmActions = await discoverNpmPluginActions(projectRoot, registry, verbose, viteServer);
2680
+ allActions.push(...npmActions);
2681
+ return allActions;
2682
+ }
2683
+ async function discoverNpmPluginActions(projectRoot, registry, verbose, viteServer) {
2684
+ const allActions = [];
2685
+ const packageJsonPath = path.join(projectRoot, "package.json");
2686
+ if (!fs.existsSync(packageJsonPath)) {
2687
+ return allActions;
2688
+ }
2689
+ try {
2690
+ const packageJson = JSON.parse(await fs.promises.readFile(packageJsonPath, "utf-8"));
2691
+ const dependencies = {
2692
+ ...packageJson.dependencies,
2693
+ ...packageJson.devDependencies
2694
+ };
2695
+ for (const packageName of Object.keys(dependencies)) {
2696
+ try {
2697
+ const pluginYamlPath = tryResolvePluginYaml(packageName, projectRoot);
2698
+ if (!pluginYamlPath) {
2699
+ continue;
2700
+ }
2701
+ const pluginDir = path.dirname(pluginYamlPath);
2702
+ const pluginConfig = loadPluginManifest(pluginDir);
2703
+ if (!pluginConfig || !pluginConfig.actions || !Array.isArray(pluginConfig.actions)) {
2704
+ continue;
2705
+ }
2706
+ if (verbose) {
2707
+ getLogger().info(
2708
+ `[Actions] NPM plugin "${packageName}" declares actions: ${JSON.stringify(pluginConfig.actions)}`
2709
+ );
2710
+ }
2711
+ const actions = await registerNpmPluginActions(
2712
+ packageName,
2713
+ pluginConfig,
2714
+ pluginDir,
2715
+ registry,
2716
+ verbose,
2717
+ viteServer
2718
+ );
2719
+ allActions.push(...actions);
2720
+ } catch {
2721
+ continue;
2722
+ }
2723
+ }
2724
+ } catch (error) {
2725
+ getLogger().error(`[Actions] Failed to read project package.json: ${error}`);
2726
+ }
2727
+ return allActions;
2728
+ }
2729
+ function tryResolvePluginYaml(packageName, projectRoot) {
2730
+ try {
2731
+ return require$2.resolve(`${packageName}/plugin.yaml`, {
2732
+ paths: [projectRoot]
2733
+ });
2734
+ } catch {
2735
+ return null;
2736
+ }
2737
+ }
2738
+ function resolveNpmActionMetadataPath(actionPath, packageName, pluginDir) {
2739
+ if (!actionPath.startsWith(".")) {
2740
+ try {
2741
+ return require$2.resolve(`${packageName}/${actionPath}`, {
2742
+ paths: [pluginDir]
2743
+ });
2744
+ } catch {
2745
+ }
2746
+ }
2747
+ const resolved = resolveActionMetadataPath(actionPath, pluginDir);
2748
+ if (fs.existsSync(resolved)) {
2749
+ return resolved;
2750
+ }
2751
+ getLogger().warn(
2752
+ `[Actions] Could not resolve .jay-action file "${actionPath}" for package "${packageName}"`
2753
+ );
2754
+ return null;
2755
+ }
2756
+ async function registerNpmPluginActions(packageName, pluginConfig, pluginDir, registry, verbose, viteServer) {
2757
+ const registeredActions = [];
2758
+ try {
2759
+ let pluginModule;
2760
+ if (viteServer) {
2761
+ pluginModule = await viteServer.ssrLoadModule(packageName);
2762
+ } else {
2763
+ pluginModule = await import(packageName);
2764
+ }
2765
+ for (const entry of pluginConfig.actions) {
2766
+ const { name: actionName, action: actionPath } = normalizeActionEntry(entry);
2767
+ const actionExport = pluginModule[actionName];
2768
+ if (actionExport && isJayAction(actionExport)) {
2769
+ registry.register(actionExport);
2770
+ const registeredName = actionExport.actionName;
2771
+ registeredActions.push(registeredName);
2772
+ if (actionPath) {
2773
+ const metadataFilePath = resolveNpmActionMetadataPath(
2774
+ actionPath,
2775
+ packageName,
2776
+ pluginDir
2777
+ );
2778
+ if (metadataFilePath) {
2779
+ const metadata = loadActionMetadata(metadataFilePath);
2780
+ if (metadata) {
2781
+ registry.setMetadata(registeredName, metadata);
2782
+ if (verbose) {
2783
+ getLogger().info(
2784
+ `[Actions] Loaded metadata for "${registeredName}" from ${actionPath}`
2785
+ );
2786
+ }
2787
+ }
2788
+ }
2789
+ }
2790
+ if (verbose) {
2791
+ getLogger().info(`[Actions] Registered NPM plugin action: ${registeredName}`);
2792
+ }
2793
+ } else if (actionExport && isJayStreamAction(actionExport)) {
2794
+ registry.registerStream(actionExport);
2795
+ const registeredName = actionExport.actionName;
2796
+ registeredActions.push(registeredName);
2797
+ if (verbose) {
2798
+ getLogger().info(`[Actions] Registered NPM plugin stream: ${registeredName}`);
2799
+ }
2800
+ } else {
2801
+ getLogger().warn(
2802
+ `[Actions] NPM plugin "${packageName}" declares action "${actionName}" but it's not exported or not a JayAction`
2803
+ );
2804
+ }
2805
+ }
2806
+ } catch (importError) {
2807
+ getLogger().error(`[Actions] Failed to import NPM plugin "${packageName}": ${importError}`);
2808
+ }
2809
+ return registeredActions;
2810
+ }
2811
+ async function discoverPluginActions(pluginPath, projectRoot, registry = actionRegistry, verbose = false, viteServer) {
2812
+ const pluginConfig = loadPluginManifest(pluginPath);
2813
+ if (!pluginConfig) {
2814
+ return [];
2815
+ }
2816
+ if (!pluginConfig.actions || !Array.isArray(pluginConfig.actions)) {
2817
+ return [];
2818
+ }
2819
+ const registeredActions = [];
2820
+ const pluginName = pluginConfig.name || path.basename(pluginPath);
2821
+ if (verbose) {
2822
+ getLogger().info(
2823
+ `[Actions] Plugin "${pluginName}" declares actions: ${JSON.stringify(pluginConfig.actions)}`
2824
+ );
2825
+ }
2826
+ let modulePath = pluginConfig.module ? path.join(pluginPath, pluginConfig.module) : path.join(pluginPath, "index.ts");
2827
+ if (!fs.existsSync(modulePath)) {
2828
+ const tsPath = modulePath + ".ts";
2829
+ const jsPath = modulePath + ".js";
2830
+ if (fs.existsSync(tsPath)) {
2831
+ modulePath = tsPath;
2832
+ } else if (fs.existsSync(jsPath)) {
2833
+ modulePath = jsPath;
2834
+ } else {
2835
+ getLogger().warn(`[Actions] Plugin "${pluginName}" module not found at ${modulePath}`);
2836
+ return [];
2837
+ }
2838
+ }
2839
+ try {
2840
+ let pluginModule;
2841
+ if (viteServer) {
2842
+ pluginModule = await viteServer.ssrLoadModule(modulePath);
2843
+ } else {
2844
+ pluginModule = await import(modulePath);
2845
+ }
2846
+ for (const entry of pluginConfig.actions) {
2847
+ const { name: actionName, action: actionPath } = normalizeActionEntry(entry);
2848
+ const actionExport = pluginModule[actionName];
2849
+ if (actionExport && isJayAction(actionExport)) {
2850
+ registry.register(actionExport);
2851
+ const registeredName = actionExport.actionName;
2852
+ registeredActions.push(registeredName);
2853
+ if (actionPath) {
2854
+ const metadataFilePath = resolveActionMetadataPath(actionPath, pluginPath);
2855
+ const metadata = loadActionMetadata(metadataFilePath);
2856
+ if (metadata) {
2857
+ registry.setMetadata(registeredName, metadata);
2858
+ if (verbose) {
2859
+ getLogger().info(
2860
+ `[Actions] Loaded metadata for "${registeredName}" from ${actionPath}`
2861
+ );
2862
+ }
2863
+ }
2864
+ }
2865
+ if (verbose) {
2866
+ getLogger().info(`[Actions] Registered plugin action: ${registeredName}`);
2867
+ }
2868
+ } else if (actionExport && isJayStreamAction(actionExport)) {
2869
+ registry.registerStream(actionExport);
2870
+ const registeredName = actionExport.actionName;
2871
+ registeredActions.push(registeredName);
2872
+ if (verbose) {
2873
+ getLogger().info(`[Actions] Registered plugin stream: ${registeredName}`);
2874
+ }
2875
+ } else {
2876
+ getLogger().warn(
2877
+ `[Actions] Plugin "${pluginName}" declares action "${actionName}" but it's not exported or not a JayAction`
2878
+ );
2879
+ }
2880
+ }
2881
+ } catch (importError) {
2882
+ getLogger().error(
2883
+ `[Actions] Failed to import plugin module at ${modulePath}: ${importError}`
2884
+ );
2885
+ }
2886
+ return registeredActions;
2887
+ }
2888
+ const require$1 = createRequire(import.meta.url);
2889
+ async function executeDynamicGenerator(plugin, config, projectRoot, services, verbose, viteServer) {
2890
+ const { pluginPath, name: pluginName, isLocal, packageName } = plugin;
2891
+ if (!config.generator) {
2892
+ throw new Error(
2893
+ `Plugin "${pluginName}" has dynamic_contracts entry but no generator specified`
2894
+ );
2895
+ }
2896
+ const isFilePath = config.generator.startsWith("./") || config.generator.startsWith("/") || config.generator.includes(".ts") || config.generator.includes(".js");
2897
+ let generator;
2898
+ if (isFilePath) {
2899
+ let generatorPath;
2900
+ if (!isLocal) {
2901
+ try {
2902
+ generatorPath = require$1.resolve(`${packageName}/${config.generator}`, {
2903
+ paths: [projectRoot]
2904
+ });
2905
+ } catch {
2906
+ generatorPath = path.join(pluginPath, config.generator);
2907
+ }
2908
+ } else {
2909
+ generatorPath = path.join(pluginPath, config.generator);
2910
+ }
2911
+ if (!fs.existsSync(generatorPath)) {
2912
+ const withTs = generatorPath + ".ts";
2913
+ const withJs = generatorPath + ".js";
2914
+ if (fs.existsSync(withTs)) {
2915
+ generatorPath = withTs;
2916
+ } else if (fs.existsSync(withJs)) {
2917
+ generatorPath = withJs;
2918
+ }
2919
+ }
2920
+ if (!fs.existsSync(generatorPath)) {
2921
+ throw new Error(
2922
+ `Generator file not found for plugin "${pluginName}": ${config.generator}`
2923
+ );
2924
+ }
2925
+ if (verbose) {
2926
+ getLogger().info(` Loading generator from file: ${generatorPath}`);
2927
+ }
2928
+ let generatorModule;
2929
+ if (viteServer) {
2930
+ generatorModule = await viteServer.ssrLoadModule(generatorPath);
2931
+ } else {
2932
+ generatorModule = await import(generatorPath);
2933
+ }
2934
+ generator = generatorModule.generator || generatorModule.default;
2935
+ } else {
2936
+ if (verbose) {
2937
+ getLogger().info(
2938
+ ` Loading generator export: ${config.generator} from ${packageName}`
2939
+ );
2940
+ }
2941
+ let pluginModule;
2942
+ if (viteServer) {
2943
+ pluginModule = await viteServer.ssrLoadModule(packageName);
2944
+ } else {
2945
+ pluginModule = await import(packageName);
2946
+ }
2947
+ generator = pluginModule[config.generator];
2948
+ if (!generator) {
2949
+ throw new Error(
2950
+ `Generator "${config.generator}" not exported from plugin "${pluginName}". Ensure it's exported from the package's index.ts`
2951
+ );
2952
+ }
2953
+ }
2954
+ if (!generator || typeof generator.generate !== "function") {
2955
+ throw new Error(
2956
+ `Generator "${config.generator}" for plugin "${pluginName}" must have a 'generate' function. Use makeContractGenerator() to create valid generators.`
2957
+ );
2958
+ }
2959
+ const resolvedServices = [];
2960
+ for (const marker of generator.services) {
2961
+ const service = services.get(marker);
2962
+ if (!service) {
2963
+ const markerName = marker.description ?? "unknown";
2964
+ throw new Error(
2965
+ `Service "${markerName}" required by ${pluginName} generator not found. Ensure it's registered in init.ts`
2966
+ );
2967
+ }
2968
+ resolvedServices.push(service);
2969
+ }
2970
+ if (verbose) {
2971
+ getLogger().info(` Executing generator...`);
2972
+ }
2973
+ return await generator.generate(...resolvedServices);
2974
+ }
2975
+ function resolveStaticContractPath(plugin, contractSpec, projectRoot) {
2976
+ const { pluginPath, isLocal, packageName } = plugin;
2977
+ if (!isLocal) {
2978
+ try {
2979
+ return require$1.resolve(`${packageName}/${contractSpec}`, {
2980
+ paths: [projectRoot]
2981
+ });
2982
+ } catch {
2983
+ const possiblePaths = [
2984
+ path.join(pluginPath, "dist", contractSpec),
2985
+ path.join(pluginPath, "lib", contractSpec),
2986
+ path.join(pluginPath, contractSpec)
2987
+ ];
2988
+ const found = possiblePaths.find((p) => fs.existsSync(p));
2989
+ return found || possiblePaths[0];
2990
+ }
2991
+ } else {
2992
+ return path.join(pluginPath, contractSpec);
2993
+ }
2994
+ }
2995
+ function resolveActionFilePath(actionPath, packageName, pluginPath, isLocal, projectRoot) {
2996
+ if (!isLocal && !actionPath.startsWith(".")) {
2997
+ try {
2998
+ return require$1.resolve(`${packageName}/${actionPath}`, {
2999
+ paths: [projectRoot]
3000
+ });
3001
+ } catch {
3002
+ const possiblePaths = [
3003
+ path.join(pluginPath, "dist", actionPath),
3004
+ path.join(pluginPath, "lib", actionPath),
3005
+ path.join(pluginPath, actionPath)
3006
+ ];
3007
+ const found = possiblePaths.find((p) => fs.existsSync(p));
3008
+ return found || null;
3009
+ }
3010
+ }
3011
+ const resolved = resolveActionMetadataPath(actionPath, pluginPath);
3012
+ return fs.existsSync(resolved) ? resolved : null;
3013
+ }
3014
+ function toKebabCase(str) {
3015
+ return str.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "");
3016
+ }
3017
+ async function materializeContracts(options, services = /* @__PURE__ */ new Map()) {
3018
+ const {
3019
+ projectRoot,
3020
+ outputDir = path.join(projectRoot, "agent-kit", "materialized-contracts"),
3021
+ dynamicOnly = false,
3022
+ pluginFilter,
3023
+ verbose = false,
3024
+ viteServer
3025
+ } = options;
3026
+ const pluginsIndexMap = /* @__PURE__ */ new Map();
3027
+ let staticCount = 0;
3028
+ let dynamicCount = 0;
3029
+ if (verbose) {
3030
+ getLogger().info("Scanning for plugins...");
3031
+ }
3032
+ const plugins = await scanPlugins({
3033
+ projectRoot,
3034
+ verbose,
3035
+ includeDevDeps: true
3036
+ // Include dev deps for contract discovery
3037
+ });
3038
+ if (verbose) {
3039
+ getLogger().info(`Found ${plugins.size} plugin(s)`);
3040
+ }
3041
+ for (const [pluginKey, plugin] of plugins) {
3042
+ if (pluginFilter && plugin.name !== pluginFilter && pluginKey !== pluginFilter) continue;
3043
+ if (verbose) {
3044
+ getLogger().info(`
3045
+ 📦 Processing plugin: ${plugin.name}`);
3046
+ }
3047
+ const { manifest } = plugin;
3048
+ const pluginRelPath = path.relative(projectRoot, plugin.pluginPath);
3049
+ if (!pluginsIndexMap.has(plugin.name)) {
3050
+ const entry = {
3051
+ path: "./" + pluginRelPath.replace(/\\/g, "/"),
3052
+ contracts: [],
3053
+ actions: []
3054
+ };
3055
+ if (manifest.services?.length) {
3056
+ entry.services = manifest.services.map((s2) => {
3057
+ const docPath = s2.doc ? "./" + path.relative(projectRoot, path.resolve(plugin.pluginPath, s2.doc)) : void 0;
3058
+ return {
3059
+ name: s2.name,
3060
+ marker: s2.marker,
3061
+ ...s2.description && { description: s2.description },
3062
+ ...docPath && { doc: docPath }
3063
+ };
3064
+ });
3065
+ }
3066
+ if (manifest.contexts?.length) {
3067
+ entry.contexts = manifest.contexts.map((c2) => {
3068
+ const docPath = c2.doc ? "./" + path.relative(projectRoot, path.resolve(plugin.pluginPath, c2.doc)) : void 0;
3069
+ return {
3070
+ name: c2.name,
3071
+ marker: c2.marker,
3072
+ ...c2.description && { description: c2.description },
3073
+ ...docPath && { doc: docPath }
3074
+ };
3075
+ });
3076
+ }
3077
+ if (manifest.routes?.length) {
3078
+ entry.routes = manifest.routes.map((r) => ({
3079
+ path: r.path,
3080
+ ...r.description && { description: r.description }
3081
+ }));
3082
+ }
3083
+ if (manifest.commands?.length) {
3084
+ entry.commands = manifest.commands.map((c2) => {
3085
+ let description;
3086
+ if (c2.command) {
3087
+ try {
3088
+ const cmdPath = path.resolve(plugin.pluginPath, c2.command);
3089
+ const cmdContent = fs.readFileSync(cmdPath, "utf-8");
3090
+ const parsed = YAML.parse(cmdContent);
3091
+ description = parsed?.description;
3092
+ } catch {
3093
+ }
3094
+ }
3095
+ return {
3096
+ name: c2.name,
3097
+ ...description && { description }
3098
+ };
3099
+ });
3100
+ }
3101
+ pluginsIndexMap.set(plugin.name, entry);
3102
+ }
3103
+ if (!dynamicOnly && manifest.contracts) {
3104
+ for (const contract of manifest.contracts) {
3105
+ const contractPath = resolveStaticContractPath(
3106
+ plugin,
3107
+ contract.contract,
3108
+ projectRoot
3109
+ );
3110
+ const relativePath = path.relative(projectRoot, contractPath);
3111
+ let description = contract.description;
3112
+ if (!description) {
3113
+ try {
3114
+ const contractContent = fs.readFileSync(contractPath, "utf-8");
3115
+ const parsed = YAML.parse(contractContent);
3116
+ if (parsed?.description && typeof parsed.description === "string") {
3117
+ description = parsed.description;
3118
+ }
3119
+ } catch {
3120
+ }
3121
+ }
3122
+ pluginsIndexMap.get(plugin.name).contracts.push({
3123
+ name: contract.name,
3124
+ ...description && { description },
3125
+ type: "static",
3126
+ path: "./" + relativePath
3127
+ });
3128
+ staticCount++;
3129
+ if (verbose) {
3130
+ getLogger().info(` 📄 Static: ${contract.name}`);
3131
+ }
3132
+ }
3133
+ }
3134
+ if (manifest.dynamic_contracts) {
3135
+ const dynamicConfigs = Array.isArray(manifest.dynamic_contracts) ? manifest.dynamic_contracts : [manifest.dynamic_contracts];
3136
+ const pluginOutputDir = path.join(outputDir, plugin.name.replace(/[@/]/g, "_"));
3137
+ fs.mkdirSync(pluginOutputDir, { recursive: true });
3138
+ for (const config of dynamicConfigs) {
3139
+ if (verbose) {
3140
+ getLogger().info(` ⚡ Dynamic contracts (prefix: ${config.prefix})`);
3141
+ }
3142
+ try {
3143
+ const generatedContracts = await executeDynamicGenerator(
3144
+ plugin,
3145
+ config,
3146
+ projectRoot,
3147
+ services,
3148
+ verbose,
3149
+ viteServer
3150
+ );
3151
+ const prefix = config.prefix;
3152
+ if (generatedContracts.length > 1) {
3153
+ const missing = generatedContracts.filter((c2) => !c2.name);
3154
+ if (missing.length > 0) {
3155
+ getLogger().error(
3156
+ ` ❌ Dynamic contract generator for "${prefix}" returned ${generatedContracts.length} contracts but ${missing.length} are missing a name. Names are required when a generator returns multiple contracts.`
3157
+ );
3158
+ continue;
3159
+ }
3160
+ }
3161
+ for (const generated of generatedContracts) {
3162
+ const kebabName = generated.name ? toKebabCase(generated.name) : null;
3163
+ const fullName = kebabName ? `${prefix}/${kebabName}` : prefix;
3164
+ const fileName = kebabName ? `${prefix}-${kebabName}.jay-contract` : `${prefix}.jay-contract`;
3165
+ const filePath = path.join(pluginOutputDir, fileName);
3166
+ fs.writeFileSync(filePath, generated.yaml, "utf-8");
3167
+ const relativePath = path.relative(projectRoot, filePath);
3168
+ let dynDescription;
3169
+ try {
3170
+ const parsedYaml = YAML.parse(generated.yaml);
3171
+ if (parsedYaml?.description && typeof parsedYaml.description === "string") {
3172
+ dynDescription = parsedYaml.description;
3173
+ }
3174
+ } catch {
3175
+ }
3176
+ const contractEntry = {
3177
+ name: fullName,
3178
+ ...dynDescription && { description: dynDescription },
3179
+ type: "dynamic",
3180
+ path: "./" + relativePath,
3181
+ ...generated.metadata && { metadata: generated.metadata }
3182
+ };
3183
+ pluginsIndexMap.get(plugin.name).contracts.push(contractEntry);
3184
+ dynamicCount++;
3185
+ if (verbose) {
3186
+ getLogger().info(` ⚡ Materialized: ${fullName}`);
3187
+ }
3188
+ }
3189
+ } catch (error) {
3190
+ getLogger().error(
3191
+ ` ❌ Failed to materialize dynamic contracts for ${plugin.name} (${config.prefix}): ${error}`
3192
+ );
3193
+ }
3194
+ }
3195
+ }
3196
+ if (manifest.actions && Array.isArray(manifest.actions)) {
3197
+ for (const entry of manifest.actions) {
3198
+ const { name: actionName, action: actionPath } = normalizeActionEntry(entry);
3199
+ if (!actionPath) continue;
3200
+ const metadataFilePath = resolveActionFilePath(
3201
+ actionPath,
3202
+ plugin.packageName,
3203
+ plugin.pluginPath,
3204
+ plugin.isLocal,
3205
+ projectRoot
3206
+ );
3207
+ if (!metadataFilePath) continue;
3208
+ const metadata = loadActionMetadata(metadataFilePath);
3209
+ if (!metadata) continue;
3210
+ const actionRelPath = path.relative(projectRoot, metadataFilePath);
3211
+ const pluginEntry = pluginsIndexMap.get(plugin.name);
3212
+ if (!pluginEntry.actions) pluginEntry.actions = [];
3213
+ pluginEntry.actions.push({
3214
+ name: metadata.name,
3215
+ description: metadata.description,
3216
+ path: "./" + actionRelPath.replace(/\\/g, "/")
3217
+ });
3218
+ if (verbose) {
3219
+ getLogger().info(` 🔧 Action: ${metadata.name} (${actionPath})`);
3220
+ }
3221
+ }
3222
+ }
3223
+ }
3224
+ const pluginsIndex = {
3225
+ plugins: Array.from(pluginsIndexMap.entries()).map(([name, data]) => ({
3226
+ name,
3227
+ path: data.path,
3228
+ contracts: data.contracts,
3229
+ ...data.actions && data.actions.length > 0 && { actions: data.actions },
3230
+ ...data.services?.length && { services: data.services },
3231
+ ...data.contexts?.length && { contexts: data.contexts },
3232
+ ...data.routes?.length && { routes: data.routes },
3233
+ ...data.commands?.length && { commands: data.commands }
3234
+ }))
3235
+ };
3236
+ fs.mkdirSync(outputDir, { recursive: true });
3237
+ const agentKitDir = path.dirname(outputDir);
3238
+ const pluginsIndexPath = path.join(agentKitDir, "plugins-index.yaml");
3239
+ fs.writeFileSync(pluginsIndexPath, YAML.stringify(pluginsIndex), "utf-8");
3240
+ if (verbose) {
3241
+ getLogger().info(`
3242
+ ✅ Plugins index written to: ${pluginsIndexPath}`);
3243
+ }
3244
+ return {
3245
+ pluginsIndex,
3246
+ staticCount,
3247
+ dynamicCount,
3248
+ outputDir
3249
+ };
3250
+ }
3251
+ const CACHE_TAG_START = '<script type="application/jay-cache">';
3252
+ const CACHE_TAG_END = "<\/script>";
3253
+ function hashParams(params) {
3254
+ const sortedParams = Object.keys(params).sort().reduce(
3255
+ (acc, key) => {
3256
+ acc[key] = params[key];
3257
+ return acc;
3258
+ },
3259
+ {}
3260
+ );
3261
+ const json = JSON.stringify(sortedParams);
3262
+ if (json === "{}") return "";
3263
+ return "_" + crypto.createHash("md5").update(json).digest("hex").substring(0, 8);
3264
+ }
3265
+ function embedCacheMetadata(jayHtmlContent, slowViewState, carryForward, sourcePath) {
3266
+ const metadata = JSON.stringify({ slowViewState, carryForward, sourcePath });
3267
+ const cacheTag = `${CACHE_TAG_START}${metadata}${CACHE_TAG_END}`;
3268
+ const headMatch = jayHtmlContent.match(/<head[^>]*>/i);
3269
+ if (headMatch) {
3270
+ const insertPos = headMatch.index + headMatch[0].length;
3271
+ return jayHtmlContent.substring(0, insertPos) + "\n" + cacheTag + jayHtmlContent.substring(insertPos);
3272
+ }
3273
+ return `${cacheTag}
3274
+ ${jayHtmlContent}`;
3275
+ }
3276
+ function extractCacheMetadata(fileContent) {
3277
+ const startIdx = fileContent.indexOf(CACHE_TAG_START);
3278
+ if (startIdx === -1) return void 0;
3279
+ const jsonStart = startIdx + CACHE_TAG_START.length;
3280
+ const endIdx = fileContent.indexOf(CACHE_TAG_END, jsonStart);
3281
+ if (endIdx === -1) return void 0;
3282
+ const jsonStr = fileContent.substring(jsonStart, endIdx);
3283
+ const metadata = JSON.parse(jsonStr);
3284
+ const tagEnd = endIdx + CACHE_TAG_END.length;
3285
+ const afterTag = fileContent[tagEnd] === "\n" ? tagEnd + 1 : tagEnd;
3286
+ const content = fileContent.substring(0, startIdx) + fileContent.substring(afterTag);
3287
+ return {
3288
+ content,
3289
+ slowViewState: metadata.slowViewState,
3290
+ carryForward: metadata.carryForward,
3291
+ sourcePath: metadata.sourcePath
3292
+ };
3293
+ }
3294
+ class SlowRenderCache {
3295
+ /**
3296
+ * @param cacheDir - Directory where pre-rendered jay-html files are stored
3297
+ * @param pagesRoot - Root directory of the pages (for relative path calculation)
3298
+ */
3299
+ constructor(cacheDir, pagesRoot) {
3300
+ __publicField2(this, "pathToFiles", /* @__PURE__ */ new Map());
3301
+ __publicField2(this, "cacheDir");
3302
+ __publicField2(this, "pagesRoot");
3303
+ this.cacheDir = cacheDir;
3304
+ this.pagesRoot = pagesRoot;
3305
+ }
3306
+ /**
3307
+ * Get a cached pre-rendered jay-html entry by reading from disk.
3308
+ * Returns undefined if the cache file doesn't exist or has no metadata tag.
3309
+ */
3310
+ async get(jayHtmlPath, params, routeDir) {
3311
+ const preRenderedPath = this.computeCachePath(jayHtmlPath, params, routeDir);
3312
+ let fileContent;
3313
+ try {
3314
+ fileContent = await fs__default.readFile(preRenderedPath, "utf-8");
3315
+ } catch {
3316
+ return void 0;
3317
+ }
3318
+ const extracted = extractCacheMetadata(fileContent);
3319
+ if (!extracted) return void 0;
3320
+ this.trackFile(jayHtmlPath, preRenderedPath);
3321
+ return {
3322
+ preRenderedPath,
3323
+ preRenderedContent: extracted.content,
3324
+ slowViewState: extracted.slowViewState,
3325
+ carryForward: extracted.carryForward,
3326
+ sourcePath: extracted.sourcePath
3327
+ };
3328
+ }
3329
+ /**
3330
+ * Store a pre-rendered jay-html entry.
3331
+ * Embeds metadata as a <script> tag and writes to disk.
3332
+ * Returns the full cache entry with stripped content.
3333
+ */
3334
+ async set(jayHtmlPath, params, preRenderedJayHtml, slowViewState, carryForward, routeDir) {
3335
+ const preRenderedPath = this.computeCachePath(jayHtmlPath, params, routeDir);
3336
+ const fileContent = embedCacheMetadata(
3337
+ preRenderedJayHtml,
3338
+ slowViewState,
3339
+ carryForward,
3340
+ jayHtmlPath
3341
+ );
3342
+ await fs__default.mkdir(path__default.dirname(preRenderedPath), { recursive: true });
3343
+ await fs__default.writeFile(preRenderedPath, fileContent, "utf-8");
3344
+ this.trackFile(jayHtmlPath, preRenderedPath);
3345
+ return {
3346
+ preRenderedPath,
3347
+ preRenderedContent: preRenderedJayHtml,
3348
+ slowViewState,
3349
+ carryForward,
3350
+ sourcePath: jayHtmlPath
3351
+ };
3352
+ }
3353
+ /**
3354
+ * Check if a pre-rendered entry exists for the given path and params
3355
+ */
3356
+ async has(jayHtmlPath, params, routeDir) {
3357
+ const preRenderedPath = this.computeCachePath(jayHtmlPath, params, routeDir);
3358
+ try {
3359
+ await fs__default.access(preRenderedPath);
3360
+ return true;
3361
+ } catch {
3362
+ return false;
3363
+ }
3364
+ }
3365
+ /**
3366
+ * Invalidate all cached entries for a given jay-html source path.
3367
+ * Deletes cached files from disk.
3368
+ */
3369
+ async invalidate(jayHtmlPath) {
3370
+ const files = this.pathToFiles.get(jayHtmlPath);
3371
+ if (files) {
3372
+ for (const filePath of files) {
3373
+ try {
3374
+ await fs__default.unlink(filePath);
3375
+ } catch {
3376
+ }
3377
+ }
3378
+ this.pathToFiles.delete(jayHtmlPath);
3379
+ }
3380
+ await this.scanAndDeleteCacheFiles(jayHtmlPath);
3381
+ }
3382
+ /**
3383
+ * Invalidate all entries that depend on a changed file.
3384
+ * The changedPath could be:
3385
+ * - A jay-html file itself
3386
+ * - A component file (page.ts)
3387
+ * - Any other dependency
3388
+ *
3389
+ * @param changedPath - Absolute path to the changed file
3390
+ * @param resolveDependencies - Optional function to resolve which jay-html files depend on the changed file
3391
+ */
3392
+ async invalidateByDependency(changedPath, resolveDependencies) {
3393
+ if (changedPath.endsWith(".jay-html")) {
3394
+ await this.invalidate(changedPath);
3395
+ return;
3396
+ }
3397
+ if (resolveDependencies) {
3398
+ const dependentPaths = resolveDependencies(changedPath);
3399
+ for (const depPath of dependentPaths) {
3400
+ await this.invalidate(depPath);
3401
+ }
3402
+ }
3403
+ }
3404
+ /**
3405
+ * Clear all cached entries and delete cached files from disk
3406
+ */
3407
+ async clear() {
3408
+ for (const files of this.pathToFiles.values()) {
3409
+ for (const filePath of files) {
3410
+ try {
3411
+ await fs__default.unlink(filePath);
3412
+ } catch {
3413
+ }
3414
+ }
3415
+ }
3416
+ this.pathToFiles.clear();
3417
+ try {
3418
+ await fs__default.rm(this.cacheDir, { recursive: true, force: true });
3419
+ } catch {
3420
+ }
3421
+ }
3422
+ /**
3423
+ * Get all cached jay-html paths (for debugging/monitoring)
3424
+ */
3425
+ getCachedPaths() {
3426
+ return Array.from(this.pathToFiles.keys());
3427
+ }
3428
+ /**
3429
+ * Compute the cache file path for a given jay-html path and params.
3430
+ */
3431
+ computeCachePath(jayHtmlPath, params, routeDir) {
3432
+ let dir;
3433
+ if (routeDir) {
3434
+ dir = routeDir;
3435
+ } else {
3436
+ const relativePath = path__default.relative(this.pagesRoot, jayHtmlPath);
3437
+ dir = relativePath.startsWith("..") ? path__default.join(
3438
+ "_plugins",
3439
+ crypto.createHash("md5").update(jayHtmlPath).digest("hex").slice(0, 12)
3440
+ ) : path__default.dirname(relativePath);
3441
+ }
3442
+ const basename = path__default.basename(jayHtmlPath, ".jay-html");
3443
+ const paramsHash = hashParams(params);
3444
+ const cacheFileName = `${basename}${paramsHash}.jay-html`;
3445
+ return path__default.join(this.cacheDir, dir, cacheFileName);
3446
+ }
3447
+ /**
3448
+ * Track a pre-rendered file path for invalidation.
3449
+ */
3450
+ trackFile(jayHtmlPath, preRenderedPath) {
3451
+ if (!this.pathToFiles.has(jayHtmlPath)) {
3452
+ this.pathToFiles.set(jayHtmlPath, /* @__PURE__ */ new Set());
3453
+ }
3454
+ this.pathToFiles.get(jayHtmlPath).add(preRenderedPath);
3455
+ }
3456
+ /**
3457
+ * Scan the cache directory for files matching a route and delete them.
3458
+ * Handles the startup case where pathToFiles is not populated from a previous session.
3459
+ */
3460
+ async scanAndDeleteCacheFiles(jayHtmlPath) {
3461
+ const relativePath = path__default.relative(this.pagesRoot, jayHtmlPath);
3462
+ const dir = relativePath.startsWith("..") ? path__default.join(
3463
+ "_plugins",
3464
+ crypto.createHash("md5").update(jayHtmlPath).digest("hex").slice(0, 12)
3465
+ ) : path__default.dirname(relativePath);
3466
+ const basename = path__default.basename(jayHtmlPath, ".jay-html");
3467
+ const cacheSubDir = path__default.join(this.cacheDir, dir);
3468
+ try {
3469
+ const files = await fs__default.readdir(cacheSubDir);
3470
+ for (const file of files) {
3471
+ if (file.startsWith(basename)) {
3472
+ try {
3473
+ await fs__default.unlink(path__default.join(cacheSubDir, file));
3474
+ } catch {
3475
+ }
3476
+ }
3477
+ }
3478
+ } catch {
3479
+ }
3480
+ }
3481
+ }
1613
3482
  class ServiceLifecycleManager {
1614
3483
  constructor(projectRoot, sourceBase = "src") {
1615
3484
  /** Path to project's lib/init.ts (makeJayInit pattern) */
@@ -1634,7 +3503,7 @@ class ServiceLifecycleManager {
1634
3503
  const extensions = [".ts", ".js"];
1635
3504
  for (const ext of extensions) {
1636
3505
  const filePath = path.join(this.projectRoot, this.sourceBase, "init" + ext);
1637
- if (fsSync.existsSync(filePath)) {
3506
+ if (fs.existsSync(filePath)) {
1638
3507
  return filePath;
1639
3508
  }
1640
3509
  }
@@ -2028,7 +3897,7 @@ function mergeDottedMultipartKeys(body) {
2028
3897
  }
2029
3898
  function parseMultipart(req, tempDir) {
2030
3899
  return new Promise((resolve, reject) => {
2031
- fs$1.mkdirSync(tempDir, { recursive: true });
3900
+ fs$2.mkdirSync(tempDir, { recursive: true });
2032
3901
  const files = {};
2033
3902
  let jsonData = {};
2034
3903
  let fileCount = 0;
@@ -2043,7 +3912,7 @@ function parseMultipart(req, tempDir) {
2043
3912
  const filename = info.filename || `upload-${fileCount}`;
2044
3913
  const tempPath = path$1.join(tempDir, `${fileCount}-${filename}`);
2045
3914
  let size = 0;
2046
- const writeStream = fs$1.createWriteStream(tempPath);
3915
+ const writeStream = fs$2.createWriteStream(tempPath);
2047
3916
  stream.pipe(writeStream);
2048
3917
  stream.on("data", (data) => {
2049
3918
  size += data.length;
@@ -2102,7 +3971,7 @@ function parseMultipart(req, tempDir) {
2102
3971
  }
2103
3972
  function cleanupTempDir(dir) {
2104
3973
  try {
2105
- fs$1.rmSync(dir, { recursive: true, force: true });
3974
+ fs$2.rmSync(dir, { recursive: true, force: true });
2106
3975
  } catch {
2107
3976
  }
2108
3977
  }
@@ -2133,7 +4002,7 @@ function actionBodyParser(options) {
2133
4002
  });
2134
4003
  return;
2135
4004
  }
2136
- const requestId = crypto.randomUUID();
4005
+ const requestId = crypto$1.randomUUID();
2137
4006
  const tempDir = path$1.join(buildFolder, ".tmp", "actions", requestId);
2138
4007
  parseMultipart(req, tempDir).then(({ body: body2, tempDir: td }) => {
2139
4008
  req.body = body2;
@@ -2180,7 +4049,7 @@ class FreezeStore {
2180
4049
  this.dir = path.join(buildFolder, "freezes");
2181
4050
  }
2182
4051
  async save(route, viewState, routePattern) {
2183
- await fs.mkdir(this.dir, { recursive: true });
4052
+ await fs$1.mkdir(this.dir, { recursive: true });
2184
4053
  const entry = {
2185
4054
  id: randomUUID().slice(0, 8),
2186
4055
  route,
@@ -2188,7 +4057,7 @@ class FreezeStore {
2188
4057
  viewState,
2189
4058
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
2190
4059
  };
2191
- await fs.writeFile(
4060
+ await fs$1.writeFile(
2192
4061
  path.join(this.dir, `${entry.id}.json`),
2193
4062
  JSON.stringify(entry, null, 2),
2194
4063
  "utf-8"
@@ -2197,7 +4066,7 @@ class FreezeStore {
2197
4066
  }
2198
4067
  async get(id) {
2199
4068
  try {
2200
- const content = await fs.readFile(path.join(this.dir, `${id}.json`), "utf-8");
4069
+ const content = await fs$1.readFile(path.join(this.dir, `${id}.json`), "utf-8");
2201
4070
  return JSON.parse(content);
2202
4071
  } catch {
2203
4072
  return void 0;
@@ -2205,12 +4074,12 @@ class FreezeStore {
2205
4074
  }
2206
4075
  async list(route) {
2207
4076
  try {
2208
- const files = await fs.readdir(this.dir);
4077
+ const files = await fs$1.readdir(this.dir);
2209
4078
  const entries = [];
2210
4079
  for (const file of files) {
2211
4080
  if (!file.endsWith(".json")) continue;
2212
4081
  try {
2213
- const content = await fs.readFile(path.join(this.dir, file), "utf-8");
4082
+ const content = await fs$1.readFile(path.join(this.dir, file), "utf-8");
2214
4083
  const entry = JSON.parse(content);
2215
4084
  if (!route || entry.routePattern === route || entry.route === route) {
2216
4085
  entries.push(entry);
@@ -2229,7 +4098,7 @@ class FreezeStore {
2229
4098
  const entry = await this.get(id);
2230
4099
  if (!entry) return false;
2231
4100
  entry.name = name;
2232
- await fs.writeFile(
4101
+ await fs$1.writeFile(
2233
4102
  path.join(this.dir, `${id}.json`),
2234
4103
  JSON.stringify(entry, null, 2),
2235
4104
  "utf-8"
@@ -2238,7 +4107,7 @@ class FreezeStore {
2238
4107
  }
2239
4108
  async delete(id) {
2240
4109
  try {
2241
- await fs.unlink(path.join(this.dir, `${id}.json`));
4110
+ await fs$1.unlink(path.join(this.dir, `${id}.json`));
2242
4111
  return true;
2243
4112
  } catch {
2244
4113
  return false;
@@ -2361,7 +4230,7 @@ function resolvePluginExport(pluginPath, exportSubpath) {
2361
4230
  const normalized = exportSubpath.replace(/^\.\//, "");
2362
4231
  const packageJsonPath = path__default.join(pluginPath, "package.json");
2363
4232
  try {
2364
- const packageJson = JSON.parse(fsSync__default.readFileSync(packageJsonPath, "utf-8"));
4233
+ const packageJson = JSON.parse(fs__default$1.readFileSync(packageJsonPath, "utf-8"));
2365
4234
  if (packageJson.exports) {
2366
4235
  const exportKey = "./" + normalized;
2367
4236
  const exportValue = packageJson.exports[exportKey];
@@ -2378,7 +4247,7 @@ function resolvePluginExport(pluginPath, exportSubpath) {
2378
4247
  for (const dir of ["dist", "lib", ""]) {
2379
4248
  const candidate = path__default.join(pluginPath, dir, normalized);
2380
4249
  try {
2381
- fsSync__default.accessSync(candidate);
4250
+ fs__default$1.accessSync(candidate);
2382
4251
  return candidate;
2383
4252
  } catch {
2384
4253
  }
@@ -2387,14 +4256,14 @@ function resolvePluginExport(pluginPath, exportSubpath) {
2387
4256
  }
2388
4257
  function resolvePluginModule(plugin) {
2389
4258
  const pkgJsonPath = path__default.join(plugin.pluginPath, "package.json");
2390
- if (fsSync__default.existsSync(pkgJsonPath)) {
4259
+ if (fs__default$1.existsSync(pkgJsonPath)) {
2391
4260
  try {
2392
- const pkg = JSON.parse(fsSync__default.readFileSync(pkgJsonPath, "utf-8"));
4261
+ const pkg = JSON.parse(fs__default$1.readFileSync(pkgJsonPath, "utf-8"));
2393
4262
  const mainExport = pkg.exports?.["."];
2394
4263
  const mainPath = typeof mainExport === "string" ? mainExport : mainExport?.default || mainExport?.import || pkg.main;
2395
4264
  if (mainPath) {
2396
4265
  const resolved = path__default.join(plugin.pluginPath, mainPath);
2397
- if (fsSync__default.existsSync(resolved)) return resolved;
4266
+ if (fs__default$1.existsSync(resolved)) return resolved;
2398
4267
  }
2399
4268
  } catch {
2400
4269
  }
@@ -2403,7 +4272,7 @@ function resolvePluginModule(plugin) {
2403
4272
  for (const ext of [".ts", ".js", "/index.ts", "/index.js"]) {
2404
4273
  const candidate = path__default.join(plugin.pluginPath, modulePath + ext);
2405
4274
  try {
2406
- fsSync__default.accessSync(candidate);
4275
+ fs__default$1.accessSync(candidate);
2407
4276
  return candidate;
2408
4277
  } catch {
2409
4278
  }
@@ -2411,7 +4280,7 @@ function resolvePluginModule(plugin) {
2411
4280
  for (const ext of [".ts", ".js"]) {
2412
4281
  const candidate = path__default.join(plugin.pluginPath, "lib", path__default.basename(modulePath) + ext);
2413
4282
  try {
2414
- fsSync__default.accessSync(candidate);
4283
+ fs__default$1.accessSync(candidate);
2415
4284
  return candidate;
2416
4285
  } catch {
2417
4286
  }
@@ -3184,7 +5053,7 @@ async function mkDevServer(rawOptions) {
3184
5053
  for (let i = devServerRoutes.length - 1; i >= 0; i--) {
3185
5054
  const route = devServerRoutes[i];
3186
5055
  const fsPath = route.fsRoute.jayHtmlPath;
3187
- const stillOnDisk = scannedRawRoutes.has(route.fsRoute.rawRoute) && fsSync__default.existsSync(fsPath);
5056
+ const stillOnDisk = scannedRawRoutes.has(route.fsRoute.rawRoute) && fs__default$1.existsSync(fsPath);
3188
5057
  if (!stillOnDisk) {
3189
5058
  devServerRoutes.splice(i, 1);
3190
5059
  }