@tinycloud/node-sdk 3.3.2-beta.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.
@@ -1,8 +1,8 @@
1
1
  import * as _tinycloud_sdk_core from '@tinycloud/sdk-core';
2
2
  import { AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, CapabilityPresentationEnvelopeV1, IUserAuthorization, ISigner, ISessionStorage, ISpaceCreationHandler, RegistryPolicy, RegistrySequenceStore, OnNodeNotFound, LocalNodeIdentityStore, IWasmBindings, ISessionManager, SiweConfig, Manifest, ComposedManifestRequest, ReplicationOptions, TinyCloudSession, PersistedSessionData, ClientSession, PermissionEntry, Extension, SignRequest, SpaceHostResult, SignInOptions, Delegation, DelegatedResource, TelemetryConfig, ServiceContext, IKVService, ISQLService, IDuckDbService, IHooksService, CreateOwnerDelegationParams as CreateOwnerDelegationParams$1, OwnerDelegationReceipt as OwnerDelegationReceipt$1, StorageFullEvent, IStorageService, INotificationHandler, IENSResolver, RetryPolicy, KVReplicaStorage, AccountService, InvokeAnyFunction as InvokeAnyFunction$1, UnifiedPolicyCapability, RegisterPolicyV3Input, RegisterPolicyV3Receipt, CanonicalJson, ShareDeliveryAuthorizationV3Receipt, IDataVaultService, IEncryptionService, NetworkDescriptor, ISecretsService, ICapabilityKeyRegistry, DelegationManager, ISpaceService, ISpace, ISharingService, DelegationResult, DelegationRevocationReceipt, DelegationStatus, ServiceSession, ReplicationControl, ResolvedDelegate } from '@tinycloud/sdk-core';
3
3
  import { ReplicaSessionStamp } from '@tinycloud/sdk-services/kv/replication';
4
+ import { InvokeFunction, InvokeAnyFunction, FetchFunction, RequestSink, RequestLogReader } from '@tinycloud/sdk-services';
4
5
  import { EventEmitter } from 'events';
5
- import { InvokeFunction, InvokeAnyFunction, FetchFunction } from '@tinycloud/sdk-services';
6
6
 
7
7
  /**
8
8
  * Node.js-specific SignStrategy types for TinyCloud authorization.
@@ -1090,6 +1090,7 @@ declare class DelegatedAccess {
1090
1090
  constructor(session: TinyCloudSession, delegation: PortableDelegation, host: string, invoke: InvokeFunction, invokeAny?: InvokeAnyFunction, telemetry?: TelemetryConfig, lifetime?: {
1091
1091
  fetch: FetchFunction;
1092
1092
  track(context: ServiceContext): ServiceContext;
1093
+ requestSink?: RequestSink;
1093
1094
  });
1094
1095
  /**
1095
1096
  * Get the delegation this access was created from.
@@ -1230,6 +1231,8 @@ interface TinyCloudNodeConfig {
1230
1231
  signStrategy?: SignStrategy;
1231
1232
  /** Explicit TinyCloud server URL. When omitted, signIn resolves the user's host. */
1232
1233
  host?: string;
1234
+ /** Build a developer request log (sanitized, in-memory, bounded). Off by default. */
1235
+ requestLog?: boolean;
1233
1236
  /** TinyCloud location registry URL. Default: https://registry.tinycloud.xyz. */
1234
1237
  tinycloudRegistryUrl?: string | null;
1235
1238
  /**
@@ -1517,6 +1520,7 @@ declare class TinyCloudNode {
1517
1520
  private wasmBindings;
1518
1521
  private sessionManager;
1519
1522
  private _serviceGraph;
1523
+ private readonly _requestLog?;
1520
1524
  private _serviceContext?;
1521
1525
  private _kv?;
1522
1526
  private readonly replicationRuntime?;
@@ -1713,6 +1717,8 @@ declare class TinyCloudNode {
1713
1717
  */
1714
1718
  private setupAuth;
1715
1719
  private shouldUseBootstrapSignInRequest;
1720
+ /** Sanitized developer request events; undefined unless the node was built with `requestLog: true`. */
1721
+ get requests(): RequestLogReader | undefined;
1716
1722
  private createServiceGraphLifetime;
1717
1723
  private syncResolvedHostFromAuth;
