@oh-my-pi/pi-utils 18.11.1 → 18.11.3
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 +18 -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/dom/parser.ts +6 -1
- package/src/index.ts +1 -0
- package/src/marked/core.ts +4 -1
- 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,24 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.11.3] - 2026-10-16
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed Markdown fence info-string parsing to keep backtick-containing backtick openers as paragraph text while accepting backticks in tilde info strings and Unicode line separators in either fence's info string ([#15928](https://github.com/can1357/oh-my-pi/pull/15928) by [@jchanghong023](https://github.com/jchanghong023)).
|
|
10
|
+
- Fixed `parseHTML` treating `<link>` in RSS/Atom documents as an HTML void element, which dropped the URL text of RSS `<link>…</link>` ([#15954](https://github.com/can1357/oh-my-pi/issues/15954))
|
|
11
|
+
|
|
12
|
+
## [18.11.2] - 2026-10-16
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- 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))
|
|
17
|
+
- 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))
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- 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))
|
|
22
|
+
|
|
5
23
|
## [18.10.0] - 2026-10-12
|
|
6
24
|
|
|
7
25
|
### 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.3",
|
|
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.3"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@types/bun": "^1.3.14"
|
package/src/dom/parser.ts
CHANGED
|
@@ -366,7 +366,12 @@ export function parseInto(html: string, document: Document, root: Document | Doc
|
|
|
366
366
|
index = match ? closePattern.lastIndex : html.length;
|
|
367
367
|
continue;
|
|
368
368
|
}
|
|
369
|
-
|
|
369
|
+
// Feed documents follow XML rules: only an explicit `/>` closes immediately, so RSS `<link>url</link>`
|
|
370
|
+
// keeps its text instead of being treated as an HTML void element.
|
|
371
|
+
const isVoid = xmlLike
|
|
372
|
+
? parsed.selfClosing
|
|
373
|
+
: VOID_ELEMENTS[parsed.name] || (namespace === SVG_NAMESPACE && parsed.selfClosing);
|
|
374
|
+
if (!isVoid) {
|
|
370
375
|
stack.push(element);
|
|
371
376
|
parent = element;
|
|
372
377
|
}
|
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/marked/core.ts
CHANGED
|
@@ -775,7 +775,10 @@ function splitTableRow(line: string): string[] {
|
|
|
775
775
|
}
|
|
776
776
|
|
|
777
777
|
function isFence(line: string): RegExpExecArray | null {
|
|
778
|
-
|
|
778
|
+
// Unicode line separators may occur in the info string.
|
|
779
|
+
const match = /^ {0,3}(`{3,}|~{3,})([^\n]*?)(?:\n|$)$/.exec(line);
|
|
780
|
+
// Backtick fences cannot contain backticks in their info string.
|
|
781
|
+
return match && match[1]!.startsWith("`") && match[2]!.includes("`") ? null : match;
|
|
779
782
|
}
|
|
780
783
|
function isHeading(line: string): boolean {
|
|
781
784
|
return /^ {0,3}#{1,6}(?:\s|$)/.test(line);
|
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}`;
|