@solarpunkltd/swarm-chat-js 8.1.0 → 10.0.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.
@@ -13,11 +13,11 @@ export declare const historyLinkSchema: z.ZodObject<{
13
13
  }, z.core.$strict>;
14
14
  export type HistoryLink = z.infer<typeof historyLinkSchema>;
15
15
  export declare const feedEntrySchema: z.ZodObject<{
16
- v: z.ZodLiteral<8>;
16
+ v: z.ZodLiteral<9>;
17
17
  seq: z.ZodNumber;
18
18
  at: z.ZodNumber;
19
19
  msg: z.ZodObject<{
20
- v: z.ZodLiteral<8>;
20
+ v: z.ZodLiteral<9>;
21
21
  topic: z.ZodString;
22
22
  id: z.ZodString;
23
23
  type: z.ZodEnum<{
@@ -34,11 +34,15 @@ export declare const feedEntrySchema: z.ZodObject<{
34
34
  gateway: "gateway";
35
35
  "own-stamp": "own-stamp";
36
36
  }>;
37
- own: z.ZodBoolean;
38
37
  idb: z.ZodOptional<z.ZodObject<{
39
38
  msg: z.ZodString;
40
39
  sig: z.ZodString;
41
40
  }, z.core.$strict>>;
41
+ wl: z.ZodOptional<z.ZodObject<{
42
+ msg: z.ZodString;
43
+ wsig: z.ZodString;
44
+ asig: z.ZodString;
45
+ }, z.core.$strict>>;
42
46
  sig: z.ZodString;
43
47
  }, z.core.$strict>;
44
48
  history: z.ZodNullable<z.ZodObject<{
@@ -51,7 +55,7 @@ export declare const historyRowSchema: z.ZodObject<{
51
55
  seq: z.ZodNumber;
52
56
  at: z.ZodNumber;
53
57
  msg: z.ZodObject<{
54
- v: z.ZodLiteral<8>;
58
+ v: z.ZodLiteral<9>;
55
59
  topic: z.ZodString;
56
60
  id: z.ZodString;
57
61
  type: z.ZodEnum<{
@@ -68,18 +72,22 @@ export declare const historyRowSchema: z.ZodObject<{
68
72
  gateway: "gateway";
69
73
  "own-stamp": "own-stamp";
70
74
  }>;
71
- own: z.ZodBoolean;
72
75
  idb: z.ZodOptional<z.ZodObject<{
73
76
  msg: z.ZodString;
74
77
  sig: z.ZodString;
75
78
  }, z.core.$strict>>;
79
+ wl: z.ZodOptional<z.ZodObject<{
80
+ msg: z.ZodString;
81
+ wsig: z.ZodString;
82
+ asig: z.ZodString;
83
+ }, z.core.$strict>>;
76
84
  sig: z.ZodString;
77
85
  }, z.core.$strict>;
78
86
  }, z.core.$strict>;
79
87
  export type HistoryRow = z.infer<typeof historyRowSchema>;
80
88
  /** A row that fails its shape is left out and counted, so one bad row never hides the rest of the file. */
81
89
  export declare const historyFileSchema: z.ZodObject<{
82
- v: z.ZodLiteral<8>;
90
+ v: z.ZodLiteral<9>;
83
91
  topic: z.ZodString;
84
92
  fromSeq: z.ZodNumber;
85
93
  toSeq: z.ZodNumber;
@@ -1,5 +1,6 @@
1
1
  export * from './message.js';
2
2
  export * from './binding.js';
3
+ export * from './walletLink.js';
3
4
  export * from './ownFeed.js';
4
5
  export * from './entry.js';
5
6
  export * from './note.js';
@@ -1,16 +1,18 @@
1
1
  import { PrivateKey } from '@ethersphere/bee-js';
2
2
  import { z } from 'zod';
3
3
  import { type ChatBinding, type ChatIdentity } from './binding.js';
4
+ import { type WalletLink } from './walletLink.js';
4
5
  /**
5
- * The chat message of version 8: what a browser sends as a GSOC payload, what the server verifies before it
6
+ * The chat message of version 9: what a browser sends as a GSOC payload, what the server verifies before it
6
7
  * publishes, and what every feed entry carries back as `msg`. This module is the only place its bytes are built,
7
8
  * signed, checked and read, and the server imports it from here rather than keeping a copy. A message of any other
8
9
  * version fails the shape check.
9
10
  */
10
- export declare const MESSAGE_VERSION = 8;
11
+ export declare const MESSAGE_VERSION = 9;
11
12
  /**
12
- * Measured on the UTF-8 of the whole JSON object, on top of the per-field caps. Large enough for a 500 character text
13
- * of two byte characters with a Swarm ID binding, at every other cap.
13
+ * Measured on the UTF-8 of the whole JSON object, on top of the per-field caps, wallet link included. Large enough for
14
+ * a 500 character text of two byte characters with a Swarm ID binding and a wallet link, at every other cap, which
15
+ * comes to 2,376 bytes.
14
16
  */
15
17
  export declare const MAX_MESSAGE_BYTES = 3072;
16
18
  export declare const MAX_TOPIC_CHARACTERS = 128;
@@ -33,8 +35,18 @@ export type MessageVia = (typeof MessageVia)[keyof typeof MessageVia];
33
35
  * once. A composer that shows a count uses this function, so it and the check agree.
34
36
  */
35
37
  export declare function countCharacters(value: string): number;
38
+ /**
39
+ * Why a name is refused: `length` is outside 1 to {@link MAX_NAME_CHARACTERS} characters, `invisible` holds a format
40
+ * or control character, `ens-like` ends in `.eth` as a reader sees it, which only a verified ENS name may show.
41
+ */
42
+ export type DisplayNameProblem = 'length' | 'invisible' | 'ens-like';
43
+ /**
44
+ * Whether `name` is one a message may carry, as the message check reads it, and why not when it is not. A composer
45
+ * calls this on the name as it will be sent, so it and the check agree.
46
+ */
47
+ export declare function displayNameProblem(name: string): DisplayNameProblem | null;
36
48
  export declare const chatMessageSchema: z.ZodObject<{
37
- v: z.ZodLiteral<8>;
49
+ v: z.ZodLiteral<9>;
38
50
  topic: z.ZodString;
39
51
  id: z.ZodString;
40
52
  type: z.ZodEnum<{
@@ -51,11 +63,15 @@ export declare const chatMessageSchema: z.ZodObject<{
51
63
  gateway: "gateway";
52
64
  "own-stamp": "own-stamp";
53
65
  }>;
54
- own: z.ZodBoolean;
55
66
  idb: z.ZodOptional<z.ZodObject<{
56
67
  msg: z.ZodString;
57
68
  sig: z.ZodString;
58
69
  }, z.core.$strict>>;
70
+ wl: z.ZodOptional<z.ZodObject<{
71
+ msg: z.ZodString;
72
+ wsig: z.ZodString;
73
+ asig: z.ZodString;
74
+ }, z.core.$strict>>;
59
75
  sig: z.ZodString;
60
76
  }, z.core.$strict>;
61
77
  export type ChatMessage = z.infer<typeof chatMessageSchema>;
@@ -72,11 +88,12 @@ export type ChatMessageCheck = {
72
88
  detail: string;
73
89
  };
74
90
  /**
75
- * `origin` is the site the chat runs on, a bare origin such as `https://example.com`, and is required: a binding for
76
- * any other site is refused, so a binding signed for one site never passes in another site's chat.
91
+ * `origins` are the sites the chat runs on, each a bare origin such as `https://example.com`, and are required: a
92
+ * binding for any other site is refused, so a binding signed for one site never passes in a chat that site is not
93
+ * part of. A chat served from several pages, such as one domain and a copy on Swarm under an ENS name, lists each.
77
94
  */
78
95
  export interface VerifyOptions {
79
- origin: string;
96
+ origins: readonly string[];
80
97
  /** Accept a binding for `http://localhost`, `http://127.0.0.1` or `http://[::1]`, as only a development build signs. */
81
98
  allowLocalHttp?: boolean;
82
99
  }
@@ -102,10 +119,10 @@ export interface ChatMessageDraft {
102
119
  ts?: number;
103
120
  /** Who pays for the inbox write. Required, since nothing can tell from here. */
104
121
  via: MessageVia;
105
- /** Whether the sender also keeps a copy in its own feed, false when left out. */
106
- own?: boolean;
107
122
  /** The sender's Swarm ID chat binding, given only for a signed-in sender. */
108
123
  idb?: ChatBinding;
124
+ /** The sender's Swarm ID wallet link, given only beside `idb`, for a sender who shows an ENS name. */
125
+ wl?: WalletLink;
109
126
  }
110
127
  export interface SignedChatMessage {
111
128
  message: ChatMessage;
@@ -123,8 +140,10 @@ export declare function createChatMessage(privateKey: PrivateKey | Uint8Array |
123
140
  export declare function hasValidSignature(message: ChatMessage): boolean;
124
141
  /**
125
142
  * Verifies a message that arrived as an object: its shape, its signature, and, when it carries a binding, that the
126
- * binding is the fork's four lines for this `addr` and for `options.origin`, and recovers an account. A message whose
127
- * binding fails is refused as `binding`, never passed as a guest, and so is a bound message checked with no origin.
143
+ * binding is Swarm ID's four lines for this `addr` and for one of `options.origins`, and recovers an account. When it also
144
+ * carries a wallet link, the link must be Swarm ID's five lines for that account, signed by its wallet and by the
145
+ * account, and the identity then names the wallet. A message whose binding or link fails is refused as `binding`,
146
+ * never passed as a guest, and so is a bound message checked with no origins.
128
147
  * Never throws, and touches no DOM and no Node API, so a page runs it in a Web Worker as the server runs it.
129
148
  */
130
149
  export declare function verifyChatMessage(value: unknown, options: VerifyOptions): ChatMessageCheck;
@@ -1,6 +1,6 @@
1
1
  /**
2
- * A sender may keep a copy of each message it sends in a feed of its own, owned by its chat address `addr`, and says
3
- * so with `own: true`. That feed is named by `ownFeedTopic(chatTopic)`, turned into a feed topic with
2
+ * A sender may keep a copy of each message it sends in a feed of its own, owned by its chat address `addr`. The
3
+ * message says nothing about it, so whether a copy exists is read from that feed. That feed is named by `ownFeedTopic(chatTopic)`, turned into a feed topic with
4
4
  * `Topic.fromString` as the chat's own feed is named by `chatTopic`. The prefix carries a version, so a later layout of
5
5
  * the copies takes a new topic rather than mixing with this one.
6
6
  */
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The address that made `sig` over `text`, `0x` and 40 lowercase hex characters, or null for a signature Swarm ID
3
+ * would refuse or nothing recovers from. `sig` is `0x` and 130 hex characters of r, s and v, already shape-checked.
4
+ */
5
+ export declare function recoverPersonalSigner(text: string, sig: string): string | null;
6
+ /** Whether `text` is a date in the one spelling `Date.prototype.toISOString` writes. */
7
+ export declare function isCanonicalDate(text: string): boolean;
@@ -0,0 +1,45 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * The Swarm ID wallet link: the statement, signed by an account's wallet and countersigned by the account's own key,
4
+ * that the two belong to the same person, which lets a reader show the wallet's ENS name. The Swarm ID fork makes it
5
+ * while the person has "Show my ENS name on sites" on, and its canonical description is `docs/Wallet-Link.md` in the
6
+ * fork (solarpunk-id), with `verifyWalletLink` in its `lib/src/utils/wallet-link.ts`. This module reads it exactly as
7
+ * that function does, without importing Swarm ID: the same five lines, the second of them read exactly, and both
8
+ * EIP-191 personal_sign signatures with v 27 or 28 and a low s. It looks nothing up: a reader takes the checked wallet to its own ENS lookup.
9
+ */
10
+ /** The first line, which also versions the wallet link format. */
11
+ export declare const WALLET_LINK_TITLE = "Swarm ID wallet link v1";
12
+ /** The second line, what the wallet's owner agrees to, which a wallet shows them before they sign. */
13
+ export declare const WALLET_LINK_STATEMENT = "I link this wallet to the Swarm ID account below, so sites may show my ENS name beside its messages.";
14
+ /** Swarm ID's own bound on a wallet link's text, which is 258 characters in the format it writes. */
15
+ export declare const MAX_WALLET_LINK_CHARACTERS = 320;
16
+ /**
17
+ * A wallet link as a message carries it: Swarm ID's `{ message, walletSignature, accountSignature }` as `msg`, `wsig`
18
+ * and `asig`. Each signature is spelled as Swarm ID writes it, `0x` and 130 lowercase hex characters of r, s and v, so
19
+ * one link has one spelling inside the signed bytes.
20
+ */
21
+ export declare const walletLinkSchema: z.ZodObject<{
22
+ msg: z.ZodString;
23
+ wsig: z.ZodString;
24
+ asig: z.ZodString;
25
+ }, z.core.$strict>;
26
+ export type WalletLink = z.infer<typeof walletLinkSchema>;
27
+ export type WalletLinkCheck = {
28
+ ok: true;
29
+ /** The wallet both signatures were checked for, `0x` and 40 lowercase hex characters. */
30
+ wallet: string;
31
+ /** The account the link names, which is the one it was checked against. */
32
+ account: string;
33
+ /** When the account says it signed, ISO 8601 UTC. */
34
+ date: string;
35
+ } | {
36
+ ok: false;
37
+ detail: string;
38
+ };
39
+ /**
40
+ * Reads a wallet link for `account` (`0x` and 40 lowercase hex characters), the account a verified chat binding
41
+ * named. Refuses every text but the five lines Swarm ID writes, its statement included, a wallet signature that does not recover the wallet
42
+ * the link names, an account signature that does not recover the account it names, and a link naming any other
43
+ * account. Never throws.
44
+ */
45
+ export declare function checkWalletLink(link: WalletLink, account: string): WalletLinkCheck;