@tnldotdev/tnl 0.1.0-rc.24 → 0.1.0-rc.26

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
@@ -41,11 +41,14 @@ export default defineConfig({
41
41
  });
42
42
  ```
43
43
 
44
- Each service overrides root defaults. Commands run in the service directory,
45
- which must exist within the project root. Choose explicitly when several
46
- services exist: `tnl dev web` or `tnl publish api`. A single service is selected
47
- automatically. Service names are 1-32 lowercase ASCII letters/digits/hyphens,
48
- begin with a letter, and cannot end with a hyphen; at most 32 services are allowed.
44
+ Each project service inherits the root settings and can override them. Commands
45
+ run in the service directory, which must be inside the project root. If the
46
+ project has several services, choose one with a command such as `tnl dev web` or
47
+ `tnl publish api`. A project with one service selects it automatically.
48
+
49
+ Service names must start with a lowercase ASCII letter. They may contain
50
+ lowercase letters, digits, and hyphens, cannot end with a hyphen, and may be up
51
+ to 32 characters long. A project may define up to 32 services.
49
52
 
50
53
  `defineConfig` also accepts a synchronous or asynchronous factory:
51
54
 
@@ -56,25 +59,27 @@ export default defineConfig(({ worktree }) => ({
56
59
  }));
57
60
  ```
58
61
 
