@oh-my-pi/pi-utils 17.4.2 → 18.0.1
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/CHANGELOG.md +7 -0
- package/package.json +2 -2
- package/src/env.ts +36 -5
- package/src/logger.ts +19 -15
- package/src/stream.ts +11 -23
- package/src/vendor/mermaid-ascii/ascii/grid.ts +16 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.0.1] - 2026-08-23
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed the Mermaid ASCII renderer throwing on left-to-right diagrams containing a `subgraph`, which made the fenced block fall back to raw source in the terminal. `offsetDrawingForSubgraphs` shifts every drawing coordinate to make room for subgraph borders that extend past the origin, but the canvas had already been sized from the pre-shift grid extents, so edges routed to the outermost column wrote past the allocation and `drawLine` threw on the missing column. The canvas and role canvas now grow by the same shift. ([#9340](https://github.com/can1357/oh-my-pi/issues/9340))
|
|
10
|
+
- Fixed child shell environments inheriting Bun-autoloaded `.env.<mode>.local` values from the launch directory. ([#9290](https://github.com/can1357/oh-my-pi/issues/9290))
|
|
11
|
+
|
|
5
12
|
## [17.4.2] - 2026-08-21
|
|
6
13
|
|
|
7
14
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-utils",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "18.0.1",
|
|
5
5
|
"description": "Shared utilities for pi packages",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Stencil Labs, Inc.",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"fmt": "biome format --write ."
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@oh-my-pi/pi-natives": "
|
|
34
|
+
"@oh-my-pi/pi-natives": "18.0.1"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/bun": "^1.3.14"
|
package/src/env.ts
CHANGED
|
@@ -102,16 +102,39 @@ export function filterChildShellEnv(
|
|
|
102
102
|
): Record<string, string> {
|
|
103
103
|
const result = filterProcessEnv(env);
|
|
104
104
|
const projectEnv = parseEnvFile(path.join(cwd, ".env"));
|
|
105
|
-
const
|
|
105
|
+
const launchNodeEnv = launchEnvValues ? launchEnvValues.get("NODE_ENV") : env.NODE_ENV;
|
|
106
|
+
const nodeEnvName = `.env.${launchNodeEnv || "development"}`;
|
|
106
107
|
const modeEnv = parseEnvFile(path.join(cwd, nodeEnvName));
|
|
107
108
|
const localEnv = parseEnvFile(path.join(cwd, ".env.local"));
|
|
108
|
-
const
|
|
109
|
+
const modeLocalEnv = parseEnvFile(path.join(cwd, `${nodeEnvName}.local`));
|
|
110
|
+
const launchEnv = { ...projectEnv, ...modeEnv, ...localEnv, ...modeLocalEnv };
|
|
109
111
|
const expandedLaunchEnv = {
|
|
110
112
|
...expandDotenvValues(projectEnv, result),
|
|
111
113
|
...expandDotenvValues(modeEnv, result),
|
|
112
114
|
...expandDotenvValues(localEnv, result),
|
|
115
|
+
...expandDotenvValues(modeLocalEnv, result),
|
|
113
116
|
};
|
|
114
|
-
|
|
117
|
+
let fallbackLaunchEnv: Record<string, string> | undefined;
|
|
118
|
+
let expandedFallbackLaunchEnv: Record<string, string> | undefined;
|
|
119
|
+
if (!launchEnvValues && nodeEnvName !== ".env.development") {
|
|
120
|
+
const fallbackModeEnv = parseEnvFile(path.join(cwd, ".env.development"));
|
|
121
|
+
const fallbackModeLocalEnv = parseEnvFile(path.join(cwd, ".env.development.local"));
|
|
122
|
+
const candidate = { ...projectEnv, ...fallbackModeEnv, ...localEnv, ...fallbackModeLocalEnv };
|
|
123
|
+
const expandedCandidate = {
|
|
124
|
+
...expandDotenvValues(projectEnv, result),
|
|
125
|
+
...expandDotenvValues(fallbackModeEnv, result),
|
|
126
|
+
...expandDotenvValues(localEnv, result),
|
|
127
|
+
...expandDotenvValues(fallbackModeLocalEnv, result),
|
|
128
|
+
};
|
|
129
|
+
if (candidate.NODE_ENV === env.NODE_ENV || expandedCandidate.NODE_ENV === env.NODE_ENV) {
|
|
130
|
+
// Without a launch snapshot, NODE_ENV may itself have come from dotenv.
|
|
131
|
+
// Bun chose the default mode before loading it, so retain both candidates.
|
|
132
|
+
fallbackLaunchEnv = candidate;
|
|
133
|
+
expandedFallbackLaunchEnv = expandedCandidate;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const allLaunchEnv = fallbackLaunchEnv ? { ...launchEnv, ...fallbackLaunchEnv } : launchEnv;
|
|
137
|
+
for (const key in allLaunchEnv) {
|
|
115
138
|
const launchValue = launchEnvValues?.get(key);
|
|
116
139
|
if (launchValue !== undefined) {
|
|
117
140
|
// Launcher-owned name: it keeps the launcher's own value. Bun overwrites
|
|
@@ -119,7 +142,10 @@ export function filterChildShellEnv(
|
|
|
119
142
|
// value whenever what survived is exactly what the dotenv file defines.
|
|
120
143
|
if (
|
|
121
144
|
result[key] !== launchValue &&
|
|
122
|
-
(result[key] === launchEnv[key] ||
|
|
145
|
+
(result[key] === launchEnv[key] ||
|
|
146
|
+
result[key] === expandedLaunchEnv[key] ||
|
|
147
|
+
result[key] === fallbackLaunchEnv?.[key] ||
|
|
148
|
+
result[key] === expandedFallbackLaunchEnv?.[key])
|
|
123
149
|
) {
|
|
124
150
|
result[key] = launchValue;
|
|
125
151
|
}
|
|
@@ -130,7 +156,12 @@ export function filterChildShellEnv(
|
|
|
130
156
|
// absent from it, or OMP itself injected the value — either way it came
|
|
131
157
|
// from a project dotenv file, not the parent shell.
|
|
132
158
|
delete result[key];
|
|
133
|
-
} else if (
|
|
159
|
+
} else if (
|
|
160
|
+
result[key] === launchEnv[key] ||
|
|
161
|
+
result[key] === expandedLaunchEnv[key] ||
|
|
162
|
+
result[key] === fallbackLaunchEnv?.[key] ||
|
|
163
|
+
result[key] === expandedFallbackLaunchEnv?.[key]
|
|
164
|
+
) {
|
|
134
165
|
// No launch-env snapshot (dotenv autoloaded without procfs): best-effort
|
|
135
166
|
// value match against the Bun-parsed dotenv.
|
|
136
167
|
delete result[key];
|
package/src/logger.ts
CHANGED
|
@@ -91,7 +91,7 @@ function pruneStaleProcessLogs(dir: string): void {
|
|
|
91
91
|
`${cutoff.getFullYear()}-${String(cutoff.getMonth() + 1).padStart(2, "0")}-` +
|
|
92
92
|
String(cutoff.getDate()).padStart(2, "0");
|
|
93
93
|
|
|
94
|
-
const staleLogsByProcessDay = new Map<string, Array<{ path: string;
|
|
94
|
+
const staleLogsByProcessDay = new Map<string, Array<{ path: string; rollover: number }>>();
|
|
95
95
|
for (const entry of entries) {
|
|
96
96
|
if (!entry.isFile()) continue;
|
|
97
97
|
const logMatch = PROCESS_LOG_PATTERN.exec(entry.name);
|
|
@@ -120,25 +120,29 @@ function pruneStaleProcessLogs(dir: string): void {
|
|
|
120
120
|
continue;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
});
|
|
131
|
-
staleLogsByProcessDay.set(key, staleLogs);
|
|
132
|
-
} catch {
|
|
133
|
-
// Another process may have pruned the same stale namespace.
|
|
134
|
-
}
|
|
123
|
+
const key = `${pidText}:${logMatch[1]}`;
|
|
124
|
+
const staleLogs = staleLogsByProcessDay.get(key) ?? [];
|
|
125
|
+
staleLogs.push({
|
|
126
|
+
path: entryPath,
|
|
127
|
+
rollover: Number(logMatch[3] ?? 0),
|
|
128
|
+
});
|
|
129
|
+
staleLogsByProcessDay.set(key, staleLogs);
|
|
135
130
|
}
|
|
136
131
|
|
|
137
132
|
for (const staleLogs of staleLogsByProcessDay.values()) {
|
|
138
|
-
staleLogs.
|
|
133
|
+
if (staleLogs.length <= RETAINED_STALE_LOGS_PER_PROCESS_DAY) continue;
|
|
134
|
+
const ranked: Array<{ path: string; mtimeMs: number; rollover: number }> = [];
|
|
135
|
+
for (const stale of staleLogs) {
|
|
136
|
+
try {
|
|
137
|
+
ranked.push({ ...stale, mtimeMs: fs.statSync(stale.path).mtimeMs });
|
|
138
|
+
} catch {
|
|
139
|
+
// Another process may have pruned the same stale namespace.
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
ranked.sort(
|
|
139
143
|
(a, b) => b.mtimeMs - a.mtimeMs || b.rollover - a.rollover || (a.path < b.path ? -1 : a.path > b.path ? 1 : 0),
|
|
140
144
|
);
|
|
141
|
-
for (const stale of
|
|
145
|
+
for (const stale of ranked.slice(RETAINED_STALE_LOGS_PER_PROCESS_DAY)) {
|
|
142
146
|
try {
|
|
143
147
|
fs.rmSync(stale.path, { force: true });
|
|
144
148
|
} catch {
|
package/src/stream.ts
CHANGED
|
@@ -4,25 +4,6 @@ import { abortableSource } from "./abortable";
|
|
|
4
4
|
import { parseStreamingJson } from "./json-parse";
|
|
5
5
|
|
|
6
6
|
const LF = 0x0a;
|
|
7
|
-
type JsonlChunkResult = {
|
|
8
|
-
values: unknown[];
|
|
9
|
-
error: unknown;
|
|
10
|
-
read: number;
|
|
11
|
-
done: boolean;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
function parseJsonlChunkCompat(input: Uint8Array, beg?: number, end?: number): JsonlChunkResult;
|
|
15
|
-
function parseJsonlChunkCompat(input: string): JsonlChunkResult;
|
|
16
|
-
function parseJsonlChunkCompat(input: Uint8Array | string, beg?: number, end?: number): JsonlChunkResult {
|
|
17
|
-
if (typeof input === "string") {
|
|
18
|
-
const { values, error, read, done } = Bun.JSONL.parseChunk(input);
|
|
19
|
-
return { values, error, read, done };
|
|
20
|
-
}
|
|
21
|
-
const start = beg ?? 0;
|
|
22
|
-
const stop = end ?? input.length;
|
|
23
|
-
const { values, error, read, done } = Bun.JSONL.parseChunk(input, start, stop);
|
|
24
|
-
return { values, error, read, done };
|
|
25
|
-
}
|
|
26
7
|
|
|
27
8
|
export async function* readLines(stream: ReadableStream<Uint8Array>, signal?: AbortSignal): AsyncGenerator<Uint8Array> {
|
|
28
9
|
const buffer = new ConcatSink();
|
|
@@ -58,7 +39,7 @@ export async function* readJsonl<T>(stream: ReadableStream<Uint8Array>, signal?:
|
|
|
58
39
|
const tail = buffer.flush();
|
|
59
40
|
if (tail) {
|
|
60
41
|
buffer.clear();
|
|
61
|
-
const { values, error, done } =
|
|
42
|
+
const { values, error, done } = Bun.JSONL.parseChunk(tail, 0, tail.length);
|
|
62
43
|
if (values.length > 0) {
|
|
63
44
|
yield* values as T[];
|
|
64
45
|
}
|
|
@@ -174,8 +155,15 @@ class ConcatSink {
|
|
|
174
155
|
return text;
|
|
175
156
|
}
|
|
176
157
|
*pullJSONL<T>(chunk: Uint8Array, beg: number, end: number) {
|
|
158
|
+
const newline = chunk.indexOf(LF, beg);
|
|
159
|
+
if (newline === -1 || newline >= end) {
|
|
160
|
+
if (this.isEmpty) this.reset(chunk.subarray(beg, end));
|
|
161
|
+
else this.append(chunk.subarray(beg, end));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
177
165
|
if (this.isEmpty) {
|
|
178
|
-
const { values, error, read, done } =
|
|
166
|
+
const { values, error, read, done } = Bun.JSONL.parseChunk(chunk, beg, end);
|
|
179
167
|
if (values.length > 0) {
|
|
180
168
|
yield* values as T[];
|
|
181
169
|
}
|
|
@@ -192,7 +180,7 @@ class ConcatSink {
|
|
|
192
180
|
space.set(chunk.subarray(beg, end), offset);
|
|
193
181
|
this.#length = total;
|
|
194
182
|
|
|
195
|
-
const { values, error, read, done } =
|
|
183
|
+
const { values, error, read, done } = Bun.JSONL.parseChunk(space, 0, total);
|
|
196
184
|
if (values.length > 0) {
|
|
197
185
|
yield* values as T[];
|
|
198
186
|
}
|
|
@@ -456,7 +444,7 @@ export function parseJsonlLenient<T>(buffer: string, options: { onMalformedRecor
|
|
|
456
444
|
let entries: T[] | undefined;
|
|
457
445
|
|
|
458
446
|
while (buffer.length > 0) {
|
|
459
|
-
const { values, error, read, done } =
|
|
447
|
+
const { values, error, read, done } = Bun.JSONL.parseChunk(buffer);
|
|
460
448
|
if (values.length > 0) {
|
|
461
449
|
const ext = values as T[];
|
|
462
450
|
if (!entries) {
|
|
@@ -11,7 +11,14 @@ import type {
|
|
|
11
11
|
GridCoord, DrawingCoord, Direction, AsciiGraph, AsciiNode, AsciiSubgraph,
|
|
12
12
|
} from './types'
|
|
13
13
|
import { gridKey } from './types'
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
getCanvasSize,
|
|
16
|
+
increaseRoleCanvasSize,
|
|
17
|
+
increaseSize,
|
|
18
|
+
mkCanvas,
|
|
19
|
+
setCanvasSizeToGrid,
|
|
20
|
+
setRoleCanvasSizeToGrid,
|
|
21
|
+
} from './canvas'
|
|
15
22
|
import { determinePath, determineLabelLine } from './edge-routing'
|
|
16
23
|
import { analyzeEdgeBundles, processBundles } from './edge-bundling'
|
|
17
24
|
import { drawBox } from './draw'
|
|
@@ -374,6 +381,14 @@ export function offsetDrawingForSubgraphs(graph: AsciiGraph): void {
|
|
|
374
381
|
node.drawingCoord.y += offsetY
|
|
375
382
|
}
|
|
376
383
|
}
|
|
384
|
+
|
|
385
|
+
// The canvas was sized from the pre-shift grid extents, but every drawing
|
|
386
|
+
// coordinate — including edge path endpoints, which gridToDrawingCoord
|
|
387
|
+
// offsets on read — now sits `offset` cells further out. Grow it to match,
|
|
388
|
+
// or drawing the rightmost/bottom-most edges writes past the allocation.
|
|
389
|
+
const [maxX, maxY] = getCanvasSize(graph.canvas)
|
|
390
|
+
increaseSize(graph.canvas, maxX + offsetX, maxY + offsetY)
|
|
391
|
+
increaseRoleCanvasSize(graph.roleCanvas, maxX + offsetX, maxY + offsetY)
|
|
377
392
|
}
|
|
378
393
|
|
|
379
394
|
// ============================================================================
|