@hraness/sponge 0.3.1 → 0.3.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/dist/src/annotations/quote.d.ts +2 -0
- package/dist/src/annotations/quote.js +29 -1
- package/dist/src/cli/commands.js +1 -1
- package/dist/src/digest/pipeline.js +6 -4
- package/dist/src/errors.d.ts +2 -0
- package/dist/src/errors.js +2 -0
- package/dist/src/portability/files.js +12 -2
- package/native/runtime-manifest.json +4 -4
- package/package.json +1 -1
|
@@ -7,6 +7,8 @@ export interface TextQuote {
|
|
|
7
7
|
start: number;
|
|
8
8
|
end: number;
|
|
9
9
|
}
|
|
10
|
+
/** Spoken text without transcript cues, whitespace collapsed: what a reader quotes from a transcript. */
|
|
11
|
+
export declare function stripCues(raw: string): string;
|
|
10
12
|
export declare function foldText(value: string): string;
|
|
11
13
|
/** Every occurrence of `quote` in `source`, compared after folding. */
|
|
12
14
|
export declare function locateAll(source: string, quote: string): {
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
const context = 32;
|
|
2
2
|
const graphemes = new Intl.Segmenter(undefined, { granularity: 'grapheme' });
|
|
3
3
|
const escapable = /^[\\`*_{}[\]()#+\-.!|>~<"']$/;
|
|
4
|
+
/** A transcript cue at a line start: `- [00:02] ` (clipped captions) or `[00:00:02] ` (Sponge transcripts). */
|
|
5
|
+
const cue = /^[ \t]*(?:[-*+][ \t]+)?\[(?:\d{1,2}:)?\d{1,2}:\d{2}\][ \t]+/;
|
|
6
|
+
/** Spoken text without transcript cues, whitespace collapsed: what a reader quotes from a transcript. */
|
|
7
|
+
export function stripCues(raw) {
|
|
8
|
+
return raw
|
|
9
|
+
.split('\n')
|
|
10
|
+
.map((line) => line.replace(cue, ''))
|
|
11
|
+
.join('\n')
|
|
12
|
+
.replace(/\s+/g, ' ')
|
|
13
|
+
.trim();
|
|
14
|
+
}
|
|
4
15
|
/**
|
|
5
16
|
* Fold the differences a reader cannot see: Markdown escapes, curly quotes, dashes, runs of
|
|
6
|
-
* whitespace and Unicode normalization. Folding works on grapheme clusters, each NFC-normalized;
|
|
17
|
+
* whitespace, line-start transcript cues and Unicode normalization. Folding works on grapheme clusters, each NFC-normalized;
|
|
7
18
|
* every folded code unit of a cluster maps to the cluster's raw bounds, so `map[i]` and `ends[i]`
|
|
8
19
|
* bound the raw text of folded unit i and matches map back exactly.
|
|
9
20
|
*/
|
|
@@ -19,6 +30,17 @@ function fold(raw) {
|
|
|
19
30
|
};
|
|
20
31
|
for (let c = 0; c < clusters.length; c++) {
|
|
21
32
|
const { segment, index } = clusters[c];
|
|
33
|
+
// A transcript cue reads as the space between caption lines, so a quote of the speech spans them.
|
|
34
|
+
const marker = (index === 0 || raw[index - 1] === '\n') && cue.exec(raw.slice(index, index + 32));
|
|
35
|
+
if (marker) {
|
|
36
|
+
const stop = index + marker[0].length;
|
|
37
|
+
if (!space && text.length)
|
|
38
|
+
emit(' ', index, stop);
|
|
39
|
+
space = true;
|
|
40
|
+
while (c + 1 < clusters.length && clusters[c + 1].index < stop)
|
|
41
|
+
c++;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
22
44
|
// An escaped character maps to its backslash, so a match starting there keeps the escape.
|
|
23
45
|
let ch = segment, end = index + segment.length;
|
|
24
46
|
if (segment === '\\' && c + 1 < clusters.length && escapable.test(clusters[c + 1].segment)) {
|
|
@@ -98,6 +120,12 @@ function rendered(raw) {
|
|
|
98
120
|
for (let i = 0; i < raw.length; i++) {
|
|
99
121
|
let ch = raw[i], at = i;
|
|
100
122
|
if (lineStart) {
|
|
123
|
+
const c = cue.exec(raw.slice(i, i + 32));
|
|
124
|
+
if (c) {
|
|
125
|
+
i += c[0].length - 1;
|
|
126
|
+
lineStart = false;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
101
129
|
const m = /^[ \t]*(?:>[ \t]?|#{1,6}[ \t]+|[-*+][ \t]+|\d{1,3}[.)][ \t]+)*/.exec(raw.slice(i, i + 64));
|
|
102
130
|
if (m[0].length) {
|
|
103
131
|
i += m[0].length - 1;
|
package/dist/src/cli/commands.js
CHANGED
|
@@ -791,5 +791,5 @@ export const usageOf = (name) => command(name)
|
|
|
791
791
|
.join('\n');
|
|
792
792
|
export function commandHelp(name) {
|
|
793
793
|
const c = command(name), flags = Object.keys(c.flags);
|
|
794
|
-
return `sponge ${c.name} — ${c.summary}\n\nUsage:\n${c.usage.map(row).join('')
|
|
794
|
+
return `sponge ${c.name} — ${c.summary}\n\nUsage:\n${c.usage.map(([synopsis, description]) => row([`sponge ${synopsis}`, description])).join('')}${flags.length ? `\nFlags: ${flags.map((f) => '--' + f).join(' ')}\n` : ''}\nGlobal flags: --library /absolute/path --json --debug\n`;
|
|
795
795
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { foldText, locateAll, locateLoose, words } from '../annotations/quote.js';
|
|
1
|
+
import { foldText, locateAll, locateLoose, stripCues, words } from '../annotations/quote.js';
|
|
2
2
|
import { checkDigest, digestLimits as L, forms, parseDigest, } from './schema.js';
|
|
3
3
|
import { SpongeError } from '../errors.js';
|
|
4
4
|
import { sha256 } from '../util/hash.js';
|
|
@@ -97,7 +97,9 @@ function lastSentenceEnd(window) {
|
|
|
97
97
|
best = m.index + m[0].length;
|
|
98
98
|
return best;
|
|
99
99
|
}
|
|
100
|
-
const display = (p) => `[${p.id}] ${
|
|
100
|
+
const display = (p) => `[${p.id}] ${stripCues(p.text)}`;
|
|
101
|
+
/** A raw source span as a quote: transcript cues inside it are dropped, so the quote reads as the speech. */
|
|
102
|
+
const spoken = (raw) => (raw.includes('\n') && stripCues(raw) !== collapse(raw) ? stripCues(raw) : raw);
|
|
101
103
|
/** Pack passages into sections of at most `budget` display characters, preferring heading boundaries past half a budget. */
|
|
102
104
|
export function sectionsOf(list, budget) {
|
|
103
105
|
const out = [];
|
|
@@ -295,7 +297,7 @@ export function snapQuote(source, text, within) {
|
|
|
295
297
|
const folded = locateAll(source, text);
|
|
296
298
|
if (folded.length) {
|
|
297
299
|
const m = pick(folded);
|
|
298
|
-
return { text: m ? source.slice(m.start, m.end) : text, match: 'folded' };
|
|
300
|
+
return { text: m ? spoken(source.slice(m.start, m.end)) : text, match: 'folded' };
|
|
299
301
|
}
|
|
300
302
|
const loose = (within && pick(locateLoose(source, text, within))) || pick(locateLoose(source, text));
|
|
301
303
|
if (!loose)
|
|
@@ -306,7 +308,7 @@ export function snapQuote(source, text, within) {
|
|
|
306
308
|
start--;
|
|
307
309
|
while (end < source.length && /[*_`]/.test(source[end]))
|
|
308
310
|
end++;
|
|
309
|
-
return { text: source.slice(start, end), match: 'snapped' };
|
|
311
|
+
return { text: spoken(source.slice(start, end)), match: 'snapped' };
|
|
310
312
|
}
|
|
311
313
|
/**
|
|
312
314
|
* A located quote over the word limit keeps its leading words: a prefix of a source span is still verbatim. Trailing
|
package/dist/src/errors.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ declare const parents: {
|
|
|
16
16
|
readonly 'not-digest': "unsupported";
|
|
17
17
|
readonly malformed: "invalid-input";
|
|
18
18
|
readonly 'no-capture': "invalid-input";
|
|
19
|
+
readonly 'parent-missing': "invalid-input";
|
|
20
|
+
readonly 'target-exists': "invalid-input";
|
|
19
21
|
};
|
|
20
22
|
export type DetailCode = keyof typeof parents;
|
|
21
23
|
export type SpongeCode = ErrorCode | DetailCode;
|
package/dist/src/errors.js
CHANGED
|
@@ -112,8 +112,18 @@ export function copyVerified(source, target, record) {
|
|
|
112
112
|
export function newDirectory(path) {
|
|
113
113
|
if (resolve(path) !== path)
|
|
114
114
|
throw new SpongeError('invalid-input');
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
try {
|
|
116
|
+
safeDirectory(dirname(path));
|
|
117
|
+
mkdirSync(path, { mode: 0o700 });
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
const code = error.code;
|
|
121
|
+
if (code === 'ENOENT')
|
|
122
|
+
throw new SpongeError('parent-missing', { cause: error });
|
|
123
|
+
if (code === 'EEXIST')
|
|
124
|
+
throw new SpongeError('target-exists', { cause: error });
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
117
127
|
const stat = lstatSync(path);
|
|
118
128
|
return { root: path, device: stat.dev, inode: stat.ino };
|
|
119
129
|
}
|
|
@@ -27,14 +27,14 @@
|
|
|
27
27
|
}
|
|
28
28
|
],
|
|
29
29
|
"build": {
|
|
30
|
-
"asrSourceSha256": "
|
|
30
|
+
"asrSourceSha256": "8bd77ebffabae786902304eacb983fc950f70c981fb1538696517d85052c9407",
|
|
31
31
|
"clangVersion": "Apple clang version 21.0.0 (clang-2100.1.1.101)",
|
|
32
|
-
"embeddingSourceSha256": "
|
|
32
|
+
"embeddingSourceSha256": "2b0cd2b9c52c9dee578a3be50a443d528d5204d47f3f9ba750f6a1f37a191e78",
|
|
33
33
|
"swiftVersion": "Apple Swift version 6.3.3 (swiftlang-6.3.3.1.3 clang-2100.1.1.101)"
|
|
34
34
|
},
|
|
35
35
|
"minimumMacOS": "26.0",
|
|
36
|
-
"packageVersion": "0.3.
|
|
36
|
+
"packageVersion": "0.3.2",
|
|
37
37
|
"platform": "darwin",
|
|
38
38
|
"schemaVersion": 1,
|
|
39
|
-
"sourceCommit": "
|
|
39
|
+
"sourceCommit": "e9eb0613365859c5809a05e8ad909504ac6e8d66"
|
|
40
40
|
}
|