@gryt/core 0.2.0 → 0.3.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.
package/dist/index.d.ts CHANGED
@@ -14,5 +14,6 @@
14
14
  export { buildReport, describeAttached, MESSAGE_MAX, TITLE_MAX, type Diagnostics, type Report, type ReportType, } from "./reports/report.js";
15
15
  export { getAccentColor, getLinkProvider, getProviderDetail, hostnameOf, LINK_PROVIDERS, type LinkProvider, } from "./links/providers.js";
16
16
  export { describePreviewFailure, extractUrls, getCardSubtitle, getLinkCardLayout, type LinkCardLayout, type LinkPreviewData, } from "./links/preview.js";
17
+ export { normalizeCode, normalizeHost, parseServerInput, type ServerInput, } from "./servers/address.js";
17
18
  export { NAME_POOL, pickRandomName } from "./profile/randomName.js";
18
19
  export { cellState, CUSTOM_VALUE, describeRules, EVERYONE_VALUE, indexRules, nextCellState, scopeChoiceFromValue, scopeChoiceValue, scopeOptions, scopeSetPayload, withCell, type CellState, type ChannelRule, type RuleEffect, type ScopeChoice, } from "./permissions/channelRules.js";
package/dist/index.js CHANGED
@@ -14,5 +14,6 @@
14
14
  export { buildReport, describeAttached, MESSAGE_MAX, TITLE_MAX, } from "./reports/report.js";
15
15
  export { getAccentColor, getLinkProvider, getProviderDetail, hostnameOf, LINK_PROVIDERS, } from "./links/providers.js";
16
16
  export { describePreviewFailure, extractUrls, getCardSubtitle, getLinkCardLayout, } from "./links/preview.js";
17
+ export { normalizeCode, normalizeHost, parseServerInput, } from "./servers/address.js";
17
18
  export { NAME_POOL, pickRandomName } from "./profile/randomName.js";
