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

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
@@ -15,65 +15,125 @@ platform. It has no install script and does not download executable code from a
15
15
  third-party host. Node.js 22.18 or newer is required by the launcher,
16
16
  integrations, and TypeScript configuration loader.
17
17
 
18
- ## Project Runtime
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.
19
28
 
20
- The framework integrations expose generated project metadata through the root
21
- module during development:
29
+ For a project with existing `apps/api` and `apps/web` directories:
22
30
 
23
31
  ```ts
24
- import { tnl } from "@tnldotdev/tnl";
32
+ // tnl.config.ts
33
+ import { defineConfig } from "@tnldotdev/tnl/config";
25
34
 
26
- if (tnl) {
27
- tnl.memberNamespace;
28
- tnl.services.api.hostname;
29
- tnl.services.api.url;
30
- tnl.runningUnderTnlDev;
31
- }
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
+ });
32
42
  ```
33
43
 
34
- The `tnl` value is undefined during builds, previews, and production. During
35
- `tnl dev`, `runningUnderTnlDev` is true and the integration receives complete
36
- project metadata from the private protocol. During an ordinary framework
37
- development server, the integration reads the generated `.tnl/project.json`
38
- file without changing network configuration and sets `runningUnderTnlDev` to
39
- false. If generated metadata is absent, `tnl` is undefined.
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.
40
49
 
41
- The tnl client also generates `.tnl/project.d.ts`. Include that directory in the
42
- application's TypeScript inputs to get exact service keys and literal hostname,
43
- URL, and common member-namespace values. Without the generated declaration,
44
- the root module uses safe broad string and service-record types.
50
+ `defineConfig` also accepts a synchronous or asynchronous factory:
45
51
 
46
- `tnl publish` cannot inject metadata into an application process that is already
47
- running. Metadata generated earlier can still be available to ordinary local
48
- development, with `runningUnderTnlDev` remaining false.
52
+ ```ts
53
+ export default defineConfig(({ worktree }) => ({
54
+ tunnel: { subdomain: worktree.label },
55
+ dev: { command: ["pnpm", "dev"], startupTimeout: "90s" },
56
+ }));
57
+ ```
49
58
 
50
- ## Configuration
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.
51
78
 
52
- The package exports `defineConfig` and project configuration types:
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.
94
+
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`:
98
+
99
+ ```json
100
+ {
101
+ "include": ["**/*.ts", "**/*.tsx", "../../.tnl/project.d.ts"]
102
+ }
103
+ ```
104
+
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.
108
+
109
+ The integrations expose this browser-safe runtime during development:
53
110
 
54
111
  ```ts
55
- // tnl.config.ts
56
- import { defineConfig } from "@tnldotdev/tnl/config";
112
+ import { tnl } from "@tnldotdev/tnl";
57
113
 
58
- export default defineConfig(({ worktree }) => ({
59
- tunnel: { subdomain: worktree.label },
60
- dev: { command: ["pnpm", "dev"] },
61
- }));
114
+ if (tnl) {
115
+ tnl.memberNamespace;
116
+ tnl.services.api.hostname;
117
+ tnl.services.api.url;
118
+ tnl.runningUnderTnlDev;
119
+ }
62
120
  ```
63
121
 
64
- `worktree.label` combines a readable worktree name with an eight-character hash.
65
- It is stable for one client state directory and differs across worktrees and
66
- installations. Its private random input is not exposed to the configuration
67
- factory.
122
+ The `tnl` value is undefined during builds, previews, and production. During
123
+ development, behavior depends on discovery:
68
124
 
69
- `tnl.config.ts` is implicitly configuration version 1. Static `tnl.yml`,
70
- `tnl.yaml`, and `tnl.json` files require `version: 1`; the JSON Schema is
71
- available at `https://tnl.dev/schema/v1.json`.
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 |
72
130
 
73
- `tnl init` adds this package, creates project and framework configuration when
74
- it is absent, and ignores generated `.tnl` state. It preserves existing Next.js,
75
- Vite, and TypeScript configuration and reports the exact integration actions
76
- still needed.
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.
77
137
 
