@jay-framework/stack-server-runtime 0.8.0 → 0.10.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.
Files changed (3) hide show
  1. package/dist/index.d.ts +410 -6
  2. package/dist/index.js +800 -25
  3. package/package.json +11 -9
package/dist/index.js CHANGED
@@ -1,8 +1,22 @@
1
- import { notFound, partialRender } from "@jay-framework/fullstack-component";
2
- import fs from "node:fs/promises";
3
- import path from "node:path";
4
- import { parseJayFile } from "@jay-framework/compiler-jay-html";
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => {
4
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
+ return value;
6
+ };
7
+ import { notFound, partialRender, phaseOutput, isJayAction } from "@jay-framework/fullstack-component";
8
+ import fs$1 from "node:fs/promises";
9
+ import * as path from "node:path";
10
+ import path__default from "node:path";
11
+ import { parseJayFile, JAY_IMPORT_RESOLVER } from "@jay-framework/compiler-jay-html";
5
12
  import { createRequire } from "module";
13
+ import * as fs from "node:fs";
14
+ import { createRequire as createRequire$1 } from "node:module";
15
+ import "prettier";
16
+ import "js-beautify";
17
+ import fs$2 from "fs";
18
+ import path$1 from "path";
19
+ import YAML from "yaml";
6
20
  const serviceRegistry = /* @__PURE__ */ new Map();
