@finesoft/front 0.5.3 → 0.5.4

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 (51) hide show
  1. package/dist/{browser-kFMjlLGT.d.mts → browser-CrZcKSpr.d.mts} +4 -4
  2. package/dist/{browser-CR5vhgXg.mjs → browser-D6If-9VF.mjs} +2 -2
  3. package/dist/browser.d.mts +6 -6
  4. package/dist/browser.mjs +4 -4
  5. package/dist/{cookies-BiRlUanX.d.mts → cookies-BXCwZoS_.d.mts} +4 -4
  6. package/dist/{fetch-policy-BHT8RtrL.mjs → fetch-policy-Cxk7F-Cu.mjs} +2 -2
  7. package/dist/{host-guard-DDWxLpFL.mjs → host-guard-DX4e3-45.mjs} +5 -1
  8. package/dist/{http-B6CJqDyf.d.mts → http-B8r7MWao.d.mts} +2 -2
  9. package/dist/http.d.mts +1 -1
  10. package/dist/index-node.d.mts +11 -11
  11. package/dist/index-node.mjs +7 -7
  12. package/dist/index.d.mts +15 -15
  13. package/dist/index.mjs +6 -6
  14. package/dist/{native-contract-DuR25hYB.d.mts → native-contract-B_2pn5ST.d.mts} +2 -2
  15. package/dist/native-contract.d.mts +1 -1
  16. package/dist/{node-D9hB4dsz.d.mts → node-DbGzbatF.d.mts} +4 -4
  17. package/dist/node.d.mts +1 -1
  18. package/dist/node.mjs +1 -1
  19. package/dist/{path-CXT6xGPO.d.mts → path-C-b1ChKq.d.mts} +6 -1
  20. package/dist/portable.d.mts +8 -8
  21. package/dist/portable.mjs +6 -6
  22. package/dist/{proxy-z02VvGIj.mjs → proxy-BkKVP3Ha.mjs} +2 -2
  23. package/dist/{react-DhwBRw01.d.mts → react-C8AnI9sx.d.mts} +2 -2
  24. package/dist/react.d.mts +2 -2
  25. package/dist/{secure-fetch-Xlht2jd7.d.mts → secure-fetch--QwYr5uJ.d.mts} +5 -1
  26. package/dist/{server-controller-proxy-BkVuVWVD.d.mts → server-controller-proxy-CFdU_nGs.d.mts} +3 -3
  27. package/dist/{session-DnB4ZC3x.d.mts → session-BR1vd3yY.d.mts} +3 -3
  28. package/dist/{src-BQBfaaPO.mjs → src-D_Bzbr80.mjs} +16 -7
  29. package/dist/{ssr-Tn4YkuxM.mjs → ssr-DqwPGqOd.mjs} +3 -3
  30. package/dist/{ssr-BLzYP4wU.d.mts → ssr-lzuGJ2ux.d.mts} +6 -6
  31. package/dist/ssr.d.mts +1 -1
  32. package/dist/ssr.mjs +2 -2
  33. package/dist/{svelte-Dr5to3SE.d.mts → svelte-CMFFDLBi.d.mts} +2 -2
  34. package/dist/svelte.d.mts +2 -2
  35. package/dist/{types-B1BT0N3t.mjs → types-DD09OkGZ.mjs} +3 -3
  36. package/dist/{vite-Cj4SPA8D.d.mts → vite-BjWtbemg.d.mts} +10 -4
  37. package/dist/vite.d.mts +2 -2
  38. package/dist/vite.mjs +218 -44
  39. package/dist/{vue-DGmzuKho.d.mts → vue-DsZIYP1c.d.mts} +2 -2
  40. package/dist/vue.d.mts +2 -2
  41. package/dist/web.d.mts +5 -5
  42. package/dist/web.mjs +3 -3
  43. package/dist/worker.d.mts +1 -1
  44. package/docs/02-routing-and-controllers.md +2 -0
  45. package/docs/06-http-client.md +4 -0
  46. package/docs/09-server-and-deployment.md +13 -0
  47. package/docs/11-navigation.md +2 -0
  48. package/docs/advanced/multi-tenant-scopes.md +24 -0
  49. package/docs/zh/02-routing-and-controllers.md +2 -0
  50. package/docs/zh/09-server-and-deployment.md +13 -0
  51. package/package.json +22 -22
package/dist/vite.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { a as isPublicSSRResult, i as matchRenderModeOverride, n as registerProxyRoutes, r as createSSRHandler, t as generateProxyCode } from "./proxy-z02VvGIj.mjs";
2
- import { r as dynamicImport, t as nodeSafeFetchOptions } from "./fetch-policy-BHT8RtrL.mjs";
1
+ import { a as isPublicSSRResult, i as matchRenderModeOverride, n as registerProxyRoutes, r as createSSRHandler, t as generateProxyCode } from "./proxy-BkKVP3Ha.mjs";
2
+ import { r as dynamicImport, t as nodeSafeFetchOptions } from "./fetch-policy-Cxk7F-Cu.mjs";
3
3
  import { n as createControllerTypeWatcher, r as generateControllerTypes, t as generateFrontTypes } from "./public-types-BcJM-AYc.mjs";
4
4
  import { createRequire } from "node:module";
5
5
  import fs from "node:fs";
@@ -85,8 +85,8 @@ const RENDER_MODES = ${JSON.stringify(ctx.renderModes ?? {})};
85
85
  const DEFAULT_LOCALE = ${JSON.stringify(ctx.defaultLocale ?? null)};
86
86
  ${opts.platformCache ?? ""}
87
87
  const app = new Hono();
88
- ${generateProxyCode(ctx.proxies ?? [])}
89
88
  ${ctx.setupPath ? "await _setupDefault(app);" : ""}
89
+ ${generateProxyCode(ctx.proxies ?? [])}
90
90
  ${opts.platformMiddleware ?? ""}
