CI and images / lint (push) Successful in 2s
CI and images / extension-version (push) Successful in 2s
CI and images / extension-test (push) Successful in 20s
CI and images / frontend-build (push) Successful in 24s
CI and images / backend-lint-and-test (push) Successful in 32s
CI and images / integration (push) Successful in 2m22s
CI and images / build-agent (push) Successful in 5s
CI and images / sign-extension (push) Successful in 3m13s
CI and images / build-web (push) Successful in 1m42s
CI and images / smoke-web (push) Successful in 54s
CI and images / promote (push) Successful in 1s
Server (#4420) - extension_service gains a Discord pattern (server or channel, jump links, ptb/canary; not DMs or threads), mirrored in platforms.js and pinned by the shared artist-url-samples.json. - probe on a Discord URL matches the source by ids under any artist, reports a whole-server source as covering the channel, suggests the artist who owns another source on the same server, and names server/channel via the stored token (best-effort, bounded, no rate-limit waits). - quick-add takes artist_id / artist_name; Discord URLs are stored canonical. Extension (#4421, #4422) - Content script on discord.com; SPA navigation by URL polling (the old pushState patch ran in the isolated world and never fired); stale probes are dropped. - Discord chip opens an Add panel: this channel or the whole server, and the suggested artist / a search / a new name. - Popup: sources show artist, platform and state; a Discord token export is verified by FC and the result shown. Token capture covers ptb/canary. - Pure logic in lib/chip.js and lib/popup-format.js, with specs. CI (#4423) - extension.yml's lane (web-ext lint, vitest, XPI contents) moves into build.yml as extension-test and joins the needs of sign-extension, build-web and build-agent. As a separate workflow it gated nothing: a red extension suite still signed and shipped the XPI (rule 177). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVjrnpQjRgHdvq95rASoiR
176 lines
7.2 KiB
JSON
176 lines
7.2 KiB
JSON
{
|
|
"$comment": [
|
|
"THE SHARED ARTIFACT for the JS<->Py artist-pattern mirror (issue #3093).",
|
|
"",
|
|
"extension/lib/platforms.js PLATFORM_ARTIST_PATTERNS and",
|
|
"backend/app/services/extension_service.py _PLATFORM_PATTERNS are two hand-kept",
|
|
"copies of one table, and they gate OPPOSITE HALVES of a single interaction:",
|
|
"the JS copy decides whether the 'Add to FC' button appears, the Python copy",
|
|
"decides whether the resulting POST is accepted. Drift is therefore never",
|
|
"cosmetic -- JS looser than Py shows a button that 400s, Py looser than JS",
|
|
"silently never offers a button for a URL the backend would take. Issue #1485",
|
|
"was the second of those, and its fix had to be applied to both files by hand.",
|
|
"",
|
|
"Neither runtime imports the other. This file is the shared artifact instead:",
|
|
"both suites read it and assert it against their OWN copy of the patterns, so",
|
|
"a change to one copy alone fails the other runtime's suite.",
|
|
" - extension/test/platforms.spec.js (vitest)",
|
|
" - tests/test_extension_artist_patterns.py (pytest)",
|
|
"",
|
|
"Lives under extension/test/ because that path is excluded from BOTH the XPI",
|
|
"file set and the extension version derivation (see scripts/packaging.sh:",
|
|
"NOT_PACKAGED_TRACKED and NOT_VERSION_RELEVANT both carry 'test/**'), so",
|
|
"adding samples here never ships bytes and never forces a re-sign.",
|
|
"",
|
|
"Adding a case: put the URL in the platform's match/no_match list with a",
|
|
"'why'. Run both suites. If only one goes red, you have found drift, which is",
|
|
"the entire point of the file.",
|
|
"",
|
|
"'slug' is read by the Python side only -- its _derive returns (platform,",
|
|
"slug) where the JS isArtistPage returns a boolean. It is not optional on a",
|
|
"match entry; the backend deriving the WRONG slug from a URL both copies",
|
|
"agree on is its own defect class, and this pins it."
|
|
],
|
|
|
|
"patreon": {
|
|
"match": [
|
|
{
|
|
"url": "https://www.patreon.com/maewix",
|
|
"slug": "maewix",
|
|
"why": "bare creator root"
|
|
},
|
|
{
|
|
"url": "https://patreon.com/maewix",
|
|
"slug": "maewix",
|
|
"why": "www is optional"
|
|
},
|
|
{
|
|
"url": "http://patreon.com/maewix",
|
|
"slug": "maewix",
|
|
"why": "http as well as https"
|
|
},
|
|
{
|
|
"url": "https://www.patreon.com/c/Atole",
|
|
"slug": "Atole",
|
|
"why": "#1485: the /c/ creator shape"
|
|
},
|
|
{
|
|
"url": "https://www.patreon.com/cw/Atole",
|
|
"slug": "Atole",
|
|
"why": "#1485: /cw/ is the 'creator workspace' URL Patreon serves once you are SUBSCRIBED -- exactly when the button matters most, and exactly what the pre-#1485 pattern missed"
|
|
},
|
|
{
|
|
"url": "https://www.patreon.com/cw/Atole/posts",
|
|
"slug": "Atole",
|
|
"why": "#1485: a creator inner page still derives the creator"
|
|
},
|
|
{
|
|
"url": "https://www.patreon.com/Atole/membership",
|
|
"slug": "Atole",
|
|
"why": "#1485: inner page on the bare shape"
|
|
}
|
|
],
|
|
"no_match": [
|
|
{ "url": "https://www.patreon.com/home", "why": "nav page, not a creator" },
|
|
{ "url": "https://www.patreon.com/search", "why": "nav page" },
|
|
{ "url": "https://www.patreon.com/messages", "why": "nav page" },
|
|
{ "url": "https://www.patreon.com/notifications", "why": "nav page" },
|
|
{ "url": "https://www.patreon.com/library", "why": "nav page" },
|
|
{ "url": "https://www.patreon.com/settings", "why": "nav page" },
|
|
{ "url": "https://www.patreon.com/posts", "why": "nav page; also the post-permalink prefix" },
|
|
{ "url": "https://www.patreon.com/home/anything", "why": "a nav page's inner path is still not a creator" },
|
|
{ "url": "https://www.patreon.com/settings/profile", "why": "as above" }
|
|
]
|
|
},
|
|
|
|
"subscribestar": {
|
|
"match": [
|
|
{
|
|
"url": "https://www.subscribestar.com/foobar",
|
|
"slug": "foobar",
|
|
"why": "creator root on the .com TLD"
|
|
},
|
|
{
|
|
"url": "https://subscribestar.adult/foobar",
|
|
"slug": "foobar",
|
|
"why": "creator root on the .adult TLD"
|
|
},
|
|
{
|
|
"url": "https://subscribestar.adult/foobar/",
|
|
"slug": "foobar",
|
|
"why": "trailing slash is tolerated"
|
|
}
|
|
],
|
|
"no_match": [
|
|
{ "url": "https://subscribestar.adult/feed", "why": "nav page" },
|
|
{ "url": "https://subscribestar.adult/messages", "why": "nav page" },
|
|
{ "url": "https://subscribestar.adult/library", "why": "nav page" },
|
|
{
|
|
"url": "https://subscribestar.adult/foobar/posts",
|
|
"why": "SubscribeStar's pattern end-anchors on the creator root, unlike Patreon's -- an inner page does NOT derive. Pinned so the asymmetry between the two platforms stays deliberate rather than becoming a silently-fixed bug in one copy only."
|
|
}
|
|
]
|
|
},
|
|
|
|
"hentaifoundry": {
|
|
"match": [
|
|
{
|
|
"url": "https://www.hentai-foundry.com/user/Foo",
|
|
"slug": "Foo",
|
|
"why": "user page"
|
|
},
|
|
{
|
|
"url": "https://www.hentai-foundry.com/user/Foo/profile",
|
|
"slug": "Foo",
|
|
"why": "inner page still derives the user"
|
|
},
|
|
{
|
|
"url": "https://hentai-foundry.com/user/Foo",
|
|
"slug": "Foo",
|
|
"why": "www is optional"
|
|
}
|
|
],
|
|
"no_match": [
|
|
{ "url": "https://www.hentai-foundry.com/pictures/popular", "why": "gallery listing, not a user" },
|
|
{ "url": "https://www.hentai-foundry.com/", "why": "site root" }
|
|
]
|
|
},
|
|
|
|
"discord": {
|
|
"match": [
|
|
{
|
|
"url": "https://discord.com/channels/111111111111111111/222222222222222222",
|
|
"slug": "111111111111111111/222222222222222222",
|
|
"why": "a channel: the slug is server/channel -- a Discord URL names a place, not a creator, so the artist is chosen in the Add panel"
|
|
},
|
|
{
|
|
"url": "https://discord.com/channels/111111111111111111",
|
|
"slug": "111111111111111111",
|
|
"why": "a whole server"
|
|
},
|
|
{
|
|
"url": "https://discord.com/channels/111111111111111111/222222222222222222/333333333333333333",
|
|
"slug": "111111111111111111/222222222222222222",
|
|
"why": "a message jump link still names its channel"
|
|
},
|
|
{
|
|
"url": "https://ptb.discord.com/channels/111111111111111111/222222222222222222",
|
|
"slug": "111111111111111111/222222222222222222",
|
|
"why": "the ptb and canary clients serve the same channels"
|
|
},
|
|
{
|
|
"url": "https://discord.com/channels/111111111111111111/222222222222222222/",
|
|
"slug": "111111111111111111/222222222222222222",
|
|
"why": "trailing slash is tolerated"
|
|
}
|
|
],
|
|
"no_match": [
|
|
{ "url": "https://discord.com/channels/@me", "why": "the DM list is not a source" },
|
|
{ "url": "https://discord.com/channels/@me/222222222222222222", "why": "a DM is not a source" },
|
|
{ "url": "https://discord.com/app", "why": "the app shell, no server open" },
|
|
{ "url": "https://discord.com/channels/111111111111111111/222222222222222222/threads/444444444444444444", "why": "thread links are left to the manual Add form" },
|
|
{ "url": "https://discord.com/servers/111111111111111111", "why": "a server-discovery page, not a channel" }
|
|
]
|
|
}
|
|
}
|