@tnldotdev/tnl 0.1.0-rc.15 → 0.1.0-rc.20

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/NOTICE ADDED
@@ -0,0 +1,6 @@
1
+ tnl
2
+ Copyright 2026 Chase Adams
3
+
4
+ This product includes software developed for the tnl project.
5
+
6
+ Third-party license terms and notices are provided in THIRD_PARTY_LICENSES.txt.
package/README.md CHANGED
@@ -1,34 +1,196 @@
1
1
  # `@tnldotdev/tnl`
2
2
 
3
- This package installs the `tnl` client for macOS or Linux on arm64 or x64. It
4
- does not include the `tnld` server daemon.
3
+ This package installs the `tnl` client for macOS or Linux on arm64 or x64 and
4
+ provides its browser-safe project runtime plus official Next.js and Vite
5
+ integrations. It does not include the `tnld` server process.
5
6
 
6
7
  ## Install
7
8
 
8
- Install the client:
9
-
10
9
  ```console
11
10
  pnpm add --save-dev @tnldotdev/tnl@next
12
11
  ```
13
12
 
14
- Add `@tnldotdev/next` or `@tnldotdev/vite` for framework integration.
13
+ The package selects an exact-version native optional dependency for the current
14
+ platform. It has no install script and does not download executable code from a
15
+ third-party host. Node.js 22.18 or newer is required by the launcher,
16
+ integrations, and TypeScript configuration loader.
17
+
18
+ The native CLI enables pseudonymous telemetry by default. Use
19
+ `TNL_NO_TELEMETRY=true` or `--no-telemetry` to disable it; see the
20
+ [telemetry disclosure](../../README.md#telemetry).
21
+
22
+ ## Configuration
23
+
24
+ Run `tnl init` to add the package and missing project/framework configuration.
25
+ It preserves existing Next.js, Vite, and TypeScript configuration and reports
26
+ remaining integration actions. Configure an integration below before starting
27
+ a framework-discovered tunnel.
28
+
29
+ For a project with existing `apps/api` and `apps/web` directories:
30
+
31
+ ```ts
32
+ // tnl.config.ts
33
+ import { defineConfig } from "@tnldotdev/tnl/config";
34
+
35
+ export default defineConfig({
36
+ dev: { command: ["pnpm", "dev"] },
37
+ services: {
38
+ api: { directory: "apps/api", publish: { target: 3001 } },
39
+ web: { directory: "apps/web" },
40
+ },
41
+ });
42
+ ```
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.
49
+
50
+ `defineConfig` also accepts a synchronous or asynchronous factory:
51
+
52
+ ```ts
53
+ export default defineConfig(({ worktree }) => ({
54
+ tunnel: { subdomain: worktree.label },
55
+ dev: { command: ["pnpm", "dev"], startupTimeout: "90s" },
56
+ }));
57
+ ```
58
+
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
70
+ [discovery and precedence](../../README.md#project-configuration) and the
71
+ [JSON Schema](https://tnl.dev/schema/v1.json).
72
+
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
+
79
+ ## Project Runtime
80
+
81
+ Authenticate to the configured server, then generate metadata from the project
82
+ root:
83
+
84
+ ```console
85
+ tnl login https://control.tnl.example.com --token
86
+ tnl config generate
87
+ ```
88
+
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.
15
94
 
16
- Then invoke `tnl` from a package script:
95
+ Add the declaration to the application's existing TypeScript `include` list.
96
+ For an app at the project root, include `.tnl/project.d.ts`; for the example's
97
+ `apps/web/tsconfig.json`, include `../../.tnl/project.d.ts`:
17
98
 
18
99
  ```json
19
100
  {
20
- "scripts": {
21
- "dev:public": "tnl dev -- pnpm dev"
22
- }
101
+ "include": ["**/*.ts", "**/*.tsx", "../../.tnl/project.d.ts"]
23
102
  }
24
103
  ```
25
104
 
26
- Use `tnl publish 3000 --open` or `tnl dev --open -- pnpm dev` to launch the
27
- public URL once it is ready.
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.
28
108
 
29
- The package selects an exact-version native optional dependency for the current
30
- platform. It does not run an install script or download executable code from a
31
- third-party host. Node.js 22.15 or newer is required by the launcher.
109
+ The integrations expose this browser-safe runtime during development:
110
+
111
+ ```ts
112
+ import { tnl } from "@tnldotdev/tnl";
113
+
114
+ if (tnl) {
115
+ tnl.memberNamespace;
116
+ tnl.services.api.hostname;
117
+ tnl.services.api.url;
118
+ tnl.runningUnderTnlDev;
119
+ }
120
+ ```
121
+
122
+ The `tnl` value is undefined during builds, previews, and production. During
123
+ development, behavior depends on discovery:
124
+
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 |
130
+
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.
137
+
138
+ ## Next.js
139
+
140
+ ```ts
141
+ // next.config.ts
142
+ import { withTnl } from "@tnldotdev/tnl/next";
143
+
144
+ export default withTnl({
145
+ reactStrictMode: true,
146
+ });
147
+ ```
148
+
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
+
160
+ Next.js 16.3.4 or newer is supported.
161
+
162
+ ## Vite
163
+
164
+ ```ts
165
+ // vite.config.ts
166
+ import { defineConfig } from "vite";
167
+ import tnl from "@tnldotdev/tnl/vite";
168
+
169
+ export default defineConfig({
170
+ plugins: [tnl()],
171
+ });
172
+ ```
173
+
174
+ 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.
181
+
182
+ Next.js and Vite are optional peers, so only the framework already used by the
183
+ project is required. Keep hostname, policy, server, service, and command
184
+ settings in project configuration rather than passing integration options.
185
+
186
+ ### Listener Requirements
187
+
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.
32
194
 
33
195
  Deploy `tnld` with the release container or install it from Homebrew or a
34
196
  release archive.