@tnldotdev/tnl 0.1.0-rc.13 → 0.1.0-rc.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/next.js ADDED
@@ -0,0 +1,85 @@
1
+ import { canonicalLoopbackTarget, readDevelopmentContext, registerLocalTarget, requestTunnelAssignment, runtimePayload, } from "./internal/dev.js";
2
+ const developmentServerPhase = "phase-development-server";
3
+ const runtimeEnvironmentName = "TNL_PROJECT_RUNTIME";
4
+ /** Adds tnl project metadata and safe `tnl dev` routing to a Next.js development server. */
5
+ export function withTnl(config = {}, ...extra) {
6
+ if (extra.length !== 0) {
7
+ throw new Error("withTnl() does not accept tunnel options; use project configuration");
8
+ }
9
+ return async function tnlNextConfig(phase, context) {
10
+ const resolved = typeof config === "function" ? await config(phase, context) : await config;
11
+ const nextConfig = resolved ?? {};
12
+ if (phase !== developmentServerPhase) {
13
+ return nextConfig;
14
+ }
15
+ const development = readDevelopmentContext();
16
+ if (development.bootstrap === null) {
17
+ if (development.localProject === null) {
18
+ return nextConfig;
19
+ }
20
+ return injectRuntime(nextConfig, runtimePayload(development.localProject, false));
21
+ }
22
+ const allowedDevOrigins = nextConfig.allowedDevOrigins ?? [];
23
+ if (!Array.isArray(allowedDevOrigins)) {
24
+ throw new Error("Next.js allowedDevOrigins must be an array when used with tnl");
25
+ }
26
+ const target = nextTarget(process.env);
27
+ const assignment = await requestTunnelAssignment("next", development.bootstrap);
28
+ await registerLocalTarget(assignment, target);
29
+ return injectRuntime({
30
+ ...nextConfig,
31
+ allowedDevOrigins: unique([...allowedDevOrigins, assignment.hostname]),
32
+ }, runtimePayload(assignment.project, true));
33
+ };
34
+ }
35
+ function injectRuntime(config, payload) {
36
+ return {
37
+ ...config,
38
+ env: {
39
+ ...config.env,
40
+ [runtimeEnvironmentName]: payload,
41
+ },
42
+ };
43
+ }
44
+ function nextTarget(environment) {
45
+ // Next 16.3.4 binds and sets this origin before loading next.config, so it is authoritative.
46
+ const value = environment.__NEXT_PRIVATE_ORIGIN;
47
+ if (value === undefined) {
48
+ throw new Error("Next.js did not report its bound development listener to tnl dev");
49
+ }
50
+ let origin;
51
+ try {
52
+ origin = new URL(value);
53
+ if (origin.protocol !== "http:" ||
54
+ origin.username !== "" ||
55
+ origin.password !== "" ||
56
+ origin.pathname !== "/" ||
57
+ origin.search !== "" ||
58
+ origin.hash !== "" ||
59
+ origin.port === "") {
60
+ throw new Error("invalid origin");
61
+ }
62
+ }
63
+ catch (error) {
64
+ throw new Error("Next.js reported an invalid development listener", { cause: error });
65
+ }
66
+ const port = parsePort(origin.port, "Next.js listener");
67
+ const reportedPort = environment.PORT;
68
+ if (reportedPort !== undefined && parsePort(reportedPort, "PORT") !== port) {
69
+ throw new Error("Next.js reported inconsistent development listener ports");
70
+ }
71
+ return canonicalLoopbackTarget(origin.hostname, port);
72
+ }
73
+ function parsePort(value, source) {
74
+ if (!/^[0-9]+$/.test(value)) {
75
+ throw new Error(`${source} must be a port between 1 and 65535`);
76
+ }
77
+ const port = Number(value);
78
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
79
+ throw new Error(`${source} must be a port between 1 and 65535`);
80
+ }
81
+ return port;
82
+ }
83
+ function unique(values) {
84
+ return [...new Set(values)];
85
+ }
package/dist/vite.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from "vite";
2
+ /** Adds tnl project metadata and safe `tnl dev` routing to a Vite development server. */
3
+ export default function tnl(...arguments_: never[]): Plugin;
package/dist/vite.js ADDED
@@ -0,0 +1,98 @@
1
+ import { canonicalLoopbackTarget, readDevelopmentContext, registerLocalTarget, requestTunnelAssignment, runtimePayload, } from "./internal/dev.js";
2
+ const runtimeDefineName = "process.env.TNL_PROJECT_RUNTIME";
3
+ /** Adds tnl project metadata and safe `tnl dev` routing to a Vite development server. */
4
+ export default function tnl(...arguments_) {
5
+ if (arguments_.length !== 0) {
6
+ throw new Error("tnl() does not accept tunnel options; use project configuration");
7
+ }
8
+ let assignment = null;
9
+ let localPortRegistered = false;
10
+ return {
11
+ name: "tnl",
12
+ enforce: "post",
13
+ apply: "serve",
14
+ async config(userConfig = {}, configEnvironment) {
15
+ assignment = null;
16
+ localPortRegistered = false;
17
+ if (configEnvironment.command !== "serve" || configEnvironment.isPreview === true) {
18
+ return undefined;
19
+ }
20
+ const development = readDevelopmentContext();
21
+ if (development.bootstrap === null) {
22
+ if (development.localProject === null) {
23
+ return undefined;
24
+ }
25
+ return runtimeDefine(runtimePayload(development.localProject, false));
26
+ }
27
+ const server = userConfig.server ?? {};
28
+ validateAllowedHosts(server.allowedHosts);
29
+ assignment = await requestTunnelAssignment("vite", development.bootstrap);
30
+ const result = {
31
+ ...runtimeDefine(runtimePayload(assignment.project, true)),
32
+ server: {
33
+ allowedHosts: addAllowedHost(server.allowedHosts, assignment.hostname),
34
+ },
35
+ };
36
+ if (development.bootstrap.port === undefined) {
37
+ return result;
38
+ }
39
+ return {
40
+ ...result,
41
+ server: {
42
+ ...result.server,
43
+ port: development.bootstrap.port,
44
+ // Let Vite report a fallback listener so tnl can reject it with the target diagnostic.
45
+ strictPort: false,
46
+ },
47
+ };
48
+ },
49
+ configureServer(server) {
50
+ const configured = assignment;
51
+ if (configured === null) {
52
+ return;
53
+ }
54
+ if (server.httpServer === null) {
55
+ throw new Error("Vite middleware mode cannot register a listening target with tnl dev");
56
+ }
57
+ const originalListen = server.listen.bind(server);
58
+ server.listen = async (port, isRestart) => {
59
+ const listening = await originalListen(port, isRestart);
60
+ if (localPortRegistered) {
61
+ return listening;
62
+ }
63
+ const address = server.httpServer?.address();
64
+ if (address === null || address === undefined || typeof address === "string") {
65
+ await server.close();
66
+ throw new Error("Vite did not report its listening port to tnl dev");
67
+ }
68
+ try {
69
+ await registerLocalTarget(configured, canonicalLoopbackTarget(address.address, address.port));
70
+ localPortRegistered = true;
71
+ }
72
+ catch (error) {
73
+ await server.close();
74
+ throw error;
75
+ }
76
+ return listening;
77
+ };
78
+ },
79
+ };
80
+ }
81
+ function runtimeDefine(payload) {
82
+ return {
83
+ define: {
84
+ [runtimeDefineName]: JSON.stringify(payload),
85
+ },
86
+ };
87
+ }
88
+ function addAllowedHost(allowedHosts, hostname) {
89
+ if (allowedHosts === true) {
90
+ return true;
91
+ }
92
+ return [...new Set([...(allowedHosts ?? []), hostname])];
93
+ }
94
+ function validateAllowedHosts(value) {
95
+ if (value !== undefined && value !== true && !Array.isArray(value)) {
96
+ throw new Error("Vite server.allowedHosts must be an array or true when used with tnl");
97
+ }
98
+ }
@@ -0,0 +1,69 @@
1
+ export interface TNL {
2
+ server?: string;
3
+ team?: string;
4
+ tunnel?: {
5
+ host?: string;
6
+ subdomain?: string;
7
+ public?: boolean;
8
+ ephemeral?: boolean;
9
+ /**
10
+ * @maxItems 63
11
+ */
12
+ allowIP?: string[];
13
+ };
14
+ publish?: Publish;
15
+ dev?: Dev;
16
+ services?: {
17
+ [k: string]: {
18
+ directory?: string;
19
+ server?: string;
20
+ team?: string;
21
+ tunnel?: {
22
+ host?: string;
23
+ subdomain?: string;
24
+ public?: boolean;
25
+ ephemeral?: boolean;
26
+ /**
27
+ * @maxItems 63
28
+ */
29
+ allowIP?: string[];
30
+ };
31
+ publish?: Publish;
32
+ dev?: Dev;
33
+ };
34
+ };
35
+ }
36
+ export interface Publish {
37
+ target?: string | number;
38
+ }
39
+ export interface Dev {
40
+ /**
41
+ * @minItems 1
42
+ */
43
+ command?: [string, ...string[]];
44
+ port?: number;
45
+ startupTimeout?: string;
46
+ }
47
+
48
+ /** Details about the Git worktree or project directory containing tnl.config.ts. */
49
+ export interface TnlWorktree {
50
+ readonly isGit: boolean;
51
+ /** DNS-safe name keyed to this worktree and client state directory. */
52
+ readonly label: string;
53
+ readonly name: string;
54
+ readonly root: string;
55
+ }
56
+
57
+ /** Values available to a tnl.config.ts configuration factory. */
58
+ export interface TnlConfigContext {
59
+ readonly cwd: string;
60
+ readonly env: Readonly<Record<string, string>>;
61
+ readonly worktree: TnlWorktree;
62
+ }
63
+
64
+ export type TnlConfigFactory = (context: TnlConfigContext) => TNL | Promise<TNL>;
65
+
66
+ export type TnlConfigInput = TNL | TnlConfigFactory;
67
+
68
+ /** Provides type checking for an implicit-version-1 tnl.config.ts configuration. */
69
+ export declare function defineConfig<const Config extends TnlConfigInput>(config: Config): Config;
package/lib/config.mjs ADDED
@@ -0,0 +1,3 @@
1
+ export function defineConfig(config) {
2
+ return config;
3
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tnldotdev/tnl",
3
- "version": "0.1.0-rc.13",
4
- "description": "The tnl client for project-local development workflows.",
3
+ "version": "0.1.0-rc.17",
4
+ "description": "The tnl client and framework integrations for project-local development.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -13,22 +13,70 @@
13
13
  },
