@jay-framework/stack-server-runtime 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +337 -29
- package/dist/index.js +672 -89
- package/package.json +13 -11
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import "js-beautify";
|
|
|
17
17
|
import fs$2 from "fs";
|
|
18
18
|
import path$1 from "path";
|
|
19
19
|
import YAML from "yaml";
|
|
20
|
+
import { getLogger } from "@jay-framework/logger";
|
|
20
21
|
import crypto from "node:crypto";
|
|
21
22
|
const serviceRegistry = /* @__PURE__ */ new Map();
|
|
22
23
|
function registerService(marker, service) {
|
|
@@ -40,9 +41,13 @@ function hasService(marker) {
|
|
|
40
41
|
function clearServiceRegistry() {
|
|
41
42
|
serviceRegistry.clear();
|
|
42
43
|
}
|
|
44
|
+
function getServiceRegistry() {
|
|
45
|
+
return serviceRegistry;
|
|
46
|
+
}
|
|
43
47
|
function resolveServices(serviceMarkers) {
|
|
44
48
|
return serviceMarkers.map((marker) => getService(marker));
|
|
45
49
|
}
|
|
50
|
+
globalThis.__JAY_SERVICE_RESOLVER__ = resolveServices;
|
|
46
51
|
const initCallbacks = [];
|
|
47
52
|
const shutdownCallbacks = [];
|
|
48
53
|
function onInit(callback) {
|
|
@@ -94,10 +99,17 @@ class DevSlowlyChangingPhase {
|
|
|
94
99
|
}
|
|
95
100
|
async runSlowlyForPage(pageParams, pageProps, parts) {
|
|
96
101
|
for (const part of parts) {
|
|
97
|
-
const { compDefinition } = part;
|
|
102
|
+
const { compDefinition, contractInfo } = part;
|
|
98
103
|
if (compDefinition.loadParams) {
|
|
99
104
|
const services = resolveServices(compDefinition.services);
|
|
100
|
-
const
|
|
105
|
+
const loadParamsArgs = contractInfo ? [
|
|
106
|
+
...services,
|
|
107
|
+
{
|
|
108
|
+
contractName: contractInfo.contractName,
|
|
109
|
+
metadata: contractInfo.metadata
|
|
110
|
+
}
|
|
111
|
+
] : services;
|
|
112
|
+
const compParams = compDefinition.loadParams(loadParamsArgs);
|
|
101
113
|
if (!await findMatchingParams(pageParams, compParams))
|
|
102
114
|
return notFound();
|
|
103
115
|
}
|
|
@@ -105,11 +117,19 @@ class DevSlowlyChangingPhase {
|
|
|
105
117
|
let slowlyViewState = {};
|
|
106
118
|
let carryForward = {};
|
|
107
119
|
for (const part of parts) {
|
|
108
|
-
const { compDefinition, key } = part;
|
|
120
|
+
const { compDefinition, key, contractInfo } = part;
|
|
109
121
|
if (compDefinition.slowlyRender) {
|
|
110
122
|
const services = resolveServices(compDefinition.services);
|
|
123
|
+
const partProps = {
|
|
124
|
+
...pageProps,
|
|
125
|
+
...pageParams,
|
|
126
|
+
...contractInfo && {
|
|
127
|
+
contractName: contractInfo.contractName,
|
|
128
|
+
metadata: contractInfo.metadata
|
|
129
|
+
}
|
|
130
|
+
};
|
|
111
131
|
const slowlyRenderedPart = await compDefinition.slowlyRender(
|
|
112
|
-
|
|
132
|
+
partProps,
|
|
113
133
|
...services
|
|
114
134
|
);
|
|
115
135
|
if (slowlyRenderedPart.kind === "PhaseOutput") {
|
|
@@ -136,12 +156,20 @@ async function renderFastChangingData(pageParams, pageProps, carryForward, parts
|
|
|
136
156
|
let fastViewState = {};
|
|
137
157
|
let fastCarryForward = {};
|
|
138
158
|
for (const part of parts) {
|
|
139
|
-
const { compDefinition, key } = part;
|
|
159
|
+
const { compDefinition, key, contractInfo } = part;
|
|
140
160
|
if (compDefinition.fastRender) {
|
|
141
161
|
const partSlowlyCarryForward = key ? carryForward[key] : carryForward;
|
|
142
162
|
const services = resolveServices(compDefinition.services);
|
|
163
|
+
const partProps = {
|
|
164
|
+
...pageProps,
|
|
165
|
+
...pageParams,
|
|
166
|
+
...contractInfo && {
|
|
167
|
+
contractName: contractInfo.contractName,
|
|
168
|
+
metadata: contractInfo.metadata
|
|
169
|
+
}
|
|
170
|
+
};
|
|
143
171
|
const fastRenderedPart = await compDefinition.fastRender(
|
|
144
|
-
|
|
172
|
+
partProps,
|
|
145
173
|
partSlowlyCarryForward,
|
|
146
174
|
...services
|
|
147
175
|
);
|
|
@@ -234,13 +262,13 @@ ${clientInitExecution}
|
|
|
234
262
|
const target = document.getElementById('target');
|
|
235
263
|
const pageComp = makeCompositeJayComponent(render, viewState, fastCarryForward, ${compositeParts}, trackByMap)
|
|
236
264
|
|
|
237
|
-
const instance = pageComp({
|
|
265
|
+
const instance = pageComp({/* placeholder for page props */})
|
|
238
266
|
${automationWrap}
|
|
239
267
|
<\/script>
|
|
240
268
|
</body>
|
|
241
269
|
</html>`;
|
|
242
270
|
}
|
|
243
|
-
const require$
|
|
271
|
+
const require$3 = createRequire(import.meta.url);
|
|
244
272
|
async function loadPageParts(vite, route, pagesBase, projectBase, jayRollupConfig, options) {
|
|
245
273
|
const exists = await fs$1.access(route.compPath, fs$1.constants.F_OK).then(() => true).catch(() => false);
|
|
246
274
|
const parts = [];
|
|
@@ -268,11 +296,12 @@ async function loadPageParts(vite, route, pagesBase, projectBase, jayRollupConfi
|
|
|
268
296
|
);
|
|
269
297
|
return jayHtmlWithValidations.mapAsync(async (jayHtml) => {
|
|
270
298
|
const usedPackages = /* @__PURE__ */ new Set();
|
|
299
|
+
const headlessInstanceComponents = [];
|
|
271
300
|
for await (const headlessImport of jayHtml.headlessImports) {
|
|
272
301
|
const module = headlessImport.codeLink.module;
|
|
273
302
|
const name = headlessImport.codeLink.names[0].name;
|
|
274
303
|
const isLocalModule = module[0] === "." || module[0] === "/";
|
|
275
|
-
const modulePath = isLocalModule ? path__default.resolve(dirName, module) : require$
|
|
304
|
+
const modulePath = isLocalModule ? path__default.resolve(dirName, module) : require$3.resolve(module, { paths: require$3.resolve.paths(dirName) });
|
|
276
305
|
const compDefinition = (await vite.ssrLoadModule(modulePath))[name];
|
|
277
306
|
const moduleImport = isLocalModule ? path__default.resolve(dirName, module) : module;
|
|
278
307
|
const isNpmPackage = !isLocalModule;
|
|
@@ -281,23 +310,85 @@ async function loadPageParts(vite, route, pagesBase, projectBase, jayRollupConfi
|
|
|
281
310
|
const packageName = module.startsWith("@") ? module.split("/").slice(0, 2).join("/") : module.split("/")[0];
|
|
282
311
|
usedPackages.add(packageName);
|
|
283
312
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
313
|
+
if (headlessImport.key) {
|
|
314
|
+
const key = headlessImport.key;
|
|
315
|
+
const part = {
|
|
316
|
+
key,
|
|
317
|
+
compDefinition,
|
|
318
|
+
clientImport: `import {${name}} from '${clientModuleImport}'`,
|
|
319
|
+
clientPart: `{comp: ${name}.comp, contextMarkers: ${name}.contexts || [], key: '${key}'}`,
|
|
320
|
+
// Include contract info for dynamic contract components
|
|
321
|
+
contractInfo: headlessImport.contract ? {
|
|
322
|
+
contractName: headlessImport.contract.name,
|
|
323
|
+
metadata: headlessImport.metadata
|
|
324
|
+
} : void 0
|
|
325
|
+
};
|
|
326
|
+
parts.push(part);
|
|
327
|
+
}
|
|
328
|
+
if (!headlessImport.key && headlessImport.contract) {
|
|
329
|
+
headlessInstanceComponents.push({
|
|
330
|
+
contractName: headlessImport.contractName,
|
|
331
|
+
compDefinition,
|
|
332
|
+
contract: headlessImport.contract
|
|
333
|
+
});
|
|
334
|
+
}
|
|
292
335
|
}
|
|
336
|
+
const headlessContracts = jayHtml.headlessImports.filter((hi) => hi.contract !== void 0 && hi.key !== void 0).map((hi) => ({
|
|
337
|
+
key: hi.key,
|
|
338
|
+
contract: hi.contract,
|
|
339
|
+
contractPath: hi.contractPath
|
|
340
|
+
}));
|
|
293
341
|
return {
|
|
294
342
|
parts,
|
|
295
343
|
serverTrackByMap: jayHtml.serverTrackByMap,
|
|
296
344
|
clientTrackByMap: jayHtml.clientTrackByMap,
|
|
297
|
-
usedPackages
|
|
345
|
+
usedPackages,
|
|
346
|
+
headlessContracts,
|
|
347
|
+
headlessInstanceComponents
|
|
298
348
|
};
|
|
299
349
|
});
|
|
300
350
|
}
|
|
351
|
+
async function slowRenderInstances(discovered, headlessInstanceComponents) {
|
|
352
|
+
const componentByContractName = /* @__PURE__ */ new Map();
|
|
353
|
+
for (const comp of headlessInstanceComponents) {
|
|
354
|
+
componentByContractName.set(comp.contractName, comp);
|
|
355
|
+
}
|
|
356
|
+
const resolvedData = [];
|
|
357
|
+
const slowViewStates = {};
|
|
358
|
+
const discoveredForFast = [];
|
|
359
|
+
const carryForwards = {};
|
|
360
|
+
for (const instance of discovered) {
|
|
361
|
+
const comp = componentByContractName.get(instance.contractName);
|
|
362
|
+
if (!comp || !comp.compDefinition.slowlyRender) {
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
const services = resolveServices(comp.compDefinition.services);
|
|
366
|
+
const slowResult = await comp.compDefinition.slowlyRender(instance.props, ...services);
|
|
367
|
+
if (slowResult.kind === "PhaseOutput") {
|
|
368
|
+
const coordKey = instance.coordinate.join("/");
|
|
369
|
+
resolvedData.push({
|
|
370
|
+
coordinate: instance.coordinate,
|
|
371
|
+
contract: comp.contract,
|
|
372
|
+
slowViewState: slowResult.rendered
|
|
373
|
+
});
|
|
374
|
+
slowViewStates[coordKey] = slowResult.rendered;
|
|
375
|
+
carryForwards[coordKey] = slowResult.carryForward;
|
|
376
|
+
discoveredForFast.push({
|
|
377
|
+
contractName: instance.contractName,
|
|
378
|
+
props: instance.props,
|
|
379
|
+
coordinate: instance.coordinate
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (discoveredForFast.length === 0) {
|
|
384
|
+
return void 0;
|
|
385
|
+
}
|
|
386
|
+
return {
|
|
387
|
+
resolvedData,
|
|
388
|
+
slowViewStates,
|
|
389
|
+
instancePhaseData: { discovered: discoveredForFast, carryForwards }
|
|
390
|
+
};
|
|
391
|
+
}
|
|
301
392
|
class ActionRegistry {
|
|
302
393
|
constructor() {
|
|
303
394
|
__publicField(this, "actions", /* @__PURE__ */ new Map());
|
|
@@ -445,10 +536,6 @@ async function executeAction(actionName, input) {
|
|
|
445
536
|
function getActionCacheHeaders(actionName) {
|
|
446
537
|
return actionRegistry.getCacheHeaders(actionName);
|
|
447
538
|
}
|
|
448
|
-
async function runAction(action, input) {
|
|
449
|
-
const services = resolveServices(action.services);
|
|
450
|
-
return action.handler(input, ...services);
|
|
451
|
-
}
|
|
452
539
|
var __defProp2 = Object.defineProperty;
|
|
453
540
|
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
454
541
|
var __publicField2 = (obj, key, value) => {
|
|
@@ -545,7 +632,7 @@ new Proxy(e, {
|
|
|
545
632
|
return t[r];
|
|
546
633
|
}
|
|
547
634
|
});
|
|
548
|
-
const require$
|
|
635
|
+
const require$2 = createRequire$1(import.meta.url);
|
|
549
636
|
async function discoverAndRegisterActions(options) {
|
|
550
637
|
const {
|
|
551
638
|
projectRoot,
|
|
@@ -562,13 +649,13 @@ async function discoverAndRegisterActions(options) {
|
|
|
562
649
|
const actionsPath = path.resolve(projectRoot, actionsDir);
|
|
563
650
|
if (!fs.existsSync(actionsPath)) {
|
|
564
651
|
if (verbose) {
|
|
565
|
-
|
|
652
|
+
getLogger().info(`[Actions] No actions directory found at ${actionsPath}`);
|
|
566
653
|
}
|
|
567
654
|
return result;
|
|
568
655
|
}
|
|
569
656
|
const actionFiles = await findActionFiles(actionsPath);
|
|
570
657
|
if (verbose) {
|
|
571
|
-
|
|
658
|
+
getLogger().info(`[Actions] Found ${actionFiles.length} action file(s)`);
|
|
572
659
|
}
|
|
573
660
|
for (const filePath of actionFiles) {
|
|
574
661
|
result.scannedFiles.push(filePath);
|
|
@@ -585,12 +672,14 @@ async function discoverAndRegisterActions(options) {
|
|
|
585
672
|
result.actionNames.push(exportValue.actionName);
|
|
586
673
|
result.actionCount++;
|
|
587
674
|
if (verbose) {
|
|
588
|
-
|
|
675
|
+
getLogger().info(
|
|
676
|
+
`[Actions] Registered: ${exportValue.actionName}`
|
|
677
|
+
);
|
|
589
678
|
}
|
|
590
679
|
}
|
|
591
680
|
}
|
|
592
681
|
} catch (error) {
|
|
593
|
-
|
|
682
|
+
getLogger().error(`[Actions] Failed to import ${filePath}: ${error}`);
|
|
594
683
|
}
|
|
595
684
|
}
|
|
596
685
|
return result;
|
|
@@ -657,9 +746,8 @@ async function discoverNpmPluginActions(projectRoot, registry, verbose, viteServ
|
|
|
657
746
|
continue;
|
|
658
747
|
}
|
|
659
748
|
if (verbose) {
|
|
660
|
-
|
|
661
|
-
`[Actions] NPM plugin "${packageName}" declares actions
|
|
662
|
-
pluginConfig.actions
|
|
749
|
+
getLogger().info(
|
|
750
|
+
`[Actions] NPM plugin "${packageName}" declares actions: ${JSON.stringify(pluginConfig.actions)}`
|
|
663
751
|
);
|
|
664
752
|
}
|
|
665
753
|
const actions = await registerNpmPluginActions(
|
|
@@ -675,13 +763,13 @@ async function discoverNpmPluginActions(projectRoot, registry, verbose, viteServ
|
|
|
675
763
|
}
|
|
676
764
|
}
|
|
677
765
|
} catch (error) {
|
|
678
|
-
|
|
766
|
+
getLogger().error(`[Actions] Failed to read project package.json: ${error}`);
|
|
679
767
|
}
|
|
680
768
|
return allActions;
|
|
681
769
|
}
|
|
682
770
|
function tryResolvePluginYaml(packageName, projectRoot) {
|
|
683
771
|
try {
|
|
684
|
-
return require$
|
|
772
|
+
return require$2.resolve(`${packageName}/plugin.yaml`, {
|
|
685
773
|
paths: [projectRoot]
|
|
686
774
|
});
|
|
687
775
|
} catch {
|
|
@@ -703,18 +791,18 @@ async function registerNpmPluginActions(packageName, pluginConfig, registry, ver
|
|
|
703
791
|
registry.register(actionExport);
|
|
704
792
|
registeredActions.push(actionExport.actionName);
|
|
705
793
|
if (verbose) {
|
|
706
|
-
|
|
794
|
+
getLogger().info(
|
|
707
795
|
`[Actions] Registered NPM plugin action: ${actionExport.actionName}`
|
|
708
796
|
);
|
|
709
797
|
}
|
|
710
798
|
} else {
|
|
711
|
-
|
|
799
|
+
getLogger().warn(
|
|
712
800
|
`[Actions] NPM plugin "${packageName}" declares action "${actionName}" but it's not exported or not a JayAction`
|
|
713
801
|
);
|
|
714
802
|
}
|
|
715
803
|
}
|
|
716
804
|
} catch (importError) {
|
|
717
|
-
|
|
805
|
+
getLogger().error(`[Actions] Failed to import NPM plugin "${packageName}": ${importError}`);
|
|
718
806
|
}
|
|
719
807
|
return registeredActions;
|
|
720
808
|
}
|
|
@@ -729,7 +817,9 @@ async function discoverPluginActions(pluginPath, projectRoot, registry = actionR
|
|
|
729
817
|
const registeredActions = [];
|
|
730
818
|
const pluginName = pluginConfig.name || path.basename(pluginPath);
|
|
731
819
|
if (verbose) {
|
|
732
|
-
|
|
820
|
+
getLogger().info(
|
|
821
|
+
`[Actions] Plugin "${pluginName}" declares actions: ${JSON.stringify(pluginConfig.actions)}`
|
|
822
|
+
);
|
|
733
823
|
}
|
|
734
824
|
let modulePath = pluginConfig.module ? path.join(pluginPath, pluginConfig.module) : path.join(pluginPath, "index.ts");
|
|
735
825
|
if (!fs.existsSync(modulePath)) {
|
|
@@ -740,7 +830,7 @@ async function discoverPluginActions(pluginPath, projectRoot, registry = actionR
|
|
|
740
830
|
} else if (fs.existsSync(jsPath)) {
|
|
741
831
|
modulePath = jsPath;
|
|
742
832
|
} else {
|
|
743
|
-
|
|
833
|
+
getLogger().warn(`[Actions] Plugin "${pluginName}" module not found at ${modulePath}`);
|
|
744
834
|
return [];
|
|
745
835
|
}
|
|
746
836
|
}
|
|
@@ -757,25 +847,32 @@ async function discoverPluginActions(pluginPath, projectRoot, registry = actionR
|
|
|
757
847
|
registry.register(actionExport);
|
|
758
848
|
registeredActions.push(actionExport.actionName);
|
|
759
849
|
if (verbose) {
|
|
760
|
-
|
|
850
|
+
getLogger().info(
|
|
761
851
|
`[Actions] Registered plugin action: ${actionExport.actionName}`
|
|
762
852
|
);
|
|
763
853
|
}
|
|
764
854
|
} else {
|
|
765
|
-
|
|
855
|
+
getLogger().warn(
|
|
766
856
|
`[Actions] Plugin "${pluginName}" declares action "${actionName}" but it's not exported or not a JayAction`
|
|
767
857
|
);
|
|
768
858
|
}
|
|
769
859
|
}
|
|
770
860
|
} catch (importError) {
|
|
771
|
-
|
|
861
|
+
getLogger().error(
|
|
862
|
+
`[Actions] Failed to import plugin module at ${modulePath}: ${importError}`
|
|
863
|
+
);
|
|
772
864
|
}
|
|
773
865
|
return registeredActions;
|
|
774
866
|
}
|
|
775
|
-
const
|
|
776
|
-
async function
|
|
777
|
-
const {
|
|
778
|
-
|
|
867
|
+
const require$1 = createRequire$1(import.meta.url);
|
|
868
|
+
async function scanPlugins(options) {
|
|
869
|
+
const {
|
|
870
|
+
projectRoot,
|
|
871
|
+
verbose = false,
|
|
872
|
+
includeDevDeps = false,
|
|
873
|
+
discoverTransitive = false
|
|
874
|
+
} = options;
|
|
875
|
+
const plugins = /* @__PURE__ */ new Map();
|
|
779
876
|
const visitedPackages = /* @__PURE__ */ new Set();
|
|
780
877
|
const localPluginsPath = path.join(projectRoot, "src/plugins");
|
|
781
878
|
if (fs.existsSync(localPluginsPath)) {
|
|
@@ -788,37 +885,37 @@ async function discoverPluginsWithInit(options) {
|
|
|
788
885
|
const manifest = loadPluginManifest(pluginPath);
|
|
789
886
|
if (!manifest)
|
|
790
887
|
continue;
|
|
791
|
-
const initConfig = resolvePluginInit(pluginPath, manifest.init, true);
|
|
792
|
-
if (!initConfig)
|
|
793
|
-
continue;
|
|
794
888
|
const dependencies = await getPackageDependencies(pluginPath);
|
|
795
|
-
|
|
796
|
-
|
|
889
|
+
const pluginName = manifest.name || entry.name;
|
|
890
|
+
plugins.set(entry.name, {
|
|
891
|
+
name: pluginName,
|
|
797
892
|
pluginPath,
|
|
798
893
|
packageName: pluginPath,
|
|
799
|
-
// For local, use path
|
|
894
|
+
// For local, use path as identifier
|
|
800
895
|
isLocal: true,
|
|
801
|
-
|
|
802
|
-
initExport: initConfig.export,
|
|
896
|
+
manifest,
|
|
803
897
|
dependencies
|
|
804
898
|
});
|
|
805
899
|
visitedPackages.add(pluginPath);
|
|
806
900
|
if (verbose) {
|
|
807
|
-
|
|
808
|
-
`[PluginInit] Found local plugin with init: ${manifest.name || entry.name}`
|
|
809
|
-
);
|
|
901
|
+
getLogger().info(`[PluginScanner] Found local plugin: ${pluginName}`);
|
|
810
902
|
}
|
|
811
903
|
}
|
|
812
904
|
} catch (error) {
|
|
813
|
-
|
|
905
|
+
if (verbose) {
|
|
906
|
+
getLogger().warn(`[PluginScanner] Failed to scan local plugins: ${error}`);
|
|
907
|
+
}
|
|
814
908
|
}
|
|
815
909
|
}
|
|
816
910
|
const projectPackageJsonPath = path.join(projectRoot, "package.json");
|
|
817
911
|
if (fs.existsSync(projectPackageJsonPath)) {
|
|
818
912
|
try {
|
|
819
913
|
const projectPackageJson = JSON.parse(fs.readFileSync(projectPackageJsonPath, "utf-8"));
|
|
820
|
-
const
|
|
821
|
-
|
|
914
|
+
const deps = {
|
|
915
|
+
...projectPackageJson.dependencies,
|
|
916
|
+
...includeDevDeps ? projectPackageJson.devDependencies : {}
|
|
917
|
+
};
|
|
918
|
+
const packagesToCheck = Object.keys(deps);
|
|
822
919
|
while (packagesToCheck.length > 0) {
|
|
823
920
|
const depName = packagesToCheck.shift();
|
|
824
921
|
if (visitedPackages.has(depName))
|
|
@@ -826,7 +923,7 @@ async function discoverPluginsWithInit(options) {
|
|
|
826
923
|
visitedPackages.add(depName);
|
|
827
924
|
let pluginYamlPath;
|
|
828
925
|
try {
|
|
829
|
-
pluginYamlPath =
|
|
926
|
+
pluginYamlPath = require$1.resolve(`${depName}/plugin.yaml`, {
|
|
830
927
|
paths: [projectRoot]
|
|
831
928
|
});
|
|
832
929
|
} catch {
|
|
@@ -836,30 +933,76 @@ async function discoverPluginsWithInit(options) {
|
|
|
836
933
|
const manifest = loadPluginManifest(pluginPath);
|
|
837
934
|
if (!manifest)
|
|
838
935
|
continue;
|
|
839
|
-
const initConfig = resolvePluginInit(pluginPath, manifest.init, false);
|
|
840
|
-
if (!initConfig)
|
|
841
|
-
continue;
|
|
842
936
|
const dependencies = await getPackageDependencies(pluginPath);
|
|
843
|
-
plugins.
|
|
937
|
+
plugins.set(depName, {
|
|
844
938
|
name: manifest.name || depName,
|
|
845
939
|
pluginPath,
|
|
846
940
|
packageName: depName,
|
|
847
941
|
isLocal: false,
|
|
848
|
-
|
|
849
|
-
initExport: initConfig.export,
|
|
942
|
+
manifest,
|
|
850
943
|
dependencies
|
|
851
944
|
});
|
|
852
945
|
if (verbose) {
|
|
853
|
-
|
|
946
|
+
getLogger().info(`[PluginScanner] Found NPM plugin: ${depName}`);
|
|
854
947
|
}
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
948
|
+
if (discoverTransitive) {
|
|
949
|
+
for (const transitiveDep of dependencies) {
|
|
950
|
+
if (!visitedPackages.has(transitiveDep)) {
|
|
951
|
+
packagesToCheck.push(transitiveDep);
|
|
952
|
+
}
|
|
858
953
|
}
|
|
859
954
|
}
|
|
860
955
|
}
|
|
861
956
|
} catch (error) {
|
|
862
|
-
|
|
957
|
+
if (verbose) {
|
|
958
|
+
getLogger().warn(`[PluginScanner] Failed to scan NPM plugins: ${error}`);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
return plugins;
|
|
963
|
+
}
|
|
964
|
+
async function getPackageDependencies(pluginPath) {
|
|
965
|
+
const packageJsonPath = path.join(pluginPath, "package.json");
|
|
966
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
967
|
+
return [];
|
|
968
|
+
}
|
|
969
|
+
try {
|
|
970
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
971
|
+
return Object.keys(packageJson.dependencies || {});
|
|
972
|
+
} catch {
|
|
973
|
+
return [];
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
async function discoverPluginsWithInit(options) {
|
|
977
|
+
const { projectRoot, verbose = false } = options;
|
|
978
|
+
const scannedPlugins = await scanPlugins({
|
|
979
|
+
projectRoot,
|
|
980
|
+
verbose,
|
|
981
|
+
includeDevDeps: false,
|
|
982
|
+
// Only runtime dependencies
|
|
983
|
+
discoverTransitive: true
|
|
984
|
+
// Need transitive for dependency ordering
|
|
985
|
+
});
|
|
986
|
+
const plugins = [];
|
|
987
|
+
for (const [key, scanned] of scannedPlugins) {
|
|
988
|
+
const initConfig = resolvePluginInit(
|
|
989
|
+
scanned.pluginPath,
|
|
990
|
+
scanned.manifest.init,
|
|
991
|
+
scanned.isLocal
|
|
992
|
+
);
|
|
993
|
+
if (!initConfig)
|
|
994
|
+
continue;
|
|
995
|
+
plugins.push({
|
|
996
|
+
name: scanned.name,
|
|
997
|
+
pluginPath: scanned.pluginPath,
|
|
998
|
+
packageName: scanned.packageName,
|
|
999
|
+
isLocal: scanned.isLocal,
|
|
1000
|
+
initModule: initConfig.module,
|
|
1001
|
+
initExport: initConfig.export,
|
|
1002
|
+
dependencies: scanned.dependencies
|
|
1003
|
+
});
|
|
1004
|
+
if (verbose) {
|
|
1005
|
+
getLogger().info(`[PluginInit] Found plugin with init: ${scanned.name}`);
|
|
863
1006
|
}
|
|
864
1007
|
}
|
|
865
1008
|
return plugins;
|
|
@@ -899,18 +1042,6 @@ function resolvePluginInit(pluginPath, initConfig, isLocal) {
|
|
|
899
1042
|
return null;
|
|
900
1043
|
}
|
|
901
1044
|
}
|
|
902
|
-
async function getPackageDependencies(pluginPath) {
|
|
903
|
-
const packageJsonPath = path.join(pluginPath, "package.json");
|
|
904
|
-
if (!fs.existsSync(packageJsonPath)) {
|
|
905
|
-
return [];
|
|
906
|
-
}
|
|
907
|
-
try {
|
|
908
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
909
|
-
return Object.keys(packageJson.dependencies || {});
|
|
910
|
-
} catch {
|
|
911
|
-
return [];
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
1045
|
function sortPluginsByDependencies(plugins) {
|
|
915
1046
|
const pluginNames = new Set(plugins.map((p) => p.packageName));
|
|
916
1047
|
const sorted = [];
|
|
@@ -920,7 +1051,7 @@ function sortPluginsByDependencies(plugins) {
|
|
|
920
1051
|
if (visited.has(plugin.packageName))
|
|
921
1052
|
return;
|
|
922
1053
|
if (visiting.has(plugin.packageName)) {
|
|
923
|
-
|
|
1054
|
+
getLogger().warn(`[PluginInit] Circular dependency detected for ${plugin.name}`);
|
|
924
1055
|
return;
|
|
925
1056
|
}
|
|
926
1057
|
visiting.add(plugin.packageName);
|
|
@@ -960,14 +1091,14 @@ async function executePluginServerInits(plugins, viteServer, verbose = false) {
|
|
|
960
1091
|
}
|
|
961
1092
|
const jayInit = pluginModule[plugin.initExport];
|
|
962
1093
|
if (!jayInit || jayInit.__brand !== "JayInit") {
|
|
963
|
-
|
|
1094
|
+
getLogger().warn(
|
|
964
1095
|
`[PluginInit] Plugin "${plugin.name}" init module doesn't export a valid JayInit at "${plugin.initExport}"`
|
|
965
1096
|
);
|
|
966
1097
|
continue;
|
|
967
1098
|
}
|
|
968
1099
|
if (typeof jayInit._serverInit === "function") {
|
|
969
1100
|
if (verbose) {
|
|
970
|
-
|
|
1101
|
+
getLogger().info(`[DevServer] Running server init: ${plugin.name}`);
|
|
971
1102
|
}
|
|
972
1103
|
const clientData = await jayInit._serverInit();
|
|
973
1104
|
if (clientData !== void 0 && clientData !== null) {
|
|
@@ -975,9 +1106,8 @@ async function executePluginServerInits(plugins, viteServer, verbose = false) {
|
|
|
975
1106
|
}
|
|
976
1107
|
}
|
|
977
1108
|
} catch (error) {
|
|
978
|
-
|
|
979
|
-
`[PluginInit] Failed to execute server init for "${plugin.name}"
|
|
980
|
-
error
|
|
1109
|
+
getLogger().error(
|
|
1110
|
+
`[PluginInit] Failed to execute server init for "${plugin.name}": ${error}`
|
|
981
1111
|
);
|
|
982
1112
|
}
|
|
983
1113
|
}
|
|
@@ -1146,6 +1276,451 @@ class SlowRenderCache {
|
|
|
1146
1276
|
return Array.from(this.pathToKeys.keys());
|
|
1147
1277
|
}
|
|
1148
1278
|
}
|
|
1279
|
+
const require2 = createRequire(import.meta.url);
|
|
1280
|
+
async function executeDynamicGenerator(plugin, config, projectRoot, services, verbose, viteServer) {
|
|
1281
|
+
const { pluginPath, name: pluginName, isLocal, packageName } = plugin;
|
|
1282
|
+
if (!config.generator) {
|
|
1283
|
+
throw new Error(
|
|
1284
|
+
`Plugin "${pluginName}" has dynamic_contracts entry but no generator specified`
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1287
|
+
const isFilePath = config.generator.startsWith("./") || config.generator.startsWith("/") || config.generator.includes(".ts") || config.generator.includes(".js");
|
|
1288
|
+
let generator;
|
|
1289
|
+
if (isFilePath) {
|
|
1290
|
+
let generatorPath;
|
|
1291
|
+
if (!isLocal) {
|
|
1292
|
+
try {
|
|
1293
|
+
generatorPath = require2.resolve(`${packageName}/${config.generator}`, {
|
|
1294
|
+
paths: [projectRoot]
|
|
1295
|
+
});
|
|
1296
|
+
} catch {
|
|
1297
|
+
generatorPath = path.join(pluginPath, config.generator);
|
|
1298
|
+
}
|
|
1299
|
+
} else {
|
|
1300
|
+
generatorPath = path.join(pluginPath, config.generator);
|
|
1301
|
+
}
|
|
1302
|
+
if (!fs.existsSync(generatorPath)) {
|
|
1303
|
+
const withTs = generatorPath + ".ts";
|
|
1304
|
+
const withJs = generatorPath + ".js";
|
|
1305
|
+
if (fs.existsSync(withTs)) {
|
|
1306
|
+
generatorPath = withTs;
|
|
1307
|
+
} else if (fs.existsSync(withJs)) {
|
|
1308
|
+
generatorPath = withJs;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
if (!fs.existsSync(generatorPath)) {
|
|
1312
|
+
throw new Error(
|
|
1313
|
+
`Generator file not found for plugin "${pluginName}": ${config.generator}`
|
|
1314
|
+
);
|
|
1315
|
+
}
|
|
1316
|
+
if (verbose) {
|
|
1317
|
+
getLogger().info(` Loading generator from file: ${generatorPath}`);
|
|
1318
|
+
}
|
|
1319
|
+
let generatorModule;
|
|
1320
|
+
if (viteServer) {
|
|
1321
|
+
generatorModule = await viteServer.ssrLoadModule(generatorPath);
|
|
1322
|
+
} else {
|
|
1323
|
+
generatorModule = await import(generatorPath);
|
|
1324
|
+
}
|
|
1325
|
+
generator = generatorModule.generator || generatorModule.default;
|
|
1326
|
+
} else {
|
|
1327
|
+
if (verbose) {
|
|
1328
|
+
getLogger().info(
|
|
1329
|
+
` Loading generator export: ${config.generator} from ${packageName}`
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
let pluginModule;
|
|
1333
|
+
if (viteServer) {
|
|
1334
|
+
pluginModule = await viteServer.ssrLoadModule(packageName);
|
|
1335
|
+
} else {
|
|
1336
|
+
pluginModule = await import(packageName);
|
|
1337
|
+
}
|
|
1338
|
+
generator = pluginModule[config.generator];
|
|
1339
|
+
if (!generator) {
|
|
1340
|
+
throw new Error(
|
|
1341
|
+
`Generator "${config.generator}" not exported from plugin "${pluginName}". Ensure it's exported from the package's index.ts`
|
|
1342
|
+
);
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
if (!generator || typeof generator.generate !== "function") {
|
|
1346
|
+
throw new Error(
|
|
1347
|
+
`Generator "${config.generator}" for plugin "${pluginName}" must have a 'generate' function. Use makeContractGenerator() to create valid generators.`
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1350
|
+
const resolvedServices = [];
|
|
1351
|
+
for (const marker of generator.services) {
|
|
1352
|
+
const service = services.get(marker);
|
|
1353
|
+
if (!service) {
|
|
1354
|
+
const markerName = marker.description ?? "unknown";
|
|
1355
|
+
throw new Error(
|
|
1356
|
+
`Service "${markerName}" required by ${pluginName} generator not found. Ensure it's registered in init.ts`
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
resolvedServices.push(service);
|
|
1360
|
+
}
|
|
1361
|
+
if (verbose) {
|
|
1362
|
+
getLogger().info(` Executing generator...`);
|
|
1363
|
+
}
|
|
1364
|
+
const result = await generator.generate(...resolvedServices);
|
|
1365
|
+
return result;
|
|
1366
|
+
}
|
|
1367
|
+
function resolveStaticContractPath(plugin, contractSpec, projectRoot) {
|
|
1368
|
+
const { pluginPath, isLocal, packageName } = plugin;
|
|
1369
|
+
if (!isLocal) {
|
|
1370
|
+
try {
|
|
1371
|
+
return require2.resolve(`${packageName}/${contractSpec}`, {
|
|
1372
|
+
paths: [projectRoot]
|
|
1373
|
+
});
|
|
1374
|
+
} catch {
|
|
1375
|
+
const possiblePaths = [
|
|
1376
|
+
path.join(pluginPath, "dist", contractSpec),
|
|
1377
|
+
path.join(pluginPath, "lib", contractSpec),
|
|
1378
|
+
path.join(pluginPath, contractSpec)
|
|
1379
|
+
];
|
|
1380
|
+
const found = possiblePaths.find((p) => fs.existsSync(p));
|
|
1381
|
+
return found || possiblePaths[0];
|
|
1382
|
+
}
|
|
1383
|
+
} else {
|
|
1384
|
+
return path.join(pluginPath, contractSpec);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
function toKebabCase(str) {
|
|
1388
|
+
return str.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "");
|
|
1389
|
+
}
|
|
1390
|
+
function getJayStackVersion() {
|
|
1391
|
+
try {
|
|
1392
|
+
const packageJsonPath = path.join(__dirname, "..", "package.json");
|
|
1393
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
1394
|
+
return packageJson.version || "0.0.0";
|
|
1395
|
+
} catch {
|
|
1396
|
+
return "0.0.0";
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
async function materializeContracts(options, services = /* @__PURE__ */ new Map()) {
|
|
1400
|
+
const {
|
|
1401
|
+
projectRoot,
|
|
1402
|
+
outputDir = path.join(projectRoot, "build", "materialized-contracts"),
|
|
1403
|
+
dynamicOnly = false,
|
|
1404
|
+
pluginFilter,
|
|
1405
|
+
verbose = false,
|
|
1406
|
+
viteServer
|
|
1407
|
+
} = options;
|
|
1408
|
+
const contracts = [];
|
|
1409
|
+
const pluginsIndexMap = /* @__PURE__ */ new Map();
|
|
1410
|
+
let staticCount = 0;
|
|
1411
|
+
let dynamicCount = 0;
|
|
1412
|
+
if (verbose) {
|
|
1413
|
+
getLogger().info("Scanning for plugins...");
|
|
1414
|
+
}
|
|
1415
|
+
const plugins = await scanPlugins({
|
|
1416
|
+
projectRoot,
|
|
1417
|
+
verbose,
|
|
1418
|
+
includeDevDeps: true
|
|
1419
|
+
// Include dev deps for contract discovery
|
|
1420
|
+
});
|
|
1421
|
+
if (verbose) {
|
|
1422
|
+
getLogger().info(`Found ${plugins.size} plugin(s)`);
|
|
1423
|
+
}
|
|
1424
|
+
for (const [pluginKey, plugin] of plugins) {
|
|
1425
|
+
if (pluginFilter && plugin.name !== pluginFilter && pluginKey !== pluginFilter)
|
|
1426
|
+
continue;
|
|
1427
|
+
if (verbose) {
|
|
1428
|
+
getLogger().info(`
|
|
1429
|
+
📦 Processing plugin: ${plugin.name}`);
|
|
1430
|
+
}
|
|
1431
|
+
const { manifest } = plugin;
|
|
1432
|
+
if (!dynamicOnly && manifest.contracts) {
|
|
1433
|
+
for (const contract of manifest.contracts) {
|
|
1434
|
+
const contractPath = resolveStaticContractPath(
|
|
1435
|
+
plugin,
|
|
1436
|
+
contract.contract,
|
|
1437
|
+
projectRoot
|
|
1438
|
+
);
|
|
1439
|
+
const relativePath = path.relative(projectRoot, contractPath);
|
|
1440
|
+
const entry = {
|
|
1441
|
+
plugin: plugin.name,
|
|
1442
|
+
name: contract.name,
|
|
1443
|
+
type: "static",
|
|
1444
|
+
path: "./" + relativePath
|
|
1445
|
+
};
|
|
1446
|
+
contracts.push(entry);
|
|
1447
|
+
staticCount++;
|
|
1448
|
+
const pluginRelPath = path.relative(projectRoot, plugin.pluginPath);
|
|
1449
|
+
if (!pluginsIndexMap.has(plugin.name)) {
|
|
1450
|
+
pluginsIndexMap.set(plugin.name, {
|
|
1451
|
+
path: "./" + pluginRelPath.replace(/\\/g, "/"),
|
|
1452
|
+
contracts: []
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
pluginsIndexMap.get(plugin.name).contracts.push({
|
|
1456
|
+
name: contract.name,
|
|
1457
|
+
type: "static",
|
|
1458
|
+
path: "./" + relativePath
|
|
1459
|
+
});
|
|
1460
|
+
if (verbose) {
|
|
1461
|
+
getLogger().info(` 📄 Static: ${contract.name}`);
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
if (manifest.dynamic_contracts) {
|
|
1466
|
+
const dynamicConfigs = Array.isArray(manifest.dynamic_contracts) ? manifest.dynamic_contracts : [manifest.dynamic_contracts];
|
|
1467
|
+
const pluginOutputDir = path.join(outputDir, plugin.name.replace(/[@/]/g, "_"));
|
|
1468
|
+
fs.mkdirSync(pluginOutputDir, { recursive: true });
|
|
1469
|
+
for (const config of dynamicConfigs) {
|
|
1470
|
+
if (verbose) {
|
|
1471
|
+
getLogger().info(` ⚡ Dynamic contracts (prefix: ${config.prefix})`);
|
|
1472
|
+
}
|
|
1473
|
+
try {
|
|
1474
|
+
const generatedContracts = await executeDynamicGenerator(
|
|
1475
|
+
plugin,
|
|
1476
|
+
config,
|
|
1477
|
+
projectRoot,
|
|
1478
|
+
services,
|
|
1479
|
+
verbose,
|
|
1480
|
+
viteServer
|
|
1481
|
+
);
|
|
1482
|
+
const prefix = config.prefix;
|
|
1483
|
+
for (const generated of generatedContracts) {
|
|
1484
|
+
const fullName = `${prefix}/${toKebabCase(generated.name)}`;
|
|
1485
|
+
const fileName = `${prefix}-${toKebabCase(generated.name)}.jay-contract`;
|
|
1486
|
+
const filePath = path.join(pluginOutputDir, fileName);
|
|
1487
|
+
fs.writeFileSync(filePath, generated.yaml, "utf-8");
|
|
1488
|
+
const relativePath = path.relative(projectRoot, filePath);
|
|
1489
|
+
const dynEntry = {
|
|
1490
|
+
plugin: plugin.name,
|
|
1491
|
+
name: fullName,
|
|
1492
|
+
type: "dynamic",
|
|
1493
|
+
path: "./" + relativePath,
|
|
1494
|
+
metadata: generated.metadata
|
|
1495
|
+
};
|
|
1496
|
+
contracts.push(dynEntry);
|
|
1497
|
+
dynamicCount++;
|
|
1498
|
+
const pluginRelPath = path.relative(projectRoot, plugin.pluginPath);
|
|
1499
|
+
if (!pluginsIndexMap.has(plugin.name)) {
|
|
1500
|
+
pluginsIndexMap.set(plugin.name, {
|
|
1501
|
+
path: "./" + pluginRelPath.replace(/\\/g, "/"),
|
|
1502
|
+
contracts: []
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
pluginsIndexMap.get(plugin.name).contracts.push({
|
|
1506
|
+
name: fullName,
|
|
1507
|
+
type: "dynamic",
|
|
1508
|
+
path: "./" + relativePath
|
|
1509
|
+
});
|
|
1510
|
+
if (verbose) {
|
|
1511
|
+
getLogger().info(` ⚡ Materialized: ${fullName}`);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
} catch (error) {
|
|
1515
|
+
getLogger().error(
|
|
1516
|
+
` ❌ Failed to materialize dynamic contracts for ${plugin.name} (${config.prefix}): ${error}`
|
|
1517
|
+
);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
const index = {
|
|
1523
|
+
materialized_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1524
|
+
jay_stack_version: getJayStackVersion(),
|
|
1525
|
+
contracts
|
|
1526
|
+
};
|
|
1527
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
1528
|
+
const indexPath = path.join(outputDir, "contracts-index.yaml");
|
|
1529
|
+
fs.writeFileSync(indexPath, YAML.stringify(index), "utf-8");
|
|
1530
|
+
const pluginsIndex = {
|
|
1531
|
+
materialized_at: index.materialized_at,
|
|
1532
|
+
jay_stack_version: index.jay_stack_version,
|
|
1533
|
+
plugins: Array.from(pluginsIndexMap.entries()).map(([name, data]) => ({
|
|
1534
|
+
name,
|
|
1535
|
+
path: data.path,
|
|
1536
|
+
contracts: data.contracts
|
|
1537
|
+
}))
|
|
1538
|
+
};
|
|
1539
|
+
const pluginsIndexPath = path.join(outputDir, "plugins-index.yaml");
|
|
1540
|
+
fs.writeFileSync(pluginsIndexPath, YAML.stringify(pluginsIndex), "utf-8");
|
|
1541
|
+
if (verbose) {
|
|
1542
|
+
getLogger().info(`
|
|
1543
|
+
✅ Contracts index written to: ${indexPath}`);
|
|
1544
|
+
getLogger().info(`✅ Plugins index written to: ${pluginsIndexPath}`);
|
|
1545
|
+
}
|
|
1546
|
+
return {
|
|
1547
|
+
index,
|
|
1548
|
+
staticCount,
|
|
1549
|
+
dynamicCount,
|
|
1550
|
+
outputDir
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
async function listContracts(options) {
|
|
1554
|
+
const { projectRoot, dynamicOnly = false, pluginFilter } = options;
|
|
1555
|
+
const contracts = [];
|
|
1556
|
+
const plugins = await scanPlugins({
|
|
1557
|
+
projectRoot,
|
|
1558
|
+
includeDevDeps: true
|
|
1559
|
+
});
|
|
1560
|
+
for (const [pluginKey, plugin] of plugins) {
|
|
1561
|
+
if (pluginFilter && plugin.name !== pluginFilter && pluginKey !== pluginFilter)
|
|
1562
|
+
continue;
|
|
1563
|
+
const { manifest } = plugin;
|
|
1564
|
+
if (!dynamicOnly && manifest.contracts) {
|
|
1565
|
+
for (const contract of manifest.contracts) {
|
|
1566
|
+
const contractPath = resolveStaticContractPath(
|
|
1567
|
+
plugin,
|
|
1568
|
+
contract.contract,
|
|
1569
|
+
projectRoot
|
|
1570
|
+
);
|
|
1571
|
+
const relativePath = path.relative(projectRoot, contractPath);
|
|
1572
|
+
contracts.push({
|
|
1573
|
+
plugin: plugin.name,
|
|
1574
|
+
name: contract.name,
|
|
1575
|
+
type: "static",
|
|
1576
|
+
path: "./" + relativePath
|
|
1577
|
+
});
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
if (manifest.dynamic_contracts) {
|
|
1581
|
+
const dynamicConfigs = Array.isArray(manifest.dynamic_contracts) ? manifest.dynamic_contracts : [manifest.dynamic_contracts];
|
|
1582
|
+
for (const config of dynamicConfigs) {
|
|
1583
|
+
contracts.push({
|
|
1584
|
+
plugin: plugin.name,
|
|
1585
|
+
name: `${config.prefix}/*`,
|
|
1586
|
+
type: "dynamic",
|
|
1587
|
+
path: "(run materialization to generate)"
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
return {
|
|
1593
|
+
materialized_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1594
|
+
jay_stack_version: getJayStackVersion(),
|
|
1595
|
+
contracts
|
|
1596
|
+
};
|
|
1597
|
+
}
|
|
1598
|
+
async function discoverPluginsWithSetup(options) {
|
|
1599
|
+
const { projectRoot, verbose, pluginFilter } = options;
|
|
1600
|
+
const allPlugins = await scanPlugins({
|
|
1601
|
+
projectRoot,
|
|
1602
|
+
verbose,
|
|
1603
|
+
discoverTransitive: true
|
|
1604
|
+
});
|
|
1605
|
+
const pluginsWithSetup = [];
|
|
1606
|
+
for (const [packageName, plugin] of allPlugins) {
|
|
1607
|
+
if (!plugin.manifest.setup?.handler)
|
|
1608
|
+
continue;
|
|
1609
|
+
if (pluginFilter && plugin.name !== pluginFilter && packageName !== pluginFilter) {
|
|
1610
|
+
continue;
|
|
1611
|
+
}
|
|
1612
|
+
pluginsWithSetup.push({
|
|
1613
|
+
name: plugin.name,
|
|
1614
|
+
pluginPath: plugin.pluginPath,
|
|
1615
|
+
packageName: plugin.packageName,
|
|
1616
|
+
isLocal: plugin.isLocal,
|
|
1617
|
+
setupHandler: plugin.manifest.setup.handler,
|
|
1618
|
+
setupDescription: plugin.manifest.setup.description,
|
|
1619
|
+
dependencies: plugin.dependencies
|
|
1620
|
+
});
|
|
1621
|
+
if (verbose) {
|
|
1622
|
+
getLogger().info(`[Setup] Found plugin with setup: ${plugin.name}`);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
return sortByDependencies(pluginsWithSetup);
|
|
1626
|
+
}
|
|
1627
|
+
async function discoverPluginsWithReferences(options) {
|
|
1628
|
+
const { projectRoot, verbose, pluginFilter } = options;
|
|
1629
|
+
const allPlugins = await scanPlugins({
|
|
1630
|
+
projectRoot,
|
|
1631
|
+
verbose,
|
|
1632
|
+
discoverTransitive: true
|
|
1633
|
+
});
|
|
1634
|
+
const pluginsWithRefs = [];
|
|
1635
|
+
for (const [packageName, plugin] of allPlugins) {
|
|
1636
|
+
if (!plugin.manifest.setup?.references)
|
|
1637
|
+
continue;
|
|
1638
|
+
if (pluginFilter && plugin.name !== pluginFilter && packageName !== pluginFilter) {
|
|
1639
|
+
continue;
|
|
1640
|
+
}
|
|
1641
|
+
pluginsWithRefs.push({
|
|
1642
|
+
name: plugin.name,
|
|
1643
|
+
pluginPath: plugin.pluginPath,
|
|
1644
|
+
packageName: plugin.packageName,
|
|
1645
|
+
isLocal: plugin.isLocal,
|
|
1646
|
+
referencesHandler: plugin.manifest.setup.references,
|
|
1647
|
+
dependencies: plugin.dependencies
|
|
1648
|
+
});
|
|
1649
|
+
if (verbose) {
|
|
1650
|
+
getLogger().info(`[AgentKit] Found plugin with references: ${plugin.name}`);
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
return sortByDependencies(pluginsWithRefs);
|
|
1654
|
+
}
|
|
1655
|
+
function sortByDependencies(plugins) {
|
|
1656
|
+
return [...plugins].sort((a, b) => {
|
|
1657
|
+
const aDepsOnB = a.dependencies.some((d) => d === b.name || d === b.packageName) ? 1 : 0;
|
|
1658
|
+
const bDepsOnA = b.dependencies.some((d) => d === a.name || d === a.packageName) ? 1 : 0;
|
|
1659
|
+
return aDepsOnB - bDepsOnA;
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
async function executePluginSetup(plugin, options) {
|
|
1663
|
+
const { projectRoot, configDir, force, initError, viteServer, verbose } = options;
|
|
1664
|
+
const context = {
|
|
1665
|
+
pluginName: plugin.name,
|
|
1666
|
+
projectRoot,
|
|
1667
|
+
configDir,
|
|
1668
|
+
services: getServiceRegistry(),
|
|
1669
|
+
initError,
|
|
1670
|
+
force
|
|
1671
|
+
};
|
|
1672
|
+
const handler = await loadHandler(plugin, plugin.setupHandler, viteServer);
|
|
1673
|
+
return handler(context);
|
|
1674
|
+
}
|
|
1675
|
+
async function executePluginReferences(plugin, options) {
|
|
1676
|
+
const { projectRoot, force, viteServer } = options;
|
|
1677
|
+
const referencesDir = path.join(projectRoot, "agent-kit", "references", plugin.name);
|
|
1678
|
+
const context = {
|
|
1679
|
+
pluginName: plugin.name,
|
|
1680
|
+
projectRoot,
|
|
1681
|
+
referencesDir,
|
|
1682
|
+
services: getServiceRegistry(),
|
|
1683
|
+
force
|
|
1684
|
+
};
|
|
1685
|
+
const handler = await loadHandler(
|
|
1686
|
+
plugin,
|
|
1687
|
+
plugin.referencesHandler,
|
|
1688
|
+
viteServer
|
|
1689
|
+
);
|
|
1690
|
+
return handler(context);
|
|
1691
|
+
}
|
|
1692
|
+
async function loadHandler(plugin, handlerName, viteServer) {
|
|
1693
|
+
let module;
|
|
1694
|
+
if (plugin.isLocal) {
|
|
1695
|
+
const handlerPath = path.resolve(plugin.pluginPath, handlerName);
|
|
1696
|
+
if (viteServer) {
|
|
1697
|
+
module = await viteServer.ssrLoadModule(handlerPath);
|
|
1698
|
+
} else {
|
|
1699
|
+
module = await import(handlerPath);
|
|
1700
|
+
}
|
|
1701
|
+
if (typeof module[handlerName] === "function")
|
|
1702
|
+
return module[handlerName];
|
|
1703
|
+
if (typeof module.setup === "function")
|
|
1704
|
+
return module.setup;
|
|
1705
|
+
if (typeof module.default === "function")
|
|
1706
|
+
return module.default;
|
|
1707
|
+
throw new Error(
|
|
1708
|
+
`Handler "${handlerName}" not found in "${plugin.pluginPath}". Available exports: ${Object.keys(module).join(", ")}`
|
|
1709
|
+
);
|
|
1710
|
+
} else {
|
|
1711
|
+
if (viteServer) {
|
|
1712
|
+
module = await viteServer.ssrLoadModule(plugin.packageName);
|
|
1713
|
+
} else {
|
|
1714
|
+
module = await import(plugin.packageName);
|
|
1715
|
+
}
|
|
1716
|
+
if (typeof module[handlerName] !== "function") {
|
|
1717
|
+
throw new Error(
|
|
1718
|
+
`Handler "${handlerName}" not found as export in "${plugin.packageName}". Available exports: ${Object.keys(module).join(", ")}`
|
|
1719
|
+
);
|
|
1720
|
+
}
|
|
1721
|
+
return module[handlerName];
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1149
1724
|
export {
|
|
1150
1725
|
ActionRegistry,
|
|
1151
1726
|
DevSlowlyChangingPhase,
|
|
@@ -1159,8 +1734,12 @@ export {
|
|
|
1159
1734
|
discoverAndRegisterActions,
|
|
1160
1735
|
discoverPluginActions,
|
|
1161
1736
|
discoverPluginsWithInit,
|
|
1737
|
+
discoverPluginsWithReferences,
|
|
1738
|
+
discoverPluginsWithSetup,
|
|
1162
1739
|
executeAction,
|
|
1740
|
+
executePluginReferences,
|
|
1163
1741
|
executePluginServerInits,
|
|
1742
|
+
executePluginSetup,
|
|
1164
1743
|
generateClientScript,
|
|
1165
1744
|
getActionCacheHeaders,
|
|
1166
1745
|
getClientInitData,
|
|
@@ -1168,9 +1747,12 @@ export {
|
|
|
1168
1747
|
getRegisteredAction,
|
|
1169
1748
|
getRegisteredActionNames,
|
|
1170
1749
|
getService,
|
|
1750
|
+
getServiceRegistry,
|
|
1171
1751
|
hasAction,
|
|
1172
1752
|
hasService,
|
|
1753
|
+
listContracts,
|
|
1173
1754
|
loadPageParts,
|
|
1755
|
+
materializeContracts,
|
|
1174
1756
|
onInit,
|
|
1175
1757
|
onShutdown,
|
|
1176
1758
|
preparePluginClientInits,
|
|
@@ -1178,11 +1760,12 @@ export {
|
|
|
1178
1760
|
registerService,
|
|
1179
1761
|
renderFastChangingData,
|
|
1180
1762
|
resolveServices,
|
|
1181
|
-
runAction,
|
|
1182
1763
|
runInitCallbacks,
|
|
1183
1764
|
runLoadParams,
|
|
1184
1765
|
runShutdownCallbacks,
|
|
1185
1766
|
runSlowlyChangingRender,
|
|
1767
|
+
scanPlugins,
|
|
1186
1768
|
setClientInitData,
|
|
1769
|
+
slowRenderInstances,
|
|
1187
1770
|
sortPluginsByDependencies
|
|
1188
1771
|
};
|