cursedbelt-server 4.19.1 → 4.19.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.
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* Two ways to get the events, and a consumer needs neither code nor a new Worker for the first:
|
|
16
16
|
*
|
|
17
17
|
* · **`wrangler tail <worker> --format json`** during a bench or a smoke against the live
|
|
18
|
-
* Worker —
|
|
18
|
+
* Worker — pretty-printed objects back to back, parsed with {@link parseTailLines}.
|
|
19
19
|
* · **a Tail Worker** (`tail_consumers` in the producer's wrangler config) whose `tail(events)`
|
|
20
20
|
* hands the array straight to {@link recordTailTraces}.
|
|
21
21
|
*
|
|
@@ -90,8 +90,14 @@ export interface TailRecordResult {
|
|
|
90
90
|
*/
|
|
91
91
|
export declare function recordTailTraces(recorder: CpuRecorder, traces: readonly TailTraceLike[], opts?: RecordTailTracesOpts): TailRecordResult;
|
|
92
92
|
/**
|
|
93
|
-
* `wrangler tail --format json` output → trace events.
|
|
94
|
-
*
|
|
93
|
+
* `wrangler tail --format json` output → trace events.
|
|
94
|
+
*
|
|
95
|
+
* 🔴 That output is PRETTY-PRINTED — one object spread over ~100 lines, objects back to back — not
|
|
96
|
+
* one JSON document per line. Measured 2026-09-23 against the live `collections` Worker: 61 traces
|
|
97
|
+
* in 6,771 lines, and a line-by-line parser (this function's first version) found none of them. So
|
|
98
|
+
* this scans for balanced top-level `{…}` objects, string- and escape-aware, and parses each; it
|
|
99
|
+
* reads JSON-lines output identically. Banner text between objects is skipped, and a truncated
|
|
100
|
+
* object (a tail killed mid-write) is dropped rather than guessed at.
|
|
95
101
|
*/
|
|
96
102
|
export declare function parseTailLines(text: string): TailTraceLike[];
|
|
97
103
|
export interface TailTrafficRate {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* Two ways to get the events, and a consumer needs neither code nor a new Worker for the first:
|
|
16
16
|
*
|
|
17
17
|
* · **`wrangler tail <worker> --format json`** during a bench or a smoke against the live
|
|
18
|
-
* Worker —
|
|
18
|
+
* Worker — pretty-printed objects back to back, parsed with {@link parseTailLines}.
|
|
19
19
|
* · **a Tail Worker** (`tail_consumers` in the producer's wrangler config) whose `tail(events)`
|
|
20
20
|
* hands the array straight to {@link recordTailTraces}.
|
|
21
21
|
*
|
|
@@ -144,22 +144,65 @@ export function recordTailTraces(recorder, traces, opts = {}) {
|
|
|
144
144
|
return result;
|
|
145
145
|
}
|
|
146
146
|
/**
|
|
147
|
-
* `wrangler tail --format json` output → trace events.
|
|
148
|
-
*
|
|
147
|
+
* `wrangler tail --format json` output → trace events.
|
|
148
|
+
*
|
|
149
|
+
* 🔴 That output is PRETTY-PRINTED — one object spread over ~100 lines, objects back to back — not
|
|
150
|
+
* one JSON document per line. Measured 2026-09-23 against the live `collections` Worker: 61 traces
|
|
151
|
+
* in 6,771 lines, and a line-by-line parser (this function's first version) found none of them. So
|
|
152
|
+
* this scans for balanced top-level `{…}` objects, string- and escape-aware, and parses each; it
|
|
153
|
+
* reads JSON-lines output identically. Banner text between objects is skipped, and a truncated
|
|
154
|
+
* object (a tail killed mid-write) is dropped rather than guessed at.
|
|
149
155
|
*/
|
|
150
156
|
export function parseTailLines(text) {
|
|
151
157
|
const out = [];
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
158
|
+
let depth = 0;
|
|
159
|
+
let start = -1;
|
|
160
|
+
let inString = false;
|
|
161
|
+
let escaped = false;
|
|
162
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
163
|
+
const ch = text[i];
|
|
164
|
+
if (inString && ch === '\n') {
|
|
165
|
+
// A raw newline cannot occur inside a JSON string: the object this began is truncated.
|
|
166
|
+
inString = false;
|
|
167
|
+
escaped = false;
|
|
168
|
+
depth = 0;
|
|
169
|
+
start = -1;
|
|
155
170
|
continue;
|
|
156
|
-
try {
|
|
157
|
-
const parsed = JSON.parse(trimmed);
|
|
158
|
-
if (parsed && typeof parsed === 'object')
|
|
159
|
-
out.push(parsed);
|
|
160
171
|
}
|
|
161
|
-
|
|
162
|
-
|
|
172
|
+
if (inString) {
|
|
173
|
+
if (escaped)
|
|
174
|
+
escaped = false;
|
|
175
|
+
else if (ch === '\\')
|
|
176
|
+
escaped = true;
|
|
177
|
+
else if (ch === '"')
|
|
178
|
+
inString = false;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (ch === '"' && depth > 0) {
|
|
182
|
+
inString = true;
|
|
183
|
+
}
|
|
184
|
+
else if (ch === '{') {
|
|
185
|
+
// wrangler writes every top-level object from column 0 and indents everything nested, so a
|
|
186
|
+
// `{` at the start of a line while one is still open means the open one was cut short.
|
|
187
|
+
if (depth > 0 && (i === 0 || text[i - 1] === '\n'))
|
|
188
|
+
depth = 0;
|
|
189
|
+
if (depth === 0)
|
|
190
|
+
start = i;
|
|
191
|
+
depth += 1;
|
|
192
|
+
}
|
|
193
|
+
else if (ch === '}' && depth > 0) {
|
|
194
|
+
depth -= 1;
|
|
195
|
+
if (depth === 0 && start >= 0) {
|
|
196
|
+
try {
|
|
197
|
+
const parsed = JSON.parse(text.slice(start, i + 1));
|
|
198
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
|
|
199
|
+
out.push(parsed);
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// not JSON after all (banner text with braces in it) — not a trace
|
|
203
|
+
}
|
|
204
|
+
start = -1;
|
|
205
|
+
}
|
|
163
206
|
}
|
|
164
207
|
}
|
|
165
208
|
return out;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursedbelt-server",
|
|
3
|
-
"version": "4.19.
|
|
3
|
+
"version": "4.19.2",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The app-facing Bun/Hono server tier of the cursedbelt split — storage, sharing, activity, guard, sync. React-free; cursedbelt-core below it.",
|
|
@@ -117,6 +117,18 @@ describe('parseTailLines and tailTrafficRate', () => {
|
|
|
117
117
|
expect(parseTailLines(text).map((t) => t.cpuTime)).toEqual([1, 2]);
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
+
it('🔴 reads PRETTY-PRINTED objects back to back — what wrangler actually prints', () => {
|
|
121
|
+
// The shape measured against the live `collections` Worker, 2026-09-23: 61 traces over
|
|
122
|
+
// 6,771 lines. The first version of this parser split on newlines and found none.
|
|
123
|
+
const text = [
|
|
124
|
+
'Successfully created tail',
|
|
125
|
+
JSON.stringify({ ...trace('GET', '/healthz', 16, 1), logs: [{ message: ['a } brace', 'q "x"'] }] }, null, 4),
|
|
126
|
+
JSON.stringify(trace('GET', '/api/folders', 3, 2), null, 4),
|
|
127
|
+
JSON.stringify(trace('GET', '/cut', 9, 3), null, 4).slice(0, 40),
|
|
128
|
+
].join('\n');
|
|
129
|
+
expect(parseTailLines(text).map((t) => t.cpuTime)).toEqual([16, 3]);
|
|
130
|
+
});
|
|
131
|
+
|
|
120
132
|
it('scales a window to requests/day — and refuses to scale one shorter than an hour', () => {
|
|
121
133
|
const hour = 3_600_000;
|
|
122
134
|
const two = [trace('GET', '/healthz', 1, 0), trace('GET', '/healthz', 1, 2 * hour), { cpuTime: 5 }];
|
package/src/server/bench/tail.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* Two ways to get the events, and a consumer needs neither code nor a new Worker for the first:
|
|
16
16
|
*
|
|
17
17
|
* · **`wrangler tail <worker> --format json`** during a bench or a smoke against the live
|
|
18
|
-
* Worker —
|
|
18
|
+
* Worker — pretty-printed objects back to back, parsed with {@link parseTailLines}.
|
|
19
19
|
* · **a Tail Worker** (`tail_consumers` in the producer's wrangler config) whose `tail(events)`
|
|
20
20
|
* hands the array straight to {@link recordTailTraces}.
|
|
21
21
|
*
|
|
@@ -192,19 +192,56 @@ export function recordTailTraces(
|
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
/**
|
|
195
|
-
* `wrangler tail --format json` output → trace events.
|
|
196
|
-
*
|
|
195
|
+
* `wrangler tail --format json` output → trace events.
|
|
196
|
+
*
|
|
197
|
+
* 🔴 That output is PRETTY-PRINTED — one object spread over ~100 lines, objects back to back — not
|
|
198
|
+
* one JSON document per line. Measured 2026-09-23 against the live `collections` Worker: 61 traces
|
|
199
|
+
* in 6,771 lines, and a line-by-line parser (this function's first version) found none of them. So
|
|
200
|
+
* this scans for balanced top-level `{…}` objects, string- and escape-aware, and parses each; it
|
|
201
|
+
* reads JSON-lines output identically. Banner text between objects is skipped, and a truncated
|
|
202
|
+
* object (a tail killed mid-write) is dropped rather than guessed at.
|
|
197
203
|
*/
|
|
198
204
|
export function parseTailLines(text: string): TailTraceLike[] {
|
|
199
205
|
const out: TailTraceLike[] = [];
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
//
|
|
206
|
+
let depth = 0;
|
|
207
|
+
let start = -1;
|
|
208
|
+
let inString = false;
|
|
209
|
+
let escaped = false;
|
|
210
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
211
|
+
const ch = text[i];
|
|
212
|
+
if (inString && ch === '\n') {
|
|
213
|
+
// A raw newline cannot occur inside a JSON string: the object this began is truncated.
|
|
214
|
+
inString = false;
|
|
215
|
+
escaped = false;
|
|
216
|
+
depth = 0;
|
|
217
|
+
start = -1;
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (inString) {
|
|
221
|
+
if (escaped) escaped = false;
|
|
222
|
+
else if (ch === '\\') escaped = true;
|
|
223
|
+
else if (ch === '"') inString = false;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (ch === '"' && depth > 0) {
|
|
227
|
+
inString = true;
|
|
228
|
+
} else if (ch === '{') {
|
|
229
|
+
// wrangler writes every top-level object from column 0 and indents everything nested, so a
|
|
230
|
+
// `{` at the start of a line while one is still open means the open one was cut short.
|
|
231
|
+
if (depth > 0 && (i === 0 || text[i - 1] === '\n')) depth = 0;
|
|
232
|
+
if (depth === 0) start = i;
|
|
233
|
+
depth += 1;
|
|
234
|
+
} else if (ch === '}' && depth > 0) {
|
|
235
|
+
depth -= 1;
|
|
236
|
+
if (depth === 0 && start >= 0) {
|
|
237
|
+
try {
|
|
238
|
+
const parsed = JSON.parse(text.slice(start, i + 1)) as unknown;
|
|
239
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) out.push(parsed as TailTraceLike);
|
|
240
|
+
} catch {
|
|
241
|
+
// not JSON after all (banner text with braces in it) — not a trace
|
|
242
|
+
}
|
|
243
|
+
start = -1;
|
|
244
|
+
}
|
|
208
245
|
}
|
|
209
246
|
}
|
|
210
247
|
return out;
|