@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.
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1914 -45
- 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 {
|
|
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
|
|
14
|
-
import
|
|
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 {
|
|
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
|
|
22
|
-
import
|
|
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
|
|
32
|
-
var
|
|
33
|
-
var
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
1433
|
+
if (fs.existsSync(resolvedPath + ".ts")) {
|
|
1432
1434
|
resolvedPath += ".ts";
|
|
1433
|
-
} else if (
|
|
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") && !
|
|
1440
|
+
} else if (resolvedPath.endsWith(".js") && !fs.existsSync(resolvedPath)) {
|
|
1439
1441
|
const tsPath = resolvedPath.slice(0, -3) + ".ts";
|
|
1440
|
-
if (
|
|
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
|
|
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 (
|
|
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$
|
|
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$
|
|
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$
|
|
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(
|
|
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
|
-
|
|
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 (
|
|
4259
|
+
if (fs__default$1.existsSync(pkgJsonPath)) {
|
|
2391
4260
|
try {
|
|
2392
|
-
const pkg = JSON.parse(
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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) &&
|
|
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
|
}
|