@peers-app/peers-sdk 0.20.4 → 0.20.5

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,3 +1,20 @@
1
- export declare const userConnectStatus: import("../data/persistent-vars").PersistentVar<any>;
1
+ import { type UserConnectStatus } from "./user-connect.types";
2
+ /**
3
+ * Shared lifetime for connection codes, aliases, invite intents, and UI countdowns.
4
+ * Ten minutes from creation or capture.
5
+ */
6
+ export declare const USER_CONNECT_TTL_MS = 600000;
7
+ /**
8
+ * Current structured status of the local user-connect ceremony.
9
+ * Prefer reading through {@link normalizeUserConnectStatus} when consuming
10
+ * values that may have been persisted before the structured type existed.
11
+ */
12
+ export declare const userConnectStatus: import("../data/persistent-vars").PersistentVar<UserConnectStatus>;
13
+ /**
14
+ * Active offer connection code (12 Crockford chars). Cleared after {@link USER_CONNECT_TTL_MS}.
15
+ */
2
16
  export declare const userConnectCodeOffer: import("../data/persistent-vars").PersistentVar<string>;
17
+ /**
18
+ * Active answer connection code (12 Crockford chars). Cleared after {@link USER_CONNECT_TTL_MS}.
19
+ */
3
20
  export declare const userConnectCodeAnswer: import("../data/persistent-vars").PersistentVar<string>;
@@ -1,35 +1,63 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.userConnectCodeAnswer = exports.userConnectCodeOffer = exports.userConnectStatus = void 0;
3
+ exports.userConnectCodeAnswer = exports.userConnectCodeOffer = exports.userConnectStatus = exports.USER_CONNECT_TTL_MS = void 0;
4
4
  const persistent_vars_1 = require("../data/persistent-vars");
5
- exports.userConnectStatus = (0, persistent_vars_1.deviceVar)("userConnectStatus", { defaultValue: undefined });
5
+ const user_connect_types_1 = require("./user-connect.types");
6
+ /**
7
+ * Shared lifetime for connection codes, aliases, invite intents, and UI countdowns.
8
+ * Ten minutes from creation or capture.
9
+ */
10
+ exports.USER_CONNECT_TTL_MS = 600_000;
11
+ /**
12
+ * Current structured status of the local user-connect ceremony.
13
+ * Prefer reading through {@link normalizeUserConnectStatus} when consuming
14
+ * values that may have been persisted before the structured type existed.
15
+ */
16
+ exports.userConnectStatus = (0, persistent_vars_1.deviceVar)("userConnectStatus", {
17
+ defaultValue: user_connect_types_1.USER_CONNECT_IDLE_STATUS,
18
+ });
19
+ /**
20
+ * Active offer connection code (12 Crockford chars). Cleared after {@link USER_CONNECT_TTL_MS}.
21
+ */
6
22
  exports.userConnectCodeOffer = (0, persistent_vars_1.deviceVar)("userConnectCodeOffer", { defaultValue: "" });
7
23
  exports.userConnectCodeOffer.loadingPromise.then(() => {
8
24
  let codeTimeout;
9
- (0, exports.userConnectStatus)("");
25
+ if ((0, exports.userConnectCodeOffer)()) {
26
+ (0, exports.userConnectCodeOffer)("");
27
+ }
28
+ (0, exports.userConnectStatus)(user_connect_types_1.USER_CONNECT_IDLE_STATUS);
10
29
  exports.userConnectCodeOffer.subscribe(() => {
11
- (0, exports.userConnectStatus)("");
30
+ (0, exports.userConnectStatus)(user_connect_types_1.USER_CONNECT_IDLE_STATUS);
12
31
  clearTimeout(codeTimeout);
13
32
  if ((0, exports.userConnectCodeOffer)()) {
14
33
  codeTimeout = setTimeout(() => {
15
34
  (0, exports.userConnectCodeOffer)("");
16
- }, 600_000); // 10 minutes
35
+ }, exports.USER_CONNECT_TTL_MS);
17
36
  }
18
37
  });
19
38
  });
39
+ /**
40
+ * Active answer connection code (12 Crockford chars). Cleared after {@link USER_CONNECT_TTL_MS}.
41
+ */
20
42
  exports.userConnectCodeAnswer = (0, persistent_vars_1.deviceVar)("userConnectCodeAnswer", {
21
43
  defaultValue: "",
22
44
  });