18
19
  export { cellState, CUSTOM_VALUE, describeRules, EVERYONE_VALUE, indexRules, nextCellState, scopeChoiceFromValue, scopeChoiceValue, scopeOptions, scopeSetPayload, withCell, } from "./permissions/channelRules.js";
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Reading whatever somebody pasted into the join field.
3
+ *
4
+ * Both apps had this, byte for byte: `normalizeHost`, `normalizeCode` and all
5
+ * of `parseServerInput`, down to the comments. The phone said so in its own
6
+ * header — a copy, because the desktop's `common` package was never published,
7
+ * written down in GRYT-406 rather than left to be found once the two had
8
+ * drifted.
9
+ *
10
+ * They had not drifted, which is the only comfortable moment to do this. The
11
+ * two apps talk to the same servers, so an address one reads differently is a
12
+ * server the other cannot join, and that failure looks like the server being
13
+ * down rather than like a parser disagreeing.
14
+ *
15
+ * What stayed behind is the scheme memory — `schemeFor`, `rememberScheme` and
16
+ * the rest. Those reach for storage and the two apps store differently, so they
17
+ * fail the test at the top of this package.
18
+ */
19
+ export declare function normalizeHost(input: string): string;
20
+ export declare function normalizeCode(input: string): string;
21
+ export interface ServerInput {
22
+ /** Empty when nothing usable was in the input. */
23
+ host: string;
24
+ /** Empty for a plain address, which carries no code. */
25
+ code: string;
26
+ }
27
+ /**
28
+ * Three shapes arrive and they are not the same thing: a full invite link
29
+ * (`https://gryt.chat/invite?host=…&code=…`), a legacy one
30
+ * (`https://app.gryt.chat/invite/<code>`), and a plain address (`gryt.chat`,
31
+ * `192.168.1.5:5001`).
32
+ *
33
+ * `normalizeHost` on its own is wrong for the first two — it returns the
34
+ * *link's* host, which is gryt.chat, and joining that instead of the server
35
+ * named in the query is a confusing failure rather than an obvious one.
36
+ *
37
+ * Anything that does not parse as a link falls through to being an address, so
38
+ * a typo in a URL still gets the address treatment rather than an error about
39
+ * invite formats.
40
+ */
41
+ export declare function parseServerInput(input: string, opts?: {
42
+ defaultLegacyHost?: string;
43
+ }): ServerInput;
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Reading whatever somebody pasted into the join field.
3
+ *
4
+ * Both apps had this, byte for byte: `normalizeHost`, `normalizeCode` and all
5
+ * of `parseServerInput`, down to the comments. The phone said so in its own
6
+ * header — a copy, because the desktop's `common` package was never published,
7
+ * written down in GRYT-406 rather than left to be found once the two had
8
+ * drifted.
9
+ *
10
+ * They had not drifted, which is the only comfortable moment to do this. The
11
+ * two apps talk to the same servers, so an address one reads differently is a
12
+ * server the other cannot join, and that failure looks like the server being
13
+ * down rather than like a parser disagreeing.
14
+ *
15
+ * What stayed behind is the scheme memory — `schemeFor`, `rememberScheme` and
16
+ * the rest. Those reach for storage and the two apps store differently, so they
17
+ * fail the test at the top of this package.
18
+ */
19
+ export function normalizeHost(input) {
20
+ let h = String(input || "").trim();
21
+ h = h.replace(/^(wss?:\/\/|https?:\/\/)/i, "");
22
+ h = h.split("/")[0] || "";
23
+ h = h.replace(/\s+/g, "");
24
+ return h;
25
+ }
26
+ export function normalizeCode(input) {
27
+ return String(input || "")
28
+ .trim()
29
+ .replace(/\s+/g, "")
30
+ .toLowerCase();
31
+ }
32
+ /**
33
+ * The default host for a legacy `/invite/<code>` link.
34
+ *
35
+ * Those links carry no host, and the only client ever served from a path like
36
+ * that is the hosted one.
37
+ */
38
+ const DEFAULT_LEGACY_HOST = "app.gryt.chat";
39
+ /**
40
+ * Three shapes arrive and they are not the same thing: a full invite link
41
+ * (`https://gryt.chat/invite?host=…&code=…`), a legacy one
42
+ * (`https://app.gryt.chat/invite/<code>`), and a plain address (`gryt.chat`,
43
+ * `192.168.1.5:5001`).
44
+ *
45
+ * `normalizeHost` on its own is wrong for the first two — it returns the
46
+ * *link's* host, which is gryt.chat, and joining that instead of the server
47
+ * named in the query is a confusing failure rather than an obvious one.
48
+ *
49
+ * Anything that does not parse as a link falls through to being an address, so
50
+ * a typo in a URL still gets the address treatment rather than an error about
51
+ * invite formats.
52
+ */
53
+ export function parseServerInput(input, opts) {
54
+ const raw = String(input || "").trim();
55
+ if (!raw)
56
+ return { host: "", code: "" };
57
+ const legacyHost = normalizeHost(opts?.defaultLegacyHost || DEFAULT_LEGACY_HOST);
58
+ // Only something carrying a scheme can be a link. Without this, `gryt.chat`
59
+ // parses as a URL in some engines with "gryt.chat" as the protocol.
60
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(raw)) {
61
+ try {
62
+ const url = new URL(raw);
63
+ const path = url.pathname || "/";
64
+ // gryt://invite?host=…&code=… puts "invite" in the authority rather than
65
+ // the path, because the scheme is not one the URL parser treats as
66
+ // special. Both spellings mean the same thing.
67
+ const isInvite = path.startsWith("/invite") || url.hostname === "invite";
68
+ if (isInvite) {
69
+ const host = normalizeHost(url.searchParams.get("host") || "");
70
+ const code = normalizeCode(url.searchParams.get("code") || "");
71
+ if (host && code)
72
+ return { host, code };
73
+ const parts = path.split("/").filter(Boolean);
74
+ if (parts[0] === "invite" && parts[1]) {
75
+ return { host: legacyHost, code: normalizeCode(parts[1]) };
76
+ }
77
+ }
78
+ }
79
+ catch {
80
+ // Not a URL after all. It is still probably an address.
81
+ }
82
+ }
83
+ return { host: normalizeHost(raw), code: "" };
84
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gryt/core",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Gryt's shared application logic: one implementation of the things the desktop and mobile apps both do.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {