@webtypen/webframez-core 0.3.66 → 0.3.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -63,6 +63,44 @@ Projektspezifische Ergaenzung:
63
63
 
64
64
  Wenn eine Regel aus der lokalen Projekt-AGENTS.md einer Regel aus der Paketdatei widerspricht, sollte die lokale Projektregel Vorrang haben.
65
65
 
66
+ ## Application basename
67
+
68
+ Set the public mount once in `config/application.ts`:
69
+
70
+ ```ts
71
+ export default {
72
+ router: {
73
+ get basename() { return process.env.BASENAME || ""; },
74
+ },
75
+ };
76
+ ```
77
+
78
+ Pass this config as `boot({ config: { application }, ... })`. For example,
79
+ `BASENAME=/my-app` mounts every registered route (including route groups and
80
+ module/API routes) below `/my-app`. Empty or `/` keeps the application at the
81
+ root. The legacy `boot({ basename })` option takes precedence when explicitly
82
+ provided, including `""` or `null`. The basename is normalized and validated at
83
+ boot; changing it requires restarting the application. Web and Lambda use the
84
+ same configuration.
85
+
86
+ `Route.path("/api/items")`, `appPath("/api/items")`, and
87
+ `res.redirect("/login")` include the basename. Already-prefixed URLs retain one
88
+ prefix, and external URLs, anchors, and relative URLs remain unchanged.
89
+ `Route.relativePath(...)` / `appRelativePath(...)` remove a matching basename
90
+ at a complete path boundary. Route registration always takes application-local
91
+ paths; do not pass `Route.path(...)` into `Route.get(...)`.
92
+
93
+ Node applications import these helpers from `@webtypen/webframez-core`.
94
+ Browser code can use the public, Node-free `@webtypen/webframez-core/routing`
95
+ entry. Rendering adapters provide the runtime basename to that shared API;
96
+ Core does not rewrite arbitrary HTML strings, native `fetch` calls or raw
97
+ `Location` headers. Use `appPath` for those application URLs.
98
+
99
+ `webframez-react` automatically adopts the Core basename for `Route.renderReact`,
100
+ including nested mounts and route groups. It publishes the basename for browser
101
+ navigation, forms and asset URLs. The build output directory remains independent
102
+ of the deployment path, so one artifact can be mounted at different basenames.
103
+
66
104
  ## Basic Web Setup
67
105
 
