@oh-my-pi/pi-utils 18.11.1 → 18.11.2
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 +11 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/slugify.d.ts +4 -0
- package/dist/types/sqlite.d.ts +12 -0
- package/package.json +2 -2
- package/src/index.ts +1 -0
- package/src/ptree.ts +5 -2
- package/src/slugify.ts +11 -0
- package/src/sqlite.ts +12 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.11.2] - 2026-10-16
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Exported `sqliteFileIdentity()`, so a caller holding a long-lived SQLite connection can tell when its path now names a different file (e.g. after a corrupt store was quarantined and replaced) ([#14361](https://github.com/can1357/oh-my-pi/pull/14361) by [@F0Rextasy](https://github.com/F0Rextasy))
|
|
10
|
+
- Added `slugify()`, the shared lowercase ASCII kebab-case helper with an optional `maxLength` ([#14283](https://github.com/can1357/oh-my-pi/pull/14283) by [@jaredlyon](https://github.com/jaredlyon))
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Fixed an unhandled rejection when a subprocess is spawned with an already-aborted signal ([#15750](https://github.com/can1357/oh-my-pi/pull/15750) by [@harshaygadekar](https://github.com/harshaygadekar))
|
|
15
|
+
|
|
5
16
|
## [18.10.0] - 2026-10-12
|
|
6
17
|
|
|
7
18
|
### Added
|
package/dist/types/index.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export * as ptree from "./ptree.js";
|
|
|
31
31
|
export { AbortError, ChildProcess, Exception, NonZeroExitError } from "./ptree.js";
|
|
32
32
|
export * from "./runtime-install.js";
|
|
33
33
|
export * from "./sanitize-text.js";
|
|
34
|
+
export * from "./slugify.js";
|
|
34
35
|
export * from "./snowflake.js";
|
|
35
36
|
export * from "./sqlite.js";
|
|
36
37
|
export * from "./stderr-guard.js";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Lowercase ASCII kebab slug: [^a-z0-9]+ → "-", edge dashes trimmed; optional maxLength truncates then re-trims trailing dashes; throws RangeError unless maxLength is a non-negative integer. Returns "" when nothing survives. */
|
|
2
|
+
export declare function slugify(input: string, options?: {
|
|
3
|
+
maxLength?: number;
|
|
4
|
+
}): string;
|
package/dist/types/sqlite.d.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
/** Shared SQLite opening, error attribution, and result-code classification for persistent stores. */
|
|
2
2
|
import { Database } from "bun:sqlite";
|
|
3
|
+
/**
|
|
4
|
+
* A store's identity as `dev:ino:birthtimeMs`, `null` when the path holds no
|
|
5
|
+
* file, or `undefined` when the stat itself failed and no conclusion is safe.
|
|
6
|
+
*/
|
|
7
|
+
export type SqliteFileIdentity = string | null | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Identity of the file currently at `dbPath`. Quarantine moves a corrupt store
|
|
10
|
+
* aside and recreates one at the same path, so a connection cached on the path
|
|
11
|
+
* alone keeps writing to the unlinked inode: callers that hold a long-lived
|
|
12
|
+
* handle compare this before reusing it.
|
|
13
|
+
*/
|
|
14
|
+
export declare function sqliteFileIdentity(dbPath: string): SqliteFileIdentity;
|
|
3
15
|
/** Controls opt-in replacement of an unrecoverably corrupt SQLite store. */
|
|
4
16
|
export interface SqliteOpenOptions {
|
|
5
17
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-utils",
|
|
3
|
-
"version": "18.11.
|
|
3
|
+
"version": "18.11.2",
|
|
4
4
|
"description": "Shared utilities for pi packages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@oh-my-pi/pi-natives": "18.11.
|
|
60
|
+
"@oh-my-pi/pi-natives": "18.11.2"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@types/bun": "^1.3.14"
|
package/src/index.ts
CHANGED
|
@@ -31,6 +31,7 @@ export * as ptree from "./ptree";
|
|
|
31
31
|
export { AbortError, ChildProcess, Exception, NonZeroExitError } from "./ptree";
|
|
32
32
|
export * from "./runtime-install";
|
|
33
33
|
export * from "./sanitize-text";
|
|
34
|
+
export * from "./slugify";
|
|
34
35
|
export * from "./snowflake";
|
|
35
36
|
export * from "./sqlite";
|
|
36
37
|
export * from "./stderr-guard";
|
package/src/ptree.ts
CHANGED
|
@@ -443,7 +443,9 @@ export class ChildProcess<In extends InMask = InMask> {
|
|
|
443
443
|
// ── Output helpers ───────────────────────────────────────────────────
|
|
444
444
|
|
|
445
445
|
async #throwIfAborted(): Promise<void> {
|
|
446
|
-
|
|
446
|
+
// A kill() issued before Bun reports the root's exit only records the
|
|
447
|
+
// reason as pending; output readers that do not wait for exit must still see it.
|
|
448
|
+
const exitReason = this.#exitReason ?? this.#exitReasonPending;
|
|
447
449
|
if (!exitReason?.aborted) return;
|
|
448
450
|
if (this.#terminating) await this.#terminating;
|
|
449
451
|
throw exitReason;
|
|
@@ -589,10 +591,11 @@ export class ChildProcess<In extends InMask = InMask> {
|
|
|
589
591
|
// ── Signal / timeout ─────────────────────────────────────────────────
|
|
590
592
|
|
|
591
593
|
attachSignal(signal: AbortSignal): void {
|
|
594
|
+
const cleanup = this.#exited.catch(() => {});
|
|
592
595
|
const onAbort = () => this.kill(new AbortError(signal.reason, "<cancelled>"));
|
|
593
596
|
if (signal.aborted) return void onAbort();
|
|
594
597
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
595
|
-
|
|
598
|
+
cleanup.finally(() => signal.removeEventListener("abort", onAbort));
|
|
596
599
|
}
|
|
597
600
|
|
|
598
601
|
#clearTimeout(): void {
|
package/src/slugify.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Lowercase ASCII kebab slug: [^a-z0-9]+ → "-", edge dashes trimmed; optional maxLength truncates then re-trims trailing dashes; throws RangeError unless maxLength is a non-negative integer. Returns "" when nothing survives. */
|
|
2
|
+
export function slugify(input: string, options?: { maxLength?: number }): string {
|
|
3
|
+
if (options?.maxLength !== undefined && !(Number.isInteger(options.maxLength) && options.maxLength >= 0)) {
|
|
4
|
+
throw new RangeError(`slugify maxLength must be a non-negative integer, got ${options.maxLength}`);
|
|
5
|
+
}
|
|
6
|
+
const slug = input
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
9
|
+
.replace(/^-+|-+$/g, "");
|
|
10
|
+
return options?.maxLength === undefined ? slug : slug.slice(0, options.maxLength).replace(/-+$/g, "");
|
|
11
|
+
}
|
package/src/sqlite.ts
CHANGED
|
@@ -11,7 +11,11 @@ const BUSY_MAX_ATTEMPTS = 4;
|
|
|
11
11
|
const BUSY_BASE_DELAY_MS = 100;
|
|
12
12
|
const SQLITE_STORE_SUFFIXES = ["-wal", "-shm", "-journal", ""];
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* A store's identity as `dev:ino:birthtimeMs`, `null` when the path holds no
|
|
16
|
+
* file, or `undefined` when the stat itself failed and no conclusion is safe.
|
|
17
|
+
*/
|
|
18
|
+
export type SqliteFileIdentity = string | null | undefined;
|
|
15
19
|
|
|
16
20
|
/**
|
|
17
21
|
* Corrupt handles held open by in-process openers awaiting recovery, keyed by
|
|
@@ -77,7 +81,13 @@ class SqliteAttemptFailure extends Error {
|
|
|
77
81
|
}
|
|
78
82
|
}
|
|
79
83
|
|
|
80
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Identity of the file currently at `dbPath`. Quarantine moves a corrupt store
|
|
86
|
+
* aside and recreates one at the same path, so a connection cached on the path
|
|
87
|
+
* alone keeps writing to the unlinked inode: callers that hold a long-lived
|
|
88
|
+
* handle compare this before reusing it.
|
|
89
|
+
*/
|
|
90
|
+
export function sqliteFileIdentity(dbPath: string): SqliteFileIdentity {
|
|
81
91
|
try {
|
|
82
92
|
const stat = fs.statSync(dbPath);
|
|
83
93
|
return `${stat.dev}:${stat.ino}:${stat.birthtimeMs}`;
|