portweave 0.1.0
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/LICENSE +21 -0
- package/README.md +535 -0
- package/dist/allocator/allocate.concurrent.d.ts +3 -0
- package/dist/allocator/allocate.concurrent.d.ts.map +1 -0
- package/dist/allocator/allocate.concurrent.js +10 -0
- package/dist/allocator/allocate.concurrent.js.map +1 -0
- package/dist/allocator/allocate.d.ts +20 -0
- package/dist/allocator/allocate.d.ts.map +1 -0
- package/dist/allocator/allocate.js +106 -0
- package/dist/allocator/allocate.js.map +1 -0
- package/dist/allocator/cross-project.d.ts +2 -0
- package/dist/allocator/cross-project.d.ts.map +1 -0
- package/dist/allocator/cross-project.js +9 -0
- package/dist/allocator/cross-project.js.map +1 -0
- package/dist/allocator/order.d.ts +2 -0
- package/dist/allocator/order.d.ts.map +1 -0
- package/dist/allocator/order.js +8 -0
- package/dist/allocator/order.js.map +1 -0
- package/dist/allocator/pool.d.ts +35 -0
- package/dist/allocator/pool.d.ts.map +1 -0
- package/dist/allocator/pool.js +74 -0
- package/dist/allocator/pool.js.map +1 -0
- package/dist/allocator/probe.d.ts +22 -0
- package/dist/allocator/probe.d.ts.map +1 -0
- package/dist/allocator/probe.js +40 -0
- package/dist/allocator/probe.js.map +1 -0
- package/dist/cli/banner.d.ts +25 -0
- package/dist/cli/banner.d.ts.map +1 -0
- package/dist/cli/banner.js +61 -0
- package/dist/cli/banner.js.map +1 -0
- package/dist/cli/run.d.ts +15 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +156 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/show.d.ts +21 -0
- package/dist/cli/show.d.ts.map +1 -0
- package/dist/cli/show.js +141 -0
- package/dist/cli/show.js.map +1 -0
- package/dist/cli/spawn.d.ts +30 -0
- package/dist/cli/spawn.d.ts.map +1 -0
- package/dist/cli/spawn.js +85 -0
- package/dist/cli/spawn.js.map +1 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +52 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/anonymous.d.ts +5 -0
- package/dist/config/anonymous.d.ts.map +1 -0
- package/dist/config/anonymous.js +21 -0
- package/dist/config/anonymous.js.map +1 -0
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +4 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +8 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +59 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +21 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +125 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/env/build.d.ts +4 -0
- package/dist/env/build.d.ts.map +1 -0
- package/dist/env/build.js +17 -0
- package/dist/env/build.js.map +1 -0
- package/dist/env/dotenv-merge.d.ts +5 -0
- package/dist/env/dotenv-merge.d.ts.map +1 -0
- package/dist/env/dotenv-merge.js +73 -0
- package/dist/env/dotenv-merge.js.map +1 -0
- package/dist/env/index.d.ts +4 -0
- package/dist/env/index.d.ts.map +1 -0
- package/dist/env/index.js +4 -0
- package/dist/env/index.js.map +1 -0
- package/dist/env/resolve.d.ts +11 -0
- package/dist/env/resolve.d.ts.map +1 -0
- package/dist/env/resolve.js +29 -0
- package/dist/env/resolve.js.map +1 -0
- package/dist/env/templates.d.ts +2 -0
- package/dist/env/templates.d.ts.map +1 -0
- package/dist/env/templates.js +11 -0
- package/dist/env/templates.js.map +1 -0
- package/dist/env/writer.d.ts +6 -0
- package/dist/env/writer.d.ts.map +1 -0
- package/dist/env/writer.js +68 -0
- package/dist/env/writer.js.map +1 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +32 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/registry/atomic-write.d.ts +3 -0
- package/dist/registry/atomic-write.d.ts.map +1 -0
- package/dist/registry/atomic-write.js +43 -0
- package/dist/registry/atomic-write.js.map +1 -0
- package/dist/registry/lock.d.ts +4 -0
- package/dist/registry/lock.d.ts.map +1 -0
- package/dist/registry/lock.js +86 -0
- package/dist/registry/lock.js.map +1 -0
- package/dist/registry/paths.d.ts +7 -0
- package/dist/registry/paths.d.ts.map +1 -0
- package/dist/registry/paths.js +13 -0
- package/dist/registry/paths.js.map +1 -0
- package/dist/registry/prune.d.ts +3 -0
- package/dist/registry/prune.d.ts.map +1 -0
- package/dist/registry/prune.js +24 -0
- package/dist/registry/prune.js.map +1 -0
- package/dist/registry/serialize.d.ts +6 -0
- package/dist/registry/serialize.d.ts.map +1 -0
- package/dist/registry/serialize.js +134 -0
- package/dist/registry/serialize.js.map +1 -0
- package/dist/registry/storage.concurrent.d.ts +3 -0
- package/dist/registry/storage.concurrent.d.ts.map +1 -0
- package/dist/registry/storage.concurrent.js +10 -0
- package/dist/registry/storage.concurrent.js.map +1 -0
- package/dist/registry/storage.d.ts +11 -0
- package/dist/registry/storage.d.ts.map +1 -0
- package/dist/registry/storage.js +101 -0
- package/dist/registry/storage.js.map +1 -0
- package/dist/registry/types.d.ts +14 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +2 -0
- package/dist/registry/types.js.map +1 -0
- package/dist/result.d.ts +11 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +6 -0
- package/dist/result.js.map +1 -0
- package/dist/runtime/error-passthrough.d.ts +20 -0
- package/dist/runtime/error-passthrough.d.ts.map +1 -0
- package/dist/runtime/error-passthrough.js +20 -0
- package/dist/runtime/error-passthrough.js.map +1 -0
- package/dist/runtime/exports-smoke.d.ts +4 -0
- package/dist/runtime/exports-smoke.d.ts.map +1 -0
- package/dist/runtime/exports-smoke.js +36 -0
- package/dist/runtime/exports-smoke.js.map +1 -0
- package/dist/runtime/index.d.ts +15 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +99 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/upward-walk.d.ts +14 -0
- package/dist/runtime/upward-walk.d.ts.map +1 -0
- package/dist/runtime/upward-walk.js +39 -0
- package/dist/runtime/upward-walk.js.map +1 -0
- package/dist/worktree/git.d.ts +13 -0
- package/dist/worktree/git.d.ts.map +1 -0
- package/dist/worktree/git.js +66 -0
- package/dist/worktree/git.js.map +1 -0
- package/dist/worktree/key.d.ts +10 -0
- package/dist/worktree/key.d.ts.map +1 -0
- package/dist/worktree/key.js +38 -0
- package/dist/worktree/key.js.map +1 -0
- package/dist/worktree/namespace.d.ts +8 -0
- package/dist/worktree/namespace.d.ts.map +1 -0
- package/dist/worktree/namespace.js +58 -0
- package/dist/worktree/namespace.js.map +1 -0
- package/package.json +109 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gameweave
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
# Portweave
|
|
2
|
+
|
|
3
|
+
> Zero-thought, conflict-free local-dev port allocation across projects and git worktrees.
|
|
4
|
+
|
|
5
|
+
Portweave hands every project, git worktree, and parallel agent run its own block of ports from a single machine-wide pool. You declare your services once; Portweave allocates a unique, sticky port block per worktree, injects them as environment variables, and writes a `.portweave/current.env` file for tools that don't inherit a parent process's environment. There is no daemon, no network call, and no telemetry — all coordination happens through one lock-protected JSON file in your config directory.
|
|
6
|
+
|
|
7
|
+
It is built for developers who run several projects on one machine, who use git worktrees for parallel feature work, and for AI coding agents that spin up dev servers and verification loops in parallel worktrees without colliding on ports.
|
|
8
|
+
|
|
9
|
+
## Contents
|
|
10
|
+
|
|
11
|
+
- [Implementation prompt](#implementation-prompt)
|
|
12
|
+
- [Install](#install)
|
|
13
|
+
- [Quick start](#quick-start)
|
|
14
|
+
- [How Portweave works](#how-portweave-works)
|
|
15
|
+
- [Configuration](#configuration)
|
|
16
|
+
- [CLI reference](#cli-reference)
|
|
17
|
+
- [Recipes](#recipes)
|
|
18
|
+
- [Migrating from an existing port setup](#migrating-from-an-existing-port-setup)
|
|
19
|
+
- [Environment variable overrides](#environment-variable-overrides)
|
|
20
|
+
- [Runtime library API](#runtime-library-api)
|
|
21
|
+
- [Errors and recovery](#errors-and-recovery)
|
|
22
|
+
- [How allocations are stored](#how-allocations-are-stored)
|
|
23
|
+
- [Roadmap](#roadmap)
|
|
24
|
+
- [Contributing](#contributing)
|
|
25
|
+
- [License and resources](#license-and-resources)
|
|
26
|
+
|
|
27
|
+
## Implementation prompt
|
|
28
|
+
|
|
29
|
+
**Want your AI agent to set this up?** Paste the prompt below into Claude Code, Cursor, or any coding agent working in your project. It is self-contained — the agent doesn't need the rest of this README.
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
Integrate the `portweave` npm package so this project's local dev servers
|
|
33
|
+
get conflict-free, sticky ports per git worktree.
|
|
34
|
+
|
|
35
|
+
1. Install it as a dev dependency, matching this repo's package manager:
|
|
36
|
+
`npm install --save-dev portweave` (or `pnpm add -D portweave`,
|
|
37
|
+
or `yarn add -D portweave`).
|
|
38
|
+
2. Inspect the project for every process that binds a local port — dev
|
|
39
|
+
server, API, websocket, database, mail/queue emulators, etc. — and note
|
|
40
|
+
the environment variable each one reads its port from.
|
|
41
|
+
3. Create `portweave.config.json` at the repo root. Add one entry per
|
|
42
|
+
service under `services`, keyed by a kebab-case name:
|
|
43
|
+
`{ "envVar": "<PORT_ENV_VAR>" }`. For derived values like base URLs or
|
|
44
|
+
connection strings, add a `discoveryEnv` map whose templates use
|
|
45
|
+
`${serviceName}` to interpolate the allocated port. Give services that
|
|
46
|
+
must receive adjacent ports the same `group`.
|
|
47
|
+
4. In `package.json`, wrap each dev/test command that needs ports with
|
|
48
|
+
`portweave run -- <command>` (e.g. `"dev": "portweave run -- vite"`).
|
|
49
|
+
5. For config files evaluated at startup (`vite.config`, `next.config`,
|
|
50
|
+
`vitest.config`), do not read `process.env` — import the allocation from
|
|
51
|
+
the async runtime API instead:
|
|
52
|
+
import { ports } from 'portweave/runtime'
|
|
53
|
+
const p = await ports()
|
|
54
|
+
if (!p.ok) throw new Error(`${p.error.message} (${p.error.code})`)
|
|
55
|
+
// then use p.value.<serviceName>
|
|
56
|
+
6. Replace any hardcoded port literals with reads of the injected env vars.
|
|
57
|
+
7. Add `.portweave/` to `.gitignore`.
|
|
58
|
+
8. Verify: run the wrapped dev script and confirm the `[portweave]` banner
|
|
59
|
+
lists every service and the app binds the allocated ports.
|
|
60
|
+
|
|
61
|
+
CLI note: global flags go BEFORE the subcommand —
|
|
62
|
+
`portweave --count 3 run -- npm run dev`, never after `run`.
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Install
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm install --save-dev portweave
|
|
69
|
+
# or
|
|
70
|
+
pnpm add -D portweave
|
|
71
|
+
# or
|
|
72
|
+
yarn add -D portweave
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Portweave is a dev dependency. Invoke it with `npx portweave …` or from a `package.json` script — there is no global install and no `portweave init`. Requires Node.js 24 or newer.
|
|
76
|
+
|
|
77
|
+
Add the per-project output directory to your `.gitignore`:
|
|
78
|
+
|
|
79
|
+
```gitignore
|
|
80
|
+
.portweave/
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Quick start
|
|
84
|
+
|
|
85
|
+
**1. Create `portweave.config.json` in your project root.** Declare each service that needs a port and the environment variable it should be exposed as:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"$schema": "https://raw.githubusercontent.com/gameweave/portweave/main/schema/v1.json",
|
|
90
|
+
"services": {
|
|
91
|
+
"api": {
|
|
92
|
+
"envVar": "API_PORT",
|
|
93
|
+
"discoveryEnv": { "API_URL": "http://localhost:${api}" }
|
|
94
|
+
},
|
|
95
|
+
"web": { "envVar": "WEB_PORT" }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**2. Wrap your dev command** in `package.json` so it runs under `portweave run`:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"scripts": {
|
|
105
|
+
"dev": "portweave run -- vite"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**3. Run it.** Portweave allocates a port block, prints what it did, injects the env vars into `vite`, and writes `.portweave/current.env`:
|
|
111
|
+
|
|
112
|
+
```text
|
|
113
|
+
$ npm run dev
|
|
114
|
+
[portweave] worktree: my-app (namespace: main)
|
|
115
|
+
[portweave] allocated:
|
|
116
|
+
api → 30002 (API_PORT)
|
|
117
|
+
web → 30003 (WEB_PORT)
|
|
118
|
+
[portweave] wrote .portweave/current.env
|
|
119
|
+
[portweave] launching: vite
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Inside `vite` (and any process it spawns), `process.env.API_PORT` is `30002`, `process.env.WEB_PORT` is `30003`, and `process.env.API_URL` is `http://localhost:30002`.
|
|
123
|
+
|
|
124
|
+
Run the same command again from the same worktree and you get the **same** ports back (the banner reads `reusing existing allocation:`). Create a second git worktree of the project and run it there — you get a **different** block automatically, with no configuration change. Start an unrelated project on the same machine — also no collision, because the registry is machine-wide.
|
|
125
|
+
|
|
126
|
+
## How Portweave works
|
|
127
|
+
|
|
128
|
+
- **One config file per project.** `portweave.config.json` lists the services that need ports and the environment-variable names they map to. That is the only project-level configuration Portweave needs.
|
|
129
|
+
|
|
130
|
+
- **A single machine-wide pool.** All allocations are recorded in one registry at `~/.config/portweave/registry.json` (or `$XDG_CONFIG_HOME/portweave/registry.json`). Because every project on the machine draws from the same pool, two projects that both default to port 5173 never collide. The default pool is ports `30000`–`60000`.
|
|
131
|
+
|
|
132
|
+
- **Sticky, per-worktree allocations.** Each allocation is keyed on the worktree's git common directory, its derived namespace, and its path on disk. The same worktree gets the same ports across restarts and across terminals; a different worktree of the same repo gets a different block. The main worktree's namespace is `main`; other worktrees derive a namespace like `feature-auth-7a2b91c3` (a slug from the directory name plus a short hash of its path). Directories that are not git repositories fall back to using the directory itself as the key.
|
|
133
|
+
|
|
134
|
+
- **Live conflict detection.** Before claiming a block, Portweave opens a TCP listener on each candidate port. If something external (a system Postgres, another tool) already holds a port, Portweave re-rolls and picks a free block instead.
|
|
135
|
+
|
|
136
|
+
- **Two outputs from one code path.** `portweave run` injects the allocated ports as environment variables into the child process **and** writes the same values to `.portweave/current.env`. Use the injected env directly for anything launched by `portweave run`; use the file for tools that evaluate before the child inherits an environment — Docker Compose, Vite/Next config files, IDE run configurations.
|
|
137
|
+
|
|
138
|
+
## Configuration
|
|
139
|
+
|
|
140
|
+
Portweave looks for `portweave.config.json`, starting in the working directory and walking up toward the filesystem root (so it is found from subdirectories too). Here is a configuration that exercises every supported field:
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"$schema": "https://raw.githubusercontent.com/gameweave/portweave/main/schema/v1.json",
|
|
145
|
+
"services": {
|
|
146
|
+
"api": {
|
|
147
|
+
"envVar": "API_PORT",
|
|
148
|
+
"discoveryEnv": {
|
|
149
|
+
"API_URL": "http://localhost:${api}",
|
|
150
|
+
"VITE_API_URL": "http://localhost:${api}"
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
"web": { "envVar": "WEB_PORT" },
|
|
154
|
+
"ws": {
|
|
155
|
+
"envVar": "WS_PORT",
|
|
156
|
+
"discoveryEnv": { "VITE_WS_URL": "ws://localhost:${ws}" }
|
|
157
|
+
},
|
|
158
|
+
"db": {
|
|
159
|
+
"group": "data",
|
|
160
|
+
"envVar": "DB_PORT",
|
|
161
|
+
"discoveryEnv": { "DATABASE_URL": "postgres://localhost:${db}/app" }
|
|
162
|
+
},
|
|
163
|
+
"db-admin": { "group": "data", "envVar": "DB_ADMIN_PORT" }
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Field reference:
|
|
169
|
+
|
|
170
|
+
| Field | Required | Rules |
|
|
171
|
+
| ------------------------------ | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
172
|
+
| `$schema` | optional | A schema URL string. Ignored at runtime; useful for editor autocompletion. |
|
|
173
|
+
| `services` | **required** | An object with at least one entry. Each key is a service name in kebab-case (`^[a-z][a-z0-9-]*$`). |
|
|
174
|
+
| `services.<name>.envVar` | **required** | The environment-variable name the allocated port is exposed as. Must match `^[A-Z][A-Z0-9_]*$`. Must be unique across all services. |
|
|
175
|
+
| `services.<name>.group` | optional | A group label. Services sharing a group are allocated as a contiguous block, so they move together (handy when a tool expects two adjacent ports). |
|
|
176
|
+
| `services.<name>.discoveryEnv` | optional | A map of additional environment variables to derived values. Each value is a template; `${serviceName}` is replaced with that service's allocated port. Keys must be valid env-var names and unique across the whole config. A `${name}` that does not match a declared service is a configuration error. |
|
|
177
|
+
|
|
178
|
+
A copyable starting point lives at [`examples/web-app.config.json`](examples/web-app.config.json).
|
|
179
|
+
|
|
180
|
+
Notes that affect how you write templates:
|
|
181
|
+
|
|
182
|
+
- `${serviceName}` always resolves to the **allocated** port for that service, even if you override the service's own `envVar` through a project `.env` file. Derived URLs stay internally consistent.
|
|
183
|
+
- The configuration file is strict: unknown top-level keys and unknown service fields are rejected with a `PW0102` error that names the offending path.
|
|
184
|
+
|
|
185
|
+
## CLI reference
|
|
186
|
+
|
|
187
|
+
```text
|
|
188
|
+
portweave [global options] <command> [-- command to run]
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Global options are parsed **before** the subcommand. The following all live on the root command:
|
|
192
|
+
|
|
193
|
+
| Global option | Description |
|
|
194
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------- |
|
|
195
|
+
| `--config <path>` | Use a specific config file instead of discovering `portweave.config.json`. |
|
|
196
|
+
| `--count <n>` | Anonymous mode: allocate `n` ports with no config file (see below). Mutually exclusive with `--config`. |
|
|
197
|
+
| `--verbose` | Print extra diagnostic lines (config path, registry path, allocation key). |
|
|
198
|
+
| `-V`, `--version` | Print the version. |
|
|
199
|
+
| `-h`, `--help` | Print help. |
|
|
200
|
+
|
|
201
|
+
> Because these are global options, they go **before** the subcommand: `portweave --count 3 run -- npm run dev` works; `portweave run --count 3 …` does not (the flag is ignored).
|
|
202
|
+
|
|
203
|
+
### `portweave run -- <command>`
|
|
204
|
+
|
|
205
|
+
Allocates (or reuses) the port block for the current worktree, writes `.portweave/current.env`, injects the env vars, and runs `<command>` as a child process. Everything after `--` is the command and its arguments.
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
portweave run -- vite
|
|
209
|
+
portweave run -- npm run dev
|
|
210
|
+
portweave --config ./ports.dev.json run -- node server.js
|
|
211
|
+
portweave --count 3 run -- npm run dev # anonymous mode, no config file
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
- The allocation banner is printed to **stderr**, so it never pollutes a pipeline reading the child's stdout.
|
|
215
|
+
- Signals (`SIGINT`, `SIGTERM`) are forwarded to the child.
|
|
216
|
+
- **Exit code:** the child's exit code on success; `1` for a Portweave error (invalid flags, missing config, locked registry, etc.); `127` if the child command could not be spawned.
|
|
217
|
+
|
|
218
|
+
**Anonymous mode** (`--count n`) needs no config file. It synthesizes `n` services named `port-1`…`port-n`, exposed as `PORT_1`…`PORT_n`. `n` must be an integer in `[1, 100]`. Useful for throwaway scripts or agents that just need "some free ports":
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
$ portweave --count 2 run -- node -e "console.log(process.env.PORT_1, process.env.PORT_2)"
|
|
222
|
+
[portweave] worktree: my-app (namespace: main)
|
|
223
|
+
[portweave] allocated:
|
|
224
|
+
port-1 → 30000 (PORT_1)
|
|
225
|
+
port-2 → 30001 (PORT_2)
|
|
226
|
+
[portweave] wrote .portweave/current.env
|
|
227
|
+
[portweave] launching: node -e console.log(process.env.PORT_1, process.env.PORT_2)
|
|
228
|
+
30000 30001
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### `portweave show`
|
|
232
|
+
|
|
233
|
+
Prints the current worktree's existing allocation without changing anything. Output goes to stdout.
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
$ portweave show
|
|
237
|
+
[portweave] worktree: my-app (namespace: main)
|
|
238
|
+
[portweave] reusing existing allocation:
|
|
239
|
+
api → 30002 (API_PORT)
|
|
240
|
+
web → 30003 (WEB_PORT)
|
|
241
|
+
db → 30004 (DB_PORT)
|
|
242
|
+
db-admin → 30005 (DB_ADMIN_PORT)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Add `--json` for machine-readable output — ideal for scripts and agents. Keys are sorted:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
$ portweave show --json
|
|
249
|
+
{
|
|
250
|
+
"env": {
|
|
251
|
+
"API_PORT": "30002",
|
|
252
|
+
"API_URL": "http://localhost:30002",
|
|
253
|
+
"DATABASE_URL": "postgres://localhost:30004/app",
|
|
254
|
+
"DB_ADMIN_PORT": "30005",
|
|
255
|
+
"DB_PORT": "30004",
|
|
256
|
+
"WEB_PORT": "30003"
|
|
257
|
+
},
|
|
258
|
+
"namespace": "main",
|
|
259
|
+
"ports": { "api": 30002, "db": 30004, "db-admin": 30005, "web": 30003 },
|
|
260
|
+
"worktreeRoot": "/path/to/my-app"
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
If the worktree has no allocation yet, `show` exits `1` and tells you to run `portweave run` first.
|
|
265
|
+
|
|
266
|
+
## Recipes
|
|
267
|
+
|
|
268
|
+
### Use with an npm/pnpm/yarn dev script
|
|
269
|
+
|
|
270
|
+
Wrap the script's command with `portweave run --`. Nothing else changes; your dev tool reads the injected env vars.
|
|
271
|
+
|
|
272
|
+
```jsonc
|
|
273
|
+
// package.json
|
|
274
|
+
{
|
|
275
|
+
"scripts": {
|
|
276
|
+
"dev": "portweave run -- vite",
|
|
277
|
+
"dev:server": "portweave run -- node server.js",
|
|
278
|
+
"test:e2e": "portweave run -- playwright test",
|
|
279
|
+
},
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Read the ports inside your code from the env vars you declared (`process.env.API_PORT`, etc.). This works because `portweave run` injects the allocation into the child process before your tool starts.
|
|
284
|
+
|
|
285
|
+
### Use across git worktrees in parallel
|
|
286
|
+
|
|
287
|
+
This is the headline case and needs **no per-worktree configuration**. The same `portweave.config.json` is committed once; each worktree gets its own sticky block because the allocation key includes the worktree path.
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
# Terminal 1 — main worktree
|
|
291
|
+
~/code/my-app $ npm run dev
|
|
292
|
+
[portweave] worktree: my-app (namespace: main)
|
|
293
|
+
[portweave] allocated:
|
|
294
|
+
api → 30002 (API_PORT)
|
|
295
|
+
web → 30003 (WEB_PORT)
|
|
296
|
+
|
|
297
|
+
# Terminal 2 — a feature worktree of the same repo, at the same time
|
|
298
|
+
~/code/my-app-feature $ npm run dev
|
|
299
|
+
[portweave] worktree: my-app-feature (namespace: feature-auth-7a2b91c3)
|
|
300
|
+
[portweave] allocated:
|
|
301
|
+
api → 30004 (API_PORT)
|
|
302
|
+
web → 30005 (WEB_PORT)
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Both dev servers run simultaneously with no collision. Re-running in either worktree reuses that worktree's block. When you delete a worktree, its registry entry is pruned automatically on the next Portweave run.
|
|
306
|
+
|
|
307
|
+
### Use with Docker Compose
|
|
308
|
+
|
|
309
|
+
Compose does not inherit a parent process's environment, but it can read an env file. Point it at the file Portweave writes on every `portweave run`:
|
|
310
|
+
|
|
311
|
+
```yaml
|
|
312
|
+
# docker-compose.yml
|
|
313
|
+
services:
|
|
314
|
+
db:
|
|
315
|
+
image: postgres:16
|
|
316
|
+
env_file: .portweave/current.env
|
|
317
|
+
ports:
|
|
318
|
+
- '${DB_PORT}:5432'
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Run Compose under Portweave so the file exists and `${DB_PORT}` is substituted in the Compose file itself:
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
portweave run -- docker compose up
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
`.portweave/current.env` is a plain `KEY=value` file (see [`portweave show --json`](#portweave-show) for the same data programmatically), regenerated on every run.
|
|
328
|
+
|
|
329
|
+
### Use in a Vite, Next.js, or Vitest config file
|
|
330
|
+
|
|
331
|
+
Config files are evaluated by the bundler at startup, before any child process inherits an environment — so reading `process.env.API_PORT` there is unreliable. Import the allocation directly from the runtime API instead. It runs the same allocator the CLI uses, so it returns the same sticky ports:
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
// vite.config.ts
|
|
335
|
+
import { defineConfig } from 'vite'
|
|
336
|
+
import { ports } from 'portweave/runtime'
|
|
337
|
+
|
|
338
|
+
export default defineConfig(async () => {
|
|
339
|
+
const result = await ports()
|
|
340
|
+
if (!result.ok) {
|
|
341
|
+
throw new Error(`portweave: ${result.error.message} (${result.error.code})`)
|
|
342
|
+
}
|
|
343
|
+
return {
|
|
344
|
+
server: { port: result.value.web },
|
|
345
|
+
define: { __API_PORT__: result.value.api },
|
|
346
|
+
}
|
|
347
|
+
})
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
See [Runtime library API](#runtime-library-api) for the full surface.
|
|
351
|
+
|
|
352
|
+
### Use in CI
|
|
353
|
+
|
|
354
|
+
In CI each job runs in isolation, so collisions are unlikely — but to pin a deterministic block (for example, to reference the same ports across steps), set `PORTWEAVE_OFFSET`:
|
|
355
|
+
|
|
356
|
+
```yaml
|
|
357
|
+
# .github/workflows/ci.yml
|
|
358
|
+
jobs:
|
|
359
|
+
e2e:
|
|
360
|
+
runs-on: ubuntu-latest
|
|
361
|
+
env:
|
|
362
|
+
PORTWEAVE_OFFSET: '0'
|
|
363
|
+
steps:
|
|
364
|
+
- uses: actions/checkout@v4
|
|
365
|
+
- uses: actions/setup-node@v4
|
|
366
|
+
with:
|
|
367
|
+
node-version: 24
|
|
368
|
+
- run: npm ci
|
|
369
|
+
- run: npm run test:e2e # script wraps the command with `portweave run --`
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Read the allocated ports from a sibling script
|
|
373
|
+
|
|
374
|
+
Anything launched by `portweave run` already has the env vars. For a separate process, either read the file or call the runtime API.
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
# Shell: source the file Portweave wrote
|
|
378
|
+
set -a; . .portweave/current.env; set +a
|
|
379
|
+
echo "API is on $API_PORT"
|
|
380
|
+
|
|
381
|
+
# Or get JSON without launching anything
|
|
382
|
+
portweave show --json | jq -r '.ports.api'
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
```ts
|
|
386
|
+
// Node: call the runtime API directly
|
|
387
|
+
import { ports } from 'portweave/runtime'
|
|
388
|
+
|
|
389
|
+
const result = await ports()
|
|
390
|
+
if (result.ok) console.log(result.value.api)
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
## Migrating from an existing port setup
|
|
394
|
+
|
|
395
|
+
Portweave replaces ad-hoc port coordination. The shape of the migration depends on what you have today.
|
|
396
|
+
|
|
397
|
+
### From hardcoded ports in `.env` / `.env.local`
|
|
398
|
+
|
|
399
|
+
If you currently keep ports in a committed or copied `.env` file:
|
|
400
|
+
|
|
401
|
+
```diff
|
|
402
|
+
# .env (before)
|
|
403
|
+
- API_PORT=3001
|
|
404
|
+
- WEB_PORT=5173
|
|
405
|
+
- DATABASE_URL=postgres://localhost:5432/app
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Move those names into `portweave.config.json` and let Portweave compute them:
|
|
409
|
+
|
|
410
|
+
```json
|
|
411
|
+
{
|
|
412
|
+
"services": {
|
|
413
|
+
"api": { "envVar": "API_PORT" },
|
|
414
|
+
"web": { "envVar": "WEB_PORT" },
|
|
415
|
+
"db": {
|
|
416
|
+
"envVar": "DB_PORT",
|
|
417
|
+
"discoveryEnv": { "DATABASE_URL": "postgres://localhost:${db}/app" }
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
Then wrap your dev/test scripts with `portweave run --`. A project `.env` still works for _non-port_ settings; Portweave only overrides the keys it computes, and leaves everything else for your existing dotenv loader. If you set one of Portweave's keys in `.env`, that value wins over the computed one — useful for pinning a single port locally.
|
|
424
|
+
|
|
425
|
+
### From a hand-rolled `base + offset` convention
|
|
426
|
+
|
|
427
|
+
Some teams compute ports with a formula like `BASE_PORT + (worktreeOffset * 100)` and a per-repo registry. Portweave subsumes this: it gives you the same "same worktree → same ports, all services move together" behavior, plus cross-project collision protection, without the offset bookkeeping or a per-repo cap. Replace the formula with a config file that lists the services, delete the helper that computed offsets, and wrap your startup command with `portweave run --`. If a downstream tool (e.g. a process manager) needs a stable name per worktree, read `PORTWEAVE_NAMESPACE`, which Portweave sets in the child environment.
|
|
428
|
+
|
|
429
|
+
### From auto-increment-on-collision (e.g. Vite's default)
|
|
430
|
+
|
|
431
|
+
Vite picks the next free port when its preferred one is taken, which means a server's port can change between runs and differs from whatever your tests or proxy expect. When you want a **stable** port per worktree instead, declare the service in `portweave.config.json` and start Vite under `portweave run -- vite`, reading `server.port` from the runtime API (see the [Vite recipe](#use-in-a-vite-nextjs-or-vitest-config-file)). The port is now deterministic for that worktree across restarts.
|
|
432
|
+
|
|
433
|
+
## Environment variable overrides
|
|
434
|
+
|
|
435
|
+
These variables tune allocation behavior. Set them in your shell, a `.env` consumed before invoking Portweave, or a CI job's `env:` block.
|
|
436
|
+
|
|
437
|
+
| Variable | Effect | Default |
|
|
438
|
+
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
|
|
439
|
+
| `PORTWEAVE_OFFSET` | Forces a specific allocation offset. Must be a non-negative integer. Useful for pinning a deterministic block in CI or on a shared machine. | derived from the worktree path |
|
|
440
|
+
| `PORTWEAVE_NAMESPACE` | Overrides the namespace string (the value exposed as `PORTWEAVE_NAMESPACE` and used in the banner). The value is slugified. | `main` for the main worktree; `<slug>-<hash>` otherwise |
|
|
441
|
+
| `PORTWEAVE_POOL_RANGE` | Overrides the candidate port pool. Format `<start>-<end>`; both integers, `start >= 1024`, `end > start`. | `30000-60000` |
|
|
442
|
+
| `PORTWEAVE_LOCK_TIMEOUT_MS` | How long to wait when acquiring the registry lock before giving up with `PW0301`. | ~2500 ms (100 retries × 25 ms) |
|
|
443
|
+
| `XDG_CONFIG_HOME` | Base directory for the registry. Portweave uses `$XDG_CONFIG_HOME/portweave/`. | `~/.config` |
|
|
444
|
+
|
|
445
|
+
Malformed values for `PORTWEAVE_POOL_RANGE` and `PORTWEAVE_LOCK_TIMEOUT_MS` fall back to the default; the pool-range case also prints a one-line warning to stderr so a typo doesn't silently change allocations. A non-integer `PORTWEAVE_OFFSET` is a hard error (`PW0202`).
|
|
446
|
+
|
|
447
|
+
## Runtime library API
|
|
448
|
+
|
|
449
|
+
For config files and scripts that need the allocation before a child process exists, import from `portweave/runtime`. All three exports are async functions that run the same allocate-and-resolve pipeline as the CLI and return a `Result` you must narrow:
|
|
450
|
+
|
|
451
|
+
```ts
|
|
452
|
+
import { ports, env, allocation } from 'portweave/runtime'
|
|
453
|
+
|
|
454
|
+
// ports() → Result<Record<string, number>, PortweaveError>
|
|
455
|
+
const p = await ports()
|
|
456
|
+
if (p.ok) console.log(p.value.api) // 30002
|
|
457
|
+
|
|
458
|
+
// env() → Result<Record<string, string>, PortweaveError>
|
|
459
|
+
// The full computed env map, including discoveryEnv URLs.
|
|
460
|
+
const e = await env()
|
|
461
|
+
if (e.ok) console.log(e.value.DATABASE_URL)
|
|
462
|
+
|
|
463
|
+
// allocation() → Result<Allocation, PortweaveError>
|
|
464
|
+
// The complete allocation: namespace, ports, and worktree key.
|
|
465
|
+
const a = await allocation()
|
|
466
|
+
if (a.ok) console.log(a.value.namespace)
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
A `Result` is `{ ok: true, value } | { ok: false, error }`; on the error arm, `error` is a `PortweaveError` with a `.code` (one of the `PW####` codes) and a `.message`. Each function accepts an optional options object:
|
|
470
|
+
|
|
471
|
+
| Option | Type | Meaning |
|
|
472
|
+
| ------------ | -------- | ----------------------------------------------------------------------------------------------------- |
|
|
473
|
+
| `cwd` | `string` | Directory used for worktree-key resolution and config discovery. Defaults to `process.cwd()`. |
|
|
474
|
+
| `configPath` | `string` | Use a specific config file; skips upward-directory discovery. |
|
|
475
|
+
| `count` | `number` | Anonymous-mode fallback: if no config file is found, synthesize `count` services (`port-1`…`port-N`). |
|
|
476
|
+
|
|
477
|
+
Calling the runtime API allocates exactly as the CLI does — it acquires the registry lock, reuses the sticky block for the worktree, and writes `.portweave/current.env` as a side effect.
|
|
478
|
+
|
|
479
|
+
## Errors and recovery
|
|
480
|
+
|
|
481
|
+
Errors carry a stable `PW####` code, printed as `[portweave] error: <message> (<code>)`. The codes an end user is likely to encounter:
|
|
482
|
+
|
|
483
|
+
| Code | Meaning | Recovery |
|
|
484
|
+
| -------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
485
|
+
| `PW0101` | No `portweave.config.json` found. | Add a config file, or use anonymous mode: `portweave --count N run -- …`. |
|
|
486
|
+
| `PW0102` | Config failed validation. | The message names the offending path (e.g. `services.api.envVar: …`). Fix that field. |
|
|
487
|
+
| `PW0202` | `PORTWEAVE_OFFSET` is not a non-negative integer. | Correct or unset the variable. |
|
|
488
|
+
| `PW0301` | Could not acquire the registry lock in time. | Usually transient — Portweave retries. If it persists, a process crashed holding the lock; remove `~/.config/portweave/registry.lock`, or raise `PORTWEAVE_LOCK_TIMEOUT_MS`. |
|
|
489
|
+
| `PW0302` | The registry JSON is corrupt. | Inspect `~/.config/portweave/registry.json`; fix or delete it (it will be recreated). |
|
|
490
|
+
| `PW0401` | No free port block large enough in the pool. | Widen the pool with `PORTWEAVE_POOL_RANGE`, or remove stale entries from the registry. |
|
|
491
|
+
| `PW0502` | A `.env` line could not be parsed. | The message names the line number; fix or quote the value. |
|
|
492
|
+
| `PW0601` | Invalid CLI flags (e.g. `--config` with `--count`, or no command after `--`). | Correct the invocation. |
|
|
493
|
+
| `PW0602` | The command after `--` could not be spawned (exit `127`). | Check the command exists and is on `PATH`. |
|
|
494
|
+
| `PW0701` | The runtime API found no config and was given no `count`. | Pass `{ count }`, `{ configPath }`, or add a `portweave.config.json`. |
|
|
495
|
+
|
|
496
|
+
Add `--verbose` to any `portweave run` invocation to print the resolved config path, registry path, and allocation key alongside the error.
|
|
497
|
+
|
|
498
|
+
## How allocations are stored
|
|
499
|
+
|
|
500
|
+
- **The registry** lives at `~/.config/portweave/registry.json` (or `$XDG_CONFIG_HOME/portweave/registry.json`). It is plain, human-readable JSON — safe to inspect, and safe to hand-edit when Portweave is not running. Entries whose worktree directory no longer exists are pruned automatically on the next run.
|
|
501
|
+
- **The lock** is a directory mutex at `~/.config/portweave/registry.lock`, held only for the duration of a read-modify-write. A lock older than 30 seconds is treated as stale and reclaimed, so a crashed process can't wedge the registry permanently.
|
|
502
|
+
- **Per-project output** is `.portweave/current.env` in the worktree, rewritten on every `portweave run`. Add `.portweave/` to `.gitignore`.
|
|
503
|
+
- **No daemon, no network, no telemetry.** Every invocation is a one-shot process that coordinates solely through the lock-protected registry file.
|
|
504
|
+
|
|
505
|
+
## Roadmap
|
|
506
|
+
|
|
507
|
+
These are directional and not commitments. Nothing here is required for, or part of, v0.
|
|
508
|
+
|
|
509
|
+
- Agent-spawned ephemeral allocations with a time-to-live.
|
|
510
|
+
- A cross-project discovery layer so services can find each other by name.
|
|
511
|
+
- Local dev DNS (a hostname that resolves to a worktree's allocated port).
|
|
512
|
+
- Optionally team-shared registries.
|
|
513
|
+
- First-party framework adapters (a Vite plugin, a Next.js plugin) layered over the runtime API.
|
|
514
|
+
|
|
515
|
+
## Contributing
|
|
516
|
+
|
|
517
|
+
The design rationale lives in [`.ai/DESIGN.md`](.ai/DESIGN.md) and the reasoning behind individual decisions in [`.ai/decision-log.md`](.ai/decision-log.md). Read those first for non-trivial work.
|
|
518
|
+
|
|
519
|
+
The full quality suite runs with:
|
|
520
|
+
|
|
521
|
+
```bash
|
|
522
|
+
npm install
|
|
523
|
+
npm run dev-workflow # format, lint, typecheck, dup/dead-code, tests — run before pushing
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
Features are specified before they're built; see the spec workflow under [`.ai/specs/`](.ai/specs/).
|
|
527
|
+
|
|
528
|
+
## License and resources
|
|
529
|
+
|
|
530
|
+
Released under the [MIT License](LICENSE).
|
|
531
|
+
|
|
532
|
+
- [Design document](.ai/DESIGN.md)
|
|
533
|
+
- [Decision log](.ai/decision-log.md)
|
|
534
|
+
- [Specs](.ai/specs/)
|
|
535
|
+
- [Example configuration](examples/web-app.config.json)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allocate.concurrent.d.ts","sourceRoot":"","sources":["../../src/allocator/allocate.concurrent.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,yBAAyB,EAAE,MAAmC,CAAA;AAC3E,eAAO,MAAM,0BAA0B,EAAG,CAAU,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Concurrency test surface — kept separate from allocate.ts so the path
|
|
2
|
+
// helpers and constants used by the concurrent integration test live next
|
|
3
|
+
// to the test rather than polluting the runtime module. Imported only from
|
|
4
|
+
// __tests__/allocate.concurrent.test.ts; structure-check requires that a
|
|
5
|
+
// .test.ts file has a sibling source module at this location.
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
const FIXTURE_URL = new URL('./__tests__/fixtures/concurrent-allocator.ts', import.meta.url);
|
|
8
|
+
export const CONCURRENT_ALLOCATOR_PATH = fileURLToPath(FIXTURE_URL);
|
|
9
|
+
export const CONCURRENT_ALLOCATOR_COUNT = 4;
|
|
10
|
+
//# sourceMappingURL=allocate.concurrent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allocate.concurrent.js","sourceRoot":"","sources":["../../src/allocator/allocate.concurrent.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,0EAA0E;AAC1E,2EAA2E;AAC3E,yEAAyE;AACzE,8DAA8D;AAE9D,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,MAAM,WAAW,GAAG,IAAI,GAAG,CACzB,8CAA8C,EAC9C,MAAM,CAAC,IAAI,CAAC,GAAG,CAChB,CAAA;AAED,MAAM,CAAC,MAAM,yBAAyB,GAAW,aAAa,CAAC,WAAW,CAAC,CAAA;AAC3E,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAU,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Config, ServiceSpec } from '../config/index.ts';
|
|
2
|
+
import { PortweaveError } from '../errors.ts';
|
|
3
|
+
import { type Result } from '../result.ts';
|
|
4
|
+
import type { AllocationKey, RegistryEntry } from '../registry/types.ts';
|
|
5
|
+
export type Allocation = RegistryEntry;
|
|
6
|
+
export interface AllocationResult {
|
|
7
|
+
readonly allocation: Allocation;
|
|
8
|
+
readonly reused: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare const MAX_PROBE_RETRIES: 100;
|
|
11
|
+
/**
|
|
12
|
+
* Re-order services so that services sharing a `group` label are contiguous
|
|
13
|
+
* and groups appear in first-occurrence order. Ungrouped services keep their
|
|
14
|
+
* original sequential position relative to the groups.
|
|
15
|
+
*
|
|
16
|
+
* Pure — no I/O.
|
|
17
|
+
*/
|
|
18
|
+
export declare function orderServicesForAllocation(config: Config): ServiceSpec[];
|
|
19
|
+
export declare function allocate(key: AllocationKey, config: Config, env?: NodeJS.ProcessEnv): Promise<Result<AllocationResult, PortweaveError>>;
|
|
20
|
+
//# sourceMappingURL=allocate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allocate.d.ts","sourceRoot":"","sources":["../../src/allocator/allocate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAC7D,OAAO,EAAE,cAAc,EAAkB,MAAM,cAAc,CAAA;AAC7D,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,cAAc,CAAA;AAEnD,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAOxE,MAAM,MAAM,UAAU,GAAG,aAAa,CAAA;AAEtC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;IAC/B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CACzB;AAED,eAAO,MAAM,iBAAiB,EAAG,GAAY,CAAA;AAE7C;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE,CAqBxE;AA4FD,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,MAAM,EACd,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,cAAc,CAAC,CAAC,CAsBnD"}
|