1718
1724
  /**
@@ -1,8 +1,8 @@
1
1
  import * as _tinycloud_sdk_core from '@tinycloud/sdk-core';
2
2
  import { AutoSignStrategy, AutoRejectStrategy, CallbackStrategy, CapabilityPresentationEnvelopeV1, IUserAuthorization, ISigner, ISessionStorage, ISpaceCreationHandler, RegistryPolicy, RegistrySequenceStore, OnNodeNotFound, LocalNodeIdentityStore, IWasmBindings, ISessionManager, SiweConfig, Manifest, ComposedManifestRequest, ReplicationOptions, TinyCloudSession, PersistedSessionData, ClientSession, PermissionEntry, Extension, SignRequest, SpaceHostResult, SignInOptions, Delegation, DelegatedResource, TelemetryConfig, ServiceContext, IKVService, ISQLService, IDuckDbService, IHooksService, CreateOwnerDelegationParams as CreateOwnerDelegationParams$1, OwnerDelegationReceipt as OwnerDelegationReceipt$1, StorageFullEvent, IStorageService, INotificationHandler, IENSResolver, RetryPolicy, KVReplicaStorage, AccountService, InvokeAnyFunction as InvokeAnyFunction$1, UnifiedPolicyCapability, RegisterPolicyV3Input, RegisterPolicyV3Receipt, CanonicalJson, ShareDeliveryAuthorizationV3Receipt, IDataVaultService, IEncryptionService, NetworkDescriptor, ISecretsService, ICapabilityKeyRegistry, DelegationManager, ISpaceService, ISpace, ISharingService, DelegationResult, DelegationRevocationReceipt, DelegationStatus, ServiceSession, ReplicationControl, ResolvedDelegate } from '@tinycloud/sdk-core';
3
3
  import { ReplicaSessionStamp } from '@tinycloud/sdk-services/kv/replication';
4
+ import { InvokeFunction, InvokeAnyFunction, FetchFunction, RequestSink, RequestLogReader } from '@tinycloud/sdk-services';
4
5
  import { EventEmitter } from 'events';
5
- import { InvokeFunction, InvokeAnyFunction, FetchFunction } from '@tinycloud/sdk-services';
6
6
 
7
7
  /**
8
8
  * Node.js-specific SignStrategy types for TinyCloud authorization.
@@ -1090,6 +1090,7 @@ declare class DelegatedAccess {
1090
1090
  constructor(session: TinyCloudSession, delegation: PortableDelegation, host: string, invoke: InvokeFunction, invokeAny?: InvokeAnyFunction, telemetry?: TelemetryConfig, lifetime?: {
1091
1091
  fetch: FetchFunction;
1092
1092
  track(context: ServiceContext): ServiceContext;
1093
+ requestSink?: RequestSink;
1093
1094
  });
1094
1095
  /**
1095
1096
  * Get the delegation this access was created from.
@@ -1230,6 +1231,8 @@ interface TinyCloudNodeConfig {
1230
1231
  signStrategy?: SignStrategy;
1231
1232
  /** Explicit TinyCloud server URL. When omitted, signIn resolves the user's host. */
1232
1233
  host?: string;
1234
+ /** Build a developer request log (sanitized, in-memory, bounded). Off by default. */
1235
+ requestLog?: boolean;
1233
1236
  /** TinyCloud location registry URL. Default: https://registry.tinycloud.xyz. */
1234
1237
  tinycloudRegistryUrl?: string | null;
1235
1238
  /**
@@ -1517,6 +1520,7 @@ declare class TinyCloudNode {
1517
1520
  private wasmBindings;
1518
1521
  private sessionManager;
1519
1522
  private _serviceGraph;
1523
+ private readonly _requestLog?;
1520
1524
  private _serviceContext?;
1521
1525
  private _kv?;
1522
1526
  private readonly replicationRuntime?;
@@ -1713,6 +1717,8 @@ declare class TinyCloudNode {
1713
1717
  */
1714
1718
  private setupAuth;
1715
1719
  private shouldUseBootstrapSignInRequest;
1720
+ /** Sanitized developer request events; undefined unless the node was built with `requestLog: true`. */
1721
+ get requests(): RequestLogReader | undefined;
1716
1722
  private createServiceGraphLifetime;
1717
1723
  private syncResolvedHostFromAuth;
