@freqhole/haruspex 0.3.5 → 0.3.7

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 +1 @@
1
- {"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../src/share/codec.ts"],"names":[],"mappings":"AAiBA,kEAAkE;AAClE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,KAAK,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;CAC3B;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,GAAG,eAAe,GAAG,kBAAkB,CAAC;AA2IxF,gEAAgE;AAChE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAInE;AAED,kEAAkE;AAClE,wBAAgB,aAAa,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAEhE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAcvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CA0CxE"}
1
+ {"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../src/share/codec.ts"],"names":[],"mappings":"AAiBA,kEAAkE;AAClE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,KAAK,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;CAC3B;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,GAAG,eAAe,GAAG,kBAAkB,CAAC;AA2IxF,gEAAgE;AAChE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAInE;AAED,kEAAkE;AAClE,wBAAgB,aAAa,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAEhE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA0BvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CA0CxE"}
@@ -158,16 +158,28 @@ export function shareFragment(payload) {
158
158
  */
159
159
  export function extractShareToken(input) {
160
160
  let raw = input.trim();
161
+ let matchedPrefix = false;
161
162
  const hashIdx = raw.indexOf("#share/");
162
163
  if (hashIdx !== -1) {
163
164
  raw = raw.slice(hashIdx + "#share/".length);
165
+ matchedPrefix = true;
164
166
  }
165
167
  else if (raw.startsWith("share/")) {
166
168
  raw = raw.slice("share/".length);
169
+ matchedPrefix = true;
170
+ }
171
+ // the trailing-query truncation only makes sense once we know `raw` is
172
+ // actually sitting right after a real `#share/`/`share/` prefix - doing
173
+ // it unconditionally previously mangled any input that merely CONTAINS
174
+ // an unrelated "&" (e.g. a host app's own `?foo=1&bar=2` route in the
175
+ // same hash), which then silently masqueraded as "a share token was
176
+ // found here" purely because truncation made the output differ from
177
+ // the input - see callers that infer "found a token" from that.
178
+ if (matchedPrefix) {
179
+ const ampIdx = raw.indexOf("&");
180
+ if (ampIdx !== -1)
181
+ raw = raw.slice(0, ampIdx);
167
182
  }
168
- const ampIdx = raw.indexOf("&");
169
- if (ampIdx !== -1)
170
- raw = raw.slice(0, ampIdx);
171
183
  return raw;
172
184
  }
173
185
  /**
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { decodeShareToken, encodeShareToken, extractShareToken, shareFragment, } from "./codec.js";
2
+ import { decodeShareToken, encodeShareToken, extractShareToken, shareFragment } from "./codec.js";
3
3
  const NODE_ID = "ab".repeat(32);
4
4
  const OTHER_NODE_ID = "cd".repeat(32);
5
5
  describe("encodeShareToken / decodeShareToken (current wire)", () => {
@@ -61,6 +61,22 @@ describe("encodeShareToken / decodeShareToken (current wire)", () => {
61
61
  expect(decodeShareToken(url)).toEqual(payload);
62
62
  expect(extractShareToken(url)).toBe(token);
63
63
  });
64
+ it("truncates a trailing '&'-joined query only when a real #share/ prefix precedes it", () => {
65
+ const payload = { kind: "node", nodeId: NODE_ID };
66
+ const token = encodeShareToken(payload);
67
+ expect(extractShareToken(`#share/${token}&foo=bar`)).toBe(token);
68
+ expect(extractShareToken(`share/${token}&foo=bar`)).toBe(token);
69
+ });
70
+ it("does NOT truncate at '&' when no #share/ or share/ prefix is present", () => {
71
+ // a host app's own unrelated route can contain "&" without ever being
72
+ // a share link at all (e.g. spume's `/radio?node_id=...&station_id=
73
+ // ...`) - truncating here anyway used to make the output differ from
74
+ // the input, which callers (see spume's extractShareTokenFromHash)
75
+ // relied on as "a token was found", wrongly treating the truncated
76
+ // leftovers as a real share token.
77
+ const input = "/radio?node_id=deadbeef&station_id=abc123";
78
+ expect(extractShareToken(input)).toBe(input);
79
+ });
64
80
  });
65
81
  describe("decodeShareToken (legacy shapes)", () => {
66
82
  it("decodes the legacy versioned doc shape {v:1,n,d,t,m}", () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@freqhole/haruspex",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "freqhole auth domain: identity, webauthn, knock + friendz protocols, permissions",