@smthrs/platform-bun 1.0.0-rc.8 → 1.0.0-rc.9
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 +8 -7
- package/dist/cjs/BunHost.d.ts +18 -18
- package/dist/cjs/BunHost.d.ts.map +1 -1
- package/dist/cjs/BunHost.js +3 -2
- package/dist/cjs/BunHost.js.map +2 -2
- package/dist/esm/BunHost.d.ts +18 -18
- package/dist/esm/BunHost.d.ts.map +1 -1
- package/dist/esm/BunHost.js +22 -16
- package/dist/esm/BunHost.js.map +1 -1
- package/docs/README.md +4 -4
- package/docs/api.md +20 -20
- package/docs/guides/bind-a-repository-root.md +2 -2
- package/docs/guides/contain-child-processes.md +10 -9
- package/docs/installation.md +20 -16
- package/docs/quickstart.md +15 -12
- package/docs/troubleshooting.md +15 -33
- package/package.json +6 -6
- package/src/BunHost.ts +29 -22
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ package is that object for a process running on Bun.
|
|
|
23
23
|
## Install
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
bun add @smthrs/platform-bun@1.0.0-rc.
|
|
26
|
+
bun add @smthrs/platform-bun@1.0.0-rc.9 effect
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
`@smthrs/platform-node` and `effect` are required peers, as `package.json`
|
|
@@ -58,12 +58,13 @@ The program names Bun nowhere. It asks for a service tag, and the layer decides
|
|
|
58
58
|
what fills it, so swapping `BunHost.layer` for `NodeHost.layer` from
|
|
59
59
|
`@smthrs/platform-node` does not change a character of it.
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
`
|
|
64
|
-
selected process runner. Contained bundles record
|
|
65
|
-
host process ledger. Probe results are cached per
|
|
66
|
-
path and runner, with a separate cache for each
|
|
61
|
+
Every host bundle builds without jj. The `Jj` slot needs jj 0.39.0 or newer: its
|
|
62
|
+
first operation runs one version probe and fails with `JjError` `not_installed`
|
|
63
|
+
or `unsupported_version` when jj is missing or older. The version probe and
|
|
64
|
+
repository commands use the selected process runner. Contained bundles record
|
|
65
|
+
and retire the probe in the host process ledger. Probe results are cached per
|
|
66
|
+
resolved absolute executable path and runner, with a separate cache for each
|
|
67
|
+
spawner instance.
|
|
67
68
|
|
|
68
69
|
## Modules
|
|
69
70
|
|
package/dist/cjs/BunHost.d.ts
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import * as BunChildProcessSpawner from "../vendor/cjs/bun/BunChildProcessSpawner.js";
|
|
25
25
|
import * as BunCrypto from "../vendor/cjs/bun/BunCrypto.js";
|
|
26
|
-
import type { Jj
|
|
26
|
+
import type { Jj } from "@smthrs/jj";
|
|
27
27
|
import type { HostServiceIds } from "@smthrs/kernel/HostServices";
|
|
28
28
|
import type * as KernelProcessConfinement from "@smthrs/kernel/ProcessConfinement";
|
|
29
29
|
import type * as ProcessLedger from "@smthrs/kernel/ProcessLedger";
|
|
@@ -69,7 +69,8 @@ export type BunHost = FileSystem | Path.Path | ChildProcessSpawner | Jj | HttpCl
|
|
|
69
69
|
*
|
|
70
70
|
* A code is the contract a caller branches on; the message beside it is for a
|
|
71
71
|
* person. `invalid_repository_root` is the root handed to {@link layerAt} or
|
|
72
|
-
* {@link layerContainedAt} not being absolute, the empty
|
|
72
|
+
* {@link layerContainedAt} not being absolute or containing NUL, the empty
|
|
73
|
+
* string included.
|
|
73
74
|
*
|
|
74
75
|
* @category models
|
|
75
76
|
* @since 1.0.0-rc.0
|
|
@@ -147,30 +148,30 @@ export declare const layerHttpClient: Layer.Layer<HttpClient>;
|
|
|
147
148
|
* Provides all five Bun Host services, including the runtime-independent Path
|
|
148
149
|
* service.
|
|
149
150
|
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
* `
|
|
153
|
-
*
|
|
151
|
+
* Builds on a host without jj. The `Jj` slot probes `jj --version` on its
|
|
152
|
+
* first operation and fails that operation with `JjError` carrying
|
|
153
|
+
* `not_installed` or `unsupported_version`; a program that never calls `Jj`
|
|
154
|
+
* runs without jj. `Jj` requires jj 0.39.0 or later.
|
|
154
155
|
*
|
|
155
156
|
* @category layers
|
|
156
157
|
* @since 1.0.0-rc.0
|
|
157
158
|
* @slop
|
|
158
159
|
*/
|
|
159
|
-
export declare const layer: Layer.Layer<BunHost | Crypto.Crypto
|
|
160
|
+
export declare const layer: Layer.Layer<BunHost | Crypto.Crypto>;
|
|
160
161
|
/**
|
|
161
162
|
* Provides all five Bun Host services bound to one absolute repository root.
|
|
162
163
|
*
|
|
163
164
|
* Throws {@link BunHostError} with code `invalid_repository_root` when `root`
|
|
164
|
-
* is not absolute; the empty string counts. The check runs
|
|
165
|
-
* called, before any layer exists.
|
|
165
|
+
* is not absolute or contains NUL; the empty string counts. The check runs
|
|
166
|
+
* when the factory is called, before any layer exists.
|
|
166
167
|
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
168
|
+
* Builds without jj exactly as {@link layer} does: the `Jj` slot probes
|
|
169
|
+
* `jj --version` on its first operation.
|
|
169
170
|
*
|
|
170
171
|
* @category layers
|
|
171
172
|
* @since 1.0.0-rc.0
|
|
172
173
|
*/
|
|
173
|
-
export declare const layerAt: (root: string) => Layer.Layer<BunHost | Crypto.Crypto
|
|
174
|
+
export declare const layerAt: (root: string) => Layer.Layer<BunHost | Crypto.Crypto>;
|
|
174
175
|
/**
|
|
175
176
|
* Provides the Bun host with process containment turned on.
|
|
176
177
|
*
|
|
@@ -182,24 +183,23 @@ export declare const layerAt: (root: string) => Layer.Layer<BunHost | Crypto.Cry
|
|
|
182
183
|
* it makes, `process.kill` and `taskkill`, are Node's, and Bun implements
|
|
183
184
|
* them unchanged.
|
|
184
185
|
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
186
|
+
* Builds without jj exactly as {@link layer} does; here the first
|
|
187
|
+
* operation's `jj --version` probe runs through the contained spawner, so its
|
|
187
188
|
* record enters and leaves the ledger like any other child.
|
|
188
189
|
*
|
|
189
190
|
* @category layers
|
|
190
191
|
* @since 1.0.0-rc.0
|
|
191
192
|
*/
|
|
192
|
-
export declare const layerContained: (options?: ContainedOptions) => Layer.Layer<BunHost | Crypto.Crypto,
|
|
193
|
+
export declare const layerContained: (options?: ContainedOptions) => Layer.Layer<BunHost | Crypto.Crypto, never, ProcessLedger.ProcessLedger>;
|
|
193
194
|
/**
|
|
194
195
|
* Provides the contained Bun host bound to one absolute repository root.
|
|
195
196
|
*
|
|
196
197
|
* Refuses a root exactly as {@link layerAt} does: {@link BunHostError} with
|
|
197
198
|
* code `invalid_repository_root`, thrown when the factory is called.
|
|
198
|
-
*
|
|
199
|
-
* does.
|
|
199
|
+
* Builds without jj exactly as {@link layerContained} does.
|
|
200
200
|
*
|
|
201
201
|
* @category layers
|
|
202
202
|
* @since 1.0.0-rc.0
|
|
203
203
|
*/
|
|
204
|
-
export declare const layerContainedAt: (root: string, options?: ContainedOptions) => Layer.Layer<BunHost | Crypto.Crypto,
|
|
204
|
+
export declare const layerContainedAt: (root: string, options?: ContainedOptions) => Layer.Layer<BunHost | Crypto.Crypto, never, ProcessLedger.ProcessLedger>;
|
|
205
205
|
//# sourceMappingURL=BunHost.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BunHost.d.ts","sourceRoot":"","sources":["../../src/BunHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,sBAAsB,MAAM,6CAA6C,CAAA;AACrF,OAAO,KAAK,SAAS,MAAM,gCAAgC,CAAA;AAE3D,OAAO,KAAK,EAAE,EAAE,EAAE,
|
|
1
|
+
{"version":3,"file":"BunHost.d.ts","sourceRoot":"","sources":["../../src/BunHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,sBAAsB,MAAM,6CAA6C,CAAA;AACrF,OAAO,KAAK,SAAS,MAAM,gCAAgC,CAAA;AAE3D,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,YAAY,CAAA;AAEpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,KAAK,KAAK,wBAAwB,MAAM,mCAAmC,CAAA;AAClF,OAAO,KAAK,KAAK,aAAa,MAAM,8BAA8B,CAAA;AAClE,OAAO,KAAK,gBAAgB,MAAM,wCAAwC,CAAA;AAE1E,OAAO,KAAK,YAAY,MAAM,oCAAoC,CAAA;AAElE,OAAO,KAAK,aAAa,MAAM,qCAAqC,CAAA;AACpE,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,KAAK,IAAI,MAAM,aAAa,CAAA;AACxC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAA;AAC7E,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAA;AAEnD;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,CAAA;AAE1G;;;;;;GAMG;AACH,MAAM,MAAM,OAAO,GACf,UAAU,GACV,IAAI,CAAC,IAAI,GACT,mBAAmB,GACnB,EAAE,GACF,UAAU,GACV,wBAAwB,CAAC,kBAAkB,CAAA;AAE/C;;;;;;;;;;GAUG;AACH,MAAM,MAAM,gBAAgB,GAAG,yBAAyB,CAAA;;;;;AAExD;;;;;;;;;;;GAWG;AACH,qBAAa,YAAa,SAAQ,iBAGhC;IACA,SAAkB,IAAI,kBAAiB;CACxC;AA4DD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAMvF,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,gBAAgB,GAAG,aAAa,CAAC,cAAc,GAAG,aAAa,CAAC,OAAO,CAAA;AAWnF;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,KAAK,CAAC,UAAU,CAAuC,CAAA;AAE3F;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CAMtD,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,KAAG,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CASzE,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,cAAc,GACzB,UAAU,gBAAgB,KACzB,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,aAAa,CAczE,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAC3B,MAAM,MAAM,EACZ,UAAU,gBAAgB,KACzB,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,aAAa,CAYzE,CAAA"}
|
package/dist/cjs/BunHost.js
CHANGED
|
@@ -79,8 +79,9 @@ const preview = (root, budget) => {
|
|
|
79
79
|
return `"${excerpt}"${suffix}`;
|
|
80
80
|
};
|
|
81
81
|
const absoluteRoot = (factory, root) => {
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
const requirement = !(0, import_node_path.isAbsolute)(root) ? "an absolute repository root" : root.includes("\0") ? "a repository root without NUL" : void 0;
|
|
83
|
+
if (requirement === void 0) return root;
|
|
84
|
+
const prefix = `BunHost.${factory} requires ${requirement}, got `;
|
|
84
85
|
throw new BunHostError({
|
|
85
86
|
code: "invalid_repository_root",
|
|
86
87
|
message: `${prefix}${preview(root, messageLimit - prefix.length)}`
|
package/dist/cjs/BunHost.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/BunHost.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Aggregate Bun Host bundle.\n *\n * Runtime-specific dependencies stay inside this package; callers get the same\n * closed five-service Host surface every other bundle provides, plus native\n * `ProcessConfinement` for guarded commands.\n *\n * There is no Bun shell module: running a command is Effect's\n * `ChildProcessSpawner`, and `@effect/platform-bun`'s implementation is\n * `@effect/platform-node-shared`'s re-exported: the same code on both\n * runtimes, so no runtime detection is needed here.\n *\n * Outgoing HTTP uses the shared EgressHttpClient on both runtimes. Web\n * requests pin approved addresses; ordinary requests retain the shared pool.\n * Redirects remain visible to the capability kernel, and proxy routing follows\n * the host environment.\n *\n * The filesystem slot is `@smthrs/platform-node`'s `AtomicFileSystem`, byte for\n * byte the layer `NodeHost` uses, so a guarded path operation is\n * descriptor-relative and no-follow on both runtimes.\n *\n * @since 1.0.0-rc.0\n */\n\nimport * as BunChildProcessSpawner from \"@effect/platform-bun/BunChildProcessSpawner\"\nimport * as BunCrypto from \"@effect/platform-bun/BunCrypto\"\nimport * as BunPath from \"@effect/platform-bun/BunPath\"\nimport type { Jj, JjError } from \"@smthrs/jj\"\nimport * as BunJj from \"@smthrs/jj/bun/BunJj\"\nimport type { HostServiceIds } from \"@smthrs/kernel/HostServices\"\nimport type * as KernelProcessConfinement from \"@smthrs/kernel/ProcessConfinement\"\nimport type * as ProcessLedger from \"@smthrs/kernel/ProcessLedger\"\nimport * as AtomicFileSystem from \"@smthrs/platform-node/AtomicFileSystem\"\nimport * as EgressHttpClient from \"@smthrs/platform-node/EgressHttpClient\"\nimport * as HostLiveness from \"@smthrs/platform-node/HostLiveness\"\nimport * as ProcessConfinement from \"@smthrs/platform-node/ProcessConfinement\"\nimport * as ProcessReaper from \"@smthrs/platform-node/ProcessReaper\"\nimport type * as Crypto from \"effect/Crypto\"\nimport type { FileSystem } from \"effect/FileSystem\"\nimport type { HttpClient } from \"effect/http/HttpClient\"\nimport * as Layer from \"effect/Layer\"\nimport type * as Path from \"effect/Path\"\nimport type { ChildProcessSpawner } from \"effect/process/ChildProcessSpawner\"\nimport * as Schema from \"effect/Schema\"\nimport { isAbsolute } from \"node:path\"\nimport * as BunFileSystem from \"./BunFileSystem.ts\"\n\n/**\n * Bun platform modules for selectively providing individual services.\n *\n * `AtomicFileSystem` is here for the same reason `NodeHost` re-exports it: it\n * owns the only configuration escape hatch the filesystem slot has, and a Bun\n * program with a different packaged helper path needs to reach\n * `AtomicFileSystem.layerWith` without hand-composing the other four tags.\n *\n * `BunJj` is deliberately absent: it belongs to `@smthrs/jj` and is imported\n * from there, never re-exported here. So is `@effect/platform-bun/BunHttpClient`:\n * its `layer` follows redirects to origins the capability kernel never\n * authorized. The network slot on its own is {@link layerHttpClient}.\n *\n * @category re-exports\n * @since 1.0.0-rc.0\n * @slop\n */\nexport { AtomicFileSystem, BunChildProcessSpawner, BunCrypto, BunFileSystem, HostLiveness, ProcessReaper }\n\n/**\n * The complete closed Host service union provided by Bun.\n *\n * @category models\n * @since 1.0.0-rc.0\n * @slop\n */\nexport type BunHost =\n | FileSystem\n | Path.Path\n | ChildProcessSpawner\n | Jj\n | HttpClient\n | KernelProcessConfinement.ProcessConfinement\n\n/**\n * The stable codes a `BunHost` factory refuses with.\n *\n * A code is the contract a caller branches on; the message beside it is for a\n * person. `invalid_repository_root` is the root handed to {@link layerAt} or\n * {@link layerContainedAt} not being absolute, the empty string included.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type BunHostErrorCode = \"invalid_repository_root\"\n\n/**\n * The refusal a `BunHost` factory throws before it builds a layer.\n *\n * It is thrown rather than failed because a factory is called while a program\n * composes its layers, where there is no fiber to fail yet and a wrong root is\n * a composition mistake, not a runtime outcome. It is this package's class,\n * not the `Jj` adapter's, because a Bun caller composes `BunHost` and should\n * learn nothing about the adapter behind the `Jj` slot from an error message.\n *\n * @category errors\n * @since 1.0.0-rc.0\n */\nexport class BunHostError extends Schema.TaggedError<BunHostError>()(\"@smthrs/platform-bun/BunHostError\", {\n code: Schema.Literal(\"invalid_repository_root\"),\n message: Schema.String\n}) {\n override readonly name = \"BunHostError\"\n}\n\n/** The longest message a factory refuses with, in UTF-16 code units. */\nconst messageLimit = 255\n\n/** How many code points of a refused root the message repeats at most. */\nconst previewLimit = 64\n\n/**\n * A bounded, quoted excerpt of a refused root that fits in `budget` code\n * units of the message.\n *\n * Counted in code points rather than UTF-16 units so a cut never splits a\n * surrogate pair, and passed through `JSON.stringify` so a newline in the root\n * cannot break the log line the message lands on. Each code point is encoded\n * on its own and admitted only while the quoted excerpt plus the length\n * suffix still fit, so a control character that widens to a `\\u0001` escape\n * shortens the excerpt instead of lengthening the message, and a cut never\n * lands inside an escape.\n */\nconst preview = (root: string, budget: number): string => {\n const points = Array.from(root)\n const whole = JSON.stringify(root)\n if (points.length <= previewLimit && whole.length <= budget) return whole\n const suffix = `... (${points.length} characters)`\n const room = budget - suffix.length - 2\n let excerpt = \"\"\n for (const point of points.slice(0, previewLimit)) {\n const encoded = JSON.stringify(point).slice(1, -1)\n if (excerpt.length + encoded.length > room) break\n excerpt += encoded\n }\n return `\"${excerpt}\"${suffix}`\n}\n\n/**\n * Refuses a root the `Jj` adapter underneath would refuse, here and in this\n * package's name.\n *\n * `BunJj.layerAt` and `BunJj.layerSpawnerAt` are `NodeJj`'s, and those throw a\n * bare `TypeError` that names `NodeJj`, carries no code, and echoes the whole\n * string. The predicate here is the same `isAbsolute` the adapter applies, so\n * a root that passes this check cannot reach that throw.\n */\nconst absoluteRoot = (factory: \"layerAt\" | \"layerContainedAt\", root: string): string => {\n if (isAbsolute(root)) return root\n const prefix = `BunHost.${factory} requires an absolute repository root, got `\n throw new BunHostError({\n code: \"invalid_repository_root\",\n message: `${prefix}${preview(root, messageLimit - prefix.length)}`\n })\n}\n\n/**\n * Stable implementation identities keyed by the closed Host service slots.\n *\n * Each value names the module behind that slot in the raw host bundle.\n * Contained POSIX factories replace the spawner with {@link ProcessReaper.layerSpawner}.\n * These are identity\n * tokens rather than import specifiers: the filesystem entry is\n * `@smthrs/platform-node/AtomicFileSystem` because that is the implementation,\n * even though a consumer reaches it through `@smthrs/platform-bun`.\n *\n * Nothing digests them yet. `@smthrs/plan`'s step key carries a `layers`\n * component these values are meant to feed, but no planner derives it from a\n * host bundle today, so changing one invalidates no cached step.\n *\n * The keys are written as literals rather than `HostServiceIds` positions so\n * that reordering the closed list cannot silently pair a slot with another\n * slot's implementation.\n *\n * @category models\n * @since 1.0.0-rc.0\n * @slop\n */\nexport const implementationIds: Readonly<Record<(typeof HostServiceIds)[number], string>> = {\n \"effect/FileSystem\": \"@smthrs/platform-node/AtomicFileSystem\",\n \"effect/Path\": \"@effect/platform-bun/BunPath\",\n \"effect/process/ChildProcessSpawner\": \"@effect/platform-bun/BunChildProcessSpawner\",\n \"@smthrs/jj/Jj\": \"@smthrs/jj/bun/BunJj\",\n \"effect/HttpClient\": \"@smthrs/platform-node/EgressHttpClient\"\n}\n\n/**\n * What a caller may configure about containment.\n *\n * `ContainedSpawner.Options.platform` is deliberately not part of it. It\n * decides one thing, whether a command that names no `detached` option gets a\n * process group of its own, and this native host detaches by `process.platform`\n * whatever a record claims. A\n * caller-supplied `\"win32\"` on a POSIX host would therefore record\n * `pgid: null` for a child that really does lead a group, and\n * {@link ProcessReaper.reap} would retire that record as `no-group` and leave\n * the orphan running forever: a durable lie rather than a compile error.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type ContainedOptions = ProcessReaper.SpawnerOptions & ProcessReaper.Options\n\n/** The reaper half of {@link ContainedOptions}. */\nconst reaping = (options?: ContainedOptions): ProcessReaper.Options => ({\n ownerPid: options?.ownerPid,\n system: options?.system\n})\n\n/** The two services `BunChildProcessSpawner` resolves paths and files with. */\nconst platform = Layer.mergeAll(BunFileSystem.layer, BunPath.layer, BunCrypto.layer)\n\n/**\n * The network slot on its own: the shared egress client, which never follows a\n * redirect. A `3xx` comes back to the caller with its `location` intact and the\n * second origin uncontacted; following a redirect is `@smthrs/kernel`'s guarded\n * `HttpClient.layer`, which rechecks every hop.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerHttpClient: Layer.Layer<HttpClient> = EgressHttpClient.layer(process.env)\n\n/**\n * Provides all five Bun Host services, including the runtime-independent Path\n * service.\n *\n * Fails at construction with {@link JjError} carrying `not_installed` or\n * `unsupported_version`: the `Jj` slot is merged eagerly and probes\n * `jj --version` while the layer is built, before the program body runs and\n * whether or not the program asks for `Jj`. Requires jj 0.39.0 or later.\n *\n * @category layers\n * @since 1.0.0-rc.0\n * @slop\n */\nexport const layer: Layer.Layer<BunHost | Crypto.Crypto, JjError> = Layer.mergeAll(\n ProcessConfinement.layer(),\n platform,\n Layer.provide(BunChildProcessSpawner.layer, platform),\n BunJj.layer,\n layerHttpClient\n)\n\n/**\n * Provides all five Bun Host services bound to one absolute repository root.\n *\n * Throws {@link BunHostError} with code `invalid_repository_root` when `root`\n * is not absolute; the empty string counts. The check runs when the factory is\n * called, before any layer exists.\n *\n * Fails at construction with {@link JjError} exactly as {@link layer} does:\n * the `Jj` slot probes `jj --version` while the layer is built.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerAt = (root: string): Layer.Layer<BunHost | Crypto.Crypto, JjError> => {\n const repositoryRoot = absoluteRoot(\"layerAt\", root)\n return Layer.mergeAll(\n ProcessConfinement.layer(),\n platform,\n Layer.provide(BunChildProcessSpawner.layer, platform),\n BunJj.layerAt(repositoryRoot),\n layerHttpClient\n )\n}\n\n/**\n * Provides the Bun host with process containment turned on.\n *\n * Bun uses the same prepared native process lifecycle as Node on POSIX, while\n * preserving its underlying runtime spawner on Windows. `ContainedSpawner` gives\n * every child an escalation deadline and a ledger entry, and\n * `ProcessReaper` sweeps the entries a crashed incarnation of this host left\n * behind. The reaper module lives in `@smthrs/platform-node` because the calls\n * it makes, `process.kill` and `taskkill`, are Node's, and Bun implements\n * them unchanged.\n *\n * Fails at construction with {@link JjError} exactly as {@link layer} does;\n * here the `jj --version` probe runs through the contained spawner, so its\n * record enters and leaves the ledger like any other child.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerContained = (\n options?: ContainedOptions\n): Layer.Layer<BunHost | Crypto.Crypto, JjError, ProcessLedger.ProcessLedger> => {\n const spawner = Layer.provide(\n ProcessReaper.layerSpawner({ graceMs: options?.graceMs }),\n Layer.provide(BunChildProcessSpawner.layer, platform)\n )\n return Layer.mergeAll(\n ProcessConfinement.layer(),\n platform,\n layerHttpClient,\n // jj goes through the CONTAINED spawner here, not around it, exactly as in\n // `NodeHost.layerContained`: a jj child that starts its own process leads\n // no recorded group, is in no ledger, and outlives the host that ran it.\n Layer.provideMerge(BunJj.layerSpawner, spawner)\n ).pipe(Layer.provideMerge(ProcessReaper.layer(reaping(options))))\n}\n\n/**\n * Provides the contained Bun host bound to one absolute repository root.\n *\n * Refuses a root exactly as {@link layerAt} does: {@link BunHostError} with\n * code `invalid_repository_root`, thrown when the factory is called.\n * Fails at construction with {@link JjError} exactly as {@link layerContained}\n * does.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerContainedAt = (\n root: string,\n options?: ContainedOptions\n): Layer.Layer<BunHost | Crypto.Crypto, JjError, ProcessLedger.ProcessLedger> => {\n const repositoryRoot = absoluteRoot(\"layerContainedAt\", root)\n const spawner = Layer.provide(\n ProcessReaper.layerSpawner({ graceMs: options?.graceMs }),\n Layer.provide(BunChildProcessSpawner.layer, platform)\n )\n return Layer.mergeAll(\n ProcessConfinement.layer(),\n platform,\n layerHttpClient,\n Layer.provideMerge(BunJj.layerSpawnerAt(repositoryRoot), spawner)\n ).pipe(Layer.provideMerge(ProcessReaper.layer(reaping(options))))\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwBA,6BAAwC;AACxC,gBAA2B;AAC3B,cAAyB;AAEzB,YAAuB;AAIvB,uBAAkC;AAClC,uBAAkC;AAClC,mBAA8B;AAC9B,yBAAoC;AACpC,oBAA+B;AAI/B,YAAuB;AAGvB,aAAwB;AACxB,uBAA2B;AAC3B,oBAA+B;
|
|
4
|
+
"sourcesContent": ["/**\n * Aggregate Bun Host bundle.\n *\n * Runtime-specific dependencies stay inside this package; callers get the same\n * closed five-service Host surface every other bundle provides, plus native\n * `ProcessConfinement` for guarded commands.\n *\n * There is no Bun shell module: running a command is Effect's\n * `ChildProcessSpawner`, and `@effect/platform-bun`'s implementation is\n * `@effect/platform-node-shared`'s re-exported: the same code on both\n * runtimes, so no runtime detection is needed here.\n *\n * Outgoing HTTP uses the shared EgressHttpClient on both runtimes. Web\n * requests pin approved addresses; ordinary requests retain the shared pool.\n * Redirects remain visible to the capability kernel, and proxy routing follows\n * the host environment.\n *\n * The filesystem slot is `@smthrs/platform-node`'s `AtomicFileSystem`, byte for\n * byte the layer `NodeHost` uses, so a guarded path operation is\n * descriptor-relative and no-follow on both runtimes.\n *\n * @since 1.0.0-rc.0\n */\n\nimport * as BunChildProcessSpawner from \"@effect/platform-bun/BunChildProcessSpawner\"\nimport * as BunCrypto from \"@effect/platform-bun/BunCrypto\"\nimport * as BunPath from \"@effect/platform-bun/BunPath\"\nimport type { Jj } from \"@smthrs/jj\"\nimport * as BunJj from \"@smthrs/jj/bun/BunJj\"\nimport type { HostServiceIds } from \"@smthrs/kernel/HostServices\"\nimport type * as KernelProcessConfinement from \"@smthrs/kernel/ProcessConfinement\"\nimport type * as ProcessLedger from \"@smthrs/kernel/ProcessLedger\"\nimport * as AtomicFileSystem from \"@smthrs/platform-node/AtomicFileSystem\"\nimport * as EgressHttpClient from \"@smthrs/platform-node/EgressHttpClient\"\nimport * as HostLiveness from \"@smthrs/platform-node/HostLiveness\"\nimport * as ProcessConfinement from \"@smthrs/platform-node/ProcessConfinement\"\nimport * as ProcessReaper from \"@smthrs/platform-node/ProcessReaper\"\nimport type * as Crypto from \"effect/Crypto\"\nimport type { FileSystem } from \"effect/FileSystem\"\nimport type { HttpClient } from \"effect/http/HttpClient\"\nimport * as Layer from \"effect/Layer\"\nimport type * as Path from \"effect/Path\"\nimport type { ChildProcessSpawner } from \"effect/process/ChildProcessSpawner\"\nimport * as Schema from \"effect/Schema\"\nimport { isAbsolute } from \"node:path\"\nimport * as BunFileSystem from \"./BunFileSystem.ts\"\n\n/**\n * Bun platform modules for selectively providing individual services.\n *\n * `AtomicFileSystem` is here for the same reason `NodeHost` re-exports it: it\n * owns the only configuration escape hatch the filesystem slot has, and a Bun\n * program with a different packaged helper path needs to reach\n * `AtomicFileSystem.layerWith` without hand-composing the other four tags.\n *\n * `BunJj` is deliberately absent: it belongs to `@smthrs/jj` and is imported\n * from there, never re-exported here. So is `@effect/platform-bun/BunHttpClient`:\n * its `layer` follows redirects to origins the capability kernel never\n * authorized. The network slot on its own is {@link layerHttpClient}.\n *\n * @category re-exports\n * @since 1.0.0-rc.0\n * @slop\n */\nexport { AtomicFileSystem, BunChildProcessSpawner, BunCrypto, BunFileSystem, HostLiveness, ProcessReaper }\n\n/**\n * The complete closed Host service union provided by Bun.\n *\n * @category models\n * @since 1.0.0-rc.0\n * @slop\n */\nexport type BunHost =\n | FileSystem\n | Path.Path\n | ChildProcessSpawner\n | Jj\n | HttpClient\n | KernelProcessConfinement.ProcessConfinement\n\n/**\n * The stable codes a `BunHost` factory refuses with.\n *\n * A code is the contract a caller branches on; the message beside it is for a\n * person. `invalid_repository_root` is the root handed to {@link layerAt} or\n * {@link layerContainedAt} not being absolute or containing NUL, the empty\n * string included.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type BunHostErrorCode = \"invalid_repository_root\"\n\n/**\n * The refusal a `BunHost` factory throws before it builds a layer.\n *\n * It is thrown rather than failed because a factory is called while a program\n * composes its layers, where there is no fiber to fail yet and a wrong root is\n * a composition mistake, not a runtime outcome. It is this package's class,\n * not the `Jj` adapter's, because a Bun caller composes `BunHost` and should\n * learn nothing about the adapter behind the `Jj` slot from an error message.\n *\n * @category errors\n * @since 1.0.0-rc.0\n */\nexport class BunHostError extends Schema.TaggedError<BunHostError>()(\"@smthrs/platform-bun/BunHostError\", {\n code: Schema.Literal(\"invalid_repository_root\"),\n message: Schema.String\n}) {\n override readonly name = \"BunHostError\"\n}\n\n/** The longest message a factory refuses with, in UTF-16 code units. */\nconst messageLimit = 255\n\n/** How many code points of a refused root the message repeats at most. */\nconst previewLimit = 64\n\n/**\n * A bounded, quoted excerpt of a refused root that fits in `budget` code\n * units of the message.\n *\n * Counted in code points rather than UTF-16 units so a cut never splits a\n * surrogate pair, and passed through `JSON.stringify` so a newline in the root\n * cannot break the log line the message lands on. Each code point is encoded\n * on its own and admitted only while the quoted excerpt plus the length\n * suffix still fit, so a control character that widens to a `\\u0001` escape\n * shortens the excerpt instead of lengthening the message, and a cut never\n * lands inside an escape.\n */\nconst preview = (root: string, budget: number): string => {\n const points = Array.from(root)\n const whole = JSON.stringify(root)\n if (points.length <= previewLimit && whole.length <= budget) return whole\n const suffix = `... (${points.length} characters)`\n const room = budget - suffix.length - 2\n let excerpt = \"\"\n for (const point of points.slice(0, previewLimit)) {\n const encoded = JSON.stringify(point).slice(1, -1)\n if (excerpt.length + encoded.length > room) break\n excerpt += encoded\n }\n return `\"${excerpt}\"${suffix}`\n}\n\n/**\n * Refuses a root the `Jj` adapter underneath would refuse, here and in this\n * package's name.\n *\n * `BunJj.layerAt` and `BunJj.layerSpawnerAt` are `NodeJj`'s, and those throw a\n * bare `TypeError` that names `NodeJj`, carries no code, and echoes the whole\n * string. The predicate includes the same `isAbsolute` the adapter applies, so\n * a root that passes this check cannot reach that throw. An absolute root\n * containing NUL is refused too: no filesystem or process API can represent it.\n */\nconst absoluteRoot = (factory: \"layerAt\" | \"layerContainedAt\", root: string): string => {\n // An absolute root with NUL names that defect, not absoluteness it already has.\n const requirement = !isAbsolute(root)\n ? \"an absolute repository root\"\n : root.includes(\"\\0\")\n ? \"a repository root without NUL\"\n : undefined\n if (requirement === undefined) return root\n const prefix = `BunHost.${factory} requires ${requirement}, got `\n throw new BunHostError({\n code: \"invalid_repository_root\",\n message: `${prefix}${preview(root, messageLimit - prefix.length)}`\n })\n}\n\n/**\n * Stable implementation identities keyed by the closed Host service slots.\n *\n * Each value names the module behind that slot in the raw host bundle.\n * Contained POSIX factories replace the spawner with {@link ProcessReaper.layerSpawner}.\n * These are identity\n * tokens rather than import specifiers: the filesystem entry is\n * `@smthrs/platform-node/AtomicFileSystem` because that is the implementation,\n * even though a consumer reaches it through `@smthrs/platform-bun`.\n *\n * Nothing digests them yet. `@smthrs/plan`'s step key carries a `layers`\n * component these values are meant to feed, but no planner derives it from a\n * host bundle today, so changing one invalidates no cached step.\n *\n * The keys are written as literals rather than `HostServiceIds` positions so\n * that reordering the closed list cannot silently pair a slot with another\n * slot's implementation.\n *\n * @category models\n * @since 1.0.0-rc.0\n * @slop\n */\nexport const implementationIds: Readonly<Record<(typeof HostServiceIds)[number], string>> = {\n \"effect/FileSystem\": \"@smthrs/platform-node/AtomicFileSystem\",\n \"effect/Path\": \"@effect/platform-bun/BunPath\",\n \"effect/process/ChildProcessSpawner\": \"@effect/platform-bun/BunChildProcessSpawner\",\n \"@smthrs/jj/Jj\": \"@smthrs/jj/bun/BunJj\",\n \"effect/HttpClient\": \"@smthrs/platform-node/EgressHttpClient\"\n}\n\n/**\n * What a caller may configure about containment.\n *\n * `ContainedSpawner.Options.platform` is deliberately not part of it. It\n * decides one thing, whether a command that names no `detached` option gets a\n * process group of its own, and this native host detaches by `process.platform`\n * whatever a record claims. A\n * caller-supplied `\"win32\"` on a POSIX host would therefore record\n * `pgid: null` for a child that really does lead a group, and\n * {@link ProcessReaper.reap} would retire that record as `no-group` and leave\n * the orphan running forever: a durable lie rather than a compile error.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type ContainedOptions = ProcessReaper.SpawnerOptions & ProcessReaper.Options\n\n/** The reaper half of {@link ContainedOptions}. */\nconst reaping = (options?: ContainedOptions): ProcessReaper.Options => ({\n ownerPid: options?.ownerPid,\n system: options?.system\n})\n\n/** The two services `BunChildProcessSpawner` resolves paths and files with. */\nconst platform = Layer.mergeAll(BunFileSystem.layer, BunPath.layer, BunCrypto.layer)\n\n/**\n * The network slot on its own: the shared egress client, which never follows a\n * redirect. A `3xx` comes back to the caller with its `location` intact and the\n * second origin uncontacted; following a redirect is `@smthrs/kernel`'s guarded\n * `HttpClient.layer`, which rechecks every hop.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerHttpClient: Layer.Layer<HttpClient> = EgressHttpClient.layer(process.env)\n\n/**\n * Provides all five Bun Host services, including the runtime-independent Path\n * service.\n *\n * Builds on a host without jj. The `Jj` slot probes `jj --version` on its\n * first operation and fails that operation with `JjError` carrying\n * `not_installed` or `unsupported_version`; a program that never calls `Jj`\n * runs without jj. `Jj` requires jj 0.39.0 or later.\n *\n * @category layers\n * @since 1.0.0-rc.0\n * @slop\n */\nexport const layer: Layer.Layer<BunHost | Crypto.Crypto> = Layer.mergeAll(\n ProcessConfinement.layer(),\n platform,\n Layer.provide(BunChildProcessSpawner.layer, platform),\n BunJj.layer,\n layerHttpClient\n)\n\n/**\n * Provides all five Bun Host services bound to one absolute repository root.\n *\n * Throws {@link BunHostError} with code `invalid_repository_root` when `root`\n * is not absolute or contains NUL; the empty string counts. The check runs\n * when the factory is called, before any layer exists.\n *\n * Builds without jj exactly as {@link layer} does: the `Jj` slot probes\n * `jj --version` on its first operation.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerAt = (root: string): Layer.Layer<BunHost | Crypto.Crypto> => {\n const repositoryRoot = absoluteRoot(\"layerAt\", root)\n return Layer.mergeAll(\n ProcessConfinement.layer(),\n platform,\n Layer.provide(BunChildProcessSpawner.layer, platform),\n BunJj.layerAt(repositoryRoot),\n layerHttpClient\n )\n}\n\n/**\n * Provides the Bun host with process containment turned on.\n *\n * Bun uses the same prepared native process lifecycle as Node on POSIX, while\n * preserving its underlying runtime spawner on Windows. `ContainedSpawner` gives\n * every child an escalation deadline and a ledger entry, and\n * `ProcessReaper` sweeps the entries a crashed incarnation of this host left\n * behind. The reaper module lives in `@smthrs/platform-node` because the calls\n * it makes, `process.kill` and `taskkill`, are Node's, and Bun implements\n * them unchanged.\n *\n * Builds without jj exactly as {@link layer} does; here the first\n * operation's `jj --version` probe runs through the contained spawner, so its\n * record enters and leaves the ledger like any other child.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerContained = (\n options?: ContainedOptions\n): Layer.Layer<BunHost | Crypto.Crypto, never, ProcessLedger.ProcessLedger> => {\n const spawner = Layer.provide(\n ProcessReaper.layerSpawner({ graceMs: options?.graceMs }),\n Layer.provide(BunChildProcessSpawner.layer, platform)\n )\n return Layer.mergeAll(\n ProcessConfinement.layer(),\n platform,\n layerHttpClient,\n // jj goes through the CONTAINED spawner here, not around it, exactly as in\n // `NodeHost.layerContained`: a jj child that starts its own process leads\n // no recorded group, is in no ledger, and outlives the host that ran it.\n Layer.provideMerge(BunJj.layerSpawner, spawner)\n ).pipe(Layer.provideMerge(ProcessReaper.layer(reaping(options))))\n}\n\n/**\n * Provides the contained Bun host bound to one absolute repository root.\n *\n * Refuses a root exactly as {@link layerAt} does: {@link BunHostError} with\n * code `invalid_repository_root`, thrown when the factory is called.\n * Builds without jj exactly as {@link layerContained} does.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerContainedAt = (\n root: string,\n options?: ContainedOptions\n): Layer.Layer<BunHost | Crypto.Crypto, never, ProcessLedger.ProcessLedger> => {\n const repositoryRoot = absoluteRoot(\"layerContainedAt\", root)\n const spawner = Layer.provide(\n ProcessReaper.layerSpawner({ graceMs: options?.graceMs }),\n Layer.provide(BunChildProcessSpawner.layer, platform)\n )\n return Layer.mergeAll(\n ProcessConfinement.layer(),\n platform,\n layerHttpClient,\n Layer.provideMerge(BunJj.layerSpawnerAt(repositoryRoot), spawner)\n ).pipe(Layer.provideMerge(ProcessReaper.layer(reaping(options))))\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/esm/BunHost.d.ts
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import * as BunChildProcessSpawner from "../vendor/bun/BunChildProcessSpawner.js";
|
|
25
25
|
import * as BunCrypto from "../vendor/bun/BunCrypto.js";
|
|
26
|
-
import type { Jj
|
|
26
|
+
import type { Jj } from "@smthrs/jj";
|
|
27
27
|
import type { HostServiceIds } from "@smthrs/kernel/HostServices";
|
|
28
28
|
import type * as KernelProcessConfinement from "@smthrs/kernel/ProcessConfinement";
|
|
29
29
|
import type * as ProcessLedger from "@smthrs/kernel/ProcessLedger";
|
|
@@ -69,7 +69,8 @@ export type BunHost = FileSystem | Path.Path | ChildProcessSpawner | Jj | HttpCl
|
|
|
69
69
|
*
|
|
70
70
|
* A code is the contract a caller branches on; the message beside it is for a
|
|
71
71
|
* person. `invalid_repository_root` is the root handed to {@link layerAt} or
|
|
72
|
-
* {@link layerContainedAt} not being absolute, the empty
|
|
72
|
+
* {@link layerContainedAt} not being absolute or containing NUL, the empty
|
|
73
|
+
* string included.
|
|
73
74
|
*
|
|
74
75
|
* @category models
|
|
75
76
|
* @since 1.0.0-rc.0
|
|
@@ -147,30 +148,30 @@ export declare const layerHttpClient: Layer.Layer<HttpClient>;
|
|
|
147
148
|
* Provides all five Bun Host services, including the runtime-independent Path
|
|
148
149
|
* service.
|
|
149
150
|
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
* `
|
|
153
|
-
*
|
|
151
|
+
* Builds on a host without jj. The `Jj` slot probes `jj --version` on its
|
|
152
|
+
* first operation and fails that operation with `JjError` carrying
|
|
153
|
+
* `not_installed` or `unsupported_version`; a program that never calls `Jj`
|
|
154
|
+
* runs without jj. `Jj` requires jj 0.39.0 or later.
|
|
154
155
|
*
|
|
155
156
|
* @category layers
|
|
156
157
|
* @since 1.0.0-rc.0
|
|
157
158
|
* @slop
|
|
158
159
|
*/
|
|
159
|
-
export declare const layer: Layer.Layer<BunHost | Crypto.Crypto
|
|
160
|
+
export declare const layer: Layer.Layer<BunHost | Crypto.Crypto>;
|
|
160
161
|
/**
|
|
161
162
|
* Provides all five Bun Host services bound to one absolute repository root.
|
|
162
163
|
*
|
|
163
164
|
* Throws {@link BunHostError} with code `invalid_repository_root` when `root`
|
|
164
|
-
* is not absolute; the empty string counts. The check runs
|
|
165
|
-
* called, before any layer exists.
|
|
165
|
+
* is not absolute or contains NUL; the empty string counts. The check runs
|
|
166
|
+
* when the factory is called, before any layer exists.
|
|
166
167
|
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
168
|
+
* Builds without jj exactly as {@link layer} does: the `Jj` slot probes
|
|
169
|
+
* `jj --version` on its first operation.
|
|
169
170
|
*
|
|
170
171
|
* @category layers
|
|
171
172
|
* @since 1.0.0-rc.0
|
|
172
173
|
*/
|
|
173
|
-
export declare const layerAt: (root: string) => Layer.Layer<BunHost | Crypto.Crypto
|
|
174
|
+
export declare const layerAt: (root: string) => Layer.Layer<BunHost | Crypto.Crypto>;
|
|
174
175
|
/**
|
|
175
176
|
* Provides the Bun host with process containment turned on.
|
|
176
177
|
*
|
|
@@ -182,24 +183,23 @@ export declare const layerAt: (root: string) => Layer.Layer<BunHost | Crypto.Cry
|
|
|
182
183
|
* it makes, `process.kill` and `taskkill`, are Node's, and Bun implements
|
|
183
184
|
* them unchanged.
|
|
184
185
|
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
186
|
+
* Builds without jj exactly as {@link layer} does; here the first
|
|
187
|
+
* operation's `jj --version` probe runs through the contained spawner, so its
|
|
187
188
|
* record enters and leaves the ledger like any other child.
|
|
188
189
|
*
|
|
189
190
|
* @category layers
|
|
190
191
|
* @since 1.0.0-rc.0
|
|
191
192
|
*/
|
|
192
|
-
export declare const layerContained: (options?: ContainedOptions) => Layer.Layer<BunHost | Crypto.Crypto,
|
|
193
|
+
export declare const layerContained: (options?: ContainedOptions) => Layer.Layer<BunHost | Crypto.Crypto, never, ProcessLedger.ProcessLedger>;
|
|
193
194
|
/**
|
|
194
195
|
* Provides the contained Bun host bound to one absolute repository root.
|
|
195
196
|
*
|
|
196
197
|
* Refuses a root exactly as {@link layerAt} does: {@link BunHostError} with
|
|
197
198
|
* code `invalid_repository_root`, thrown when the factory is called.
|
|
198
|
-
*
|
|
199
|
-
* does.
|
|
199
|
+
* Builds without jj exactly as {@link layerContained} does.
|
|
200
200
|
*
|
|
201
201
|
* @category layers
|
|
202
202
|
* @since 1.0.0-rc.0
|
|
203
203
|
*/
|
|
204
|
-
export declare const layerContainedAt: (root: string, options?: ContainedOptions) => Layer.Layer<BunHost | Crypto.Crypto,
|
|
204
|
+
export declare const layerContainedAt: (root: string, options?: ContainedOptions) => Layer.Layer<BunHost | Crypto.Crypto, never, ProcessLedger.ProcessLedger>;
|
|
205
205
|
//# sourceMappingURL=BunHost.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BunHost.d.ts","sourceRoot":"","sources":["../../src/BunHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,sBAAsB,MAAM,6CAA6C,CAAA;AACrF,OAAO,KAAK,SAAS,MAAM,gCAAgC,CAAA;AAE3D,OAAO,KAAK,EAAE,EAAE,EAAE,
|
|
1
|
+
{"version":3,"file":"BunHost.d.ts","sourceRoot":"","sources":["../../src/BunHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,sBAAsB,MAAM,6CAA6C,CAAA;AACrF,OAAO,KAAK,SAAS,MAAM,gCAAgC,CAAA;AAE3D,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,YAAY,CAAA;AAEpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,KAAK,KAAK,wBAAwB,MAAM,mCAAmC,CAAA;AAClF,OAAO,KAAK,KAAK,aAAa,MAAM,8BAA8B,CAAA;AAClE,OAAO,KAAK,gBAAgB,MAAM,wCAAwC,CAAA;AAE1E,OAAO,KAAK,YAAY,MAAM,oCAAoC,CAAA;AAElE,OAAO,KAAK,aAAa,MAAM,qCAAqC,CAAA;AACpE,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,KAAK,IAAI,MAAM,aAAa,CAAA;AACxC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAA;AAC7E,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAA;AAEnD;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,CAAA;AAE1G;;;;;;GAMG;AACH,MAAM,MAAM,OAAO,GACf,UAAU,GACV,IAAI,CAAC,IAAI,GACT,mBAAmB,GACnB,EAAE,GACF,UAAU,GACV,wBAAwB,CAAC,kBAAkB,CAAA;AAE/C;;;;;;;;;;GAUG;AACH,MAAM,MAAM,gBAAgB,GAAG,yBAAyB,CAAA;;;;;AAExD;;;;;;;;;;;GAWG;AACH,qBAAa,YAAa,SAAQ,iBAGhC;IACA,SAAkB,IAAI,kBAAiB;CACxC;AA4DD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAMvF,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,gBAAgB,GAAG,aAAa,CAAC,cAAc,GAAG,aAAa,CAAC,OAAO,CAAA;AAWnF;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,KAAK,CAAC,UAAU,CAAuC,CAAA;AAE3F;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CAMtD,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,KAAG,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CASzE,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,cAAc,GACzB,UAAU,gBAAgB,KACzB,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,aAAa,CAczE,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAC3B,MAAM,MAAM,EACZ,UAAU,gBAAgB,KACzB,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,aAAa,CAYzE,CAAA"}
|
package/dist/esm/BunHost.js
CHANGED
|
@@ -108,13 +108,20 @@ const preview = (root, budget) => {
|
|
|
108
108
|
*
|
|
109
109
|
* `BunJj.layerAt` and `BunJj.layerSpawnerAt` are `NodeJj`'s, and those throw a
|
|
110
110
|
* bare `TypeError` that names `NodeJj`, carries no code, and echoes the whole
|
|
111
|
-
* string. The predicate
|
|
112
|
-
* a root that passes this check cannot reach that throw.
|
|
111
|
+
* string. The predicate includes the same `isAbsolute` the adapter applies, so
|
|
112
|
+
* a root that passes this check cannot reach that throw. An absolute root
|
|
113
|
+
* containing NUL is refused too: no filesystem or process API can represent it.
|
|
113
114
|
*/
|
|
114
115
|
const absoluteRoot = (factory, root) => {
|
|
115
|
-
|
|
116
|
+
// An absolute root with NUL names that defect, not absoluteness it already has.
|
|
117
|
+
const requirement = !isAbsolute(root)
|
|
118
|
+
? "an absolute repository root"
|
|
119
|
+
: root.includes("\0")
|
|
120
|
+
? "a repository root without NUL"
|
|
121
|
+
: undefined;
|
|
122
|
+
if (requirement === undefined)
|
|
116
123
|
return root;
|
|
117
|
-
const prefix = `BunHost.${factory} requires
|
|
124
|
+
const prefix = `BunHost.${factory} requires ${requirement}, got `;
|
|
118
125
|
throw new BunHostError({
|
|
119
126
|
code: "invalid_repository_root",
|
|
120
127
|
message: `${prefix}${preview(root, messageLimit - prefix.length)}`
|
|
@@ -170,10 +177,10 @@ export const layerHttpClient = EgressHttpClient.layer(process.env);
|
|
|
170
177
|
* Provides all five Bun Host services, including the runtime-independent Path
|
|
171
178
|
* service.
|
|
172
179
|
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
* `
|
|
176
|
-
*
|
|
180
|
+
* Builds on a host without jj. The `Jj` slot probes `jj --version` on its
|
|
181
|
+
* first operation and fails that operation with `JjError` carrying
|
|
182
|
+
* `not_installed` or `unsupported_version`; a program that never calls `Jj`
|
|
183
|
+
* runs without jj. `Jj` requires jj 0.39.0 or later.
|
|
177
184
|
*
|
|
178
185
|
* @category layers
|
|
179
186
|
* @since 1.0.0-rc.0
|
|
@@ -184,11 +191,11 @@ export const layer = Layer.mergeAll(ProcessConfinement.layer(), platform, Layer.
|
|
|
184
191
|
* Provides all five Bun Host services bound to one absolute repository root.
|
|
185
192
|
*
|
|
186
193
|
* Throws {@link BunHostError} with code `invalid_repository_root` when `root`
|
|
187
|
-
* is not absolute; the empty string counts. The check runs
|
|
188
|
-
* called, before any layer exists.
|
|
194
|
+
* is not absolute or contains NUL; the empty string counts. The check runs
|
|
195
|
+
* when the factory is called, before any layer exists.
|
|
189
196
|
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
197
|
+
* Builds without jj exactly as {@link layer} does: the `Jj` slot probes
|
|
198
|
+
* `jj --version` on its first operation.
|
|
192
199
|
*
|
|
193
200
|
* @category layers
|
|
194
201
|
* @since 1.0.0-rc.0
|
|
@@ -208,8 +215,8 @@ export const layerAt = (root) => {
|
|
|
208
215
|
* it makes, `process.kill` and `taskkill`, are Node's, and Bun implements
|
|
209
216
|
* them unchanged.
|
|
210
217
|
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
218
|
+
* Builds without jj exactly as {@link layer} does; here the first
|
|
219
|
+
* operation's `jj --version` probe runs through the contained spawner, so its
|
|
213
220
|
* record enters and leaves the ledger like any other child.
|
|
214
221
|
*
|
|
215
222
|
* @category layers
|
|
@@ -228,8 +235,7 @@ export const layerContained = (options) => {
|
|
|
228
235
|
*
|
|
229
236
|
* Refuses a root exactly as {@link layerAt} does: {@link BunHostError} with
|
|
230
237
|
* code `invalid_repository_root`, thrown when the factory is called.
|
|
231
|
-
*
|
|
232
|
-
* does.
|
|
238
|
+
* Builds without jj exactly as {@link layerContained} does.
|
|
233
239
|
*
|
|
234
240
|
* @category layers
|
|
235
241
|
* @since 1.0.0-rc.0
|
package/dist/esm/BunHost.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BunHost.js","sourceRoot":"","sources":["../../src/BunHost.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Aggregate Bun Host bundle.\n *\n * Runtime-specific dependencies stay inside this package; callers get the same\n * closed five-service Host surface every other bundle provides, plus native\n * `ProcessConfinement` for guarded commands.\n *\n * There is no Bun shell module: running a command is Effect's\n * `ChildProcessSpawner`, and `@effect/platform-bun`'s implementation is\n * `@effect/platform-node-shared`'s re-exported: the same code on both\n * runtimes, so no runtime detection is needed here.\n *\n * Outgoing HTTP uses the shared EgressHttpClient on both runtimes. Web\n * requests pin approved addresses; ordinary requests retain the shared pool.\n * Redirects remain visible to the capability kernel, and proxy routing follows\n * the host environment.\n *\n * The filesystem slot is `@smthrs/platform-node`'s `AtomicFileSystem`, byte for\n * byte the layer `NodeHost` uses, so a guarded path operation is\n * descriptor-relative and no-follow on both runtimes.\n *\n * @since 1.0.0-rc.0\n */\n\nimport * as BunChildProcessSpawner from \"@effect/platform-bun/BunChildProcessSpawner\"\nimport * as BunCrypto from \"@effect/platform-bun/BunCrypto\"\nimport * as BunPath from \"@effect/platform-bun/BunPath\"\nimport type { Jj, JjError } from \"@smthrs/jj\"\nimport * as BunJj from \"@smthrs/jj/bun/BunJj\"\nimport type { HostServiceIds } from \"@smthrs/kernel/HostServices\"\nimport type * as KernelProcessConfinement from \"@smthrs/kernel/ProcessConfinement\"\nimport type * as ProcessLedger from \"@smthrs/kernel/ProcessLedger\"\nimport * as AtomicFileSystem from \"@smthrs/platform-node/AtomicFileSystem\"\nimport * as EgressHttpClient from \"@smthrs/platform-node/EgressHttpClient\"\nimport * as HostLiveness from \"@smthrs/platform-node/HostLiveness\"\nimport * as ProcessConfinement from \"@smthrs/platform-node/ProcessConfinement\"\nimport * as ProcessReaper from \"@smthrs/platform-node/ProcessReaper\"\nimport type * as Crypto from \"effect/Crypto\"\nimport type { FileSystem } from \"effect/FileSystem\"\nimport type { HttpClient } from \"effect/http/HttpClient\"\nimport * as Layer from \"effect/Layer\"\nimport type * as Path from \"effect/Path\"\nimport type { ChildProcessSpawner } from \"effect/process/ChildProcessSpawner\"\nimport * as Schema from \"effect/Schema\"\nimport { isAbsolute } from \"node:path\"\nimport * as BunFileSystem from \"./BunFileSystem.ts\"\n\n/**\n * Bun platform modules for selectively providing individual services.\n *\n * `AtomicFileSystem` is here for the same reason `NodeHost` re-exports it: it\n * owns the only configuration escape hatch the filesystem slot has, and a Bun\n * program with a different packaged helper path needs to reach\n * `AtomicFileSystem.layerWith` without hand-composing the other four tags.\n *\n * `BunJj` is deliberately absent: it belongs to `@smthrs/jj` and is imported\n * from there, never re-exported here. So is `@effect/platform-bun/BunHttpClient`:\n * its `layer` follows redirects to origins the capability kernel never\n * authorized. The network slot on its own is {@link layerHttpClient}.\n *\n * @category re-exports\n * @since 1.0.0-rc.0\n * @slop\n */\nexport { AtomicFileSystem, BunChildProcessSpawner, BunCrypto, BunFileSystem, HostLiveness, ProcessReaper }\n\n/**\n * The complete closed Host service union provided by Bun.\n *\n * @category models\n * @since 1.0.0-rc.0\n * @slop\n */\nexport type BunHost =\n | FileSystem\n | Path.Path\n | ChildProcessSpawner\n | Jj\n | HttpClient\n | KernelProcessConfinement.ProcessConfinement\n\n/**\n * The stable codes a `BunHost` factory refuses with.\n *\n * A code is the contract a caller branches on; the message beside it is for a\n * person. `invalid_repository_root` is the root handed to {@link layerAt} or\n * {@link layerContainedAt} not being absolute, the empty string included.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type BunHostErrorCode = \"invalid_repository_root\"\n\n/**\n * The refusal a `BunHost` factory throws before it builds a layer.\n *\n * It is thrown rather than failed because a factory is called while a program\n * composes its layers, where there is no fiber to fail yet and a wrong root is\n * a composition mistake, not a runtime outcome. It is this package's class,\n * not the `Jj` adapter's, because a Bun caller composes `BunHost` and should\n * learn nothing about the adapter behind the `Jj` slot from an error message.\n *\n * @category errors\n * @since 1.0.0-rc.0\n */\nexport class BunHostError extends Schema.TaggedError<BunHostError>()(\"@smthrs/platform-bun/BunHostError\", {\n code: Schema.Literal(\"invalid_repository_root\"),\n message: Schema.String\n}) {\n override readonly name = \"BunHostError\"\n}\n\n/** The longest message a factory refuses with, in UTF-16 code units. */\nconst messageLimit = 255\n\n/** How many code points of a refused root the message repeats at most. */\nconst previewLimit = 64\n\n/**\n * A bounded, quoted excerpt of a refused root that fits in `budget` code\n * units of the message.\n *\n * Counted in code points rather than UTF-16 units so a cut never splits a\n * surrogate pair, and passed through `JSON.stringify` so a newline in the root\n * cannot break the log line the message lands on. Each code point is encoded\n * on its own and admitted only while the quoted excerpt plus the length\n * suffix still fit, so a control character that widens to a `\\u0001` escape\n * shortens the excerpt instead of lengthening the message, and a cut never\n * lands inside an escape.\n */\nconst preview = (root: string, budget: number): string => {\n const points = Array.from(root)\n const whole = JSON.stringify(root)\n if (points.length <= previewLimit && whole.length <= budget) return whole\n const suffix = `... (${points.length} characters)`\n const room = budget - suffix.length - 2\n let excerpt = \"\"\n for (const point of points.slice(0, previewLimit)) {\n const encoded = JSON.stringify(point).slice(1, -1)\n if (excerpt.length + encoded.length > room) break\n excerpt += encoded\n }\n return `\"${excerpt}\"${suffix}`\n}\n\n/**\n * Refuses a root the `Jj` adapter underneath would refuse, here and in this\n * package's name.\n *\n * `BunJj.layerAt` and `BunJj.layerSpawnerAt` are `NodeJj`'s, and those throw a\n * bare `TypeError` that names `NodeJj`, carries no code, and echoes the whole\n * string. The predicate here is the same `isAbsolute` the adapter applies, so\n * a root that passes this check cannot reach that throw.\n */\nconst absoluteRoot = (factory: \"layerAt\" | \"layerContainedAt\", root: string): string => {\n if (isAbsolute(root)) return root\n const prefix = `BunHost.${factory} requires an absolute repository root, got `\n throw new BunHostError({\n code: \"invalid_repository_root\",\n message: `${prefix}${preview(root, messageLimit - prefix.length)}`\n })\n}\n\n/**\n * Stable implementation identities keyed by the closed Host service slots.\n *\n * Each value names the module behind that slot in the raw host bundle.\n * Contained POSIX factories replace the spawner with {@link ProcessReaper.layerSpawner}.\n * These are identity\n * tokens rather than import specifiers: the filesystem entry is\n * `@smthrs/platform-node/AtomicFileSystem` because that is the implementation,\n * even though a consumer reaches it through `@smthrs/platform-bun`.\n *\n * Nothing digests them yet. `@smthrs/plan`'s step key carries a `layers`\n * component these values are meant to feed, but no planner derives it from a\n * host bundle today, so changing one invalidates no cached step.\n *\n * The keys are written as literals rather than `HostServiceIds` positions so\n * that reordering the closed list cannot silently pair a slot with another\n * slot's implementation.\n *\n * @category models\n * @since 1.0.0-rc.0\n * @slop\n */\nexport const implementationIds: Readonly<Record<(typeof HostServiceIds)[number], string>> = {\n \"effect/FileSystem\": \"@smthrs/platform-node/AtomicFileSystem\",\n \"effect/Path\": \"@effect/platform-bun/BunPath\",\n \"effect/process/ChildProcessSpawner\": \"@effect/platform-bun/BunChildProcessSpawner\",\n \"@smthrs/jj/Jj\": \"@smthrs/jj/bun/BunJj\",\n \"effect/HttpClient\": \"@smthrs/platform-node/EgressHttpClient\"\n}\n\n/**\n * What a caller may configure about containment.\n *\n * `ContainedSpawner.Options.platform` is deliberately not part of it. It\n * decides one thing, whether a command that names no `detached` option gets a\n * process group of its own, and this native host detaches by `process.platform`\n * whatever a record claims. A\n * caller-supplied `\"win32\"` on a POSIX host would therefore record\n * `pgid: null` for a child that really does lead a group, and\n * {@link ProcessReaper.reap} would retire that record as `no-group` and leave\n * the orphan running forever: a durable lie rather than a compile error.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type ContainedOptions = ProcessReaper.SpawnerOptions & ProcessReaper.Options\n\n/** The reaper half of {@link ContainedOptions}. */\nconst reaping = (options?: ContainedOptions): ProcessReaper.Options => ({\n ownerPid: options?.ownerPid,\n system: options?.system\n})\n\n/** The two services `BunChildProcessSpawner` resolves paths and files with. */\nconst platform = Layer.mergeAll(BunFileSystem.layer, BunPath.layer, BunCrypto.layer)\n\n/**\n * The network slot on its own: the shared egress client, which never follows a\n * redirect. A `3xx` comes back to the caller with its `location` intact and the\n * second origin uncontacted; following a redirect is `@smthrs/kernel`'s guarded\n * `HttpClient.layer`, which rechecks every hop.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerHttpClient: Layer.Layer<HttpClient> = EgressHttpClient.layer(process.env)\n\n/**\n * Provides all five Bun Host services, including the runtime-independent Path\n * service.\n *\n * Fails at construction with {@link JjError} carrying `not_installed` or\n * `unsupported_version`: the `Jj` slot is merged eagerly and probes\n * `jj --version` while the layer is built, before the program body runs and\n * whether or not the program asks for `Jj`. Requires jj 0.39.0 or later.\n *\n * @category layers\n * @since 1.0.0-rc.0\n * @slop\n */\nexport const layer: Layer.Layer<BunHost | Crypto.Crypto, JjError> = Layer.mergeAll(\n ProcessConfinement.layer(),\n platform,\n Layer.provide(BunChildProcessSpawner.layer, platform),\n BunJj.layer,\n layerHttpClient\n)\n\n/**\n * Provides all five Bun Host services bound to one absolute repository root.\n *\n * Throws {@link BunHostError} with code `invalid_repository_root` when `root`\n * is not absolute; the empty string counts. The check runs when the factory is\n * called, before any layer exists.\n *\n * Fails at construction with {@link JjError} exactly as {@link layer} does:\n * the `Jj` slot probes `jj --version` while the layer is built.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerAt = (root: string): Layer.Layer<BunHost | Crypto.Crypto, JjError> => {\n const repositoryRoot = absoluteRoot(\"layerAt\", root)\n return Layer.mergeAll(\n ProcessConfinement.layer(),\n platform,\n Layer.provide(BunChildProcessSpawner.layer, platform),\n BunJj.layerAt(repositoryRoot),\n layerHttpClient\n )\n}\n\n/**\n * Provides the Bun host with process containment turned on.\n *\n * Bun uses the same prepared native process lifecycle as Node on POSIX, while\n * preserving its underlying runtime spawner on Windows. `ContainedSpawner` gives\n * every child an escalation deadline and a ledger entry, and\n * `ProcessReaper` sweeps the entries a crashed incarnation of this host left\n * behind. The reaper module lives in `@smthrs/platform-node` because the calls\n * it makes, `process.kill` and `taskkill`, are Node's, and Bun implements\n * them unchanged.\n *\n * Fails at construction with {@link JjError} exactly as {@link layer} does;\n * here the `jj --version` probe runs through the contained spawner, so its\n * record enters and leaves the ledger like any other child.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerContained = (\n options?: ContainedOptions\n): Layer.Layer<BunHost | Crypto.Crypto, JjError, ProcessLedger.ProcessLedger> => {\n const spawner = Layer.provide(\n ProcessReaper.layerSpawner({ graceMs: options?.graceMs }),\n Layer.provide(BunChildProcessSpawner.layer, platform)\n )\n return Layer.mergeAll(\n ProcessConfinement.layer(),\n platform,\n layerHttpClient,\n // jj goes through the CONTAINED spawner here, not around it, exactly as in\n // `NodeHost.layerContained`: a jj child that starts its own process leads\n // no recorded group, is in no ledger, and outlives the host that ran it.\n Layer.provideMerge(BunJj.layerSpawner, spawner)\n ).pipe(Layer.provideMerge(ProcessReaper.layer(reaping(options))))\n}\n\n/**\n * Provides the contained Bun host bound to one absolute repository root.\n *\n * Refuses a root exactly as {@link layerAt} does: {@link BunHostError} with\n * code `invalid_repository_root`, thrown when the factory is called.\n * Fails at construction with {@link JjError} exactly as {@link layerContained}\n * does.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerContainedAt = (\n root: string,\n options?: ContainedOptions\n): Layer.Layer<BunHost | Crypto.Crypto, JjError, ProcessLedger.ProcessLedger> => {\n const repositoryRoot = absoluteRoot(\"layerContainedAt\", root)\n const spawner = Layer.provide(\n ProcessReaper.layerSpawner({ graceMs: options?.graceMs }),\n Layer.provide(BunChildProcessSpawner.layer, platform)\n )\n return Layer.mergeAll(\n ProcessConfinement.layer(),\n platform,\n layerHttpClient,\n Layer.provideMerge(BunJj.layerSpawnerAt(repositoryRoot), spawner)\n ).pipe(Layer.provideMerge(ProcessReaper.layer(reaping(options))))\n}\n"]}
|
|
1
|
+
{"version":3,"file":"BunHost.js","sourceRoot":"","sources":["../../src/BunHost.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Aggregate Bun Host bundle.\n *\n * Runtime-specific dependencies stay inside this package; callers get the same\n * closed five-service Host surface every other bundle provides, plus native\n * `ProcessConfinement` for guarded commands.\n *\n * There is no Bun shell module: running a command is Effect's\n * `ChildProcessSpawner`, and `@effect/platform-bun`'s implementation is\n * `@effect/platform-node-shared`'s re-exported: the same code on both\n * runtimes, so no runtime detection is needed here.\n *\n * Outgoing HTTP uses the shared EgressHttpClient on both runtimes. Web\n * requests pin approved addresses; ordinary requests retain the shared pool.\n * Redirects remain visible to the capability kernel, and proxy routing follows\n * the host environment.\n *\n * The filesystem slot is `@smthrs/platform-node`'s `AtomicFileSystem`, byte for\n * byte the layer `NodeHost` uses, so a guarded path operation is\n * descriptor-relative and no-follow on both runtimes.\n *\n * @since 1.0.0-rc.0\n */\n\nimport * as BunChildProcessSpawner from \"@effect/platform-bun/BunChildProcessSpawner\"\nimport * as BunCrypto from \"@effect/platform-bun/BunCrypto\"\nimport * as BunPath from \"@effect/platform-bun/BunPath\"\nimport type { Jj } from \"@smthrs/jj\"\nimport * as BunJj from \"@smthrs/jj/bun/BunJj\"\nimport type { HostServiceIds } from \"@smthrs/kernel/HostServices\"\nimport type * as KernelProcessConfinement from \"@smthrs/kernel/ProcessConfinement\"\nimport type * as ProcessLedger from \"@smthrs/kernel/ProcessLedger\"\nimport * as AtomicFileSystem from \"@smthrs/platform-node/AtomicFileSystem\"\nimport * as EgressHttpClient from \"@smthrs/platform-node/EgressHttpClient\"\nimport * as HostLiveness from \"@smthrs/platform-node/HostLiveness\"\nimport * as ProcessConfinement from \"@smthrs/platform-node/ProcessConfinement\"\nimport * as ProcessReaper from \"@smthrs/platform-node/ProcessReaper\"\nimport type * as Crypto from \"effect/Crypto\"\nimport type { FileSystem } from \"effect/FileSystem\"\nimport type { HttpClient } from \"effect/http/HttpClient\"\nimport * as Layer from \"effect/Layer\"\nimport type * as Path from \"effect/Path\"\nimport type { ChildProcessSpawner } from \"effect/process/ChildProcessSpawner\"\nimport * as Schema from \"effect/Schema\"\nimport { isAbsolute } from \"node:path\"\nimport * as BunFileSystem from \"./BunFileSystem.ts\"\n\n/**\n * Bun platform modules for selectively providing individual services.\n *\n * `AtomicFileSystem` is here for the same reason `NodeHost` re-exports it: it\n * owns the only configuration escape hatch the filesystem slot has, and a Bun\n * program with a different packaged helper path needs to reach\n * `AtomicFileSystem.layerWith` without hand-composing the other four tags.\n *\n * `BunJj` is deliberately absent: it belongs to `@smthrs/jj` and is imported\n * from there, never re-exported here. So is `@effect/platform-bun/BunHttpClient`:\n * its `layer` follows redirects to origins the capability kernel never\n * authorized. The network slot on its own is {@link layerHttpClient}.\n *\n * @category re-exports\n * @since 1.0.0-rc.0\n * @slop\n */\nexport { AtomicFileSystem, BunChildProcessSpawner, BunCrypto, BunFileSystem, HostLiveness, ProcessReaper }\n\n/**\n * The complete closed Host service union provided by Bun.\n *\n * @category models\n * @since 1.0.0-rc.0\n * @slop\n */\nexport type BunHost =\n | FileSystem\n | Path.Path\n | ChildProcessSpawner\n | Jj\n | HttpClient\n | KernelProcessConfinement.ProcessConfinement\n\n/**\n * The stable codes a `BunHost` factory refuses with.\n *\n * A code is the contract a caller branches on; the message beside it is for a\n * person. `invalid_repository_root` is the root handed to {@link layerAt} or\n * {@link layerContainedAt} not being absolute or containing NUL, the empty\n * string included.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type BunHostErrorCode = \"invalid_repository_root\"\n\n/**\n * The refusal a `BunHost` factory throws before it builds a layer.\n *\n * It is thrown rather than failed because a factory is called while a program\n * composes its layers, where there is no fiber to fail yet and a wrong root is\n * a composition mistake, not a runtime outcome. It is this package's class,\n * not the `Jj` adapter's, because a Bun caller composes `BunHost` and should\n * learn nothing about the adapter behind the `Jj` slot from an error message.\n *\n * @category errors\n * @since 1.0.0-rc.0\n */\nexport class BunHostError extends Schema.TaggedError<BunHostError>()(\"@smthrs/platform-bun/BunHostError\", {\n code: Schema.Literal(\"invalid_repository_root\"),\n message: Schema.String\n}) {\n override readonly name = \"BunHostError\"\n}\n\n/** The longest message a factory refuses with, in UTF-16 code units. */\nconst messageLimit = 255\n\n/** How many code points of a refused root the message repeats at most. */\nconst previewLimit = 64\n\n/**\n * A bounded, quoted excerpt of a refused root that fits in `budget` code\n * units of the message.\n *\n * Counted in code points rather than UTF-16 units so a cut never splits a\n * surrogate pair, and passed through `JSON.stringify` so a newline in the root\n * cannot break the log line the message lands on. Each code point is encoded\n * on its own and admitted only while the quoted excerpt plus the length\n * suffix still fit, so a control character that widens to a `\\u0001` escape\n * shortens the excerpt instead of lengthening the message, and a cut never\n * lands inside an escape.\n */\nconst preview = (root: string, budget: number): string => {\n const points = Array.from(root)\n const whole = JSON.stringify(root)\n if (points.length <= previewLimit && whole.length <= budget) return whole\n const suffix = `... (${points.length} characters)`\n const room = budget - suffix.length - 2\n let excerpt = \"\"\n for (const point of points.slice(0, previewLimit)) {\n const encoded = JSON.stringify(point).slice(1, -1)\n if (excerpt.length + encoded.length > room) break\n excerpt += encoded\n }\n return `\"${excerpt}\"${suffix}`\n}\n\n/**\n * Refuses a root the `Jj` adapter underneath would refuse, here and in this\n * package's name.\n *\n * `BunJj.layerAt` and `BunJj.layerSpawnerAt` are `NodeJj`'s, and those throw a\n * bare `TypeError` that names `NodeJj`, carries no code, and echoes the whole\n * string. The predicate includes the same `isAbsolute` the adapter applies, so\n * a root that passes this check cannot reach that throw. An absolute root\n * containing NUL is refused too: no filesystem or process API can represent it.\n */\nconst absoluteRoot = (factory: \"layerAt\" | \"layerContainedAt\", root: string): string => {\n // An absolute root with NUL names that defect, not absoluteness it already has.\n const requirement = !isAbsolute(root)\n ? \"an absolute repository root\"\n : root.includes(\"\\0\")\n ? \"a repository root without NUL\"\n : undefined\n if (requirement === undefined) return root\n const prefix = `BunHost.${factory} requires ${requirement}, got `\n throw new BunHostError({\n code: \"invalid_repository_root\",\n message: `${prefix}${preview(root, messageLimit - prefix.length)}`\n })\n}\n\n/**\n * Stable implementation identities keyed by the closed Host service slots.\n *\n * Each value names the module behind that slot in the raw host bundle.\n * Contained POSIX factories replace the spawner with {@link ProcessReaper.layerSpawner}.\n * These are identity\n * tokens rather than import specifiers: the filesystem entry is\n * `@smthrs/platform-node/AtomicFileSystem` because that is the implementation,\n * even though a consumer reaches it through `@smthrs/platform-bun`.\n *\n * Nothing digests them yet. `@smthrs/plan`'s step key carries a `layers`\n * component these values are meant to feed, but no planner derives it from a\n * host bundle today, so changing one invalidates no cached step.\n *\n * The keys are written as literals rather than `HostServiceIds` positions so\n * that reordering the closed list cannot silently pair a slot with another\n * slot's implementation.\n *\n * @category models\n * @since 1.0.0-rc.0\n * @slop\n */\nexport const implementationIds: Readonly<Record<(typeof HostServiceIds)[number], string>> = {\n \"effect/FileSystem\": \"@smthrs/platform-node/AtomicFileSystem\",\n \"effect/Path\": \"@effect/platform-bun/BunPath\",\n \"effect/process/ChildProcessSpawner\": \"@effect/platform-bun/BunChildProcessSpawner\",\n \"@smthrs/jj/Jj\": \"@smthrs/jj/bun/BunJj\",\n \"effect/HttpClient\": \"@smthrs/platform-node/EgressHttpClient\"\n}\n\n/**\n * What a caller may configure about containment.\n *\n * `ContainedSpawner.Options.platform` is deliberately not part of it. It\n * decides one thing, whether a command that names no `detached` option gets a\n * process group of its own, and this native host detaches by `process.platform`\n * whatever a record claims. A\n * caller-supplied `\"win32\"` on a POSIX host would therefore record\n * `pgid: null` for a child that really does lead a group, and\n * {@link ProcessReaper.reap} would retire that record as `no-group` and leave\n * the orphan running forever: a durable lie rather than a compile error.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type ContainedOptions = ProcessReaper.SpawnerOptions & ProcessReaper.Options\n\n/** The reaper half of {@link ContainedOptions}. */\nconst reaping = (options?: ContainedOptions): ProcessReaper.Options => ({\n ownerPid: options?.ownerPid,\n system: options?.system\n})\n\n/** The two services `BunChildProcessSpawner` resolves paths and files with. */\nconst platform = Layer.mergeAll(BunFileSystem.layer, BunPath.layer, BunCrypto.layer)\n\n/**\n * The network slot on its own: the shared egress client, which never follows a\n * redirect. A `3xx` comes back to the caller with its `location` intact and the\n * second origin uncontacted; following a redirect is `@smthrs/kernel`'s guarded\n * `HttpClient.layer`, which rechecks every hop.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerHttpClient: Layer.Layer<HttpClient> = EgressHttpClient.layer(process.env)\n\n/**\n * Provides all five Bun Host services, including the runtime-independent Path\n * service.\n *\n * Builds on a host without jj. The `Jj` slot probes `jj --version` on its\n * first operation and fails that operation with `JjError` carrying\n * `not_installed` or `unsupported_version`; a program that never calls `Jj`\n * runs without jj. `Jj` requires jj 0.39.0 or later.\n *\n * @category layers\n * @since 1.0.0-rc.0\n * @slop\n */\nexport const layer: Layer.Layer<BunHost | Crypto.Crypto> = Layer.mergeAll(\n ProcessConfinement.layer(),\n platform,\n Layer.provide(BunChildProcessSpawner.layer, platform),\n BunJj.layer,\n layerHttpClient\n)\n\n/**\n * Provides all five Bun Host services bound to one absolute repository root.\n *\n * Throws {@link BunHostError} with code `invalid_repository_root` when `root`\n * is not absolute or contains NUL; the empty string counts. The check runs\n * when the factory is called, before any layer exists.\n *\n * Builds without jj exactly as {@link layer} does: the `Jj` slot probes\n * `jj --version` on its first operation.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerAt = (root: string): Layer.Layer<BunHost | Crypto.Crypto> => {\n const repositoryRoot = absoluteRoot(\"layerAt\", root)\n return Layer.mergeAll(\n ProcessConfinement.layer(),\n platform,\n Layer.provide(BunChildProcessSpawner.layer, platform),\n BunJj.layerAt(repositoryRoot),\n layerHttpClient\n )\n}\n\n/**\n * Provides the Bun host with process containment turned on.\n *\n * Bun uses the same prepared native process lifecycle as Node on POSIX, while\n * preserving its underlying runtime spawner on Windows. `ContainedSpawner` gives\n * every child an escalation deadline and a ledger entry, and\n * `ProcessReaper` sweeps the entries a crashed incarnation of this host left\n * behind. The reaper module lives in `@smthrs/platform-node` because the calls\n * it makes, `process.kill` and `taskkill`, are Node's, and Bun implements\n * them unchanged.\n *\n * Builds without jj exactly as {@link layer} does; here the first\n * operation's `jj --version` probe runs through the contained spawner, so its\n * record enters and leaves the ledger like any other child.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerContained = (\n options?: ContainedOptions\n): Layer.Layer<BunHost | Crypto.Crypto, never, ProcessLedger.ProcessLedger> => {\n const spawner = Layer.provide(\n ProcessReaper.layerSpawner({ graceMs: options?.graceMs }),\n Layer.provide(BunChildProcessSpawner.layer, platform)\n )\n return Layer.mergeAll(\n ProcessConfinement.layer(),\n platform,\n layerHttpClient,\n // jj goes through the CONTAINED spawner here, not around it, exactly as in\n // `NodeHost.layerContained`: a jj child that starts its own process leads\n // no recorded group, is in no ledger, and outlives the host that ran it.\n Layer.provideMerge(BunJj.layerSpawner, spawner)\n ).pipe(Layer.provideMerge(ProcessReaper.layer(reaping(options))))\n}\n\n/**\n * Provides the contained Bun host bound to one absolute repository root.\n *\n * Refuses a root exactly as {@link layerAt} does: {@link BunHostError} with\n * code `invalid_repository_root`, thrown when the factory is called.\n * Builds without jj exactly as {@link layerContained} does.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerContainedAt = (\n root: string,\n options?: ContainedOptions\n): Layer.Layer<BunHost | Crypto.Crypto, never, ProcessLedger.ProcessLedger> => {\n const repositoryRoot = absoluteRoot(\"layerContainedAt\", root)\n const spawner = Layer.provide(\n ProcessReaper.layerSpawner({ graceMs: options?.graceMs }),\n Layer.provide(BunChildProcessSpawner.layer, platform)\n )\n return Layer.mergeAll(\n ProcessConfinement.layer(),\n platform,\n layerHttpClient,\n Layer.provideMerge(BunJj.layerSpawnerAt(repositoryRoot), spawner)\n ).pipe(Layer.provideMerge(ProcessReaper.layer(reaping(options))))\n}\n"]}
|
package/docs/README.md
CHANGED
|
@@ -38,15 +38,15 @@ is Effect's Node spawner re-exported; contained POSIX variants use
|
|
|
38
38
|
## Install
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
|
-
bun add @smthrs/platform-bun@1.0.0-rc.
|
|
41
|
+
bun add @smthrs/platform-bun@1.0.0-rc.9 effect
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
`effect` is a required peer in the range `^4.0.4`. `@effect/platform-bun` is
|
|
45
45
|
bundled privately, built from `effect@4.0.4`. The filesystem slot runs the
|
|
46
46
|
`smithers-jj-export` helper that the required peer `@smthrs/platform-node`
|
|
47
|
-
ships
|
|
48
|
-
|
|
49
|
-
and the import forms.
|
|
47
|
+
ships. The `Jj` slot needs jj 0.39.0 or later, checked on its first operation;
|
|
48
|
+
every bundle builds without jj. [Installation](./installation.md) covers all
|
|
49
|
+
three and the import forms.
|
|
50
50
|
|
|
51
51
|
## Run a command and a file operation through the host
|
|
52
52
|
|
package/docs/api.md
CHANGED
|
@@ -27,13 +27,13 @@ a browser; a page composes
|
|
|
27
27
|
[`@smthrs/platform-browser`](/api/platform-browser) instead.
|
|
28
28
|
:::
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
executable
|
|
32
|
-
`
|
|
33
|
-
bundles route that probe
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
Every host bundle builds without jj. The `Jj` slot requires jj 0.39.0 or newer
|
|
31
|
+
and checks its executable on its first operation, which fails with `JjError`
|
|
32
|
+
`not_installed` or `unsupported_version` when jj is missing or older. The
|
|
33
|
+
contained bundles route that probe through their process spawner and retire its
|
|
34
|
+
ledger entry when it exits. Probe results are cached per resolved absolute
|
|
35
|
+
executable path and runner; contained spawners share a result only within the
|
|
36
|
+
same spawner instance.
|
|
37
37
|
|
|
38
38
|
## Entry points
|
|
39
39
|
|
|
@@ -52,24 +52,24 @@ Supported runtimes are Bun >=1.4.0 and Node.js >=26.4.0.
|
|
|
52
52
|
|
|
53
53
|
## BunHost
|
|
54
54
|
|
|
55
|
-
The complete closed Host bundle: layers,
|
|
55
|
+
The complete closed Host bundle: layers, root errors, models,
|
|
56
56
|
implementation identities, and platform module re-exports.
|
|
57
57
|
|
|
58
58
|
### Layers
|
|
59
59
|
|
|
60
|
-
| Export | Signature
|
|
61
|
-
| ------------------ |
|
|
62
|
-
| `layer` | `Layer.Layer<BunHost \| Crypto.Crypto
|
|
63
|
-
| `layerAt` | `(root: string) => Layer.Layer<BunHost \| Crypto.Crypto
|
|
64
|
-
| `layerContained` | `(options?: ContainedOptions) => Layer.Layer<BunHost \| Crypto.Crypto,
|
|
65
|
-
| `layerContainedAt` | `(root: string, options?: ContainedOptions) => Layer.Layer<BunHost \| Crypto.Crypto,
|
|
66
|
-
| `layerHttpClient` | `Layer.Layer<HttpClient>`
|
|
60
|
+
| Export | Signature |
|
|
61
|
+
| ------------------ | ----------------------------------------------------------------------------------------------------------- |
|
|
62
|
+
| `layer` | `Layer.Layer<BunHost \| Crypto.Crypto>` |
|
|
63
|
+
| `layerAt` | `(root: string) => Layer.Layer<BunHost \| Crypto.Crypto>` |
|
|
64
|
+
| `layerContained` | `(options?: ContainedOptions) => Layer.Layer<BunHost \| Crypto.Crypto, never, ProcessLedger>` |
|
|
65
|
+
| `layerContainedAt` | `(root: string, options?: ContainedOptions) => Layer.Layer<BunHost \| Crypto.Crypto, never, ProcessLedger>` |
|
|
66
|
+
| `layerHttpClient` | `Layer.Layer<HttpClient>` |
|
|
67
67
|
|
|
68
68
|
`layer` provides all five Host services, including the runtime-independent
|
|
69
69
|
`Path`. `Jj` is bound to the process working directory.
|
|
70
70
|
|
|
71
71
|
`layerAt` is `layer` with `Jj` bound to `root` instead. `root` must be an
|
|
72
|
-
absolute path; see [BunHostError](#bunhosterror).
|
|
72
|
+
absolute path without NUL; see [BunHostError](#bunhosterror).
|
|
73
73
|
|
|
74
74
|
`layerContained` is `layer` with process containment turned on: every child is
|
|
75
75
|
spawned through `@smthrs/kernel`'s `ContainedSpawner` with
|
|
@@ -117,10 +117,10 @@ type BunHostErrorCode = "invalid_repository_root"
|
|
|
117
117
|
```
|
|
118
118
|
|
|
119
119
|
The refusal `layerAt` and `layerContainedAt` throw when `root` is not absolute,
|
|
120
|
-
the empty string included. It is thrown rather than failed
|
|
121
|
-
runs while a program composes its layers, where there is no
|
|
122
|
-
and a wrong root is a composition mistake, not a runtime
|
|
123
|
-
runs when the factory is called, before any layer exists.
|
|
120
|
+
the empty string included, or contains NUL. It is thrown rather than failed
|
|
121
|
+
because a factory runs while a program composes its layers, where there is no
|
|
122
|
+
fiber to fail into and a wrong root is a composition mistake, not a runtime
|
|
123
|
+
outcome. The check runs when the factory is called, before any layer exists.
|
|
124
124
|
|
|
125
125
|
It is this package's class rather than the `Jj` adapter's: a Bun caller
|
|
126
126
|
composed `BunHost` and should learn nothing about the adapter behind the `Jj`
|
|
@@ -42,8 +42,8 @@ BunHost.layerContainedAt("/srv/repositories/app", { graceMs: 2_000 })
|
|
|
42
42
|
|
|
43
43
|
## Pass an absolute root
|
|
44
44
|
|
|
45
|
-
Both factories require an absolute path. A relative root,
|
|
46
|
-
are refused:
|
|
45
|
+
Both factories require an absolute path without NUL. A relative root, the
|
|
46
|
+
empty string, and an absolute root containing NUL are refused:
|
|
47
47
|
|
|
48
48
|
```ts
|
|
49
49
|
BunHost.layerAt("repositories/app")
|
|
@@ -130,15 +130,16 @@ const status = Effect.flatMap(Jj, (jj) => jj.status()).pipe(
|
|
|
130
130
|
```
|
|
131
131
|
|
|
132
132
|
That is the observable difference: the same call under `BunHost.layer` leaves
|
|
133
|
-
the ledger empty. The
|
|
134
|
-
too, so a contained host records and retires it
|
|
135
|
-
|
|
136
|
-
separate cache for each
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
133
|
+
the ledger empty. The `jj --version` probe that the first `Jj` operation runs
|
|
134
|
+
uses the selected runner too, so a contained host records and retires it like
|
|
135
|
+
any other child. Building the host spawns nothing. Probe results are cached per
|
|
136
|
+
resolved absolute executable path and runner, with a separate cache for each
|
|
137
|
+
spawner instance.
|
|
138
|
+
|
|
139
|
+
The first `Jj` operation on every aggregate host waits up to 5000 ms for the
|
|
140
|
+
version probe, then interrupts it and awaits process cleanup. A probe that misses this budget, or that the
|
|
140
141
|
spawner was too busy to start in time, is retried once. A second timeout fails
|
|
141
|
-
with a typed `JjError` that has `method: "version"`, `code: "unknown"`,
|
|
142
|
+
the operation with a typed `JjError` that has `method: "version"`, `code: "unknown"`,
|
|
142
143
|
`cause.code: "ETIMEDOUT"`, and the executable path in its command and message.
|
|
143
144
|
Cleanup can add the contained spawner's shutdown grace period to each attempt. Raw hosts close the probe's pipes and kill its
|
|
144
145
|
direct child; they do not add a ledger entry.
|
|
@@ -154,7 +155,7 @@ const boundedHost = host.pipe(
|
|
|
154
155
|
```
|
|
155
156
|
|
|
156
157
|
The budget is a positive, finite number of milliseconds, at most 2147483647.
|
|
157
|
-
Timeout and interruption allow
|
|
158
|
+
Timeout and interruption allow the next operation to retry the probe.
|
|
158
159
|
|
|
159
160
|
## What the reaper refuses to kill
|
|
160
161
|
|
package/docs/installation.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Installation"
|
|
3
|
-
description: "Install @smthrs/platform-bun from npm with its required peers, the smithers-jj-export helper @smthrs/platform-node ships for the filesystem slot, the jj executable
|
|
3
|
+
description: "Install @smthrs/platform-bun from npm with its required peers, the smithers-jj-export helper @smthrs/platform-node ships for the filesystem slot, the jj executable the Jj slot probes on first use, and the import forms for each entry point."
|
|
4
4
|
sidebar:
|
|
5
5
|
order: 1
|
|
6
6
|
---
|
|
@@ -11,9 +11,13 @@ The package is on npm under the `next` tag, which carries the 1.0 release
|
|
|
11
11
|
candidates. Install the current candidate with Bun:
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
bun add @smthrs/platform-bun@1.0.0-rc.
|
|
14
|
+
bun add @smthrs/platform-bun@1.0.0-rc.9 effect @smthrs/kernel@1.0.0-rc.9
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
`@smthrs/kernel` is on the line because the [Quickstart](./quickstart.md)'s
|
|
18
|
+
`contained.ts` imports its `ProcessLedger`, which the contained bundles require.
|
|
19
|
+
Bun's isolated linker and pnpm link only the packages a project declares.
|
|
20
|
+
|
|
17
21
|
The manifest declares two required peers, neither optional:
|
|
18
22
|
|
|
19
23
|
| Peer | Version | Why |
|
|
@@ -51,15 +55,15 @@ packaged helper with another absolute path; see
|
|
|
51
55
|
|
|
52
56
|
Windows is unsupported for this slot.
|
|
53
57
|
|
|
54
|
-
## Install Jujutsu for the
|
|
58
|
+
## Install Jujutsu for the Jj slot
|
|
55
59
|
|
|
56
|
-
Every
|
|
57
|
-
`layerContainedAt`,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
the
|
|
60
|
+
Every bundle, `BunHost.layer`, `layerAt`, `layerContained`, and
|
|
61
|
+
`layerContainedAt`, builds without jj, so a program that never calls `Jj` runs
|
|
62
|
+
on a host without it. The `Jj` slot requires jj 0.39.0 or later: its first
|
|
63
|
+
operation runs one `jj --version` probe, and on a host without the executable,
|
|
64
|
+
or with an older one, that operation fails with `JjError` carrying
|
|
65
|
+
`not_installed` or `unsupported_version`. The slot selects the executable when
|
|
66
|
+
the layer is built, so restart the program after installing jj.
|
|
63
67
|
|
|
64
68
|
Install [Jujutsu](https://jj-vcs.github.io), a version-control system that
|
|
65
69
|
works on a Git repository, and confirm the version:
|
|
@@ -72,9 +76,9 @@ This package vendors no binaries. [`@smthrs/jj`](/api/jj) documents the
|
|
|
72
76
|
resolution order and the `SMITHERS_JJ_PATH` override for an executable that is
|
|
73
77
|
not on `PATH`.
|
|
74
78
|
|
|
75
|
-
A program that
|
|
76
|
-
instead of a complete bundle: `BunFileSystem.layer`, `Path.layer`, and
|
|
77
|
-
spawner and HTTP client layers
|
|
79
|
+
A program that wants only some of the services composes the individual service
|
|
80
|
+
layers instead of a complete bundle: `BunFileSystem.layer`, `Path.layer`, and
|
|
81
|
+
the spawner and HTTP client layers. See
|
|
78
82
|
[The Host surface on Bun](./concepts/host-surface.md) for taking one service
|
|
79
83
|
without the other four.
|
|
80
84
|
|
|
@@ -117,9 +121,9 @@ page composes [`@smthrs/platform-browser`](/api/platform-browser) instead.
|
|
|
117
121
|
[Contain and reap child processes](./guides/contain-child-processes.md).
|
|
118
122
|
|
|
119
123
|
The `Jj` slot spawns the `jj` command that
|
|
120
|
-
[Install Jujutsu for the
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
[Install Jujutsu for the Jj slot](#install-jujutsu-for-the-jj-slot) covers. The
|
|
125
|
+
slot probes it on its first operation, so jj is a requirement of the programs
|
|
126
|
+
that call `Jj`, not of every host.
|
|
123
127
|
|
|
124
128
|
## Next step
|
|
125
129
|
|
package/docs/quickstart.md
CHANGED
|
@@ -13,16 +13,18 @@ appear in, and disappear from, the process ledger.
|
|
|
13
13
|
## Prerequisites
|
|
14
14
|
|
|
15
15
|
- Bun 1.4.0 or later, or Node.js 26.4.0 or later.
|
|
16
|
-
- A project with the
|
|
17
|
-
`bun add @smthrs/platform-bun@1.0.0-rc.
|
|
18
|
-
required peer `@smthrs/platform-node`, which ships the
|
|
19
|
-
helper the filesystem slot runs.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
- A project with the packages installed:
|
|
17
|
+
`bun add @smthrs/platform-bun@1.0.0-rc.9 effect @smthrs/kernel@1.0.0-rc.9`.
|
|
18
|
+
That also installs the required peer `@smthrs/platform-node`, which ships the
|
|
19
|
+
`smithers-jj-export` helper the filesystem slot runs. `@smthrs/kernel` is on
|
|
20
|
+
the line because `contained.ts` imports its `ProcessLedger`, and Bun's
|
|
21
|
+
isolated linker and pnpm link only the packages a project declares. See
|
|
22
|
+
[Installation](./installation.md).
|
|
23
|
+
- [Jujutsu](https://jj-vcs.github.io) at jj 0.39.0 or later on `PATH`, only for
|
|
24
|
+
a program that calls `Jj`. Neither program below does, and every bundle
|
|
25
|
+
builds without jj; a `Jj` operation on a host without it fails with
|
|
26
|
+
`JjError` `not_installed`. See [Installation](./installation.md) for
|
|
27
|
+
`SMITHERS_JJ_PATH`.
|
|
26
28
|
|
|
27
29
|
## Run a file operation and a command
|
|
28
30
|
|
|
@@ -64,8 +66,9 @@ bun run quickstart.ts
|
|
|
64
66
|
```
|
|
65
67
|
|
|
66
68
|
`BunHost.layer` provided all five slots; the program used two of them. The
|
|
67
|
-
other three were built and never asked for
|
|
68
|
-
jj version probe
|
|
69
|
+
other three were built and never asked for. Building `Jj` spawned nothing: its
|
|
70
|
+
jj version probe runs on the first `Jj` operation, which this program never
|
|
71
|
+
makes.
|
|
69
72
|
|
|
70
73
|
## Turn containment on
|
|
71
74
|
|
package/docs/troubleshooting.md
CHANGED
|
@@ -1,33 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Troubleshooting"
|
|
3
|
-
description: "The failures @smthrs/platform-bun produces: a
|
|
3
|
+
description: "The failures @smthrs/platform-bun produces: a refused repository root, a filesystem guard that fails closed, an unfollowed redirect, a missing process ledger, and a browser bundle that will not resolve."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Find the symptom, read the cause, apply the fix. Every failure here is one this
|
|
7
7
|
package or its slots actually produce; the signatures behind them are on the
|
|
8
8
|
[API reference](./api.md).
|
|
9
9
|
|
|
10
|
-
## ERR_MODULE_NOT_FOUND for @effect/platform-bun/BunChildProcessSpawner
|
|
11
|
-
|
|
12
|
-
**Symptom.** The first `import { BunHost } from "@smthrs/platform-bun"`, or
|
|
13
|
-
`import * as BunHost from "@smthrs/platform-bun/BunHost"`, throws
|
|
14
|
-
`ERR_MODULE_NOT_FOUND` before any of your code runs.
|
|
15
|
-
|
|
16
|
-
**Cause.** The install is missing the required `@effect/platform-bun` peer,
|
|
17
|
-
which both the root entry point and `BunHost` import at module load. This can
|
|
18
|
-
happen with an older package version that marked it optional, or when peer
|
|
19
|
-
installation was disabled.
|
|
20
|
-
|
|
21
|
-
**Fix.** Install it yourself, at the version your composition's `effect` is
|
|
22
|
-
built against:
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
pnpm add @effect/platform-bun @effect/platform-node-shared
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
`@smthrs/platform-bun/BunFileSystem` is the one entry point that resolves
|
|
29
|
-
without it, because it imports nothing from `@effect/platform-bun`.
|
|
30
|
-
|
|
31
10
|
## BunHostError: requires an absolute repository root
|
|
32
11
|
|
|
33
12
|
**Symptom.** `BunHost.layerAt` or `BunHost.layerContainedAt` throws while your
|
|
@@ -35,10 +14,12 @@ program is still composing its layers:
|
|
|
35
14
|
|
|
36
15
|
```text
|
|
37
16
|
BunHostError: BunHost.layerAt requires an absolute repository root, got "repositories/app"
|
|
17
|
+
BunHostError: BunHost.layerAt requires a repository root without NUL, got "/repo\u0000"
|
|
38
18
|
```
|
|
39
19
|
|
|
40
|
-
**Cause.** The root is not absolute
|
|
41
|
-
|
|
20
|
+
**Cause.** The root is not absolute, or it contains NUL, which no filesystem
|
|
21
|
+
or process API can represent. The empty string is not absolute, and neither is
|
|
22
|
+
a path that looks absolute only after the process working directory is applied.
|
|
42
23
|
|
|
43
24
|
**Fix.** Resolve the root before you hand it over, and branch on
|
|
44
25
|
`error.code === "invalid_repository_root"` rather than on the message text. The
|
|
@@ -115,7 +96,7 @@ your code.
|
|
|
115
96
|
`@smthrs/kernel/ProcessLedger`.
|
|
116
97
|
|
|
117
98
|
**Cause.** Both contained factories return
|
|
118
|
-
`Layer.Layer<BunHost | Crypto.Crypto,
|
|
99
|
+
`Layer.Layer<BunHost | Crypto.Crypto, never, ProcessLedger.ProcessLedger>`.
|
|
119
100
|
The ledger is a
|
|
120
101
|
requirement rather than a built-in default, because the durable half is only as
|
|
121
102
|
good as the journal underneath it, and only your program knows whether it has
|
|
@@ -167,15 +148,16 @@ between them does not change. See
|
|
|
167
148
|
|
|
168
149
|
## Jj fails with not_installed
|
|
169
150
|
|
|
170
|
-
**Symptom.**
|
|
171
|
-
|
|
172
|
-
|
|
151
|
+
**Symptom.** A `Jj` operation fails with a `JjError` whose code is
|
|
152
|
+
`not_installed` or `unsupported_version` and whose method is `version`. The
|
|
153
|
+
host built and every other service works.
|
|
173
154
|
|
|
174
|
-
**Cause.** No usable `jj` executable, or one older than 0.39.0.
|
|
175
|
-
`
|
|
176
|
-
|
|
177
|
-
|
|
155
|
+
**Cause.** No usable `jj` executable, or one older than 0.39.0. The `Jj` slot
|
|
156
|
+
probes `jj --version` on its first operation, and the operation that triggered
|
|
157
|
+
the probe did not run. This package vendors no binaries and installs nothing on
|
|
158
|
+
your behalf.
|
|
178
159
|
|
|
179
160
|
**Fix.** Install [Jujutsu](https://jj-vcs.github.io), which provides the `jj`
|
|
180
|
-
command, or set `SMITHERS_JJ_PATH` to the executable you want spawned
|
|
161
|
+
command, or set `SMITHERS_JJ_PATH` to the executable you want spawned, then
|
|
162
|
+
restart the program: the slot selects the executable when the layer is built.
|
|
181
163
|
[`@smthrs/jj`](/api/jj) documents the resolution order and the failure codes.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smthrs/platform-bun",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.0-rc.
|
|
4
|
+
"version": "1.0.0-rc.9",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"smthrs": {
|
|
7
7
|
"group": "engine",
|
|
@@ -102,15 +102,15 @@
|
|
|
102
102
|
"tag": "next"
|
|
103
103
|
},
|
|
104
104
|
"dependencies": {
|
|
105
|
-
"@smthrs/jj": "1.0.0-rc.
|
|
106
|
-
"@smthrs/kernel": "1.0.0-rc.
|
|
105
|
+
"@smthrs/jj": "1.0.0-rc.9",
|
|
106
|
+
"@smthrs/kernel": "1.0.0-rc.9",
|
|
107
107
|
"undici": "8.11.2",
|
|
108
108
|
"redis": "6.2.1",
|
|
109
109
|
"@types/ws": "8.18.2",
|
|
110
110
|
"ws": "8.22.0"
|
|
111
111
|
},
|
|
112
112
|
"peerDependencies": {
|
|
113
|
-
"@smthrs/platform-node": "1.0.0-rc.
|
|
113
|
+
"@smthrs/platform-node": "1.0.0-rc.9",
|
|
114
114
|
"effect": "^4.0.4"
|
|
115
115
|
},
|
|
116
116
|
"devDependencies": {
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"@effect/platform-node-shared": "4.0.4",
|
|
121
121
|
"@effect/vitest": "4.0.4",
|
|
122
122
|
"@eslint/js": "9.39.5",
|
|
123
|
-
"@smthrs/platform-node": "1.0.0-rc.
|
|
123
|
+
"@smthrs/platform-node": "1.0.0-rc.9",
|
|
124
124
|
"@types/node": "26.4.1",
|
|
125
125
|
"@vitest/coverage-v8": "5.0.0",
|
|
126
126
|
"dprint": "0.57.1",
|
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
"typescript": "5.9.3",
|
|
136
136
|
"typescript-eslint": "8.69.0",
|
|
137
137
|
"vitest": "5.0.0",
|
|
138
|
-
"@smthrs/artifacts": "1.0.0-rc.
|
|
138
|
+
"@smthrs/artifacts": "1.0.0-rc.9"
|
|
139
139
|
},
|
|
140
140
|
"scripts": {
|
|
141
141
|
"lint": "eslint src --max-warnings=0 && dprint check",
|
package/src/BunHost.ts
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
import * as BunChildProcessSpawner from "@effect/platform-bun/BunChildProcessSpawner"
|
|
26
26
|
import * as BunCrypto from "@effect/platform-bun/BunCrypto"
|
|
27
27
|
import * as BunPath from "@effect/platform-bun/BunPath"
|
|
28
|
-
import type { Jj
|
|
28
|
+
import type { Jj } from "@smthrs/jj"
|
|
29
29
|
import * as BunJj from "@smthrs/jj/bun/BunJj"
|
|
30
30
|
import type { HostServiceIds } from "@smthrs/kernel/HostServices"
|
|
31
31
|
import type * as KernelProcessConfinement from "@smthrs/kernel/ProcessConfinement"
|
|
@@ -84,7 +84,8 @@ export type BunHost =
|
|
|
84
84
|
*
|
|
85
85
|
* A code is the contract a caller branches on; the message beside it is for a
|
|
86
86
|
* person. `invalid_repository_root` is the root handed to {@link layerAt} or
|
|
87
|
-
* {@link layerContainedAt} not being absolute, the empty
|
|
87
|
+
* {@link layerContainedAt} not being absolute or containing NUL, the empty
|
|
88
|
+
* string included.
|
|
88
89
|
*
|
|
89
90
|
* @category models
|
|
90
91
|
* @since 1.0.0-rc.0
|
|
@@ -149,12 +150,19 @@ const preview = (root: string, budget: number): string => {
|
|
|
149
150
|
*
|
|
150
151
|
* `BunJj.layerAt` and `BunJj.layerSpawnerAt` are `NodeJj`'s, and those throw a
|
|
151
152
|
* bare `TypeError` that names `NodeJj`, carries no code, and echoes the whole
|
|
152
|
-
* string. The predicate
|
|
153
|
-
* a root that passes this check cannot reach that throw.
|
|
153
|
+
* string. The predicate includes the same `isAbsolute` the adapter applies, so
|
|
154
|
+
* a root that passes this check cannot reach that throw. An absolute root
|
|
155
|
+
* containing NUL is refused too: no filesystem or process API can represent it.
|
|
154
156
|
*/
|
|
155
157
|
const absoluteRoot = (factory: "layerAt" | "layerContainedAt", root: string): string => {
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
+
// An absolute root with NUL names that defect, not absoluteness it already has.
|
|
159
|
+
const requirement = !isAbsolute(root)
|
|
160
|
+
? "an absolute repository root"
|
|
161
|
+
: root.includes("\0")
|
|
162
|
+
? "a repository root without NUL"
|
|
163
|
+
: undefined
|
|
164
|
+
if (requirement === undefined) return root
|
|
165
|
+
const prefix = `BunHost.${factory} requires ${requirement}, got `
|
|
158
166
|
throw new BunHostError({
|
|
159
167
|
code: "invalid_repository_root",
|
|
160
168
|
message: `${prefix}${preview(root, messageLimit - prefix.length)}`
|
|
@@ -232,16 +240,16 @@ export const layerHttpClient: Layer.Layer<HttpClient> = EgressHttpClient.layer(p
|
|
|
232
240
|
* Provides all five Bun Host services, including the runtime-independent Path
|
|
233
241
|
* service.
|
|
234
242
|
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
* `
|
|
238
|
-
*
|
|
243
|
+
* Builds on a host without jj. The `Jj` slot probes `jj --version` on its
|
|
244
|
+
* first operation and fails that operation with `JjError` carrying
|
|
245
|
+
* `not_installed` or `unsupported_version`; a program that never calls `Jj`
|
|
246
|
+
* runs without jj. `Jj` requires jj 0.39.0 or later.
|
|
239
247
|
*
|
|
240
248
|
* @category layers
|
|
241
249
|
* @since 1.0.0-rc.0
|
|
242
250
|
* @slop
|
|
243
251
|
*/
|
|
244
|
-
export const layer: Layer.Layer<BunHost | Crypto.Crypto
|
|
252
|
+
export const layer: Layer.Layer<BunHost | Crypto.Crypto> = Layer.mergeAll(
|
|
245
253
|
ProcessConfinement.layer(),
|
|
246
254
|
platform,
|
|
247
255
|
Layer.provide(BunChildProcessSpawner.layer, platform),
|
|
@@ -253,16 +261,16 @@ export const layer: Layer.Layer<BunHost | Crypto.Crypto, JjError> = Layer.mergeA
|
|
|
253
261
|
* Provides all five Bun Host services bound to one absolute repository root.
|
|
254
262
|
*
|
|
255
263
|
* Throws {@link BunHostError} with code `invalid_repository_root` when `root`
|
|
256
|
-
* is not absolute; the empty string counts. The check runs
|
|
257
|
-
* called, before any layer exists.
|
|
264
|
+
* is not absolute or contains NUL; the empty string counts. The check runs
|
|
265
|
+
* when the factory is called, before any layer exists.
|
|
258
266
|
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
267
|
+
* Builds without jj exactly as {@link layer} does: the `Jj` slot probes
|
|
268
|
+
* `jj --version` on its first operation.
|
|
261
269
|
*
|
|
262
270
|
* @category layers
|
|
263
271
|
* @since 1.0.0-rc.0
|
|
264
272
|
*/
|
|
265
|
-
export const layerAt = (root: string): Layer.Layer<BunHost | Crypto.Crypto
|
|
273
|
+
export const layerAt = (root: string): Layer.Layer<BunHost | Crypto.Crypto> => {
|
|
266
274
|
const repositoryRoot = absoluteRoot("layerAt", root)
|
|
267
275
|
return Layer.mergeAll(
|
|
268
276
|
ProcessConfinement.layer(),
|
|
@@ -284,8 +292,8 @@ export const layerAt = (root: string): Layer.Layer<BunHost | Crypto.Crypto, JjEr
|
|
|
284
292
|
* it makes, `process.kill` and `taskkill`, are Node's, and Bun implements
|
|
285
293
|
* them unchanged.
|
|
286
294
|
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
295
|
+
* Builds without jj exactly as {@link layer} does; here the first
|
|
296
|
+
* operation's `jj --version` probe runs through the contained spawner, so its
|
|
289
297
|
* record enters and leaves the ledger like any other child.
|
|
290
298
|
*
|
|
291
299
|
* @category layers
|
|
@@ -293,7 +301,7 @@ export const layerAt = (root: string): Layer.Layer<BunHost | Crypto.Crypto, JjEr
|
|
|
293
301
|
*/
|
|
294
302
|
export const layerContained = (
|
|
295
303
|
options?: ContainedOptions
|
|
296
|
-
): Layer.Layer<BunHost | Crypto.Crypto,
|
|
304
|
+
): Layer.Layer<BunHost | Crypto.Crypto, never, ProcessLedger.ProcessLedger> => {
|
|
297
305
|
const spawner = Layer.provide(
|
|
298
306
|
ProcessReaper.layerSpawner({ graceMs: options?.graceMs }),
|
|
299
307
|
Layer.provide(BunChildProcessSpawner.layer, platform)
|
|
@@ -314,8 +322,7 @@ export const layerContained = (
|
|
|
314
322
|
*
|
|
315
323
|
* Refuses a root exactly as {@link layerAt} does: {@link BunHostError} with
|
|
316
324
|
* code `invalid_repository_root`, thrown when the factory is called.
|
|
317
|
-
*
|
|
318
|
-
* does.
|
|
325
|
+
* Builds without jj exactly as {@link layerContained} does.
|
|
319
326
|
*
|
|
320
327
|
* @category layers
|
|
321
328
|
* @since 1.0.0-rc.0
|
|
@@ -323,7 +330,7 @@ export const layerContained = (
|
|
|
323
330
|
export const layerContainedAt = (
|
|
324
331
|
root: string,
|
|
325
332
|
options?: ContainedOptions
|
|
326
|
-
): Layer.Layer<BunHost | Crypto.Crypto,
|
|
333
|
+
): Layer.Layer<BunHost | Crypto.Crypto, never, ProcessLedger.ProcessLedger> => {
|
|
327
334
|
const repositoryRoot = absoluteRoot("layerContainedAt", root)
|
|
328
335
|
const spawner = Layer.provide(
|
|
329
336
|
ProcessReaper.layerSpawner({ graceMs: options?.graceMs }),
|