91
91
  const ssr = createSSRHandler({
92
92
  ownRenderers: true,
@@ -95,7 +95,7 @@ const ssr = createSSRHandler({
95
95
  serializeServerData,
96
96
  renderModes: RENDER_MODES,
97
97
  defaultLocale: DEFAULT_LOCALE,
98
- safeFetch: ${opts.dnsPolicy === "hostname" ? "{ validateDns: false }" : "_safeFetchOptions"},
98
+ safeFetch: ${opts.dnsPolicy === "hostname" ? JSON.stringify({ trustedOrigins: opts.trustedOrigins ?? [] }) : "_safeFetchOptions"},
99
99
  fetch: (request, bindings) => app.fetch(request, bindings),
100
100
  ${opts.platformCache ? "cache: {get: platformCacheGet, set: platformCacheSet}," : ""}
101
101
  ${opts.publicCacheHeaders ? `publicCacheHeaders: ${JSON.stringify(opts.publicCacheHeaders)},` : ""}
@@ -224,7 +224,12 @@ async function prerenderRoutes(ctx) {
224
224
  * 注意:Cloudflare Workers 不支持原生 Node.js API。
225
225
  * 若 setup 代理使用了 process.env,需在 wrangler.toml 启用 nodejs_compat。
226
226
  */
227
- function cloudflareAdapter() {
227
+ function cloudflareAdapter(options = {}) {
228
+ const trustedOrigins = [...options.trustedOrigins ?? []];
229
+ for (const origin of trustedOrigins) {
230
+ const parsed = new URL(origin);
231
+ if (parsed.origin !== origin || !["https:", "http:"].includes(parsed.protocol)) throw new TypeError(`Expected an exact HTTP(S) origin: ${origin}`);
232
+ }
228
233
  return {
229
234
  name: "cloudflare",
230
235
  async build(ctx) {
@@ -236,6 +241,7 @@ function cloudflareAdapter() {
236
241
  });
237
242
  await buildGeneratedEntry(ctx, ".cf-entry.tmp.mjs", generateSSREntry(ctx, {
238
243
  dnsPolicy: "hostname",
244
+ trustedOrigins,
239
245
  platformImport: ``,
240
246
  platformExport: `export default app;`,
241
247
  platformCache: `
@@ -334,7 +340,7 @@ function nodeAdapter() {
334
340
  await buildGeneratedEntry(ctx, ".node-entry.tmp.mjs", generateSSREntry(ctx, {
335
341
  platformImport: `import { startNodeHandler } from "@finesoft/front";
336
342
  import { readFileSync, existsSync } from "node:fs";
337
- import { resolve, dirname } from "node:path";
343
+ import { resolve, dirname, relative, isAbsolute, sep } from "node:path";
338
344
  import { fileURLToPath } from "node:url";`,
339
345
  platformMiddleware: `
340
346
  // 预渲染文件中间件:检查 dist/prerender/ 下是否有对应的静态 HTML
@@ -349,6 +355,8 @@ app.use("*", async (c, next) => {
349
355
  ];
350
356
  if (urlPath === "/") candidates.unshift(resolve(prerenderDir, "index.html"));
351
357
  for (const f of candidates) {
358
+ const contained = relative(prerenderDir, f);
359
+ if (contained === ".." || contained.startsWith(".." + sep) || isAbsolute(contained)) continue;
352
360
  if (existsSync(f)) {
353
361
  const html = readFileSync(f, "utf-8");
354
362
  return c.html(html);
@@ -604,7 +612,99 @@ function serverControllerModules(getRoot) {
604
612
  return source;
605
613
  };
606
614
  const has = (node, kind) => getCompiler().canHaveModifiers(node) && getCompiler().getModifiers(node)?.some((m) => m.kind === kind);
607
- async function exported(context, specifier, importer, name, seen) {
615
+ function unwrap(node) {
616
+ const c = getCompiler();
617
+ while (c.isParenthesizedExpression(node) || c.isAsExpression(node) || c.isTypeAssertionExpression(node) || c.isNonNullExpression(node) || c.isSatisfiesExpression(node)) node = node.expression;
618
+ return node;
619
+ }
620
+ function propertyName(node) {
621
+ const c = getCompiler();
622
+ if (c.isIdentifier(node) || c.isStringLiteralLike(node) || c.isNumericLiteral(node)) return node.text;
623
+ return c.isComputedPropertyName(node) && c.isStringLiteralLike(unwrap(node.expression)) ? unwrap(node.expression).text : void 0;
624
+ }
625
+ function bindingPath(node, name) {
626
+ const c = getCompiler();
627
+ if (c.isIdentifier(node)) return node.text === name ? [] : void 0;
628
+ for (let index = 0; index < node.elements.length; index++) {
629
+ const item = node.elements[index];
630
+ if (!c.isBindingElement(item) || item.dotDotDotToken) continue;
631
+ const nested = bindingPath(item.name, name);
632
+ const member = c.isArrayBindingPattern(node) ? String(index) : item.propertyName ? propertyName(item.propertyName) : c.isIdentifier(item.name) ? item.name.text : void 0;
633
+ if (nested && member !== void 0) return [member, ...nested];
634
+ }
635
+ }
636
+ async function exportedMember(context, specifier, importer, name, members, seen, baseOnly) {
637
+ if (!members.length) return exported(context, specifier, importer, name, seen, baseOnly);
638
+ if (specifier.startsWith("@finesoft/")) return false;
639
+ const file = (await context.resolve(specifier, importer, { skipSelf: true }))?.id.split("?")[0];
640
+ if (!file) return false;
641
+ const key = `${file}:export-member:${JSON.stringify([name, ...members])}`;
642
+ if (seen.has(key)) return false;
643
+ seen.add(key);
644
+ const source = read(file);
645
+ if (!source) return false;
646
+ context.addWatchFile(file);
647
+ const c = getCompiler();
648
+ for (const statement of source.statements) {
649
+ if (c.isExportDeclaration(statement) && !statement.isTypeOnly) {
650
+ const module = statement.moduleSpecifier && c.isStringLiteral(statement.moduleSpecifier) ? statement.moduleSpecifier.text : void 0;
651
+ const clause = statement.exportClause;
652
+ if (module && clause && c.isNamespaceExport(clause) && clause.name.text === name) return exportedMember(context, module, file, members[0], members.slice(1), seen, baseOnly);
653
+ if (module && !clause && await exportedMember(context, module, file, name, members, new Set(seen), baseOnly)) return true;
654
+ if (clause && c.isNamedExports(clause)) for (const item of clause.elements) {
655
+ if (item.isTypeOnly || item.name.text !== name) continue;
656
+ const local = item.propertyName ?? item.name;
657
+ return module ? exportedMember(context, module, file, local.text, members, seen, baseOnly) : memberExpression(context, source, local, members, seen, baseOnly);
658
+ }
659
+ }
660
+ if (c.isVariableStatement(statement) && has(statement, c.SyntaxKind.ExportKeyword)) {
661
+ for (const item of statement.declarationList.declarations) if (c.isIdentifier(item.name) && item.name.text === name && item.initializer) return memberExpression(context, source, item.initializer, members, seen, baseOnly);
662
+ }
663
+ if (name === "default" && c.isExportAssignment(statement)) return memberExpression(context, source, statement.expression, members, seen, baseOnly);
664
+ }
665
+ return false;
666
+ }
667
+ async function memberExpression(context, source, node, members, seen, baseOnly) {
668
+ if (!members.length) return expression(context, source, node, seen, baseOnly);
669
+ node = unwrap(node);
670
+ const key = `${source.fileName}:member:${node.pos}:${node.end}:${JSON.stringify(members)}`;
671
+ if (seen.has(key)) return false;
672
+ seen.add(key);
673
+ const c = getCompiler();
674
+ if (c.isPropertyAccessExpression(node)) return memberExpression(context, source, node.expression, [node.name.text, ...members], seen, baseOnly);
675
+ if (c.isElementAccessExpression(node) && c.isStringLiteralLike(node.argumentExpression)) return memberExpression(context, source, node.expression, [node.argumentExpression.text, ...members], seen, baseOnly);
676
+ if (c.isObjectLiteralExpression(node)) for (const item of [...node.properties].reverse()) {
677
+ if (c.isSpreadAssignment(item)) {
678
+ if (await memberExpression(context, source, item.expression, members, new Set(seen), baseOnly)) return true;
679
+ continue;
680
+ }
681
+ if (propertyName(item.name) !== members[0]) continue;
682
+ if (c.isPropertyAssignment(item)) return memberExpression(context, source, item.initializer, members.slice(1), seen, baseOnly);
683
+ if (c.isShorthandPropertyAssignment(item)) return memberExpression(context, source, item.name, members.slice(1), seen, baseOnly);
684
+ }
685
+ if (c.isArrayLiteralExpression(node)) {
686
+ const item = node.elements[Number(members[0])];
687
+ return !!item && memberExpression(context, source, item, members.slice(1), seen, baseOnly);
688
+ }
689
+ if (!c.isIdentifier(node)) return false;
690
+ for (const statement of source.statements) {
691
+ if (c.isVariableStatement(statement)) for (const item of statement.declarationList.declarations) {
692
+ const prefix = bindingPath(item.name, node.text);
693
+ if (prefix && item.initializer) return memberExpression(context, source, item.initializer, [...prefix, ...members], seen, baseOnly);
694
+ }
695
+ if (!c.isImportDeclaration(statement) || !c.isStringLiteral(statement.moduleSpecifier) || statement.importClause?.isTypeOnly) continue;
696
+ const clause = statement.importClause;
697
+ const binding = clause?.namedBindings;
698
+ const specifier = statement.moduleSpecifier.text;
699
+ if (binding && c.isNamespaceImport(binding) && binding.name.text === node.text) return exportedMember(context, specifier, source.fileName, members[0], members.slice(1), seen, baseOnly);
700
+ if (clause?.name?.text === node.text) return exportedMember(context, specifier, source.fileName, "default", members, seen, baseOnly);
701
+ if (binding && c.isNamedImports(binding)) {
702
+ for (const item of binding.elements) if (!item.isTypeOnly && item.name.text === node.text) return exportedMember(context, specifier, source.fileName, (item.propertyName ?? item.name).text, members, seen, baseOnly);
703
+ }
704
+ }
705
+ return false;
706
+ }
707
+ async function exported(context, specifier, importer, name, seen, baseOnly = false) {
608
708
  if (specifier === "@finesoft/front" || serverEntries.has(specifier)) return name === "BaseServerController";
609
709
  if (specifier.startsWith("@finesoft/")) return false;
610
710
  const resolved = await context.resolve(specifier, importer, { skipSelf: true });
@@ -619,52 +719,55 @@ function serverControllerModules(getRoot) {
619
719
  const c = getCompiler();
620
720
  for (const statement of source.statements) {
621
721
  if (c.isClassDeclaration(statement) && has(statement, c.SyntaxKind.ExportKeyword)) {
622
- if ((has(statement, c.SyntaxKind.DefaultKeyword) ? "default" : statement.name?.text) === name) return serverClass(context, source, statement, seen);
722
+ if ((has(statement, c.SyntaxKind.DefaultKeyword) ? "default" : statement.name?.text) === name) return !baseOnly && serverClass(context, source, statement, seen);
723
+ }
724
+ if (c.isVariableStatement(statement) && has(statement, c.SyntaxKind.ExportKeyword)) {
725
+ for (const item of statement.declarationList.declarations) if (c.isIdentifier(item.name) && item.name.text === name && item.initializer) return expression(context, source, item.initializer, seen, baseOnly);
623
726
  }
624
727
  if (c.isExportDeclaration(statement) && !statement.isTypeOnly) {
625
728
  const module = statement.moduleSpecifier && c.isStringLiteral(statement.moduleSpecifier) ? statement.moduleSpecifier.text : void 0;
626
- if (!statement.exportClause && module && await exported(context, module, file, name, new Set(seen))) return true;
729
+ if (!statement.exportClause && module && await exported(context, module, file, name, new Set(seen), baseOnly)) return true;
627
730
  if (statement.exportClause && c.isNamedExports(statement.exportClause)) for (const item of statement.exportClause.elements) {
628
731
  if (item.isTypeOnly || item.name.text !== name) continue;
629
732
  const local = (item.propertyName ?? item.name).text;
630
- return module ? exported(context, module, file, local, seen) : identifier(context, source, local, seen);
733
+ return module ? exported(context, module, file, local, seen, baseOnly) : identifier(context, source, local, seen, baseOnly);
631
734
  }
632
735
  }
633
- if (name === "default" && c.isExportAssignment(statement)) return expression(context, source, statement.expression, seen);
736
+ if (name === "default" && c.isExportAssignment(statement)) return expression(context, source, statement.expression, seen, baseOnly);
634
737
  }
635
738
  return false;
636
739
  }
637
- async function identifier(context, source, name, seen) {
740
+ async function identifier(context, source, name, seen, baseOnly = false) {
638
741
  const key = `${source.fileName}:local:${name}`;
639
742
  if (seen.has(key)) return false;
640
743
  seen.add(key);
641
744
  const c = getCompiler();
642
745
  for (const statement of source.statements) {
643
- if (c.isClassDeclaration(statement) && statement.name?.text === name) return serverClass(context, source, statement, seen);
644
- if (c.isVariableStatement(statement)) {
645
- for (const item of statement.declarationList.declarations) if (c.isIdentifier(item.name) && item.name.text === name && item.initializer) return expression(context, source, item.initializer, seen);
746
+ if (c.isClassDeclaration(statement) && statement.name?.text === name) return !baseOnly && serverClass(context, source, statement, seen);
747
+ if (c.isVariableStatement(statement)) for (const item of statement.declarationList.declarations) {
748
+ const members = bindingPath(item.name, name);
749
+ if (members && item.initializer) return memberExpression(context, source, item.initializer, members, seen, baseOnly);
646
750
  }
647
751
  if (!c.isImportDeclaration(statement) || !c.isStringLiteral(statement.moduleSpecifier) || statement.importClause?.isTypeOnly) continue;
648
752
  const clause = statement.importClause;
649
- if (clause?.name?.text === name) return exported(context, statement.moduleSpecifier.text, source.fileName, "default", seen);
753
+ if (clause?.name?.text === name) return exported(context, statement.moduleSpecifier.text, source.fileName, "default", seen, baseOnly);
650
754
  if (clause?.namedBindings && c.isNamedImports(clause.namedBindings)) {
651
- for (const item of clause.namedBindings.elements) if (!item.isTypeOnly && item.name.text === name) return exported(context, statement.moduleSpecifier.text, source.fileName, (item.propertyName ?? item.name).text, seen);
755
+ for (const item of clause.namedBindings.elements) if (!item.isTypeOnly && item.name.text === name) return exported(context, statement.moduleSpecifier.text, source.fileName, (item.propertyName ?? item.name).text, seen, baseOnly);
652
756
  }
653
757
  }
654
758
  return false;
655
759
  }
656
- async function expression(context, source, node, seen) {
760
+ async function expression(context, source, node, seen, baseOnly = false) {
657
761
  const c = getCompiler();
658
- if (c.isIdentifier(node)) return identifier(context, source, node.text, seen);
659
- if (c.isPropertyAccessExpression(node) && c.isIdentifier(node.expression)) for (const statement of source.statements) {
660
- if (!c.isImportDeclaration(statement) || !c.isStringLiteral(statement.moduleSpecifier)) continue;
661
- const binding = statement.importClause?.namedBindings;
662
- if (binding && c.isNamespaceImport(binding) && binding.name.text === node.expression.text) return exported(context, statement.moduleSpecifier.text, source.fileName, node.name.text, seen);
663
- }
664
- if (c.isClassExpression(node)) return serverClass(context, source, node, seen);
762
+ node = unwrap(node);
763
+ if (c.isIdentifier(node)) return identifier(context, source, node.text, seen, baseOnly);
764
+ const member = c.isPropertyAccessExpression(node) ? node.name.text : c.isElementAccessExpression(node) && c.isStringLiteralLike(node.argumentExpression) ? node.argumentExpression.text : void 0;
765
+ if (member && (c.isPropertyAccessExpression(node) || c.isElementAccessExpression(node))) return memberExpression(context, source, node.expression, [member], seen, baseOnly);
766
+ if (baseOnly && c.isElementAccessExpression(node) && !member) return memberExpression(context, source, node.expression, ["BaseServerController"], seen, true);
767
+ if (c.isClassExpression(node)) return !baseOnly && serverClass(context, source, node, seen);
665
768
  if (c.isCallExpression(node)) {
666
- if (await expression(context, source, node.expression, new Set(seen))) return true;
667
- for (const argument of node.arguments) if (await expression(context, source, argument, new Set(seen))) return true;
769
+ if (!baseOnly && await expression(context, source, node.expression, new Set(seen))) return true;
770
+ for (const argument of node.arguments) if (await expression(context, source, argument, new Set(seen), baseOnly) || baseOnly && await memberExpression(context, source, argument, ["BaseServerController"], new Set(seen), true)) return true;
668
771
  }
669
772
  return false;
670
773
  }
@@ -672,6 +775,77 @@ function serverControllerModules(getRoot) {
672
775
  const base = node.heritageClauses?.find((clause) => clause.token === getCompiler().SyntaxKind.ExtendsKeyword)?.types[0];
673
776
  return !!base && expression(context, source, base.expression, seen);
674
777
  }
778
+ /** One classification drives cold dependency indexing and every browser load form. */
779
+ async function classify(context, source) {
780
+ const c = getCompiler();
781
+ const classes = [];
782
+ const uses = [];
783
+ const visit = (node) => {
784
+ if (c.isImportDeclaration(node) || c.isExportDeclaration(node) || c.isTypeNode(node)) return;
785
+ if (c.isClassDeclaration(node) || c.isClassExpression(node)) classes.push(node);
786
+ if (c.isIdentifier(node) || c.isPropertyAccessExpression(node) || c.isElementAccessExpression(node) || c.isCallExpression(node)) uses.push(node);
787
+ c.forEachChild(node, visit);
788
+ };
789
+ visit(source);
790
+ const owned = /* @__PURE__ */ new Set();
791
+ for (const node of classes) if (await serverClass(context, source, node, /* @__PURE__ */ new Set())) owned.add(node);
792
+ const names = /* @__PURE__ */ new Map();
793
+ const defaultNames = /* @__PURE__ */ new Map();
794
+ const reserved = new Set(uses.filter(c.isIdentifier).map((node) => node.text));
795
+ const generatedName = (prefix) => {
796
+ let name = prefix;
797
+ while (reserved.has(name)) name += "_";
798
+ reserved.add(name);
799
+ return name;
800
+ };
801
+ for (const statement of source.statements) {
802
+ if (c.isClassDeclaration(statement) && owned.has(statement)) {
803
+ const name = statement.name?.text ?? "__ServerController";
804
+ names.set(name, name);
805
+ }
806
+ if (c.isVariableStatement(statement)) {
807
+ for (const item of statement.declarationList.declarations) if (c.isIdentifier(item.name) && item.initializer && owned.has(unwrap(item.initializer))) names.set(item.name.text, item.name.text);
808
+ }
809
+ if (c.isExportAssignment(statement) && owned.has(unwrap(statement.expression))) {
810
+ const name = generatedName("__ServerControllerDefault");
811
+ defaultNames.set(statement, name);
812
+ names.set(name, name);
813
+ }
814
+ }
815
+ let changed = true;
816
+ while (changed) {
817
+ changed = false;
818
+ for (const statement of source.statements) {
819
+ if (!c.isVariableStatement(statement)) continue;
820
+ for (const item of statement.declarationList.declarations) {
821
+ if (!c.isIdentifier(item.name) || !item.initializer || names.has(item.name.text)) continue;
822
+ const value = unwrap(item.initializer);
823
+ if (c.isIdentifier(value) && names.has(value.text)) {
824
+ names.set(item.name.text, names.get(value.text));
825
+ changed = true;
826
+ }
827
+ }
828
+ }
829
+ }
830
+ let server = owned.size > 0;
831
+ if (!server) {
832
+ const checked = /* @__PURE__ */ new Set();
833
+ for (const use of uses) {
834
+ const key = use.getText(source);
835
+ if (checked.has(key)) continue;
836
+ checked.add(key);
837
+ if (await expression(context, source, use, /* @__PURE__ */ new Set(), true)) {
838
+ server = true;
839
+ break;
840
+ }
841
+ }
842
+ }
843
+ return {
844
+ server,
845
+ names,
846
+ defaultNames
847
+ };
848
+ }
675
849
  async function protectDependencies(context, file, seen = /* @__PURE__ */ new Set()) {
676
850
  if (seen.has(file)) return;
677
851
  seen.add(file);
@@ -719,12 +893,11 @@ function serverControllerModules(getRoot) {
719
893
  }
720
894
  if (!pending.size) return;
721
895
  indexing = (async () => {
722
- const c = getCompiler();
723
896
  for (const file of pending) {
724
897
  pending.delete(file);
725
898
  const source = read(file);
726
899
  if (!source) continue;
727
- if (privateFiles.has(file) || (await Promise.all(source.statements.filter(c.isClassDeclaration).map((node) => serverClass(context, source, node, /* @__PURE__ */ new Set())))).some(Boolean)) await protectDependencies(context, file);
900
+ if (privateFiles.has(file) || (await classify(context, source)).server) await protectDependencies(context, file);
728
901
  }
729
902
  })();
730
903
  try {
@@ -748,12 +921,8 @@ function serverControllerModules(getRoot) {
748
921
  return null;
749
922
  }
750
923
  const c = getCompiler();
751
- const names = /* @__PURE__ */ new Map();
752
- for (const statement of source.statements) if (c.isClassDeclaration(statement) && await serverClass(context, source, statement, /* @__PURE__ */ new Set())) {
753
- const name = statement.name?.text ?? "__ServerController";
754
- names.set(name, name);
755
- }
756
- if (!names.size) {
924
+ const { server, names, defaultNames } = await classify(context, source);
925
+ if (!server) {
757
926
  if (privateFiles.has(file)) throw Error(`Server controller dependency cannot be loaded by the browser: ${file}`);
758
927
  return null;
759
928
  }
@@ -763,6 +932,7 @@ function serverControllerModules(getRoot) {
763
932
  "v",
764
933
  "import"
765
934
  ].includes(key))) throw Error(`Server controller source cannot be loaded as an asset: ${file}`);
935
+ if (!names.size) throw Error(`Unsupported server controller syntax; use a module-scope class: ${file}`);
766
936
  const exports = [];
767
937
  for (const statement of source.statements) {
768
938
  if (c.isInterfaceDeclaration(statement) || c.isTypeAliasDeclaration(statement)) continue;
@@ -770,7 +940,11 @@ function serverControllerModules(getRoot) {
770
940
  const name = statement.name?.text ?? "__ServerController";
771
941
  if (!names.has(name)) throw Error(`Server controller modules cannot export shared implementations: ${file}`);
772
942
  exports.push(has(statement, c.SyntaxKind.DefaultKeyword) ? `export default ${name};` : `export { ${name} };`);
773
- } else if (c.isExportDeclaration(statement) && !statement.isTypeOnly) {
943
+ } else if (c.isVariableStatement(statement) && has(statement, c.SyntaxKind.ExportKeyword)) for (const item of statement.declarationList.declarations) {
944
+ if (!c.isIdentifier(item.name) || !names.has(item.name.text)) throw Error(`Server controller modules can only export controllers and types: ${file}`);
945
+ exports.push(`export { ${item.name.text} };`);
946
+ }
947
+ else if (c.isExportDeclaration(statement) && !statement.isTypeOnly) {
774
948
  if (statement.moduleSpecifier || !statement.exportClause || !c.isNamedExports(statement.exportClause)) throw Error(`Move re-exports out of the server controller implementation module: ${file}`);
775
949
  for (const item of statement.exportClause.elements) {
776
950
  if (item.isTypeOnly) continue;
@@ -779,12 +953,14 @@ function serverControllerModules(getRoot) {
779
953
  exports.push(`export { ${name} as ${item.name.text} };`);
780
954
  }
781
955
  } else if (c.isExportAssignment(statement)) {
782
- if (!c.isIdentifier(statement.expression) || !names.has(statement.expression.text)) throw Error(`Invalid server controller default export: ${file}`);
783
- exports.push(`export default ${statement.expression.text};`);
956
+ const value = unwrap(statement.expression);
957
+ const name = defaultNames.get(statement) ?? (c.isIdentifier(value) ? value.text : void 0);
958
+ if (!name || !names.has(name)) throw Error(`Invalid server controller default export: ${file}`);
959
+ exports.push(`export default ${name};`);
784
960
  } else if (has(statement, c.SyntaxKind.ExportKeyword)) throw Error(`Server controller modules can only export controllers and types: ${file}`);
785
961
  }
786
962
  return {
787
- code: `import { ServerControllerProxy as __Proxy } from "@finesoft/front";\n${[...names].map(([name]) => `class ${name} extends __Proxy {}`).join("\n")}\n${exports.join("\n")}`,
963
+ code: `import { ServerControllerProxy as __Proxy } from "@finesoft/front";\n${[...names].map(([name, original]) => name === original ? `class ${name} extends __Proxy {}` : `const ${name} = ${original};`).join("\n")}\n${exports.join("\n")}`,
788
964
  map: {
789
965
  version: 3,
790
966
  sources: [],
@@ -2202,9 +2378,9 @@ export async function loadMessages(locale) {
2202
2378
  const { getRequestListener } = await dynamicImport("@hono/node-server");
2203
2379
  printDevSafetyBanner();
2204
2380
  const app = new HonoClass();
2205
- if (options.proxies?.length) registerProxyRoutes(app, options.proxies);
2206
2381
  if (typeof options.setup === "function") await options.setup(app);
2207
2382
  else if (typeof options.setup === "string") await (await server.ssrLoadModule("/" + options.setup)).default(app);
2383
+ if (options.proxies?.length) registerProxyRoutes(app, options.proxies);
2208
2384
  const ssrApp = createSSRApp({
2209
2385
  root,
2210
2386
  vite: server,
@@ -2236,14 +2412,12 @@ export async function loadMessages(locale) {
2236
2412
  const { Hono: HonoClass } = await dynamicImport("hono");
2237
2413
  const { getRequestListener } = await dynamicImport("@hono/node-server");
2238
2414
  const app = new HonoClass();
2239
- if (options.proxies?.length) registerProxyRoutes(app, options.proxies);
2240
2415
  if (typeof options.setup === "function") await options.setup(app);
2241
- else if (typeof options.setup === "string") try {
2416
+ else if (typeof options.setup === "string") {
2242
2417
  const setupPath = pathToFileURL(path.resolve(root, "dist/server/setup.mjs")).href;
2243
2418
  await (await dynamicImport(setupPath)).default(app);
2244
- } catch {
2245
- console.warn("[finesoft] Could not load setup module for preview. API routes disabled.");
2246
2419
  }
2420
+ if (options.proxies?.length) registerProxyRoutes(app, options.proxies);
2247
2421
  const template = readFileSync(path.resolve(root, "dist/client/index.html"), "utf-8");
2248
2422
  const ssrPath = pathToFileURL(path.resolve(root, "dist/server/ssr.js")).href;
2249
2423
  const ssrModule = await dynamicImport(ssrPath);
@@ -1,4 +1,4 @@
1
- import { f as AppSnapshot, g as WebAppView, h as ViewProps } from "./session-DnB4ZC3x.mjs";
1
+ import { f as AppSnapshot, g as WebAppView, h as ViewProps } from "./session-BR1vd3yY.mjs";
2
2
  import { Component, PropType } from "vue";
3
3
  declare namespace vue_d_exports {
4
4
  export { NativeView, NativeViews, Outlet, ViewProps, WebAppView, useSnapshot };
@@ -29,4 +29,4 @@ declare const Outlet: import("vue").DefineComponent<import("vue").ExtractPropTyp
29
29
  }>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
30
30
  //#endregion
31
31
  export { vue_d_exports as a, useSnapshot as i, NativeViews as n, Outlet as r, NativeView as t };
32
- //# sourceMappingURL=vue-DGmzuKho.d.mts.map
32
+ //# sourceMappingURL=vue-DsZIYP1c.d.mts.map
package/dist/vue.d.mts CHANGED
@@ -1,3 +1,3 @@
1
- import { g as WebAppView, h as ViewProps } from "./session-DnB4ZC3x.mjs";
2
- import { i as useSnapshot, n as NativeViews, r as Outlet, t as NativeView } from "./vue-DGmzuKho.mjs";
1
+ import { g as WebAppView, h as ViewProps } from "./session-BR1vd3yY.mjs";
2
+ import { i as useSnapshot, n as NativeViews, r as Outlet, t as NativeView } from "./vue-DsZIYP1c.mjs";
3
3
  export { NativeView, NativeViews, Outlet, type ViewProps, type WebAppView, useSnapshot };
package/dist/web.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { A as makeSchema, C as ImpressionEntry, D as StandardIssue, E as ParamSchema, F as Token, I as createToken, M as Provider, N as ProviderContext, O as StandardResult, P as provide, S as EventRecorder, T as InferOutput, _ as OperationPolicy, a as compilePath, b as RuntimeOptions, c as ExecutionContext, d as ExecutionHandle, f as Implementation, g as OperationHandler, h as Operation, i as PathParams, j as Container, k as StandardSchemaV1, l as ExecutionError, m as ModuleDefinition, n as PathDescriptor, o as AnyOperation, p as Invocation, r as PathParameterDescriptor, s as AppDefinition, t as CompiledPath, u as ExecutionErrorCode, v as QueryCache, w as MetricsFieldsProvider, x as executionErrorFromHttp, y as RuntimeHandle } from "./path-CXT6xGPO.mjs";
2
- import { $ as createActiveLeafCodec, $n as list, $t as MiddlewareResult, A as SessionStore, An as PlatformInfo, At as isCompoundAction, B as ControllerContext, Bn as ConsoleLoggerFactory, Bt as NavigationPath, C as SessionFailure, Cn as Storage, Ct as ACTION_KINDS, D as SessionSlice, Dn as LocaleInfo, Dt as ExternalUrlAction, E as SessionRestoreResult, En as LocaleAttributes, Et as CompoundAction, F as WebRuntime, Fn as ReportCallback, Ft as LeafNode, G as controllerContext, Gn as InferParams, Gt as SplitColumn, H as SERVER_CONTROLLER, Hn as Logger, Ht as NavigationSnapshot, I as WebRuntimeOptions, In as ReportingLogger, It as NAVIGATION_NODE_KINDS, J as resolveConfiguredMessages, Jn as QuerySchemaMap, Jt as StackNode, K as MessagesLoader, Kn as InferQuery, Kt as SplitNode, L as createWebRuntime, Ln as ReportingLoggerFactory, Lt as NavigationError, M as SessionWriteResult, Mn as BaseController, Mt as isFlowAction, N as StorageUnavailableError, Nn as ControllerFailure, Nt as makeExternalUrlAction, O as SessionSnapshot, On as TextDirection, Ot as FlowAction, P as WebConfiguration, Pn as ControllerInput, Pt as makeFlowAction, Q as NavigationRouterLike, Qn as isMultiValueSchema, Qt as DenyResult, R as PageControllerDefinition, Rn as ReportingLoggerOptions, Rt as NavigationNode, S as SessionErrorContext, Sn as FeatureFlagsProvider, St as ActionHandler, T as SessionNavigation, Tn as resolveMessages, Tt as ActionInvocation, U as SERVER_REQUEST, Un as LoggerFactory, Ut as ResolvedDestination, V as PageControllerInput, Vn as LogFilter, Vt as NavigationPathStep, W as ServerRequestState, Wn as ExtractParamNames, Wt as SPLIT_VISIBILITIES, X as FullStateCodecOptions, Xn as ListOptions, Xt as AfterLoadGuard, Y as DEFAULT_NAV_PARAM, Yn as StripOptional, Yt as TabsNode, Z as NavigationCodec, Zn as MultiValueSchema, Zt as BeforeLoadGuard, _ as AsyncStorage, _n as RouteInput, _t as PrefetchedIntents, a as NavigationCommitContext, an as deny, at as SerializedSplit, b as SESSION_DEFAULT_VERSION, bn as DEP_KEYS, bt as Router, c as NavigationTransactionContext, cn as rewrite, ct as SerializedTabs, d as createWebSession, dn as FINESOFT_PUBLIC, dt as serializeNavigationStable, en as NavigationContext, er as Intent, et as createFullStateCodec, f as AppSnapshot, fn as PublicProjection, ft as PageRoute, g as WebAppView, gn as markPublic, gt as PrefetchedIntent, h as ViewProps, hn as isPublicMarked, ht as route, i as BeforeNavigateResult, in as RewriteResult, it as SerializedNavigation, j as SessionStoreOptions, jn as detectPlatform, jt as isExternalUrlAction, k as SessionStateProvider, kn as Translator, kt as TreeAction, l as WebSession, ln as BASE_PAGE_FIELDS, lt as deserializeNavigation, m as ViewEntry, mn as getPublicFields, mt as RouteDefinition, n as BeforeCommitResult, nn as PostLoadContext, nt as encodeNavigationTreeParam, o as NavigationCommitError, on as next, ot as SerializedSplitColumn, p as NavigationSummary, pn as PublicValueCodec, pt as RenderMode, q as MessagesLoaderContext, qn as ParamsFor, qt as SplitVisibility, r as BeforeNavigatePolicy, rn as RedirectResult, rt as SerializedLeaf, s as NavigationContextInput, sn as redirect, st as SerializedStack, t as BeforeCommitPolicy, tn as NextResult, tt as decodeNavigationTreeParam, u as WebSessionOptions, un as BasePage, ut as serializeNavigation, v as NavigationScopedState, vn as RouteIntent, vt as RouteAddOptions, w as SessionLoadResult, wn as TranslationMessages, wt as Action, x as SessionError, xn as FeatureFlags, xt as ActionDispatcher, y as SESSION_DEFAULT_KEY, yn as RouteParams, yt as RouteMatch, z as WebAppDefinition, zn as ConsoleLogger, zt as NavigationNodeKind } from "./session-DnB4ZC3x.mjs";
3
- import { $ as SimpleTranslator, $t as str, A as setVisibility, At as pipeAsync, B as split, Bt as HostGuardError, C as popTo, Ct as isNone, D as resolveActivePath, Dt as Mapper, E as replaceTop, Et as AsyncMapper, F as isLeafNode, Ft as HttpClient, G as SafeErrorPageOptions, Gt as optional, H as tabs, Ht as stableStringify, I as isSplitNode, It as HttpClientConfig, J as defineApp, Jt as StrOptions, K as safeErrorPage, Kt as withDefault, L as isStackNode, Lt as HttpRequestOptions, M as resourceKey, Mt as HostCheckResult, N as SplitColumnInit, Nt as classifyHost, O as selectColumn, Ot as mapEach, P as TabsInit, Pt as classifyUrl, Q as implementOperation, Qt as oneOf, R as isTabsNode, Rt as RequestInterceptor, S as pop, St as Optional, T as push, Tt as LruMap, U as RouteInputFor, Ut as CompositeLogger, V as stack, Vt as HttpError, W as createHydrationDigest, Wt as CompositeLoggerFactory, X as defineOperation, Xt as int, Y as defineModule, Yt as bool, Z as implementController, Zt as num, _ as collectAllLeaves, _t as getBaseUrl, a as LoadPageOptions, at as resolveLocaleFromUrl, b as findNode, bt as removeScheme, c as defineWebApp, ct as englishPlural, d as runBeforeLoadGuards, dt as WithFieldsRecorder, en as uuid, et as SimpleTranslatorOptions, f as createSessionStore, ft as VoidEventRecorder, g as encodeSnapshot, gt as buildUrl, h as decodeSnapshot, ht as generateUuid, i as resolveInitialNavigation, it as makeLocaleInfo, j as visibleSplitColumns, jt as fetchWithRedirects, k as selectTab, kt as pipe, l as getWebPlan, lt as interpolate, m as createNavigationScopedState, mt as CompositeEventRecorder, n as PageReference, nt as getTextDirection, o as PageLoadResult, ot as PluralCategory, p as collectLeafKeys, pt as ConsoleEventRecorder, q as createRuntime, qt as NumOptions, r as definePage, rt as isRtl, s as loadPage, st as PluralRuleProvider, t as parseCookieString, tt as getLocaleAttributes, u as runAfterLoadGuards, ut as resolvePluralKey, v as collectVisibleDestinations, vt as removeHost, w as popToRoot, wt as isSome, x as mapNavigationLeaves, xt as None, y as findNearestStack, yt as removeQueryParams, z as leaf, zt as ResponseInterceptor } from "./cookies-BiRlUanX.mjs";
4
- import { a as enforceHostGuard, i as TargetGuardOptions, n as secureFetch, r as DnsLookup, t as SecureFetchOptions } from "./secure-fetch-Xlht2jd7.mjs";
5
- import { a as WebHydration, c as decodeWireEnvelope, i as FRAMEWORK_PROTOCOL_VERSION, l as getFrameworkBuildId, n as ServerControllerProxy, o as WireDecodeResult, r as ServerControllerRequest, s as WireEnvelope, t as SERVER_CONTROLLER_PATH } from "./server-controller-proxy-BkVuVWVD.mjs";
1
+ import { A as makeSchema, C as ImpressionEntry, D as StandardIssue, E as ParamSchema, F as Token, I as createToken, M as Provider, N as ProviderContext, O as StandardResult, P as provide, S as EventRecorder, T as InferOutput, _ as OperationPolicy, a as compilePath, b as RuntimeOptions, c as ExecutionContext, d as ExecutionHandle, f as Implementation, g as OperationHandler, h as Operation, i as PathParams, j as Container, k as StandardSchemaV1, l as ExecutionError, m as ModuleDefinition, n as PathDescriptor, o as AnyOperation, p as Invocation, r as PathParameterDescriptor, s as AppDefinition, t as CompiledPath, u as ExecutionErrorCode, v as QueryCache, w as MetricsFieldsProvider, x as executionErrorFromHttp, y as RuntimeHandle } from "./path-C-b1ChKq.mjs";
2
+ import { $ as createActiveLeafCodec, $n as list, $t as MiddlewareResult, A as SessionStore, An as PlatformInfo, At as isCompoundAction, B as ControllerContext, Bn as ConsoleLoggerFactory, Bt as NavigationPath, C as SessionFailure, Cn as Storage, Ct as ACTION_KINDS, D as SessionSlice, Dn as LocaleInfo, Dt as ExternalUrlAction, E as SessionRestoreResult, En as LocaleAttributes, Et as CompoundAction, F as WebRuntime, Fn as ReportCallback, Ft as LeafNode, G as controllerContext, Gn as InferParams, Gt as SplitColumn, H as SERVER_CONTROLLER, Hn as Logger, Ht as NavigationSnapshot, I as WebRuntimeOptions, In as ReportingLogger, It as NAVIGATION_NODE_KINDS, J as resolveConfiguredMessages, Jn as QuerySchemaMap, Jt as StackNode, K as MessagesLoader, Kn as InferQuery, Kt as SplitNode, L as createWebRuntime, Ln as ReportingLoggerFactory, Lt as NavigationError, M as SessionWriteResult, Mn as BaseController, Mt as isFlowAction, N as StorageUnavailableError, Nn as ControllerFailure, Nt as makeExternalUrlAction, O as SessionSnapshot, On as TextDirection, Ot as FlowAction, P as WebConfiguration, Pn as ControllerInput, Pt as makeFlowAction, Q as NavigationRouterLike, Qn as isMultiValueSchema, Qt as DenyResult, R as PageControllerDefinition, Rn as ReportingLoggerOptions, Rt as NavigationNode, S as SessionErrorContext, Sn as FeatureFlagsProvider, St as ActionHandler, T as SessionNavigation, Tn as resolveMessages, Tt as ActionInvocation, U as SERVER_REQUEST, Un as LoggerFactory, Ut as ResolvedDestination, V as PageControllerInput, Vn as LogFilter, Vt as NavigationPathStep, W as ServerRequestState, Wn as ExtractParamNames, Wt as SPLIT_VISIBILITIES, X as FullStateCodecOptions, Xn as ListOptions, Xt as AfterLoadGuard, Y as DEFAULT_NAV_PARAM, Yn as StripOptional, Yt as TabsNode, Z as NavigationCodec, Zn as MultiValueSchema, Zt as BeforeLoadGuard, _ as AsyncStorage, _n as RouteInput, _t as PrefetchedIntents, a as NavigationCommitContext, an as deny, at as SerializedSplit, b as SESSION_DEFAULT_VERSION, bn as DEP_KEYS, bt as Router, c as NavigationTransactionContext, cn as rewrite, ct as SerializedTabs, d as createWebSession, dn as FINESOFT_PUBLIC, dt as serializeNavigationStable, en as NavigationContext, er as Intent, et as createFullStateCodec, f as AppSnapshot, fn as PublicProjection, ft as PageRoute, g as WebAppView, gn as markPublic, gt as PrefetchedIntent, h as ViewProps, hn as isPublicMarked, ht as route, i as BeforeNavigateResult, in as RewriteResult, it as SerializedNavigation, j as SessionStoreOptions, jn as detectPlatform, jt as isExternalUrlAction, k as SessionStateProvider, kn as Translator, kt as TreeAction, l as WebSession, ln as BASE_PAGE_FIELDS, lt as deserializeNavigation, m as ViewEntry, mn as getPublicFields, mt as RouteDefinition, n as BeforeCommitResult, nn as PostLoadContext, nt as encodeNavigationTreeParam, o as NavigationCommitError, on as next, ot as SerializedSplitColumn, p as NavigationSummary, pn as PublicValueCodec, pt as RenderMode, q as MessagesLoaderContext, qn as ParamsFor, qt as SplitVisibility, r as BeforeNavigatePolicy, rn as RedirectResult, rt as SerializedLeaf, s as NavigationContextInput, sn as redirect, st as SerializedStack, t as BeforeCommitPolicy, tn as NextResult, tt as decodeNavigationTreeParam, u as WebSessionOptions, un as BasePage, ut as serializeNavigation, v as NavigationScopedState, vn as RouteIntent, vt as RouteAddOptions, w as SessionLoadResult, wn as TranslationMessages, wt as Action, x as SessionError, xn as FeatureFlags, xt as ActionDispatcher, y as SESSION_DEFAULT_KEY, yn as RouteParams, yt as RouteMatch, z as WebAppDefinition, zn as ConsoleLogger, zt as NavigationNodeKind } from "./session-BR1vd3yY.mjs";
3
+ import { $ as SimpleTranslator, $t as str, A as setVisibility, At as pipeAsync, B as split, Bt as HostGuardError, C as popTo, Ct as isNone, D as resolveActivePath, Dt as Mapper, E as replaceTop, Et as AsyncMapper, F as isLeafNode, Ft as HttpClient, G as SafeErrorPageOptions, Gt as optional, H as tabs, Ht as stableStringify, I as isSplitNode, It as HttpClientConfig, J as defineApp, Jt as StrOptions, K as safeErrorPage, Kt as withDefault, L as isStackNode, Lt as HttpRequestOptions, M as resourceKey, Mt as HostCheckResult, N as SplitColumnInit, Nt as classifyHost, O as selectColumn, Ot as mapEach, P as TabsInit, Pt as classifyUrl, Q as implementOperation, Qt as oneOf, R as isTabsNode, Rt as RequestInterceptor, S as pop, St as Optional, T as push, Tt as LruMap, U as RouteInputFor, Ut as CompositeLogger, V as stack, Vt as HttpError, W as createHydrationDigest, Wt as CompositeLoggerFactory, X as defineOperation, Xt as int, Y as defineModule, Yt as bool, Z as implementController, Zt as num, _ as collectAllLeaves, _t as getBaseUrl, a as LoadPageOptions, at as resolveLocaleFromUrl, b as findNode, bt as removeScheme, c as defineWebApp, ct as englishPlural, d as runBeforeLoadGuards, dt as WithFieldsRecorder, en as uuid, et as SimpleTranslatorOptions, f as createSessionStore, ft as VoidEventRecorder, g as encodeSnapshot, gt as buildUrl, h as decodeSnapshot, ht as generateUuid, i as resolveInitialNavigation, it as makeLocaleInfo, j as visibleSplitColumns, jt as fetchWithRedirects, k as selectTab, kt as pipe, l as getWebPlan, lt as interpolate, m as createNavigationScopedState, mt as CompositeEventRecorder, n as PageReference, nt as getTextDirection, o as PageLoadResult, ot as PluralCategory, p as collectLeafKeys, pt as ConsoleEventRecorder, q as createRuntime, qt as NumOptions, r as definePage, rt as isRtl, s as loadPage, st as PluralRuleProvider, t as parseCookieString, tt as getLocaleAttributes, u as runAfterLoadGuards, ut as resolvePluralKey, v as collectVisibleDestinations, vt as removeHost, w as popToRoot, wt as isSome, x as mapNavigationLeaves, xt as None, y as findNearestStack, yt as removeQueryParams, z as leaf, zt as ResponseInterceptor } from "./cookies-BXCwZoS_.mjs";
4
+ import { a as enforceHostGuard, i as TargetGuardOptions, n as secureFetch, r as DnsLookup, t as SecureFetchOptions } from "./secure-fetch--QwYr5uJ.mjs";
5
+ import { a as WebHydration, c as decodeWireEnvelope, i as FRAMEWORK_PROTOCOL_VERSION, l as getFrameworkBuildId, n as ServerControllerProxy, o as WireDecodeResult, r as ServerControllerRequest, s as WireEnvelope, t as SERVER_CONTROLLER_PATH } from "./server-controller-proxy-CFdU_nGs.mjs";
6
6
  export { ACTION_KINDS, type Action, ActionDispatcher, type ActionHandler, type ActionInvocation, type AfterLoadGuard, AnyOperation, AppDefinition, AppSnapshot, type AsyncMapper, type AsyncStorage, BASE_PAGE_FIELDS, BaseController, type BasePage, type BeforeCommitPolicy, type BeforeCommitResult, type BeforeLoadGuard, type BeforeNavigatePolicy, type BeforeNavigateResult, type CompiledPath, CompositeEventRecorder, CompositeLogger, CompositeLoggerFactory, type CompoundAction, ConsoleEventRecorder, ConsoleLogger, ConsoleLoggerFactory, Container, ControllerContext, type ControllerFailure, type ControllerInput, DEFAULT_NAV_PARAM, DEP_KEYS, type DenyResult, type DnsLookup, type EventRecorder, ExecutionContext, ExecutionError, ExecutionErrorCode, ExecutionHandle, type ExternalUrlAction, type ExtractParamNames, FINESOFT_PUBLIC, FRAMEWORK_PROTOCOL_VERSION, type FeatureFlags, type FeatureFlagsProvider, type FlowAction, type FullStateCodecOptions, type HostCheckResult, HostGuardError, HttpClient, type HttpClientConfig, HttpError, type HttpRequestOptions, Implementation, type ImpressionEntry, type InferOutput, type InferParams, type InferQuery, type Intent, Invocation, type LeafNode, type ListOptions, type LoadPageOptions, type LocaleAttributes, type LocaleInfo, type LogFilter, type Logger, type LoggerFactory, LruMap, type Mapper, type MessagesLoader, type MessagesLoaderContext, type MetricsFieldsProvider, type MiddlewareResult, ModuleDefinition, type MultiValueSchema, NAVIGATION_NODE_KINDS, type NavigationCodec, type NavigationCommitContext, NavigationCommitError, type NavigationContext, type NavigationContextInput, NavigationError, type NavigationNode, type NavigationNodeKind, type NavigationPath, type NavigationPathStep, type NavigationRouterLike, type NavigationScopedState, type NavigationSnapshot, NavigationSummary, type NavigationTransactionContext, type NextResult, type None, type NumOptions, Operation, OperationHandler, OperationPolicy, type Optional, type PageControllerDefinition, PageControllerInput, type PageLoadResult, type PageReference, type PageRoute, type ParamSchema, type ParamsFor, type PathDescriptor, type PathParameterDescriptor, type PathParams, type PlatformInfo, type PluralCategory, type PluralRuleProvider, type PostLoadContext, type PrefetchedIntent, PrefetchedIntents, type Provider, type ProviderContext, type PublicProjection, type PublicValueCodec, QueryCache, type QuerySchemaMap, type RedirectResult, type RenderMode, type ReportCallback, ReportingLogger, ReportingLoggerFactory, type ReportingLoggerOptions, type RequestInterceptor, type ResolvedDestination, type ResponseInterceptor, type RewriteResult, type RouteAddOptions, type RouteDefinition, type RouteInput, type RouteInputFor, type RouteIntent, type RouteMatch, type RouteParams, Router, RuntimeHandle, RuntimeOptions, SERVER_CONTROLLER, SERVER_CONTROLLER_PATH, SERVER_REQUEST, SESSION_DEFAULT_KEY, SESSION_DEFAULT_VERSION, SPLIT_VISIBILITIES, type SafeErrorPageOptions, type SecureFetchOptions, type SerializedLeaf, type SerializedNavigation, type SerializedSplit, type SerializedSplitColumn, type SerializedStack, type SerializedTabs, ServerControllerProxy, type ServerControllerRequest, ServerRequestState, SessionError, type SessionErrorContext, type SessionFailure, type SessionLoadResult, type SessionNavigation, type SessionRestoreResult, type SessionSlice, type SessionSnapshot, type SessionStateProvider, type SessionStore, type SessionStoreOptions, type SessionWriteResult, SimpleTranslator, type SimpleTranslatorOptions, type SplitColumn, type SplitColumnInit, type SplitNode, type SplitVisibility, type StackNode, type StandardIssue, type StandardResult, type StandardSchemaV1, type Storage, StorageUnavailableError, type StrOptions, type StripOptional, type TabsInit, type TabsNode, type TargetGuardOptions, type TextDirection, type Token, type TranslationMessages, type Translator, type TreeAction, ViewEntry, ViewProps, VoidEventRecorder, type WebAppDefinition, WebAppView, type WebConfiguration, type WebHydration, type WebRuntime, type WebRuntimeOptions, type WebSession, type WebSessionOptions, type WireDecodeResult, type WireEnvelope, WithFieldsRecorder, bool, buildUrl, classifyHost, classifyUrl, collectAllLeaves, collectLeafKeys, collectVisibleDestinations, compilePath, controllerContext, createActiveLeafCodec, createFullStateCodec, createHydrationDigest, createNavigationScopedState, createRuntime, createSessionStore, createToken, createWebRuntime, createWebSession, decodeNavigationTreeParam, decodeSnapshot, decodeWireEnvelope, defineApp, defineModule, defineOperation, definePage, defineWebApp, deny, deserializeNavigation, detectPlatform, encodeNavigationTreeParam, encodeSnapshot, enforceHostGuard, englishPlural, executionErrorFromHttp, fetchWithRedirects, findNearestStack, findNode, generateUuid, getBaseUrl, getFrameworkBuildId, getLocaleAttributes, getPublicFields, getTextDirection, getWebPlan, implementController, implementOperation, int, interpolate, isCompoundAction, isExternalUrlAction, isFlowAction, isLeafNode, isMultiValueSchema, isNone, isPublicMarked, isRtl, isSome, isSplitNode, isStackNode, isTabsNode, leaf, list, loadPage, makeExternalUrlAction, makeFlowAction, makeLocaleInfo, makeSchema, mapEach, mapNavigationLeaves, markPublic, next, num, oneOf, optional, parseCookieString, pipe, pipeAsync, pop, popTo, popToRoot, provide, push, redirect, removeHost, removeQueryParams, removeScheme, replaceTop, resolveActivePath, resolveConfiguredMessages, resolveInitialNavigation, resolveLocaleFromUrl, resolveMessages, resolvePluralKey, resourceKey, rewrite, route, runAfterLoadGuards, runBeforeLoadGuards, safeErrorPage, secureFetch, selectColumn, selectTab, serializeNavigation, serializeNavigationStable, setVisibility, split, stableStringify, stack, str, tabs, uuid, visibleSplitColumns, withDefault };
package/dist/web.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { $ as isLeafNode, A as createActiveLeafCodec, At as getLocaleAttributes, B as findNode, Bt as secureFetch, C as runBeforeLoadGuards, Ct as defineApp, D as SERVER_REQUEST, Dt as implementOperation, E as SERVER_CONTROLLER, Et as implementController, F as serializeNavigation, Ft as englishPlural, G as push, Gt as ReportingLoggerFactory, H as pop, Ht as enforceHostGuard, I as serializeNavigationStable, It as interpolate, J as selectColumn, Jt as CompositeLogger, K as replaceTop, Kt as ConsoleLogger, L as collectAllLeaves, Lt as resolvePluralKey, M as decodeNavigationTreeParam, Mt as isRtl, N as encodeNavigationTreeParam, Nt as makeLocaleInfo, O as controllerContext, Ot as SimpleTranslator, P as deserializeNavigation, Pt as resolveLocaleFromUrl, Q as resourceKey, Qt as Container, R as collectVisibleDestinations, Rt as generateUuid, S as runAfterLoadGuards, St as createRuntime, T as getWebPlan, Tt as defineOperation, U as popTo, Ut as stableStringify, V as mapNavigationLeaves, Vt as fetchWithRedirects, W as popToRoot, Wt as ReportingLogger, X as setVisibility, Xt as isMultiValueSchema, Y as selectTab, Yt as CompositeLoggerFactory, Z as visibleSplitColumns, Zt as list, _ as SERVER_CONTROLLER_PATH, _t as isExternalUrlAction, a as createSessionStore, at as stack, b as decodeWireEnvelope, bt as makeFlowAction, c as decodeSnapshot, ct as NavigationError, d as createWebSession, dt as createHydrationDigest, et as isSplitNode, f as SESSION_DEFAULT_KEY, ft as PrefetchedIntents, g as loadPage, gt as isCompoundAction, h as StorageUnavailableError, ht as ACTION_KINDS, i as createWebRuntime, it as split, j as createFullStateCodec, jt as getTextDirection, k as DEFAULT_NAV_PARAM, kt as resolveMessages, l as encodeSnapshot, lt as SPLIT_VISIBILITIES, m as SessionError, mt as ActionDispatcher, n as definePage, nt as isTabsNode, o as collectLeafKeys, ot as tabs, p as SESSION_DEFAULT_VERSION, pt as Router, q as resolveActivePath, qt as ConsoleLoggerFactory, r as resolveInitialNavigation, rt as leaf, s as createNavigationScopedState, st as NAVIGATION_NODE_KINDS, t as parseCookieString, tt as isStackNode, u as NavigationCommitError, ut as route, v as ServerControllerProxy, vt as isFlowAction, w as defineWebApp, wt as defineModule, x as getFrameworkBuildId, xt as provide, y as FRAMEWORK_PROTOCOL_VERSION, yt as makeExternalUrlAction, z as findNearestStack, zt as detectPlatform } from "./src-BQBfaaPO.mjs";
2
- import { C as bool, D as str, E as oneOf, O as uuid, S as withDefault, T as num, _ as mapEach, a as safeErrorPage, b as HttpClient, c as ConsoleEventRecorder, d as getBaseUrl, f as removeHost, g as isSome, h as isNone, i as rewrite, k as makeSchema, l as CompositeEventRecorder, m as removeScheme, n as next, o as WithFieldsRecorder, p as removeQueryParams, r as redirect, s as VoidEventRecorder, t as deny, u as buildUrl, v as pipe, w as int, x as optional, y as pipeAsync } from "./types-B1BT0N3t.mjs";
3
- import { i as HttpError, n as classifyUrl, r as HostGuardError, t as classifyHost } from "./host-guard-DDWxLpFL.mjs";
1
+ import { $ as isLeafNode, A as createActiveLeafCodec, At as getLocaleAttributes, B as findNode, Bt as secureFetch, C as runBeforeLoadGuards, Ct as defineApp, D as SERVER_REQUEST, Dt as implementOperation, E as SERVER_CONTROLLER, Et as implementController, F as serializeNavigation, Ft as englishPlural, G as push, Gt as ReportingLoggerFactory, H as pop, Ht as enforceHostGuard, I as serializeNavigationStable, It as interpolate, J as selectColumn, Jt as CompositeLogger, K as replaceTop, Kt as ConsoleLogger, L as collectAllLeaves, Lt as resolvePluralKey, M as decodeNavigationTreeParam, Mt as isRtl, N as encodeNavigationTreeParam, Nt as makeLocaleInfo, O as controllerContext, Ot as SimpleTranslator, P as deserializeNavigation, Pt as resolveLocaleFromUrl, Q as resourceKey, Qt as Container, R as collectVisibleDestinations, Rt as generateUuid, S as runAfterLoadGuards, St as createRuntime, T as getWebPlan, Tt as defineOperation, U as popTo, Ut as stableStringify, V as mapNavigationLeaves, Vt as fetchWithRedirects, W as popToRoot, Wt as ReportingLogger, X as setVisibility, Xt as isMultiValueSchema, Y as selectTab, Yt as CompositeLoggerFactory, Z as visibleSplitColumns, Zt as list, _ as SERVER_CONTROLLER_PATH, _t as isExternalUrlAction, a as createSessionStore, at as stack, b as decodeWireEnvelope, bt as makeFlowAction, c as decodeSnapshot, ct as NavigationError, d as createWebSession, dt as createHydrationDigest, et as isSplitNode, f as SESSION_DEFAULT_KEY, ft as PrefetchedIntents, g as loadPage, gt as isCompoundAction, h as StorageUnavailableError, ht as ACTION_KINDS, i as createWebRuntime, it as split, j as createFullStateCodec, jt as getTextDirection, k as DEFAULT_NAV_PARAM, kt as resolveMessages, l as encodeSnapshot, lt as SPLIT_VISIBILITIES, m as SessionError, mt as ActionDispatcher, n as definePage, nt as isTabsNode, o as collectLeafKeys, ot as tabs, p as SESSION_DEFAULT_VERSION, pt as Router, q as resolveActivePath, qt as ConsoleLoggerFactory, r as resolveInitialNavigation, rt as leaf, s as createNavigationScopedState, st as NAVIGATION_NODE_KINDS, t as parseCookieString, tt as isStackNode, u as NavigationCommitError, ut as route, v as ServerControllerProxy, vt as isFlowAction, w as defineWebApp, wt as defineModule, x as getFrameworkBuildId, xt as provide, y as FRAMEWORK_PROTOCOL_VERSION, yt as makeExternalUrlAction, z as findNearestStack, zt as detectPlatform } from "./src-D_Bzbr80.mjs";
2
+ import { C as bool, D as str, E as oneOf, O as uuid, S as withDefault, T as num, _ as mapEach, a as safeErrorPage, b as HttpClient, c as ConsoleEventRecorder, d as getBaseUrl, f as removeHost, g as isSome, h as isNone, i as rewrite, k as makeSchema, l as CompositeEventRecorder, m as removeScheme, n as next, o as WithFieldsRecorder, p as removeQueryParams, r as redirect, s as VoidEventRecorder, t as deny, u as buildUrl, v as pipe, w as int, x as optional, y as pipeAsync } from "./types-DD09OkGZ.mjs";
3
+ import { i as HttpError, n as classifyUrl, r as HostGuardError, t as classifyHost } from "./host-guard-DX4e3-45.mjs";
4
4
  import { n as ExecutionError, r as executionErrorFromHttp, t as compilePath } from "./path-CGFl2w7D.mjs";
5
5
  import { a as isPublicMarked, i as getPublicFields, n as BASE_PAGE_FIELDS, o as markPublic, r as FINESOFT_PUBLIC, s as BaseController, t as resolveConfiguredMessages } from "./messages-CAt2QdGr.mjs";
6
6
  import { t as LruMap } from "./lru-map-BKoUAySU.mjs";
package/dist/worker.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as HttpHost, i as HttpHandlerOptions, l as runManagedTask, o as ManagedTask, r as HttpHandler, s as createHttpHandler } from "./http-B6CJqDyf.mjs";
1
+ import { a as HttpHost, i as HttpHandlerOptions, l as runManagedTask, o as ManagedTask, r as HttpHandler, s as createHttpHandler } from "./http-B8r7MWao.mjs";
2
2
  export { type HttpHandler, type HttpHandlerOptions, type HttpHost, type ManagedTask, createHttpHandler, runManagedTask };
@@ -199,6 +199,8 @@ Generated page inputs use `ControllerContext` or `ServerControllerContext`. Port
199
199
 
200
200
  A server controller module may export controller classes and types only. Its local runtime imports are also private to the server; importing these dependencies from client code fails. Use `import type` for shared contracts and separate modules for client implementations. Keep secret-dependent construction inside the protected module or server configuration, rather than arguments to the shared `create` factory.
201
201
 
202
+ The compiler recognizes module-scope class declarations, variable-bound and default class expressions, transparent parentheses/type assertions, local aliases, namespace imports and static re-exports. Unsupported factories or computed selections involving the raw server base fail the browser build instead of returning original source. Prefer a module-scope class when that diagnostic occurs. Pure re-export barrels and ordinary consumers of the generated controller references remain shared.
203
+
202
204
  Client bundles and source maps omit the replaced implementation. The development plugin rejects raw controller assets and direct browser source requests for protected dependencies. It indexes modules once, caches ASTs, and reanalyzes changed files. Dependencies remain protected for the lifetime of a development server; restart when intentionally moving one back into shared code. Computed file paths, `public/` assets, and independently copied source are outside the static import boundary. Development servers still belong on trusted development networks.
203
205
 
204
206
  Use the framework Vite plugin and a request host such as Node or Worker. Static-only hosting cannot run the remote endpoint. The endpoint loads registered server page controllers; it does not expose arbitrary methods or replace explicit login/registration command endpoints.
@@ -34,10 +34,14 @@ const api = new UserApi({ baseUrl: "https://api.example.com", fetch, ...nodeSafe
34
34
 
35
35
  Browser code may construct the same class with an absolute API URL, its chosen fetch implementation and `validateDns: false`. That explicitly gives DNS resolution to the browser. Do not silently disable a required server capability. Internal/loopback targets are blocked by default; host configuration must deliberately opt in where required.
36
36
 
37
+ The shared literal/connection policy also rejects the non-public benchmark range `198.18.0.0/15`, including mapped IPv6 forms. For hosts without a DNS policy, `trustedOrigins` lists exact origins that the application explicitly trusts without DNS checks; all other hostnames still require a resolver. These exceptions are checked on every redirect and do not allow private IP literals. Cloudflare adapter configuration is described in [Deployment hosts](./09-server-and-deployment.md).
38
+
37
39
  The Node entry includes its connection transport as a lazy chunk; consumers need no extra transport package. Standard Node/Vercel/Netlify, development and preview hosts use `nodeSafeFetchOptions`: all DNS answers are checked inside connection creation, and those exact addresses reach the socket while hostname, TLS SNI, cancellation and pooling remain intact. The pool loads lazily and stays outside browser/Worker graphs. Custom Node fetch implementations must honor Undici's `dispatcher` option. `nodeDnsLookup` alone is only a preflight and does not prevent a DNS change before connection.
38
40
 
39
41
  Protected fetch checks each redirect, follows at most 20 hops and removes authentication/cookie headers across origins. Explicit `manual` / `error` modes retain their meaning. Opaque browser redirects are rejected because their destination is unreadable. Redirects requiring replay of `Request.body` or a streamed body are rejected without unbounded tee buffering; use a replayable `RequestInit.body` (such as a string) or the final URL. `allowInternalHosts: true` explicitly opts out of these protections.
40
42
 
41
43
  For scoped business services, declare a typed provider and acquire it with `context.get(token)`; see [Dependencies](./07-di-container.md). Bind the invocation's fetch and signal so request cancellation propagates. A cancelled command may already have performed a write; cancellation is not rollback. The operation runtime does not implicitly retry or cache commands.
42
44
 
45
+ The full-template `ApiClient` forwards the entire configuration before applying its `/api` and JSON-header defaults. Subclasses should preserve `wrapFetch`, `context`, `trustedOrigins` and interceptors together with the other options; copying `validateDns: false` without its Node transport wrapper removes connection-address protection.
46
+
43
47
  Request and response interceptors run in registration order. `HttpError` represents non-success HTTP results. The generic result type describes expected data; validate untrusted response payloads at the business boundary when necessary. The lower-level protected methods are `get`, `post`, `put`, `del` and `request`; `get`/`del` accept query parameters before request options.
@@ -77,6 +77,19 @@ Node’s host requires `@hono/node-server`. Worker fetch has no Node compatibili
77
77
 
78
78
  When `setup` is a module path, that module must `export default` its setup function. Development, preview and generated hosts use this explicit export; named functions are not discovered automatically.
79
79
 
80
+ Setup runs before declared proxies, so `app.use(...)` authentication covers proxy requests as well as application routes. Register guards before terminal handlers in setup. A terminal setup route can override a proxy on the same path. A configured setup that fails to load aborts preview startup. Proxy `auth` supplies an upstream credential; it does not authenticate the incoming caller. Proxies without application guards remain public.
81
+
82
+ Cloudflare output fails closed for arbitrary `SAFE_FETCH` hostnames because the adapter has no connection-time DNS enforcement. To use a known trusted API, configure exact origins explicitly:
83
+
84
+ ```ts
85
+ import { cloudflareAdapter, finesoftFrontViteConfig } from "@finesoft/front";
86
+ finesoftFrontViteConfig({
87
+ adapter: cloudflareAdapter({ trustedOrigins: ["https://api.example.com"] }),
88
+ });
89
+ ```
90
+
91
+ This is an explicit trust exception for those origins, not DNS pinning. The origin includes scheme and port; no wildcards, paths or prefix matching are used. Redirects are checked again and private IP literals remain blocked. Relative in-process API calls retain their behavior. The `"cloudflare"` adapter shortcut has an empty trusted-origin list.
92
+
80
93
  ## Static hosting boundary
81
94
 
82
95
  `staticAdapter` reads built `render.routes` by default, uses the same SSR host to generate HTML and awaits cleanup. `dynamicRoutes` supplies concrete dynamic paths; `routesExport` is an explicit extension. Discovery and rendering failures fail the build. Plain HTML cannot express redirects, error status, Set-Cookie or custom HTTP response headers, so the adapter rejects these responses; choose a request host when they are required.
@@ -205,6 +205,8 @@ Admission receives `from`, candidate `tree`, stable `transitionId`, active `exec
205
205
 
206
206
  A denial returns an uncommitted snapshot with `rejection`, including for empty trees. Session restore rejects it before replacing scopes or slices. Initial browser denial renders the error without committing navigation or recording a page visit; later denial preserves the displayed draft. Both flat and navigation SSR honor these policies and emit no rejected page data or public-cache permission. CSR shells still defer navigation to the browser.
207
207
 
208
+ If any visible page fails during initial navigation, the whole candidate stays uncommitted and only its error presentation is exposed. Successful siblings are withheld from the native view and SSR hydration data until a successful retry passes the commit boundary. Initial browser redirects that hand off to an external site likewise withhold successful siblings while the browser leaves. Page failures do not invoke `beforeCommit`; later failed navigation preserves the previously committed view.
209
+
208
210
  Owned back/forward rejection compensates to the committed history entry and restores its scroll identity. Additive history metadata preserves ownership and position across reloads. Entries without compatible ownership metadata are diagnosed; the framework cannot infer a safe traversal distance for external entries.
209
211
 
210
212
  Low-level hosts configuring `createWebSession({ createContext })` return a `NavigationContext` directly. The execution supplies its DI container and cancellation signal; request cookies and headers stay in the host context. The session uses the application's `getErrorPage` unless explicitly overridden.
@@ -15,3 +15,27 @@ await runtime.execute(operation, input, {
15
15
  ```
16
16
 
17
17
  Use `provide({ token, lifetime: "scope", create, dispose })` for per-request resources. Concurrent initialization deduplicates and a failed initialization may retry. Runtime-scoped providers cannot depend on request-scoped values. Externally supplied values remain external unless owned explicitly; created values are released in dependency order. A command is not retried or cached automatically; cancellation does not roll back an already applied mutation.
18
+
19
+ ## Query cache partitions
20
+
21
+ Cached queries with invocation bindings or an invocation-specific `fetch` default to execution-local reuse, even with `scope: "runtime"`. Bindings can contain opaque resources and tenant-specific providers, so the runtime does not serialize them into a shared key. Web and SSR hosts also supply bindings and therefore use this isolation by default. Queries without invocation bindings/fetch retain runtime reuse partitioned by identity, locale and input.
22
+
23
+ To share completed results across executions, explicitly declare the complete trusted security partition:
24
+
25
+ ```ts
26
+ const tenantSummary = defineOperation({
27
+ id: "tenant-summary",
28
+ kind: "query",
29
+ cache: {
30
+ ttlMs: 10_000,
31
+ partition: (context) => {
32
+ const tenant = context.bindings.tenant;
33
+ if (typeof tenant !== "string") throw new ExecutionError("configuration");
34
+ return tenant;
35
+ },
36
+ },
37
+ handler: loadTenantSummary,
38
+ });
39
+ ```
40
+
41
+ The partition must cover every result-affecting binding and fetch authority in addition to the existing identity, locale and input key. A constant partition asserts that the result is independent of all other invocation state. `cache.key(input)` still controls only the input key; it cannot enable shared caching by itself. Policies run before every cache hit, invalidation still applies across partitions, and `scope: "execution"` always remains local.
@@ -150,6 +150,8 @@ export class AccountController extends BaseServerController {
150
150
 
151
151
  服务端 Controller 模块只导出 Controller 类和类型。该模块的本地运行时依赖也属于服务端边界;客户端直接导入这些依赖会报错。共享类型请使用 `import type`,共享的客户端实现放在独立模块。构造函数内的依赖创建属于受保护模块;不要在共享的 `create` 工厂参数、共享配置或页面组件中写入机密。
152
152
 
153
+ 编译器识别模块级类声明、绑定到变量或默认导出的类表达式、括号/类型断言、本地别名、命名空间导入及静态转导出。涉及原始服务端基类但无法安全代理的工厂或计算属性形式会令浏览器构建报错,不再直接返回原始源码;出现该诊断时改用模块级类。纯转导出模块和使用生成代理的普通业务模块仍可共享。
154
+
153
155
  生产客户端 JS / source map 不包含被替换的实现。开发服务拒绝 Controller 的原始资源导入和受保护依赖的源码请求。模块与依赖索引在启动时建立,后续缓存 AST,只重新分析变更文件;开发期间已识别的私有依赖保持保护,若要将它迁回共享模块,应重启开发服务。动态计算的文件路径、`public/` 资源和另行复制的源码不属于静态导入边界。开发服务器仍只用于受信任的开发环境。
154
156
 
155
157
  使用框架 Vite 插件及具有 SSR 请求处理能力的 Node / Worker 等宿主。纯静态托管没有远程执行端点。此入口用于注册的服务端页面 Controller,不会自动发布任意方法或代替登录、注册等命令端点。