@cendor/squeeze 0.2.0
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/LICENSE +201 -0
- package/README.md +47 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +550 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +28 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +250 -0
- package/dist/json.js.map +1 -0
- package/dist/store.d.ts +44 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +104 -0
- package/dist/store.js.map +1 -0
- package/dist/text.d.ts +26 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +109 -0
- package/dist/text.js.map +1 -0
- package/package.json +51 -0
package/dist/json.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A tiny order-preserving JSON parser + a serializer that mirrors Python's
|
|
3
|
+
* `json.dumps(obj, ensure_ascii=False, separators=(",", ":"))` as closely as practical.
|
|
4
|
+
*
|
|
5
|
+
* Why not `JSON.parse` / `JSON.stringify`? Three Python behaviors must be reproduced for byte-close
|
|
6
|
+
* output: (1) object key order is document order (JS objects reorder integer-like keys, so parsed
|
|
7
|
+
* objects are `Map`s here); (2) duplicate keys keep the last value; (3) an integer-valued *float*
|
|
8
|
+
* literal (`1.0`, `1e5`) serializes with a trailing `.0` (`"1.0"`, `"100000.0"`) where JS would emit
|
|
9
|
+
* `"1"`. Numbers are therefore carried as raw source tokens (`JNum`). String escaping already matches
|
|
10
|
+
* (`JSON.stringify` of a string == Python `ensure_ascii=False` for the C0 range, `"`, and `\\`).
|
|
11
|
+
*
|
|
12
|
+
* Reversibility never depends on this: `expand()` returns the stored original verbatim.
|
|
13
|
+
*/
|
|
14
|
+
/** A JSON number carried as its raw source token, so int/float formatting round-trips. */
|
|
15
|
+
export class JNum {
|
|
16
|
+
raw;
|
|
17
|
+
constructor(raw) {
|
|
18
|
+
this.raw = raw;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const WS = new Set([' ', '\t', '\n', '\r']);
|
|
22
|
+
class Parser {
|
|
23
|
+
s;
|
|
24
|
+
i = 0;
|
|
25
|
+
constructor(s) {
|
|
26
|
+
this.s = s;
|
|
27
|
+
}
|
|
28
|
+
parse() {
|
|
29
|
+
this.ws();
|
|
30
|
+
const v = this.value();
|
|
31
|
+
this.ws();
|
|
32
|
+
if (this.i !== this.s.length)
|
|
33
|
+
throw new SyntaxError(`trailing content at ${this.i}`);
|
|
34
|
+
return v;
|
|
35
|
+
}
|
|
36
|
+
ws() {
|
|
37
|
+
while (this.i < this.s.length && WS.has(this.s[this.i]))
|
|
38
|
+
this.i++;
|
|
39
|
+
}
|
|
40
|
+
value() {
|
|
41
|
+
const c = this.s[this.i];
|
|
42
|
+
if (c === '{')
|
|
43
|
+
return this.object();
|
|
44
|
+
if (c === '[')
|
|
45
|
+
return this.array();
|
|
46
|
+
if (c === '"')
|
|
47
|
+
return this.string();
|
|
48
|
+
if (c === '-' || (c !== undefined && c >= '0' && c <= '9'))
|
|
49
|
+
return this.number();
|
|
50
|
+
if (this.s.startsWith('true', this.i)) {
|
|
51
|
+
this.i += 4;
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (this.s.startsWith('false', this.i)) {
|
|
55
|
+
this.i += 5;
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
if (this.s.startsWith('null', this.i)) {
|
|
59
|
+
this.i += 4;
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
throw new SyntaxError(`unexpected token '${String(c)}' at ${this.i}`);
|
|
63
|
+
}
|
|
64
|
+
object() {
|
|
65
|
+
const obj = new Map();
|
|
66
|
+
this.i++; // {
|
|
67
|
+
this.ws();
|
|
68
|
+
if (this.s[this.i] === '}') {
|
|
69
|
+
this.i++;
|
|
70
|
+
return obj;
|
|
71
|
+
}
|
|
72
|
+
for (;;) {
|
|
73
|
+
this.ws();
|
|
74
|
+
if (this.s[this.i] !== '"')
|
|
75
|
+
throw new SyntaxError(`expected key string at ${this.i}`);
|
|
76
|
+
const key = this.string();
|
|
77
|
+
this.ws();
|
|
78
|
+
if (this.s[this.i] !== ':')
|
|
79
|
+
throw new SyntaxError(`expected ':' at ${this.i}`);
|
|
80
|
+
this.i++;
|
|
81
|
+
this.ws();
|
|
82
|
+
obj.set(key, this.value()); // duplicate keys: last wins (Map.set overwrites, keeps position)
|
|
83
|
+
this.ws();
|
|
84
|
+
const ch = this.s[this.i];
|
|
85
|
+
if (ch === ',') {
|
|
86
|
+
this.i++;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (ch === '}') {
|
|
90
|
+
this.i++;
|
|
91
|
+
return obj;
|
|
92
|
+
}
|
|
93
|
+
throw new SyntaxError(`expected ',' or '}' at ${this.i}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
array() {
|
|
97
|
+
const arr = [];
|
|
98
|
+
this.i++; // [
|
|
99
|
+
this.ws();
|
|
100
|
+
if (this.s[this.i] === ']') {
|
|
101
|
+
this.i++;
|
|
102
|
+
return arr;
|
|
103
|
+
}
|
|
104
|
+
for (;;) {
|
|
105
|
+
this.ws();
|
|
106
|
+
arr.push(this.value());
|
|
107
|
+
this.ws();
|
|
108
|
+
const ch = this.s[this.i];
|
|
109
|
+
if (ch === ',') {
|
|
110
|
+
this.i++;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (ch === ']') {
|
|
114
|
+
this.i++;
|
|
115
|
+
return arr;
|
|
116
|
+
}
|
|
117
|
+
throw new SyntaxError(`expected ',' or ']' at ${this.i}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
string() {
|
|
121
|
+
this.i++; // opening quote
|
|
122
|
+
let out = '';
|
|
123
|
+
for (;;) {
|
|
124
|
+
const c = this.s[this.i++];
|
|
125
|
+
if (c === undefined)
|
|
126
|
+
throw new SyntaxError('unterminated string');
|
|
127
|
+
if (c === '"')
|
|
128
|
+
return out;
|
|
129
|
+
if (c === '\\') {
|
|
130
|
+
const e = this.s[this.i++];
|
|
131
|
+
switch (e) {
|
|
132
|
+
case '"':
|
|
133
|
+
out += '"';
|
|
134
|
+
break;
|
|
135
|
+
case '\\':
|
|
136
|
+
out += '\\';
|
|
137
|
+
break;
|
|
138
|
+
case '/':
|
|
139
|
+
out += '/';
|
|
140
|
+
break;
|
|
141
|
+
case 'b':
|
|
142
|
+
out += '\b';
|
|
143
|
+
break;
|
|
144
|
+
case 'f':
|
|
145
|
+
out += '\f';
|
|
146
|
+
break;
|
|
147
|
+
case 'n':
|
|
148
|
+
out += '\n';
|
|
149
|
+
break;
|
|
150
|
+
case 'r':
|
|
151
|
+
out += '\r';
|
|
152
|
+
break;
|
|
153
|
+
case 't':
|
|
154
|
+
out += '\t';
|
|
155
|
+
break;
|
|
156
|
+
case 'u': {
|
|
157
|
+
const hex = this.s.slice(this.i, this.i + 4);
|
|
158
|
+
if (hex.length !== 4)
|
|
159
|
+
throw new SyntaxError('bad \\u escape');
|
|
160
|
+
this.i += 4;
|
|
161
|
+
out += String.fromCharCode(Number.parseInt(hex, 16));
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
default:
|
|
165
|
+
throw new SyntaxError(`bad escape \\${String(e)}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
out += c;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
number() {
|
|
174
|
+
const start = this.i;
|
|
175
|
+
if (this.s[this.i] === '-')
|
|
176
|
+
this.i++;
|
|
177
|
+
let sawDigit = false;
|
|
178
|
+
while (this.i < this.s.length) {
|
|
179
|
+
const c = this.s[this.i];
|
|
180
|
+
if (c >= '0' && c <= '9') {
|
|
181
|
+
sawDigit = true;
|
|
182
|
+
this.i++;
|
|
183
|
+
}
|
|
184
|
+
else if (c === '.' || c === 'e' || c === 'E' || c === '+' || c === '-') {
|
|
185
|
+
this.i++;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (!sawDigit)
|
|
192
|
+
throw new SyntaxError(`invalid number at ${start}`);
|
|
193
|
+
return new JNum(this.s.slice(start, this.i));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/** Parse standard JSON into the ordered `JVal` tree. Throws `SyntaxError` on invalid input. */
|
|
197
|
+
export function parseJson(text) {
|
|
198
|
+
return new Parser(text).parse();
|
|
199
|
+
}
|
|
200
|
+
function formatNumberToken(raw) {
|
|
201
|
+
// Integer literal: emit the exact digits (Python `int` -> same string, exact for big ints too).
|
|
202
|
+
if (!/[.eE]/.test(raw))
|
|
203
|
+
return raw;
|
|
204
|
+
// Float literal: match Python's `repr(float)` for the common cases. Integer-valued floats keep a
|
|
205
|
+
// trailing ".0"; anything JS already prints with a '.'/'e' (1.5, 1e+21) is left as-is.
|
|
206
|
+
const n = Number(raw);
|
|
207
|
+
let s = String(n);
|
|
208
|
+
if (!/[.eE]/.test(s))
|
|
209
|
+
s += '.0';
|
|
210
|
+
return s;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Serialize a value the way Python's compact `json.dumps` would. Accepts both the parsed `JVal` tree
|
|
214
|
+
* (`Map`/`JNum`) and plain JS values (used for object input, where numbers are plain `number`).
|
|
215
|
+
*/
|
|
216
|
+
export function dumps(v) {
|
|
217
|
+
if (v === null || v === undefined)
|
|
218
|
+
return 'null';
|
|
219
|
+
if (typeof v === 'boolean')
|
|
220
|
+
return v ? 'true' : 'false';
|
|
221
|
+
if (typeof v === 'string')
|
|
222
|
+
return JSON.stringify(v);
|
|
223
|
+
if (v instanceof JNum)
|
|
224
|
+
return formatNumberToken(v.raw);
|
|
225
|
+
if (typeof v === 'number') {
|
|
226
|
+
if (!Number.isFinite(v))
|
|
227
|
+
return v > 0 ? 'Infinity' : Number.isNaN(v) ? 'NaN' : '-Infinity';
|
|
228
|
+
return String(v);
|
|
229
|
+
}
|
|
230
|
+
if (Array.isArray(v))
|
|
231
|
+
return `[${v.map(dumps).join(',')}]`;
|
|
232
|
+
if (v instanceof Map) {
|
|
233
|
+
const parts = [];
|
|
234
|
+
for (const [k, val] of v)
|
|
235
|
+
parts.push(`${JSON.stringify(String(k))}:${dumps(val)}`);
|
|
236
|
+
return `{${parts.join(',')}}`;
|
|
237
|
+
}
|
|
238
|
+
if (typeof v === 'object') {
|
|
239
|
+
const parts = [];
|
|
240
|
+
for (const k of Object.keys(v)) {
|
|
241
|
+
const val = v[k];
|
|
242
|
+
if (val === undefined)
|
|
243
|
+
continue; // JS undefined has no JSON form (JSON.stringify drops it)
|
|
244
|
+
parts.push(`${JSON.stringify(k)}:${dumps(val)}`);
|
|
245
|
+
}
|
|
246
|
+
return `{${parts.join(',')}}`;
|
|
247
|
+
}
|
|
248
|
+
return 'null';
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=json.js.map
|
package/dist/json.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,0FAA0F;AAC1F,MAAM,OAAO,IAAI;IACa;IAA5B,YAA4B,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;IAAG,CAAC;CAC5C;AAKD,MAAM,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AAE5C,MAAM,MAAM;IAEmB;IADrB,CAAC,GAAG,CAAC,CAAC;IACd,YAA6B,CAAS;QAAT,MAAC,GAAD,CAAC,CAAQ;IAAG,CAAC;IAE1C,KAAK;QACH,IAAI,CAAC,EAAE,EAAE,CAAC;QACV,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QACvB,IAAI,CAAC,EAAE,EAAE,CAAC;QACV,IAAI,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM;YAAE,MAAM,IAAI,WAAW,CAAC,uBAAuB,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;QACrF,OAAO,CAAC,CAAC;IACX,CAAC;IAEO,EAAE;QACR,OAAO,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAW,CAAC;YAAE,IAAI,CAAC,CAAC,EAAE,CAAC;IAC9E,CAAC;IAEO,KAAK;QACX,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzB,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;QACpC,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC;QACnC,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;QACpC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;QACjF,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;YACZ,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACvC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;YACZ,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;YACZ,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,IAAI,WAAW,CAAC,qBAAqB,MAAM,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;IACxE,CAAC;IAEO,MAAM;QACZ,MAAM,GAAG,GAAG,IAAI,GAAG,EAAgB,CAAC;QACpC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI;QACd,IAAI,CAAC,EAAE,EAAE,CAAC;QACV,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YAC3B,IAAI,CAAC,CAAC,EAAE,CAAC;YACT,OAAO,GAAG,CAAC;QACb,CAAC;QACD,SAAS,CAAC;YACR,IAAI,CAAC,EAAE,EAAE,CAAC;YACV,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;gBAAE,MAAM,IAAI,WAAW,CAAC,0BAA0B,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;YACtF,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YAC1B,IAAI,CAAC,EAAE,EAAE,CAAC;YACV,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;gBAAE,MAAM,IAAI,WAAW,CAAC,mBAAmB,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;YAC/E,IAAI,CAAC,CAAC,EAAE,CAAC;YACT,IAAI,CAAC,EAAE,EAAE,CAAC;YACV,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,iEAAiE;YAC7F,IAAI,CAAC,EAAE,EAAE,CAAC;YACV,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;gBACf,IAAI,CAAC,CAAC,EAAE,CAAC;gBACT,SAAS;YACX,CAAC;YACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;gBACf,IAAI,CAAC,CAAC,EAAE,CAAC;gBACT,OAAO,GAAG,CAAC;YACb,CAAC;YACD,MAAM,IAAI,WAAW,CAAC,0BAA0B,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAEO,KAAK;QACX,MAAM,GAAG,GAAW,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI;QACd,IAAI,CAAC,EAAE,EAAE,CAAC;QACV,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YAC3B,IAAI,CAAC,CAAC,EAAE,CAAC;YACT,OAAO,GAAG,CAAC;QACb,CAAC;QACD,SAAS,CAAC;YACR,IAAI,CAAC,EAAE,EAAE,CAAC;YACV,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YACvB,IAAI,CAAC,EAAE,EAAE,CAAC;YACV,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;gBACf,IAAI,CAAC,CAAC,EAAE,CAAC;gBACT,SAAS;YACX,CAAC;YACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;gBACf,IAAI,CAAC,CAAC,EAAE,CAAC;gBACT,OAAO,GAAG,CAAC;YACb,CAAC;YACD,MAAM,IAAI,WAAW,CAAC,0BAA0B,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAEO,MAAM;QACZ,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,gBAAgB;QAC1B,IAAI,GAAG,GAAG,EAAE,CAAC;QACb,SAAS,CAAC;YACR,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;YAC3B,IAAI,CAAC,KAAK,SAAS;gBAAE,MAAM,IAAI,WAAW,CAAC,qBAAqB,CAAC,CAAC;YAClE,IAAI,CAAC,KAAK,GAAG;gBAAE,OAAO,GAAG,CAAC;YAC1B,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;gBACf,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC3B,QAAQ,CAAC,EAAE,CAAC;oBACV,KAAK,GAAG;wBACN,GAAG,IAAI,GAAG,CAAC;wBACX,MAAM;oBACR,KAAK,IAAI;wBACP,GAAG,IAAI,IAAI,CAAC;wBACZ,MAAM;oBACR,KAAK,GAAG;wBACN,GAAG,IAAI,GAAG,CAAC;wBACX,MAAM;oBACR,KAAK,GAAG;wBACN,GAAG,IAAI,IAAI,CAAC;wBACZ,MAAM;oBACR,KAAK,GAAG;wBACN,GAAG,IAAI,IAAI,CAAC;wBACZ,MAAM;oBACR,KAAK,GAAG;wBACN,GAAG,IAAI,IAAI,CAAC;wBACZ,MAAM;oBACR,KAAK,GAAG;wBACN,GAAG,IAAI,IAAI,CAAC;wBACZ,MAAM;oBACR,KAAK,GAAG;wBACN,GAAG,IAAI,IAAI,CAAC;wBACZ,MAAM;oBACR,KAAK,GAAG,CAAC,CAAC,CAAC;wBACT,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;wBAC7C,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;4BAAE,MAAM,IAAI,WAAW,CAAC,gBAAgB,CAAC,CAAC;wBAC9D,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;wBACZ,GAAG,IAAI,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;wBACrD,MAAM;oBACR,CAAC;oBACD;wBACE,MAAM,IAAI,WAAW,CAAC,gBAAgB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBACvD,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,GAAG,IAAI,CAAC,CAAC;YACX,CAAC;QACH,CAAC;IACH,CAAC;IAEO,MAAM;QACZ,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC;QACrB,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;YAAE,IAAI,CAAC,CAAC,EAAE,CAAC;QACrC,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,OAAO,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;YAC9B,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAW,CAAC;YACnC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;gBACzB,QAAQ,GAAG,IAAI,CAAC;gBAChB,IAAI,CAAC,CAAC,EAAE,CAAC;YACX,CAAC;iBAAM,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;gBACzE,IAAI,CAAC,CAAC,EAAE,CAAC;YACX,CAAC;iBAAM,CAAC;gBACN,MAAM;YACR,CAAC;QACH,CAAC;QACD,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,WAAW,CAAC,qBAAqB,KAAK,EAAE,CAAC,CAAC;QACnE,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;CACF;AAED,+FAA+F;AAC/F,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;AAClC,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAW;IACpC,gGAAgG;IAChG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IACnC,iGAAiG;IACjG,uFAAuF;IACvF,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAClB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,CAAC,IAAI,IAAI,CAAC;IAChC,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAC,CAAU;IAC9B,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACjD,IAAI,OAAO,CAAC,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IACxD,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACpD,IAAI,CAAC,YAAY,IAAI;QAAE,OAAO,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACvD,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC;QAC3F,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAC3D,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;QACrB,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnF,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAChC,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAW,CAAC,EAAE,CAAC;YACzC,MAAM,GAAG,GAAI,CAA6B,CAAC,CAAC,CAAC,CAAC;YAC9C,IAAI,GAAG,KAAK,SAAS;gBAAE,SAAS,CAAC,0DAA0D;YAC3F,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnD,CAAC;QACD,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAChC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Thrown when a key is absent from a store (mirrors Python's `KeyError`). */
|
|
2
|
+
export declare class KeyError extends Error {
|
|
3
|
+
constructor(key?: string);
|
|
4
|
+
}
|
|
5
|
+
/** The minimal CCR backend contract: content-addressed `get`/`put`. */
|
|
6
|
+
export interface StoreBackend {
|
|
7
|
+
get(key: string): string;
|
|
8
|
+
put(key: string, value: string): void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* In-process CCR store (the default). Fast, ephemeral, deduped by key.
|
|
12
|
+
*
|
|
13
|
+
* `maxItems` bounds the store with a least-recently-used (LRU) policy: reading a key (`get`) or
|
|
14
|
+
* re-storing it (`put`) refreshes its recency, so a handle you keep expanding survives eviction and
|
|
15
|
+
* only genuinely-cold originals are dropped. Expanding a handle whose original was evicted throws
|
|
16
|
+
* `KeyError` — the documented trade-off of a capped store. `null` (default) means unbounded (no
|
|
17
|
+
* eviction, so recency is not tracked).
|
|
18
|
+
*/
|
|
19
|
+
export declare class MemoryStore implements StoreBackend {
|
|
20
|
+
private readonly data;
|
|
21
|
+
private readonly max;
|
|
22
|
+
constructor(maxItems?: number | null);
|
|
23
|
+
get(key: string): string;
|
|
24
|
+
put(key: string, value: string): void;
|
|
25
|
+
has(key: string): boolean;
|
|
26
|
+
get size(): number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Local SQLite CCR store: originals persist across processes, deduped by key (via `better-sqlite3`,
|
|
30
|
+
* a synchronous, optional dependency — required only when this class is constructed).
|
|
31
|
+
*
|
|
32
|
+
* Writes are idempotent `INSERT OR IGNORE`s (content-addressed), so concurrent puts of the same
|
|
33
|
+
* content are safe. `path` may be a file path or `":memory:"`.
|
|
34
|
+
*/
|
|
35
|
+
export declare class SQLiteStore implements StoreBackend {
|
|
36
|
+
private readonly db;
|
|
37
|
+
constructor(path: string);
|
|
38
|
+
get(key: string): string;
|
|
39
|
+
put(key: string, value: string): void;
|
|
40
|
+
has(key: string): boolean;
|
|
41
|
+
get size(): number;
|
|
42
|
+
close(): void;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAWA,8EAA8E;AAC9E,qBAAa,QAAS,SAAQ,KAAK;gBACrB,GAAG,CAAC,EAAE,MAAM;CAIzB;AAED,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED;;;;;;;;GAQG;AACH,qBAAa,WAAY,YAAW,YAAY;IAC9C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6B;IAClD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgB;gBAExB,QAAQ,GAAE,MAAM,GAAG,IAAW;IAI1C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAWxB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAoBrC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF;AAED;;;;;;GAMG;AACH,qBAAa,WAAY,YAAW,YAAY;IAC9C,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAyB;gBAEhC,IAAI,EAAE,MAAM;IAOxB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAQxB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAIrC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB,IAAI,IAAI,IAAI,MAAM,CAGjB;IAED,KAAK,IAAI,IAAI;CAGd"}
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-addressed store (CCR) backends for squeeze — the TS port of `cendor.squeeze.store`.
|
|
3
|
+
*
|
|
4
|
+
* The original of every compression is kept keyed by its hash so `handle.expand()` is exact.
|
|
5
|
+
* `MemoryStore` (the default) keeps originals in-process; `SQLiteStore` persists them to a local
|
|
6
|
+
* file so they survive the process and dedupe across runs. A backend is any object with
|
|
7
|
+
* `get(key) -> string` and `put(key, value) -> void`; swap one in via `useStore(...)`.
|
|
8
|
+
*/
|
|
9
|
+
import { createRequire } from 'node:module';
|
|
10
|
+
/** Thrown when a key is absent from a store (mirrors Python's `KeyError`). */
|
|
11
|
+
export class KeyError extends Error {
|
|
12
|
+
constructor(key) {
|
|
13
|
+
super(key);
|
|
14
|
+
this.name = 'KeyError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* In-process CCR store (the default). Fast, ephemeral, deduped by key.
|
|
19
|
+
*
|
|
20
|
+
* `maxItems` bounds the store with a least-recently-used (LRU) policy: reading a key (`get`) or
|
|
21
|
+
* re-storing it (`put`) refreshes its recency, so a handle you keep expanding survives eviction and
|
|
22
|
+
* only genuinely-cold originals are dropped. Expanding a handle whose original was evicted throws
|
|
23
|
+
* `KeyError` — the documented trade-off of a capped store. `null` (default) means unbounded (no
|
|
24
|
+
* eviction, so recency is not tracked).
|
|
25
|
+
*/
|
|
26
|
+
export class MemoryStore {
|
|
27
|
+
data = new Map();
|
|
28
|
+
max;
|
|
29
|
+
constructor(maxItems = null) {
|
|
30
|
+
this.max = maxItems;
|
|
31
|
+
}
|
|
32
|
+
get(key) {
|
|
33
|
+
if (!this.data.has(key))
|
|
34
|
+
throw new KeyError(key);
|
|
35
|
+
const value = this.data.get(key);
|
|
36
|
+
if (this.max !== null) {
|
|
37
|
+
// LRU: mark as most-recently-used (delete + reinsert moves it to the tail).
|
|
38
|
+
this.data.delete(key);
|
|
39
|
+
this.data.set(key, value);
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
put(key, value) {
|
|
44
|
+
if (this.data.has(key)) {
|
|
45
|
+
if (this.max !== null) {
|
|
46
|
+
// re-put refreshes recency; content-addressed, so the value is identical anyway.
|
|
47
|
+
const existing = this.data.get(key);
|
|
48
|
+
this.data.delete(key);
|
|
49
|
+
this.data.set(key, existing);
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
this.data.set(key, value);
|
|
54
|
+
if (this.max !== null) {
|
|
55
|
+
while (this.data.size > this.max) {
|
|
56
|
+
// evict the least-recently-used (the front / oldest entry).
|
|
57
|
+
const oldest = this.data.keys().next().value;
|
|
58
|
+
this.data.delete(oldest);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
has(key) {
|
|
63
|
+
return this.data.has(key);
|
|
64
|
+
}
|
|
65
|
+
get size() {
|
|
66
|
+
return this.data.size;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Local SQLite CCR store: originals persist across processes, deduped by key (via `better-sqlite3`,
|
|
71
|
+
* a synchronous, optional dependency — required only when this class is constructed).
|
|
72
|
+
*
|
|
73
|
+
* Writes are idempotent `INSERT OR IGNORE`s (content-addressed), so concurrent puts of the same
|
|
74
|
+
* content are safe. `path` may be a file path or `":memory:"`.
|
|
75
|
+
*/
|
|
76
|
+
export class SQLiteStore {
|
|
77
|
+
db;
|
|
78
|
+
constructor(path) {
|
|
79
|
+
const require = createRequire(import.meta.url);
|
|
80
|
+
const Database = require('better-sqlite3');
|
|
81
|
+
this.db = new Database(path);
|
|
82
|
+
this.db.exec('CREATE TABLE IF NOT EXISTS ccr (key TEXT PRIMARY KEY, value TEXT)');
|
|
83
|
+
}
|
|
84
|
+
get(key) {
|
|
85
|
+
const row = this.db.prepare('SELECT value FROM ccr WHERE key = ?').get(key);
|
|
86
|
+
if (row === undefined)
|
|
87
|
+
throw new KeyError(key);
|
|
88
|
+
return row.value;
|
|
89
|
+
}
|
|
90
|
+
put(key, value) {
|
|
91
|
+
this.db.prepare('INSERT OR IGNORE INTO ccr (key, value) VALUES (?, ?)').run(key, value);
|
|
92
|
+
}
|
|
93
|
+
has(key) {
|
|
94
|
+
return this.db.prepare('SELECT 1 FROM ccr WHERE key = ?').get(key) !== undefined;
|
|
95
|
+
}
|
|
96
|
+
get size() {
|
|
97
|
+
const row = this.db.prepare('SELECT count(*) AS c FROM ccr').get();
|
|
98
|
+
return row.c;
|
|
99
|
+
}
|
|
100
|
+
close() {
|
|
101
|
+
this.db.close();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/dist/text.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python-faithful string helpers used by the compressors. JS's built-ins differ from CPython in ways
|
|
3
|
+
* that matter for a byte-reproducible port: `str.splitlines()` breaks on many boundaries and drops a
|
|
4
|
+
* single trailing terminator's empty line; `str.strip()` uses the Unicode whitespace set. These
|
|
5
|
+
* reproduce that behavior. (Reversibility never depends on them — `expand()` returns the stored
|
|
6
|
+
* original — but detection/splitting shape does.)
|
|
7
|
+
*/
|
|
8
|
+
/** `true` if `ch` (a single char) is Python whitespace. */
|
|
9
|
+
export declare function isPySpace(ch: string): boolean;
|
|
10
|
+
/** `true` if `ch` is an ASCII digit (0-9). */
|
|
11
|
+
export declare function isAsciiDigit(ch: string): boolean;
|
|
12
|
+
/** Python `str.strip()` (no arg) — strip Unicode whitespace from both ends. */
|
|
13
|
+
export declare function pyStrip(s: string): string;
|
|
14
|
+
/** Python `str.lstrip()` (no arg). */
|
|
15
|
+
export declare function pyLStrip(s: string): string;
|
|
16
|
+
/** Python `str.rstrip()` (no arg). */
|
|
17
|
+
export declare function pyRStrip(s: string): string;
|
|
18
|
+
/** Python `str.rstrip(chars)` — strip any trailing char present in `chars`. */
|
|
19
|
+
export declare function rstripChars(s: string, chars: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Python `str.splitlines()` (no `keepends`). Breaks on `\n`, `\r`, `\r\n`, `\v`, `\f`, the file/
|
|
22
|
+
* group/record separators, NEL, and the Unicode line/paragraph separators; does not emit a trailing
|
|
23
|
+
* empty line for a terminal boundary.
|
|
24
|
+
*/
|
|
25
|
+
export declare function splitlines(s: string): string[];
|
|
26
|
+
//# sourceMappingURL=text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../src/text.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA0CH,2DAA2D;AAC3D,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE7C;AAED,8CAA8C;AAC9C,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED,+EAA+E;AAC/E,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAMzC;AAED,sCAAsC;AACtC,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI1C;AAED,sCAAsC;AACtC,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI1C;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI5D;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAiB9C"}
|
package/dist/text.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python-faithful string helpers used by the compressors. JS's built-ins differ from CPython in ways
|
|
3
|
+
* that matter for a byte-reproducible port: `str.splitlines()` breaks on many boundaries and drops a
|
|
4
|
+
* single trailing terminator's empty line; `str.strip()` uses the Unicode whitespace set. These
|
|
5
|
+
* reproduce that behavior. (Reversibility never depends on them — `expand()` returns the stored
|
|
6
|
+
* original — but detection/splitting shape does.)
|
|
7
|
+
*/
|
|
8
|
+
const cc = (cp) => String.fromCharCode(cp);
|
|
9
|
+
// CPython `str.isspace()` set: ASCII controls + NEL/NBSP + Unicode Zs + line/paragraph separators.
|
|
10
|
+
// Built from code points so the source stays pure-ASCII.
|
|
11
|
+
const PY_SPACE = new Set([
|
|
12
|
+
'\t',
|
|
13
|
+
'\n',
|
|
14
|
+
'\v',
|
|
15
|
+
'\f',
|
|
16
|
+
'\r',
|
|
17
|
+
'\x1c',
|
|
18
|
+
'\x1d',
|
|
19
|
+
'\x1e',
|
|
20
|
+
'\x1f',
|
|
21
|
+
' ',
|
|
22
|
+
cc(0x85), // NEL
|
|
23
|
+
cc(0xa0), // NBSP
|
|
24
|
+
cc(0x1680),
|
|
25
|
+
...Array.from({ length: 11 }, (_, k) => cc(0x2000 + k)), // U+2000..U+200A
|
|
26
|
+
cc(0x2028), // LINE SEPARATOR
|
|
27
|
+
cc(0x2029), // PARAGRAPH SEPARATOR
|
|
28
|
+
cc(0x202f), // NARROW NBSP
|
|
29
|
+
cc(0x205f), // MEDIUM MATH SPACE
|
|
30
|
+
cc(0x3000), // IDEOGRAPHIC SPACE
|
|
31
|
+
]);
|
|
32
|
+
// CPython `str.splitlines()` boundaries (besides the `\r\n` pair, handled specially).
|
|
33
|
+
const LINE_BOUNDARIES = new Set([
|
|
34
|
+
'\n',
|
|
35
|
+
'\r',
|
|
36
|
+
'\v',
|
|
37
|
+
'\f',
|
|
38
|
+
'\x1c',
|
|
39
|
+
'\x1d',
|
|
40
|
+
'\x1e',
|
|
41
|
+
cc(0x85), // NEL
|
|
42
|
+
cc(0x2028), // LINE SEPARATOR
|
|
43
|
+
cc(0x2029), // PARAGRAPH SEPARATOR
|
|
44
|
+
]);
|
|
45
|
+
/** `true` if `ch` (a single char) is Python whitespace. */
|
|
46
|
+
export function isPySpace(ch) {
|
|
47
|
+
return PY_SPACE.has(ch);
|
|
48
|
+
}
|
|
49
|
+
/** `true` if `ch` is an ASCII digit (0-9). */
|
|
50
|
+
export function isAsciiDigit(ch) {
|
|
51
|
+
return ch >= '0' && ch <= '9';
|
|
52
|
+
}
|
|
53
|
+
/** Python `str.strip()` (no arg) — strip Unicode whitespace from both ends. */
|
|
54
|
+
export function pyStrip(s) {
|
|
55
|
+
let start = 0;
|
|
56
|
+
let end = s.length;
|
|
57
|
+
while (start < end && PY_SPACE.has(s[start]))
|
|
58
|
+
start++;
|
|
59
|
+
while (end > start && PY_SPACE.has(s[end - 1]))
|
|
60
|
+
end--;
|
|
61
|
+
return s.slice(start, end);
|
|
62
|
+
}
|
|
63
|
+
/** Python `str.lstrip()` (no arg). */
|
|
64
|
+
export function pyLStrip(s) {
|
|
65
|
+
let start = 0;
|
|
66
|
+
while (start < s.length && PY_SPACE.has(s[start]))
|
|
67
|
+
start++;
|
|
68
|
+
return s.slice(start);
|
|
69
|
+
}
|
|
70
|
+
/** Python `str.rstrip()` (no arg). */
|
|
71
|
+
export function pyRStrip(s) {
|
|
72
|
+
let end = s.length;
|
|
73
|
+
while (end > 0 && PY_SPACE.has(s[end - 1]))
|
|
74
|
+
end--;
|
|
75
|
+
return s.slice(0, end);
|
|
76
|
+
}
|
|
77
|
+
/** Python `str.rstrip(chars)` — strip any trailing char present in `chars`. */
|
|
78
|
+
export function rstripChars(s, chars) {
|
|
79
|
+
let end = s.length;
|
|
80
|
+
while (end > 0 && chars.includes(s[end - 1]))
|
|
81
|
+
end--;
|
|
82
|
+
return s.slice(0, end);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Python `str.splitlines()` (no `keepends`). Breaks on `\n`, `\r`, `\r\n`, `\v`, `\f`, the file/
|
|
86
|
+
* group/record separators, NEL, and the Unicode line/paragraph separators; does not emit a trailing
|
|
87
|
+
* empty line for a terminal boundary.
|
|
88
|
+
*/
|
|
89
|
+
export function splitlines(s) {
|
|
90
|
+
const out = [];
|
|
91
|
+
const n = s.length;
|
|
92
|
+
let i = 0;
|
|
93
|
+
let start = 0;
|
|
94
|
+
while (i < n) {
|
|
95
|
+
const c = s[i];
|
|
96
|
+
if (LINE_BOUNDARIES.has(c)) {
|
|
97
|
+
out.push(s.slice(start, i));
|
|
98
|
+
i += c === '\r' && s[i + 1] === '\n' ? 2 : 1;
|
|
99
|
+
start = i;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
i += 1;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (start < n)
|
|
106
|
+
out.push(s.slice(start));
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=text.js.map
|
package/dist/text.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.js","sourceRoot":"","sources":["../src/text.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,EAAE,GAAG,CAAC,EAAU,EAAU,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;AAE3D,mGAAmG;AACnG,yDAAyD;AACzD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAS;IAC/B,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,GAAG;IACH,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM;IAChB,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO;IACjB,EAAE,CAAC,MAAM,CAAC;IACV,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,iBAAiB;IAC1E,EAAE,CAAC,MAAM,CAAC,EAAE,iBAAiB;IAC7B,EAAE,CAAC,MAAM,CAAC,EAAE,sBAAsB;IAClC,EAAE,CAAC,MAAM,CAAC,EAAE,cAAc;IAC1B,EAAE,CAAC,MAAM,CAAC,EAAE,oBAAoB;IAChC,EAAE,CAAC,MAAM,CAAC,EAAE,oBAAoB;CACjC,CAAC,CAAC;AAEH,sFAAsF;AACtF,MAAM,eAAe,GAAG,IAAI,GAAG,CAAS;IACtC,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,MAAM;IACN,MAAM;IACN,MAAM;IACN,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM;IAChB,EAAE,CAAC,MAAM,CAAC,EAAE,iBAAiB;IAC7B,EAAE,CAAC,MAAM,CAAC,EAAE,sBAAsB;CACnC,CAAC,CAAC;AAEH,2DAA2D;AAC3D,MAAM,UAAU,SAAS,CAAC,EAAU;IAClC,OAAO,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAC1B,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,OAAO,EAAE,IAAI,GAAG,IAAI,EAAE,IAAI,GAAG,CAAC;AAChC,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,OAAO,CAAC,CAAS;IAC/B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC;IACnB,OAAO,KAAK,GAAG,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAW,CAAC;QAAE,KAAK,EAAE,CAAC;IAChE,OAAO,GAAG,GAAG,KAAK,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAW,CAAC;QAAE,GAAG,EAAE,CAAC;IAChE,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAC7B,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,QAAQ,CAAC,CAAS;IAChC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,KAAK,GAAG,CAAC,CAAC,MAAM,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAW,CAAC;QAAE,KAAK,EAAE,CAAC;IACrE,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AACxB,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,QAAQ,CAAC,CAAS;IAChC,IAAI,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC;IACnB,OAAO,GAAG,GAAG,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAW,CAAC;QAAE,GAAG,EAAE,CAAC;IAC5D,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,WAAW,CAAC,CAAS,EAAE,KAAa;IAClD,IAAI,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC;IACnB,OAAO,GAAG,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAW,CAAC;QAAE,GAAG,EAAE,CAAC;IAC9D,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IACnB,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAW,CAAC;QACzB,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3B,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;YAC5B,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7C,KAAK,GAAG,CAAC,CAAC;QACZ,CAAC;aAAM,CAAC;YACN,CAAC,IAAI,CAAC,CAAC;QACT,CAAC;IACH,CAAC;IACD,IAAI,KAAK,GAAG,CAAC;QAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IACxC,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cendor/squeeze",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Content-aware, deterministic, reversible context compression. The TypeScript port of cendor.squeeze.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./store": {
|
|
15
|
+
"types": "./dist/store.d.ts",
|
|
16
|
+
"default": "./dist/store.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@cendor/core": "^0.2.0"
|
|
29
|
+
},
|
|
30
|
+
"optionalDependencies": {
|
|
31
|
+
"better-sqlite3": "^12.11.1"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"llm",
|
|
35
|
+
"context",
|
|
36
|
+
"compression",
|
|
37
|
+
"tokens",
|
|
38
|
+
"cendor"
|
|
39
|
+
],
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/cendorhq/cendor-libs-js.git",
|
|
43
|
+
"directory": "packages/squeeze"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"fast-check": "^3.23.2"
|
|
50
|
+
}
|
|
51
|
+
}
|