14
14
  "files": [
15
15
  "bin",
16
- "lib"
16
+ "dist",
17
+ "lib",
18
+ "NOTICE",
19
+ "THIRD_PARTY_LICENSES.txt"
17
20
  ],
18
21
  "type": "module",
22
+ "sideEffects": false,
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.js",
27
+ "default": "./dist/index.js"
28
+ },
29
+ "./config": {
30
+ "types": "./lib/config.d.ts",
31
+ "import": "./lib/config.mjs"
32
+ },
33
+ "./next": {
34
+ "types": "./dist/next.d.ts",
35
+ "import": "./dist/next.js",
36
+ "default": "./dist/next.js"
37
+ },
38
+ "./vite": {
39
+ "types": "./dist/vite.d.ts",
40
+ "import": "./dist/vite.js",
41
+ "default": "./dist/vite.js"
42
+ }
43
+ },
19
44
  "publishConfig": {
20
45
  "access": "public"
21
46
  },
47
+ "scripts": {
48
+ "build": "pnpm run clean && tsc --project tsconfig.json",
49
+ "clean": "node ../../scripts/clean-package.mjs",
50
+ "prepack": "pnpm run build"
51
+ },
52
+ "devDependencies": {
53
+ "next": "16.3.4",
54
+ "react": "19.2.8",
55
+ "react-dom": "19.2.8",
56
+ "vite": "8.2.2"
57
+ },
58
+ "peerDependencies": {
59
+ "next": ">=16.3.4",
60
+ "vite": ">=6.0.9"
61
+ },
62
+ "peerDependenciesMeta": {
63
+ "next": {
64
+ "optional": true
65
+ },
66
+ "vite": {
67
+ "optional": true
68
+ }
69
+ },
22
70
  "engines": {
23
- "node": ">=22.15"
71
+ "node": ">=22.18"
24
72
  },
25
73
  "optionalDependencies": {
26
- "@tnldotdev/tnl-darwin-arm64": "0.1.0-rc.13",
27
- "@tnldotdev/tnl-darwin-x64": "0.1.0-rc.13",
28
- "@tnldotdev/tnl-linux-arm64": "0.1.0-rc.13",
29
- "@tnldotdev/tnl-linux-x64": "0.1.0-rc.13"
74
+ "@tnldotdev/tnl-darwin-arm64": "0.1.0-rc.17",
75
+ "@tnldotdev/tnl-darwin-x64": "0.1.0-rc.17",
76
+ "@tnldotdev/tnl-linux-arm64": "0.1.0-rc.17",
77
+ "@tnldotdev/tnl-linux-x64": "0.1.0-rc.17"
30
78
  },
31
79
  "tnl": {
32
- "commit": "5ea468a77835db128d4ff826c126dc234595704a"
80
+ "commit": "bd70c08fe7c09aef1153e1afbbb25161d519a7ec"
33
81
  }
34
82
  }