@gmickel/gno 1.29.6 → 1.30.1
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 +15 -5
- package/browser-extension/artifacts/{gno-browser-clipper-v1.29.6.zip → gno-browser-clipper-v1.30.1.zip} +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.30.1.zip.sha256 +1 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/package.json +4 -1
- package/src/core/network-boundary-inventory.ts +51 -0
- package/src/serve/AGENTS.md +5 -1
- package/src/serve/CLAUDE.md +5 -1
- package/src/serve/fn112-routes.ts +232 -0
- package/src/serve/pdfjs-assets.ts +391 -0
- package/src/serve/public/components/pdf/PdfPageView.tsx +427 -0
- package/src/serve/public/components/pdf/PdfToolbar.tsx +384 -0
- package/src/serve/public/components/pdf/PdfViewer.tsx +539 -0
- package/src/serve/public/components/pdf/pdf-viewer-deps.tsx +94 -0
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/globals.css +113 -0
- package/src/serve/public/hooks/use-pdf-document.ts +227 -0
- package/src/serve/public/hooks/use-pdf-pages.ts +1197 -0
- package/src/serve/public/lib/doc-asset-url.ts +57 -0
- package/src/serve/public/lib/math-sum-precise.ts +34 -0
- package/src/serve/public/lib/pdf.ts +772 -0
- package/src/serve/public/pages/DocView.tsx +295 -39
- package/src/serve/public/pages/doc-pdf-viewer.tsx +7 -0
- package/src/serve/routes/api.ts +154 -14
- package/src/serve/server.ts +190 -37
- package/src/serve/spa-bundle-source.ts +99 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.29.6.zip.sha256 +0 -1
package/README.md
CHANGED
|
@@ -20,13 +20,23 @@ gno mcp install --target cursor # or claude-code, claude-desktop, zed, ...
|
|
|
20
20
|
|
|
21
21
|
## What you get
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
**One local index across everything you have.** Markdown, PDFs, Office documents, plain text, source code, and portable mail, calendar and transcript exports. Point it at a folder that mixes all of them and it handles the mix.
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
**Three ways to search it.** Keyword (BM25), semantic (vector), and hybrid — fused, reranked, and explainable, with structured intent controls and metadata filters. Ask for "how we handle retries" and find the paragraph about exponential backoff that never uses the word.
|
|
26
|
+
|
|
27
|
+
**A workspace, not a search box.** Cross-collection folder tree, per-tab browse context, a markdown editor, provenance-carrying quick capture, and a navigable knowledge graph.
|
|
28
|
+
|
|
29
|
+
**Answers with citations.** Ask a question in natural language and get an answer built from your own documents, with citations that resolve to the source passage.
|
|
30
|
+
|
|
31
|
+
**Six interfaces on one index.** A fast CLI, a web UI, a REST API, a TypeScript SDK, an MCP server, and a headless daemon — plus one-command install into ten agent clients. Nothing drifts between them.
|
|
32
|
+
|
|
33
|
+
**Optional hosted publishing** at [gno.sh](https://gno.sh) when a slice needs a URL.
|
|
34
|
+
|
|
35
|
+
No GPU required, no account required, no telemetry. Free and MIT licensed.
|
|
26
36
|
|
|
27
37
|
## Why not just another local RAG tool
|
|
28
38
|
|
|
29
|
-
|
|
39
|
+
The above is most of what people use day to day. Beyond it, four things here are different, and each one is measurable rather than adjectival:
|
|
30
40
|
|
|
31
41
|
| | What it does | Why it matters |
|
|
32
42
|
| :----------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -37,7 +47,7 @@ Most retrieval tools return a ranked list and leave the rest to optimism. Four t
|
|
|
37
47
|
|
|
38
48
|
Everything runs on your machine. Zero telemetry. The three network boundaries are explicit: downloading a model, configuring an HTTP inference endpoint, and uploading an artifact you exported for publishing.
|
|
39
49
|
|
|
40
|
-
**And
|
|
50
|
+
**And the receipts ship too.** Every benchmark behind these numbers is committed to the repository against a pinned corpus, with its limitations stated alongside the result, so you can replay it rather than take it on trust.
|
|
41
51
|
|
|
42
52
|
## Use it when
|
|
43
53
|
|
|
@@ -107,7 +117,7 @@ gno daemon --detach # headless indexing + resident MCP gateway
|
|
|
107
117
|
|
|
108
118
|
<!-- public-truth:current-version -->
|
|
109
119
|
|
|
110
|
-
> Current release: **v1.
|
|
120
|
+
> Current release: **v1.30.1** — see [CHANGELOG.md](./CHANGELOG.md)
|
|
111
121
|
|
|
112
122
|
<!-- /public-truth -->
|
|
113
123
|
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
7f28a8daa50eec43dcd95d5af91002a5e25cd87682ff7c99905af8c9f3457b24 gno-browser-clipper-v1.30.1.zip
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gmickel/gno",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.1",
|
|
4
4
|
"description": "Local semantic search for your documents. Index Markdown, PDF, and Office files with hybrid BM25 + vector search.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"embeddings",
|
|
@@ -66,6 +66,8 @@
|
|
|
66
66
|
"test:web": "bun test test/serve/public --timeout 30000",
|
|
67
67
|
"test:e2e": "bun scripts/web-ui-smoke.ts",
|
|
68
68
|
"test:e2e:clipper": "bun test test/clipper/e2e.test.ts --timeout 240000",
|
|
69
|
+
"test:e2e:pdf": "bun scripts/pdf-viewer-smoke.ts",
|
|
70
|
+
"smoke:pdf-viewer": "bun scripts/pdf-viewer-smoke.ts",
|
|
69
71
|
"test:e2e:install": "bunx playwright install chromium",
|
|
70
72
|
"test:package": "bun scripts/package-smoke.ts",
|
|
71
73
|
"test:package:clipper": "bun scripts/package-smoke-clipper-cli.ts",
|
|
@@ -180,6 +182,7 @@
|
|
|
180
182
|
"nanoid": "5.1.6",
|
|
181
183
|
"node-llama-cpp": "3.18.1",
|
|
182
184
|
"officeparser": "6.0.4",
|
|
185
|
+
"pdfjs-dist": "5.7.284",
|
|
183
186
|
"picocolors": "1.1.1",
|
|
184
187
|
"react": "19.2.4",
|
|
185
188
|
"react-dom": "19.2.4",
|
|
@@ -218,6 +218,57 @@ export const NETWORK_BOUNDARY_INVENTORY = [
|
|
|
218
218
|
action: "serve",
|
|
219
219
|
enforcement: "loopback_only",
|
|
220
220
|
},
|
|
221
|
+
{
|
|
222
|
+
// Loopback self-request that warms the SPA shell cache from the server's
|
|
223
|
+
// own bound port (127.0.0.1). Carries no collection data and never leaves
|
|
224
|
+
// the host.
|
|
225
|
+
id: "serve-spa-shell",
|
|
226
|
+
key: "src/serve/server.ts::fetch#1",
|
|
227
|
+
path: "src/serve/server.ts",
|
|
228
|
+
primitive: "fetch",
|
|
229
|
+
action: "serve",
|
|
230
|
+
enforcement: "loopback_only",
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
// Windows-only private SPA bundle listener. Bun does not support Unix
|
|
234
|
+
// sockets on Windows, so this binds an ephemeral loopback port instead.
|
|
235
|
+
id: "serve-spa-bundle-windows-listener",
|
|
236
|
+
key: "src/serve/spa-bundle-source.ts::bun_serve#1",
|
|
237
|
+
path: "src/serve/spa-bundle-source.ts",
|
|
238
|
+
primitive: "bun_serve",
|
|
239
|
+
action: "serve",
|
|
240
|
+
enforcement: "loopback_only",
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
// Unix-only private SPA bundle listener. The generated assets are exposed
|
|
244
|
+
// through a process-owned Unix-domain socket, never a TCP interface.
|
|
245
|
+
id: "serve-spa-bundle-unix-listener",
|
|
246
|
+
key: "src/serve/spa-bundle-source.ts::bun_serve#2",
|
|
247
|
+
path: "src/serve/spa-bundle-source.ts",
|
|
248
|
+
primitive: "bun_serve",
|
|
249
|
+
action: "serve",
|
|
250
|
+
enforcement: "local_process_only",
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
// Windows-only request from the public server to its private, ephemeral
|
|
254
|
+
// loopback SPA bundle listener.
|
|
255
|
+
id: "serve-spa-bundle-windows-fetch",
|
|
256
|
+
key: "src/serve/spa-bundle-source.ts::fetch#1",
|
|
257
|
+
path: "src/serve/spa-bundle-source.ts",
|
|
258
|
+
primitive: "fetch",
|
|
259
|
+
action: "serve",
|
|
260
|
+
enforcement: "loopback_only",
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
// Unix-only request over the process-owned SPA bundle socket. The HTTP URL
|
|
264
|
+
// is a Bun fetch API requirement; the request is routed through `unix`.
|
|
265
|
+
id: "serve-spa-bundle-unix-fetch",
|
|
266
|
+
key: "src/serve/spa-bundle-source.ts::fetch#2",
|
|
267
|
+
path: "src/serve/spa-bundle-source.ts",
|
|
268
|
+
primitive: "fetch",
|
|
269
|
+
action: "serve",
|
|
270
|
+
enforcement: "local_process_only",
|
|
271
|
+
},
|
|
221
272
|
{
|
|
222
273
|
id: "http-mcp-tools",
|
|
223
274
|
key: "logical::http-mcp-tools",
|
package/src/serve/AGENTS.md
CHANGED
|
@@ -74,6 +74,8 @@ Answer generation uses shared module to stay in sync with CLI:
|
|
|
74
74
|
| `/api/publish/export` | POST | Export gno.sh publish artifact JSON |
|
|
75
75
|
| `/api/docs` | GET | List documents |
|
|
76
76
|
| `/api/doc` | GET | Get document content |
|
|
77
|
+
| `/api/doc-asset` | GET | Original source file bytes (Range, HEAD) |
|
|
78
|
+
| `/vendor/pdfjs/*` | GET | Same-origin pdfjs worker/cmaps/fonts |
|
|
77
79
|
| `/api/search` | POST | BM25 search |
|
|
78
80
|
| `/api/query` | POST | Hybrid search |
|
|
79
81
|
| `/api/ask` | POST | AI answer with citations |
|
|
@@ -106,7 +108,9 @@ gno serve --port 3000
|
|
|
106
108
|
- Binds to `127.0.0.1` only (no LAN exposure)
|
|
107
109
|
- CSP headers on all responses
|
|
108
110
|
- CORS protection on POST endpoints
|
|
109
|
-
- No external font/script loading
|
|
111
|
+
- No external font/script loading — the PDF.js worker, cMaps, and standard fonts
|
|
112
|
+
are served same-origin from the installed `pdfjs-dist` package via
|
|
113
|
+
`/vendor/pdfjs/*`, so the CSP keeps `worker-src 'self'` and `font-src 'self'`
|
|
110
114
|
|
|
111
115
|
## Bun.serve() Patterns
|
|
112
116
|
|
package/src/serve/CLAUDE.md
CHANGED
|
@@ -76,6 +76,8 @@ Answer generation uses shared module to stay in sync with CLI:
|
|
|
76
76
|
| `/api/publish/export` | POST | Export gno.sh publish artifact JSON |
|
|
77
77
|
| `/api/docs` | GET | List documents |
|
|
78
78
|
| `/api/doc` | GET | Get document content |
|
|
79
|
+
| `/api/doc-asset` | GET | Original source file bytes (Range, HEAD) |
|
|
80
|
+
| `/vendor/pdfjs/*` | GET | Same-origin pdfjs worker/cmaps/fonts |
|
|
79
81
|
| `/api/search` | POST | BM25 search |
|
|
80
82
|
| `/api/query` | POST | Hybrid search |
|
|
81
83
|
| `/api/ask` | POST | AI answer with citations |
|
|
@@ -108,7 +110,9 @@ gno serve --port 3000
|
|
|
108
110
|
- Binds to `127.0.0.1` only (no LAN exposure)
|
|
109
111
|
- CSP headers on all responses
|
|
110
112
|
- CORS protection on POST endpoints
|
|
111
|
-
- No external font/script loading
|
|
113
|
+
- No external font/script loading — the PDF.js worker, cMaps, and standard fonts
|
|
114
|
+
are served same-origin from the installed `pdfjs-dist` package via
|
|
115
|
+
`/vendor/pdfjs/*`, so the CSP keeps `worker-src 'self'` and `font-src 'self'`
|
|
112
116
|
|
|
113
117
|
## Bun.serve() Patterns
|
|
114
118
|
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Production route factories for fn-112 doc-asset + pdfjs vendor surfaces.
|
|
3
|
+
* server.ts and tests MUST consume these — no duplicated route maps.
|
|
4
|
+
*
|
|
5
|
+
* Vendor: ONE production dispatcher (`handlePdfjsVendorRequest`) is the sole
|
|
6
|
+
* path for every /vendor/pdfjs/* request (valid or malformed, any method).
|
|
7
|
+
* server.ts routes that prefix through fetch; tests call the same function.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { Config } from "../config/types";
|
|
11
|
+
import type { SqliteAdapter } from "../store/sqlite/adapter";
|
|
12
|
+
import type { ResidentRuntime } from "./resident-runtime";
|
|
13
|
+
|
|
14
|
+
import { handlePdfjsAsset, PDFJS_ASSET_CACHE_CONTROL } from "./pdfjs-assets";
|
|
15
|
+
import { handleResidentRead } from "./resident-request";
|
|
16
|
+
import { handleDocAsset } from "./routes/api";
|
|
17
|
+
|
|
18
|
+
const PDFJS_WORKER_BOOTSTRAP = `
|
|
19
|
+
if (typeof Math.sumPrecise !== "function") {
|
|
20
|
+
Object.defineProperty(Math, "sumPrecise", {
|
|
21
|
+
configurable: true,
|
|
22
|
+
writable: true,
|
|
23
|
+
value(values) {
|
|
24
|
+
let sum = 0;
|
|
25
|
+
let compensation = 0;
|
|
26
|
+
for (const value of values) {
|
|
27
|
+
const next = sum + value;
|
|
28
|
+
compensation += Math.abs(sum) >= Math.abs(value)
|
|
29
|
+
? sum - next + value
|
|
30
|
+
: value - next + sum;
|
|
31
|
+
sum = next;
|
|
32
|
+
}
|
|
33
|
+
return sum + compensation;
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
await import("/vendor/pdfjs/pdf.worker.raw.min.mjs");
|
|
38
|
+
`.trimStart();
|
|
39
|
+
|
|
40
|
+
export type SecurityHeaderWrap = (
|
|
41
|
+
response: Response,
|
|
42
|
+
isDev: boolean
|
|
43
|
+
) => Response;
|
|
44
|
+
|
|
45
|
+
export type DocAssetRouteContext = {
|
|
46
|
+
store: SqliteAdapter;
|
|
47
|
+
/** Live config getter (ctxHolder.config may change). */
|
|
48
|
+
getConfig: () => Config;
|
|
49
|
+
runtime: ResidentRuntime;
|
|
50
|
+
isDev: boolean;
|
|
51
|
+
withSecurityHeaders: SecurityHeaderWrap;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type MethodHandlers = {
|
|
55
|
+
GET: (req: Request) => Promise<Response> | Response;
|
|
56
|
+
HEAD: (req: Request) => Promise<Response> | Response;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** Exact production error envelopes for vendor routes (I1-04 round 3). */
|
|
60
|
+
export const PDFJS_VENDOR_ERRORS = {
|
|
61
|
+
NOT_FOUND: {
|
|
62
|
+
code: "NOT_FOUND",
|
|
63
|
+
message: "Asset not found",
|
|
64
|
+
},
|
|
65
|
+
METHOD_NOT_ALLOWED: {
|
|
66
|
+
code: "METHOD_NOT_ALLOWED",
|
|
67
|
+
message: "Only GET and HEAD are supported",
|
|
68
|
+
},
|
|
69
|
+
} as const;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Production `/api/doc-asset` GET+HEAD handlers.
|
|
73
|
+
* Both methods traverse handleResidentRead (admission) then withSecurityHeaders.
|
|
74
|
+
*/
|
|
75
|
+
export function createDocAssetRouteHandlers(
|
|
76
|
+
ctx: DocAssetRouteContext
|
|
77
|
+
): MethodHandlers {
|
|
78
|
+
const dispatch = async (req: Request): Promise<Response> => {
|
|
79
|
+
const url = new URL(req.url);
|
|
80
|
+
return ctx.withSecurityHeaders(
|
|
81
|
+
await handleResidentRead(ctx.runtime, req, () =>
|
|
82
|
+
handleDocAsset(ctx.store, ctx.getConfig(), url, req)
|
|
83
|
+
),
|
|
84
|
+
ctx.isDev
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
GET: dispatch,
|
|
89
|
+
HEAD: dispatch,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export type PdfjsVendorDispatchOptions = {
|
|
94
|
+
isDev: boolean;
|
|
95
|
+
withSecurityHeaders: SecurityHeaderWrap;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Production vendor dispatcher — the ONLY production path for `/vendor/pdfjs/*`.
|
|
100
|
+
*
|
|
101
|
+
* Handles every method and every pathname under the prefix (worker, cmaps,
|
|
102
|
+
* standard_fonts, multi-segment, empty, encoded traversal, invalid encoding).
|
|
103
|
+
* ALL responses — success, 404, 405 — go through withSecurityHeaders exactly once.
|
|
104
|
+
*
|
|
105
|
+
* server.ts must call this for the prefix (via fetch); tests must call this
|
|
106
|
+
* identical function (no separate test-only fallback).
|
|
107
|
+
*/
|
|
108
|
+
export async function handlePdfjsVendorRequest(
|
|
109
|
+
request: Request,
|
|
110
|
+
options: PdfjsVendorDispatchOptions
|
|
111
|
+
): Promise<Response> {
|
|
112
|
+
const wrap = options.withSecurityHeaders;
|
|
113
|
+
const isDev = options.isDev;
|
|
114
|
+
const method = request.method.toUpperCase();
|
|
115
|
+
const pathname = new URL(request.url).pathname;
|
|
116
|
+
|
|
117
|
+
// Never double-wrap: build the inner response, then wrap once.
|
|
118
|
+
const inner = await dispatchPdfjsVendorInner(method, pathname);
|
|
119
|
+
return wrap(inner, isDev);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* True when this request should be handled by the vendor dispatcher.
|
|
124
|
+
* Used by server fetch to claim the whole prefix.
|
|
125
|
+
*/
|
|
126
|
+
export function isPdfjsVendorPath(pathname: string): boolean {
|
|
127
|
+
return pathname === "/vendor/pdfjs" || pathname.startsWith("/vendor/pdfjs/");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function dispatchPdfjsVendorInner(
|
|
131
|
+
method: string,
|
|
132
|
+
pathname: string
|
|
133
|
+
): Promise<Response> {
|
|
134
|
+
// Only GET/HEAD are ever valid on any vendor path
|
|
135
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
136
|
+
return methodNotAllowedResponse();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Public worker bootstrap: install the ES2026 Math.sumPrecise compatibility
|
|
140
|
+
// required by pdfjs-dist before evaluating its worker module.
|
|
141
|
+
if (pathname === "/vendor/pdfjs/pdf.worker.min.mjs") {
|
|
142
|
+
const headers = {
|
|
143
|
+
"Cache-Control": PDFJS_ASSET_CACHE_CONTROL,
|
|
144
|
+
"Content-Type": "text/javascript",
|
|
145
|
+
"Content-Length": String(
|
|
146
|
+
new TextEncoder().encode(PDFJS_WORKER_BOOTSTRAP).byteLength
|
|
147
|
+
),
|
|
148
|
+
};
|
|
149
|
+
return new Response(method === "HEAD" ? null : PDFJS_WORKER_BOOTSTRAP, {
|
|
150
|
+
headers,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Package worker stays behind a fixed same-origin route; never referenced
|
|
155
|
+
// directly by application code, only by the bootstrap above.
|
|
156
|
+
if (pathname === "/vendor/pdfjs/pdf.worker.raw.min.mjs") {
|
|
157
|
+
return handlePdfjsAsset({ kind: "worker", method });
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// cmaps/:file — single segment only
|
|
161
|
+
const cmapMatch = pathname.match(/^\/vendor\/pdfjs\/cmaps\/([^/]*)$/u);
|
|
162
|
+
if (cmapMatch) {
|
|
163
|
+
const raw = cmapMatch[1] ?? "";
|
|
164
|
+
if (raw === "") {
|
|
165
|
+
return notFoundResponse();
|
|
166
|
+
}
|
|
167
|
+
const file = decodeRouteSegment(raw);
|
|
168
|
+
if (file === null) {
|
|
169
|
+
return notFoundResponse();
|
|
170
|
+
}
|
|
171
|
+
return handlePdfjsAsset({ kind: "cmaps", file, method });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// standard_fonts/:file — single segment only
|
|
175
|
+
const fontMatch = pathname.match(
|
|
176
|
+
/^\/vendor\/pdfjs\/standard_fonts\/([^/]*)$/u
|
|
177
|
+
);
|
|
178
|
+
if (fontMatch) {
|
|
179
|
+
const raw = fontMatch[1] ?? "";
|
|
180
|
+
if (raw === "") {
|
|
181
|
+
return notFoundResponse();
|
|
182
|
+
}
|
|
183
|
+
const file = decodeRouteSegment(raw);
|
|
184
|
+
if (file === null) {
|
|
185
|
+
return notFoundResponse();
|
|
186
|
+
}
|
|
187
|
+
return handlePdfjsAsset({ kind: "standard_fonts", file, method });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Multi-segment, empty prefix, unknown subpaths, traversal that doesn't
|
|
191
|
+
// collapse to a single-segment match → 404
|
|
192
|
+
return notFoundResponse();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function decodeRouteSegment(segment: string): string | null {
|
|
196
|
+
try {
|
|
197
|
+
return decodeURIComponent(segment);
|
|
198
|
+
} catch {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function notFoundResponse(): Response {
|
|
204
|
+
return Response.json(
|
|
205
|
+
{ error: { ...PDFJS_VENDOR_ERRORS.NOT_FOUND } },
|
|
206
|
+
{ status: 404 }
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function methodNotAllowedResponse(): Response {
|
|
211
|
+
return Response.json(
|
|
212
|
+
{ error: { ...PDFJS_VENDOR_ERRORS.METHOD_NOT_ALLOWED } },
|
|
213
|
+
{ status: 405 }
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* @deprecated Use handlePdfjsVendorRequest — kept only if any import remains.
|
|
219
|
+
* Prefer the single production dispatcher.
|
|
220
|
+
*/
|
|
221
|
+
export async function dispatchPdfjsVendorRoute(
|
|
222
|
+
_routeMap: unknown,
|
|
223
|
+
request: Request,
|
|
224
|
+
options?: PdfjsVendorDispatchOptions
|
|
225
|
+
): Promise<Response> {
|
|
226
|
+
if (!options) {
|
|
227
|
+
throw new Error(
|
|
228
|
+
"dispatchPdfjsVendorRoute requires production options; use handlePdfjsVendorRequest"
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
return handlePdfjsVendorRequest(request, options);
|
|
232
|
+
}
|