23
45
  exports.userConnectCodeAnswer.loadingPromise.then(() => {
24
46
  let codeTimeout;
25
- (0, exports.userConnectStatus)("");
47
+ if ((0, exports.userConnectCodeAnswer)()) {
48
+ (0, exports.userConnectCodeAnswer)("");
49
+ }
50
+ (0, exports.userConnectStatus)(user_connect_types_1.USER_CONNECT_IDLE_STATUS);
26
51
  exports.userConnectCodeAnswer.subscribe(() => {
27
- (0, exports.userConnectStatus)("");
52
+ (0, exports.userConnectStatus)(user_connect_types_1.USER_CONNECT_IDLE_STATUS);
28
53
  clearTimeout(codeTimeout);
29
54
  if ((0, exports.userConnectCodeAnswer)()) {
30
55
  codeTimeout = setTimeout(() => {
31
56
  (0, exports.userConnectCodeAnswer)("");
32
- }, 600_000); // 10 minutes
57
+ }, exports.USER_CONNECT_TTL_MS);
33
58
  }
34
59
  });
35
60
  });
61
+ exports.userConnectStatus.loadingPromise.then(() => {
62
+ (0, exports.userConnectStatus)(user_connect_types_1.USER_CONNECT_IDLE_STATUS);
63
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const user_connect_types_1 = require("./user-connect.types");
4
+ describe("normalizeUserConnectStatus", () => {
5
+ it("clears legacy string and malformed persisted statuses", () => {
6
+ expect((0, user_connect_types_1.normalizeUserConnectStatus)("")).toEqual(user_connect_types_1.USER_CONNECT_IDLE_STATUS);
7
+ expect((0, user_connect_types_1.normalizeUserConnectStatus)("Error: old error")).toEqual(user_connect_types_1.USER_CONNECT_IDLE_STATUS);
8
+ expect((0, user_connect_types_1.normalizeUserConnectStatus)("old-remote-user-id")).toEqual(user_connect_types_1.USER_CONNECT_IDLE_STATUS);
9
+ expect((0, user_connect_types_1.normalizeUserConnectStatus)(null)).toEqual(user_connect_types_1.USER_CONNECT_IDLE_STATUS);
10
+ expect((0, user_connect_types_1.normalizeUserConnectStatus)({ kind: "connected" })).toEqual(user_connect_types_1.USER_CONNECT_IDLE_STATUS);
11
+ expect((0, user_connect_types_1.normalizeUserConnectStatus)({ kind: "error", code: "unknown" })).toEqual(user_connect_types_1.USER_CONNECT_IDLE_STATUS);
12
+ });
13
+ it("preserves valid structured statuses", () => {
14
+ expect((0, user_connect_types_1.normalizeUserConnectStatus)({ kind: "offer-ready" })).toEqual({
15
+ kind: "offer-ready",
16
+ });
17
+ expect((0, user_connect_types_1.normalizeUserConnectStatus)({ kind: "connecting", attempt: 3 })).toEqual({
18
+ kind: "connecting",
19
+ attempt: 3,
20
+ });
21
+ expect((0, user_connect_types_1.normalizeUserConnectStatus)({
22
+ kind: "connected",
23
+ remoteUserId: "remote-user",
24
+ remoteDeviceId: "remote-device",
25
+ })).toEqual({
26
+ kind: "connected",
27
+ remoteUserId: "remote-user",
28
+ remoteDeviceId: "remote-device",
29
+ });
30
+ expect((0, user_connect_types_1.normalizeUserConnectStatus)({
31
+ kind: "error",
32
+ code: "remote-not-waiting",
33
+ detail: "diagnostic",
34
+ })).toEqual({
35
+ kind: "error",
36
+ code: "remote-not-waiting",
37
+ detail: "diagnostic",
38
+ });
39
+ });
40
+ it("normalizes invalid connection attempt numbers to the first attempt", () => {
41
+ expect((0, user_connect_types_1.normalizeUserConnectStatus)({ kind: "connecting", attempt: 0 })).toEqual({
42
+ kind: "connecting",
43
+ attempt: 1,
44
+ });
45
+ });
46
+ });
@@ -4,6 +4,42 @@
4
4
  * This flow allows two users to securely exchange user information
5
5
  * using a 12-character code (4-char rendezvous alias + 8-char shared secret).
6
6
  */
7
+ /**
8
+ * Stable protocol error codes for the user-connect exchange.
9
+ * The UI owns presentation text; the device layer reports these codes.
10
+ */
11
+ export type UserConnectErrorCode = "invalid-code" | "no-route" | "not-found-or-expired" | "remote-not-waiting" | "canceled" | "protocol-error";
12
+ /**
13
+ * Discriminated status published by the device layer during a user-connect ceremony.
14
+ */
15
+ export type UserConnectStatus = {
16
+ kind: "idle";
17
+ } | {
18
+ kind: "offer-registering";
19
+ } | {
20
+ kind: "offer-ready";
21
+ } | {
22
+ kind: "connecting";
23
+ attempt: number;
24
+ } | {
25
+ kind: "connected";
26
+ remoteUserId: string;
27
+ remoteDeviceId: string;
28
+ } | {
29
+ kind: "error";
30
+ code: UserConnectErrorCode;
31
+ detail?: string;
32
+ };
33
+ /** Default idle status for a quiet connection ceremony. */
34
+ export declare const USER_CONNECT_IDLE_STATUS: UserConnectStatus;
35
+ /**
36
+ * Normalize a persisted or in-memory status value to the structured shape.
37
+ * Clears legacy string statuses (`""`, `"Error: …"`, bare user IDs) to idle so
38
+ * callers never treat stale strings as protocol facts.
39
+ * @param value Raw pvar value
40
+ * @returns A valid {@link UserConnectStatus}
41
+ */
42
+ export declare function normalizeUserConnectStatus(value: unknown): UserConnectStatus;
7
43
  /**
8
44
  * User information exchanged during the connection flow.
9
45
  */
@@ -6,3 +6,63 @@
6
6
  * using a 12-character code (4-char rendezvous alias + 8-char shared secret).
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.USER_CONNECT_IDLE_STATUS = void 0;
10
+ exports.normalizeUserConnectStatus = normalizeUserConnectStatus;
11
+ /** Default idle status for a quiet connection ceremony. */
12
+ exports.USER_CONNECT_IDLE_STATUS = { kind: "idle" };
13
+ /**
14
+ * Normalize a persisted or in-memory status value to the structured shape.
15
+ * Clears legacy string statuses (`""`, `"Error: …"`, bare user IDs) to idle so
16
+ * callers never treat stale strings as protocol facts.
17
+ * @param value Raw pvar value
18
+ * @returns A valid {@link UserConnectStatus}
19
+ */
20
+ function normalizeUserConnectStatus(value) {
21
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
22
+ return exports.USER_CONNECT_IDLE_STATUS;
23
+ }
24
+ const candidate = value;
25
+ switch (candidate.kind) {
26
+ case "idle":
27
+ case "offer-registering":
28
+ case "offer-ready":
29
+ return { kind: candidate.kind };
30
+ case "connecting": {
31
+ const attempt = value.attempt;
32
+ return {
33
+ kind: "connecting",
34
+ attempt: typeof attempt === "number" && attempt > 0 ? attempt : 1,
35
+ };
36
+ }
37
+ case "connected": {
38
+ const remoteUserId = value.remoteUserId;
39
+ const remoteDeviceId = value.remoteDeviceId;
40
+ if (typeof remoteUserId === "string" &&
41
+ remoteUserId.length > 0 &&
42
+ typeof remoteDeviceId === "string" &&
43
+ remoteDeviceId.length > 0) {
44
+ return { kind: "connected", remoteUserId, remoteDeviceId };
45
+ }
46
+ return exports.USER_CONNECT_IDLE_STATUS;
47
+ }
48
+ case "error": {
49
+ const code = value.code;
50
+ const detail = value.detail;
51
+ if (code === "invalid-code" ||
52
+ code === "no-route" ||
53
+ code === "not-found-or-expired" ||
54
+ code === "remote-not-waiting" ||
55
+ code === "canceled" ||
56
+ code === "protocol-error") {
57
+ return {
58
+ kind: "error",
59
+ code,
60
+ detail: typeof detail === "string" ? detail : undefined,
61
+ };
62
+ }
63
+ return exports.USER_CONNECT_IDLE_STATUS;
64
+ }
65
+ default:
66
+ return exports.USER_CONNECT_IDLE_STATUS;
67
+ }
68
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peers-app/peers-sdk",
3
- "version": "0.20.4",
3
+ "version": "0.20.5",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/peers-app/peers-sdk.git"