1718
1724
  /**
@@ -1,7 +1,7 @@
1
1
  import { ReplicationOptions, PermissionEntry, TinyCloudSession, KVReplicaStorage, ServiceContext, IWasmBindings, WasmRecapEntry } from '@tinycloud/sdk-core';
2
2
  import { ReplicaConsentAnchor, AuthorityRefusal, ReplicationAuthority } from '@tinycloud/sdk-services';
3
3
  import { ReplicaSessionStamp, ReplicaDenyInput } from '@tinycloud/sdk-services/kv/replication';
4
- import { T as TinyCloudNode } from './TinyCloudNode-DeV-L_14.cjs';
4
+ import { T as TinyCloudNode } from './TinyCloudNode-N4mfZI_k.cjs';
5
5
 
6
6
  /** Constructor-time validation; the space-dependent secrets check runs at sign-in and open. */
7
7
  declare function assertValidReplicationConfig(replication: (ReplicationOptions & {
@@ -1,7 +1,7 @@
1
1
  import { ReplicationOptions, PermissionEntry, TinyCloudSession, KVReplicaStorage, ServiceContext, IWasmBindings, WasmRecapEntry } from '@tinycloud/sdk-core';
2
2
  import { ReplicaConsentAnchor, AuthorityRefusal, ReplicationAuthority } from '@tinycloud/sdk-services';
3
3
  import { ReplicaSessionStamp, ReplicaDenyInput } from '@tinycloud/sdk-services/kv/replication';
4
- import { T as TinyCloudNode } from './TinyCloudNode-DeV-L_14.js';
4
+ import { T as TinyCloudNode } from './TinyCloudNode-N4mfZI_k.js';
5
5
 
6
6
  /** Constructor-time validation; the space-dependent secrets check runs at sign-in and open. */
7
7
  declare function assertValidReplicationConfig(replication: (ReplicationOptions & {
package/dist/core.cjs CHANGED
@@ -2121,6 +2121,167 @@ var NodeUserAuthorization = class {
2121
2121
  }
2122
2122
  };
2123
2123
 
2124
+ // src/recordingServiceContext.ts
2125
+ var import_sdk_services2 = require("@tinycloud/sdk-services");
2126
+ var import_requestLog = require("@tinycloud/sdk-services/telemetry/requestLog");
2127
+
2128
+ // src/recordingRegistry.ts
2129
+ var unrecordedFetches = /* @__PURE__ */ new WeakMap();
2130
+ function registerRecording(context, unrecordedFetch) {
2131
+ unrecordedFetches.set(context, unrecordedFetch);
2132
+ }
2133
+ function unrecordedFetchOf(context) {
2134
+ const unrecordedFetch = unrecordedFetches.get(context);
2135
+ return unrecordedFetch ? unrecordedFetch() : context.fetch;
2136
+ }
2137
+
2138
+ // src/recordingServiceContext.ts
2139
+ var SYNC_ABILITY = "tinycloud.kv/sync";
2140
+ var INFO_PATH = "/info";
2141
+ function outcomeCodeOf(response) {
2142
+ return response === void 0 ? "ERROR" : response.ok ? "ok" : `HTTP_${response.status}`;
2143
+ }
2144
+ function replicationOp(url, meta) {
2145
+ if (meta !== void 0) return meta.action === SYNC_ABILITY ? `${meta.service}/sync` : void 0;
2146
+ return isInfoUrl(url) ? "sync/info" : void 0;
2147
+ }
2148
+ function isInfoUrl(url) {
2149
+ try {
2150
+ return new URL(url).pathname.replace(/\/+$/, "") === INFO_PATH;
2151
+ } catch {
2152
+ return false;
2153
+ }
2154
+ }
2155
+ function replicationEvent(sample, op) {
2156
+ return (0, import_requestLog.buildRequestEvent)({
2157
+ service: "sync",
2158
+ op,
2159
+ url: sample.url,
2160
+ bytes: (0, import_requestLog.requestBytesOf)(sample.init?.body, sample.response?.headers.get("content-length")),
2161
+ durationMs: performance.now() - sample.clock,
2162
+ attempts: sample.attempts ?? 1,
2163
+ ok: sample.response?.ok === true,
2164
+ code: outcomeCodeOf(sample.response),
2165
+ at: sample.at
2166
+ });
2167
+ }
2168
+ function createServiceContext(config) {
2169
+ const { requestSink, ...base } = config;
2170
+ return requestSink === void 0 ? new import_sdk_services2.ServiceContext(base) : new RecordingServiceContext(base, requestSink);
2171
+ }
2172
+ var RecordingServiceContext = class extends import_sdk_services2.ServiceContext {
2173
+ constructor(config, sink) {
2174
+ const pending = {};
2175
+ super({
2176
+ ...config,
2177
+ invoke: rememberInvoke(config.invoke, sink, pending),
2178
+ invokeAny: config.invokeAny ? rememberInvokeAny(config.invokeAny, sink, pending) : void 0
2179
+ });
2180
+ this.requestSink = sink;
2181
+ this.pending = pending;
2182
+ registerRecording(this, () => this.unrecordedFetch);
2183
+ }
2184
+ /** Per-call recording; `_fetch` is read at access time, so overrides apply. */
2185
+ get fetch() {
2186
+ const raw = super.fetch;
2187
+ return (url, init) => this.recordCall(raw, url, init);
2188
+ }
2189
+ /** Transport without per-call recording. */
2190
+ get unrecordedFetch() {
2191
+ return super.fetch;
2192
+ }
2193
+ /**
2194
+ * One event per logical request. The first resolved attempt's init is the logged request and
2195
+ * `attempts` counts resolved builds. A retired context throws before the first build and records
2196
+ * nothing, as master does.
2197
+ */
2198
+ async invokeWithRetry(url, build) {
2199
+ const at = Date.now();
2200
+ const clock = performance.now();
2201
+ let reached = false;
2202
+ let attempts = 0;
2203
+ let first;
2204
+ let firstMeta;
2205
+ let response;
2206
+ const attempt = async () => {
2207
+ reached = true;
2208
+ this.pending.meta = void 0;
2209
+ const built = build();
2210
+ const meta = this.pending.meta;
2211
+ const init = await built;
2212
+ attempts += 1;
2213
+ if (first === void 0) {
2214
+ first = init;
2215
+ firstMeta = meta;
2216
+ }
2217
+ return init;
2218
+ };
2219
+ try {
2220
+ response = await super.invokeWithRetry(url, attempt);
2221
+ return response;
2222
+ } finally {
2223
+ if (reached) {
2224
+ this.observeAttempt({ url, init: first, response, meta: firstMeta, at, clock, attempts });
2225
+ }
2226
+ }
2227
+ }
2228
+ async recordCall(fetchFn, url, init) {
2229
+ const at = Date.now();
2230
+ const clock = performance.now();
2231
+ let response;
2232
+ try {
2233
+ response = await fetchFn(url, init);
2234
+ return response;
2235
+ } finally {
2236
+ this.observeAttempt({ url, init, response, at, clock });
2237
+ }
2238
+ }
2239
+ observeAttempt(sample) {
2240
+ try {
2241
+ const replication = replicationOp(sample.url, sample.meta ?? (0, import_requestLog.invocationMetaOf)(sample.init?.headers));
2242
+ if (replication !== void 0) {
2243
+ this.requestSink.record(replicationEvent(sample, replication));
2244
+ return;
2245
+ }
2246
+ const headers = sample.init?.headers;
2247
+ if (sample.meta !== void 0 && typeof headers === "object" && headers !== null) {
2248
+ this.requestSink.remember(headers, sample.meta);
2249
+ }
2250
+ this.requestSink.observe({
2251
+ url: sample.url,
2252
+ requestHeaders: headers,
2253
+ requestBody: sample.init?.body,
2254
+ responseContentLength: sample.response?.headers.get("content-length"),
2255
+ ok: sample.response?.ok === true,
2256
+ code: outcomeCodeOf(sample.response),
2257
+ durationMs: performance.now() - sample.clock,
2258
+ attempts: sample.attempts ?? 1,
2259
+ at: sample.at
2260
+ });
2261
+ } catch {
2262
+ }
2263
+ }
2264
+ };
2265
+ function rememberInvoke(invoke, sink, pending) {
2266
+ return (session, service, path, action, facts) => {
2267
+ const headers = invoke(session, service, path, action, facts);
2268
+ const meta = { service, path, action, spaceId: session.spaceId };
2269
+ pending.meta = meta;
2270
+ sink.remember(headers, meta);
2271
+ return headers;
2272
+ };
2273
+ }
2274
+ function rememberInvokeAny(invokeAny, sink, pending) {
2275
+ return (session, entries, facts) => {
2276
+ const headers = invokeAny(session, entries, facts);
2277
+ const first = entries[0];
2278
+ const meta = first === void 0 ? void 0 : { service: first.service, path: first.path, action: first.action, spaceId: first.spaceId ?? session.spaceId };
2279
+ pending.meta = meta;
2280
+ if (meta !== void 0) sink.remember(headers, meta);
2281
+ return headers;
2282
+ };
2283
+ }
2284
+
2124
2285
  // src/delegation.ts
2125
2286
  var import_sdk_core5 = require("@tinycloud/sdk-core");
2126
2287
  var RuntimeDelegationRejectedError = class extends Error {
@@ -2602,7 +2763,8 @@ function deserializeDelegation(data) {
2602
2763
  // src/TinyCloudNode.ts
2603
2764
  var import_siwe2 = require("siwe");
2604
2765
  var import_sdk_core10 = require("@tinycloud/sdk-core");
2605
- var import_sdk_services2 = require("@tinycloud/sdk-services");
2766
+ var import_sdk_services3 = require("@tinycloud/sdk-services");
2767
+ var import_requestLog2 = require("@tinycloud/sdk-services/telemetry/requestLog");
2606
2768
 
2607
2769
  // src/account/AccountService.ts
2608
2770
  var import_sdk_core6 = require("@tinycloud/sdk-core");
@@ -2620,12 +2782,13 @@ var DelegatedAccess = class {
2620
2782
  this.session = session;
2621
2783
  this._delegation = delegation;
2622
2784
  this.host = host;
2623
- const context = new import_sdk_core7.ServiceContext({
2785
+ const context = createServiceContext({
2624
2786
  invoke,
2625
2787
  invokeAny,
2626
2788
  fetch: lifetime?.fetch ?? globalThis.fetch.bind(globalThis),
2627
2789
  hosts: [host],
2628
- telemetry
2790
+ telemetry,
2791
+ requestSink: lifetime?.requestSink
2629
2792
  });
2630
2793
  this._serviceContext = lifetime ? lifetime.track(context) : context;
2631
2794
  const prefix = this._delegation.path.replace(/\/$/, "");
@@ -3215,11 +3378,12 @@ function createDefaultSessionKey(wasm) {
3215
3378
  return { manager, keyId, jwk: JSON.parse(jwkStr) };
3216
3379
  }
3217
3380
  var ServiceGraphLifetime = class {
3218
- constructor(invokeFn, invokeAnyFn, fetchFn, observeContext) {
3381
+ constructor(invokeFn, invokeAnyFn, fetchFn, observeContext, requestSink) {
3219
3382
  this.invokeFn = invokeFn;
3220
3383
  this.invokeAnyFn = invokeAnyFn;
3221
3384
  this.fetchFn = fetchFn;
3222
3385
  this.observeContext = observeContext;
3386
+ this.requestSink = requestSink;
3223
3387
  this.abortController = new AbortController();
3224
3388
  this.contexts = /* @__PURE__ */ new Set();
3225
3389
  this.retired = false;
@@ -3790,6 +3954,7 @@ var _TinyCloudNode = class _TinyCloudNode {
3790
3954
  this.sessionManager = sessionKey.manager;
3791
3955
  this.sessionKeyId = sessionKey.keyId;
3792
3956
  this.sessionKeyJwk = sessionKey.jwk;
3957
+ this._requestLog = config.requestLog === true ? new import_requestLog2.RequestLog() : void 0;
3793
3958
  this._serviceGraph = this.createServiceGraphLifetime();
3794
3959
  this._capabilityRegistry = new import_sdk_core10.CapabilityKeyRegistry();
3795
3960
  this._keyProvider = new WasmKeyProvider({
@@ -3961,7 +4126,8 @@ var _TinyCloudNode = class _TinyCloudNode {
3961
4126
  createKVService: (config) => {
3962
4127
  const prefix = config.pathPrefix?.replace(/\/$/, "");
3963
4128
  const kvService = new import_sdk_core10.KVService({ prefix });
3964
- const kvContext = receiveOnlyGraph.track(new import_sdk_core10.ServiceContext({
4129
+ const kvContext = receiveOnlyGraph.track(createServiceContext({
4130
+ requestSink: receiveOnlyGraph.requestSink,
3965
4131
  invoke: config.invoke,
3966
4132
  fetch: config.fetch ?? globalThis.fetch.bind(globalThis),
3967
4133
  hosts: config.hosts,
@@ -4024,6 +4190,10 @@ var _TinyCloudNode = class _TinyCloudNode {
4024
4190
  shouldUseBootstrapSignInRequest(config) {
4025
4191
  return config.autoBootstrapAccount !== false && config.manifest === void 0 && config.capabilityRequest === void 0 && (config.prefix ?? "default") === "default" && isOpenKeyAutoSignStrategy(config.signStrategy);
4026
4192
  }
4193
+ /** Sanitized developer request events; undefined unless the node was built with `requestLog: true`. */
4194
+ get requests() {
4195
+ return this._requestLog === void 0 ? void 0 : (0, import_requestLog2.readOnlyRequestLog)(this._requestLog);
4196
+ }
4027
4197
  createServiceGraphLifetime() {
4028
4198
  return new ServiceGraphLifetime(
4029
4199
  this.invokeWithRuntimePermissions,
@@ -4034,7 +4204,9 @@ var _TinyCloudNode = class _TinyCloudNode {
4034
4204
  (url, init) => globalThis.fetch(url, init),
4035
4205
  (context) => {
4036
4206
  this.storageFull.observe(context);
4037
- }
4207
+ if (this._requestLog) (0, import_requestLog2.observeReplicaReads)(context, this._requestLog);
4208
+ },
4209
+ this._requestLog
4038
4210
  );
4039
4211
  }
4040
4212
  syncResolvedHostFromAuth() {
@@ -4357,6 +4529,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4357
4529
  this.hostedOwnedSpaceIds.clear();
4358
4530
  this.cancelOwnedSpaceHosts();
4359
4531
  this.storageFull.clear();
4532
+ this._requestLog?.clear();
4360
4533
  this._bootstrapSkipped = false;
4361
4534
  this._bootstrapStatus = { skipped: false };
4362
4535
  this.replicationDelegateSession = void 0;
@@ -4448,7 +4621,10 @@ var _TinyCloudNode = class _TinyCloudNode {
4448
4621
  const denial = await this.denyReplicaGate({ reason: "account_switch", principal: priorPrincipal, next: nextPrincipal });
4449
4622
  this.replicaRestoreGate = denial?.status === "unguarded" ? "gate_unavailable" : "stamp_stale";
4450
4623
  }
4451
- if (address !== this._address || chainId !== this._chainId) this.storageFull.clear();
4624
+ if (address !== this._address || chainId !== this._chainId) {
4625
+ this.storageFull.clear();
4626
+ this._requestLog?.clear();
4627
+ }
4452
4628
  this._address = address;
4453
4629
  this._chainId = chainId;
4454
4630
  this._kv = void 0;
@@ -5027,10 +5203,11 @@ var _TinyCloudNode = class _TinyCloudNode {
5027
5203
  const contexts = [context];
5028
5204
  const accountSpaceId = this.ownedSpaceId(import_sdk_core10.ACCOUNT_REGISTRY_SPACE);
5029
5205
  const createScopedContext = (spaceId) => {
5030
- const scoped = this._serviceGraph.track(new import_sdk_core10.ServiceContext({
5206
+ const scoped = this._serviceGraph.track(createServiceContext({
5207
+ requestSink: this._serviceGraph.requestSink,
5031
5208
  invoke: context.invoke,
5032
5209
  invokeAny: context.invokeAny,
5033
- fetch: context.fetch,
5210
+ fetch: unrecordedFetchOf(context),
5034
5211
  hosts: context.hosts,
5035
5212
  telemetry: this.config.telemetry,
5036
5213
  retryPolicy: this.config.retryPolicy
@@ -5082,7 +5259,7 @@ var _TinyCloudNode = class _TinyCloudNode {
5082
5259
  if (!this.currentSessionCoversAccountRegistry()) {
5083
5260
  if (!_TinyCloudNode.accountRegistrySkipLogged) {
5084
5261
  _TinyCloudNode.accountRegistrySkipLogged = true;
5085
- import_sdk_services2.tinyCloudDebugLogger.log("registry.skip");
5262
+ import_sdk_services3.tinyCloudDebugLogger.log("registry.skip");
5086
5263
  }
5087
5264
  return;
5088
5265
  }
@@ -5094,10 +5271,11 @@ var _TinyCloudNode = class _TinyCloudNode {
5094
5271
  const promise = this.enqueueAccountRegistryOperation(session, async () => {
5095
5272
  const sharedContext = this._serviceContext;
5096
5273
  if (!sharedContext) return;
5097
- const context = this._serviceGraph.track(new import_sdk_core10.ServiceContext({
5274
+ const context = this._serviceGraph.track(createServiceContext({
5275
+ requestSink: this._serviceGraph.requestSink,
5098
5276
  invoke: sharedContext.invoke,
5099
5277
  invokeAny: sharedContext.invokeAny,
5100
- fetch: sharedContext.fetch,
5278
+ fetch: unrecordedFetchOf(sharedContext),
5101
5279
  hosts: sharedContext.hosts,
5102
5280
  telemetry: this.config.telemetry,
5103
5281
  retryPolicy: this.config.retryPolicy
@@ -5831,6 +6009,7 @@ var _TinyCloudNode = class _TinyCloudNode {
5831
6009
  if (resolvedHost) this.config.host = resolvedHost;
5832
6010
  if (spaceOwnerKey(serviceSession.spaceId) !== this.currentStorageOwner()) {
5833
6011
  this.storageFull.clear();
6012
+ this._requestLog?.clear();
5834
6013
  }
5835
6014
  this._address = stagedAddress;
5836
6015
  this._chainId = stagedChainId;
@@ -5950,7 +6129,8 @@ var _TinyCloudNode = class _TinyCloudNode {
5950
6129
  const coreContext = graph.track(core.serviceContext);
5951
6130
  coreContext.setSession(input.serviceSession);
5952
6131
  }
5953
- const serviceContext = graph.track(new import_sdk_core10.ServiceContext({
6132
+ const serviceContext = graph.track(createServiceContext({
6133
+ requestSink: graph.requestSink,
5954
6134
  invoke: graph.invoke,
5955
6135
  invokeAny: graph.invokeAny,
5956
6136
  fetch: graph.fetch,
@@ -6038,7 +6218,8 @@ var _TinyCloudNode = class _TinyCloudNode {
6038
6218
  },
6039
6219
  createKVService: (config) => {
6040
6220
  const service = new import_sdk_core10.KVService({ prefix: config.pathPrefix?.replace(/\/$/, "") });
6041
- const context = graph.track(new import_sdk_core10.ServiceContext({
6221
+ const context = graph.track(createServiceContext({
6222
+ requestSink: graph.requestSink,
6042
6223
  invoke: config.invoke,
6043
6224
  fetch: config.fetch ?? globalThis.fetch.bind(globalThis),
6044
6225
  hosts: config.hosts,
@@ -6078,7 +6259,8 @@ var _TinyCloudNode = class _TinyCloudNode {
6078
6259
  createKVService: (spaceId) => {
6079
6260
  graph.assertActive();
6080
6261
  const scopedKv = new import_sdk_core10.KVService({});
6081
- const context = graph.track(new import_sdk_core10.ServiceContext({
6262
+ const context = graph.track(createServiceContext({
6263
+ requestSink: graph.requestSink,
6082
6264
  invoke: graph.invoke,
6083
6265
  invokeAny: graph.invokeAny,
6084
6266
  fetch: graph.fetch,
@@ -6094,7 +6276,8 @@ var _TinyCloudNode = class _TinyCloudNode {
6094
6276
  createVaultService: (spaceId) => {
6095
6277
  graph.assertActive();
6096
6278
  const scopedKv = new import_sdk_core10.KVService({});
6097
- const context = graph.track(new import_sdk_core10.ServiceContext({
6279
+ const context = graph.track(createServiceContext({
6280
+ requestSink: graph.requestSink,
6098
6281
  invoke: graph.invoke,
6099
6282
  invokeAny: graph.invokeAny,
6100
6283
  fetch: graph.fetch,
@@ -6493,7 +6676,8 @@ var _TinyCloudNode = class _TinyCloudNode {
6493
6676
  this._serviceGraph.fetch
6494
6677
  );
6495
6678
  this._serviceGraph.track(this.tc.serviceContext);
6496
- this._serviceContext = this._serviceGraph.track(new import_sdk_core10.ServiceContext({
6679
+ this._serviceContext = this._serviceGraph.track(createServiceContext({
6680
+ requestSink: this._serviceGraph.requestSink,
6497
6681
  invoke: this._serviceGraph.invoke,
6498
6682
  invokeAny: this._serviceGraph.invokeAny,
6499
6683
  fetch: this._serviceGraph.fetch,
@@ -6541,10 +6725,11 @@ var _TinyCloudNode = class _TinyCloudNode {
6541
6725
  createSpaceScopedKVService(spaceId) {
6542
6726
  const kvService = new import_sdk_core10.KVService({});
6543
6727
  if (this._serviceContext) {
6544
- const spaceScopedContext = this._serviceGraph.track(new import_sdk_core10.ServiceContext({
6728
+ const spaceScopedContext = this._serviceGraph.track(createServiceContext({
6729
+ requestSink: this._serviceGraph.requestSink,
6545
6730
  invoke: this._serviceContext.invoke,
6546
6731
  invokeAny: this._serviceContext.invokeAny,
6547
- fetch: this._serviceContext.fetch,
6732
+ fetch: unrecordedFetchOf(this._serviceContext),
6548
6733
  hosts: this._serviceContext.hosts,
6549
6734
  telemetry: this.config.telemetry,
6550
6735
  retryPolicy: this.config.retryPolicy
@@ -6732,7 +6917,7 @@ var _TinyCloudNode = class _TinyCloudNode {
6732
6917
  );
6733
6918
  graph.assertActive();
6734
6919
  if (!response.ok) {
6735
- const permissionHint = response.status === 401 || response.status === 403 ? (0, import_sdk_services2.parsePermissionHint)({
6920
+ const permissionHint = response.status === 401 || response.status === 403 ? (0, import_sdk_services3.parsePermissionHint)({
6736
6921
  service: "tinycloud.encryption",
6737
6922
  path: networkId,
6738
6923
  actions: ["tinycloud.encryption/decrypt"]
@@ -7010,7 +7195,8 @@ var _TinyCloudNode = class _TinyCloudNode {
7010
7195
  createKVService: (config) => {
7011
7196
  graph.assertActive();
7012
7197
  const service = new import_sdk_core10.KVService({ prefix: config.pathPrefix?.replace(/\/$/, "") });
7013
- const context = graph.track(new import_sdk_core10.ServiceContext({
7198
+ const context = graph.track(createServiceContext({
7199
+ requestSink: graph.requestSink,
7014
7200
  invoke: config.invoke,
7015
7201
  fetch: config.fetch ?? graph.fetch,
7016
7202
  hosts: config.hosts,
@@ -7331,10 +7517,11 @@ var _TinyCloudNode = class _TinyCloudNode {
7331
7517
  throw new Error("Not signed in. Call signIn() first.");
7332
7518
  }
7333
7519
  const sql = new import_sdk_core10.SQLService({});
7334
- const spaceScopedContext = this._serviceGraph.track(new import_sdk_core10.ServiceContext({
7520
+ const spaceScopedContext = this._serviceGraph.track(createServiceContext({
7521
+ requestSink: this._serviceGraph.requestSink,
7335
7522
  invoke: this._serviceContext.invoke,
7336
7523
  invokeAny: this._serviceContext.invokeAny,
7337
- fetch: this._serviceContext.fetch,
7524
+ fetch: unrecordedFetchOf(this._serviceContext),
7338
7525
  hosts: this._serviceContext.hosts,
7339
7526
  telemetry: this.config.telemetry,
7340
7527
  retryPolicy: this.config.retryPolicy
@@ -7362,10 +7549,11 @@ var _TinyCloudNode = class _TinyCloudNode {
7362
7549
  throw new Error("Not signed in. Call signIn() first.");
7363
7550
  }
7364
7551
  const kv = new import_sdk_core10.KVService({});
7365
- const spaceScopedContext = this._serviceGraph.track(new import_sdk_core10.ServiceContext({
7552
+ const spaceScopedContext = this._serviceGraph.track(createServiceContext({
7553
+ requestSink: this._serviceGraph.requestSink,
7366
7554
  invoke: this._serviceContext.invoke,
7367
7555
  invokeAny: this._serviceContext.invokeAny,
7368
- fetch: this._serviceContext.fetch,
7556
+ fetch: unrecordedFetchOf(this._serviceContext),
7369
7557
  hosts: this._serviceContext.hosts
7370
7558
  }));
7371
7559
  spaceScopedContext.setSession({ ...this._serviceContext.session, spaceId });
@@ -7528,7 +7716,7 @@ var _TinyCloudNode = class _TinyCloudNode {
7528
7716
  );
7529
7717
  if (result.status !== "ok") {
7530
7718
  if (result.status === "permission_required") {
7531
- const hint = (0, import_sdk_services2.parsePermissionHint)(result.hint);
7719
+ const hint = (0, import_sdk_services3.parsePermissionHint)(result.hint);
7532
7720
  if (hint === void 0) return { status: "read_failed" };
7533
7721
  return {
7534
7722
  status: "permission_required",
@@ -8151,7 +8339,8 @@ var _TinyCloudNode = class _TinyCloudNode {
8151
8339
  }
8152
8340
  if (this._serviceContext) {
8153
8341
  const publicKV = new import_sdk_core10.KVService({ prefix: "" });
8154
- const publicContext = this._serviceGraph.track(new import_sdk_core10.ServiceContext({
8342
+ const publicContext = this._serviceGraph.track(createServiceContext({
8343
+ requestSink: this._serviceGraph.requestSink,
8155
8344
  invoke: this._serviceGraph.invoke,
8156
8345
  fetch: this._serviceGraph.fetch,
8157
8346
  hosts: this._serviceContext.hosts,