59
- `worktree.label` combines a readable name with an eight-character hash, stable
60
- for one client state directory but distinct across worktrees and installations.
61
- Its private random input is not exposed. Factories receive deeply frozen
62
- `cwd`, `env`, and `worktree` context. `cwd` is the invocation directory; Node
63
- executes from the configuration directory. Both the loader environment and
64
- `context.env` omit `TNL_*` and `TNLD_*`, not arbitrary application secrets.
65
-
66
- Configuration executes trusted project code, not a sandbox. `defineConfig` is
67
- type assistance, not runtime validation; the native client validates the result.
68
- TypeScript uses camel-case fields and implicitly version 1. Static YAML/JSON
69
- requires `version: 1` and snake-case fields; see
62
+ `worktree.label` combines a readable name with an eight-character hash. It stays
63
+ the same for a worktree and client state directory, but differs across worktrees
64
+ and installations. The private value used to create the hash is not exposed.
65
+
66
+ Factories receive read-only `cwd`, `env`, and `worktree` values. `cwd` is the
67
+ directory where the command started. Node runs the configuration file from its
68
+ own directory. The loader and `context.env` omit `TNL_*` and `TNLD_*` variables,
69
+ but they do not remove other application secrets.
70
+
71
+ TypeScript configuration runs as trusted project code. It is not sandboxed.
72
+ `defineConfig` provides type checking; the native client still validates the
73
+ result at runtime. TypeScript uses camel-case fields and is always version 1.
74
+ Static YAML and JSON require `version: 1` and snake-case fields; see
70
75
  [discovery and precedence](../../README.md#project-configuration) and the
71
76
  [JSON Schema](https://tnl.dev/schema/v1.json).
72
77
 
73
- `tunnel.host` and `tunnel.subdomain` are alternatives, as are `public: true` and
74
- `allowIP`. Service overrides replace the corresponding inherited alternative.
75
- `dev.port` forces the exact listener port. `dev.startupTimeout` defaults to two
76
- minutes and must be positive and at most ten minutes. `dev.command` is an
77
- argument array, not a shell command string.
78
+ Choose either `tunnel.host` or `tunnel.subdomain`. Choose either `public: true`
79
+ or `allowIP`. A project-service override replaces the other inherited choice.
80
+ `dev.port` requires the local service to use that port. `dev.startupTimeout`
81
+ defaults to two minutes and must be greater than zero and no more than ten
82
+ minutes. `dev.command` is an argument array, not a shell command string.
78
83
 
79
84
  ## Project Runtime
80
85
 
@@ -86,11 +91,13 @@ tnl login https://control.tnl.example.com --token
86
91
  tnl config generate
87
92
  ```
88
93
 
89
- Generation resolves the authenticated membership and ready domain for the root
90
- and every service, including services with server/team overrides. It writes
91
- `.tnl/project.json` and `.tnl/project.d.ts`. Regenerate after changing service,
92
- server, team, or domain configuration; `tnl dev` also generates metadata when
93
- project configuration is present. Keep `.tnl` ignored by Git.
94
+ Generation selects the current membership and a ready domain for the project and
95
+ each service. It also respects service-specific server and team settings. The
96
+ command writes `.tnl/project.json` and `.tnl/project.d.ts`.
97
+
98
+ Regenerate after changing project services, servers, teams, or domains. The
99
+ `tnl dev` command also generates metadata when the project has a configuration
100
+ file. Keep `.tnl` ignored by Git.
94
101
 
95
102
  Add the declaration to the application's existing TypeScript `include` list.
96
103
  For an app at the project root, include `.tnl/project.d.ts`; for the example's
@@ -102,9 +109,10 @@ For an app at the project root, include `.tnl/project.d.ts`; for the example's
102
109
  }
103
110
  ```
104
111
 
105
- Preserve other framework-required includes. The augmentation gives exact
106
- service keys and literal hostname/URL types. Without it, types remain broad;
107
- check that a service exists before accessing it.
112
+ Keep any other entries required by the framework. The generated declaration
113
+ adds the configured service names and their exact hostname and URL types. Without
114
+ it, service names use a general string type, so check that a service exists
115
+ before reading it.
108
116
 
109
117
  The integrations expose this browser-safe runtime during development:
110
118
 
@@ -122,18 +130,20 @@ if (tnl) {
122
130
  The `tnl` value is undefined during builds, previews, and production. During
123
131
  development, behavior depends on discovery:
124
132
 
125
- | Development context | Runtime and network behavior |
126
- | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
127
- | No generated metadata or explicit bootstrap | `tnl` is undefined; no tunnel configuration |
128
- | Metadata without a matching development socket | Frozen metadata, `runningUnderTnlDev: false`; no tunnel configuration |
129
- | Explicit bootstrap or discovered matching `tnl dev` socket | Assigned metadata, `runningUnderTnlDev: true`; configure and register the actual listener |
133
+ | Development context | Runtime and network behavior |
134
+ | --------------------------------------------------- | ----------------------------------------------------------------------------------------- |
135
+ | No generated metadata or `tnl dev` environment | `tnl` is undefined; no tunnel configuration |
136
+ | Metadata without a matching development socket | Frozen metadata, `runningUnderTnlDev: false`; no tunnel configuration |
137
+ | `tnl dev` environment or discovered matching socket | Assigned metadata, `runningUnderTnlDev: true`; configure and register the actual listener |
138
+
139
+ You can start `tnl dev web` in one terminal and start the framework from the
140
+ service directory in another. The integration looks for the development socket
141
+ when the framework configuration loads; it does not keep searching afterward.
130
142
 
131
- Socket discovery supports starting `tnl dev web` first and the framework from
132
- its service directory in another terminal. Discovery happens when framework
133
- configuration loads, not continuously. Metadata is a snapshot, not route
134
- readiness or service health. Values are deeply frozen; malformed metadata throws
135
- rather than silently becoming undefined. `tnl publish` cannot inject metadata
136
- into an already-running application.
143
+ Project metadata is a snapshot. It does not show whether the route or local
144
+ service is healthy. The values are read-only, and malformed metadata causes an
145
+ error. `tnl publish` cannot add metadata to an application that is already
146
+ running.
137
147
 
138
148
  ## Next.js
139
149
 
@@ -146,16 +156,18 @@ export default withTnl({
146
156
  });
147
157
  ```
148
158
 
149
- `withTnl` preserves object, promised, synchronous-function, and
150
- asynchronous-function configuration. During `tnl dev`, it adds the assigned
151
- hostname to `allowedDevOrigins`, injects the project runtime, and registers the
152
- actual listener target reported by Next.js after it binds. Existing host and
153
- port choices, including Next.js defaults and custom values, are preserved. An
154
- unforced port may use Next.js's normal occupied-port retry behavior without
155
- tunneling a different process on the preferred port. A port forced by
156
- `tnl dev --port` remains exact. Host settings remain independent, so a project
157
- can intentionally expose its development server on the LAN as well as through
158
- tnl.
159
+ `withTnl` accepts every Next.js configuration form: an object, a promise, or a
160
+ synchronous or asynchronous function. During `tnl dev`, it:
161
+
162
+ - Adds the assigned hostname to `allowedDevOrigins`.
163
+ - Injects the project runtime.
164
+ - Registers the listener after Next.js reports the port it actually used.
165
+
166
+ The integration preserves existing host and port settings. If no port was
167
+ forced, Next.js can retry when its preferred port is occupied. `tnl` waits for
168
+ the port Next.js chooses instead of forwarding to another process. A port set by
169
+ `tnl dev --port` must match exactly. Host settings remain independent, so the
170
+ development server can still be exposed on the LAN.
159
171
 
160
172
  Next.js 16.3.4 or newer is supported.
161
173
 
@@ -172,12 +184,14 @@ export default defineConfig({
172
184
  ```
173
185
 
174
186
  During `tnl dev`, the plugin allows the assigned hostname, injects the project
175
- runtime, and registers Vite's actual post-bind target. It preserves Vite's
176
- default or configured host and port behavior, including occupied-port retries;
177
- a port forced by `tnl dev --port` remains exact. User host settings can
178
- independently expose Vite on the LAN. Without a development socket the plugin
179
- only injects generated metadata. Builds and previews remain inert. Vite 6.0.9
180
- or newer is supported.
187
+ runtime, and registers the listener after Vite chooses its port. Vite keeps its
188
+ normal host and port behavior, including retries when a port is occupied. A port
189
+ set by `tnl dev --port` must match exactly. Host settings can still expose Vite
190
+ on the LAN.
191
+
192
+ Without a development socket, the plugin only injects generated project
193
+ metadata. It does nothing during builds and previews. Vite 6.0.9 or newer is
194
+ supported.
181
195
 
182
196
  Next.js and Vite are optional peers, so only the framework already used by the
183
197
  project is required. Keep hostname, policy, server, service, and command
@@ -185,12 +199,14 @@ settings in project configuration rather than passing integration options.
185
199
 
186
200
  ### Listener Requirements
187
201
 
188
- The target must be loopback HTTP. Wildcard bindings (`0.0.0.0` or `::`) allow
189
- LAN exposure while tnl connects through loopback; binding only a specific LAN
190
- address is rejected. Vite middleware mode has no supported listening target.
191
- Next.js must report an HTTP listener origin. Forced ports are checked against
192
- the actual listener, not assumed from configuration. Vite's `allowedHosts: true`
193
- is preserved; the integration does not re-enable host filtering you disabled.
202
+ The target must use HTTP over loopback. A wildcard binding (`0.0.0.0` or `::`)
203
+ is allowed because tnl can still connect through loopback. Binding only to a
204
+ specific LAN address is not supported. Vite middleware mode does not provide a
205
+ supported listener. Next.js must report an HTTP listener URL.
206
+
207
+ When a port is forced, the integration checks the actual listener instead of
208
+ trusting configuration alone. The integration preserves Vite's
209
+ `allowedHosts: true` and will not re-enable host filtering.
194
210
 
195
211
  Deploy `tnld` with the release container or install it from Homebrew or a
196
212
  release archive.
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { type ProjectMetadata } from "./internal/runtime.js";
2
- /** Augmented by the `.tnl/project.d.ts` file generated by the tnl client. */
2
+ /** Extended with service types from the generated `.tnl/project.d.ts` file. */
3
3
  export interface TnlProjectMetadata {
4
4
  }
5
5
  type RegisteredTnlProject = keyof TnlProjectMetadata extends never ? ProjectMetadata : Readonly<TnlProjectMetadata>;
6
6
  type TnlProject = RegisteredTnlProject & {
7
7
  readonly runningUnderTnlDev: boolean;
8
8
  };
9
- /** Validated project metadata injected by a tnl framework integration during development. */
9
+ /** Project metadata added by a tnl framework integration during development. */
10
10
  export declare const tnl: TnlProject | undefined;
11
11
  export {};
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import { parseRuntimePayload } from "./internal/runtime.js";
2
2
  const serializedRuntime = typeof process === "undefined" ? undefined : process.env?.TNL_PROJECT_RUNTIME;
3
- /** Validated project metadata injected by a tnl framework integration during development. */
3
+ /** Project metadata added by a tnl framework integration during development. */
4
4
  export const tnl = parseRuntimePayload(serializedRuntime);
@@ -37,5 +37,5 @@ export declare function registerLocalTarget(assignment: TnlTunnelAssignment, tar
37
37
  export declare function runtimePayload(project: ProjectMetadata, runningUnderTnlDev: boolean): string;
38
38
  export declare function discoverProject(cwd: string): ProjectDiscovery | null;
39
39
  export declare function socketIdentity(projectRoot: string, service: string | null): string;
40
- /** Parses digit-only listener ports in [1, 65535], preserving source-specific diagnostics. */
40
+ /** Parses a decimal listener port from 1 through 65535. */
41
41
  export declare function parseListenerPort(value: string, source: string): number;
@@ -359,7 +359,7 @@ function absoluteNormalizedPath(value, description) {
359
359
  }
360
360
  return value;
361
361
  }
362
- /** Parses digit-only listener ports in [1, 65535], preserving source-specific diagnostics. */
362
+ /** Parses a decimal listener port from 1 through 65535. */
363
363
  export function parseListenerPort(value, source) {
364
364
  if (!/^[0-9]+$/.test(value)) {
365
365
  throw new Error(`${source} must be a port between 1 and 65535`);
@@ -10,9 +10,9 @@ export interface ProjectMetadata {
10
10
  export interface ProjectRuntime extends ProjectMetadata {
11
11
  readonly runningUnderTnlDev: boolean;
12
12
  }
13
- /** Validates and deep-freezes public metadata, excluding runtime flags and Node-only discovery fields. */
13
+ /** Checks browser-safe project metadata and makes it read-only. */
14
14
  export declare function parseProjectMetadata(value: unknown, description: string): ProjectMetadata;
15
- /** Matches the native client's 1-32 byte ASCII service-name grammar without normalization. */
15
+ /** Reports whether a value is a valid 1-32 character ASCII service name. */
16
16
  export declare function validServiceName(value: unknown): value is string;
17
17
  export declare function parseRuntimePayload(serialized: string | undefined): ProjectRuntime | undefined;
18
18
  export declare function parseProjectRuntime(value: unknown, description: string): ProjectRuntime;
@@ -1,7 +1,7 @@
1
1
  const maximumRuntimeBytes = 64 * 1024;
2
2
  const maximumServices = 32;
3
3
  const serviceNamePattern = /^[a-z](?:[a-z0-9-]{0,30}[a-z0-9])?$/;
4
- /** Validates and deep-freezes public metadata, excluding runtime flags and Node-only discovery fields. */
4
+ /** Checks browser-safe project metadata and makes it read-only. */
5
5
  export function parseProjectMetadata(value, description) {
6
6
  const object = record(value, description);
7
7
  exactKeys(object, ["memberNamespace", "services"], description);
@@ -39,7 +39,7 @@ export function parseProjectMetadata(value, description) {
39
39
  services: Object.freeze(services),
40
40
  });
41
41
  }
42
- /** Matches the native client's 1-32 byte ASCII service-name grammar without normalization. */
42
+ /** Reports whether a value is a valid 1-32 character ASCII service name. */
43
43
  export function validServiceName(value) {
44
44
  return typeof value === "string" && serviceNamePattern.test(value);
45
45
  }
package/dist/next.d.ts CHANGED
@@ -6,5 +6,5 @@ export interface NextConfigContext {
6
6
  /** A function that returns Next.js configuration. */
7
7
  export type NextConfigFactory = (phase: string, context: NextConfigContext) => NextConfig | Promise<NextConfig>;
8
8
  export type NextConfigInput = NextConfig | Promise<NextConfig> | NextConfigFactory;
9
- /** Adds tnl project metadata and safe `tnl dev` routing to a Next.js development server. */
9
+ /** Configures a Next.js development server for `tnl dev` and adds project metadata. */
10
10
  export declare function withTnl(config?: NextConfigInput, ...extra: never[]): NextConfigFactory;
package/dist/next.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { canonicalLoopbackTarget, parseListenerPort, readDevelopmentContext, registerLocalTarget, requestTunnelAssignment, runtimePayload, } from "./internal/dev.js";
2
2
  const developmentServerPhase = "phase-development-server";
3
3
  const runtimeEnvironmentName = "TNL_PROJECT_RUNTIME";
4
- /** Adds tnl project metadata and safe `tnl dev` routing to a Next.js development server. */
4
+ /** Configures a Next.js development server for `tnl dev` and adds project metadata. */
5
5
  export function withTnl(config = {}, ...extra) {
6
6
  if (extra.length !== 0) {
7
7
  throw new Error("withTnl() does not accept tunnel options; use project configuration");
package/dist/vite.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import type { Plugin } from "vite";
2
- /** Adds tnl project metadata and safe `tnl dev` routing to a Vite development server. */
2
+ /** Configures a Vite development server for `tnl dev` and adds project metadata. */
3
3
  export default function tnl(...arguments_: never[]): Plugin;
package/dist/vite.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { canonicalLoopbackTarget, readDevelopmentContext, registerLocalTarget, requestTunnelAssignment, runtimePayload, } from "./internal/dev.js";
2
2
  const runtimeDefineName = "process.env.TNL_PROJECT_RUNTIME";
3
- /** Adds tnl project metadata and safe `tnl dev` routing to a Vite development server. */
3
+ /** Configures a Vite development server for `tnl dev` and adds project metadata. */
4
4
  export default function tnl(...arguments_) {
5
5
  if (arguments_.length !== 0) {
6
6
  throw new Error("tnl() does not accept tunnel options; use project configuration");
package/lib/config.d.ts CHANGED
@@ -45,16 +45,16 @@ export interface Dev {
45
45
  startupTimeout?: string;
46
46
  }
47
47
 
48
- /** Details about the Git worktree or project directory containing tnl.config.ts. */
48
+ /** The Git worktree or project directory that contains tnl.config.ts. */
49
49
  export interface TnlWorktree {
50
50
  readonly isGit: boolean;
51
- /** DNS-safe name keyed to this worktree and client state directory. */
51
+ /** DNS-safe worktree label derived from this worktree and client state. */
52
52
  readonly label: string;
53
53
  readonly name: string;
54
54
  readonly root: string;
55
55
  }
56
56
 
57
- /** Values available to a tnl.config.ts configuration factory. */
57
+ /** Values passed to a tnl.config.ts configuration factory. */
58
58
  export interface TnlConfigContext {
59
59
  readonly cwd: string;
60
60
  readonly env: Readonly<Record<string, string>>;
@@ -65,5 +65,5 @@ export type TnlConfigFactory = (context: TnlConfigContext) => TNL | Promise<TNL>
65
65
 
66
66
  export type TnlConfigInput = TNL | TnlConfigFactory;
67
67
 
68
- /** Provides type checking for an implicit-version-1 tnl.config.ts configuration. */
68
+ /** Adds type checking to a version-1 tnl.config.ts configuration. */
69
69
  export declare function defineConfig<const Config extends TnlConfigInput>(config: Config): Config;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tnldotdev/tnl",
3
- "version": "0.1.0-rc.24",
3
+ "version": "0.1.0-rc.26",
4
4
  "description": "The tnl client and framework integrations for project-local development.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -71,12 +71,12 @@
71
71
  "node": ">=22.18"
72
72
  },
73
73
  "optionalDependencies": {
74
- "@tnldotdev/tnl-darwin-arm64": "0.1.0-rc.24",
75
- "@tnldotdev/tnl-darwin-x64": "0.1.0-rc.24",
76
- "@tnldotdev/tnl-linux-arm64": "0.1.0-rc.24",
77
- "@tnldotdev/tnl-linux-x64": "0.1.0-rc.24"
74
+ "@tnldotdev/tnl-darwin-arm64": "0.1.0-rc.26",
75
+ "@tnldotdev/tnl-darwin-x64": "0.1.0-rc.26",
76
+ "@tnldotdev/tnl-linux-arm64": "0.1.0-rc.26",
77
+ "@tnldotdev/tnl-linux-x64": "0.1.0-rc.26"
78
78
  },
79
79
  "tnl": {
80
- "commit": "f7f6dd0398b8f79755791d73c74d6fdddc9b25f4"
80
+ "commit": "d6e8318fb2738a4377e67856e9eb6fe017e6097a"
81
81
  }
82
82
  }