@pipeworx/mcp-guam-code 0.1.1 → 0.1.2

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/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Guam Code Annotated (GCA) — Guam territorial statutes, by citation and by
4
4
  topic/keyword search. Keyless.
5
5
 
6
- Part of [Pipeworx](https://pipeworx.io) — an MCP gateway connecting AI agents to 1784+ live data sources. This is an independent, unofficial integration — not affiliated with, endorsed by, or published by the upstream provider.
6
+ Part of [Pipeworx](https://pipeworx.io) — an MCP gateway connecting AI agents to 1815+ live data sources. This is an independent, unofficial integration — not affiliated with, endorsed by, or published by the upstream provider.
7
7
 
8
8
  ## Tools
9
9
 
@@ -224,7 +224,7 @@ directly, instead of just this one's:
224
224
  }
225
225
  ```
226
226
 
227
- Both URLs reach the same gateway and the same 1784+ data sources. The
227
+ Both URLs reach the same gateway and the same 1815+ data sources. The
228
228
  only difference is which pack's tools are listed **directly**; `ask_pipeworx`
229
229
  reaches all of them from either one.
230
230
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipeworx/mcp-guam-code",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Guam Code Annotated (GCA) — section lookup and search. Fleet #2888.",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -26,7 +26,7 @@
26
26
  "@cloudflare/workers-types": "^4.20260405.1"
27
27
  },
28
28
  "pipeworx": {
29
- "sourceHash": "v1-964fe40aed9923b374141088f42860d68ffb5efa97b3c7c3fc37b9902c46c79f",
30
- "sourceCommit": "3723e46c6d115bd4a1fd2175e6bf758555e7cb50"
29
+ "sourceHash": "v1-0e8b06ad75d31b79367ea7bccec933bfae1665a234e87b2fd47a9220c214f878",
30
+ "sourceCommit": "fe38188882d0fe91e5a45d82b7b989b4aae1e8b6"
31
31
  }
32
32
  }
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "io.github.pipeworx-io/guam-code",
4
4
  "title": "Guam Code",
5
5
  "description": "Guam Code Annotated (GCA) — section lookup and search. Fleet #2888.",
6
- "version": "0.1.1",
6
+ "version": "0.1.2",
7
7
  "websiteUrl": "https://pipeworx.io/packs/guam-code",
8
8
  "repository": {
9
9
  "url": "https://github.com/pipeworx-io/mcp-guam-code",
package/src/index.ts CHANGED
@@ -935,6 +935,48 @@ function unescapePdfLiteral(s: string): string {
935
935
  return out;
936
936
  }
937
937
 
938
+ /**
939
+ * Recover the ORIGINAL raw byte (0-255) that `new TextDecoder('latin1')`
940
+ * produced a given codepoint for. This exists because the WHATWG Encoding
941
+ * Standard makes "latin1" an ALIAS for "windows-1252", not true ISO-8859-1 —
942
+ * every browser and Node's TextDecoder follow that spec. Bytes 0x00-0x7F and
943
+ * 0xA0-0xFF decode as identity either way, but bytes 0x80-0x9F come back as
944
+ * windows-1252's typographic symbols (0x99 -> U+2122 "™", 0x87 -> U+2021
945
+ * "‡", …) instead of themselves — so `charCodeAt(i) & 0xff`, used everywhere
946
+ * else in this file to recover a raw byte from a decoded string, silently
947
+ * returns the WRONG byte for any of those 32 codepoints (U+2122 & 0xff is
948
+ * 0x22, not the real 0x99).
949
+ *
950
+ * This only matters where the decoded character is being used as DATA (a
951
+ * CID half-byte, a `mapped1` font's 1-byte code) rather than as the actual
952
+ * text it renders (a true WinAnsi-encoded character, where the windows-1252
953
+ * reading IS the correct glyph — see `fontDecoderFor`'s `'winansi'` kind,
954
+ * which returns the decoded string unchanged on purpose and must NOT be
955
+ * routed through this function). Found on mainland China's NPC/MOJ law PDFs
956
+ * (cn-law, fleet #3136): Founder PDFGenerator (方正) writes CJK body text as
957
+ * LITERAL strings under a Type0/Identity-H CID font, and roughly an eighth
958
+ * of all 2-byte CIDs have at least one half-byte landing in 0x80-0x9F —
959
+ * `合` (U+5408, CID 0x0F87) was coming back as `骨` (U+9AA8) because its low
960
+ * byte 0x87 decoded to U+2021, and `U+2021 & 0xff` (0x21) is a different,
961
+ * also-valid CID that happens to mean `骨` in the same font.
962
+ */
963
+ function rawByteFromLatin1Char(code: number): number {
964
+ const b = WIN1252_HIGH_TO_BYTE.get(code);
965
+ return b !== undefined ? b : code & 0xff;
966
+ }
967
+ // windows-1252's 0x80-0x9F table (the WHATWG "windows-1252" index), inverted
968
+ // codepoint -> original byte. 0x81/0x8D/0x8F/0x90/0x9D are undefined in that
969
+ // table and decode as themselves (identity), so they need no entry here.
970
+ const WIN1252_HIGH_TO_BYTE = new Map<number, number>([
971
+ [0x20ac, 0x80], [0x201a, 0x82], [0x0192, 0x83], [0x201e, 0x84],
972
+ [0x2026, 0x85], [0x2020, 0x86], [0x2021, 0x87], [0x02c6, 0x88],
973
+ [0x2030, 0x89], [0x0160, 0x8a], [0x2039, 0x8b], [0x0152, 0x8c],
974
+ [0x017d, 0x8e], [0x2018, 0x91], [0x2019, 0x92], [0x201c, 0x93],
975
+ [0x201d, 0x94], [0x2022, 0x95], [0x2013, 0x96], [0x2014, 0x97],
976
+ [0x02dc, 0x98], [0x2122, 0x99], [0x0161, 0x9a], [0x203a, 0x9b],
977
+ [0x0153, 0x9c], [0x017e, 0x9e], [0x0178, 0x9f],
978
+ ]);
979
+
938
980
  interface RawObject {
939
981
  dict: string;
940
982
  streamBytes: Uint8Array | null;
@@ -1298,10 +1340,38 @@ async function extractPdfText(buf: ArrayBuffer, options?: PdfExtractOptions): Pr
1298
1340
  // font's /ToUnicode map entirely and produces line-noise control
1299
1341
  // characters instead of the real character -- route it through the
1300
1342
  // SAME decoder a hex run below already uses.
1343
+ //
1344
+ // 'cid' (Type0/Identity-H) is a 2-BYTE code space, same as the hex-
1345
+ // string path below -- a literal string's BYTES must be paired up
1346
+ // big-endian into one CID per pair, not looked up one byte at a time.
1347
+ // Found on mainland China's NPC/MOJ law PDFs (cn-law, fleet #3136):
1348
+ // Founder PDFGenerator (方正), the producer behind flk.npc.gov.cn's
1349
+ // and xzfg.moj.gov.cn's statute PDFs, writes most CJK body text as
1350
+ // literal strings like `(\000I)Tj` under a Type0 CID font rather than
1351
+ // as hex strings -- the shape this branch was missing. Decoding each
1352
+ // byte separately (the pre-fix behavior) looks up CID 0x00 and CID
1353
+ // 0x49 independently instead of the real CID 0x0049, so virtually
1354
+ // every CJK glyph misses its ToUnicode entry and comes back as
1355
+ // U+FFFD -- silently: no warning, a non-empty result, just wrong.
1356
+ // Measured against 中华人民共和国个人信息保护法 (PIPL)'s official
1357
+ // gazette PDF: 18,929 of 19,858 extracted characters were U+FFFD
1358
+ // before this fix (95%), 0 after, and `第十三条` (Article 13) and its
1359
+ // neighbors read correctly. If the literal string has an odd byte
1360
+ // count (shouldn't happen for a well-formed 2-byte-code run), the
1361
+ // trailing lone byte is silently dropped rather than guessed at.
1301
1362
  if (dec.kind === 'winansi') return unescaped;
1363
+ if (dec.kind === 'cid') {
1364
+ let out = '';
1365
+ for (let i = 0; i + 2 <= unescaped.length; i += 2) {
1366
+ const code =
1367
+ (rawByteFromLatin1Char(unescaped.charCodeAt(i)) << 8) | rawByteFromLatin1Char(unescaped.charCodeAt(i + 1));
1368
+ out += String.fromCodePoint(dec.map.get(code) ?? 0xfffd);
1369
+ }
1370
+ return out;
1371
+ }
1302
1372
  let out = '';
1303
1373
  for (let i = 0; i < unescaped.length; i++) {
1304
- const code = unescaped.charCodeAt(i) & 0xff;
1374
+ const code = rawByteFromLatin1Char(unescaped.charCodeAt(i));
1305
1375
  out += String.fromCodePoint(dec.map.get(code) ?? 0xfffd);
1306
1376
  }
1307
1377
  return out;
package/src/server.ts CHANGED
@@ -11,7 +11,7 @@ import * as bakedIndexModule from './gu-baked-data.js';
11
11
  const bakedIndex = Object.fromEntries(Object.entries(bakedIndexModule));
12
12
 
13
13
  const server = new Server(
14
- { name: '@pipeworx/mcp-guam-code', version: '0.1.1' },
14
+ { name: '@pipeworx/mcp-guam-code', version: '0.1.2' },
15
15
  { capabilities: { tools: {} } },
16
16
  );
17
17