@tinycloud/sdk-services 3.3.1 → 3.5.0-beta.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,127 @@
1
+ import { I as IServiceContext } from '../types-CJylea1R.cjs';
2
+
3
+ /**
4
+ * Developer request log core. Events are sanitized by `buildRequestEvent`, the
5
+ * only constructor, which copies an allowlist of fields. Values, headers,
6
+ * bodies, tokens, delegations, and error text have no field to land in.
7
+ */
8
+
9
+ type RequestService = "kv" | "sql" | "duckdb" | "hooks" | "vault" | "space" | "sync" | "other";
10
+ interface RequestEvent {
11
+ v: 1;
12
+ /** Monotonic per RequestLog. */
13
+ id: number;
14
+ /** Request start, ms since epoch. */
15
+ at: number;
16
+ service: RequestService;
17
+ /** `service/action`, e.g. `kv/get`; `other` when unknown. */
18
+ op: string;
19
+ /** `first/` of the key (`[redacted]` when unsafe or slashless); full key only for allowlisted first segments. */
20
+ path: string | null;
21
+ /** Tail label of the space id, never the id. */
22
+ space: string | null;
23
+ /** URL host[:port] only. */
24
+ host: string | null;
25
+ /** Body bytes for writes, response bytes for reads; null when unknown. */
26
+ bytes: number | null;
27
+ /** Whole logical request, including retry backoff; null when unknown. */
28
+ durationMs: number | null;
29
+ attempts: number;
30
+ ok: boolean;
31
+ code: string;
32
+ source: "network" | "replica";
33
+ }
34
+ /** A sanitized event before the log assigns its id. */
35
+ type RequestDraft = Omit<RequestEvent, "id">;
36
+ interface RequestLogReader {
37
+ /** Copy, oldest first. */
38
+ snapshot(): readonly RequestEvent[];
39
+ subscribe(cb: (event: RequestEvent) => void): () => void;
40
+ onClear(cb: () => void): () => void;
41
+ }
42
+ /**
43
+ * The fetch layer's only view of the developer request log. A context without a
44
+ * sink never links the sanitizer or the invocation-meta store.
45
+ */
46
+ interface RequestSink {
47
+ record(event: RequestDraft): void;
48
+ /** Derive and record one event from one transport attempt's raw facts. */
49
+ observe(observation: FetchObservation): void;
50
+ /** Attach service, path, and action to headers built for an invocation. */
51
+ remember(headers: object, meta: InvocationMeta): void;
52
+ }
53
+ /** Transport facts for one attempt, read by `observeFetch`. */
54
+ interface FetchObservation {
55
+ url: string;
56
+ requestHeaders: unknown;
57
+ requestBody: unknown;
58
+ responseContentLength: string | null | undefined;
59
+ ok: boolean;
60
+ code: string;
61
+ durationMs: number;
62
+ attempts: number;
63
+ at: number;
64
+ }
65
+ /** Raw facts about one logical request. Only `buildRequestEvent` reads them. */
66
+ interface RawRequest {
67
+ service?: string;
68
+ op?: string;
69
+ /** Unscrubbed key path. */
70
+ path?: string | null;
71
+ /** Full space id; only its tail label is kept. */
72
+ spaceId?: string | null;
73
+ url?: string | null;
74
+ bytes?: number | null;
75
+ durationMs: number | null;
76
+ attempts?: number;
77
+ ok: boolean;
78
+ code?: string;
79
+ source?: string;
80
+ at: number;
81
+ }
82
+ /** UTF-8 byte length of a request body without consuming it; null when unknown. */
83
+ declare function byteLengthOf(body: unknown): number | null;
84
+ /** Response Content-Length as bytes; null unless the header is plain digits (blank, signed, or exponent forms are unknown). */
85
+ declare function declaredLengthOf(header: string | null | undefined): number | null;
86
+ /** What the signing path knows about a request; read back by the fetch layer. */
87
+ interface InvocationMeta {
88
+ service: string;
89
+ path: string;
90
+ action: string;
91
+ spaceId?: string | null;
92
+ }
93
+ declare function invocationMetaOf(headers: unknown): InvocationMeta | undefined;
94
+ declare function buildRequestEvent(raw: RawRequest): RequestDraft;
95
+ /**
96
+ * Derives one event from one transport attempt. Invocation metadata comes from the
97
+ * request headers. Bytes are the request body length when a body was sent (null for
98
+ * streams); with no request body, the declared response length.
99
+ */
100
+ declare function observeFetch(observation: FetchObservation, emit: (event: RequestDraft) => void): void;
101
+ /**
102
+ * Request bytes: the body length when a body was sent (null when it cannot be measured
103
+ * without consuming it); with no body, the declared response Content-Length.
104
+ */
105
+ declare function requestBytesOf(body: unknown, responseContentLength: string | null | undefined): number | null;
106
+ /**
107
+ * Maps replica-served KV reads (`replication.read` with `source: "replica"`) to
108
+ * request events. Network reads and replication writes are not mapped here:
109
+ * their wire requests are recorded by the fetch layer.
110
+ */
111
+ declare function observeReplicaReads(context: IServiceContext, sink: RequestSink): () => void;
112
+ /** Fixed-size in-memory ring of sanitized events. Lost on reload by design (v1). */
113
+ declare class RequestLog implements RequestSink, RequestLogReader {
114
+ #private;
115
+ record(draft: RequestDraft): void;
116
+ /** Called only when a sink is set, so developer-off code never writes here. */
117
+ remember(headers: object, meta: InvocationMeta): void;
118
+ observe(observation: FetchObservation): void;
119
+ snapshot(): RequestEvent[];
120
+ subscribe(cb: (event: RequestEvent) => void): () => void;
121
+ onClear(cb: () => void): () => void;
122
+ clear(): void;
123
+ }
124
+ /** The public view of a log: reads and subscriptions only. `record` and `clear` stay with the owner. */
125
+ declare function readOnlyRequestLog(log: RequestLog): RequestLogReader;
126
+
127
+ export { type FetchObservation, type InvocationMeta, type RawRequest, type RequestDraft, type RequestEvent, RequestLog, type RequestLogReader, type RequestService, type RequestSink, buildRequestEvent, byteLengthOf, declaredLengthOf, invocationMetaOf, observeFetch, observeReplicaReads, readOnlyRequestLog, requestBytesOf };
@@ -0,0 +1,127 @@
1
+ import { I as IServiceContext } from '../types-CJylea1R.js';
2
+
3
+ /**
4
+ * Developer request log core. Events are sanitized by `buildRequestEvent`, the
5
+ * only constructor, which copies an allowlist of fields. Values, headers,
6
+ * bodies, tokens, delegations, and error text have no field to land in.
7
+ */
8
+
9
+ type RequestService = "kv" | "sql" | "duckdb" | "hooks" | "vault" | "space" | "sync" | "other";
10
+ interface RequestEvent {
11
+ v: 1;
12
+ /** Monotonic per RequestLog. */
13
+ id: number;
14
+ /** Request start, ms since epoch. */
15
+ at: number;
16
+ service: RequestService;
17
+ /** `service/action`, e.g. `kv/get`; `other` when unknown. */
18
+ op: string;
19
+ /** `first/` of the key (`[redacted]` when unsafe or slashless); full key only for allowlisted first segments. */
20
+ path: string | null;
21
+ /** Tail label of the space id, never the id. */
22
+ space: string | null;
23
+ /** URL host[:port] only. */
24
+ host: string | null;
25
+ /** Body bytes for writes, response bytes for reads; null when unknown. */
26
+ bytes: number | null;
27
+ /** Whole logical request, including retry backoff; null when unknown. */
28
+ durationMs: number | null;
29
+ attempts: number;
30
+ ok: boolean;
31
+ code: string;
32
+ source: "network" | "replica";
33
+ }
34
+ /** A sanitized event before the log assigns its id. */
35
+ type RequestDraft = Omit<RequestEvent, "id">;
36
+ interface RequestLogReader {
37
+ /** Copy, oldest first. */
38
+ snapshot(): readonly RequestEvent[];
39
+ subscribe(cb: (event: RequestEvent) => void): () => void;
40
+ onClear(cb: () => void): () => void;
41
+ }
42
+ /**
43
+ * The fetch layer's only view of the developer request log. A context without a
44
+ * sink never links the sanitizer or the invocation-meta store.
45
+ */
46
+ interface RequestSink {
47
+ record(event: RequestDraft): void;
48
+ /** Derive and record one event from one transport attempt's raw facts. */
49
+ observe(observation: FetchObservation): void;
50
+ /** Attach service, path, and action to headers built for an invocation. */
51
+ remember(headers: object, meta: InvocationMeta): void;
52
+ }
53
+ /** Transport facts for one attempt, read by `observeFetch`. */
54
+ interface FetchObservation {
55
+ url: string;
56
+ requestHeaders: unknown;
57
+ requestBody: unknown;
58
+ responseContentLength: string | null | undefined;
59
+ ok: boolean;
60
+ code: string;
61
+ durationMs: number;
62
+ attempts: number;
63
+ at: number;
64
+ }
65
+ /** Raw facts about one logical request. Only `buildRequestEvent` reads them. */
66
+ interface RawRequest {
67
+ service?: string;
68
+ op?: string;
69
+ /** Unscrubbed key path. */
70
+ path?: string | null;
71
+ /** Full space id; only its tail label is kept. */
72
+ spaceId?: string | null;
73
+ url?: string | null;
74
+ bytes?: number | null;
75
+ durationMs: number | null;
76
+ attempts?: number;
77
+ ok: boolean;
78
+ code?: string;
79
+ source?: string;
80
+ at: number;
81
+ }
82
+ /** UTF-8 byte length of a request body without consuming it; null when unknown. */
83
+ declare function byteLengthOf(body: unknown): number | null;
84
+ /** Response Content-Length as bytes; null unless the header is plain digits (blank, signed, or exponent forms are unknown). */
85
+ declare function declaredLengthOf(header: string | null | undefined): number | null;
86
+ /** What the signing path knows about a request; read back by the fetch layer. */
87
+ interface InvocationMeta {
88
+ service: string;
89
+ path: string;
90
+ action: string;
91
+ spaceId?: string | null;
92
+ }
93
+ declare function invocationMetaOf(headers: unknown): InvocationMeta | undefined;
94
+ declare function buildRequestEvent(raw: RawRequest): RequestDraft;
95
+ /**
96
+ * Derives one event from one transport attempt. Invocation metadata comes from the
97
+ * request headers. Bytes are the request body length when a body was sent (null for
98
+ * streams); with no request body, the declared response length.
99
+ */
100
+ declare function observeFetch(observation: FetchObservation, emit: (event: RequestDraft) => void): void;
101
+ /**
102
+ * Request bytes: the body length when a body was sent (null when it cannot be measured
103
+ * without consuming it); with no body, the declared response Content-Length.
104
+ */
105
+ declare function requestBytesOf(body: unknown, responseContentLength: string | null | undefined): number | null;
106
+ /**
107
+ * Maps replica-served KV reads (`replication.read` with `source: "replica"`) to
108
+ * request events. Network reads and replication writes are not mapped here:
109
+ * their wire requests are recorded by the fetch layer.
110
+ */
111
+ declare function observeReplicaReads(context: IServiceContext, sink: RequestSink): () => void;
112
+ /** Fixed-size in-memory ring of sanitized events. Lost on reload by design (v1). */
113
+ declare class RequestLog implements RequestSink, RequestLogReader {
114
+ #private;
115
+ record(draft: RequestDraft): void;
116
+ /** Called only when a sink is set, so developer-off code never writes here. */
117
+ remember(headers: object, meta: InvocationMeta): void;
118
+ observe(observation: FetchObservation): void;
119
+ snapshot(): RequestEvent[];
120
+ subscribe(cb: (event: RequestEvent) => void): () => void;
121
+ onClear(cb: () => void): () => void;
122
+ clear(): void;
123
+ }
124
+ /** The public view of a log: reads and subscriptions only. `record` and `clear` stay with the owner. */
125
+ declare function readOnlyRequestLog(log: RequestLog): RequestLogReader;
126
+
127
+ export { type FetchObservation, type InvocationMeta, type RawRequest, type RequestDraft, type RequestEvent, RequestLog, type RequestLogReader, type RequestService, type RequestSink, buildRequestEvent, byteLengthOf, declaredLengthOf, invocationMetaOf, observeFetch, observeReplicaReads, readOnlyRequestLog, requestBytesOf };
@@ -0,0 +1,316 @@
1
+ var __typeError = (msg) => {
2
+ throw TypeError(msg);
3
+ };
4
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
5
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
6
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
7
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
8
+ var __privateWrapper = (obj, member, setter, getter) => ({
9
+ set _(value) {
10
+ __privateSet(obj, member, value, setter);
11
+ },
12
+ get _() {
13
+ return __privateGet(obj, member, getter);
14
+ }
15
+ });
16
+
17
+ // src/types.ts
18
+ var ErrorCodes = {
19
+ // Common errors
20
+ NOT_FOUND: "NOT_FOUND",
21
+ AUTH_EXPIRED: "AUTH_EXPIRED",
22
+ AUTH_REQUIRED: "AUTH_REQUIRED",
23
+ AUTH_UNAUTHORIZED: "AUTH_UNAUTHORIZED",
24
+ /** HTTP 401: the delegation the invocation cites was revoked. */
25
+ AUTH_DELEGATION_REVOKED: "AUTH_DELEGATION_REVOKED",
26
+ /** HTTP 401: a delegation the cited one rests on was revoked. */
27
+ AUTH_DELEGATION_ANCESTOR_REVOKED: "AUTH_DELEGATION_ANCESTOR_REVOKED",
28
+ NETWORK_ERROR: "NETWORK_ERROR",
29
+ TIMEOUT: "TIMEOUT",
30
+ ABORTED: "ABORTED",
31
+ INVALID_INPUT: "INVALID_INPUT",
32
+ PERMISSION_DENIED: "PERMISSION_DENIED",
33
+ // Node retry signals (tinycloud-node #347). `meta.retryable` says whether a
34
+ // retry is safe; `meta.retryAfterMs` carries the node's Retry-After.
35
+ /** HTTP 503 `sql_busy:<stage>` / `unavailable_retryable`/`read_audit_busy`: nothing was applied; retry after `meta.retryAfterMs`. */
36
+ NODE_BUSY: "NODE_BUSY",
37
+ /** HTTP 503 `request_timeout`: the node's deadline passed before any write began; nothing was applied. */
38
+ REQUEST_TIMEOUT: "REQUEST_TIMEOUT",
39
+ /** HTTP 500 `outcome_unknown`: a write may have been applied; read back before retrying a non-idempotent operation. */
40
+ OUTCOME_UNKNOWN: "OUTCOME_UNKNOWN",
41
+ // KV-specific errors
42
+ KV_NOT_FOUND: "KV_NOT_FOUND",
43
+ KV_WRITE_FAILED: "KV_WRITE_FAILED",
44
+ KV_PRECONDITION_FAILED: "KV_PRECONDITION_FAILED",
45
+ KV_CONFLICT: "KV_CONFLICT",
46
+ KV_RESPONSE_TOO_LARGE: "KV_RESPONSE_TOO_LARGE",
47
+ /** HTTP 410 from the change feed: discard sync state and restart without a cursor; `meta.reason` says why. */
48
+ KV_SYNC_RESET_REQUIRED: "KV_SYNC_RESET_REQUIRED",
49
+ /** HTTP 403 from the change feed: the retention grant was refused; `meta.reason` says why. */
50
+ KV_RETENTION_GRANT_REFUSED: "KV_RETENTION_GRANT_REFUSED",
51
+ // SQL-specific errors
52
+ SQL_ERROR: "SQL_ERROR",
53
+ SQL_PERMISSION_DENIED: "SQL_PERMISSION_DENIED",
54
+ SQL_DATABASE_NOT_FOUND: "SQL_DATABASE_NOT_FOUND",
55
+ SQL_RESPONSE_TOO_LARGE: "SQL_RESPONSE_TOO_LARGE",
56
+ SQL_QUOTA_EXCEEDED: "SQL_QUOTA_EXCEEDED",
57
+ SQL_INVALID_STATEMENT: "SQL_INVALID_STATEMENT",
58
+ SQL_SCHEMA_ERROR: "SQL_SCHEMA_ERROR",
59
+ SQL_READONLY_VIOLATION: "SQL_READONLY_VIOLATION",
60
+ // Storage quota errors
61
+ STORAGE_QUOTA_EXCEEDED: "STORAGE_QUOTA_EXCEEDED",
62
+ STORAGE_LIMIT_REACHED: "STORAGE_LIMIT_REACHED",
63
+ /** The node answered the usage read without usage numbers (it predates the read). */
64
+ STORAGE_STATUS_UNAVAILABLE: "STORAGE_STATUS_UNAVAILABLE",
65
+ // DuckDB-specific errors
66
+ DUCKDB_ERROR: "DUCKDB_ERROR",
67
+ DUCKDB_PERMISSION_DENIED: "DUCKDB_PERMISSION_DENIED",
68
+ DUCKDB_DATABASE_NOT_FOUND: "DUCKDB_DATABASE_NOT_FOUND",
69
+ DUCKDB_RESPONSE_TOO_LARGE: "DUCKDB_RESPONSE_TOO_LARGE",
70
+ DUCKDB_QUOTA_EXCEEDED: "DUCKDB_QUOTA_EXCEEDED",
71
+ DUCKDB_INVALID_STATEMENT: "DUCKDB_INVALID_STATEMENT",
72
+ DUCKDB_SCHEMA_ERROR: "DUCKDB_SCHEMA_ERROR",
73
+ DUCKDB_READONLY_VIOLATION: "DUCKDB_READONLY_VIOLATION"
74
+ };
75
+ var defaultRetryPolicy = {
76
+ maxAttempts: 4,
77
+ backoff: "exponential",
78
+ baseDelayMs: 250,
79
+ maxDelayMs: 4e3,
80
+ retryableErrors: [ErrorCodes.NODE_BUSY, ErrorCodes.REQUEST_TIMEOUT],
81
+ maxTotalMs: 12e3
82
+ };
83
+ var TelemetryEvents = {
84
+ SPAN: "telemetry.span",
85
+ SERVICE_REQUEST: "service.request",
86
+ SERVICE_RESPONSE: "service.response",
87
+ SERVICE_ERROR: "service.error",
88
+ SERVICE_RETRY: "service.retry",
89
+ SESSION_CHANGED: "session.changed",
90
+ SESSION_EXPIRED: "session.expired",
91
+ REPLICATION_READ: "replication.read",
92
+ REPLICATION_WRITE: "replication.write",
93
+ REPLICATION_SYNC: "replication.sync",
94
+ REPLICATION_STATE: "replication.state",
95
+ REPLICATION_DIVERGENCE: "replication.divergence",
96
+ REPLICATION_GRANT: "replication.grant",
97
+ REPLICATION_LIFECYCLE: "replication.lifecycle",
98
+ /** Emitted once, on the first write rejected because storage is full. */
99
+ STORAGE_FULL: "storage.full"
100
+ };
101
+
102
+ // src/telemetry/requestLog.ts
103
+ var RING_SIZE = 500;
104
+ var MAX_PATH = 256;
105
+ var REDACTED = "[redacted]";
106
+ var SERVICE_NAME = /^(kv|sql|duckdb|hooks|vault|space|sync)$/;
107
+ var OP_SHAPE = /^[a-z]+\/[A-Za-z]+$/;
108
+ var SPACE_LABEL = /^[a-z0-9][a-z0-9_-]{0,31}$/;
109
+ var CODE_SHAPE = /^(ok|HTTP_[1-5]\d\d|[A-Z][A-Z0-9_]{1,63}|ERROR)$/;
110
+ var PATH_LABEL = /^[a-z0-9][a-z0-9_-]{0,31}$/;
111
+ var PATH_ALLOWLIST = /* @__PURE__ */ new Set();
112
+ function scrubPath(raw) {
113
+ if (typeof raw !== "string") return null;
114
+ const bare = raw.split(/[?#]/, 1)[0] ?? "";
115
+ if (bare === "") return null;
116
+ let decoded;
117
+ try {
118
+ decoded = decodeURIComponent(bare);
119
+ } catch {
120
+ return REDACTED;
121
+ }
122
+ const stripped = decoded.replace(/^\/+/, "").replace(/\/+/g, "/");
123
+ if (stripped === "") return null;
124
+ const slash = stripped.indexOf("/");
125
+ if (slash === -1) return REDACTED;
126
+ const first = stripped.slice(0, slash).toLowerCase();
127
+ if (!PATH_LABEL.test(first)) return REDACTED;
128
+ if (PATH_ALLOWLIST.has(first)) return stripped.length > MAX_PATH ? stripped.slice(0, MAX_PATH) : stripped;
129
+ return `${first}/`;
130
+ }
131
+ function spaceLabel(spaceId) {
132
+ if (typeof spaceId !== "string" || spaceId === "") return null;
133
+ const colon = spaceId.lastIndexOf(":");
134
+ if (colon === -1) return "space";
135
+ const tail = spaceId.slice(colon + 1);
136
+ return SPACE_LABEL.test(tail) ? tail : "space";
137
+ }
138
+ function hostOf(url) {
139
+ if (typeof url !== "string") return null;
140
+ try {
141
+ return new URL(url).host || null;
142
+ } catch {
143
+ return null;
144
+ }
145
+ }
146
+ function byteLengthOf(body) {
147
+ if (typeof body === "string") return new TextEncoder().encode(body).byteLength;
148
+ if (typeof Blob !== "undefined" && body instanceof Blob) return body.size;
149
+ if (body instanceof ArrayBuffer) return body.byteLength;
150
+ if (ArrayBuffer.isView(body)) return body.byteLength;
151
+ return null;
152
+ }
153
+ function declaredLengthOf(header) {
154
+ if (typeof header !== "string") return null;
155
+ const value = header.trim();
156
+ return /^\d+$/.test(value) ? Number(value) : null;
157
+ }
158
+ var invocationMeta = /* @__PURE__ */ new WeakMap();
159
+ function invocationMetaOf(headers) {
160
+ return typeof headers === "object" && headers !== null ? invocationMeta.get(headers) : void 0;
161
+ }
162
+ function buildRequestEvent(raw) {
163
+ const service = raw.service !== void 0 && SERVICE_NAME.test(raw.service) ? raw.service : "other";
164
+ const space = spaceLabel(raw.spaceId);
165
+ const op = typeof raw.op === "string" && OP_SHAPE.test(raw.op) ? raw.op : "other";
166
+ const code = raw.code === void 0 ? raw.ok ? "ok" : "ERROR" : CODE_SHAPE.test(raw.code) ? raw.code : "ERROR";
167
+ const bytes = typeof raw.bytes === "number" && Number.isFinite(raw.bytes) && raw.bytes >= 0 ? Math.floor(raw.bytes) : null;
168
+ return {
169
+ v: 1,
170
+ at: raw.at,
171
+ service,
172
+ op,
173
+ path: scrubPath(raw.path),
174
+ space,
175
+ host: hostOf(raw.url),
176
+ bytes,
177
+ durationMs: typeof raw.durationMs === "number" && Number.isFinite(raw.durationMs) ? Math.max(0, Math.round(raw.durationMs)) : null,
178
+ attempts: raw.attempts !== void 0 && Number.isFinite(raw.attempts) && raw.attempts >= 1 ? Math.floor(raw.attempts) : 1,
179
+ ok: raw.ok === true,
180
+ code,
181
+ source: raw.source === "replica" ? "replica" : "network"
182
+ };
183
+ }
184
+ function observeFetch(observation, emit) {
185
+ const meta = invocationMetaOf(observation.requestHeaders);
186
+ emit(
187
+ buildRequestEvent({
188
+ service: meta?.service ?? "other",
189
+ op: meta ? `${meta.service}/${meta.action.split("/").pop() ?? meta.action}` : "other",
190
+ path: meta?.path ?? null,
191
+ spaceId: meta?.spaceId ?? null,
192
+ url: observation.url,
193
+ bytes: requestBytesOf(observation.requestBody, observation.responseContentLength),
194
+ durationMs: observation.durationMs,
195
+ attempts: observation.attempts,
196
+ ok: observation.ok,
197
+ code: observation.code,
198
+ at: observation.at
199
+ })
200
+ );
201
+ }
202
+ function requestBytesOf(body, responseContentLength) {
203
+ if (body === void 0 || body === null) return declaredLengthOf(responseContentLength);
204
+ return byteLengthOf(body);
205
+ }
206
+ function observeReplicaReads(context, sink) {
207
+ return context.on(TelemetryEvents.REPLICATION_READ, (data) => {
208
+ try {
209
+ const read = typeof data === "object" && data !== null ? data : {};
210
+ if (read.source !== "replica") return;
211
+ sink.record(
212
+ buildRequestEvent({
213
+ service: "kv",
214
+ op: `kv/${String(read.op)}`,
215
+ path: typeof read.key === "string" ? read.key : null,
216
+ spaceId: typeof read.space === "string" ? read.space : null,
217
+ url: null,
218
+ bytes: null,
219
+ durationMs: typeof read.latencyMs === "number" ? read.latencyMs : null,
220
+ attempts: 1,
221
+ ok: read.outcome !== "error",
222
+ code: read.outcome === "not_found" ? "KV_NOT_FOUND" : typeof read.code === "string" ? read.code : void 0,
223
+ source: "replica",
224
+ at: Date.now()
225
+ })
226
+ );
227
+ } catch {
228
+ }
229
+ });
230
+ }
231
+ var _slots, _head, _count, _nextId, _subscribers, _clearListeners;
232
+ var RequestLog = class {
233
+ constructor() {
234
+ __privateAdd(this, _slots, new Array(RING_SIZE));
235
+ __privateAdd(this, _head, 0);
236
+ __privateAdd(this, _count, 0);
237
+ __privateAdd(this, _nextId, 1);
238
+ __privateAdd(this, _subscribers, /* @__PURE__ */ new Set());
239
+ __privateAdd(this, _clearListeners, /* @__PURE__ */ new Set());
240
+ }
241
+ record(draft) {
242
+ const event = Object.freeze({ ...draft, id: __privateWrapper(this, _nextId)._++ });
243
+ __privateGet(this, _slots)[__privateGet(this, _head)] = event;
244
+ __privateSet(this, _head, (__privateGet(this, _head) + 1) % RING_SIZE);
245
+ if (__privateGet(this, _count) < RING_SIZE) __privateWrapper(this, _count)._++;
246
+ for (const cb of __privateGet(this, _subscribers)) {
247
+ try {
248
+ cb(event);
249
+ } catch {
250
+ }
251
+ }
252
+ }
253
+ /** Called only when a sink is set, so developer-off code never writes here. */
254
+ remember(headers, meta) {
255
+ invocationMeta.set(headers, meta);
256
+ }
257
+ observe(observation) {
258
+ observeFetch(observation, (event) => this.record(event));
259
+ }
260
+ snapshot() {
261
+ const start = __privateGet(this, _count) < RING_SIZE ? 0 : __privateGet(this, _head);
262
+ const out = [];
263
+ for (let i = 0; i < __privateGet(this, _count); i++) {
264
+ out.push(__privateGet(this, _slots)[(start + i) % RING_SIZE]);
265
+ }
266
+ return out;
267
+ }
268
+ subscribe(cb) {
269
+ __privateGet(this, _subscribers).add(cb);
270
+ return () => {
271
+ __privateGet(this, _subscribers).delete(cb);
272
+ };
273
+ }
274
+ onClear(cb) {
275
+ __privateGet(this, _clearListeners).add(cb);
276
+ return () => {
277
+ __privateGet(this, _clearListeners).delete(cb);
278
+ };
279
+ }
280
+ clear() {
281
+ __privateGet(this, _slots).fill(void 0);
282
+ __privateSet(this, _head, 0);
283
+ __privateSet(this, _count, 0);
284
+ for (const cb of __privateGet(this, _clearListeners)) {
285
+ try {
286
+ cb();
287
+ } catch {
288
+ }
289
+ }
290
+ }
291
+ };
292
+ _slots = new WeakMap();
293
+ _head = new WeakMap();
294
+ _count = new WeakMap();
295
+ _nextId = new WeakMap();
296
+ _subscribers = new WeakMap();
297
+ _clearListeners = new WeakMap();
298
+ function readOnlyRequestLog(log) {
299
+ return Object.freeze({
300
+ snapshot: () => log.snapshot(),
301
+ subscribe: (cb) => log.subscribe(cb),
302
+ onClear: (cb) => log.onClear(cb)
303
+ });
304
+ }
305
+ export {
306
+ RequestLog,
307
+ buildRequestEvent,
308
+ byteLengthOf,
309
+ declaredLengthOf,
310
+ invocationMetaOf,
311
+ observeFetch,
312
+ observeReplicaReads,
313
+ readOnlyRequestLog,
314
+ requestBytesOf
315
+ };
316
+ //# sourceMappingURL=requestLog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types.ts","../../src/telemetry/requestLog.ts"],"sourcesContent":["/**\n * SDK Services - Core Types\n *\n * These types define the service architecture for TinyCloud SDK.\n * Services use dependency injection via IServiceContext for platform independence.\n */\n\n// =============================================================================\n// Result Type Pattern\n// =============================================================================\n\n/**\n * Result type for service operations.\n * Services return Result instead of throwing, making error handling explicit.\n *\n * @template T - The success data type\n * @template E - The error type (defaults to ServiceError)\n *\n * @example\n * ```typescript\n * const result = await kv.get('key');\n * if (result.ok) {\n * console.log(result.data);\n * } else {\n * console.error(result.error.code);\n * }\n * ```\n */\nexport type Result<T, E = ServiceError> =\n | { ok: true; data: T }\n | { ok: false; error: E };\n\n/**\n * Service error with structured information.\n */\nexport interface ServiceError {\n /** Error code for programmatic handling (e.g., 'KV_NOT_FOUND', 'AUTH_EXPIRED') */\n code: string;\n /** Human-readable error message */\n message: string;\n /** Service that produced the error (e.g., 'kv', 'sql') */\n service: string;\n /** Original error if this wraps another error */\n cause?: Error;\n /** Additional metadata about the error */\n meta?: Record<string, unknown>;\n}\n\n/**\n * A node-supplied permission hint after strict SDK validation. This is\n * deliberately narrower than a general capability: no wildcards, caveats,\n * descriptions, or transport metadata can cross the service boundary.\n */\nexport interface PermissionHint {\n readonly service: \"tinycloud.kv\" | \"tinycloud.encryption\";\n readonly space?: string;\n readonly path: string;\n readonly actions: readonly string[];\n}\n\n/**\n * Storage quota information returned with quota-related errors.\n * @deprecated Read `usedBytes`, `limitBytes` and `account` from the error's\n * `meta`, or use `StorageFullEvent` from `tc.on(\"storage.full\", handler)`.\n */\nexport interface StorageQuotaInfo {\n usedBytes: number;\n limitBytes: number;\n service: string;\n}\n\n/**\n * Standard error codes used across services.\n */\nexport const ErrorCodes = {\n // Common errors\n NOT_FOUND: \"NOT_FOUND\",\n AUTH_EXPIRED: \"AUTH_EXPIRED\",\n AUTH_REQUIRED: \"AUTH_REQUIRED\",\n AUTH_UNAUTHORIZED: \"AUTH_UNAUTHORIZED\",\n /** HTTP 401: the delegation the invocation cites was revoked. */\n AUTH_DELEGATION_REVOKED: \"AUTH_DELEGATION_REVOKED\",\n /** HTTP 401: a delegation the cited one rests on was revoked. */\n AUTH_DELEGATION_ANCESTOR_REVOKED: \"AUTH_DELEGATION_ANCESTOR_REVOKED\",\n NETWORK_ERROR: \"NETWORK_ERROR\",\n TIMEOUT: \"TIMEOUT\",\n ABORTED: \"ABORTED\",\n INVALID_INPUT: \"INVALID_INPUT\",\n PERMISSION_DENIED: \"PERMISSION_DENIED\",\n\n // Node retry signals (tinycloud-node #347). `meta.retryable` says whether a\n // retry is safe; `meta.retryAfterMs` carries the node's Retry-After.\n /** HTTP 503 `sql_busy:<stage>` / `unavailable_retryable`/`read_audit_busy`: nothing was applied; retry after `meta.retryAfterMs`. */\n NODE_BUSY: \"NODE_BUSY\",\n /** HTTP 503 `request_timeout`: the node's deadline passed before any write began; nothing was applied. */\n REQUEST_TIMEOUT: \"REQUEST_TIMEOUT\",\n /** HTTP 500 `outcome_unknown`: a write may have been applied; read back before retrying a non-idempotent operation. */\n OUTCOME_UNKNOWN: \"OUTCOME_UNKNOWN\",\n\n // KV-specific errors\n KV_NOT_FOUND: \"KV_NOT_FOUND\",\n KV_WRITE_FAILED: \"KV_WRITE_FAILED\",\n KV_PRECONDITION_FAILED: \"KV_PRECONDITION_FAILED\",\n KV_CONFLICT: \"KV_CONFLICT\",\n KV_RESPONSE_TOO_LARGE: \"KV_RESPONSE_TOO_LARGE\",\n /** HTTP 410 from the change feed: discard sync state and restart without a cursor; `meta.reason` says why. */\n KV_SYNC_RESET_REQUIRED: \"KV_SYNC_RESET_REQUIRED\",\n /** HTTP 403 from the change feed: the retention grant was refused; `meta.reason` says why. */\n KV_RETENTION_GRANT_REFUSED: \"KV_RETENTION_GRANT_REFUSED\",\n\n // SQL-specific errors\n SQL_ERROR: \"SQL_ERROR\",\n SQL_PERMISSION_DENIED: \"SQL_PERMISSION_DENIED\",\n SQL_DATABASE_NOT_FOUND: \"SQL_DATABASE_NOT_FOUND\",\n SQL_RESPONSE_TOO_LARGE: \"SQL_RESPONSE_TOO_LARGE\",\n SQL_QUOTA_EXCEEDED: \"SQL_QUOTA_EXCEEDED\",\n SQL_INVALID_STATEMENT: \"SQL_INVALID_STATEMENT\",\n SQL_SCHEMA_ERROR: \"SQL_SCHEMA_ERROR\",\n SQL_READONLY_VIOLATION: \"SQL_READONLY_VIOLATION\",\n\n // Storage quota errors\n STORAGE_QUOTA_EXCEEDED: \"STORAGE_QUOTA_EXCEEDED\",\n STORAGE_LIMIT_REACHED: \"STORAGE_LIMIT_REACHED\",\n /** The node answered the usage read without usage numbers (it predates the read). */\n STORAGE_STATUS_UNAVAILABLE: \"STORAGE_STATUS_UNAVAILABLE\",\n\n // DuckDB-specific errors\n DUCKDB_ERROR: \"DUCKDB_ERROR\",\n DUCKDB_PERMISSION_DENIED: \"DUCKDB_PERMISSION_DENIED\",\n DUCKDB_DATABASE_NOT_FOUND: \"DUCKDB_DATABASE_NOT_FOUND\",\n DUCKDB_RESPONSE_TOO_LARGE: \"DUCKDB_RESPONSE_TOO_LARGE\",\n DUCKDB_QUOTA_EXCEEDED: \"DUCKDB_QUOTA_EXCEEDED\",\n DUCKDB_INVALID_STATEMENT: \"DUCKDB_INVALID_STATEMENT\",\n DUCKDB_SCHEMA_ERROR: \"DUCKDB_SCHEMA_ERROR\",\n DUCKDB_READONLY_VIOLATION: \"DUCKDB_READONLY_VIOLATION\",\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n\n// =============================================================================\n// Service Session\n// =============================================================================\n\n/**\n * Session data required for authenticated service operations.\n * Both TinyCloudSession and web-sdk Session can be cast to this interface.\n */\nexport interface ServiceSession {\n /** The delegation header containing the UCAN */\n delegationHeader: { Authorization: string };\n /** The delegation CID */\n delegationCid: string;\n /** The space ID for this session */\n spaceId: string;\n /** The verification method DID */\n verificationMethod: string;\n /** The session key JWK (required for invoke) */\n jwk: object;\n}\n\n// =============================================================================\n// Platform Dependencies (Injected)\n// =============================================================================\n\n/**\n * Headers type - compatible with both browser and Node.js.\n */\nexport type ServiceHeaders = Record<string, string> | [string, string][];\n\n/**\n * A single fact object to include in the UCAN invocation.\n * Facts are key-value objects that the server reads from the UCAN facts field.\n */\nexport interface InvocationFact {\n [key: string]: unknown;\n}\n\n/**\n * Facts to include in the UCAN invocation.\n * This is an array of fact objects per the UCAN spec.\n * Used to pass additional parameters that the server reads from the UCAN facts field.\n */\nexport type InvocationFacts = InvocationFact[];\n\n/**\n * Invoke function signature - platform-specific implementation injected via DI.\n * Both node-sdk-wasm and web-sdk-wasm export this with identical signature.\n *\n * @param session - The service session with delegation data\n * @param service - Service name (e.g., \"kv\")\n * @param path - Resource path or key\n * @param action - Action to perform (e.g., \"tinycloud.kv/get\")\n * @param facts - Optional facts to include in the UCAN (e.g., for capabilities/read params)\n * @returns Headers to include in the request\n */\nexport type InvokeFunction = (\n session: ServiceSession,\n service: string,\n path: string,\n action: string,\n facts?: InvocationFacts,\n) => ServiceHeaders;\n\n/**\n * Multi-resource invocation entry.\n */\nexport interface InvokeAnyEntry {\n /**\n * Legacy space-scoped resource. Optional when `resource` is provided.\n */\n spaceId?: string;\n service: string;\n path: string;\n action: string;\n /** Optional raw resource URI. When set, WASM signs this URI directly. */\n resource?: string;\n /** Capability caveats that must remain on this invocation attenuation. */\n caveats?: Record<string, unknown>[];\n}\n\n/**\n * Invoke function for minting a single authorization header that covers\n * multiple capabilities across one effective invoker.\n */\nexport type InvokeAnyFunction = (\n session: ServiceSession,\n entries: InvokeAnyEntry[],\n facts?: InvocationFacts,\n) => ServiceHeaders;\n\n/**\n * Fetch request options - compatible with standard fetch API.\n */\nexport interface FetchRequestInit {\n method?: string;\n headers?: ServiceHeaders;\n body?: Blob | FormData | string;\n signal?: AbortSignal;\n redirect?: \"error\";\n}\n\n/**\n * Fetch response interface - compatible with standard Response.\n */\nexport interface FetchResponse {\n ok: boolean;\n status: number;\n statusText: string;\n body?: unknown;\n headers: {\n get(name: string): string | null;\n };\n json(): Promise<unknown>;\n text(): Promise<string>;\n arrayBuffer(): Promise<ArrayBuffer>;\n blob(): Promise<Blob>;\n /** Present on standard Responses; the retry reads a clone so the caller's body stays intact. */\n clone?(): FetchResponse;\n /** Set by `ServiceContext.invokeWithRetry` on the response it returns: how many requests it took. */\n retry?: RetryStats;\n}\n\n/** What automatic retry cost one request, so latency can be split from backoff. */\nexport interface RetryStats {\n /** Requests sent, including the first (1 means no retry). */\n attempts: number;\n /** Total milliseconds spent waiting between attempts. */\n backoffMs: number;\n}\n\n/**\n * Fetch function signature - allows for custom fetch implementations.\n * Compatible with both browser fetch and Node.js fetch.\n */\nexport type FetchFunction = (\n url: string,\n init?: FetchRequestInit,\n) => Promise<FetchResponse>;\n\n// =============================================================================\n// Retry Policy\n// =============================================================================\n\n/**\n * Automatic retry of node responses that guarantee nothing was applied\n * (`NODE_BUSY`, `REQUEST_TIMEOUT`). Applied once, in the service context's\n * `invokeWithRetry`, which re-mints the signed invocation per attempt. Backoff\n * is full-jitter: each delay is uniform in `[0, step]`, where\n * `step` is the strategy's delay capped at `maxDelayMs`. A node `Retry-After`\n * replaces the delay. Opt out with `{ maxAttempts: 1 }`.\n */\nexport interface RetryPolicy {\n /** Maximum number of attempts (including initial) */\n maxAttempts: number;\n /** Backoff strategy between retries */\n backoff: \"none\" | \"linear\" | \"exponential\";\n /** Base delay in milliseconds for backoff calculation */\n baseDelayMs: number;\n /** Maximum delay in milliseconds between retries */\n maxDelayMs: number;\n /** Node error codes that trigger a retry. `OUTCOME_UNKNOWN` is never retried. */\n retryableErrors: string[];\n /** Total time budget for all attempts and waits, in milliseconds. Unset: no cap. */\n maxTotalMs?: number;\n}\n\n/**\n * Default retry policy.\n */\nexport const defaultRetryPolicy: RetryPolicy = {\n maxAttempts: 4,\n backoff: \"exponential\",\n baseDelayMs: 250,\n maxDelayMs: 4000,\n retryableErrors: [ErrorCodes.NODE_BUSY, ErrorCodes.REQUEST_TIMEOUT],\n maxTotalMs: 12000,\n};\n\n// =============================================================================\n// Service Context\n// =============================================================================\n\n/**\n * Event handler function type.\n */\nexport type EventHandler = (data: unknown) => void;\n\n/**\n * SDK telemetry event handler.\n */\nexport type TelemetryEventHandler = (event: string, data: unknown) => void;\n\n/**\n * Default-off telemetry configuration.\n */\nexport type TelemetryConfig =\n | boolean\n | {\n enabled?: boolean;\n onEvent?: TelemetryEventHandler;\n };\n\n/**\n * Service interface - base contract for all services.\n */\nexport interface IService {\n /** Initialize service with context */\n initialize(context: IServiceContext): void;\n\n /** Called when session changes (sign-in, sign-out, refresh) */\n onSessionChange(session: ServiceSession | null): void;\n\n /** Called when SDK signs out - should abort pending operations */\n onSignOut(): void;\n\n /** Service-specific configuration */\n readonly config: Record<string, unknown>;\n}\n\n/**\n * Context provided to services for accessing platform dependencies.\n * The SDK creates this context and passes it to services during initialization.\n */\nexport interface IServiceContext {\n // Session management\n /** Current active session, or null if not authenticated */\n readonly session: ServiceSession | null;\n /** Whether there is an active authenticated session */\n readonly isAuthenticated: boolean;\n\n // Platform dependencies (injected by SDK)\n /** Platform-specific invoke function from WASM binding */\n readonly invoke: InvokeFunction;\n /** Optional multi-resource invoke function */\n readonly invokeAny?: InvokeAnyFunction;\n /** Fetch function (defaults to globalThis.fetch) */\n readonly fetch: FetchFunction;\n /** Send a signed invocation, retrying a busy node; `build` re-signs per attempt. Absent: sends once. */\n invokeWithRetry?(\n url: string,\n build: () => FetchRequestInit | Promise<FetchRequestInit>,\n ): Promise<FetchResponse>;\n /** Available TinyCloud host URLs */\n readonly hosts: string[];\n\n // Cross-service access\n /** Get another registered service by name */\n getService<T extends IService>(name: string): T | undefined;\n\n // Telemetry/Events\n /** Emit a telemetry event */\n emit(event: string, data: unknown): void;\n /** Subscribe to events */\n on(event: string, handler: EventHandler): () => void;\n\n // Lifecycle\n /** Abort signal that fires when SDK signs out. */\n readonly abortSignal: AbortSignal;\n /** Additional abort signal for a scoped operation, when active. */\n readonly operationAbortSignal?: AbortSignal;\n /** Install an additional signal for the duration of a scoped operation. */\n setOperationAbortSignal?(signal: AbortSignal | undefined): void;\n // Retry policy\n /** Retry policy for failed requests */\n readonly retryPolicy: RetryPolicy;\n}\n\n// =============================================================================\n// Telemetry Events\n// =============================================================================\n\n/**\n * Event emitted before a service request.\n */\nexport interface ServiceRequestEvent {\n service: string;\n action: string;\n span?: string;\n key?: string;\n timestamp: number;\n}\n\n/**\n * Event emitted after a service response.\n */\nexport interface ServiceResponseEvent {\n service: string;\n action: string;\n span?: string;\n ok: boolean;\n duration: number;\n durationMs?: number;\n status?: number;\n}\n\n/**\n * Event emitted on service error.\n */\nexport interface ServiceErrorEvent {\n service: string;\n span?: string;\n error: ServiceError;\n}\n\n/**\n * Event emitted on retry attempt.\n */\nexport interface ServiceRetryEvent {\n service: string;\n attempt: number;\n maxAttempts: number;\n error: ServiceError;\n}\n\n/**\n * Generic named span event for aggregating operation timings.\n */\nexport interface TelemetrySpanEvent {\n span: string;\n ok: boolean;\n durationMs: number;\n service?: string;\n action?: string;\n status?: number;\n error?: ServiceError;\n}\n\n/**\n * Telemetry event names.\n */\nexport const TelemetryEvents = {\n SPAN: \"telemetry.span\",\n SERVICE_REQUEST: \"service.request\",\n SERVICE_RESPONSE: \"service.response\",\n SERVICE_ERROR: \"service.error\",\n SERVICE_RETRY: \"service.retry\",\n SESSION_CHANGED: \"session.changed\",\n SESSION_EXPIRED: \"session.expired\",\n REPLICATION_READ: \"replication.read\",\n REPLICATION_WRITE: \"replication.write\",\n REPLICATION_SYNC: \"replication.sync\",\n REPLICATION_STATE: \"replication.state\",\n REPLICATION_DIVERGENCE: \"replication.divergence\",\n REPLICATION_GRANT: \"replication.grant\",\n REPLICATION_LIFECYCLE: \"replication.lifecycle\",\n /** Emitted once, on the first write rejected because storage is full. */\n STORAGE_FULL: \"storage.full\",\n} as const;\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Create a success result.\n */\nexport function ok<T>(data: T): Result<T> {\n return { ok: true, data };\n}\n\n/**\n * Create an error result.\n */\nexport function err<E = ServiceError>(error: E): Result<never, E> {\n return { ok: false, error };\n}\n\n/**\n * Create a ServiceError.\n */\nexport function serviceError(\n code: string,\n message: string,\n service: string,\n options?: { cause?: Error; meta?: Record<string, unknown> },\n): ServiceError {\n return {\n code,\n message,\n service,\n cause: options?.cause,\n meta: options?.meta,\n };\n}\n","/**\n * Developer request log core. Events are sanitized by `buildRequestEvent`, the\n * only constructor, which copies an allowlist of fields. Values, headers,\n * bodies, tokens, delegations, and error text have no field to land in.\n */\n\nimport { TelemetryEvents, type IServiceContext } from \"../types\";\n\nexport type RequestService =\n | \"kv\"\n | \"sql\"\n | \"duckdb\"\n | \"hooks\"\n | \"vault\"\n | \"space\"\n | \"sync\"\n | \"other\";\n\nexport interface RequestEvent {\n v: 1;\n /** Monotonic per RequestLog. */\n id: number;\n /** Request start, ms since epoch. */\n at: number;\n service: RequestService;\n /** `service/action`, e.g. `kv/get`; `other` when unknown. */\n op: string;\n /** `first/` of the key (`[redacted]` when unsafe or slashless); full key only for allowlisted first segments. */\n path: string | null;\n /** Tail label of the space id, never the id. */\n space: string | null;\n /** URL host[:port] only. */\n host: string | null;\n /** Body bytes for writes, response bytes for reads; null when unknown. */\n bytes: number | null;\n /** Whole logical request, including retry backoff; null when unknown. */\n durationMs: number | null;\n attempts: number;\n ok: boolean;\n code: string;\n source: \"network\" | \"replica\";\n}\n\n/** A sanitized event before the log assigns its id. */\nexport type RequestDraft = Omit<RequestEvent, \"id\">;\n\nexport interface RequestLogReader {\n /** Copy, oldest first. */\n snapshot(): readonly RequestEvent[];\n subscribe(cb: (event: RequestEvent) => void): () => void;\n onClear(cb: () => void): () => void;\n}\n\n/**\n * The fetch layer's only view of the developer request log. A context without a\n * sink never links the sanitizer or the invocation-meta store.\n */\nexport interface RequestSink {\n record(event: RequestDraft): void;\n /** Derive and record one event from one transport attempt's raw facts. */\n observe(observation: FetchObservation): void;\n /** Attach service, path, and action to headers built for an invocation. */\n remember(headers: object, meta: InvocationMeta): void;\n}\n\n/** Transport facts for one attempt, read by `observeFetch`. */\nexport interface FetchObservation {\n url: string;\n requestHeaders: unknown;\n requestBody: unknown;\n responseContentLength: string | null | undefined;\n ok: boolean;\n code: string;\n durationMs: number;\n attempts: number;\n at: number;\n}\n\n/** Raw facts about one logical request. Only `buildRequestEvent` reads them. */\nexport interface RawRequest {\n service?: string;\n op?: string;\n /** Unscrubbed key path. */\n path?: string | null;\n /** Full space id; only its tail label is kept. */\n spaceId?: string | null;\n url?: string | null;\n bytes?: number | null;\n durationMs: number | null;\n attempts?: number;\n ok: boolean;\n code?: string;\n source?: string;\n at: number;\n}\n\nconst RING_SIZE = 500;\nconst MAX_PATH = 256;\nconst REDACTED = \"[redacted]\";\n\nconst SERVICE_NAME = /^(kv|sql|duckdb|hooks|vault|space|sync)$/;\nconst OP_SHAPE = /^[a-z]+\\/[A-Za-z]+$/;\nconst SPACE_LABEL = /^[a-z0-9][a-z0-9_-]{0,31}$/;\nconst CODE_SHAPE = /^(ok|HTTP_[1-5]\\d\\d|[A-Z][A-Z0-9_]{1,63}|ERROR)$/;\nconst PATH_LABEL = /^[a-z0-9][a-z0-9_-]{0,31}$/;\n/** First segments whose full key may be recorded. Empty: only `first/` is ever kept. */\nconst PATH_ALLOWLIST: ReadonlySet<string> = new Set<string>();\n\nfunction scrubPath(raw: string | null | undefined): string | null {\n if (typeof raw !== \"string\") return null;\n const bare = raw.split(/[?#]/, 1)[0] ?? \"\";\n if (bare === \"\") return null;\n let decoded: string;\n try {\n decoded = decodeURIComponent(bare);\n } catch {\n return REDACTED;\n }\n const stripped = decoded.replace(/^\\/+/, \"\").replace(/\\/+/g, \"/\");\n if (stripped === \"\") return null;\n const slash = stripped.indexOf(\"/\");\n // Slashless: the whole value may be a bare secret name.\n if (slash === -1) return REDACTED;\n const first = stripped.slice(0, slash).toLowerCase();\n if (!PATH_LABEL.test(first)) return REDACTED;\n if (PATH_ALLOWLIST.has(first)) return stripped.length > MAX_PATH ? stripped.slice(0, MAX_PATH) : stripped;\n return `${first}/`;\n}\n\nfunction spaceLabel(spaceId: string | null | undefined): string | null {\n if (typeof spaceId !== \"string\" || spaceId === \"\") return null;\n // Only the name after the last colon is kept; an id without a colon has no such name.\n const colon = spaceId.lastIndexOf(\":\");\n if (colon === -1) return \"space\";\n const tail = spaceId.slice(colon + 1);\n return SPACE_LABEL.test(tail) ? tail : \"space\";\n}\n\nfunction hostOf(url: string | null | undefined): string | null {\n if (typeof url !== \"string\") return null;\n try {\n return new URL(url).host || null;\n } catch {\n return null;\n }\n}\n\n/** UTF-8 byte length of a request body without consuming it; null when unknown. */\nexport function byteLengthOf(body: unknown): number | null {\n if (typeof body === \"string\") return new TextEncoder().encode(body).byteLength;\n if (typeof Blob !== \"undefined\" && body instanceof Blob) return body.size;\n if (body instanceof ArrayBuffer) return body.byteLength;\n if (ArrayBuffer.isView(body)) return body.byteLength;\n return null;\n}\n\n/** Response Content-Length as bytes; null unless the header is plain digits (blank, signed, or exponent forms are unknown). */\nexport function declaredLengthOf(header: string | null | undefined): number | null {\n if (typeof header !== \"string\") return null;\n const value = header.trim();\n return /^\\d+$/.test(value) ? Number(value) : null;\n}\n\n/** What the signing path knows about a request; read back by the fetch layer. */\nexport interface InvocationMeta {\n service: string;\n path: string;\n action: string;\n spaceId?: string | null;\n}\n\nconst invocationMeta = new WeakMap<object, InvocationMeta>();\n\nexport function invocationMetaOf(headers: unknown): InvocationMeta | undefined {\n return typeof headers === \"object\" && headers !== null ? invocationMeta.get(headers) : undefined;\n}\n\nexport function buildRequestEvent(raw: RawRequest): RequestDraft {\n const service: RequestService =\n raw.service !== undefined && SERVICE_NAME.test(raw.service)\n ? (raw.service as RequestService)\n : \"other\";\n const space = spaceLabel(raw.spaceId);\n const op = typeof raw.op === \"string\" && OP_SHAPE.test(raw.op) ? raw.op : \"other\";\n const code =\n raw.code === undefined ? (raw.ok ? \"ok\" : \"ERROR\") : CODE_SHAPE.test(raw.code) ? raw.code : \"ERROR\";\n const bytes =\n typeof raw.bytes === \"number\" && Number.isFinite(raw.bytes) && raw.bytes >= 0\n ? Math.floor(raw.bytes)\n : null;\n return {\n v: 1,\n at: raw.at,\n service,\n op,\n path: scrubPath(raw.path),\n space,\n host: hostOf(raw.url),\n bytes,\n durationMs: typeof raw.durationMs === \"number\" && Number.isFinite(raw.durationMs) ? Math.max(0, Math.round(raw.durationMs)) : null,\n attempts:\n raw.attempts !== undefined && Number.isFinite(raw.attempts) && raw.attempts >= 1\n ? Math.floor(raw.attempts)\n : 1,\n ok: raw.ok === true,\n code,\n source: raw.source === \"replica\" ? \"replica\" : \"network\",\n };\n}\n\n/**\n * Derives one event from one transport attempt. Invocation metadata comes from the\n * request headers. Bytes are the request body length when a body was sent (null for\n * streams); with no request body, the declared response length.\n */\nexport function observeFetch(observation: FetchObservation, emit: (event: RequestDraft) => void): void {\n const meta = invocationMetaOf(observation.requestHeaders);\n emit(\n buildRequestEvent({\n service: meta?.service ?? \"other\",\n op: meta ? `${meta.service}/${meta.action.split(\"/\").pop() ?? meta.action}` : \"other\",\n path: meta?.path ?? null,\n spaceId: meta?.spaceId ?? null,\n url: observation.url,\n bytes: requestBytesOf(observation.requestBody, observation.responseContentLength),\n durationMs: observation.durationMs,\n attempts: observation.attempts,\n ok: observation.ok,\n code: observation.code,\n at: observation.at,\n }),\n );\n}\n\n/**\n * Request bytes: the body length when a body was sent (null when it cannot be measured\n * without consuming it); with no body, the declared response Content-Length.\n */\nexport function requestBytesOf(body: unknown, responseContentLength: string | null | undefined): number | null {\n if (body === undefined || body === null) return declaredLengthOf(responseContentLength);\n return byteLengthOf(body);\n}\n\n/**\n * Maps replica-served KV reads (`replication.read` with `source: \"replica\"`) to\n * request events. Network reads and replication writes are not mapped here:\n * their wire requests are recorded by the fetch layer.\n */\nexport function observeReplicaReads(context: IServiceContext, sink: RequestSink): () => void {\n return context.on(TelemetryEvents.REPLICATION_READ, (data: unknown) => {\n try {\n const read = (typeof data === \"object\" && data !== null ? data : {}) as Record<string, unknown>;\n if (read.source !== \"replica\") return;\n sink.record(\n buildRequestEvent({\n service: \"kv\",\n op: `kv/${String(read.op)}`,\n path: typeof read.key === \"string\" ? read.key : null,\n spaceId: typeof read.space === \"string\" ? read.space : null,\n url: null,\n bytes: null,\n durationMs: typeof read.latencyMs === \"number\" ? read.latencyMs : null,\n attempts: 1,\n ok: read.outcome !== \"error\",\n code: read.outcome === \"not_found\" ? \"KV_NOT_FOUND\" : typeof read.code === \"string\" ? read.code : undefined,\n source: \"replica\",\n at: Date.now(),\n }),\n );\n } catch {\n // Observability must not affect KV behavior.\n }\n });\n}\n\ntype Listener<T extends unknown[]> = (...args: T) => void;\n\n/** Fixed-size in-memory ring of sanitized events. Lost on reload by design (v1). */\nexport class RequestLog implements RequestSink, RequestLogReader {\n readonly #slots: (RequestEvent | undefined)[] = new Array(RING_SIZE);\n #head = 0;\n #count = 0;\n #nextId = 1;\n readonly #subscribers = new Set<Listener<[RequestEvent]>>();\n readonly #clearListeners = new Set<Listener<[]>>();\n\n record(draft: RequestDraft): void {\n const event: RequestEvent = Object.freeze({ ...draft, id: this.#nextId++ });\n this.#slots[this.#head] = event;\n this.#head = (this.#head + 1) % RING_SIZE;\n if (this.#count < RING_SIZE) this.#count++;\n for (const cb of this.#subscribers) {\n try {\n cb(event);\n } catch {\n // A failing subscriber must not break the request path or other subscribers.\n }\n }\n }\n\n /** Called only when a sink is set, so developer-off code never writes here. */\n remember(headers: object, meta: InvocationMeta): void {\n invocationMeta.set(headers, meta);\n }\n\n observe(observation: FetchObservation): void {\n observeFetch(observation, (event) => this.record(event));\n }\n\n snapshot(): RequestEvent[] {\n const start = this.#count < RING_SIZE ? 0 : this.#head;\n const out: RequestEvent[] = [];\n for (let i = 0; i < this.#count; i++) {\n out.push(this.#slots[(start + i) % RING_SIZE]!);\n }\n return out;\n }\n\n subscribe(cb: (event: RequestEvent) => void): () => void {\n this.#subscribers.add(cb);\n return () => {\n this.#subscribers.delete(cb);\n };\n }\n\n onClear(cb: () => void): () => void {\n this.#clearListeners.add(cb);\n return () => {\n this.#clearListeners.delete(cb);\n };\n }\n\n clear(): void {\n this.#slots.fill(undefined);\n this.#head = 0;\n this.#count = 0;\n for (const cb of this.#clearListeners) {\n try {\n cb();\n } catch {\n // Isolated like subscribers.\n }\n }\n }\n}\n\n/** The public view of a log: reads and subscriptions only. `record` and `clear` stay with the owner. */\nexport function readOnlyRequestLog(log: RequestLog): RequestLogReader {\n return Object.freeze({\n snapshot: () => log.snapshot(),\n subscribe: (cb: (event: RequestEvent) => void) => log.subscribe(cb),\n onClear: (cb: () => void) => log.onClear(cb),\n });\n}\n"],"mappings":"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","names":[]}
@@ -404,4 +404,4 @@ declare function serviceError(code: string, message: string, service: string, op
404
404
  meta?: Record<string, unknown>;
405
405
  }): ServiceError;
406
406
 
407
- export { type EventHandler as E, type FetchRequestInit as F, type IService as I, type PermissionHint as P, type Result as R, type ServiceSession as S, type TelemetryConfig as T, type IServiceContext as a, type ServiceError as b, type ServiceHeaders as c, type FetchResponse as d, type RetryStats as e, type FetchFunction as f, type InvokeFunction as g, type InvokeAnyFunction as h, type RetryPolicy as i, type StorageQuotaInfo as j, type ErrorCode as k, ErrorCodes as l, type InvocationFact as m, type InvocationFacts as n, type InvokeAnyEntry as o, type ServiceErrorEvent as p, type ServiceRequestEvent as q, type ServiceResponseEvent as r, type ServiceRetryEvent as s, type TelemetryEventHandler as t, TelemetryEvents as u, type TelemetrySpanEvent as v, defaultRetryPolicy as w, err as x, ok as y, serviceError as z };
407
+ export { type EventHandler as E, type FetchRequestInit as F, type IServiceContext as I, type PermissionHint as P, type Result as R, type ServiceSession as S, type TelemetryConfig as T, type IService as a, type ServiceError as b, type ServiceHeaders as c, type FetchResponse as d, type RetryStats as e, type FetchFunction as f, type InvokeFunction as g, type InvokeAnyFunction as h, type RetryPolicy as i, type StorageQuotaInfo as j, type ErrorCode as k, ErrorCodes as l, type InvocationFact as m, type InvocationFacts as n, type InvokeAnyEntry as o, type ServiceErrorEvent as p, type ServiceRequestEvent as q, type ServiceResponseEvent as r, type ServiceRetryEvent as s, type TelemetryEventHandler as t, TelemetryEvents as u, type TelemetrySpanEvent as v, defaultRetryPolicy as w, err as x, ok as y, serviceError as z };