@jay-framework/dev-server 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 +79 -9
  2. package/dist/index.js +1585 -57
  3. package/package.json +13 -12
package/dist/index.js CHANGED
@@ -6,17 +6,1223 @@ var __publicField = (obj, key, value) => {
6
6
  };
7
7
  import { createServer } from "vite";
8
8
  import { scanRoutes, routeToExpressRoute } from "@jay-framework/stack-route-scanner";
9
- import { runInitCallbacks, runShutdownCallbacks, clearLifecycleCallbacks, clearServiceRegistry, DevSlowlyChangingPhase, loadPageParts, renderFastChangingData, generateClientScript } from "@jay-framework/stack-server-runtime";
9
+ import { discoverPluginsWithInit, sortPluginsByDependencies, executePluginServerInits, runInitCallbacks, actionRegistry, discoverAndRegisterActions, discoverAllPluginActions, runShutdownCallbacks, clearLifecycleCallbacks, clearServiceRegistry, clearClientInitData, DevSlowlyChangingPhase, preparePluginClientInits, loadPageParts, renderFastChangingData, getClientInitData, generateClientScript } from "@jay-framework/stack-server-runtime";
10
10
  import { jayRuntime } from "@jay-framework/vite-plugin";
11
+ import { createRequire } from "module";
12
+ import "@jay-framework/compiler-shared";
11
13
  import * as path from "node:path";
12
14
  import path__default from "node:path";
15
+ import "@jay-framework/compiler-jay-html";
13
16
  import * as fs from "node:fs";
14
17
  import { pathToFileURL } from "node:url";
