Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions epicshop/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions epicshop/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{
"type": "module",
"scripts": {
"postinstall": "node ./patch-workshop-app.js",
"test:patch-workshop-app": "node --test ./patch-workshop-app.test.js"
},
"dependencies": {
"@epic-web/workshop-app": "^6.90.3",
"@epic-web/workshop-utils": "^6.90.3",
Expand Down
91 changes: 91 additions & 0 deletions epicshop/patch-workshop-app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import fs from 'node:fs/promises'
import path from 'node:path'
import { fileURLToPath } from 'node:url'

const currentFilePath = fileURLToPath(import.meta.url)
const currentDir = path.dirname(currentFilePath)

const serverPatchMarker = 'programming-foundations:sentry-noisy-catch-all-posts'

const serverPatchSearch = `app.options("*splat", (_req, res) => {
res.set("Allow", "GET, HEAD, POST, OPTIONS");
res.sendStatus(204);
});
function getNumberOrNull(value) {`

const serverPatchReplacement = `app.options("*splat", (_req, res) => {
res.set("Allow", "GET, HEAD, POST, OPTIONS");
res.sendStatus(204);
});
const noisyCatchAllMutationPrefixes = [
"/__rsc",
"/_next",
"/_rsc",
"/__nextjs_action",
"/_middleware",
"/RSC/"
];
const noisyCatchAllMutationPaths = /* @__PURE__ */ new Set([
"/",
"/.action"
]);
function isNoisyCatchAllMutationPath(pathname) {
return noisyCatchAllMutationPaths.has(pathname) || noisyCatchAllMutationPrefixes.some(
(prefix) => pathname === prefix || pathname.startsWith(prefix.endsWith("/") ? prefix : \`\${prefix}/\`)
);
}
app.all("*splat", (req, res, next) => {
if (req.method === "GET" || req.method === "HEAD" || req.method === "OPTIONS") {
return next();
}
if (!isNoisyCatchAllMutationPath(req.path)) return next();

res.status(404).type("text/plain").send("Not found");
});
// ${serverPatchMarker}
function getNumberOrNull(value) {`

function getWorkshopAppServerPath(epicshopDir = currentDir) {
return path.join(
epicshopDir,
'node_modules',
'@epic-web',
'workshop-app',
'dist',
'server',
'index.js',
)
}

export function patchWorkshopAppServer(source) {
if (source.includes(serverPatchMarker)) return source

if (!source.includes(serverPatchSearch)) {
throw new Error(
'Could not find the workshop app server insertion point. The upstream server may have changed.',
)
}

return source.replace(serverPatchSearch, serverPatchReplacement)
}

export async function patchWorkshopApp(epicshopDir = currentDir) {
const serverPath = getWorkshopAppServerPath(epicshopDir)
const source = await fs.readFile(serverPath, 'utf8')
const patchedSource = patchWorkshopAppServer(source)

if (patchedSource === source) {
console.log('workshop-app server patch already applied')
return
}

await fs.writeFile(serverPath, patchedSource)
console.log('patched workshop-app server noisy catch-all mutation handling')
}

if (process.argv[1] === currentFilePath) {
patchWorkshopApp().catch((error) => {
console.error(error)
process.exit(1)
})
}
45 changes: 45 additions & 0 deletions epicshop/patch-workshop-app.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import assert from 'node:assert/strict'
import { describe, it } from 'node:test'

import { patchWorkshopAppServer } from './patch-workshop-app.js'

const serverSource = `app.options("*splat", (_req, res) => {
res.set("Allow", "GET, HEAD, POST, OPTIONS");
res.sendStatus(204);
});
function getNumberOrNull(value) {
if (value == null) return null;
}`

describe('patchWorkshopAppServer', () => {
it('adds a narrow normal 404 handler for known scanner mutation paths', () => {
const patchedSource = patchWorkshopAppServer(serverSource)

assert.match(patchedSource, /programming-foundations:sentry-noisy-catch-all-posts/)
assert.match(patchedSource, /app\.all\("\*splat"/)
assert.match(patchedSource, /req\.method === "GET"/)
assert.match(patchedSource, /req\.method === "HEAD"/)
assert.match(patchedSource, /req\.method === "OPTIONS"/)
assert.match(patchedSource, /"\/__rsc"/)
assert.match(patchedSource, /"\/_next"/)
assert.match(patchedSource, /"\/_rsc"/)
assert.match(patchedSource, /"\/__nextjs_action"/)
assert.match(patchedSource, /"\/_middleware"/)
assert.match(patchedSource, /"\/RSC\/"/)
assert.match(patchedSource, /"\/\.action"/)
assert.match(patchedSource, /res\.status\(404\)/)
})

it('does not apply the server patch more than once', () => {
const patchedSource = patchWorkshopAppServer(serverSource)

assert.equal(patchWorkshopAppServer(patchedSource), patchedSource)
})

it('fails loudly if the upstream server shape changes', () => {
assert.throws(
() => patchWorkshopAppServer('app.all("*splat", handler)'),
/Could not find the workshop app server insertion point/,
)
})
})
Loading