7
21
  function registerService(marker, service) {
8
22
  serviceRegistry.set(marker, service);
@@ -50,6 +64,19 @@ function clearLifecycleCallbacks() {
50
64
  initCallbacks.length = 0;
51
65
  shutdownCallbacks.length = 0;
52
66
  }
67
+ let clientInitData = {};
68
+ function setClientInitData(key, data) {
69
+ clientInitData[key] = { ...clientInitData[key] || {}, ...data };
70
+ }
71
+ function getClientInitData() {
72
+ return clientInitData;
73
+ }
74
+ function getClientInitDataForKey(key) {
75
+ return clientInitData[key] || {};
76
+ }
77
+ function clearClientInitData() {
78
+ clientInitData = {};
79
+ }
53
80
  function isLeftSideParamsSubsetOfRightSideParams(left, right) {
54
81
  return Object.keys(left).reduce((prev, curr) => prev && left[curr] === right[curr], true);
55
82
  }
@@ -84,7 +111,7 @@ class DevSlowlyChangingPhase {
84
111
  { ...pageProps, ...pageParams },
85
112
  ...services
86
113
  );
87
- if (slowlyRenderedPart.kind === "PartialRender") {
114
+ if (slowlyRenderedPart.kind === "PhaseOutput") {
88
115
  if (!key) {
89
116
  slowlyViewState = { ...slowlyViewState, ...slowlyRenderedPart.rendered };
90
117
  carryForward = { ...carryForward, ...slowlyRenderedPart.carryForward };
@@ -117,7 +144,7 @@ async function renderFastChangingData(pageParams, pageProps, carryForward, parts
117
144
  partSlowlyCarryForward,
118
145
  ...services
119
146
  );
120
- if (fastRenderedPart.kind === "PartialRender") {
147
+ if (fastRenderedPart.kind === "PhaseOutput") {
121
148
  if (!key) {
122
149
  fastViewState = { ...fastViewState, ...fastRenderedPart.rendered };
123
150
  fastCarryForward = { ...fastCarryForward, ...fastRenderedPart.carryForward };
@@ -129,13 +156,37 @@ async function renderFastChangingData(pageParams, pageProps, carryForward, parts
129
156
  return fastRenderedPart;
130
157
  }
131
158
  }
132
- return Promise.resolve(partialRender(fastViewState, fastCarryForward));
159
+ return Promise.resolve(phaseOutput(fastViewState, fastCarryForward));
133
160
  }
134
- function generateClientScript(defaultViewState, fastCarryForward, parts, jayHtmlPath) {
161
+ function generateClientScript(defaultViewState, fastCarryForward, parts, jayHtmlPath, trackByMap = {}, clientInitData2 = {}, projectInit, pluginInits = []) {
135
162
  const imports = parts.length > 0 ? parts.map((part) => part.clientImport).join("\n") + "\n" : "";
136
163
  const compositeParts = parts.length > 0 ? `[
137
164
  ${parts.map((part) => " " + part.clientPart).join(",\n")}
138
165
  ]` : "[]";
166
+ const hasClientInit = projectInit || pluginInits.length > 0;
167
+ const pluginClientInitImports = pluginInits.map((plugin, idx) => {
168
+ return `import { ${plugin.initExport} as jayInit${idx} } from "${plugin.importPath}";`;
169
+ }).join("\n ");
170
+ const projectInitImport = projectInit ? `import { ${projectInit.initExport || "init"} as projectJayInit } from "${projectInit.importPath}";` : "";
171
+ const pluginClientInitCalls = pluginInits.map((plugin, idx) => {
172
+ const pluginData = clientInitData2[plugin.name] || {};
173
+ return `if (typeof jayInit${idx}._clientInit === 'function') {
174
+ console.log('[DevServer] Running client init: ${plugin.name}');
175
+ await jayInit${idx}._clientInit(${JSON.stringify(pluginData)});
176
+ }`;
177
+ }).join("\n ");
178
+ const projectInitCall = projectInit ? `if (typeof projectJayInit._clientInit === 'function') {
179
+ console.log('[DevServer] Running client init: project');
180
+ const projectData = ${JSON.stringify(clientInitData2["project"] || {})};
181
+ await projectJayInit._clientInit(projectData);
182
+ }` : "";
183
+ const clientInitExecution = hasClientInit ? `
184
+ // Plugin client initialization (in dependency order)
185
+ ${pluginClientInitCalls}
186
+
187
+ // Project client initialization
188
+ ${projectInitCall}
189
+ ` : "";
139
190
  return `<!doctype html>
140
191
  <html lang="en">
141
192
  <head>
@@ -147,13 +198,16 @@ ${parts.map((part) => " " + part.clientPart).join(",\n")}
147
198
  <div id="target"></div>
148
199
  <script type="module">
149
200
  import {makeCompositeJayComponent} from "@jay-framework/stack-client-runtime";
201
+ ${pluginClientInitImports}
202
+ ${projectInitImport}
150
203
  import { render } from '${jayHtmlPath}';
151
204
  ${imports}
152
205
  const viewState = ${JSON.stringify(defaultViewState)};
153
206
  const fastCarryForward = ${JSON.stringify(fastCarryForward)};
154
-
207
+ const trackByMap = ${JSON.stringify(trackByMap)};
208
+ ${clientInitExecution}
155
209
  const target = document.getElementById('target');
156
- const pageComp = makeCompositeJayComponent(render, viewState, fastCarryForward, ${compositeParts})
210
+ const pageComp = makeCompositeJayComponent(render, viewState, fastCarryForward, ${compositeParts}, trackByMap)
157
211
 
158
212
  const instance = pageComp({...viewState, ...fastCarryForward})
159
213
  target.appendChild(instance.element.dom);
@@ -161,23 +215,22 @@ ${parts.map((part) => " " + part.clientPart).join(",\n")}
161
215
  </body>
162
216
  </html>`;
163
217
  }
164
- const require2 = createRequire(import.meta.url);
165
- async function loadPageParts(vite, route, pagesBase, jayRollupConfig) {
166
- const exists = await fs.access(route.compPath, fs.constants.F_OK).then(() => true).catch(() => false);
218
+ const require$2 = createRequire(import.meta.url);
219
+ async function loadPageParts(vite, route, pagesBase, projectBase, jayRollupConfig) {
220
+ const exists = await fs$1.access(route.compPath, fs$1.constants.F_OK).then(() => true).catch(() => false);
167
221
  const parts = [];
168
222
  if (exists) {
169
223
  const pageComponent = (await vite.ssrLoadModule(route.compPath)).page;
170
224
  parts.push({
171
225
  compDefinition: pageComponent,
226
+ // Client import uses client-only code (server code stripped)
172
227
  clientImport: `import {page} from '${route.compPath}'`,
173
228
  clientPart: `{comp: page.comp, contextMarkers: []}`
174
229
  });
175
230
  }
176
- const jayHtmlSource = (await fs.readFile(route.jayHtmlPath)).toString();
177
- const fileName = path.basename(route.jayHtmlPath);
178
- const dirName = path.dirname(route.jayHtmlPath);
179
- const module = await import("@jay-framework/compiler-jay-html");
180
- const JAY_IMPORT_RESOLVER = module.JAY_IMPORT_RESOLVER;
231
+ const jayHtmlSource = (await fs$1.readFile(route.jayHtmlPath)).toString();
232
+ const fileName = path__default.basename(route.jayHtmlPath);
233
+ const dirName = path__default.dirname(route.jayHtmlPath);
181
234
  const jayHtmlWithValidations = await parseJayFile(
182
235
  jayHtmlSource,
183
236
  fileName,
@@ -185,42 +238,764 @@ async function loadPageParts(vite, route, pagesBase, jayRollupConfig) {
185
238
  {
186
239
  relativePath: jayRollupConfig.tsConfigFilePath
187
240
  },
188
- JAY_IMPORT_RESOLVER
241
+ JAY_IMPORT_RESOLVER,
242
+ projectBase
189
243
  );
190
244
  return jayHtmlWithValidations.mapAsync(async (jayHtml) => {
245
+ const usedPackages = /* @__PURE__ */ new Set();
191
246
  for await (const headlessImport of jayHtml.headlessImports) {
192
- const module2 = headlessImport.codeLink.module;
247
+ const module = headlessImport.codeLink.module;
193
248
  const name = headlessImport.codeLink.names[0].name;
194
- const modulePath = module2[0] === "." ? path.resolve(dirName, module2) : require2.resolve(module2, { paths: require2.resolve.paths(dirName) });
249
+ const isLocalModule = module[0] === "." || module[0] === "/";
250
+ const modulePath = isLocalModule ? path__default.resolve(dirName, module) : require$2.resolve(module, { paths: require$2.resolve.paths(dirName) });
195
251
  const compDefinition = (await vite.ssrLoadModule(modulePath))[name];
196
- const moduleImport = module2.startsWith("./") ? path.resolve(pagesBase, module2) : module2;
252
+ const moduleImport = isLocalModule ? path__default.resolve(dirName, module) : module;
253
+ const isNpmPackage = !isLocalModule;
254
+ const clientModuleImport = isNpmPackage ? `${moduleImport}/client` : `${moduleImport}`;
255
+ if (isNpmPackage) {
256
+ const packageName = module.startsWith("@") ? module.split("/").slice(0, 2).join("/") : module.split("/")[0];
257
+ usedPackages.add(packageName);
258
+ }
197
259
  const key = headlessImport.key;
198
260
  const part = {
199
261
  key,
200
262
  compDefinition,
201
- clientImport: `import {${name}} from '${moduleImport}'`,
263
+ clientImport: `import {${name}} from '${clientModuleImport}'`,
202
264
  clientPart: `{comp: ${name}.comp, contextMarkers: [], key: '${headlessImport.key}'}`
203
265
  };
204
266
  parts.push(part);
205
267
  }
206
- return parts;
268
+ return {
269
+ parts,
270
+ serverTrackByMap: jayHtml.serverTrackByMap,
271
+ clientTrackByMap: jayHtml.clientTrackByMap,
272
+ usedPackages
273
+ };
274
+ });
275
+ }
276
+ class ActionRegistry {
277
+ constructor() {
278
+ __publicField(this, "actions", /* @__PURE__ */ new Map());
279
+ }
280
+ /**
281
+ * Registers an action with the registry.
282
+ *
283
+ * @param action - The JayAction to register (created via makeJayAction/makeJayQuery)
284
+ */
285
+ register(action) {
286
+ const entry = {
287
+ actionName: action.actionName,
288
+ method: action.method,
289
+ cacheOptions: action.cacheOptions,
290
+ services: action.services,
291
+ handler: action.handler
292
+ };
293
+ this.actions.set(action.actionName, entry);
294
+ }
295
+ /**
296
+ * Retrieves a registered action by name.
297
+ *
298
+ * @param actionName - The unique action name
299
+ * @returns The registered action or undefined
300
+ */
301
+ get(actionName) {
302
+ return this.actions.get(actionName);
303
+ }
304
+ /**
305
+ * Checks if an action is registered.
306
+ *
307
+ * @param actionName - The unique action name
308
+ * @returns true if the action is registered
309
+ */
310
+ has(actionName) {
311
+ return this.actions.has(actionName);
312
+ }
313
+ /**
314
+ * Gets all registered action names.
315
+ *
316
+ * @returns Array of registered action names
317
+ */
318
+ getNames() {
319
+ return Array.from(this.actions.keys());
320
+ }
321
+ /**
322
+ * Clears all registered actions.
323
+ */
324
+ clear() {
325
+ this.actions.clear();
326
+ }
327
+ /**
328
+ * Executes a registered action with the given input.
329
+ * Resolves services and calls the handler.
330
+ *
331
+ * @param actionName - The action to execute
332
+ * @param input - The input data for the action
333
+ * @returns The action result or error
334
+ */
335
+ async execute(actionName, input) {
336
+ const action = this.actions.get(actionName);
337
+ if (!action) {
338
+ return {
339
+ success: false,
340
+ error: {
341
+ code: "ACTION_NOT_FOUND",
342
+ message: `Action '${actionName}' is not registered`,
343
+ isActionError: false
344
+ }
345
+ };
346
+ }
347
+ try {
348
+ const services = resolveServices(action.services);
349
+ const result = await action.handler(input, ...services);
350
+ return {
351
+ success: true,
352
+ data: result
353
+ };
354
+ } catch (error) {
355
+ if (error && typeof error === "object" && "code" in error && "message" in error) {
356
+ const actionError = error;
357
+ if (actionError.name === "ActionError") {
358
+ return {
359
+ success: false,
360
+ error: {
361
+ code: actionError.code,
362
+ message: actionError.message,
363
+ isActionError: true
364
+ }
365
+ };
366
+ }
367
+ }
368
+ const message = error instanceof Error ? error.message : "Unknown error occurred";
369
+ return {
370
+ success: false,
371
+ error: {
372
+ code: "INTERNAL_ERROR",
373
+ message,
374
+ isActionError: false
375
+ }
376
+ };
377
+ }
378
+ }
379
+ /**
380
+ * Gets the cache headers for an action (if applicable).
381
+ *
382
+ * @param actionName - The action name
383
+ * @returns Cache-Control header value or undefined
384
+ */
385
+ getCacheHeaders(actionName) {
386
+ const action = this.actions.get(actionName);
387
+ if (!action || action.method !== "GET" || !action.cacheOptions) {
388
+ return void 0;
389
+ }
390
+ const { maxAge, staleWhileRevalidate } = action.cacheOptions;
391
+ const parts = [];
392
+ if (maxAge !== void 0) {
393
+ parts.push(`max-age=${maxAge}`);
394
+ }
395
+ if (staleWhileRevalidate !== void 0) {
396
+ parts.push(`stale-while-revalidate=${staleWhileRevalidate}`);
397
+ }
398
+ return parts.length > 0 ? parts.join(", ") : void 0;
399
+ }
400
+ }
401
+ const actionRegistry = new ActionRegistry();
402
+ function registerAction(action) {
403
+ actionRegistry.register(action);
404
+ }
405
+ function getRegisteredAction(actionName) {
406
+ return actionRegistry.get(actionName);
407
+ }
408
+ function hasAction(actionName) {
409
+ return actionRegistry.has(actionName);
410
+ }
411
+ function getRegisteredActionNames() {
412
+ return actionRegistry.getNames();
413
+ }
414
+ function clearActionRegistry() {
415
+ actionRegistry.clear();
416
+ }
417
+ async function executeAction(actionName, input) {
418
+ return actionRegistry.execute(actionName, input);
419
+ }
420
+ function getActionCacheHeaders(actionName) {
421
+ return actionRegistry.getCacheHeaders(actionName);
422
+ }
423
+ var __defProp2 = Object.defineProperty;
424
+ var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
425
+ var __publicField2 = (obj, key, value) => {
426
+ __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
427
+ return value;
428
+ };
429
+ class JayAtomicType {
430
+ constructor(name) {
431
+ __publicField2(this, "kind", 0);
432
+ this.name = name;
433
+ }
434
+ }
435
+ new JayAtomicType("string");
436
+ new JayAtomicType("number");
437
+ new JayAtomicType("boolean");
438
+ new JayAtomicType("Date");
439
+ new JayAtomicType("Unknown");
440
+ class JayObjectType {
441
+ constructor(name, props) {
442
+ __publicField2(this, "kind", 8);
443
+ this.name = name;
444
+ this.props = props;
445
+ }
446
+ }
447
+ new JayObjectType("Error", {
448
+ message: new JayAtomicType("string"),
449
+ name: new JayAtomicType("string"),
450
+ stack: new JayAtomicType("string")
451
+ });
452
+ var RuntimeMode = /* @__PURE__ */ ((RuntimeMode2) => {
453
+ RuntimeMode2["MainTrusted"] = "mainTrusted";
454
+ RuntimeMode2["MainSandbox"] = "mainSandbox";
455
+ RuntimeMode2["WorkerTrusted"] = "workerTrusted";
456
+ RuntimeMode2["WorkerSandbox"] = "workerSandbox";
457
+ return RuntimeMode2;
458
+ })(RuntimeMode || {});
459
+ const TS_EXTENSION = ".ts";
460
+ const JAY_QUERY_PREFIX = "?jay-";
461
+ [
462
+ // Build environments
463
+ {
464
+ pattern: `${JAY_QUERY_PREFIX}${"client"}`,
465
+ buildEnv: "client"
466
+ /* Client */
467
+ },
468
+ {
469
+ pattern: `${JAY_QUERY_PREFIX}${"server"}`,
470
+ buildEnv: "server"
471
+ /* Server */
472
+ },
473
+ // Runtime modes (with .ts suffix)
474
+ {
475
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.MainSandbox}${TS_EXTENSION}`,
476
+ runtimeMode: RuntimeMode.MainSandbox
477
+ },
478
+ {
479
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.WorkerTrusted}${TS_EXTENSION}`,
480
+ runtimeMode: RuntimeMode.WorkerTrusted
481
+ },
482
+ {
483
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.WorkerSandbox}${TS_EXTENSION}`,
484
+ runtimeMode: RuntimeMode.WorkerSandbox
485
+ },
486
+ // Runtime modes (without .ts suffix)
487
+ {
488
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.MainSandbox}`,
489
+ runtimeMode: RuntimeMode.MainSandbox
490
+ },
491
+ {
492
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.WorkerTrusted}`,
493
+ runtimeMode: RuntimeMode.WorkerTrusted
494
+ },
495
+ {
496
+ pattern: `${JAY_QUERY_PREFIX}${RuntimeMode.WorkerSandbox}`,
497
+ runtimeMode: RuntimeMode.WorkerSandbox
498
+ }
499
+ ];
500
+ createRequire(import.meta.url);
501
+ function loadPluginManifest(pluginDir) {
502
+ const pluginYamlPath = path$1.join(pluginDir, "plugin.yaml");
503
+ if (!fs$2.existsSync(pluginYamlPath)) {
504
+ return null;
505
+ }
506
+ try {
507
+ const yamlContent = fs$2.readFileSync(pluginYamlPath, "utf-8");
508
+ return YAML.parse(yamlContent);
509
+ } catch (error) {
510
+ return null;
511
+ }
512
+ }
513
+ const s = createRequire(import.meta.url), e = s("typescript");
514
+ new Proxy(e, {
515
+ get(t, r) {
516
+ return t[r];
517
+ }
518
+ });
519
+ const require$1 = createRequire$1(import.meta.url);
520
+ async function discoverAndRegisterActions(options) {
521
+ const {
522
+ projectRoot,
523
+ actionsDir = "src/actions",
524
+ registry = actionRegistry,
525
+ verbose = false,
526
+ viteServer
527
+ } = options;
528
+ const result = {
529
+ actionCount: 0,
530
+ actionNames: [],
531
+ scannedFiles: []
532
+ };
533
+ const actionsPath = path.resolve(projectRoot, actionsDir);
534
+ if (!fs.existsSync(actionsPath)) {
535
+ if (verbose) {
536
+ console.log(`[Actions] No actions directory found at ${actionsPath}`);
537
+ }
538
+ return result;
539
+ }
540
+ const actionFiles = await findActionFiles(actionsPath);
541
+ if (verbose) {
542
+ console.log(`[Actions] Found ${actionFiles.length} action file(s)`);
543
+ }
544
+ for (const filePath of actionFiles) {
545
+ result.scannedFiles.push(filePath);
546
+ try {
547
+ let module;
548
+ if (viteServer) {
549
+ module = await viteServer.ssrLoadModule(filePath);
550
+ } else {
551
+ module = await import(filePath);
552
+ }
553
+ for (const [exportName, exportValue] of Object.entries(module)) {
554
+ if (isJayAction(exportValue)) {
555
+ registry.register(exportValue);
556
+ result.actionNames.push(exportValue.actionName);
557
+ result.actionCount++;
558
+ if (verbose) {
559
+ console.log(`[Actions] Registered: ${exportValue.actionName}`);
560
+ }
561
+ }
562
+ }
563
+ } catch (error) {
564
+ console.error(`[Actions] Failed to import ${filePath}:`, error);
565
+ }
566
+ }
567
+ return result;
568
+ }
569
+ async function findActionFiles(dir) {
570
+ const files = [];
571
+ const entries = await fs.promises.readdir(dir, { withFileTypes: true });
572
+ for (const entry of entries) {
573
+ const fullPath = path.join(dir, entry.name);
574
+ if (entry.isDirectory()) {
575
+ const subFiles = await findActionFiles(fullPath);
576
+ files.push(...subFiles);
577
+ } else if (entry.isFile() && entry.name.endsWith(".actions.ts")) {
578
+ files.push(fullPath);
579
+ }
580
+ }
581
+ return files;
582
+ }
583
+ async function discoverAllPluginActions(options) {
584
+ const { projectRoot, registry = actionRegistry, verbose = false, viteServer } = options;
585
+ const allActions = [];
586
+ const localPluginsPath = path.join(projectRoot, "src/plugins");
587
+ if (fs.existsSync(localPluginsPath)) {
588
+ const pluginDirs = await fs.promises.readdir(localPluginsPath, { withFileTypes: true });
589
+ for (const entry of pluginDirs) {
590
+ if (entry.isDirectory()) {
591
+ const pluginPath = path.join(localPluginsPath, entry.name);
592
+ const actions = await discoverPluginActions(
593
+ pluginPath,
594
+ projectRoot,
595
+ registry,
596
+ verbose,
597
+ viteServer
598
+ );
599
+ allActions.push(...actions);
600
+ }
601
+ }
602
+ }
603
+ const npmActions = await discoverNpmPluginActions(projectRoot, registry, verbose, viteServer);
604
+ allActions.push(...npmActions);
605
+ return allActions;
606
+ }
607
+ async function discoverNpmPluginActions(projectRoot, registry, verbose, viteServer) {
608
+ const allActions = [];
609
+ const packageJsonPath = path.join(projectRoot, "package.json");
610
+ if (!fs.existsSync(packageJsonPath)) {
611
+ return allActions;
612
+ }
613
+ try {
614
+ const packageJson = JSON.parse(await fs.promises.readFile(packageJsonPath, "utf-8"));
615
+ const dependencies = {
616
+ ...packageJson.dependencies,
617
+ ...packageJson.devDependencies
618
+ };
619
+ for (const packageName of Object.keys(dependencies)) {
620
+ try {
621
+ const pluginYamlPath = tryResolvePluginYaml(packageName, projectRoot);
622
+ if (!pluginYamlPath) {
623
+ continue;
624
+ }
625
+ const pluginDir = path.dirname(pluginYamlPath);
626
+ const pluginConfig = loadPluginManifest(pluginDir);
627
+ if (!pluginConfig || !pluginConfig.actions || !Array.isArray(pluginConfig.actions)) {
628
+ continue;
629
+ }
630
+ if (verbose) {
631
+ console.log(
632
+ `[Actions] NPM plugin "${packageName}" declares actions:`,
633
+ pluginConfig.actions
634
+ );
635
+ }
636
+ const actions = await registerNpmPluginActions(
637
+ packageName,
638
+ pluginConfig,
639
+ registry,
640
+ verbose,
641
+ viteServer
642
+ );
643
+ allActions.push(...actions);
644
+ } catch {
645
+ continue;
646
+ }
647
+ }
648
+ } catch (error) {
649
+ console.error("[Actions] Failed to read project package.json:", error);
650
+ }
651
+ return allActions;
652
+ }
653
+ function tryResolvePluginYaml(packageName, projectRoot) {
654
+ try {
655
+ return require$1.resolve(`${packageName}/plugin.yaml`, {
656
+ paths: [projectRoot]
657
+ });
658
+ } catch {
659
+ return null;
660
+ }
661
+ }
662
+ async function registerNpmPluginActions(packageName, pluginConfig, registry, verbose, viteServer) {
663
+ const registeredActions = [];
664
+ try {
665
+ let pluginModule;
666
+ if (viteServer) {
667
+ pluginModule = await viteServer.ssrLoadModule(packageName);
668
+ } else {
669
+ pluginModule = await import(packageName);
670
+ }
671
+ for (const actionName of pluginConfig.actions) {
672
+ const actionExport = pluginModule[actionName];
673
+ if (actionExport && isJayAction(actionExport)) {
674
+ registry.register(actionExport);
675
+ registeredActions.push(actionExport.actionName);
676
+ if (verbose) {
677
+ console.log(
678
+ `[Actions] Registered NPM plugin action: ${actionExport.actionName}`
679
+ );
680
+ }
681
+ } else {
682
+ console.warn(
683
+ `[Actions] NPM plugin "${packageName}" declares action "${actionName}" but it's not exported or not a JayAction`
684
+ );
685
+ }
686
+ }
687
+ } catch (importError) {
688
+ console.error(`[Actions] Failed to import NPM plugin "${packageName}":`, importError);
689
+ }
690
+ return registeredActions;
691
+ }
692
+ async function discoverPluginActions(pluginPath, projectRoot, registry = actionRegistry, verbose = false, viteServer) {
693
+ const pluginConfig = loadPluginManifest(pluginPath);
694
+ if (!pluginConfig) {
695
+ return [];
696
+ }
697
+ if (!pluginConfig.actions || !Array.isArray(pluginConfig.actions)) {
698
+ return [];
699
+ }
700
+ const registeredActions = [];
701
+ const pluginName = pluginConfig.name || path.basename(pluginPath);
702
+ if (verbose) {
703
+ console.log(`[Actions] Plugin "${pluginName}" declares actions:`, pluginConfig.actions);
704
+ }
705
+ let modulePath = pluginConfig.module ? path.join(pluginPath, pluginConfig.module) : path.join(pluginPath, "index.ts");
706
+ if (!fs.existsSync(modulePath)) {
707
+ const tsPath = modulePath + ".ts";
708
+ const jsPath = modulePath + ".js";
709
+ if (fs.existsSync(tsPath)) {
710
+ modulePath = tsPath;
711
+ } else if (fs.existsSync(jsPath)) {
712
+ modulePath = jsPath;
713
+ } else {
714
+ console.warn(`[Actions] Plugin "${pluginName}" module not found at ${modulePath}`);
715
+ return [];
716
+ }
717
+ }
718
+ try {
719
+ let pluginModule;
720
+ if (viteServer) {
721
+ pluginModule = await viteServer.ssrLoadModule(modulePath);
722
+ } else {
723
+ pluginModule = await import(modulePath);
724
+ }
725
+ for (const actionName of pluginConfig.actions) {
726
+ const actionExport = pluginModule[actionName];
727
+ if (actionExport && isJayAction(actionExport)) {
728
+ registry.register(actionExport);
729
+ registeredActions.push(actionExport.actionName);
730
+ if (verbose) {
731
+ console.log(
732
+ `[Actions] Registered plugin action: ${actionExport.actionName}`
733
+ );
734
+ }
735
+ } else {
736
+ console.warn(
737
+ `[Actions] Plugin "${pluginName}" declares action "${actionName}" but it's not exported or not a JayAction`
738
+ );
739
+ }
740
+ }
741
+ } catch (importError) {
742
+ console.error(`[Actions] Failed to import plugin module at ${modulePath}:`, importError);
743
+ }
744
+ return registeredActions;
745
+ }
746
+ const require2 = createRequire$1(import.meta.url);
747
+ async function discoverPluginsWithInit(options) {
748
+ const { projectRoot, verbose = false } = options;
749
+ const plugins = [];
750
+ const localPluginsPath = path.join(projectRoot, "src/plugins");
751
+ if (fs.existsSync(localPluginsPath)) {
752
+ try {
753
+ const entries = fs.readdirSync(localPluginsPath, { withFileTypes: true });
754
+ for (const entry of entries) {
755
+ if (!entry.isDirectory())
756
+ continue;
757
+ const pluginPath = path.join(localPluginsPath, entry.name);
758
+ const manifest = loadPluginManifest(pluginPath);
759
+ if (!manifest)
760
+ continue;
761
+ const initConfig = resolvePluginInit(pluginPath, manifest.init, true);
762
+ if (!initConfig)
763
+ continue;
764
+ const dependencies = await getPackageDependencies(pluginPath);
765
+ plugins.push({
766
+ name: manifest.name || entry.name,
767
+ pluginPath,
768
+ packageName: pluginPath,
769
+ // For local, use path
770
+ isLocal: true,
771
+ initModule: initConfig.module,
772
+ initExport: initConfig.export,
773
+ dependencies
774
+ });
775
+ if (verbose) {
776
+ console.log(
777
+ `[PluginInit] Found local plugin with init: ${manifest.name || entry.name}`
778
+ );
779
+ }
780
+ }
781
+ } catch (error) {
782
+ console.warn(`[PluginInit] Failed to scan local plugins: ${error}`);
783
+ }
784
+ }
785
+ const projectPackageJsonPath = path.join(projectRoot, "package.json");
786
+ if (fs.existsSync(projectPackageJsonPath)) {
787
+ try {
788
+ const projectPackageJson = JSON.parse(fs.readFileSync(projectPackageJsonPath, "utf-8"));
789
+ const allDeps = {
790
+ ...projectPackageJson.dependencies,
791
+ ...projectPackageJson.devDependencies
792
+ };
793
+ for (const depName of Object.keys(allDeps)) {
794
+ let pluginYamlPath;
795
+ try {
796
+ pluginYamlPath = require2.resolve(`${depName}/plugin.yaml`, {
797
+ paths: [projectRoot]
798
+ });
799
+ } catch {
800
+ continue;
801
+ }
802
+ const pluginPath = path.dirname(pluginYamlPath);
803
+ const manifest = loadPluginManifest(pluginPath);
804
+ if (!manifest)
805
+ continue;
806
+ const initConfig = resolvePluginInit(pluginPath, manifest.init, false);
807
+ if (!initConfig)
808
+ continue;
809
+ const dependencies = await getPackageDependencies(pluginPath);
810
+ plugins.push({
811
+ name: manifest.name || depName,
812
+ pluginPath,
813
+ packageName: depName,
814
+ isLocal: false,
815
+ initModule: initConfig.module,
816
+ initExport: initConfig.export,
817
+ dependencies
818
+ });
819
+ if (verbose) {
820
+ console.log(`[PluginInit] Found NPM plugin with init: ${depName}`);
821
+ }
822
+ }
823
+ } catch (error) {
824
+ console.warn(`[PluginInit] Failed to scan NPM plugins: ${error}`);
825
+ }
826
+ }
827
+ return plugins;
828
+ }
829
+ function resolvePluginInit(pluginPath, initConfig, isLocal) {
830
+ const defaultExport = "init";
831
+ if (isLocal) {
832
+ const initPaths = [
833
+ path.join(pluginPath, "lib/init.ts"),
834
+ path.join(pluginPath, "lib/init.js"),
835
+ path.join(pluginPath, "init.ts"),
836
+ path.join(pluginPath, "init.js")
837
+ ];
838
+ for (const initPath of initPaths) {
839
+ if (fs.existsSync(initPath)) {
840
+ const relativePath = path.relative(pluginPath, initPath);
841
+ const modulePath = relativePath.replace(/\.(ts|js)$/, "");
842
+ const exportName = typeof initConfig === "string" ? initConfig : defaultExport;
843
+ return { module: modulePath, export: exportName };
844
+ }
845
+ }
846
+ return null;
847
+ }
848
+ const packageJsonPath = path.join(pluginPath, "package.json");
849
+ if (!fs.existsSync(packageJsonPath)) {
850
+ return null;
851
+ }
852
+ try {
853
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
854
+ const hasMain = packageJson.main || packageJson.exports;
855
+ if (!hasMain && !initConfig) {
856
+ return null;
857
+ }
858
+ const exportName = typeof initConfig === "string" ? initConfig : defaultExport;
859
+ return { module: "", export: exportName };
860
+ } catch {
861
+ return null;
862
+ }
863
+ }
864
+ async function getPackageDependencies(pluginPath) {
865
+ const packageJsonPath = path.join(pluginPath, "package.json");
866
+ if (!fs.existsSync(packageJsonPath)) {
867
+ return [];
868
+ }
869
+ try {
870
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
871
+ return Object.keys(packageJson.dependencies || {});
872
+ } catch {
873
+ return [];
874
+ }
875
+ }
876
+ function sortPluginsByDependencies(plugins) {
877
+ const pluginNames = new Set(plugins.map((p) => p.packageName));
878
+ const sorted = [];
879
+ const visited = /* @__PURE__ */ new Set();
880
+ const visiting = /* @__PURE__ */ new Set();
881
+ function visit(plugin) {
882
+ if (visited.has(plugin.packageName))
883
+ return;
884
+ if (visiting.has(plugin.packageName)) {
885
+ console.warn(`[PluginInit] Circular dependency detected for ${plugin.name}`);
886
+ return;
887
+ }
888
+ visiting.add(plugin.packageName);
889
+ for (const dep of plugin.dependencies) {
890
+ if (pluginNames.has(dep)) {
891
+ const depPlugin = plugins.find((p) => p.packageName === dep);
892
+ if (depPlugin) {
893
+ visit(depPlugin);
894
+ }
895
+ }
896
+ }
897
+ visiting.delete(plugin.packageName);
898
+ visited.add(plugin.packageName);
899
+ sorted.push(plugin);
900
+ }
901
+ for (const plugin of plugins) {
902
+ visit(plugin);
903
+ }
904
+ return sorted;
905
+ }
906
+ async function executePluginServerInits(plugins, viteServer, verbose = false) {
907
+ for (const plugin of plugins) {
908
+ try {
909
+ let modulePath;
910
+ if (plugin.isLocal) {
911
+ modulePath = path.join(plugin.pluginPath, plugin.initModule);
912
+ } else if (plugin.initModule) {
913
+ modulePath = `${plugin.packageName}/${plugin.initModule}`;
914
+ } else {
915
+ modulePath = plugin.packageName;
916
+ }
917
+ let pluginModule;
918
+ if (viteServer) {
919
+ pluginModule = await viteServer.ssrLoadModule(modulePath);
920
+ } else {
921
+ pluginModule = await import(modulePath);
922
+ }
923
+ const jayInit = pluginModule[plugin.initExport];
924
+ if (!jayInit || jayInit.__brand !== "JayInit") {
925
+ console.warn(
926
+ `[PluginInit] Plugin "${plugin.name}" init module doesn't export a valid JayInit at "${plugin.initExport}"`
927
+ );
928
+ continue;
929
+ }
930
+ if (typeof jayInit._serverInit === "function") {
931
+ if (verbose) {
932
+ console.log(`[DevServer] Running server init: ${plugin.name}`);
933
+ }
934
+ const clientData = await jayInit._serverInit();
935
+ if (clientData !== void 0 && clientData !== null) {
936
+ setClientInitData(plugin.name, clientData);
937
+ }
938
+ }
939
+ } catch (error) {
940
+ console.error(
941
+ `[PluginInit] Failed to execute server init for "${plugin.name}":`,
942
+ error
943
+ );
944
+ }
945
+ }
946
+ }
947
+ function preparePluginClientInits(plugins) {
948
+ return plugins.map((plugin) => {
949
+ let importPath;
950
+ if (plugin.isLocal) {
951
+ importPath = path.join(plugin.pluginPath, plugin.initModule);
952
+ } else if (plugin.initModule) {
953
+ importPath = `${plugin.packageName}/${plugin.initModule}`;
954
+ } else {
955
+ importPath = `${plugin.packageName}/client`;
956
+ }
957
+ return {
958
+ name: plugin.name,
959
+ importPath,
960
+ initExport: plugin.initExport
961
+ };
207
962
  });
208
963
  }
209
964
  export {
965
+ ActionRegistry,
210
966
  DevSlowlyChangingPhase,
967
+ actionRegistry,
968
+ clearActionRegistry,
969
+ clearClientInitData,
211
970
  clearLifecycleCallbacks,
212
971
  clearServiceRegistry,
972
+ discoverAllPluginActions,
973
+ discoverAndRegisterActions,
974
+ discoverPluginActions,
975
+ discoverPluginsWithInit,
976
+ executeAction,
977
+ executePluginServerInits,
213
978
  generateClientScript,
979
+ getActionCacheHeaders,
980
+ getClientInitData,
981
+ getClientInitDataForKey,
982
+ getRegisteredAction,
983
+ getRegisteredActionNames,
214
984
  getService,
985
+ hasAction,
215
986
  hasService,
216
987
  loadPageParts,
217
988
  onInit,
218
989
  onShutdown,
990
+ preparePluginClientInits,
991
+ registerAction,
219
992
  registerService,
220
993
  renderFastChangingData,
221
994
  resolveServices,
222
995
  runInitCallbacks,
223
996
  runLoadParams,
224
997
  runShutdownCallbacks,
225
- runSlowlyChangingRender
998
+ runSlowlyChangingRender,
999
+ setClientInitData,
1000
+ sortPluginsByDependencies
226
1001
  };