18
+ const s$1 = createRequire(import.meta.url), e$1 = s$1("typescript"), c$1 = new Proxy(e$1, {
19
+ get(t, r) {
20
+ return t[r];
21
+ }
22
+ });
23
+ var __defProp2 = Object.defineProperty;
24
+ var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
25
+ var __publicField2 = (obj, key, value) => {
26
+ __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
27
+ return value;
28
+ };
29
+ const s = createRequire(import.meta.url), e = s("typescript"), c = new Proxy(e, {
30
+ get(t, r) {
31
+ return t[r];
32
+ }
33
+ });
34
+ const {
35
+ visitEachChild: visitEachChild$5,
36
+ isArrowFunction: isArrowFunction$4,
37
+ isFunctionExpression: isFunctionExpression$1,
38
+ createSourceFile: createSourceFile$1,
39
+ ScriptTarget: ScriptTarget$1,
40
+ isFunctionDeclaration: isFunctionDeclaration$3,
41
+ isMethodDeclaration,
42
+ isConstructorDeclaration,
43
+ isGetAccessorDeclaration,
44
+ isSetAccessorDeclaration,
45
+ createPrinter: createPrinter$1,
46
+ NewLineKind,
47
+ EmitHint,
48
+ setTextRange
49
+ } = c;
50
+ createPrinter$1({
51
+ newLine: NewLineKind.LineFeed
52
+ });
53
+ function isFunctionLikeDeclarationBase(node) {
54
+ return isFunctionExpression$1(node) || isArrowFunction$4(node) || isFunctionDeclaration$3(node) || isMethodDeclaration(node) || isConstructorDeclaration(node) || isGetAccessorDeclaration(node) || isSetAccessorDeclaration(node);
55
+ }
56
+ function mkTransformer(fileTransformer, config) {
57
+ return (context) => {
58
+ const { factory } = context;
59
+ return (sourceFile) => {
60
+ return fileTransformer({ factory, context, sourceFile, ...config || {} });
61
+ };
62
+ };
63
+ }
64
+ const {
65
+ isIdentifier: isIdentifier$9,
66
+ isStatement: isStatement$3,
67
+ isObjectBindingPattern,
68
+ isArrayBindingPattern,
69
+ isBindingElement,
70
+ isPropertyAccessExpression: isPropertyAccessExpression$5,
71
+ isElementAccessExpression: isElementAccessExpression$1,
72
+ isStringLiteral: isStringLiteral$8,
73
+ isParenthesizedExpression,
74
+ isAsExpression,
75
+ isObjectLiteralExpression,
76
+ isPropertyAssignment: isPropertyAssignment$1,
77
+ isShorthandPropertyAssignment,
78
+ isNumericLiteral,
79
+ isToken,
80
+ isNamespaceImport,
81
+ isNamedImports: isNamedImports$1,
82
+ isArrowFunction: isArrowFunction$2,
83
+ isFunctionExpression,
84
+ isCallExpression: isCallExpression$6,
85
+ NodeFlags,
86
+ SyntaxKind: SyntaxKind$5
87
+ } = c;
88
+ var VariableRootType = /* @__PURE__ */ ((VariableRootType2) => {
89
+ VariableRootType2[VariableRootType2["FunctionParameter"] = 0] = "FunctionParameter";
90
+ VariableRootType2[VariableRootType2["FunctionDefinition"] = 1] = "FunctionDefinition";
91
+ VariableRootType2[VariableRootType2["Literal"] = 2] = "Literal";
92
+ VariableRootType2[VariableRootType2["ImportModule"] = 3] = "ImportModule";
93
+ VariableRootType2[VariableRootType2["FunctionCall"] = 4] = "FunctionCall";
94
+ VariableRootType2[VariableRootType2["Global"] = 5] = "Global";
95
+ VariableRootType2[VariableRootType2["Other"] = 6] = "Other";
96
+ return VariableRootType2;
97
+ })(VariableRootType || {});
98
+ function mkParameterVariableRoot(param, paramIndex) {
99
+ return { kind: 0, param, paramIndex };
100
+ }
101
+ function mkFunctionVariableRoot(func) {
102
+ return { kind: 1, func };
103
+ }
104
+ function mkLiteralVariableRoot(literal) {
105
+ return { kind: 2, literal };
106
+ }
107
+ function mkImportModuleVariableRoot(module, importType) {
108
+ return { kind: 3, module, importType };
109
+ }
110
+ function mkFunctionCallVariableRoot(node) {
111
+ return { kind: 4, node };
112
+ }
113
+ function mkGlobalVariableRoot(name) {
114
+ return { kind: 5, name };
115
+ }
116
+ function mkOtherVariableRoot(node) {
117
+ return { kind: 6, node };
118
+ }
119
+ function isImportModuleVariableRoot(vr) {
120
+ return vr.kind === 3;
121
+ }
122
+ function mkVariable(members) {
123
+ return Object.fromEntries(Object.entries(members).filter(([, value]) => value !== void 0));
124
+ }
125
+ const UNKNOWN_VARIABLE = {};
126
+ const getAccessedByProperty = (binding, accessedFrom, propertyName) => {
127
+ return accessedFrom ? propertyName ? isIdentifier$9(propertyName) ? propertyName.text : void 0 : binding.text : void 0;
128
+ };
129
+ function findDeclaringStatement(node) {
130
+ if (!node)
131
+ return void 0;
132
+ else if (isStatement$3(node))
133
+ return node;
134
+ else
135
+ return findDeclaringStatement(node.parent);
136
+ }
137
+ function tsBindingNameToVariable(binding, accessedFrom, assignedFrom, propertyName, root) {
138
+ if (isIdentifier$9(binding)) {
139
+ return [
140
+ mkVariable({
141
+ name: binding.text,
142
+ accessedFrom,
143
+ accessedByProperty: getAccessedByProperty(binding, accessedFrom, propertyName),
144
+ assignedFrom,
145
+ root,
146
+ definingStatement: findDeclaringStatement(binding)
147
+ })
148
+ ];
149
+ } else if (isObjectBindingPattern(binding)) {
150
+ let variable = mkVariable({
151
+ accessedFrom,
152
+ accessedByProperty: propertyName ? isIdentifier$9(propertyName) ? propertyName.text : void 0 : void 0,
153
+ assignedFrom,
154
+ root,
155
+ definingStatement: findDeclaringStatement(binding)
156
+ });
157
+ return binding.elements.flatMap((element) => {
158
+ return tsBindingNameToVariable(element.name, variable, void 0, element.propertyName);
159
+ });
160
+ } else if (isArrayBindingPattern(binding)) {
161
+ let variable = mkVariable({
162
+ accessedFrom,
163
+ accessedByProperty: propertyName ? isIdentifier$9(propertyName) ? propertyName.text : void 0 : void 0,
164
+ assignedFrom,
165
+ root,
166
+ definingStatement: findDeclaringStatement(binding)
167
+ });
168
+ return binding.elements.flatMap((element, index) => {
169
+ return isBindingElement(element) && tsBindingNameToVariable(element.name, variable, void 0, {
170
+ kind: 80,
171
+ text: "" + index
172
+ });
173
+ }).filter((variable2) => !!variable2);
174
+ }
175
+ }
176
+ class NameBindingResolver {
177
+ constructor(parentNameResolver) {
178
+ __publicField2(this, "variables", /* @__PURE__ */ new Map());
179
+ this.parentNameResolver = parentNameResolver;
180
+ }
181
+ addVariable(name, variable) {
182
+ this.variables.set(name, variable);
183
+ }
184
+ addFunctionParams(functionDeclaration) {
185
+ functionDeclaration.parameters.map((param, paramIndex) => {
186
+ let paramVariables = tsBindingNameToVariable(
187
+ param.name,
188
+ void 0,
189
+ void 0,
190
+ void 0,
191
+ mkParameterVariableRoot(param, paramIndex)
192
+ );
193
+ paramVariables.forEach((variable) => {
194
+ if (variable.name)
195
+ this.variables.set(variable.name, variable);
196
+ });
197
+ });
198
+ }
199
+ addFunctionDeclaration(statement) {
200
+ if (statement.name) {
201
+ let functionVariable = mkVariable({
202
+ name: statement.name.text,
203
+ root: mkFunctionVariableRoot(statement),
204
+ definingStatement: statement
205
+ });
206
+ this.variables.set(statement.name.text, functionVariable);
207
+ }
208
+ }
209
+ addVariableDeclarationList(declarationList) {
210
+ const letOrConst = declarationList.flags === NodeFlags.Const ? 1 : 0;
211
+ declarationList.declarations.forEach((declaration) => {
212
+ let rightSide = declaration.initializer ? this.resolvePropertyAccessChain(declaration.initializer) : void 0;
213
+ let declaredVariable = tsBindingNameToVariable(
214
+ declaration.name,
215
+ void 0,
216
+ rightSide,
217
+ void 0
218
+ );
219
+ declaredVariable.forEach(
220
+ (variable) => this.variables.set(variable.name, { ...variable, letOrConst })
221
+ );
222
+ });
223
+ }
224
+ addVariableStatement(variableStatement) {
225
+ this.addVariableDeclarationList(variableStatement.declarationList);
226
+ }
227
+ getVariable(name) {
228
+ return this.variables.get(name) || UNKNOWN_VARIABLE;
229
+ }
230
+ resolvePropertyAccessChain(expression) {
231
+ if (isPropertyAccessExpression$5(expression)) {
232
+ const name = expression.name.text;
233
+ const identifiersFromObject = this.resolvePropertyAccessChain(expression.expression);
234
+ return { accessedFrom: identifiersFromObject, accessedByProperty: name };
235
+ } else if (isElementAccessExpression$1(expression) && isStringLiteral$8(expression.argumentExpression)) {
236
+ const name = expression.argumentExpression.text;
237
+ const identifiersFromObject = this.resolvePropertyAccessChain(expression.expression);
238
+ return { accessedFrom: identifiersFromObject, accessedByProperty: name };
239
+ } else if (isIdentifier$9(expression)) {
240
+ return this.resolveIdentifier(expression);
241
+ } else if (isParenthesizedExpression(expression)) {
242
+ return this.resolvePropertyAccessChain(expression.expression);
243
+ } else if (isAsExpression(expression)) {
244
+ return this.resolvePropertyAccessChain(expression.expression);
245
+ } else if (isObjectLiteralExpression(expression)) {
246
+ return {
247
+ properties: expression.properties.map((property) => {
248
+ if (isPropertyAssignment$1(property) && (isStringLiteral$8(property.name) || isIdentifier$9(property.name))) {
249
+ if (isIdentifier$9(property.initializer))
250
+ return {
251
+ name: property.name.text,
252
+ assignedFrom: this.resolveIdentifier(property.initializer)
253
+ };
254
+ else if (isObjectLiteralExpression(property.initializer)) {
255
+ let nestedProperty = this.resolvePropertyAccessChain(
256
+ property.initializer
257
+ );
258
+ nestedProperty.name = property.name.text;
259
+ return nestedProperty;
260
+ } else if (isArrowFunction$2(property.initializer) || isFunctionExpression(property.initializer)) {
261
+ return {
262
+ name: property.name.text,
263
+ root: mkFunctionVariableRoot(property.initializer)
264
+ };
265
+ } else
266
+ return {
267
+ name: property.name.text,
268
+ root: mkLiteralVariableRoot(property.initializer)
269
+ };
270
+ } else if (isShorthandPropertyAssignment(property))
271
+ return {
272
+ name: property.name.text,
273
+ assignedFrom: this.resolveIdentifier(property.name)
274
+ };
275
+ })
276
+ };
277
+ } else if (isArrowFunction$2(expression) || isFunctionExpression(expression)) {
278
+ return { root: mkFunctionVariableRoot(expression) };
279
+ } else if (isCallExpression$6(expression)) {
280
+ return { root: mkFunctionCallVariableRoot(expression) };
281
+ } else if (isStringLiteral$8(expression) || isNumericLiteral(expression) || isToken(expression) && expression.kind === SyntaxKind$5.TrueKeyword || isToken(expression) && expression.kind === SyntaxKind$5.FalseKeyword) {
282
+ return { root: mkLiteralVariableRoot(expression) };
283
+ } else {
284
+ return { root: mkOtherVariableRoot(expression) };
285
+ }
286
+ }
287
+ resolvePropertyAccess(expression) {
288
+ return this.resolvePropertyAccessChain(expression);
289
+ }
290
+ resolveIdentifier(expression) {
291
+ let variableName = expression.text;
292
+ let nameResolver = this;
293
+ let resolved;
294
+ while ((resolved = nameResolver.getVariable(variableName)) === UNKNOWN_VARIABLE && nameResolver.parentNameResolver)
295
+ nameResolver = nameResolver.parentNameResolver;
296
+ if (resolved === UNKNOWN_VARIABLE)
297
+ return { root: mkGlobalVariableRoot(variableName) };
298
+ return resolved;
299
+ }
300
+ addImportDeclaration(node) {
301
+ if (node.importClause?.name) {
302
+ let root = mkImportModuleVariableRoot(
303
+ node.moduleSpecifier,
304
+ 0
305
+ /* defaultImport */
306
+ );
307
+ let variable = mkVariable({
308
+ name: node.importClause.name.text,
309
+ definingStatement: node,
310
+ root
311
+ });
312
+ this.variables.set(node.importClause.name.text, variable);
313
+ }
314
+ if (node.importClause?.namedBindings) {
315
+ let root = mkImportModuleVariableRoot(
316
+ node.moduleSpecifier,
317
+ 1
318
+ /* namedImport */
319
+ );
320
+ let namedBindings = node.importClause.namedBindings;
321
+ if (isNamespaceImport(namedBindings)) {
322
+ let variable = mkVariable({
323
+ name: namedBindings.name.text,
324
+ definingStatement: node,
325
+ root
326
+ });
327
+ this.variables.set(namedBindings.name.text, variable);
328
+ } else if (isNamedImports$1(namedBindings)) {
329
+ namedBindings.elements.forEach((importSpecifier) => {
330
+ if (!importSpecifier.isTypeOnly) {
331
+ let variable = mkVariable({
332
+ name: importSpecifier.name.text,
333
+ accessedByProperty: (importSpecifier.propertyName || importSpecifier.name).text,
334
+ accessedFrom: {
335
+ definingStatement: node,
336
+ root
337
+ },
338
+ definingStatement: node
339
+ });
340
+ this.variables.set(importSpecifier.name.text, variable);
341
+ }
342
+ });
343
+ }
344
+ }
345
+ }
346
+ }
347
+ function flattenVariable(variable, path2 = []) {
348
+ if (variable.assignedFrom)
349
+ return flattenVariable(variable.assignedFrom, path2);
350
+ else if (variable.accessedFrom) {
351
+ return flattenVariable(variable.accessedFrom, [variable.accessedByProperty, ...path2]);
352
+ } else if (variable.properties && !!variable.properties.find((_) => _.name === path2[0])) {
353
+ return flattenVariable(
354
+ variable.properties.find((_) => _.name === path2[0]),
355
+ path2.slice(1)
356
+ );
357
+ } else
358
+ return { path: path2, root: variable.root };
359
+ }
360
+ function byAnd() {
361
+ return (agg, value) => agg && value;
362
+ }
363
+ const {
364
+ isIdentifier: isIdentifier$6,
365
+ SyntaxKind: SyntaxKind$4,
366
+ isStringLiteral: isStringLiteral$7,
367
+ visitNode: visitNode$4,
368
+ isFunctionDeclaration: isFunctionDeclaration$1,
369
+ isVariableStatement: isVariableStatement$2,
370
+ isImportDeclaration: isImportDeclaration$3,
371
+ isBlock: isBlock$2,
372
+ isForStatement: isForStatement$1,
373
+ isVariableDeclarationList,
374
+ isForInStatement: isForInStatement$1,
375
+ isForOfStatement: isForOfStatement$1,
376
+ isTypeReferenceNode: isTypeReferenceNode$1,
377
+ isArrayTypeNode,
378
+ isFunctionTypeNode,
379
+ isUnionTypeNode
380
+ } = c;
381
+ const BUILT_IN_TYPES = ["RegExp", "Date"];
382
+ function builtInType(text) {
383
+ return BUILT_IN_TYPES.findIndex((_) => _ === text) > -1;
384
+ }
385
+ class BuiltInResolvedType {
386
+ constructor(name) {
387
+ this.name = name;
388
+ }
389
+ canBeAssignedFrom(rightSide) {
390
+ return rightSide instanceof BuiltInResolvedType && this.name === rightSide.name;
391
+ }
392
+ }
393
+ class ImportFromModuleResolvedType {
394
+ constructor(module, path2) {
395
+ this.module = module;
396
+ this.path = path2;
397
+ }
398
+ canBeAssignedFrom(rightSide) {
399
+ if (rightSide instanceof ImportFromModuleResolvedType) {
400
+ let pathEqual = this.path.length === rightSide.path.length;
401
+ if (pathEqual) {
402
+ pathEqual = this.path.map((value, index) => value === rightSide.path[index]).reduce(byAnd(), true);
403
+ }
404
+ return pathEqual && this.module === rightSide.module;
405
+ }
406
+ return false;
407
+ }
408
+ }
409
+ class ArrayResolvedType {
410
+ constructor(itemType) {
411
+ this.itemType = itemType;
412
+ }
413
+ canBeAssignedFrom(rightSide) {
414
+ return rightSide instanceof ArrayResolvedType && this.itemType === rightSide.itemType;
415
+ }
416
+ }
417
+ class FunctionResolvedType {
418
+ constructor(params, returns) {
419
+ this.params = params;
420
+ this.returns = returns;
421
+ }
422
+ canBeAssignedFrom(rightSide) {
423
+ return rightSide instanceof FunctionResolvedType && this.returns.canBeAssignedFrom(rightSide.returns);
424
+ }
425
+ }
426
+ class UnionResolvedType {
427
+ constructor(types) {
428
+ this.types = types;
429
+ }
430
+ canBeAssignedFrom(rightSide) {
431
+ if (rightSide instanceof UnionResolvedType) {
432
+ for (const item1 of this.types)
433
+ for (const item2 of rightSide.types)
434
+ if (item1.canBeAssignedFrom(item2))
435
+ return true;
436
+ } else
437
+ for (const item1 of this.types)
438
+ if (item1.canBeAssignedFrom(rightSide))
439
+ return true;
440
+ return false;
441
+ }
442
+ }
443
+ class GlobalResolvedType {
444
+ constructor(name) {
445
+ this.name = name;
446
+ }
447
+ canBeAssignedFrom(rightSide) {
448
+ return rightSide instanceof GlobalResolvedType && this.name === rightSide.name;
449
+ }
450
+ }
451
+ class SourceFileBindingResolver {
452
+ constructor(sourceFile) {
453
+ __publicField2(this, "nameBindingResolvers", /* @__PURE__ */ new Map());
454
+ this.nameBindingResolvers.set(sourceFile, new NameBindingResolver());
455
+ const nbResolversQueue = [
456
+ this.nameBindingResolvers.get(sourceFile)
457
+ ];
458
+ const doWithChildBindingResolver = (node, callback) => {
459
+ nbResolversQueue.unshift(new NameBindingResolver(nbResolversQueue[0]));
460
+ this.nameBindingResolvers.set(node, nbResolversQueue[0]);
461
+ callback();
462
+ node.getChildren().forEach((child) => visitNode$4(child, visitor));
463
+ nbResolversQueue.shift();
464
+ return node;
465
+ };
466
+ const visitor = (node) => {
467
+ if (isFunctionDeclaration$1(node))
468
+ nbResolversQueue[0].addFunctionDeclaration(node);
469
+ if (isVariableStatement$2(node))
470
+ nbResolversQueue[0].addVariableStatement(node);
471
+ else if (isImportDeclaration$3(node))
472
+ nbResolversQueue[0].addImportDeclaration(node);
473
+ else if (isBlock$2(node))
474
+ return doWithChildBindingResolver(node, () => {
475
+ });
476
+ else if (isFunctionLikeDeclarationBase(node)) {
477
+ return doWithChildBindingResolver(
478
+ node,
479
+ () => nbResolversQueue[0].addFunctionParams(node)
480
+ );
481
+ } else if (isForStatement$1(node) && isVariableDeclarationList(node.initializer)) {
482
+ return doWithChildBindingResolver(
483
+ node,
484
+ () => nbResolversQueue[0].addVariableDeclarationList(
485
+ node.initializer
486
+ )
487
+ );
488
+ } else if ((isForInStatement$1(node) || isForOfStatement$1(node)) && isVariableDeclarationList(node.initializer) && node.initializer.declarations.length === 1 && isIdentifier$6(node.initializer.declarations[0].name)) {
489
+ return doWithChildBindingResolver(node, () => {
490
+ let name = node.initializer.declarations[0].name.text;
491
+ nbResolversQueue[0].addVariable(
492
+ name,
493
+ mkVariable({
494
+ name,
495
+ root: mkOtherVariableRoot(node),
496
+ definingStatement: node
497
+ })
498
+ );
499
+ });
500
+ }
501
+ node.getChildren().forEach((child) => visitNode$4(child, visitor));
502
+ return node;
503
+ };
504
+ visitNode$4(sourceFile, visitor);
505
+ }
506
+ findBindingResolver(node) {
507
+ let found;
508
+ while (!(found = this.nameBindingResolvers.get(node)) && node.parent)
509
+ node = node.parent;
510
+ return found;
511
+ }
512
+ explain(identifier) {
513
+ return this.findBindingResolver(identifier).resolvePropertyAccessChain(identifier);
514
+ }
515
+ explainFlattenedVariableType(flattened) {
516
+ if (!!flattened.root) {
517
+ if (isImportModuleVariableRoot(flattened.root) && isStringLiteral$7(flattened.root.module)) {
518
+ return new ImportFromModuleResolvedType(flattened.root.module.text, flattened.path);
519
+ }
520
+ } else
521
+ return void 0;
522
+ }
523
+ explainType(type) {
524
+ if (type) {
525
+ if (isTypeReferenceNode$1(type)) {
526
+ let typeName = type.typeName;
527
+ if (isIdentifier$6(typeName)) {
528
+ let resolved = this.findBindingResolver(typeName).resolveIdentifier(typeName);
529
+ let flattened = flattenVariable(resolved);
530
+ let typeFromFlattened = this.explainFlattenedVariableType(flattened);
531
+ if (typeFromFlattened)
532
+ return typeFromFlattened;
533
+ if (builtInType(typeName.text))
534
+ return new BuiltInResolvedType(typeName.text);
535
+ }
536
+ } else if (type.kind === SyntaxKind$4.StringKeyword)
537
+ return new BuiltInResolvedType("string");
538
+ else if (type.kind === SyntaxKind$4.NumberKeyword)
539
+ return new BuiltInResolvedType("number");
540
+ else if (type.kind === SyntaxKind$4.BooleanKeyword)
541
+ return new BuiltInResolvedType("boolean");
542
+ else if (type.kind === SyntaxKind$4.AnyKeyword)
543
+ return new BuiltInResolvedType("any");
544
+ else if (type.kind === SyntaxKind$4.VoidKeyword)
545
+ return new BuiltInResolvedType("void");
546
+ else if (isArrayTypeNode(type))
547
+ return new ArrayResolvedType(this.explainType(type.elementType));
548
+ else if (isFunctionTypeNode(type)) {
549
+ const params = type.parameters.map((param) => this.explainType(param.type));
550
+ const ret = this.explainType(type.type);
551
+ return new FunctionResolvedType(params, ret);
552
+ } else if (isUnionTypeNode(type))
553
+ return new UnionResolvedType(type.types.map((aType) => this.explainType(aType)));
554
+ }
555
+ return new BuiltInResolvedType("void");
556
+ }
557
+ globalType(globalVariableRoot) {
558
+ return new GlobalResolvedType(globalVariableRoot.name);
559
+ }
560
+ }
561
+ const { isStringLiteral: isStringLiteral$1 } = c;
562
+ function areFlattenedAccessChainsEqual(chain1, chain2) {
563
+ if (chain1.path.length !== chain2.path.length) {
564
+ return false;
565
+ }
566
+ const pathsEqual = chain1.path.every((value, index) => value === chain2.path[index]);
567
+ if (!pathsEqual) {
568
+ return false;
569
+ }
570
+ return areVariableRootsEqual(chain1.root, chain2.root);
571
+ }
572
+ function areVariableRootsEqual(root1, root2) {
573
+ if (!root1 && !root2) {
574
+ return true;
575
+ }
576
+ if (!root1 || !root2) {
577
+ return false;
578
+ }
579
+ if (root1.kind !== root2.kind) {
580
+ return false;
581
+ }
582
+ switch (root1.kind) {
583
+ case VariableRootType.ImportModule:
584
+ if (isImportModuleVariableRoot(root1) && isImportModuleVariableRoot(root2)) {
585
+ const module1 = isStringLiteral$1(root1.module) ? root1.module.text : String(root1.module);
586
+ const module2 = isStringLiteral$1(root2.module) ? root2.module.text : String(root2.module);
587
+ return module1 === module2 && root1.importType === root2.importType;
588
+ }
589
+ return false;
590
+ case VariableRootType.FunctionParameter:
591
+ case VariableRootType.FunctionDefinition:
592
+ case VariableRootType.Literal:
593
+ case VariableRootType.FunctionCall:
594
+ return true;
595
+ case VariableRootType.Global:
596
+ case VariableRootType.Other:
597
+ return true;
598
+ default:
599
+ return false;
600
+ }
601
+ }
602
+ const COMPONENT_SERVER_METHODS = /* @__PURE__ */ new Set([
603
+ "withServices",
604
+ "withLoadParams",
605
+ "withSlowlyRender",
606
+ "withFastRender"
607
+ ]);
608
+ const COMPONENT_CLIENT_METHODS = /* @__PURE__ */ new Set(["withInteractive", "withContexts"]);
609
+ const INIT_SERVER_METHODS = /* @__PURE__ */ new Set(["withServer"]);
610
+ const INIT_CLIENT_METHODS = /* @__PURE__ */ new Set(["withClient"]);
611
+ function shouldRemoveMethod(methodName, environment) {
612
+ if (environment === "client" && COMPONENT_SERVER_METHODS.has(methodName))
613
+ return true;
614
+ if (environment === "server" && COMPONENT_CLIENT_METHODS.has(methodName))
615
+ return true;
616
+ if (environment === "client" && INIT_SERVER_METHODS.has(methodName))
617
+ return true;
618
+ if (environment === "server" && INIT_CLIENT_METHODS.has(methodName))
619
+ return true;
620
+ return false;
621
+ }
622
+ const { isCallExpression: isCallExpression$1, isPropertyAccessExpression: isPropertyAccessExpression$1, isIdentifier: isIdentifier$2, isStringLiteral } = c$1;
623
+ function findBuilderMethodsToRemove(sourceFile, bindingResolver, environment) {
624
+ const callsToRemove = [];
625
+ const removedVariables = /* @__PURE__ */ new Set();
626
+ const visit = (node) => {
627
+ if (isCallExpression$1(node) && isPropertyAccessExpression$1(node.expression) && isPartOfJayStackChain(node, bindingResolver)) {
628
+ const methodName = node.expression.name.text;
629
+ if (shouldRemoveMethod(methodName, environment)) {
630
+ const variable = bindingResolver.explain(node.expression);
631
+ const flattened = flattenVariable(variable);
632
+ callsToRemove.push(flattened);
633
+ collectVariablesFromArguments(node.arguments, bindingResolver, removedVariables);
634
+ }
635
+ }
636
+ node.forEachChild(visit);
637
+ };
638
+ sourceFile.forEachChild(visit);
639
+ return { callsToRemove, removedVariables };
640
+ }
641
+ const JAY_BUILDER_FUNCTIONS = /* @__PURE__ */ new Set(["makeJayStackComponent", "makeJayInit"]);
642
+ function isPartOfJayStackChain(callExpr, bindingResolver) {
643
+ let current = callExpr.expression;
644
+ while (true) {
645
+ if (isPropertyAccessExpression$1(current)) {
646
+ current = current.expression;
647
+ } else if (isCallExpression$1(current)) {
648
+ if (isIdentifier$2(current.expression)) {
649
+ const variable = bindingResolver.explain(current.expression);
650
+ const flattened = flattenVariable(variable);
651
+ if (flattened.path.length === 1 && JAY_BUILDER_FUNCTIONS.has(flattened.path[0]) && isImportModuleVariableRoot(flattened.root) && isStringLiteral(flattened.root.module) && flattened.root.module.text === "@jay-framework/fullstack-component")
652
+ return true;
653
+ }
654
+ if (isPropertyAccessExpression$1(current.expression)) {
655
+ current = current.expression.expression;
656
+ continue;
657
+ } else {
658
+ break;
659
+ }
660
+ } else {
661
+ break;
662
+ }
663
+ }
664
+ return false;
665
+ }
666
+ function collectVariablesFromArguments(args, bindingResolver, variables) {
667
+ const visitor = (node) => {
668
+ if (isIdentifier$2(node)) {
669
+ const variable = bindingResolver.explain(node);
670
+ if (variable && (variable.name || variable.root)) {
671
+ variables.add(variable);
672
+ }
673
+ }
674
+ node.forEachChild(visitor);
675
+ };
676
+ args.forEach((arg) => visitor(arg));
677
+ }
678
+ const {
679
+ isIdentifier: isIdentifier$1,
680
+ isImportDeclaration: isImportDeclaration$1,
681
+ isFunctionDeclaration,
682
+ isVariableStatement,
683
+ isInterfaceDeclaration,
684
+ isTypeAliasDeclaration,
685
+ isClassDeclaration,
686
+ isEnumDeclaration,
687
+ SyntaxKind
688
+ } = c$1;
689
+ function analyzeUnusedStatements(sourceFile) {
690
+ const statementsToRemove = /* @__PURE__ */ new Set();
691
+ const collectUsedIdentifiers = () => {
692
+ const used = /* @__PURE__ */ new Set();
693
+ for (const statement of sourceFile.statements) {
694
+ if (isImportDeclaration$1(statement) || statementsToRemove.has(statement)) {
695
+ continue;
696
+ }
697
+ const definedName = getStatementDefinedName(statement);
698
+ const visitor = (node, parent) => {
699
+ if (isIdentifier$1(node)) {
700
+ if (node.text !== definedName) {
701
+ used.add(node.text);
702
+ }
703
+ }
704
+ node.forEachChild((child) => visitor(child));
705
+ };
706
+ statement.forEachChild((child) => visitor(child));
707
+ }
708
+ return used;
709
+ };
710
+ let changed = true;
711
+ while (changed) {
712
+ changed = false;
713
+ const stillUsedIdentifiers = collectUsedIdentifiers();
714
+ for (const statement of sourceFile.statements) {
715
+ if (statementsToRemove.has(statement) || isImportDeclaration$1(statement) || isExportStatement(statement)) {
716
+ continue;
717
+ }
718
+ const definedName = getStatementDefinedName(statement);
719
+ if (definedName && !stillUsedIdentifiers.has(definedName)) {
720
+ statementsToRemove.add(statement);
721
+ changed = true;
722
+ }
723
+ }
724
+ }
725
+ const finalUsedIdentifiers = collectUsedIdentifiers();
726
+ const unusedImports = /* @__PURE__ */ new Set();
727
+ for (const statement of sourceFile.statements) {
728
+ if (isImportDeclaration$1(statement) && statement.importClause?.namedBindings) {
729
+ const namedBindings = statement.importClause.namedBindings;
730
+ if ("elements" in namedBindings) {
731
+ for (const element of namedBindings.elements) {
732
+ const importName = element.name.text;
733
+ if (!finalUsedIdentifiers.has(importName)) {
734
+ unusedImports.add(importName);
735
+ }
736
+ }
737
+ }
738
+ }
739
+ }
740
+ return { statementsToRemove, unusedImports };
741
+ }
742
+ function isExportStatement(statement) {
743
+ const modifiers = "modifiers" in statement ? statement.modifiers : void 0;
744
+ if (modifiers) {
745
+ return modifiers.some((mod) => mod.kind === SyntaxKind.ExportKeyword);
746
+ }
747
+ return false;
748
+ }
749
+ function getStatementDefinedName(statement) {
750
+ if (isFunctionDeclaration(statement) && statement.name) {
751
+ return statement.name.text;
752
+ }
753
+ if (isVariableStatement(statement)) {
754
+ const firstDecl = statement.declarationList.declarations[0];
755
+ if (firstDecl && isIdentifier$1(firstDecl.name)) {
756
+ return firstDecl.name.text;
757
+ }
758
+ }
759
+ if (isInterfaceDeclaration(statement) && statement.name) {
760
+ return statement.name.text;
761
+ }
762
+ if (isTypeAliasDeclaration(statement) && statement.name) {
763
+ return statement.name.text;
764
+ }
765
+ if (isClassDeclaration(statement) && statement.name) {
766
+ return statement.name.text;
767
+ }
768
+ if (isEnumDeclaration(statement) && statement.name) {
769
+ return statement.name.text;
770
+ }
771
+ return void 0;
772
+ }
773
+ const {
774
+ createPrinter,
775
+ createSourceFile,
776
+ ScriptTarget,
777
+ visitEachChild,
778
+ isCallExpression,
779
+ isPropertyAccessExpression,
780
+ isImportDeclaration,
781
+ isNamedImports,
782
+ isIdentifier
783
+ } = c$1;
784
+ function transformJayStackBuilder(code, filePath, environment) {
785
+ const sourceFile = createSourceFile(filePath, code, ScriptTarget.Latest, true);
786
+ const transformers = [mkTransformer(mkJayStackCodeSplitTransformer, { environment })];
787
+ const printer = createPrinter();
788
+ const result = c$1.transform(sourceFile, transformers);
789
+ const transformedFile = result.transformed[0];
790
+ const transformedCode = printer.printFile(transformedFile);
791
+ result.dispose();
792
+ return {
793
+ code: transformedCode
794
+ };
795
+ }
796
+ function isCallToRemove(flattened, callsToRemove) {
797
+ return callsToRemove.some((call) => areFlattenedAccessChainsEqual(flattened, call));
798
+ }
799
+ function mkJayStackCodeSplitTransformer({
800
+ factory,
801
+ sourceFile,
802
+ context,
803
+ environment
804
+ }) {
805
+ const bindingResolver = new SourceFileBindingResolver(sourceFile);
806
+ const { callsToRemove } = findBuilderMethodsToRemove(sourceFile, bindingResolver, environment);
807
+ const transformVisitor = (node) => {
808
+ if (isCallExpression(node) && isPropertyAccessExpression(node.expression)) {
809
+ const variable = bindingResolver.explain(node.expression);
810
+ const flattened = flattenVariable(variable);
811
+ if (isCallToRemove(flattened, callsToRemove)) {
812
+ const receiver = node.expression.expression;
813
+ return transformVisitor(receiver);
814
+ }
815
+ }
816
+ return visitEachChild(node, transformVisitor, context);
817
+ };
818
+ let transformedSourceFile = visitEachChild(
819
+ sourceFile,
820
+ transformVisitor,
821
+ context
822
+ );
823
+ const { statementsToRemove, unusedImports } = analyzeUnusedStatements(transformedSourceFile);
824
+ const transformedStatements = transformedSourceFile.statements.map((statement) => {
825
+ if (statementsToRemove.has(statement)) {
826
+ return void 0;
827
+ }
828
+ if (isImportDeclaration(statement)) {
829
+ return filterImportDeclaration(statement, unusedImports, factory);
830
+ }
831
+ return statement;
832
+ }).filter((s2) => s2 !== void 0);
833
+ return factory.updateSourceFile(transformedSourceFile, transformedStatements);
834
+ }
835
+ function filterImportDeclaration(statement, unusedImports, factory) {
836
+ const importClause = statement.importClause;
837
+ if (!importClause?.namedBindings || !isNamedImports(importClause.namedBindings)) {
838
+ return statement;
839
+ }
840
+ const usedElements = importClause.namedBindings.elements.filter(
841
+ (element) => !unusedImports.has(element.name.text)
842
+ );
843
+ if (usedElements.length === 0) {
844
+ return void 0;
845
+ }
846
+ return factory.updateImportDeclaration(
847
+ statement,
848
+ statement.modifiers,
849
+ factory.updateImportClause(
850
+ importClause,
851
+ importClause.isTypeOnly,
852
+ importClause.name,
853
+ factory.updateNamedImports(importClause.namedBindings, usedElements)
854
+ ),
855
+ statement.moduleSpecifier,
856
+ statement.assertClause
857
+ );
858
+ }
859
+ const actionMetadataCache = /* @__PURE__ */ new Map();
860
+ function clearActionMetadataCache() {
861
+ actionMetadataCache.clear();
862
+ }
863
+ function isActionImport(importSource) {
864
+ return importSource.includes(".actions") || importSource.includes("-actions") || importSource.includes("/actions/") || importSource.endsWith("/actions");
865
+ }
866
+ function extractActionsFromSource(sourceCode, filePath) {
867
+ const cached = actionMetadataCache.get(filePath);
868
+ if (cached) {
869
+ return cached;
870
+ }
871
+ const actions = [];
872
+ const sourceFile = c$1.createSourceFile(
873
+ filePath,
874
+ sourceCode,
875
+ c$1.ScriptTarget.Latest,
876
+ true
877
+ );
878
+ function visit(node) {
879
+ if (c$1.isVariableStatement(node)) {
880
+ const hasExport = node.modifiers?.some(
881
+ (m) => m.kind === c$1.SyntaxKind.ExportKeyword
882
+ );
883
+ if (!hasExport) {
884
+ c$1.forEachChild(node, visit);
885
+ return;
886
+ }
887
+ for (const decl of node.declarationList.declarations) {
888
+ if (!c$1.isIdentifier(decl.name) || !decl.initializer) {
889
+ continue;
890
+ }
891
+ const exportName = decl.name.text;
892
+ const actionMeta = extractActionFromExpression(decl.initializer);
893
+ if (actionMeta) {
894
+ actions.push({
895
+ ...actionMeta,
896
+ exportName
897
+ });
898
+ }
899
+ }
900
+ }
901
+ c$1.forEachChild(node, visit);
902
+ }
903
+ visit(sourceFile);
904
+ actionMetadataCache.set(filePath, actions);
905
+ return actions;
906
+ }
907
+ function extractActionFromExpression(node) {
908
+ let current = node;
909
+ let method = "POST";
910
+ let explicitMethod = null;
911
+ while (c$1.isCallExpression(current)) {
912
+ const expr = current.expression;
913
+ if (c$1.isPropertyAccessExpression(expr) && expr.name.text === "withMethod") {
914
+ const arg = current.arguments[0];
915
+ if (arg && c$1.isStringLiteral(arg)) {
916
+ explicitMethod = arg.text;
917
+ }
918
+ current = expr.expression;
919
+ continue;
920
+ }
921
+ if (c$1.isPropertyAccessExpression(expr) && ["withServices", "withCaching", "withHandler", "withTimeout"].includes(expr.name.text)) {
922
+ current = expr.expression;
923
+ continue;
924
+ }
925
+ if (c$1.isIdentifier(expr)) {
926
+ const funcName = expr.text;
927
+ if (funcName === "makeJayAction" || funcName === "makeJayQuery") {
928
+ const nameArg = current.arguments[0];
929
+ if (nameArg && c$1.isStringLiteral(nameArg)) {
930
+ method = funcName === "makeJayQuery" ? "GET" : "POST";
931
+ if (explicitMethod) {
932
+ method = explicitMethod;
933
+ }
934
+ return {
935
+ actionName: nameArg.text,
936
+ method
937
+ };
938
+ }
939
+ }
940
+ }
941
+ break;
942
+ }
943
+ return null;
944
+ }
945
+ const SERVER_ONLY_MODULES = /* @__PURE__ */ new Set([
946
+ "module",
947
+ // createRequire
948
+ "fs",
949
+ "path",
950
+ "node:fs",
951
+ "node:path",
952
+ "node:module",
953
+ "child_process",
954
+ "node:child_process",
955
+ "crypto",
956
+ "node:crypto"
957
+ ]);
958
+ const SERVER_ONLY_PACKAGE_PATTERNS = [
959
+ "@jay-framework/compiler-shared",
960
+ "@jay-framework/stack-server-runtime",
961
+ "yaml"
962
+ // Often used in server config
963
+ ];
964
+ function createImportChainTracker(options = {}) {
965
+ const {
966
+ verbose = false,
967
+ additionalServerModules = [],
968
+ additionalServerPatterns = []
969
+ } = options;
970
+ const importChain = /* @__PURE__ */ new Map();
971
+ const detectedServerModules = /* @__PURE__ */ new Set();
972
+ const serverOnlyModules = /* @__PURE__ */ new Set([...SERVER_ONLY_MODULES, ...additionalServerModules]);
973
+ const serverOnlyPatterns = [...SERVER_ONLY_PACKAGE_PATTERNS, ...additionalServerPatterns];
974
+ function isServerOnlyModule(id) {
975
+ if (serverOnlyModules.has(id)) {
976
+ return true;
977
+ }
978
+ for (const pattern of serverOnlyPatterns) {
979
+ if (id.includes(pattern)) {
980
+ return true;
981
+ }
982
+ }
983
+ return false;
984
+ }
985
+ function buildImportChain(moduleId) {
986
+ const chain = [moduleId];
987
+ let current = moduleId;
988
+ for (let i = 0; i < 100; i++) {
989
+ const importer = importChain.get(current);
990
+ if (!importer)
991
+ break;
992
+ chain.push(importer);
993
+ current = importer;
994
+ }
995
+ return chain.reverse();
996
+ }
997
+ function formatChain(chain) {
998
+ return chain.map((id, idx) => {
999
+ const indent = " ".repeat(idx);
1000
+ const shortId = shortenPath(id);
1001
+ return `${indent}${idx === 0 ? "" : "↳ "}${shortId}`;
1002
+ }).join("\n");
1003
+ }
1004
+ function shortenPath(id) {
1005
+ if (id.includes("node_modules")) {
1006
+ const parts = id.split("node_modules/");
1007
+ return parts[parts.length - 1];
1008
+ }
1009
+ const cwd = process.cwd();
1010
+ if (id.startsWith(cwd)) {
1011
+ return id.slice(cwd.length + 1);
1012
+ }
1013
+ return id;
1014
+ }
1015
+ return {
1016
+ name: "jay-stack:import-chain-tracker",
1017
+ enforce: "pre",
1018
+ buildStart() {
1019
+ importChain.clear();
1020
+ detectedServerModules.clear();
1021
+ if (verbose) {
1022
+ console.log("[import-chain-tracker] Build started, tracking imports...");
1023
+ }
1024
+ },
1025
+ resolveId(source, importer, options2) {
1026
+ if (options2?.ssr) {
1027
+ return null;
1028
+ }
1029
+ if (source.startsWith("\0")) {
1030
+ return null;
1031
+ }
1032
+ if (importer) {
1033
+ if (verbose) {
1034
+ console.log(
1035
+ `[import-chain-tracker] ${shortenPath(importer)} imports ${source}`
1036
+ );
1037
+ }
1038
+ }
1039
+ return null;
1040
+ },
1041
+ load(id) {
1042
+ return null;
1043
+ },
1044
+ transform(code, id, options2) {
1045
+ if (options2?.ssr) {
1046
+ return null;
1047
+ }
1048
+ if (isServerOnlyModule(id)) {
1049
+ detectedServerModules.add(id);
1050
+ const chain = buildImportChain(id);
1051
+ console.error(
1052
+ `
1053
+ [import-chain-tracker] ⚠️ Server-only module detected in client build!`
1054
+ );
1055
+ console.error(`Module: ${shortenPath(id)}`);
1056
+ console.error(`Import chain:`);
1057
+ console.error(formatChain(chain));
1058
+ console.error("");
1059
+ }
1060
+ const importRegex = /import\s+(?:(?:\{[^}]*\}|[^{}\s,]+)\s+from\s+)?['"]([^'"]+)['"]/g;
1061
+ let match;
1062
+ while ((match = importRegex.exec(code)) !== null) {
1063
+ const importedModule = match[1];
1064
+ importChain.set(importedModule, id);
1065
+ if (isServerOnlyModule(importedModule)) {
1066
+ if (!detectedServerModules.has(importedModule)) {
1067
+ detectedServerModules.add(importedModule);
1068
+ console.error(
1069
+ `
1070
+ [import-chain-tracker] ⚠️ Server-only import detected in client build!`
1071
+ );
1072
+ console.error(`Module "${importedModule}" imported by: ${shortenPath(id)}`);
1073
+ const chain = buildImportChain(id);
1074
+ chain.push(importedModule);
1075
+ console.error(`Import chain:`);
1076
+ console.error(formatChain(chain));
1077
+ console.error("");
1078
+ }
1079
+ }
1080
+ }
1081
+ return null;
1082
+ },
1083
+ buildEnd() {
1084
+ if (detectedServerModules.size > 0) {
1085
+ console.warn(
1086
+ `
1087
+ [import-chain-tracker] ⚠️ ${detectedServerModules.size} server-only module(s) detected during transform:`
1088
+ );
1089
+ for (const mod of detectedServerModules) {
1090
+ console.warn(` - ${mod}`);
1091
+ }
1092
+ console.warn(
1093
+ "\nNote: These may be stripped by the code-split transform if only used in .withServer()."
1094
+ );
1095
+ console.warn(
1096
+ 'If build fails with "not exported" errors, check the import chains above.\n'
1097
+ );
1098
+ } else if (verbose) {
1099
+ console.log(
1100
+ "[import-chain-tracker] ✅ No server-only modules detected in client build"
1101
+ );
1102
+ }
1103
+ }
1104
+ };
1105
+ }
1106
+ function jayStackCompiler(options = {}) {
1107
+ const { trackImports, ...jayOptions } = options;
1108
+ const moduleCache = /* @__PURE__ */ new Map();
1109
+ const shouldTrackImports = trackImports || process.env.DEBUG_IMPORTS === "1";
1110
+ const trackerOptions = typeof trackImports === "object" ? trackImports : { verbose: process.env.DEBUG_IMPORTS === "1" };
1111
+ const plugins = [];
1112
+ if (shouldTrackImports) {
1113
+ plugins.push(createImportChainTracker(trackerOptions));
1114
+ }
1115
+ plugins.push(
1116
+ // First: Jay Stack code splitting transformation
1117
+ {
1118
+ name: "jay-stack:code-split",
1119
+ enforce: "pre",
1120
+ // Run before jay:runtime
1121
+ transform(code, id, options2) {
1122
+ if (!id.endsWith(".ts") && !id.includes(".ts?")) {
1123
+ return null;
1124
+ }
1125
+ const hasComponent = code.includes("makeJayStackComponent");
1126
+ const hasInit = code.includes("makeJayInit");
1127
+ if (!hasComponent && !hasInit) {
1128
+ return null;
1129
+ }
1130
+ const environment = options2?.ssr ? "server" : "client";
1131
+ try {
1132
+ return transformJayStackBuilder(code, id, environment);
1133
+ } catch (error) {
1134
+ console.error(`[jay-stack:code-split] Error transforming ${id}:`, error);
1135
+ return null;
1136
+ }
1137
+ }
1138
+ },
1139
+ // Second: Action import transformation (client builds only)
1140
+ // Uses resolveId + load to replace action modules with virtual modules
1141
+ // containing createActionCaller calls BEFORE bundling happens.
1142
+ (() => {
1143
+ let isSSRBuild = false;
1144
+ return {
1145
+ name: "jay-stack:action-transform",
1146
+ enforce: "pre",
1147
+ // Track SSR mode from config
1148
+ configResolved(config) {
1149
+ isSSRBuild = config.build?.ssr ?? false;
1150
+ },
1151
+ buildStart() {
1152
+ clearActionMetadataCache();
1153
+ moduleCache.clear();
1154
+ },
1155
+ async resolveId(source, importer, options2) {
1156
+ if (options2?.ssr || isSSRBuild) {
1157
+ return null;
1158
+ }
1159
+ if (!isActionImport(source)) {
1160
+ return null;
1161
+ }
1162
+ if (!source.startsWith(".") || !importer) {
1163
+ return null;
1164
+ }
1165
+ const importerDir = path.dirname(importer);
1166
+ let resolvedPath = path.resolve(importerDir, source);
1167
+ if (!resolvedPath.endsWith(".ts") && !resolvedPath.endsWith(".js")) {
1168
+ if (fs.existsSync(resolvedPath + ".ts")) {
1169
+ resolvedPath += ".ts";
1170
+ } else if (fs.existsSync(resolvedPath + ".js")) {
1171
+ resolvedPath += ".js";
1172
+ } else {
1173
+ return null;
1174
+ }
1175
+ }
1176
+ return `\0jay-action:${resolvedPath}`;
1177
+ },
1178
+ async load(id) {
1179
+ if (!id.startsWith("\0jay-action:")) {
1180
+ return null;
1181
+ }
1182
+ const actualPath = id.slice("\0jay-action:".length);
1183
+ let code;
1184
+ try {
1185
+ code = await fs.promises.readFile(actualPath, "utf-8");
1186
+ } catch (err) {
1187
+ console.error(`[action-transform] Could not read ${actualPath}:`, err);
1188
+ return null;
1189
+ }
1190
+ const actions = extractActionsFromSource(code, actualPath);
1191
+ if (actions.length === 0) {
1192
+ console.warn(`[action-transform] No actions found in ${actualPath}`);
1193
+ return null;
1194
+ }
1195
+ const lines = [
1196
+ `import { createActionCaller } from '@jay-framework/stack-client-runtime';`,
1197
+ ""
1198
+ ];
1199
+ for (const action of actions) {
1200
+ lines.push(
1201
+ `export const ${action.exportName} = createActionCaller('${action.actionName}', '${action.method}');`
1202
+ );
1203
+ }
1204
+ if (code.includes("ActionError")) {
1205
+ lines.push(
1206
+ `export { ActionError } from '@jay-framework/stack-client-runtime';`
1207
+ );
1208
+ }
1209
+ const result = lines.join("\n");
1210
+ return result;
1211
+ }
1212
+ };
1213
+ })(),
1214
+ // Third: Jay runtime compilation (existing plugin)
1215
+ jayRuntime(jayOptions)
1216
+ );
1217
+ return plugins;
1218
+ }
15
1219
  class ServiceLifecycleManager {
16
1220
  constructor(projectRoot, sourceBase = "src") {
17
- __publicField(this, "initFilePath", null);
1221
+ /** Path to project's lib/init.ts (makeJayInit pattern) */
1222
+ __publicField(this, "projectInitFilePath", null);
18
1223
  __publicField(this, "isInitialized", false);
19
1224
  __publicField(this, "viteServer", null);
1225
+ __publicField(this, "pluginsWithInit", []);
20
1226
  this.projectRoot = projectRoot;
21
1227
  this.sourceBase = sourceBase;
22
1228
  }
@@ -27,14 +1233,13 @@ class ServiceLifecycleManager {
27
1233
  this.viteServer = viteServer;
28
1234
  }
29
1235
  /**
30
- * Finds the jay.init.ts (or .js) file in the source directory.
31
- * Looks in: {projectRoot}/{sourceBase}/jay.init.{ts,js,mts,mjs}
1236
+ * Finds the project init file using makeJayInit pattern.
1237
+ * Looks in: {projectRoot}/{sourceBase}/init.{ts,js}
32
1238
  */
33
- findInitFile() {
34
- const extensions = [".ts", ".js", ".mts", ".mjs"];
35
- const baseFilename = "jay.init";
1239
+ findProjectInitFile() {
1240
+ const extensions = [".ts", ".js"];
36
1241
  for (const ext of extensions) {
37
- const filePath = path.join(this.projectRoot, this.sourceBase, baseFilename + ext);
1242
+ const filePath = path.join(this.projectRoot, this.sourceBase, "init" + ext);
38
1243
  if (fs.existsSync(filePath)) {
39
1244
  return filePath;
40
1245
  }
@@ -42,32 +1247,88 @@ class ServiceLifecycleManager {
42
1247
  return null;
43
1248
  }
44
1249
  /**
45
- * Initializes services by loading and executing jay.init.ts
1250
+ * Initializes services by:
1251
+ * 1. Discovering and executing plugin server inits (in dependency order)
1252
+ * 2. Loading and executing project lib/init.ts
1253
+ * 3. Running all registered onInit callbacks
1254
+ * 4. Auto-discovering and registering actions
46
1255
  */
47
1256
  async initialize() {
48
1257
  if (this.isInitialized) {
49
1258
  console.warn("[Services] Already initialized, skipping...");
50
1259
  return;
51
1260
  }
52
- this.initFilePath = this.findInitFile();
53
- if (!this.initFilePath) {
54
- console.log("[Services] No jay.init.ts found in src/, skipping service initialization");
55
- return;
1261
+ this.projectInitFilePath = this.findProjectInitFile();
1262
+ const discoveredPlugins = await discoverPluginsWithInit({
1263
+ projectRoot: this.projectRoot,
1264
+ verbose: true
1265
+ });
1266
+ this.pluginsWithInit = sortPluginsByDependencies(discoveredPlugins);
1267
+ if (this.pluginsWithInit.length > 0) {
1268
+ console.log(
1269
+ `[Services] Found ${this.pluginsWithInit.length} plugin(s) with init: ${this.pluginsWithInit.map((p) => p.name).join(", ")}`
1270
+ );
56
1271
  }
57
- console.log(`[Services] Loading initialization file: ${this.initFilePath}`);
58
- try {
59
- if (this.viteServer) {
60
- await this.viteServer.ssrLoadModule(this.initFilePath);
61
- } else {
62
- const fileUrl = pathToFileURL(this.initFilePath).href;
63
- await import(fileUrl);
1272
+ await executePluginServerInits(this.pluginsWithInit, this.viteServer ?? void 0, true);
1273
+ if (this.projectInitFilePath) {
1274
+ console.log("[DevServer] Loading project init: src/init.ts");
1275
+ try {
1276
+ if (this.viteServer) {
1277
+ const module = await this.viteServer.ssrLoadModule(this.projectInitFilePath);
1278
+ if (module.init?._serverInit) {
1279
+ console.log("[DevServer] Running server init: project");
1280
+ const { setClientInitData } = await import("@jay-framework/stack-server-runtime");
1281
+ const clientData = await module.init._serverInit();
1282
+ if (clientData !== void 0 && clientData !== null) {
1283
+ setClientInitData("project", clientData);
1284
+ }
1285
+ }
1286
+ } else {
1287
+ const fileUrl = pathToFileURL(this.projectInitFilePath).href;
1288
+ await import(fileUrl);
1289
+ }
1290
+ } catch (error) {
1291
+ console.error("[Services] Failed to load project init:", error);
1292
+ throw error;
64
1293
  }
65
- await runInitCallbacks();
66
- this.isInitialized = true;
67
- console.log("[Services] Initialization complete");
1294
+ } else {
1295
+ console.log("[Services] No init.ts found, skipping project initialization");
1296
+ }
1297
+ await runInitCallbacks();
1298
+ console.log("[Services] Initialization complete");
1299
+ await this.discoverActions();
1300
+ this.isInitialized = true;
1301
+ }
1302
+ /**
1303
+ * Auto-discovers and registers actions from project and plugins.
1304
+ */
1305
+ async discoverActions() {
1306
+ let totalActions = 0;
1307
+ try {
1308
+ const result = await discoverAndRegisterActions({
1309
+ projectRoot: this.projectRoot,
1310
+ actionsDir: path.join(this.sourceBase, "actions"),
1311
+ registry: actionRegistry,
1312
+ verbose: true,
1313
+ viteServer: this.viteServer ?? void 0
1314
+ });
1315
+ totalActions += result.actionCount;
68
1316
  } catch (error) {
69
- console.error("[Services] Failed to initialize:", error);
70
- throw error;
1317
+ console.error("[Actions] Failed to auto-discover project actions:", error);
1318
+ }
1319
+ try {
1320
+ const pluginActions = await discoverAllPluginActions({
1321
+ projectRoot: this.projectRoot,
1322
+ registry: actionRegistry,
1323
+ verbose: true,
1324
+ viteServer: this.viteServer ?? void 0
1325
+ });
1326
+ totalActions += pluginActions.length;
1327
+ } catch (error) {
1328
+ console.error("[Actions] Failed to auto-discover plugin actions:", error);
1329
+ }
1330
+ if (totalActions > 0) {
1331
+ console.log(`[Actions] Auto-registered ${totalActions} action(s) total`);
71
1332
  }
72
1333
  }
73
1334
  /**
@@ -100,31 +1361,48 @@ class ServiceLifecycleManager {
100
1361
  * Hot reload: shutdown, clear caches, re-import, and re-initialize
101
1362
  */
102
1363
  async reload() {
103
- if (!this.initFilePath) {
104
- console.log("[Services] No init file to reload");
105
- return;
106
- }
107
1364
  console.log("[Services] Reloading services...");
108
1365
  await this.shutdown();
109
1366
  clearLifecycleCallbacks();
110
1367
  clearServiceRegistry();
111
- if (this.viteServer) {
112
- const moduleNode = this.viteServer.moduleGraph.getModuleById(this.initFilePath);
1368
+ clearClientInitData();
1369
+ actionRegistry.clear();
1370
+ if (this.projectInitFilePath && this.viteServer) {
1371
+ const moduleNode = this.viteServer.moduleGraph.getModuleById(this.projectInitFilePath);
113
1372
  if (moduleNode) {
114
1373
  await this.viteServer.moduleGraph.invalidateModule(moduleNode);
115
1374
  }
116
- } else {
117
- delete require.cache[require.resolve(this.initFilePath)];
1375
+ } else if (this.projectInitFilePath) {
1376
+ delete require.cache[require.resolve(this.projectInitFilePath)];
118
1377
  }
119
1378
  this.isInitialized = false;
120
1379
  await this.initialize();
121
1380
  console.log("[Services] Reload complete");
122
1381
  }
123
1382
  /**
124
- * Returns the path to the init file if found
1383
+ * Returns the path to the init file if found.
125
1384
  */
126
1385
  getInitFilePath() {
127
- return this.initFilePath;
1386
+ return this.projectInitFilePath;
1387
+ }
1388
+ /**
1389
+ * Returns project init info for client-side embedding.
1390
+ */
1391
+ getProjectInit() {
1392
+ if (!this.projectInitFilePath) {
1393
+ return null;
1394
+ }
1395
+ return {
1396
+ importPath: this.projectInitFilePath,
1397
+ initExport: "init"
1398
+ };
1399
+ }
1400
+ /**
1401
+ * Returns the discovered plugins with init configurations.
1402
+ * Sorted by dependencies (plugins with no deps first).
1403
+ */
1404
+ getPluginsWithInit() {
1405
+ return this.pluginsWithInit;
128
1406
  }
129
1407
  /**
130
1408
  * Checks if services are initialized
@@ -133,6 +1411,212 @@ class ServiceLifecycleManager {
133
1411
  return this.isInitialized;
134
1412
  }
135
1413
  }
1414
+ function deepMergeViewStates(base, overlay, trackByMap, path2 = "") {
1415
+ if (!base && !overlay)
1416
+ return {};
1417
+ if (!base)
1418
+ return overlay || {};
1419
+ if (!overlay)
1420
+ return base || {};
1421
+ const result = {};
1422
+ const allKeys = /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(overlay)]);
1423
+ for (const key of allKeys) {
1424
+ const baseValue = base[key];
1425
+ const overlayValue = overlay[key];
1426
+ const currentPath = path2 ? `${path2}.${key}` : key;
1427
+ if (overlayValue === void 0) {
1428
+ result[key] = baseValue;
1429
+ } else if (baseValue === void 0) {
1430
+ result[key] = overlayValue;
1431
+ } else if (Array.isArray(baseValue) && Array.isArray(overlayValue)) {
1432
+ const trackByField = trackByMap[currentPath];
1433
+ if (trackByField) {
1434
+ result[key] = mergeArraysByTrackBy(
1435
+ baseValue,
1436
+ overlayValue,
1437
+ trackByField,
1438
+ trackByMap,
1439
+ currentPath
1440
+ );
1441
+ } else {
1442
+ result[key] = overlayValue;
1443
+ }
1444
+ } else if (typeof baseValue === "object" && baseValue !== null && typeof overlayValue === "object" && overlayValue !== null && !Array.isArray(baseValue) && !Array.isArray(overlayValue)) {
1445
+ result[key] = deepMergeViewStates(baseValue, overlayValue, trackByMap, currentPath);
1446
+ } else {
1447
+ result[key] = overlayValue;
1448
+ }
1449
+ }
1450
+ return result;
1451
+ }
1452
+ function mergeArraysByTrackBy(baseArray, overlayArray, trackByField, trackByMap, arrayPath) {
1453
+ const baseByKey = /* @__PURE__ */ new Map();
1454
+ for (const item of baseArray) {
1455
+ const key = item[trackByField];
1456
+ if (key !== void 0 && key !== null) {
1457
+ if (baseByKey.has(key)) {
1458
+ console.warn(
1459
+ `Duplicate trackBy key [${key}] in base array at path [${arrayPath}]. This may cause incorrect merging.`
1460
+ );
1461
+ }
1462
+ baseByKey.set(key, item);
1463
+ }
1464
+ }
1465
+ const overlayByKey = /* @__PURE__ */ new Map();
1466
+ for (const item of overlayArray) {
1467
+ const key = item[trackByField];
1468
+ if (key !== void 0 && key !== null) {
1469
+ overlayByKey.set(key, item);
1470
+ }
1471
+ }
1472
+ return baseArray.map((baseItem) => {
1473
+ const key = baseItem[trackByField];
1474
+ if (key === void 0 || key === null) {
1475
+ return baseItem;
1476
+ }
1477
+ const overlayItem = overlayByKey.get(key);
1478
+ if (overlayItem) {
1479
+ return deepMergeViewStates(baseItem, overlayItem, trackByMap, arrayPath);
1480
+ } else {
1481
+ return baseItem;
1482
+ }
1483
+ });
1484
+ }
1485
+ const ACTION_ENDPOINT_BASE = "/_jay/actions";
1486
+ function createActionRouter(options) {
1487
+ const registry = options?.registry ?? actionRegistry;
1488
+ return async (req, res) => {
1489
+ const actionName = req.path.slice(1);
1490
+ if (!actionName) {
1491
+ res.status(400).json({
1492
+ success: false,
1493
+ error: {
1494
+ code: "MISSING_ACTION_NAME",
1495
+ message: "Action name is required",
1496
+ isActionError: false
1497
+ }
1498
+ });
1499
+ return;
1500
+ }
1501
+ const action = registry.get(actionName);
1502
+ if (!action) {
1503
+ res.status(404).json({
1504
+ success: false,
1505
+ error: {
1506
+ code: "ACTION_NOT_FOUND",
1507
+ message: `Action '${actionName}' is not registered`,
1508
+ isActionError: false
1509
+ }
1510
+ });
1511
+ return;
1512
+ }
1513
+ const requestMethod = req.method.toUpperCase();
1514
+ if (requestMethod !== action.method) {
1515
+ res.status(405).json({
1516
+ success: false,
1517
+ error: {
1518
+ code: "METHOD_NOT_ALLOWED",
1519
+ message: `Action '${actionName}' expects ${action.method}, got ${requestMethod}`,
1520
+ isActionError: false
1521
+ }
1522
+ });
1523
+ return;
1524
+ }
1525
+ let input;
1526
+ try {
1527
+ if (requestMethod === "GET") {
1528
+ if (req.query._input) {
1529
+ input = JSON.parse(req.query._input);
1530
+ } else {
1531
+ input = { ...req.query };
1532
+ delete input._input;
1533
+ }
1534
+ } else {
1535
+ input = req.body;
1536
+ }
1537
+ } catch (parseError) {
1538
+ res.status(400).json({
1539
+ success: false,
1540
+ error: {
1541
+ code: "INVALID_INPUT",
1542
+ message: "Failed to parse request input",
1543
+ isActionError: false
1544
+ }
1545
+ });
1546
+ return;
1547
+ }
1548
+ const result = await registry.execute(actionName, input);
1549
+ if (requestMethod === "GET" && result.success) {
1550
+ const cacheHeaders = registry.getCacheHeaders(actionName);
1551
+ if (cacheHeaders) {
1552
+ res.set("Cache-Control", cacheHeaders);
1553
+ }
1554
+ }
1555
+ if (result.success) {
1556
+ res.status(200).json({
1557
+ success: true,
1558
+ data: result.data
1559
+ });
1560
+ } else {
1561
+ const statusCode = getStatusCodeForError(result.error.code, result.error.isActionError);
1562
+ res.status(statusCode).json({
1563
+ success: false,
1564
+ error: result.error
1565
+ });
1566
+ }
1567
+ };
1568
+ }
1569
+ function getStatusCodeForError(code, isActionError) {
1570
+ if (isActionError) {
1571
+ return 422;
1572
+ }
1573
+ switch (code) {
1574
+ case "ACTION_NOT_FOUND":
1575
+ return 404;
1576
+ case "INVALID_INPUT":
1577
+ case "VALIDATION_ERROR":
1578
+ return 400;
1579
+ case "UNAUTHORIZED":
1580
+ return 401;
1581
+ case "FORBIDDEN":
1582
+ return 403;
1583
+ case "INTERNAL_ERROR":
1584
+ default:
1585
+ return 500;
1586
+ }
1587
+ }
1588
+ function actionBodyParser() {
1589
+ return (req, res, next) => {
1590
+ if (!req.path.startsWith(ACTION_ENDPOINT_BASE)) {
1591
+ next();
1592
+ return;
1593
+ }
1594
+ if (req.method === "GET") {
1595
+ next();
1596
+ return;
1597
+ }
1598
+ let body = "";
1599
+ req.setEncoding("utf8");
1600
+ req.on("data", (chunk) => {
1601
+ body += chunk;
1602
+ });
1603
+ req.on("end", () => {
1604
+ try {
1605
+ req.body = body ? JSON.parse(body) : {};
1606
+ next();
1607
+ } catch (e2) {
1608
+ res.status(400).json({
1609
+ success: false,
1610
+ error: {
1611
+ code: "INVALID_JSON",
1612
+ message: "Invalid JSON in request body",
1613
+ isActionError: false
1614
+ }
1615
+ });
1616
+ }
1617
+ });
1618
+ };
1619
+ }
136
1620
  async function initRoutes(pagesBaseFolder) {
137
1621
  return await scanRoutes(pagesBaseFolder, {
138
1622
  jayHtmlFilename: "page.jay-html",
@@ -142,7 +1626,10 @@ async function initRoutes(pagesBaseFolder) {
142
1626
  function defaults(options) {
143
1627
  const publicBaseUrlPath = options.publicBaseUrlPath || process.env.BASE || "/";
144
1628
  const projectRootFolder = options.projectRootFolder || ".";
145
- const pagesRootFolder = path__default.resolve(projectRootFolder, options.pagesRootFolder || "./src/pages");
1629
+ const pagesRootFolder = path__default.resolve(
1630
+ projectRootFolder,
1631
+ options.pagesRootFolder || "./src/pages"
1632
+ );
146
1633
  const tsConfigFilePath = options.jayRollupConfig.tsConfigFilePath || path__default.resolve(projectRootFolder, "./tsconfig.json");
147
1634
  return {
148
1635
  publicBaseUrlPath,
@@ -163,7 +1650,7 @@ function handleOtherResponseCodes(res, renderedResult) {
163
1650
  else
164
1651
  res.status(renderedResult.status).end("redirect to " + renderedResult.location);
165
1652
  }
166
- function mkRoute(route, vite, slowlyPhase, options) {
1653
+ function mkRoute(route, vite, slowlyPhase, options, projectInit, allPluginsWithInit = [], allPluginClientInits = []) {
167
1654
  const path2 = routeToExpressRoute(route);
168
1655
  const handler = async (req, res) => {
169
1656
  try {
@@ -174,33 +1661,56 @@ function mkRoute(route, vite, slowlyPhase, options) {
174
1661
  url
175
1662
  };
176
1663
  let viewState, carryForward;
177
- const pageParts = await loadPageParts(
1664
+ const pagePartsResult = await loadPageParts(
178
1665
  vite,
179
1666
  route,
180
1667
  options.pagesRootFolder,
1668
+ options.projectRootFolder,
181
1669
  options.jayRollupConfig
182
1670
  );
183
- if (pageParts.val) {
1671
+ if (pagePartsResult.val) {
1672
+ const {
1673
+ parts: pageParts,
1674
+ serverTrackByMap,
1675
+ clientTrackByMap,
1676
+ usedPackages
1677
+ } = pagePartsResult.val;
1678
+ const pluginsForPage = allPluginClientInits.filter((plugin) => {
1679
+ const pluginInfo = allPluginsWithInit.find((p) => p.name === plugin.name);
1680
+ return pluginInfo && usedPackages.has(pluginInfo.packageName);
1681
+ });
184
1682
  const renderedSlowly = await slowlyPhase.runSlowlyForPage(
185
1683
  pageParams,
186
1684
  pageProps,
187
- pageParts.val
1685
+ pageParts
188
1686
  );
189
- if (renderedSlowly.kind === "PartialRender") {
1687
+ if (renderedSlowly.kind === "PhaseOutput") {
190
1688
  const renderedFast = await renderFastChangingData(
191
1689
  pageParams,
192
1690
  pageProps,
193
1691
  renderedSlowly.carryForward,
194
- pageParts.val
1692
+ pageParts
195
1693
  );
196
- if (renderedFast.kind === "PartialRender") {
197
- viewState = { ...renderedSlowly.rendered, ...renderedFast.rendered };
1694
+ if (renderedFast.kind === "PhaseOutput") {
1695
+ if (serverTrackByMap && Object.keys(serverTrackByMap).length > 0) {
1696
+ viewState = deepMergeViewStates(
1697
+ renderedSlowly.rendered,
1698
+ renderedFast.rendered,
1699
+ serverTrackByMap
1700
+ );
1701
+ } else {
1702
+ viewState = { ...renderedSlowly.rendered, ...renderedFast.rendered };
1703
+ }
198
1704
  carryForward = renderedFast.carryForward;
199
1705
  const pageHtml = generateClientScript(
200
1706
  viewState,
201
1707
  carryForward,
202
- pageParts.val,
203
- route.jayHtmlPath
1708
+ pageParts,
1709
+ route.jayHtmlPath,
1710
+ clientTrackByMap,
1711
+ getClientInitData(),
1712
+ projectInit,
1713
+ pluginsForPage
204
1714
  );
205
1715
  const compiledPageHtml = await vite.transformIndexHtml(
206
1716
  !!url ? url : "/",
@@ -214,40 +1724,50 @@ function mkRoute(route, vite, slowlyPhase, options) {
214
1724
  handleOtherResponseCodes(res, renderedSlowly);
215
1725
  }
216
1726
  } else {
217
- console.log(pageParts.validations.join("\n"));
218
- res.status(500).end(pageParts.validations.join("\n"));
1727
+ console.log(pagePartsResult.validations.join("\n"));
1728
+ res.status(500).end(pagePartsResult.validations.join("\n"));
219
1729
  }
220
- } catch (e) {
221
- vite?.ssrFixStacktrace(e);
222
- console.log(e.stack);
223
- res.status(500).end(e.stack);
1730
+ } catch (e2) {
1731
+ vite?.ssrFixStacktrace(e2);
1732
+ console.log(e2.stack);
1733
+ res.status(500).end(e2.stack);
224
1734
  }
225
1735
  };
226
1736
  return { path: path2, handler, fsRoute: route };
227
1737
  }
228
1738
  async function mkDevServer(options) {
229
- const { publicBaseUrlPath, pagesRootFolder, projectRootFolder, jayRollupConfig, dontCacheSlowly } = defaults(options);
1739
+ const {
1740
+ publicBaseUrlPath,
1741
+ pagesRootFolder,
1742
+ projectRootFolder,
1743
+ jayRollupConfig,
1744
+ dontCacheSlowly
1745
+ } = defaults(options);
230
1746
  const lifecycleManager = new ServiceLifecycleManager(projectRootFolder);
231
1747
  setupGracefulShutdown(lifecycleManager);
232
1748
  const vite = await createServer({
233
1749
  server: { middlewareMode: true },
234
- plugins: [jayRuntime(jayRollupConfig)],
1750
+ plugins: [...jayStackCompiler(jayRollupConfig)],
235
1751
  appType: "custom",
236
1752
  base: publicBaseUrlPath,
237
1753
  root: pagesRootFolder,
238
1754
  ssr: {
239
1755
  // Mark stack-server-runtime as external so Vite uses Node's require
240
- // This ensures jay.init.ts and dev-server share the same module instance
1756
+ // This ensures lib/init.ts and dev-server share the same module instance
241
1757
  external: ["@jay-framework/stack-server-runtime"]
242
1758
  }
243
1759
  });
244
1760
  lifecycleManager.setViteServer(vite);
245
1761
  await lifecycleManager.initialize();
246
1762
  setupServiceHotReload(vite, lifecycleManager);
1763
+ setupActionRouter(vite);
247
1764
  const routes = await initRoutes(pagesRootFolder);
248
1765
  const slowlyPhase = new DevSlowlyChangingPhase(dontCacheSlowly);
1766
+ const projectInit = lifecycleManager.getProjectInit() ?? void 0;
1767
+ const pluginsWithInit = lifecycleManager.getPluginsWithInit();
1768
+ const pluginClientInits = preparePluginClientInits(pluginsWithInit);
249
1769
  const devServerRoutes = routes.map(
250
- (route) => mkRoute(route, vite, slowlyPhase, options)
1770
+ (route) => mkRoute(route, vite, slowlyPhase, options, projectInit, pluginsWithInit, pluginClientInits)
251
1771
  );
252
1772
  return {
253
1773
  server: vite.middlewares,
@@ -274,7 +1794,7 @@ function setupServiceHotReload(vite, lifecycleManager) {
274
1794
  vite.watcher.add(initFilePath);
275
1795
  vite.watcher.on("change", async (changedPath) => {
276
1796
  if (changedPath === initFilePath) {
277
- console.log("[Services] jay.init.ts changed, reloading services...");
1797
+ console.log("[Services] lib/init.ts changed, reloading services...");
278
1798
  try {
279
1799
  await lifecycleManager.reload();
280
1800
  vite.ws.send({
@@ -287,6 +1807,14 @@ function setupServiceHotReload(vite, lifecycleManager) {
287
1807
  }
288
1808
  });
289
1809
  }
1810
+ function setupActionRouter(vite) {
1811
+ vite.middlewares.use(actionBodyParser());
1812
+ vite.middlewares.use(ACTION_ENDPOINT_BASE, createActionRouter());
1813
+ console.log(`[Actions] Action router mounted at ${ACTION_ENDPOINT_BASE}`);
1814
+ }
290
1815
  export {
1816
+ ACTION_ENDPOINT_BASE,
1817
+ actionBodyParser,
1818
+ createActionRouter,
291
1819
  mkDevServer
292
1820
  };