@vp-tw/ptt-link-switcher 0.1.3 → 0.2.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/README.md +10 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,19 +27,26 @@ if (result.ok) {
|
|
|
27
27
|
"label": "BePTT",
|
|
28
28
|
"url": "https://bbs.beptt.tw/Browsers/M.1750772319.A.F2C"
|
|
29
29
|
},
|
|
30
|
+
{
|
|
31
|
+
"id": "pttweb",
|
|
32
|
+
"label": "PTTweb",
|
|
33
|
+
"url": "https://www.pttweb.cc/bbs/Browsers/M.1750772319.A.F2C"
|
|
34
|
+
},
|
|
30
35
|
{
|
|
31
36
|
"id": "moptt",
|
|
32
37
|
"label": "MoPTT",
|
|
33
38
|
"url": "https://moptt.tw/p/Browsers.M.1750772319.A.F2C"
|
|
34
39
|
},
|
|
35
40
|
{
|
|
36
|
-
"id": "
|
|
37
|
-
"label": "
|
|
38
|
-
"url": "https://
|
|
41
|
+
"id": "nextptt",
|
|
42
|
+
"label": "NextPTT",
|
|
43
|
+
"url": "https://nextptt.app/boards/Browsers/post/M.1750772319.A.F2C"
|
|
39
44
|
}
|
|
40
45
|
]
|
|
41
46
|
```
|
|
42
47
|
|
|
48
|
+
The built-in order is Official PTT, BePTT, PTTweb, MoPTT, and NextPTT. This is a default ordering, not a guarantee of social-platform preview compatibility.
|
|
49
|
+
|
|
43
50
|
Provider support is adapter-based. Pass custom adapters to `parsePttInput` and `generateProviderLinks` to extend parsing and URL generation without changing the built-in registry.
|
|
44
51
|
|
|
45
52
|
The package is framework-free and does not access the DOM, network, or storage.
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ interface PttArticleRef {
|
|
|
8
8
|
readonly board: string;
|
|
9
9
|
readonly articleId: string;
|
|
10
10
|
}
|
|
11
|
-
type ProviderId = "official" | "beptt" | "moptt" | "pttweb";
|
|
11
|
+
type ProviderId = "official" | "beptt" | "moptt" | "pttweb" | "nextptt";
|
|
12
12
|
interface PttUrl {
|
|
13
13
|
readonly hostname: string;
|
|
14
14
|
readonly pathname: string;
|
|
@@ -73,7 +73,8 @@ declare const officialProvider: ProviderAdapter<"official">;
|
|
|
73
73
|
declare const bepttProvider: ProviderAdapter<"beptt">;
|
|
74
74
|
declare const mopttProvider: ProviderAdapter<"moptt">;
|
|
75
75
|
declare const pttwebProvider: ProviderAdapter<"pttweb">;
|
|
76
|
-
declare const
|
|
76
|
+
declare const nextpttProvider: ProviderAdapter<"nextptt">;
|
|
77
|
+
declare const providers: readonly [ProviderAdapter<"official">, ProviderAdapter<"beptt">, ProviderAdapter<"pttweb">, ProviderAdapter<"moptt">, ProviderAdapter<"nextptt">];
|
|
77
78
|
declare function getProvider<TAdapter extends ProviderAdapter>(adapters: readonly TAdapter[], id: string): TAdapter | undefined;
|
|
78
79
|
declare function generateProviderLinks(article: PttArticleRef): ProviderLink<ProviderId>[];
|
|
79
80
|
declare function generateProviderLinks<TAdapter extends ProviderAdapter>(article: PttArticleRef, adapters: readonly TAdapter[]): ProviderLink<TAdapter["id"]>[];
|
|
@@ -82,5 +83,5 @@ declare function generateProviderLinks<TAdapter extends ProviderAdapter>(article
|
|
|
82
83
|
declare function normalizeArticleRef(board: string, articleId: string): PttArticleRef | null;
|
|
83
84
|
declare function isValidBoard(board: string): boolean;
|
|
84
85
|
//#endregion
|
|
85
|
-
export { type ParseError, type ParseOptions, type ParseResult, type ParseSource, type ProviderAdapter, type ProviderId, type ProviderLink, type PttArticleRef, type PttUrl, aidToArticleId, articleIdToAid, bepttProvider, generateProviderLinks, getProvider, isValidAid, isValidBoard, mopttProvider, normalizeArticleRef, officialProvider, parsePttInput, providers, pttwebProvider, tryParsePttInput };
|
|
86
|
+
export { type ParseError, type ParseOptions, type ParseResult, type ParseSource, type ProviderAdapter, type ProviderId, type ProviderLink, type PttArticleRef, type PttUrl, aidToArticleId, articleIdToAid, bepttProvider, generateProviderLinks, getProvider, isValidAid, isValidBoard, mopttProvider, nextpttProvider, normalizeArticleRef, officialProvider, parsePttInput, providers, pttwebProvider, tryParsePttInput };
|
|
86
87
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/aid.ts","../src/types.ts","../src/parse.ts","../src/providers.ts","../src/validation.ts"],"mappings":";iBAIgB,WAAW;iBAIX,eAAe;iBAuBf,eAAe;;;UC/Bd;WACN;WACA;;KAGC;UAEK;WACN;WACA;WACA;;UAGM,gBAAgB;WACtB,IAAI;WACJ;WACA;WACA,WAAW,KAAK,WAAW;WAC3B,QAAQ,SAAS;;KAGhB;WACG;;WACA;WAAsB;WAA6B;;KAEtD;WACG;WAA8B;;WAC9B;WAA8B;;WAE9B;WACA;WACA;;WAGA;WACA;WACA;;WAEA;WAAqC;;KAExC;WAEG,SAAS;WACT;WACA,QAAQ;;WAER,OAAO;WAAqB;;UAE1B;WACN;WACA,qBAAqB;;UAGf,aAAa;WACnB,IAAI;WACJ;WACA;;;;iBCXK,cAAc,eAAe,UAAS,eAAoB;iBA8F1D,iBAAiB,eAAe,UAAU,eAAe;;;cCjH5D,kBAAkB;cASlB,eAAe;cAcf,eAAe;cAcf,gBAAgB;cAShB,qBACX,6BACA,0BACA,0BACA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/aid.ts","../src/types.ts","../src/parse.ts","../src/providers.ts","../src/validation.ts"],"mappings":";iBAIgB,WAAW;iBAIX,eAAe;iBAuBf,eAAe;;;UC/Bd;WACN;WACA;;KAGC;UAEK;WACN;WACA;WACA;;UAGM,gBAAgB;WACtB,IAAI;WACJ;WACA;WACA,WAAW,KAAK,WAAW;WAC3B,QAAQ,SAAS;;KAGhB;WACG;;WACA;WAAsB;WAA6B;;KAEtD;WACG;WAA8B;;WAC9B;WAA8B;;WAE9B;WACA;WACA;;WAGA;WACA;WACA;;WAEA;WAAqC;;KAExC;WAEG,SAAS;WACT;WACA,QAAQ;;WAER,OAAO;WAAqB;;UAE1B;WACN;WACA,qBAAqB;;UAGf,aAAa;WACnB,IAAI;WACJ;WACA;;;;iBCXK,cAAc,eAAe,UAAS,eAAoB;iBA8F1D,iBAAiB,eAAe,UAAU,eAAe;;;cCjH5D,kBAAkB;cASlB,eAAe;cAcf,eAAe;cAcf,gBAAgB;cAShB,iBAAiB;cAgBjB,qBACX,6BACA,0BACA,2BACA,0BACA;iBASc,YAAY,iBAAiB,iBAC3C,mBAAmB,YACnB,aACC;iBAIa,sBAAsB,SAAS,gBAAgB,aAAa;iBAC5D,sBAAsB,iBAAiB,iBACrD,SAAS,eACT,mBAAmB,aAClB,aAAa;;;iBC5GA,oBAAoB,eAAe,oBAAoB;iBAgBvD,aAAa"}
|
package/dist/index.js
CHANGED
|
@@ -113,11 +113,24 @@ const pttwebProvider = defineProvider({
|
|
|
113
113
|
parseUrl: (url) => parsePttStylePath(url.pathname),
|
|
114
114
|
toUrl: (article) => `https://www.pttweb.cc/bbs/${encodeURIComponent(article.board)}/${article.articleId}`
|
|
115
115
|
});
|
|
116
|
+
const nextpttProvider = defineProvider({
|
|
117
|
+
hosts: ["nextptt.app"],
|
|
118
|
+
id: "nextptt",
|
|
119
|
+
label: "NextPTT",
|
|
120
|
+
parseUrl: (url) => {
|
|
121
|
+
const match = /^\/boards\/([^/]+)\/post\/((?:M|G)\.\d+\.A(?:\.[0-9A-Fa-f]{3})?)\/?$/.exec(url.pathname);
|
|
122
|
+
if (!match || match[1] === void 0 || match[2] === void 0) return null;
|
|
123
|
+
const board = decodePathPart(match[1]);
|
|
124
|
+
return board === null ? null : normalizeArticleRef(board, match[2]);
|
|
125
|
+
},
|
|
126
|
+
toUrl: (article) => `https://nextptt.app/boards/${encodeURIComponent(article.board)}/post/${article.articleId}`
|
|
127
|
+
});
|
|
116
128
|
const providers = Object.freeze([
|
|
117
129
|
officialProvider,
|
|
118
130
|
bepttProvider,
|
|
131
|
+
pttwebProvider,
|
|
119
132
|
mopttProvider,
|
|
120
|
-
|
|
133
|
+
nextpttProvider
|
|
121
134
|
]);
|
|
122
135
|
function getProvider(adapters, id) {
|
|
123
136
|
return adapters.find((adapter) => adapter.id === id);
|
|
@@ -236,6 +249,6 @@ function tryParsePttInput(input, options) {
|
|
|
236
249
|
return result.ok ? result.article : null;
|
|
237
250
|
}
|
|
238
251
|
//#endregion
|
|
239
|
-
export { aidToArticleId, articleIdToAid, bepttProvider, generateProviderLinks, getProvider, isValidAid, isValidBoard, mopttProvider, normalizeArticleRef, officialProvider, parsePttInput, providers, pttwebProvider, tryParsePttInput };
|
|
252
|
+
export { aidToArticleId, articleIdToAid, bepttProvider, generateProviderLinks, getProvider, isValidAid, isValidBoard, mopttProvider, nextpttProvider, normalizeArticleRef, officialProvider, parsePttInput, providers, pttwebProvider, tryParsePttInput };
|
|
240
253
|
|
|
241
254
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["ARTICLE_ID_PATTERN","defaultProviders"],"sources":["../src/aid.ts","../src/validation.ts","../src/providers.ts","../src/parse.ts"],"sourcesContent":["const AID_ALPHABET = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_\";\nconst AID_PATTERN = /^[0-9A-Za-z_-]{8}$/;\nconst ARTICLE_ID_PATTERN = /^(M|G)\\.(\\d+)\\.A(?:\\.([0-9A-F]{3}))?$/;\n\nexport function isValidAid(value: string): boolean {\n return AID_PATTERN.test(value);\n}\n\nexport function aidToArticleId(aid: string): string | null {\n const normalized = aid.startsWith(\"#\") ? aid.slice(1) : aid;\n if (!isValidAid(normalized)) return null;\n\n let timestamp = 0;\n for (const character of normalized.slice(0, 6)) {\n const index = AID_ALPHABET.indexOf(character);\n if (index < 0) return null;\n timestamp = timestamp * 64 + index;\n }\n\n let checksum = 0;\n for (const character of normalized.slice(6)) {\n const index = AID_ALPHABET.indexOf(character);\n if (index < 0) return null;\n checksum = checksum * 64 + index;\n }\n\n const checksumPart =\n checksum === 0 ? \"\" : `.${checksum.toString(16).toUpperCase().padStart(3, \"0\")}`;\n return `M.${timestamp}.A${checksumPart}`;\n}\n\nexport function articleIdToAid(articleId: string): string | null {\n const match = ARTICLE_ID_PATTERN.exec(articleId.toUpperCase());\n if (!match || match[1] !== \"M\" || match[2] === undefined) return null;\n\n let timestamp = Number(match[2]);\n let checksum = match[3] === undefined ? 0 : Number.parseInt(match[3], 16);\n if (!Number.isSafeInteger(timestamp) || timestamp < 0 || checksum > 0xfff) return null;\n\n let encodedTimestamp = \"\";\n for (let index = 0; index < 6; index += 1) {\n encodedTimestamp = AID_ALPHABET[timestamp % 64]! + encodedTimestamp;\n timestamp = Math.floor(timestamp / 64);\n }\n\n let encodedChecksum = \"\";\n for (let index = 0; index < 2; index += 1) {\n encodedChecksum = AID_ALPHABET[checksum % 64]! + encodedChecksum;\n checksum = Math.floor(checksum / 64);\n }\n\n return `#${encodedTimestamp}${encodedChecksum}`;\n}\n","import type { PttArticleRef } from \"./types.js\";\n\nconst BOARD_PATTERN = /^[A-Za-z0-9_.-]+$/;\nconst ARTICLE_ID_PATTERN = /^(M|G)\\.\\d+\\.A(?:\\.[0-9A-F]{3})?$/;\n\nexport function normalizeArticleRef(board: string, articleId: string): PttArticleRef | null {\n const normalizedBoard = board.trim();\n const normalizedArticleId = articleId.trim().toUpperCase();\n\n if (\n normalizedBoard.length === 0 ||\n normalizedBoard.length > 64 ||\n !BOARD_PATTERN.test(normalizedBoard) ||\n !ARTICLE_ID_PATTERN.test(normalizedArticleId)\n ) {\n return null;\n }\n\n return { articleId: normalizedArticleId, board: normalizedBoard };\n}\n\nexport function isValidBoard(board: string): boolean {\n return normalizeArticleRef(board, \"M.0.A\") !== null;\n}\n","import type { ProviderAdapter, ProviderId, ProviderLink, PttArticleRef, PttUrl } from \"./types.js\";\nimport { normalizeArticleRef } from \"./validation.js\";\n\nfunction decodePathPart(value: string): string | null {\n try {\n return decodeURIComponent(value);\n } catch {\n return null;\n }\n}\n\nfunction parsePttStylePath(pathname: string): PttArticleRef | null {\n const match = /^\\/bbs\\/([^/]+)\\/((?:M|G)\\.\\d+\\.A(?:\\.[0-9A-Fa-f]{3})?)(?:\\.html)?\\/?$/.exec(\n pathname,\n );\n if (!match || match[1] === undefined || match[2] === undefined) return null;\n const board = decodePathPart(match[1]);\n return board === null ? null : normalizeArticleRef(board, match[2]);\n}\n\nfunction defineProvider<TId extends ProviderId>(\n provider: ProviderAdapter<TId>,\n): ProviderAdapter<TId> {\n return Object.freeze(provider);\n}\n\nexport const officialProvider: ProviderAdapter<\"official\"> = defineProvider({\n hosts: [\"www.ptt.cc\", \"ptt.cc\"],\n id: \"official\",\n label: \"PTT 官方\",\n parseUrl: (url: PttUrl): PttArticleRef | null => parsePttStylePath(url.pathname),\n toUrl: (article: PttArticleRef): string =>\n `https://www.ptt.cc/bbs/${encodeURIComponent(article.board)}/${article.articleId}.html`,\n});\n\nexport const bepttProvider: ProviderAdapter<\"beptt\"> = defineProvider({\n hosts: [\"bbs.beptt.tw\", \"bbs.beptt.cc\"],\n id: \"beptt\",\n label: \"BePTT\",\n parseUrl: (url: PttUrl): PttArticleRef | null => {\n const match = /^\\/([^/]+)\\/((?:M|G)\\.\\d+\\.A(?:\\.[0-9A-Fa-f]{3})?)\\/?$/.exec(url.pathname);\n if (!match || match[1] === undefined || match[2] === undefined) return null;\n const board = decodePathPart(match[1]);\n return board === null ? null : normalizeArticleRef(board, match[2]);\n },\n toUrl: (article: PttArticleRef): string =>\n `https://bbs.beptt.tw/${encodeURIComponent(article.board)}/${article.articleId}`,\n});\n\nexport const mopttProvider: ProviderAdapter<\"moptt\"> = defineProvider({\n hosts: [\"moptt.tw\", \"www.moptt.tw\"],\n id: \"moptt\",\n label: \"MoPTT\",\n parseUrl: (url: PttUrl): PttArticleRef | null => {\n const match = /^\\/p\\/(.+?)\\.((?:M|G)\\.\\d+\\.A(?:\\.[0-9A-Fa-f]{3})?)\\/?$/.exec(url.pathname);\n if (!match || match[1] === undefined || match[2] === undefined) return null;\n const board = decodePathPart(match[1]);\n return board === null ? null : normalizeArticleRef(board, match[2]);\n },\n toUrl: (article: PttArticleRef): string =>\n `https://moptt.tw/p/${encodeURIComponent(article.board)}.${article.articleId}`,\n});\n\nexport const pttwebProvider: ProviderAdapter<\"pttweb\"> = defineProvider({\n hosts: [\"www.pttweb.cc\", \"pttweb.cc\"],\n id: \"pttweb\",\n label: \"PTTweb\",\n parseUrl: (url: PttUrl): PttArticleRef | null => parsePttStylePath(url.pathname),\n toUrl: (article: PttArticleRef): string =>\n `https://www.pttweb.cc/bbs/${encodeURIComponent(article.board)}/${article.articleId}`,\n});\n\nexport const providers: readonly [\n ProviderAdapter<\"official\">,\n ProviderAdapter<\"beptt\">,\n ProviderAdapter<\"moptt\">,\n ProviderAdapter<\"pttweb\">,\n] = Object.freeze([officialProvider, bepttProvider, mopttProvider, pttwebProvider] as const);\n\nexport function getProvider<TAdapter extends ProviderAdapter>(\n adapters: readonly TAdapter[],\n id: string,\n): TAdapter | undefined {\n return adapters.find((adapter) => adapter.id === id);\n}\n\nexport function generateProviderLinks(article: PttArticleRef): ProviderLink<ProviderId>[];\nexport function generateProviderLinks<TAdapter extends ProviderAdapter>(\n article: PttArticleRef,\n adapters: readonly TAdapter[],\n): ProviderLink<TAdapter[\"id\"]>[];\nexport function generateProviderLinks(\n article: PttArticleRef,\n adapters: readonly ProviderAdapter[] = providers,\n): ProviderLink[] {\n return adapters.map((adapter) => ({\n id: adapter.id,\n label: adapter.label,\n url: adapter.toUrl(article),\n }));\n}\n","import { aidToArticleId, isValidAid } from \"./aid.js\";\nimport { providers as defaultProviders } from \"./providers.js\";\nimport type { ParseOptions, ParseResult, ProviderAdapter, PttArticleRef } from \"./types.js\";\nimport { isValidBoard, normalizeArticleRef } from \"./validation.js\";\n\nconst URL_PATTERN = /https?:\\/\\/[^\\s<>\"']+/giu;\nconst AID_INPUT_PATTERN = /^#?([0-9A-Za-z_-]{8})(?:\\s*\\(([^)]+)\\))?$/;\nconst TRAILING_PUNCTUATION = /[),.!?,。!?;;::]+$/u;\n\nfunction urlsFromText(input: string): URL[] {\n const matches = input.match(URL_PATTERN) ?? [];\n const urls: URL[] = [];\n\n for (const match of matches) {\n try {\n urls.push(new URL(match.replace(TRAILING_PUNCTUATION, \"\")));\n } catch {\n // Ignore malformed URL-like fragments and continue to AID parsing.\n }\n }\n\n return urls;\n}\n\nfunction adapterForUrl(\n url: URL,\n adapters: readonly ProviderAdapter[],\n): ProviderAdapter | undefined {\n const hostname = url.hostname.toLowerCase();\n return adapters.find((adapter) => adapter.hosts.includes(hostname));\n}\n\nfunction success(\n article: PttArticleRef,\n source:\n | { readonly kind: \"aid\" }\n | {\n readonly kind: \"url\";\n readonly providerId: string;\n readonly url: string;\n },\n): ParseResult {\n return { article, ok: true, source };\n}\n\nexport function parsePttInput(input: string, options: ParseOptions = {}): ParseResult {\n const trimmed = input.trim();\n if (trimmed.length === 0) {\n return {\n error: { code: \"empty_input\", message: \"Enter a PTT article URL or full AID.\" },\n ok: false,\n };\n }\n\n const adapters = options.providers ?? defaultProviders;\n const urls = urlsFromText(trimmed);\n let unsupportedHost: string | undefined;\n\n for (const url of urls) {\n const adapter = adapterForUrl(url, adapters);\n if (adapter === undefined) {\n unsupportedHost ??= url.hostname;\n continue;\n }\n\n const article = adapter.parseUrl(url);\n if (article !== null) {\n return success(article, {\n kind: \"url\",\n providerId: adapter.id,\n url: url.toString(),\n });\n }\n }\n\n const aidMatch = AID_INPUT_PATTERN.exec(trimmed);\n if (aidMatch?.[1] !== undefined) {\n const aid = aidMatch[1];\n if (!isValidAid(aid)) {\n return {\n error: { code: \"invalid_aid\", message: \"The AID must contain exactly 8 characters.\" },\n ok: false,\n };\n }\n\n const board = (aidMatch[2] ?? options.board)?.trim();\n if (board === undefined || board.length === 0) {\n return {\n error: {\n aid: `#${aid}`,\n code: \"missing_board\",\n message: \"Add the board name to convert this AID offline.\",\n },\n ok: false,\n };\n }\n\n if (!isValidBoard(board)) {\n return {\n error: { code: \"invalid_aid\", message: \"The board name is not valid.\" },\n ok: false,\n };\n }\n\n const articleId = aidToArticleId(aid);\n const article = articleId === null ? null : normalizeArticleRef(board, articleId);\n if (article !== null) return success(article, { kind: \"aid\" });\n }\n\n if (trimmed.startsWith(\"#\")) {\n return {\n error: {\n code: \"invalid_aid\",\n message: \"The AID must contain exactly 8 supported characters.\",\n },\n ok: false,\n };\n }\n\n if (unsupportedHost !== undefined) {\n return {\n error: {\n code: \"unsupported_provider\",\n host: unsupportedHost,\n message: `Unsupported PTT reader: ${unsupportedHost}`,\n },\n ok: false,\n };\n }\n\n return {\n error: {\n code: \"unrecognized_input\",\n message: \"No supported PTT article URL or full AID was found.\",\n },\n ok: false,\n };\n}\n\nexport function tryParsePttInput(input: string, options?: ParseOptions): PttArticleRef | null {\n const result = parsePttInput(input, options);\n return result.ok ? result.article : null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["ARTICLE_ID_PATTERN","defaultProviders"],"sources":["../src/aid.ts","../src/validation.ts","../src/providers.ts","../src/parse.ts"],"sourcesContent":["const AID_ALPHABET = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_\";\nconst AID_PATTERN = /^[0-9A-Za-z_-]{8}$/;\nconst ARTICLE_ID_PATTERN = /^(M|G)\\.(\\d+)\\.A(?:\\.([0-9A-F]{3}))?$/;\n\nexport function isValidAid(value: string): boolean {\n return AID_PATTERN.test(value);\n}\n\nexport function aidToArticleId(aid: string): string | null {\n const normalized = aid.startsWith(\"#\") ? aid.slice(1) : aid;\n if (!isValidAid(normalized)) return null;\n\n let timestamp = 0;\n for (const character of normalized.slice(0, 6)) {\n const index = AID_ALPHABET.indexOf(character);\n if (index < 0) return null;\n timestamp = timestamp * 64 + index;\n }\n\n let checksum = 0;\n for (const character of normalized.slice(6)) {\n const index = AID_ALPHABET.indexOf(character);\n if (index < 0) return null;\n checksum = checksum * 64 + index;\n }\n\n const checksumPart =\n checksum === 0 ? \"\" : `.${checksum.toString(16).toUpperCase().padStart(3, \"0\")}`;\n return `M.${timestamp}.A${checksumPart}`;\n}\n\nexport function articleIdToAid(articleId: string): string | null {\n const match = ARTICLE_ID_PATTERN.exec(articleId.toUpperCase());\n if (!match || match[1] !== \"M\" || match[2] === undefined) return null;\n\n let timestamp = Number(match[2]);\n let checksum = match[3] === undefined ? 0 : Number.parseInt(match[3], 16);\n if (!Number.isSafeInteger(timestamp) || timestamp < 0 || checksum > 0xfff) return null;\n\n let encodedTimestamp = \"\";\n for (let index = 0; index < 6; index += 1) {\n encodedTimestamp = AID_ALPHABET[timestamp % 64]! + encodedTimestamp;\n timestamp = Math.floor(timestamp / 64);\n }\n\n let encodedChecksum = \"\";\n for (let index = 0; index < 2; index += 1) {\n encodedChecksum = AID_ALPHABET[checksum % 64]! + encodedChecksum;\n checksum = Math.floor(checksum / 64);\n }\n\n return `#${encodedTimestamp}${encodedChecksum}`;\n}\n","import type { PttArticleRef } from \"./types.js\";\n\nconst BOARD_PATTERN = /^[A-Za-z0-9_.-]+$/;\nconst ARTICLE_ID_PATTERN = /^(M|G)\\.\\d+\\.A(?:\\.[0-9A-F]{3})?$/;\n\nexport function normalizeArticleRef(board: string, articleId: string): PttArticleRef | null {\n const normalizedBoard = board.trim();\n const normalizedArticleId = articleId.trim().toUpperCase();\n\n if (\n normalizedBoard.length === 0 ||\n normalizedBoard.length > 64 ||\n !BOARD_PATTERN.test(normalizedBoard) ||\n !ARTICLE_ID_PATTERN.test(normalizedArticleId)\n ) {\n return null;\n }\n\n return { articleId: normalizedArticleId, board: normalizedBoard };\n}\n\nexport function isValidBoard(board: string): boolean {\n return normalizeArticleRef(board, \"M.0.A\") !== null;\n}\n","import type { ProviderAdapter, ProviderId, ProviderLink, PttArticleRef, PttUrl } from \"./types.js\";\nimport { normalizeArticleRef } from \"./validation.js\";\n\nfunction decodePathPart(value: string): string | null {\n try {\n return decodeURIComponent(value);\n } catch {\n return null;\n }\n}\n\nfunction parsePttStylePath(pathname: string): PttArticleRef | null {\n const match = /^\\/bbs\\/([^/]+)\\/((?:M|G)\\.\\d+\\.A(?:\\.[0-9A-Fa-f]{3})?)(?:\\.html)?\\/?$/.exec(\n pathname,\n );\n if (!match || match[1] === undefined || match[2] === undefined) return null;\n const board = decodePathPart(match[1]);\n return board === null ? null : normalizeArticleRef(board, match[2]);\n}\n\nfunction defineProvider<TId extends ProviderId>(\n provider: ProviderAdapter<TId>,\n): ProviderAdapter<TId> {\n return Object.freeze(provider);\n}\n\nexport const officialProvider: ProviderAdapter<\"official\"> = defineProvider({\n hosts: [\"www.ptt.cc\", \"ptt.cc\"],\n id: \"official\",\n label: \"PTT 官方\",\n parseUrl: (url: PttUrl): PttArticleRef | null => parsePttStylePath(url.pathname),\n toUrl: (article: PttArticleRef): string =>\n `https://www.ptt.cc/bbs/${encodeURIComponent(article.board)}/${article.articleId}.html`,\n});\n\nexport const bepttProvider: ProviderAdapter<\"beptt\"> = defineProvider({\n hosts: [\"bbs.beptt.tw\", \"bbs.beptt.cc\"],\n id: \"beptt\",\n label: \"BePTT\",\n parseUrl: (url: PttUrl): PttArticleRef | null => {\n const match = /^\\/([^/]+)\\/((?:M|G)\\.\\d+\\.A(?:\\.[0-9A-Fa-f]{3})?)\\/?$/.exec(url.pathname);\n if (!match || match[1] === undefined || match[2] === undefined) return null;\n const board = decodePathPart(match[1]);\n return board === null ? null : normalizeArticleRef(board, match[2]);\n },\n toUrl: (article: PttArticleRef): string =>\n `https://bbs.beptt.tw/${encodeURIComponent(article.board)}/${article.articleId}`,\n});\n\nexport const mopttProvider: ProviderAdapter<\"moptt\"> = defineProvider({\n hosts: [\"moptt.tw\", \"www.moptt.tw\"],\n id: \"moptt\",\n label: \"MoPTT\",\n parseUrl: (url: PttUrl): PttArticleRef | null => {\n const match = /^\\/p\\/(.+?)\\.((?:M|G)\\.\\d+\\.A(?:\\.[0-9A-Fa-f]{3})?)\\/?$/.exec(url.pathname);\n if (!match || match[1] === undefined || match[2] === undefined) return null;\n const board = decodePathPart(match[1]);\n return board === null ? null : normalizeArticleRef(board, match[2]);\n },\n toUrl: (article: PttArticleRef): string =>\n `https://moptt.tw/p/${encodeURIComponent(article.board)}.${article.articleId}`,\n});\n\nexport const pttwebProvider: ProviderAdapter<\"pttweb\"> = defineProvider({\n hosts: [\"www.pttweb.cc\", \"pttweb.cc\"],\n id: \"pttweb\",\n label: \"PTTweb\",\n parseUrl: (url: PttUrl): PttArticleRef | null => parsePttStylePath(url.pathname),\n toUrl: (article: PttArticleRef): string =>\n `https://www.pttweb.cc/bbs/${encodeURIComponent(article.board)}/${article.articleId}`,\n});\n\nexport const nextpttProvider: ProviderAdapter<\"nextptt\"> = defineProvider({\n hosts: [\"nextptt.app\"],\n id: \"nextptt\",\n label: \"NextPTT\",\n parseUrl: (url: PttUrl): PttArticleRef | null => {\n const match = /^\\/boards\\/([^/]+)\\/post\\/((?:M|G)\\.\\d+\\.A(?:\\.[0-9A-Fa-f]{3})?)\\/?$/.exec(\n url.pathname,\n );\n if (!match || match[1] === undefined || match[2] === undefined) return null;\n const board = decodePathPart(match[1]);\n return board === null ? null : normalizeArticleRef(board, match[2]);\n },\n toUrl: (article: PttArticleRef): string =>\n `https://nextptt.app/boards/${encodeURIComponent(article.board)}/post/${article.articleId}`,\n});\n\nexport const providers: readonly [\n ProviderAdapter<\"official\">,\n ProviderAdapter<\"beptt\">,\n ProviderAdapter<\"pttweb\">,\n ProviderAdapter<\"moptt\">,\n ProviderAdapter<\"nextptt\">,\n] = Object.freeze([\n officialProvider,\n bepttProvider,\n pttwebProvider,\n mopttProvider,\n nextpttProvider,\n] as const);\n\nexport function getProvider<TAdapter extends ProviderAdapter>(\n adapters: readonly TAdapter[],\n id: string,\n): TAdapter | undefined {\n return adapters.find((adapter) => adapter.id === id);\n}\n\nexport function generateProviderLinks(article: PttArticleRef): ProviderLink<ProviderId>[];\nexport function generateProviderLinks<TAdapter extends ProviderAdapter>(\n article: PttArticleRef,\n adapters: readonly TAdapter[],\n): ProviderLink<TAdapter[\"id\"]>[];\nexport function generateProviderLinks(\n article: PttArticleRef,\n adapters: readonly ProviderAdapter[] = providers,\n): ProviderLink[] {\n return adapters.map((adapter) => ({\n id: adapter.id,\n label: adapter.label,\n url: adapter.toUrl(article),\n }));\n}\n","import { aidToArticleId, isValidAid } from \"./aid.js\";\nimport { providers as defaultProviders } from \"./providers.js\";\nimport type { ParseOptions, ParseResult, ProviderAdapter, PttArticleRef } from \"./types.js\";\nimport { isValidBoard, normalizeArticleRef } from \"./validation.js\";\n\nconst URL_PATTERN = /https?:\\/\\/[^\\s<>\"']+/giu;\nconst AID_INPUT_PATTERN = /^#?([0-9A-Za-z_-]{8})(?:\\s*\\(([^)]+)\\))?$/;\nconst TRAILING_PUNCTUATION = /[),.!?,。!?;;::]+$/u;\n\nfunction urlsFromText(input: string): URL[] {\n const matches = input.match(URL_PATTERN) ?? [];\n const urls: URL[] = [];\n\n for (const match of matches) {\n try {\n urls.push(new URL(match.replace(TRAILING_PUNCTUATION, \"\")));\n } catch {\n // Ignore malformed URL-like fragments and continue to AID parsing.\n }\n }\n\n return urls;\n}\n\nfunction adapterForUrl(\n url: URL,\n adapters: readonly ProviderAdapter[],\n): ProviderAdapter | undefined {\n const hostname = url.hostname.toLowerCase();\n return adapters.find((adapter) => adapter.hosts.includes(hostname));\n}\n\nfunction success(\n article: PttArticleRef,\n source:\n | { readonly kind: \"aid\" }\n | {\n readonly kind: \"url\";\n readonly providerId: string;\n readonly url: string;\n },\n): ParseResult {\n return { article, ok: true, source };\n}\n\nexport function parsePttInput(input: string, options: ParseOptions = {}): ParseResult {\n const trimmed = input.trim();\n if (trimmed.length === 0) {\n return {\n error: { code: \"empty_input\", message: \"Enter a PTT article URL or full AID.\" },\n ok: false,\n };\n }\n\n const adapters = options.providers ?? defaultProviders;\n const urls = urlsFromText(trimmed);\n let unsupportedHost: string | undefined;\n\n for (const url of urls) {\n const adapter = adapterForUrl(url, adapters);\n if (adapter === undefined) {\n unsupportedHost ??= url.hostname;\n continue;\n }\n\n const article = adapter.parseUrl(url);\n if (article !== null) {\n return success(article, {\n kind: \"url\",\n providerId: adapter.id,\n url: url.toString(),\n });\n }\n }\n\n const aidMatch = AID_INPUT_PATTERN.exec(trimmed);\n if (aidMatch?.[1] !== undefined) {\n const aid = aidMatch[1];\n if (!isValidAid(aid)) {\n return {\n error: { code: \"invalid_aid\", message: \"The AID must contain exactly 8 characters.\" },\n ok: false,\n };\n }\n\n const board = (aidMatch[2] ?? options.board)?.trim();\n if (board === undefined || board.length === 0) {\n return {\n error: {\n aid: `#${aid}`,\n code: \"missing_board\",\n message: \"Add the board name to convert this AID offline.\",\n },\n ok: false,\n };\n }\n\n if (!isValidBoard(board)) {\n return {\n error: { code: \"invalid_aid\", message: \"The board name is not valid.\" },\n ok: false,\n };\n }\n\n const articleId = aidToArticleId(aid);\n const article = articleId === null ? null : normalizeArticleRef(board, articleId);\n if (article !== null) return success(article, { kind: \"aid\" });\n }\n\n if (trimmed.startsWith(\"#\")) {\n return {\n error: {\n code: \"invalid_aid\",\n message: \"The AID must contain exactly 8 supported characters.\",\n },\n ok: false,\n };\n }\n\n if (unsupportedHost !== undefined) {\n return {\n error: {\n code: \"unsupported_provider\",\n host: unsupportedHost,\n message: `Unsupported PTT reader: ${unsupportedHost}`,\n },\n ok: false,\n };\n }\n\n return {\n error: {\n code: \"unrecognized_input\",\n message: \"No supported PTT article URL or full AID was found.\",\n },\n ok: false,\n };\n}\n\nexport function tryParsePttInput(input: string, options?: ParseOptions): PttArticleRef | null {\n const result = parsePttInput(input, options);\n return result.ok ? result.article : null;\n}\n"],"mappings":"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"}
|