78
138
  ## Next.js
79
139
 
@@ -115,7 +175,7 @@ During `tnl dev`, the plugin allows the assigned hostname, injects the project
115
175
  runtime, and registers Vite's actual post-bind target. It preserves Vite's
116
176
  default or configured host and port behavior, including occupied-port retries;
117
177
  a port forced by `tnl dev --port` remains exact. User host settings can
118
- independently expose Vite on the LAN. During ordinary development the plugin
178
+ independently expose Vite on the LAN. Without a development socket the plugin
119
179
  only injects generated metadata. Builds and previews remain inert. Vite 6.0.9
120
180
  or newer is supported.
121
181
 
@@ -123,5 +183,14 @@ Next.js and Vite are optional peers, so only the framework already used by the
123
183
  project is required. Keep hostname, policy, server, service, and command
124
184
  settings in project configuration rather than passing integration options.
125
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.
194
+
126
195
  Deploy `tnld` with the release container or install it from Homebrew or a
127
196
  release archive.
package/dist/index.d.ts CHANGED
@@ -1,16 +1,8 @@
1
- interface TnlServiceMetadata {
2
- readonly memberNamespace: string;
3
- readonly hostname: string;
4
- readonly url: `https://${string}`;
5
- }
6
- interface BroadTnlProject {
7
- readonly memberNamespace: string;
8
- readonly services: Readonly<Record<string, TnlServiceMetadata>>;
9
- }
1
+ import { type ProjectMetadata } from "./internal/runtime.js";
10
2
  /** Augmented by the `.tnl/project.d.ts` file generated by the tnl client. */
11
3
  export interface TnlProjectMetadata {
12
4
  }
13
- type RegisteredTnlProject = keyof TnlProjectMetadata extends never ? BroadTnlProject : Readonly<TnlProjectMetadata>;
5
+ type RegisteredTnlProject = keyof TnlProjectMetadata extends never ? ProjectMetadata : Readonly<TnlProjectMetadata>;
14
6
  type TnlProject = RegisteredTnlProject & {
15
7
  readonly runningUnderTnlDev: boolean;
16
8
  };
@@ -4,17 +4,10 @@ export interface TnlDevBootstrap {
4
4
  readonly port?: number;
5
5
  readonly socket: string;
6
6
  }