68
106
  ```ts
@@ -37,7 +37,7 @@ class LambdaApplication {
37
37
  Router_1.Router.init({
38
38
  mode: "aws-lambda",
39
39
  kernel: options && options.kernel ? options.kernel : null,
40
- basename: options && options.basename ? options.basename : null,
40
+ basename: options === null || options === void 0 ? void 0 : options.basename,
41
41
  routesFunction: options && options.routesFunction ? options.routesFunction : null,
42
42
  tempDir: options && options.tempDir ? options.tempDir : null,
43
43
  });
@@ -20,6 +20,8 @@ export declare class Response {
20
20
  * @returns Response
21
21
  */
22
22
  setServerResponse(res: ServerResponse): this;
23
+ /** Redirect to an application URL, retaining external destinations unchanged. */
24
+ redirect(location: string, status?: number): Response;
23
25
  /**
24
26
  * Set the http-status-code
25
27
  *
@@ -13,6 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.Response = void 0;
16
+ const routing_1 = require("../routing");
16
17
  const fs_1 = __importDefault(require("fs"));
17
18
  const promises_1 = require("stream/promises");
18
19
  const path_1 = __importDefault(require("path"));
@@ -37,6 +38,10 @@ class Response {
37
38
  this.res = res;
38
39
  return this;
39
40
  }
41
+ /** Redirect to an application URL, retaining external destinations unchanged. */
42
+ redirect(location, status = 302) {
43
+ return this.status(status).header("Location", (0, routing_1.appPath)(location)).send("");
44
+ }
40
45
  /**
41
46
  * Set the http-status-code
42
47
  *
@@ -3,6 +3,10 @@ type RouteRegistrationOptions = {
3
3
  };
4
4
  type RouteExtensionFactory = (route: RouteFacade) => (...args: any[]) => any;
5
5
  export declare class RouteFacade {
6
+ get basename(): string;
7
+ /** Public application URL, including the configured router basename. */
8
+ path(value: string): string;
9
+ relativePath(value: string): string;
6
10
  tempGroupPrefix: string | null;
7
11
  tempGroupMiddleware: string[] | null;
8
12
  tempGroupDomains: string[] | null;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Route = exports.RouteFacade = void 0;
4
+ const routing_1 = require("../routing");
4
5
  const Router_1 = require("./Router");
5
6
  class RouteFacade {
6
7
  constructor() {
@@ -24,6 +25,10 @@ class RouteFacade {
24
25
  Router_1.Router.register(method, (this.tempGroupPrefix ? this.tempGroupPrefix : "") + path, component, mergedOptions);
25
26
  };
26
27
  }
28
+ get basename() { return Router_1.Router.basename || ""; }
29
+ /** Public application URL, including the configured router basename. */
30
+ path(value) { return (0, routing_1.appPath)(value, this.basename); }
31
+ relativePath(value) { return (0, routing_1.appRelativePath)(value, this.basename); }
27
32
  normalizeStringArray(values) {
28
33
  if (!Array.isArray(values)) {
29
34
  return [];
@@ -13,6 +13,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.Router = void 0;
16
+ const Config_1 = require("../Config");
17
+ const routing_1 = require("../routing");
16
18
  const http_1 = require("http");
17
19
  const Response_1 = require("./Response");
18
20
  const Request_1 = require("./Request");
@@ -72,7 +74,9 @@ class RouterFacade {
72
74
  * Load the application-routes
73
75
  */
74
76
  init(options) {
75
- this.basename = options && options.basename ? options.basename : null;
77
+ this.basename = (0, routing_1.normalizeBasename)((options === null || options === void 0 ? void 0 : options.basename) !== undefined
78
+ ? options.basename : Config_1.Config.get("application.router.basename"));
79
+ globalThis.__WEBFRAMEZ_ROUTER_BASENAME__ = this.basename;
76
80
  this.kernel = options && options.kernel ? options.kernel : null;
77
81
  this.mode = options && options.mode ? options.mode : null;
78
82
  this.maxRequestBodySizeBytes =
@@ -71,7 +71,7 @@ class WebApplication {
71
71
  Router_1.Router.init({
72
72
  mode: options && options.mode ? options.mode : null,
73
73
  kernel: options && options.kernel ? options.kernel : null,
74
- basename: options && options.basename ? options.basename : null,
74
+ basename: options === null || options === void 0 ? void 0 : options.basename,
75
75
  routesFunction: options && options.routesFunction ? options.routesFunction : null,
76
76
  tempDir: options && options.tempDir ? options.tempDir : null,
77
77
  });
@@ -94,7 +94,7 @@ class WebApplication {
94
94
  res,
95
95
  kernel: options && options.kernel ? options.kernel : null,
96
96
  modulesLoader: this.modulesLoader,
97
- basename: options && options.basename ? options.basename : null,
97
+ basename: Router_1.Router.basename,
98
98
  options,
99
99
  });
100
100
  if (handled) {
@@ -149,7 +149,7 @@ class WebApplication {
149
149
  if (runtimeConsole && typeof runtimeConsole.log === "function") {
150
150
  runtimeConsole.log("Server started and listening on port " +
151
151
  port +
152
- (options && options.basename ? " (Basename: " + options && options.basename + ")" : ""));
152
+ (Router_1.Router.basename ? " (Basename: " + Router_1.Router.basename + ")" : ""));
153
153
  }
154
154
  if (options && options.onBoot) {
155
155
  options.onBoot(port);
package/dist/index.d.ts CHANGED
@@ -63,3 +63,4 @@ export * from "./Notifications/NotificationsOutputChannelsJob";
63
63
  export * from "./info";
64
64
  export type { InlineTextDefinition, InlineTextIcon } from "./Presentation/InlineText";
65
65
  export * from "./Queue/Queue";
66
+ export * from "./routing";
package/dist/index.js CHANGED
@@ -78,3 +78,4 @@ __exportStar(require("./Notifications/NotificationsOutputChannel"), exports);
78
78
  __exportStar(require("./Notifications/NotificationsOutputChannelsJob"), exports);
79
79
  __exportStar(require("./info"), exports);
80
80
  __exportStar(require("./Queue/Queue"), exports);
81
+ __exportStar(require("./routing"), exports);
@@ -0,0 +1,5 @@
1
+ export declare function normalizeBasename(value?: string | null): string;
2
+ export declare function getBasename(): string;
3
+ /** Prefix application-absolute URLs once; preserve external and relative URLs. */
4
+ export declare function appPath(value: string, basename?: string): string;
5
+ export declare function appRelativePath(value: string, basename?: string): string;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.appRelativePath = exports.appPath = exports.getBasename = exports.normalizeBasename = void 0;
4
+ function normalizeBasename(value) {
5
+ if (value == null || value === "" || value === "/")
6
+ return "";
7
+ if (typeof value !== "string")
8
+ throw new Error("Router basename must be a string.");
9
+ const base = value.trim().replace(/\/+$/, "");
10
+ if (!base && value.trim().length <= 1)
11
+ return "";
12
+ if (!/^\/(?:[A-Za-z0-9_~-]+(?:\.[A-Za-z0-9_~-]+)*)(?:\/[A-Za-z0-9_~-]+(?:\.[A-Za-z0-9_~-]+)*)*$/.test(base)) {
13
+ throw new Error("Router basename must be empty or an absolute path such as /my-app.");
14
+ }
15
+ return base;
16
+ }
17
+ exports.normalizeBasename = normalizeBasename;
18
+ function getBasename() {
19
+ var _a, _b, _c, _d;
20
+ const runtime = globalThis;
21
+ return (_d = (_c = (_b = (_a = runtime.__WEBFRAMEZ_ROUTING_CONTEXT__) === null || _a === void 0 ? void 0 : _a.getStore()) !== null && _b !== void 0 ? _b : runtime.__RSC_BASENAME) !== null && _c !== void 0 ? _c : runtime.__WEBFRAMEZ_ROUTER_BASENAME__) !== null && _d !== void 0 ? _d : "";
22
+ }
23
+ exports.getBasename = getBasename;
24
+ /** Prefix application-absolute URLs once; preserve external and relative URLs. */
25
+ function appPath(value, basename = getBasename()) {
26
+ const base = normalizeBasename(basename);
27
+ if (!base || !value.startsWith("/") || value.startsWith("//") || hasBasename(value, base))
28
+ return value;
29
+ return base + value;
30
+ }
31
+ exports.appPath = appPath;
32
+ function appRelativePath(value, basename = getBasename()) {
33
+ const base = normalizeBasename(basename);
34
+ if (!base || !hasBasename(value, base))
35
+ return value;
36
+ const relative = value.slice(base.length);
37
+ return !relative || relative.startsWith("?") || relative.startsWith("#") ? "/" + relative : relative;
38
+ }
39
+ exports.appRelativePath = appRelativePath;
40
+ function hasBasename(value, base) {
41
+ return value === base || value.startsWith(base + "/") || value.startsWith(base + "?") || value.startsWith(base + "#");
42
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webtypen/webframez-core",
3
- "version": "0.3.66",
3
+ "version": "0.3.67",
4
4
  "description": "The core package of the typescript based webframez (web-framework) by webtypen.de",
5
5
  "homepage": "https://webtypen.de/",
6
6
  "license": "MIT",
@@ -24,7 +24,9 @@
24
24
  "dist",
25
25
  "bin",
26
26
  "README.md",
27
- "agents"
27
+ "agents",
28
+ "routing.js",
29
+ "routing.d.ts"
28
30
  ],
29
31
  "scripts": {
30
32
  "build": "tsc",
package/routing.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { normalizeBasename, getBasename, appPath, appRelativePath } from './dist/routing';
package/routing.js ADDED
@@ -0,0 +1,2 @@
1
+ // Public browser-safe entry point. Do not import the Node server bundle here.
2
+ module.exports = require('./dist/routing.js');