@tinycloud/node-sdk 3.11.0 → 3.11.1-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.
@@ -859,9 +859,10 @@ declare class NodeUserAuthorization implements IUserAuthorization {
859
859
  /** Validate a candidate without activating a network session, so paste retries reuse one preparation. */
860
860
  private verifyOpenKeyResult;
861
861
  /**
862
- * Clear persisted session data.
862
+ * Clear persisted session data. With `ownSignature`, a stored session that provably has a different
863
+ * signature (a newer sign-in for the same address) is left alone.
863
864
  */
864
- clearPersistedSession(address?: string): Promise<void>;
865
+ clearPersistedSession(address?: string, ownSignature?: string): Promise<void>;
865
866
  /**
866
867
  * Check if a session is persisted for an address.
867
868
  */
@@ -859,9 +859,10 @@ declare class NodeUserAuthorization implements IUserAuthorization {
859
859
  /** Validate a candidate without activating a network session, so paste retries reuse one preparation. */
860
860
  private verifyOpenKeyResult;
861
861
  /**
862
- * Clear persisted session data.
862
+ * Clear persisted session data. With `ownSignature`, a stored session that provably has a different
863
+ * signature (a newer sign-in for the same address) is left alone.
863
864
  */
864
- clearPersistedSession(address?: string): Promise<void>;
865
+ clearPersistedSession(address?: string, ownSignature?: string): Promise<void>;
865
866
  /**
866
867
  * Check if a session is persisted for an address.
867
868
  */
@@ -90,6 +90,8 @@ declare class FileSessionStorage implements ISessionStorage {
90
90
  save(address: string, session: PersistedSessionData): Promise<void>;
91
91
  /**
92
92
  * Load a session for an address.
93
+ *
94
+ * Rejects when the file exists but cannot be read or parsed; the file is left in place.
93
95
  */
94
96
  load(address: string): Promise<PersistedSessionData | null>;
95
97
  /**
@@ -90,6 +90,8 @@ declare class FileSessionStorage implements ISessionStorage {
90
90
  save(address: string, session: PersistedSessionData): Promise<void>;
91
91
  /**
92
92
  * Load a session for an address.
93
+ *
94
+ * Rejects when the file exists but cannot be read or parsed; the file is left in place.
93
95
  */
94
96
  load(address: string): Promise<PersistedSessionData | null>;
95
97
  /**
@@ -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 { ReplicaLocalEraseOutcome, ReplicaPurgeLocalCopyResult, ReplicaPurgeTrigger, ReplicaPurgeFailedEvent, ReplicaSessionStamp, ReplicaDenyInput } from '@tinycloud/sdk-services/kv/replication';
4
- import { T as TinyCloudNode } from './TinyCloudNode-CnVaWnAX.cjs';
4
+ import { T as TinyCloudNode } from './TinyCloudNode-BmpjkpWR.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 { ReplicaLocalEraseOutcome, ReplicaPurgeLocalCopyResult, ReplicaPurgeTrigger, ReplicaPurgeFailedEvent, ReplicaSessionStamp, ReplicaDenyInput } from '@tinycloud/sdk-services/kv/replication';
4
- import { T as TinyCloudNode } from './TinyCloudNode-BlNcvo1c.js';
4
+ import { T as TinyCloudNode } from './TinyCloudNode-AUotFGzy.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
@@ -268,10 +268,22 @@ var FileSessionStorage = class {
268
268
  async save(address, session) {
269
269
  const filePath = this.getFilePath(address);
270
270
  const data = JSON.stringify(session, null, 2);
271
- (0, import_fs.writeFileSync)(filePath, data, "utf-8");
271
+ const tempPath = `${filePath}.${process.pid}.${process.hrtime.bigint()}.tmp`;
272
+ try {
273
+ (0, import_fs.writeFileSync)(tempPath, data, { mode: 384, flag: "wx" });
274
+ (0, import_fs.renameSync)(tempPath, filePath);
275
+ } catch (error) {
276
+ try {
277
+ (0, import_fs.unlinkSync)(tempPath);
278
+ } catch {
279
+ }
280
+ throw error;
281
+ }
272
282
  }
273
283
  /**
274
284
  * Load a session for an address.
285
+ *
286
+ * Rejects when the file exists but cannot be read or parsed; the file is left in place.
275
287
  */
276
288
  async load(address) {
277
289
  const filePath = this.getFilePath(address);
@@ -295,11 +307,9 @@ var FileSessionStorage = class {
295
307
  }
296
308
  return session;
297
309
  } catch (error) {
298
- try {
299
- (0, import_fs.unlinkSync)(filePath);
300
- } catch {
301
- }
302
- return null;
310
+ throw new Error(
311
+ `Could not read session file ${filePath}: ${error}`
312
+ );
303
313
  }
304
314
  }
305
315
  /**
@@ -453,6 +463,18 @@ function refuseCaveated(input) {
453
463
  var import_sdk_core4 = require("@tinycloud/sdk-core");
454
464
  var defaultSignStrategy = { type: "auto-sign" };
455
465
 
466
+ // src/storage/clearPersistedSessionIfOwn.ts
467
+ async function clearPersistedSessionIfOwn(storage, address, ownSignature) {
468
+ if (ownSignature !== void 0) {
469
+ try {
470
+ const stored = await storage.load(address);
471
+ if (stored !== null && stored.signature !== ownSignature) return;
472
+ } catch {
473
+ }
474
+ }
475
+ await storage.clear(address);
476
+ }
477
+
456
478
  // src/authorization/NodeUserAuthorization.ts
457
479
  function assertMaxExpiresAt(value) {
458
480
  if (value === void 0 || typeof value === "function") return;
@@ -2269,12 +2291,13 @@ var NodeUserAuthorization = class {
2269
2291
  };
2270
2292
  }
2271
2293
  /**
2272
- * Clear persisted session data.
2294
+ * Clear persisted session data. With `ownSignature`, a stored session that provably has a different
2295
+ * signature (a newer sign-in for the same address) is left alone.
2273
2296
  */
2274
- async clearPersistedSession(address) {
2297
+ async clearPersistedSession(address, ownSignature) {
2275
2298
  const targetAddress = address ?? this._address;
2276
2299
  if (targetAddress) {
2277
- await this.sessionStorage.clear(targetAddress);
2300
+ await clearPersistedSessionIfOwn(this.sessionStorage, targetAddress, ownSignature);
2278
2301
  }
2279
2302
  }
2280
2303
  /**
@@ -4757,6 +4780,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4757
4780
  const leavingChainId = this._chainId;
4758
4781
  const leavingPrincipal = this.replicaSessionPrincipal ?? (leavingAddress ? (0, import_sdk_core11.pkhDid)(leavingAddress, leavingChainId) : void 0);
4759
4782
  const leavingAuth = this.auth;
4783
+ const leavingSignature = leavingAuth?.tinyCloudSession?.signature ?? this._restoredTcSession?.signature;
4760
4784
  const leavingContext = this._serviceContext;
4761
4785
  let denied;
4762
4786
  let erased;
@@ -4773,7 +4797,7 @@ var _TinyCloudNode = class _TinyCloudNode {
4773
4797
  if (denied) erased = await this.eraseReplicaLocalCopy("sign_out", denied.status);
4774
4798
  } finally {
4775
4799
  if (leavingAddress) {
4776
- await (leavingAuth ? leavingAuth.clearPersistedSession(leavingAddress) : this.config.sessionStorage?.clear(leavingAddress));
4800
+ await (leavingAuth ? leavingAuth.clearPersistedSession(leavingAddress, leavingSignature) : this.config.sessionStorage && clearPersistedSessionIfOwn(this.config.sessionStorage, leavingAddress, leavingSignature));
4777
4801
  }
4778
4802
  }
4779
4803
  }