@tangle-network/agent-core 0.14.0 → 1.0.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.
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-Timing header parse and format (W3C Server Timing; RFC 9110 §5.6.4
|
|
3
|
+
* quoted strings). Pure ECMAScript that imports nothing, so Node, browser and
|
|
4
|
+
* Cloudflare Worker bundles can take this subpath without a dependency.
|
|
5
|
+
*/
|
|
6
|
+
/** One metric of a `Server-Timing` header. */
|
|
7
|
+
export interface ServerTimingEntry {
|
|
8
|
+
name: string;
|
|
9
|
+
/** Milliseconds, only when the header carried a well-formed `dur`. */
|
|
10
|
+
dur?: number;
|
|
11
|
+
/** `desc`, unquoted and unescaped. */
|
|
12
|
+
desc?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Parse a `Server-Timing` header. Never throws. A metric with an invalid name
|
|
16
|
+
* is skipped; a missing or malformed `dur` stays absent, never zero. The first
|
|
17
|
+
* occurrence of a parameter wins, and repeated metrics stay separate entries.
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseServerTiming(header: string | null | undefined): ServerTimingEntry[];
|
|
20
|
+
/** Duration-bearing metrics by name; a later metric of the same name wins. */
|
|
21
|
+
export declare function parseServerTimingDurations(header: string | null | undefined): Record<string, number>;
|
|
22
|
+
/**
|
|
23
|
+
* Format metrics as a `Server-Timing` header value, the inverse of
|
|
24
|
+
* {@link parseServerTiming}. Invalid names are skipped; `dur` rounds to 0.1 ms;
|
|
25
|
+
* `desc` is escaped as an RFC 9110 quoted string. A header carries at most 32
|
|
26
|
+
* metrics and each `desc` at most 120 characters.
|
|
27
|
+
*/
|
|
28
|
+
export declare function formatServerTiming(entries: readonly ServerTimingEntry[]): string;
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/server-timing/index.ts"],"mappings":";;;;;;iBAOiB;EACf;;EAEA;;EAEA;;;;;;;wBAgCc,kBACd,oCACC;;wBAgCa,2BACd,oCACC;;;;;;;wBAca,mBACd,kBAAkB"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// src/server-timing/index.ts
|
|
2
|
+
const TOKEN = /^[!#$%&'*+.^_`|~\w-]+$/;
|
|
3
|
+
const NUMBER = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i;
|
|
4
|
+
const QUOTED = /^"(?:[^"\\]|\\[\s\S])*"$/;
|
|
5
|
+
const MAX_FORMAT_ENTRIES = 32;
|
|
6
|
+
const MAX_FORMAT_DESC = 120;
|
|
7
|
+
/** Split on `separator` outside quoted strings, skipping backslash-escaped characters inside them. */
|
|
8
|
+
function split(value, separator) {
|
|
9
|
+
const parts = [];
|
|
10
|
+
let start = 0;
|
|
11
|
+
let quoted = false;
|
|
12
|
+
for (let i = 0; i < value.length; i++) {
|
|
13
|
+
const char = value[i];
|
|
14
|
+
if (quoted && char === "\\") i++;
|
|
15
|
+
else if (char === "\"") quoted = !quoted;
|
|
16
|
+
else if (!quoted && char === separator) {
|
|
17
|
+
parts.push(value.slice(start, i));
|
|
18
|
+
start = i + 1;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
parts.push(value.slice(start));
|
|
22
|
+
return parts;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Parse a `Server-Timing` header. Never throws. A metric with an invalid name
|
|
26
|
+
* is skipped; a missing or malformed `dur` stays absent, never zero. The first
|
|
27
|
+
* occurrence of a parameter wins, and repeated metrics stay separate entries.
|
|
28
|
+
*/
|
|
29
|
+
function parseServerTiming(header) {
|
|
30
|
+
if (!header) return [];
|
|
31
|
+
const entries = [];
|
|
32
|
+
for (const metric of split(header, ",")) {
|
|
33
|
+
const [rawName, ...params] = split(metric, ";");
|
|
34
|
+
const name = (rawName ?? "").trim();
|
|
35
|
+
if (!TOKEN.test(name)) continue;
|
|
36
|
+
const entry = { name };
|
|
37
|
+
const seen = /* @__PURE__ */ new Set();
|
|
38
|
+
for (const param of params) {
|
|
39
|
+
const eq = param.indexOf("=");
|
|
40
|
+
const key = (eq < 0 ? param : param.slice(0, eq)).trim().toLowerCase();
|
|
41
|
+
if (seen.has(key)) continue;
|
|
42
|
+
seen.add(key);
|
|
43
|
+
if (eq < 0) continue;
|
|
44
|
+
let value = param.slice(eq + 1).trim();
|
|
45
|
+
if (value.startsWith("\"")) {
|
|
46
|
+
if (!QUOTED.test(value)) continue;
|
|
47
|
+
value = value.slice(1, -1).replace(/\\([\s\S])/g, "$1");
|
|
48
|
+
}
|
|
49
|
+
if (key === "desc") entry.desc = value;
|
|
50
|
+
else if (key === "dur" && NUMBER.test(value)) {
|
|
51
|
+
const dur = Number(value);
|
|
52
|
+
if (Number.isFinite(dur)) entry.dur = dur;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
entries.push(entry);
|
|
56
|
+
}
|
|
57
|
+
return entries;
|
|
58
|
+
}
|
|
59
|
+
/** Duration-bearing metrics by name; a later metric of the same name wins. */
|
|
60
|
+
function parseServerTimingDurations(header) {
|
|
61
|
+
return Object.fromEntries(parseServerTiming(header).flatMap(({ name, dur }) => dur === void 0 ? [] : [[name, dur]]));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Format metrics as a `Server-Timing` header value, the inverse of
|
|
65
|
+
* {@link parseServerTiming}. Invalid names are skipped; `dur` rounds to 0.1 ms;
|
|
66
|
+
* `desc` is escaped as an RFC 9110 quoted string. A header carries at most 32
|
|
67
|
+
* metrics and each `desc` at most 120 characters.
|
|
68
|
+
*/
|
|
69
|
+
function formatServerTiming(entries) {
|
|
70
|
+
return entries.filter((entry) => TOKEN.test(entry.name)).slice(0, MAX_FORMAT_ENTRIES).map((entry) => [
|
|
71
|
+
entry.name,
|
|
72
|
+
...entry.dur === void 0 ? [] : [`dur=${Math.round(entry.dur * 10) / 10}`],
|
|
73
|
+
...entry.desc === void 0 ? [] : [`desc="${entry.desc.slice(0, MAX_FORMAT_DESC).replace(/["\\]/g, "\\$&")}"`]
|
|
74
|
+
].join(";")).join(", ");
|
|
75
|
+
}
|
|
76
|
+
export { formatServerTiming, parseServerTiming, parseServerTimingDurations };
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/server-timing/index.ts"],"sourcesContent":["/**\n * Server-Timing header parse and format (W3C Server Timing; RFC 9110 §5.6.4\n * quoted strings). Pure ECMAScript that imports nothing, so Node, browser and\n * Cloudflare Worker bundles can take this subpath without a dependency.\n */\n\n/** One metric of a `Server-Timing` header. */\nexport interface ServerTimingEntry {\n name: string;\n /** Milliseconds, only when the header carried a well-formed `dur`. */\n dur?: number;\n /** `desc`, unquoted and unescaped. */\n desc?: string;\n}\n\nconst TOKEN = /^[!#$%&'*+.^_`|~\\w-]+$/;\nconst NUMBER = /^[+-]?(?:\\d+\\.?\\d*|\\.\\d+)(?:e[+-]?\\d+)?$/i;\nconst QUOTED = /^\"(?:[^\"\\\\]|\\\\[\\s\\S])*\"$/;\nconst MAX_FORMAT_ENTRIES = 32;\nconst MAX_FORMAT_DESC = 120;\n\n/** Split on `separator` outside quoted strings, skipping backslash-escaped characters inside them. */\nfunction split(value: string, separator: string): string[] {\n const parts: string[] = [];\n let start = 0;\n let quoted = false;\n for (let i = 0; i < value.length; i++) {\n const char = value[i];\n if (quoted && char === \"\\\\\") i++;\n else if (char === '\"') quoted = !quoted;\n else if (!quoted && char === separator) {\n parts.push(value.slice(start, i));\n start = i + 1;\n }\n }\n parts.push(value.slice(start));\n return parts;\n}\n\n/**\n * Parse a `Server-Timing` header. Never throws. A metric with an invalid name\n * is skipped; a missing or malformed `dur` stays absent, never zero. The first\n * occurrence of a parameter wins, and repeated metrics stay separate entries.\n */\nexport function parseServerTiming(\n header: string | null | undefined,\n): ServerTimingEntry[] {\n if (!header) return [];\n const entries: ServerTimingEntry[] = [];\n for (const metric of split(header, \",\")) {\n const [rawName, ...params] = split(metric, \";\");\n const name = (rawName ?? \"\").trim();\n if (!TOKEN.test(name)) continue;\n const entry: ServerTimingEntry = { name };\n const seen = new Set<string>();\n for (const param of params) {\n const eq = param.indexOf(\"=\");\n const key = (eq < 0 ? param : param.slice(0, eq)).trim().toLowerCase();\n if (seen.has(key)) continue;\n seen.add(key);\n if (eq < 0) continue;\n let value = param.slice(eq + 1).trim();\n if (value.startsWith('\"')) {\n if (!QUOTED.test(value)) continue;\n value = value.slice(1, -1).replace(/\\\\([\\s\\S])/g, \"$1\");\n }\n if (key === \"desc\") entry.desc = value;\n else if (key === \"dur\" && NUMBER.test(value)) {\n const dur = Number(value);\n if (Number.isFinite(dur)) entry.dur = dur;\n }\n }\n entries.push(entry);\n }\n return entries;\n}\n\n/** Duration-bearing metrics by name; a later metric of the same name wins. */\nexport function parseServerTimingDurations(\n header: string | null | undefined,\n): Record<string, number> {\n return Object.fromEntries(\n parseServerTiming(header).flatMap(({ name, dur }) =>\n dur === undefined ? [] : [[name, dur] as const],\n ),\n );\n}\n\n/**\n * Format metrics as a `Server-Timing` header value, the inverse of\n * {@link parseServerTiming}. Invalid names are skipped; `dur` rounds to 0.1 ms;\n * `desc` is escaped as an RFC 9110 quoted string. A header carries at most 32\n * metrics and each `desc` at most 120 characters.\n */\nexport function formatServerTiming(\n entries: readonly ServerTimingEntry[],\n): string {\n return entries\n .filter((entry) => TOKEN.test(entry.name))\n .slice(0, MAX_FORMAT_ENTRIES)\n .map((entry) =>\n [\n entry.name,\n ...(entry.dur === undefined\n ? []\n : [`dur=${Math.round(entry.dur * 10) / 10}`]),\n ...(entry.desc === undefined\n ? []\n : [\n `desc=\"${entry.desc.slice(0, MAX_FORMAT_DESC).replace(/[\"\\\\]/g, \"\\\\$&\")}\"`,\n ]),\n ].join(\";\"),\n )\n .join(\", \");\n}\n"],"mappings":";AAeA,MAAM,QAAQ;AACd,MAAM,SAAS;AACf,MAAM,SAAS;AACf,MAAM,qBAAqB;AAC3B,MAAM,kBAAkB;;AAGxB,SAAS,MAAM,OAAe,WAA6B;CACzD,MAAM,QAAkB,CAAC;CACzB,IAAI,QAAQ;CACZ,IAAI,SAAS;CACb,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,OAAO,MAAM;EACnB,IAAI,UAAU,SAAS,MAAM;OACxB,IAAI,SAAS,MAAK,SAAS,CAAC;OAC5B,IAAI,CAAC,UAAU,SAAS,WAAW;GACtC,MAAM,KAAK,MAAM,MAAM,OAAO,CAAC,CAAC;GAChC,QAAQ,IAAI;EACd;CACF;CACA,MAAM,KAAK,MAAM,MAAM,KAAK,CAAC;CAC7B,OAAO;AACT;;;;;;AAOA,SAAgB,kBACd,QACqB;CACrB,IAAI,CAAC,QAAQ,OAAO,CAAC;CACrB,MAAM,UAA+B,CAAC;CACtC,KAAK,MAAM,UAAU,MAAM,QAAQ,GAAG,GAAG;EACvC,MAAM,CAAC,SAAS,GAAG,UAAU,MAAM,QAAQ,GAAG;EAC9C,MAAM,QAAQ,WAAW,GAAA,CAAI,KAAK;EAClC,IAAI,CAAC,MAAM,KAAK,IAAI,GAAG;EACvB,MAAM,QAA2B,EAAE,KAAK;EACxC,MAAM,uBAAO,IAAI,IAAY;EAC7B,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,KAAK,MAAM,QAAQ,GAAG;GAC5B,MAAM,OAAO,KAAK,IAAI,QAAQ,MAAM,MAAM,GAAG,EAAE,EAAA,CAAG,KAAK,CAAC,CAAC,YAAY;GACrE,IAAI,KAAK,IAAI,GAAG,GAAG;GACnB,KAAK,IAAI,GAAG;GACZ,IAAI,KAAK,GAAG;GACZ,IAAI,QAAQ,MAAM,MAAM,KAAK,CAAC,CAAC,CAAC,KAAK;GACrC,IAAI,MAAM,WAAW,IAAG,GAAG;IACzB,IAAI,CAAC,OAAO,KAAK,KAAK,GAAG;IACzB,QAAQ,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,QAAQ,eAAe,IAAI;GACxD;GACA,IAAI,QAAQ,QAAQ,MAAM,OAAO;QAC5B,IAAI,QAAQ,SAAS,OAAO,KAAK,KAAK,GAAG;IAC5C,MAAM,MAAM,OAAO,KAAK;IACxB,IAAI,OAAO,SAAS,GAAG,GAAG,MAAM,MAAM;GACxC;EACF;EACA,QAAQ,KAAK,KAAK;CACpB;CACA,OAAO;AACT;;AAGA,SAAgB,2BACd,QACwB;CACxB,OAAO,OAAO,YACZ,kBAAkB,MAAM,CAAC,CAAC,SAAS,EAAE,MAAM,UACzC,QAAQ,KAAA,IAAY,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAU,CAChD,CACF;AACF;;;;;;;AAQA,SAAgB,mBACd,SACQ;CACR,OAAO,QACJ,QAAQ,UAAU,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,CACzC,MAAM,GAAG,kBAAkB,CAAC,CAC5B,KAAK,UACJ;EACE,MAAM;EACN,GAAI,MAAM,QAAQ,KAAA,IACd,CAAC,IACD,CAAC,OAAO,KAAK,MAAM,MAAM,MAAM,EAAE,IAAI,IAAI;EAC7C,GAAI,MAAM,SAAS,KAAA,IACf,CAAC,IACD,CACE,SAAS,MAAM,KAAK,MAAM,GAAG,eAAe,CAAC,CAAC,QAAQ,UAAU,MAAM,EAAE,EAC1E;CACN,CAAC,CAAC,KAAK,GAAG,CACZ,CAAC,CACA,KAAK,IAAI;AACd"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Core transport interfaces, event utilities, auth, SSE parsing, and resilience primitives for talking to Tangle agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -57,6 +57,11 @@
|
|
|
57
57
|
"types": "./dist/telemetry/index.d.ts",
|
|
58
58
|
"default": "./dist/telemetry/index.js"
|
|
59
59
|
},
|
|
60
|
+
"./server-timing": {
|
|
61
|
+
"import": "./dist/server-timing/index.js",
|
|
62
|
+
"types": "./dist/server-timing/index.d.ts",
|
|
63
|
+
"default": "./dist/server-timing/index.js"
|
|
64
|
+
},
|
|
60
65
|
"./interactions": {
|
|
61
66
|
"import": "./dist/interactions/index.js",
|
|
62
67
|
"types": "./dist/interactions/index.d.ts",
|
|
@@ -114,6 +119,9 @@
|
|
|
114
119
|
"types/*": [
|
|
115
120
|
"src/types/*"
|
|
116
121
|
],
|
|
122
|
+
"server-timing": [
|
|
123
|
+
"src/server-timing/index.ts"
|
|
124
|
+
],
|
|
117
125
|
"interactions": [
|
|
118
126
|
"src/interactions/index.ts"
|
|
119
127
|
],
|