7
- export interface ProjectDocumentService {
8
- readonly memberNamespace: string;
9
- readonly hostname: string;
10
- readonly url: `https://${string}`;
11
- }
12
- export interface ProjectDocument {
13
- readonly memberNamespace: string;
7
+ export interface ProjectDocument extends ProjectMetadata {
14
8
  readonly projectRoot: string;
15
9
  readonly runningUnderTnlDev: boolean;
16
10
  readonly serviceDirectories: Readonly<Record<string, string>>;
17
- readonly services: Readonly<Record<string, ProjectDocumentService>>;
18
11
  readonly version: 1;
19
12
  }
20
13
  export interface ProjectDiscovery {
@@ -44,3 +37,5 @@ export declare function registerLocalTarget(assignment: TnlTunnelAssignment, tar
44
37
  export declare function runtimePayload(project: ProjectMetadata, runningUnderTnlDev: boolean): string;
45
38
  export declare function discoverProject(cwd: string): ProjectDiscovery | null;
46
39
  export declare function socketIdentity(projectRoot: string, service: string | null): string;
40
+ /** Parses digit-only listener ports in [1, 65535], preserving source-specific diagnostics. */
41
+ export declare function parseListenerPort(value: string, source: string): number;
@@ -4,13 +4,11 @@ import * as http from "node:http";
4
4
  import { isIP } from "node:net";
5
5
  import * as os from "node:os";
6
6
  import * as path from "node:path";
7
- import { exactKeys, parseProjectMetadata, parseProjectRuntime, record, requiredHostname, serializeRuntimePayload, } from "./runtime.js";
7
+ import { exactKeys, parseProjectMetadata, parseProjectRuntime, record, requiredHostname, serializeRuntimePayload, validServiceName, } from "./runtime.js";
8
8
  const protocolVersion = "1";
9
9
  const maximumDocumentBytes = 64 * 1024;
10
10
  const maximumResponseBytes = 64 * 1024;
11
- const maximumServices = 32;
12
11
  const registrationTimeoutMilliseconds = 10 * 60 * 1000;
13
- const serviceNamePattern = /^[a-z](?:[a-z0-9-]{0,30}[a-z0-9])?$/;
14
12
  export function readDevelopmentContext(environment = process.env, cwd = process.cwd()) {
15
13
  const protocol = environment.TNL_DEV_PROTOCOL;
16
14
  if (protocol !== undefined) {
@@ -112,7 +110,7 @@ function parseBootstrapEnvironment(environment) {
112
110
  throw new Error(`TNL_DEV_SOCKET is required by tnl dev protocol ${protocolVersion}`);
113
111
  }
114
112
  const rawPort = environment.TNL_DEV_PORT;
115
- const port = rawPort === undefined ? undefined : parsePort(rawPort, "TNL_DEV_PORT");
113
+ const port = rawPort === undefined ? undefined : parseListenerPort(rawPort, "TNL_DEV_PORT");
116
114
  return Object.freeze({ port, socket });
117
115
  }
118
116
  function discoverDevSocket(discovery, environment) {
@@ -211,64 +209,27 @@ function parseProjectDocumentValue(value, description, projectRoot) {
211
209
  if (object.runningUnderTnlDev) {
212
210
  throw new Error(`${description} cannot be marked as running under tnl dev`);
213
211
  }
214
- const memberNamespace = requiredHostname(object.memberNamespace, `${description} member namespace`);
215
- const serviceValues = record(object.services, `${description} services`);
212
+ const project = parseProjectMetadata({ memberNamespace: object.memberNamespace, services: object.services }, description);
216
213
  const directoryValues = record(object.serviceDirectories, `${description} service directories`);
217
- const entries = Object.entries(serviceValues);
218
- if (entries.length > maximumServices) {
219
- throw new Error(`${description} may contain at most ${maximumServices} services`);
220
- }
221
- if (Object.keys(directoryValues).length !== entries.length ||
222
- entries.some(([name]) => !Object.hasOwn(directoryValues, name))) {
214
+ const names = Object.keys(project.services);
215
+ if (Object.keys(directoryValues).length !== names.length ||
216
+ names.some((name) => !Object.hasOwn(directoryValues, name))) {
223
217
  throw new Error(`${description} requires one directory for every service`);
224
218
  }
225
- const services = {};
226
219
  const serviceDirectories = {};
227
- const hostnames = new Set();
228
- for (const [name, value] of entries) {
229
- if (!validServiceName(name)) {
230
- throw new Error(`${description} contains an invalid service name ${JSON.stringify(name)}`);
231
- }
232
- const service = record(value, `${description} service ${JSON.stringify(name)}`);
233
- exactKeys(service, ["hostname", "memberNamespace", "url"], `${description} service ${JSON.stringify(name)}`);
234
- const directory = relativeDirectory(directoryValues[name], `${description} service ${JSON.stringify(name)} directory`);
235
- const serviceMemberNamespace = requiredHostname(service.memberNamespace, `${description} service ${JSON.stringify(name)} member namespace`);
236
- const hostname = requiredHostname(service.hostname, `${description} service ${JSON.stringify(name)} hostname`);
237
- if (service.url !== `https://${hostname}`) {
238
- throw new Error(`${description} service ${JSON.stringify(name)} has an invalid URL`);
239
- }
240
- if (hostnames.has(hostname)) {
241
- throw new Error(`${description} contains duplicate service hostname ${hostname}`);
242
- }
243
- hostnames.add(hostname);
244
- services[name] = Object.freeze({
245
- memberNamespace: serviceMemberNamespace,
246
- hostname,
247
- url: service.url,
248
- });
249
- serviceDirectories[name] = directory;
220
+ for (const name of names) {
221
+ serviceDirectories[name] = relativeDirectory(directoryValues[name], `${description} service ${JSON.stringify(name)} directory`);
250
222
  }
251
223
  return Object.freeze({
252
- memberNamespace,
224
+ ...project,
253
225
  projectRoot,
254
226
  runningUnderTnlDev: object.runningUnderTnlDev,
255
227
  serviceDirectories: Object.freeze(serviceDirectories),
256
- services: Object.freeze(services),
257
228
  version: 1,
258
229
  });
259
230
  }
260
231
  function projectMetadata(document) {
261
- return parseProjectMetadata({
262
- memberNamespace: document.memberNamespace,
263
- services: Object.fromEntries(Object.entries(document.services).map(([name, service]) => [
264
- name,
265
- {
266
- hostname: service.hostname,
267
- memberNamespace: service.memberNamespace,
268
- url: service.url,
269
- },
270
- ])),
271
- }, "tnl project metadata");
232
+ return Object.freeze({ memberNamespace: document.memberNamespace, services: document.services });
272
233
  }
273
234
  function selectService(document, cwd) {
274
235
  const absoluteCwd = absoluteNormalizedPath(cwd, "working directory");
@@ -398,7 +359,8 @@ function absoluteNormalizedPath(value, description) {
398
359
  }
399
360
  return value;
400
361
  }
401
- function parsePort(value, source) {
362
+ /** Parses digit-only listener ports in [1, 65535], preserving source-specific diagnostics. */
363
+ export function parseListenerPort(value, source) {
402
364
  if (!/^[0-9]+$/.test(value)) {
403
365
  throw new Error(`${source} must be a port between 1 and 65535`);
404
366
  }
@@ -408,9 +370,6 @@ function parsePort(value, source) {
408
370
  }
409
371
  return port;
410
372
  }
411
- function validServiceName(value) {
412
- return typeof value === "string" && serviceNamePattern.test(value);
413
- }
414
373
  function pathWithin(value, root) {
415
374
  const relative = path.relative(root, value);
416
375
  return relative === "" || (relative !== ".." && !relative.startsWith(`..${path.sep}`));
@@ -10,7 +10,10 @@ 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
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. */
16
+ export declare function validServiceName(value: unknown): value is string;
14
17
  export declare function parseRuntimePayload(serialized: string | undefined): ProjectRuntime | undefined;
15
18
  export declare function parseProjectRuntime(value: unknown, description: string): ProjectRuntime;
16
19
  export declare function serializeRuntimePayload(project: ProjectMetadata, runningUnderTnlDev: boolean): string;
@@ -1,6 +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
5
  export function parseProjectMetadata(value, description) {
5
6
  const object = record(value, description);
6
7
  exactKeys(object, ["memberNamespace", "services"], description);
@@ -13,7 +14,7 @@ export function parseProjectMetadata(value, description) {
13
14
  const services = {};
14
15
  const hostnames = new Set();
15
16
  for (const [name, value] of entries) {
16
- if (!serviceNamePattern.test(name)) {
17
+ if (!validServiceName(name)) {
17
18
  throw new Error(`${description} contains an invalid service name ${JSON.stringify(name)}`);
18
19
  }
19
20
  const service = record(value, `${description} service ${JSON.stringify(name)}`);
@@ -38,6 +39,10 @@ export function parseProjectMetadata(value, description) {
38
39
  services: Object.freeze(services),
39
40
  });
40
41
  }
42
+ /** Matches the native client's 1-32 byte ASCII service-name grammar without normalization. */
43
+ export function validServiceName(value) {
44
+ return typeof value === "string" && serviceNamePattern.test(value);
45
+ }
41
46
  export function parseRuntimePayload(serialized) {
42
47
  if (serialized === undefined) {
43
48
  return undefined;
package/dist/next.js CHANGED
@@ -1,4 +1,4 @@
1
- import { canonicalLoopbackTarget, readDevelopmentContext, registerLocalTarget, requestTunnelAssignment, runtimePayload, } from "./internal/dev.js";
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
4
  /** Adds tnl project metadata and safe `tnl dev` routing to a Next.js development server. */
@@ -63,23 +63,13 @@ function nextTarget(environment) {
63
63
  catch (error) {
64
64
  throw new Error("Next.js reported an invalid development listener", { cause: error });
65
65
  }
66
- const port = parsePort(origin.port, "Next.js listener");
66
+ const port = parseListenerPort(origin.port, "Next.js listener");
67
67
  const reportedPort = environment.PORT;
68
- if (reportedPort !== undefined && parsePort(reportedPort, "PORT") !== port) {
68
+ if (reportedPort !== undefined && parseListenerPort(reportedPort, "PORT") !== port) {
69
69
  throw new Error("Next.js reported inconsistent development listener ports");
70
70
  }
71
71
  return canonicalLoopbackTarget(origin.hostname, port);
72
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
73
  function unique(values) {
84
74
  return [...new Set(values)];
85
75
  }
package/lib/launcher.mjs CHANGED
@@ -2,16 +2,17 @@ import { constants, accessSync, readFileSync } from "node:fs";
2
2
  import { createRequire } from "node:module";
3
3
  import path from "node:path";
4
4
  import process from "node:process";
5
+ import { nativeTargets } from "./native-targets.mjs";
5
6
 
6
7
  const require = createRequire(import.meta.url);
7
8
  const launcherManifest = new URL("../package.json", import.meta.url);
8
9
 
9
- const nativePackages = new Map([
10
- ["darwin-arm64", "@tnldotdev/tnl-darwin-arm64"],
11
- ["darwin-x64", "@tnldotdev/tnl-darwin-x64"],
12
- ["linux-arm64", "@tnldotdev/tnl-linux-arm64"],
13
- ["linux-x64", "@tnldotdev/tnl-linux-x64"],
14
- ]);
10
+ const nativePackages = new Map(
11
+ nativeTargets.map(({ platform, architecture, packageName }) => [
12
+ `${platform}-${architecture}`,
13
+ packageName,
14
+ ]),
15
+ );
15
16
 
16
17
  export function nativePackageName(platform, architecture) {
17
18
  const packageName = nativePackages.get(`${platform}-${architecture}`);
@@ -0,0 +1,12 @@
1
+ /** Internal distribution catalog shared by the launcher and release scripts.
2
+ * Node architecture names belong here; Go artifact paths and versions do not.
3
+ * Entries retain native publish order. This module is not a public package export.
4
+ */
5
+ export const nativeTargets = Object.freeze(
6
+ [
7
+ { platform: "darwin", architecture: "arm64", packageName: "@tnldotdev/tnl-darwin-arm64" },
8
+ { platform: "darwin", architecture: "x64", packageName: "@tnldotdev/tnl-darwin-x64" },
9
+ { platform: "linux", architecture: "arm64", packageName: "@tnldotdev/tnl-linux-arm64" },
10
+ { platform: "linux", architecture: "x64", packageName: "@tnldotdev/tnl-linux-x64" },
11
+ ].map((target) => Object.freeze(target)),
12
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tnldotdev/tnl",
3
- "version": "0.1.0-rc.17",
3
+ "version": "0.1.0-rc.21",
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.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"
74
+ "@tnldotdev/tnl-darwin-arm64": "0.1.0-rc.21",
75
+ "@tnldotdev/tnl-darwin-x64": "0.1.0-rc.21",
76
+ "@tnldotdev/tnl-linux-arm64": "0.1.0-rc.21",
77
+ "@tnldotdev/tnl-linux-x64": "0.1.0-rc.21"
78
78
  },
79
79
  "tnl": {
80
- "commit": "bd70c08fe7c09aef1153e1afbbb25161d519a7ec"
80
+ "commit": "41fdf3fc97a2e6ceb1bc495a985ff341a13ce30a"
81
81
  }
82
82
  }