@telecrypt-io/storage 0.4.0 → 0.5.8

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.
@@ -8,7 +8,8 @@
8
8
  * the client's own live background sync loop (already running in this
9
9
  * process since TeleCryptIOStorage.create() started it).
10
10
  */
11
- export declare function waitForCondition<T>(check: () => T | null | undefined, opts?: {
11
+ export declare function waitForCondition<T>(check: (signal?: AbortSignal) => T | null | undefined | Promise<T | null | undefined>, opts?: {
12
12
  timeoutMs?: number;
13
13
  intervalMs?: number;
14
+ signal?: AbortSignal;
14
15
  }): Promise<T>;
package/dist/core/poll.js CHANGED
@@ -12,13 +12,69 @@ export async function waitForCondition(check, opts) {
12
12
  const timeoutMs = opts?.timeoutMs ?? 15000;
13
13
  const intervalMs = opts?.intervalMs ?? 300;
14
14
  const deadline = Date.now() + timeoutMs;
15
+ const timeoutError = () => new Error(`timed out after ${timeoutMs}ms waiting for condition`);
16
+ const abortError = () => {
17
+ const reason = opts?.signal?.reason;
18
+ return reason instanceof Error ? reason : new Error("condition wait aborted");
19
+ };
15
20
  for (;;) {
16
- const result = check();
21
+ if (opts?.signal?.aborted)
22
+ throw abortError();
23
+ const remaining = deadline - Date.now();
24
+ if (remaining < 0)
25
+ throw timeoutError();
26
+ // A condition check may perform I/O. Awaiting it directly would make the
27
+ // outer timeout ineffective when that I/O hangs forever. The per-attempt
28
+ // controller gives checks that support AbortSignal a chance to stop, while
29
+ // the race still bounds checks that do not.
30
+ const checkController = new AbortController();
31
+ let timer;
32
+ const abortFromCaller = () => checkController.abort(opts?.signal?.reason);
33
+ opts?.signal?.addEventListener("abort", abortFromCaller, { once: true });
34
+ const checkPromise = Promise.resolve().then(() => check(checkController.signal));
35
+ const timeoutPromise = new Promise((_, reject) => {
36
+ timer = setTimeout(() => {
37
+ checkController.abort();
38
+ reject(timeoutError());
39
+ }, remaining);
40
+ });
41
+ let callerAbort;
42
+ const callerAbortPromise = new Promise((_, reject) => {
43
+ callerAbort = () => reject(abortError());
44
+ opts?.signal?.addEventListener("abort", callerAbort, { once: true });
45
+ });
46
+ let result;
47
+ try {
48
+ result = await Promise.race([checkPromise, timeoutPromise, callerAbortPromise]);
49
+ }
50
+ finally {
51
+ if (timer !== undefined)
52
+ clearTimeout(timer);
53
+ opts?.signal?.removeEventListener("abort", abortFromCaller);
54
+ if (callerAbort)
55
+ opts?.signal?.removeEventListener("abort", callerAbort);
56
+ }
17
57
  if (result !== null && result !== undefined)
18
58
  return result;
19
59
  if (Date.now() >= deadline) {
20
- throw new Error(`timed out after ${timeoutMs}ms waiting for condition`);
60
+ throw timeoutError();
21
61
  }
22
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
62
+ const delay = Math.min(intervalMs, deadline - Date.now());
63
+ await new Promise((resolve, reject) => {
64
+ let timer;
65
+ const onAbort = () => {
66
+ clearTimeout(timer);
67
+ opts?.signal?.removeEventListener("abort", onAbort);
68
+ reject(abortError());
69
+ };
70
+ const done = () => {
71
+ opts?.signal?.removeEventListener("abort", onAbort);
72
+ resolve();
73
+ };
74
+ timer = setTimeout(done, Math.max(0, delay));
75
+ opts?.signal?.addEventListener("abort", onAbort, { once: true });
76
+ if (opts?.signal?.aborted)
77
+ onAbort();
78
+ });
23
79
  }
24
80
  }
@@ -53,6 +53,20 @@ export interface RecoveryRestore {
53
53
  imported: number;
54
54
  total: number;
55
55
  }
56
+ /** One atomic recovery topology snapshot used to decide whether setup is safe. */
57
+ export interface RecoveryStatus {
58
+ state: "unconfigured" | "ready" | "partial";
59
+ crossSigning: {
60
+ publicKeysOnDevice: boolean;
61
+ privateKeysCachedLocally: boolean;
62
+ privateKeysInSecretStorage: boolean;
63
+ };
64
+ secretStorage: {
65
+ ready: boolean;
66
+ defaultKeyId: string | null;
67
+ };
68
+ backupVersion: string | null;
69
+ }
56
70
  export interface FileDetails {
57
71
  name: string;
58
72
  mimetype: string | null;
@@ -0,0 +1,3 @@
1
+ /** Small shared input validators kept out of the public core barrel. */
2
+ export declare const MAX_NAME_LENGTH = 512;
3
+ export declare function validateName(value: unknown, label: string): asserts value is string;
@@ -0,0 +1,10 @@
1
+ /** Small shared input validators kept out of the public core barrel. */
2
+ export const MAX_NAME_LENGTH = 512;
3
+ export function validateName(value, label) {
4
+ if (typeof value !== "string" ||
5
+ value.length === 0 ||
6
+ value.length > MAX_NAME_LENGTH ||
7
+ /[\u0000-\u001f\u007f]/.test(value)) {
8
+ throw new Error(`invalid ${label}`);
9
+ }
10
+ }
package/dist/index.d.ts CHANGED
@@ -1 +1,3 @@
1
1
  export { TeleCryptIOStorage } from "./TeleCryptIOStorage.js";
2
+ /** The full platform-agnostic surface for user-facing clients. */
3
+ export * from "./core/index.js";
package/dist/index.js CHANGED
@@ -1 +1,3 @@
1
1
  export { TeleCryptIOStorage } from "./TeleCryptIOStorage.js";
2
+ /** The full platform-agnostic surface for user-facing clients. */
3
+ export * from "./core/index.js";
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@telecrypt-io/storage",
3
- "version": "0.4.0",
3
+ "version": "0.5.8",
4
4
  "license": "BUSL-1.1",
5
5
  "type": "module",
6
+ "packageManager": "npm@10.9.8",
6
7
  "main": "dist/index.js",
7
8
  "types": "dist/index.d.ts",
8
9
  "exports": {
@@ -20,12 +21,16 @@
20
21
  "README.md",
21
22
  "LICENSE"
22
23
  ],
24
+ "engines": {
25
+ "node": "22.23.2"
26
+ },
23
27
  "publishConfig": {
24
28
  "access": "public",
25
29
  "provenance": true
26
30
  },
27
31
  "scripts": {
28
32
  "build": "tsc",
33
+ "typecheck:tests": "tsc -p tsconfig.test.json",
29
34
  "lint": "eslint src test --ext .ts",
30
35
  "test": "vitest run",
31
36
  "test:unit": "vitest run --config vitest.unit.config.ts",
@@ -36,19 +41,16 @@
36
41
  },
37
42
  "dependencies": {
38
43
  "matrix-encrypt-attachment": "1.0.3",
39
- "matrix-js-sdk": "41.9.0",
40
- "oidc-client-ts": "3.5.0"
44
+ "matrix-js-sdk": "42.2.0"
41
45
  },
42
46
  "devDependencies": {
43
- "@playwright/test": "1.61.1",
44
47
  "@types/node": "22.20.1",
45
- "@typescript-eslint/eslint-plugin": "8.64.0",
46
- "@typescript-eslint/parser": "8.64.0",
47
- "eslint": "10.8.0",
48
+ "@typescript-eslint/eslint-plugin": "8.67.0",
49
+ "@typescript-eslint/parser": "8.67.0",
50
+ "eslint": "10.9.0",
48
51
  "fake-indexeddb": "6.2.5",
49
- "tsx": "4.23.1",
50
52
  "typescript": "5.9.3",
51
- "vitest": "4.1.10"
53
+ "vitest": "4.1.11"
52
54
  },
53
55
  "repository": {
54
56
  "type": "git",