@hy-sde-org/dsh-internal-urls 0.1.1-rc.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/LICENSE +37 -0
- package/README.md +101 -0
- package/cordis.patch.yml +25 -0
- package/dist/conflict.d.ts +161 -0
- package/dist/conflict.d.ts.map +1 -0
- package/dist/conflict.js +625 -0
- package/dist/conflict.js.map +1 -0
- package/dist/gh.d.ts +63 -0
- package/dist/gh.d.ts.map +1 -0
- package/dist/gh.js +146 -0
- package/dist/gh.js.map +1 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +121 -0
- package/dist/index.js.map +1 -0
- package/dist/invariant.d.ts +16 -0
- package/dist/invariant.d.ts.map +1 -0
- package/dist/invariant.js +26 -0
- package/dist/invariant.js.map +1 -0
- package/dist/issue-pr.d.ts +96 -0
- package/dist/issue-pr.d.ts.map +1 -0
- package/dist/issue-pr.js +430 -0
- package/dist/issue-pr.js.map +1 -0
- package/dist/parse.d.ts +38 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +95 -0
- package/dist/parse.js.map +1 -0
- package/dist/router.d.ts +44 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +114 -0
- package/dist/router.js.map +1 -0
- package/dist/types.d.ts +151 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -0
- package/examples/agent-preset/agent.cordis.yml +26 -0
- package/examples/agent-preset/preset.yml +3 -0
- package/package.json +104 -0
package/dist/issue-pr.js
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub-as-filesystem: `issue://` and `pr://` protocol handlers that resolve
|
|
3
|
+
* through the `gh` CLI so read/grep can consume issues, pull requests, and PR
|
|
4
|
+
* diffs without tool sprawl. Ported in shape from oh-my-pi
|
|
5
|
+
* (`packages/coding-agent/src/internal-urls/issue-pr-protocol.ts`), MIT; the
|
|
6
|
+
* fetching layer is injectable for tests.
|
|
7
|
+
*
|
|
8
|
+
* URL shapes:
|
|
9
|
+
* - `issue://` / `pr://` — list recent items in the caller's default repo.
|
|
10
|
+
* - `issue://owner/repo` / `pr://owner/repo` — list a specific repo.
|
|
11
|
+
* - `issue://123` / `pr://123` — single item; repo derived from the session cwd.
|
|
12
|
+
* - `issue://owner/repo/123` / `pr://owner/repo/123` — fully qualified.
|
|
13
|
+
* - `issue://…?comments=0` — suppress comments.
|
|
14
|
+
* - `issue://owner/repo?state=closed&limit=20` — list options pass through to `gh`.
|
|
15
|
+
* - `pr://N/diff` — changed-file listing; `pr://N/diff/all` — full unified diff;
|
|
16
|
+
* `pr://N/diff/<i>` — one file's diff chunk (and the repo-qualified variants).
|
|
17
|
+
* @module @hy-sde-org/dsh-internal-urls/issue-pr
|
|
18
|
+
*/
|
|
19
|
+
import { ghJson, ghOutput } from "./gh.js";
|
|
20
|
+
/** The concrete `gh` runner bound to a plugin context. */
|
|
21
|
+
export function gitHubCliOf(ctx) {
|
|
22
|
+
return {
|
|
23
|
+
json: (cwd, args, signal) => ghJson(ctx, cwd, args, signal),
|
|
24
|
+
output: (cwd, args, signal) => ghOutput(ctx, cwd, args, signal),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const LIST_LIMIT_DEFAULT = 30;
|
|
28
|
+
const LIST_LIMIT_MAX = 100;
|
|
29
|
+
function parsePositiveDecimalInt(value) {
|
|
30
|
+
if (!/^\d+$/.test(value))
|
|
31
|
+
return undefined;
|
|
32
|
+
const parsed = Number.parseInt(value, 10);
|
|
33
|
+
return Number.isFinite(parsed) && parsed >= 1 ? parsed : undefined;
|
|
34
|
+
}
|
|
35
|
+
function parseListOptions(url, scheme, repo) {
|
|
36
|
+
const stateRaw = url.searchParams.get('state');
|
|
37
|
+
const allowedStates = scheme === 'pr' ? ['open', 'closed', 'merged', 'all'] : ['open', 'closed', 'all'];
|
|
38
|
+
if (stateRaw !== null && !allowedStates.includes(stateRaw)) {
|
|
39
|
+
throw new Error(`Invalid ${scheme}:// list state '${stateRaw}'. Expected one of: ${allowedStates.join(', ')}.`);
|
|
40
|
+
}
|
|
41
|
+
const state = stateRaw ?? 'open';
|
|
42
|
+
let limit = LIST_LIMIT_DEFAULT;
|
|
43
|
+
const limitRaw = url.searchParams.get('limit');
|
|
44
|
+
if (limitRaw !== null) {
|
|
45
|
+
const parsed = parsePositiveDecimalInt(limitRaw);
|
|
46
|
+
if (parsed === undefined) {
|
|
47
|
+
throw new Error(`Invalid ${scheme}:// list limit '${limitRaw}'. Expected a positive integer (max ${LIST_LIMIT_MAX}).`);
|
|
48
|
+
}
|
|
49
|
+
limit = Math.min(parsed, LIST_LIMIT_MAX);
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
kind: 'list',
|
|
53
|
+
...repo !== undefined ? { repo } : {},
|
|
54
|
+
state,
|
|
55
|
+
limit,
|
|
56
|
+
author: url.searchParams.get('author') ?? undefined,
|
|
57
|
+
label: url.searchParams.get('label') ?? undefined,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Parse an `issue://`/`pr://` URL into the union shape. Pure and exported for tests.
|
|
62
|
+
*/
|
|
63
|
+
export function parseIssuePrUrl(url, scheme) {
|
|
64
|
+
const host = url.rawHost;
|
|
65
|
+
const parts = url.pathSegments;
|
|
66
|
+
if (!host && parts.length === 0) {
|
|
67
|
+
return parseListOptions(url, scheme, undefined);
|
|
68
|
+
}
|
|
69
|
+
if (host && parts.length === 0) {
|
|
70
|
+
// scheme://N (numeric) — a bare host is the item number.
|
|
71
|
+
const num = parsePositiveDecimalInt(host);
|
|
72
|
+
if (num === undefined) {
|
|
73
|
+
throw new Error(`Invalid ${scheme}:// number: ${host}`);
|
|
74
|
+
}
|
|
75
|
+
return { kind: 'single', number: num, comments: commentsOn(url) };
|
|
76
|
+
}
|
|
77
|
+
if (scheme === 'pr' && parts.length >= 1 && parts[0] === 'diff') {
|
|
78
|
+
// pr://N/diff[/<sub>] — the number lives in the host, the suffix in parts.
|
|
79
|
+
const num = parsePositiveDecimalInt(host);
|
|
80
|
+
if (num === undefined) {
|
|
81
|
+
throw new Error(`Invalid pr:// number: ${host}`);
|
|
82
|
+
}
|
|
83
|
+
return parsePrDiff(parts[1], { kind: 'pr-diff', number: num, mode: 'list' });
|
|
84
|
+
}
|
|
85
|
+
if (scheme === 'issue' && host && /^\d+$/.test(host)) {
|
|
86
|
+
throw new Error('Invalid issue:// URL. Issue views do not have a diff; use pr://<owner>/<repo>/<n>/diff for pull requests.');
|
|
87
|
+
}
|
|
88
|
+
if (host && parts.length === 1) {
|
|
89
|
+
// scheme://owner/repo → list
|
|
90
|
+
return parseListOptions(url, scheme, `${host}/${parts[0]}`);
|
|
91
|
+
}
|
|
92
|
+
if (host && parts.length >= 2) {
|
|
93
|
+
// scheme://owner/repo/N[/diff[/<sub>]]
|
|
94
|
+
const repo = `${host}/${parts[0]}`;
|
|
95
|
+
const numberPart = parts[1];
|
|
96
|
+
const num = parsePositiveDecimalInt(numberPart ?? '');
|
|
97
|
+
if (num === undefined) {
|
|
98
|
+
throw new Error(`Invalid ${scheme}:// number: ${numberPart ?? '(missing)'}`);
|
|
99
|
+
}
|
|
100
|
+
const diffParts = parts.slice(2);
|
|
101
|
+
if (diffParts.length > 0) {
|
|
102
|
+
if (scheme === 'issue') {
|
|
103
|
+
throw new Error('Invalid issue:// URL. Issue views do not have a diff; use pr://<owner>/<repo>/<n>/diff for pull requests.');
|
|
104
|
+
}
|
|
105
|
+
if (diffParts[0] !== 'diff' || diffParts.length > 2) {
|
|
106
|
+
throw new Error('Invalid pr:// URL. Expected pr://<n>/diff, pr://<n>/diff/all, or pr://<n>/diff/<i>');
|
|
107
|
+
}
|
|
108
|
+
return parsePrDiff(diffParts[1], { kind: 'pr-diff', repo, number: num, mode: 'list' });
|
|
109
|
+
}
|
|
110
|
+
return { kind: 'single', repo, number: num, comments: commentsOn(url) };
|
|
111
|
+
}
|
|
112
|
+
throw new Error(`Invalid ${scheme}:// URL. Expected ${scheme}://, ${scheme}://<number>, ${scheme}://<owner>/<repo>, or ${scheme}://<owner>/<repo>/<number>`);
|
|
113
|
+
}
|
|
114
|
+
/** Split the diff sub-path and range-check the index/slice. */
|
|
115
|
+
function parsePrDiff(sub, base) {
|
|
116
|
+
if (sub === undefined || sub === 'all')
|
|
117
|
+
return { ...base, mode: sub === undefined ? 'list' : 'all' };
|
|
118
|
+
const idx = parsePositiveDecimalInt(sub);
|
|
119
|
+
if (idx === undefined) {
|
|
120
|
+
throw new Error(`Invalid pr:// diff sub-path '${sub}'. Use 'all' or a 1-indexed file number.`);
|
|
121
|
+
}
|
|
122
|
+
return { ...base, mode: 'slice', index: idx };
|
|
123
|
+
}
|
|
124
|
+
function commentsOn(url) {
|
|
125
|
+
const raw = url.searchParams.get('comments');
|
|
126
|
+
return raw === null || !(raw === '0' || raw.toLowerCase() === 'false');
|
|
127
|
+
}
|
|
128
|
+
function formatListItem(scheme, repo, item) {
|
|
129
|
+
const number = item.number ?? '?';
|
|
130
|
+
const title = item.title ?? '(no title)';
|
|
131
|
+
const state = item.state?.toLowerCase() ?? '?';
|
|
132
|
+
const author = item.author?.login ?? '?';
|
|
133
|
+
const updated = item.updatedAt ?? item.createdAt ?? '';
|
|
134
|
+
const draftSuffix = scheme === 'pr' && item.isDraft ? ' [draft]' : '';
|
|
135
|
+
const labels = (item.labels ?? []).map(l => l.name).filter(Boolean).join(', ');
|
|
136
|
+
const labelSuffix = labels ? ` labels: ${labels}` : '';
|
|
137
|
+
const itemUrl = number === '?' ? `${scheme}://${repo}` : `${scheme}://${repo}/${number}`;
|
|
138
|
+
return `- [${state}${draftSuffix}] #${number} @${author} ${updated}\n ${title}${labelSuffix}\n ${itemUrl}`;
|
|
139
|
+
}
|
|
140
|
+
/** Render one issue or PR body (title + metadata + body + comment thread) to markdown. */
|
|
141
|
+
function renderSingle(scheme, repo, item) {
|
|
142
|
+
const out = [];
|
|
143
|
+
const noun = scheme === 'issue' ? 'Issue' : 'Pull Request';
|
|
144
|
+
const number = item.number ?? '?';
|
|
145
|
+
out.push(`# ${noun} #${number}: ${item.title ?? '(no title)'}`);
|
|
146
|
+
const bits = [];
|
|
147
|
+
if (item.state)
|
|
148
|
+
bits.push(item.state);
|
|
149
|
+
if (item.isDraft)
|
|
150
|
+
bits.push('draft');
|
|
151
|
+
if (item.author?.login)
|
|
152
|
+
bits.push(`by @${item.author.login}`);
|
|
153
|
+
if (item.updatedAt)
|
|
154
|
+
bits.push(`updated ${item.updatedAt}`);
|
|
155
|
+
if (scheme === 'pr' && item.baseRefName && item.headRefName)
|
|
156
|
+
bits.push(`${item.baseRefName} ← ${item.headRefName}`);
|
|
157
|
+
out.push(`**${bits.join(' · ')}**`);
|
|
158
|
+
out.push('');
|
|
159
|
+
out.push((item.body ?? '').trim() || '_No description._');
|
|
160
|
+
out.push('');
|
|
161
|
+
out.push(`---\nRepo: \`${repo}\` · Item URL: \`${scheme}://${repo}/${number}\``);
|
|
162
|
+
if (scheme === 'pr')
|
|
163
|
+
out.push(`Diff: \`pr://${repo}/${number}/diff\``);
|
|
164
|
+
const comments = item.comments ?? [];
|
|
165
|
+
if (comments.length > 0) {
|
|
166
|
+
out.push('', '## Comments');
|
|
167
|
+
for (const comment of comments) {
|
|
168
|
+
out.push('', `**@${comment.author?.login ?? 'unknown'}**:`, (comment.body ?? '').trim());
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return out.join('\n') + '\n';
|
|
172
|
+
}
|
|
173
|
+
/** The `# Pull Requests in …` listing header and its body/footer. */
|
|
174
|
+
function renderList(scheme, repo, options, items) {
|
|
175
|
+
const header = scheme === 'issue'
|
|
176
|
+
? `# Issues in ${repo} (${options.state}, up to ${options.limit})`
|
|
177
|
+
: `# Pull Requests in ${repo} (${options.state}, up to ${options.limit})`;
|
|
178
|
+
const body = items.length === 0
|
|
179
|
+
? '_No matches._'
|
|
180
|
+
: items.map(item => formatListItem(scheme, repo, item)).join('\n\n');
|
|
181
|
+
const footer = `\n\n---\nRead a specific item: \`${scheme}://${repo}/<N>\` (or \`${scheme}://<N>\` for the current repo).`;
|
|
182
|
+
return `${header}\n\n${body}${footer}`;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Split a complete `gh pr diff` unified text into per-file chunks by scanning
|
|
186
|
+
* `diff --git a/… b/…` headers. Additions/deletions are counted from the hunk
|
|
187
|
+
* body lines themselves (`+`/`-` prefixes inside hunks only), so the scan is
|
|
188
|
+
* single-pass and precise. Pure and exported for tests.
|
|
189
|
+
*/
|
|
190
|
+
export function splitPrDiff(unified) {
|
|
191
|
+
const lines = unified.split('\n');
|
|
192
|
+
const files = [];
|
|
193
|
+
let current = null;
|
|
194
|
+
const flush = (endLine) => {
|
|
195
|
+
if (current === null)
|
|
196
|
+
return;
|
|
197
|
+
files.push({
|
|
198
|
+
path: current.path,
|
|
199
|
+
...current.oldPath !== undefined ? { oldPath: current.oldPath } : {},
|
|
200
|
+
additions: current.additions,
|
|
201
|
+
deletions: current.deletions,
|
|
202
|
+
changeType: current.changeType,
|
|
203
|
+
// The slice [startOffset, endOffset) covers this file's header + hunks.
|
|
204
|
+
startOffset: current.start,
|
|
205
|
+
endOffset: endLine,
|
|
206
|
+
});
|
|
207
|
+
current = null;
|
|
208
|
+
};
|
|
209
|
+
for (let i = 0; i < lines.length; i++) {
|
|
210
|
+
const line = lines[i] ?? '';
|
|
211
|
+
if (line.startsWith('diff --git ')) {
|
|
212
|
+
flush(i);
|
|
213
|
+
const match = /^diff --git a\/(.*?) b\/(.*)$/.exec(line);
|
|
214
|
+
const newPath = (match?.[2] ?? '').split('\t')[0] ?? '';
|
|
215
|
+
const oldPath = (match?.[1] ?? '').split('\t')[0] ?? '';
|
|
216
|
+
current = {
|
|
217
|
+
path: newPath || oldPath,
|
|
218
|
+
...oldPath !== '' && oldPath !== newPath ? { oldPath } : {},
|
|
219
|
+
start: i,
|
|
220
|
+
additions: 0,
|
|
221
|
+
deletions: 0,
|
|
222
|
+
changeType: 'modified',
|
|
223
|
+
inHunk: false,
|
|
224
|
+
};
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (current === null)
|
|
228
|
+
continue;
|
|
229
|
+
if (line.startsWith('new file mode '))
|
|
230
|
+
current.changeType = 'added';
|
|
231
|
+
else if (line.startsWith('deleted file mode '))
|
|
232
|
+
current.changeType = 'deleted';
|
|
233
|
+
else if (line.startsWith('rename from '))
|
|
234
|
+
current.changeType = 'renamed';
|
|
235
|
+
else if (line.startsWith('similarity index '))
|
|
236
|
+
current.changeType = 'renamed';
|
|
237
|
+
else if (line.startsWith('Binary files '))
|
|
238
|
+
current.changeType = 'binary';
|
|
239
|
+
const hunk = /^@@ -(?:\d+)(?:,\d+)? \+(?:\d+)(?:,\d+)? @@/.exec(line);
|
|
240
|
+
if (hunk !== null) {
|
|
241
|
+
current.inHunk = true;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (current.inHunk && line.startsWith('+'))
|
|
245
|
+
current.additions += 1;
|
|
246
|
+
else if (current.inHunk && line.startsWith('-'))
|
|
247
|
+
current.deletions += 1;
|
|
248
|
+
}
|
|
249
|
+
flush(lines.length);
|
|
250
|
+
return files;
|
|
251
|
+
}
|
|
252
|
+
function cwdOf(context) {
|
|
253
|
+
return context?.cwd;
|
|
254
|
+
}
|
|
255
|
+
/** Resolve the caller's default repo or throw with a short-form hint. */
|
|
256
|
+
async function resolveRepo(deps, scheme, parsedRepo, context) {
|
|
257
|
+
if (parsedRepo)
|
|
258
|
+
return parsedRepo;
|
|
259
|
+
const cwd = cwdOf(context);
|
|
260
|
+
if (cwd === undefined) {
|
|
261
|
+
throw new Error(`${scheme}:// needs a repo. Use ${scheme}://<owner>/<repo> — the calling session has no working directory to derive one from.`);
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
const repo = await deps.defaultRepo(cwd, context?.signal);
|
|
265
|
+
if (repo !== undefined)
|
|
266
|
+
return repo;
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
// fall through to the friendly error
|
|
270
|
+
}
|
|
271
|
+
throw new Error(`${scheme}:// could not resolve a default repo from the current session. Use ${scheme}://<owner>/<repo> instead.`);
|
|
272
|
+
}
|
|
273
|
+
async function fetchAndRenderList(deps, scheme, options, url, context) {
|
|
274
|
+
const repo = await resolveRepo(deps, scheme, options.repo, context);
|
|
275
|
+
const cwd = cwdOf(context);
|
|
276
|
+
if (cwd === undefined)
|
|
277
|
+
throw new Error(`${scheme}:// listing needs a working directory`);
|
|
278
|
+
const fields = scheme === 'issue'
|
|
279
|
+
? 'number,title,state,author,labels,createdAt,updatedAt'
|
|
280
|
+
: 'number,title,state,isDraft,author,labels,createdAt,updatedAt';
|
|
281
|
+
const args = [
|
|
282
|
+
scheme,
|
|
283
|
+
'list',
|
|
284
|
+
'--repo', repo,
|
|
285
|
+
'--state', options.state,
|
|
286
|
+
'--limit', String(options.limit),
|
|
287
|
+
'--json', fields,
|
|
288
|
+
];
|
|
289
|
+
if (options.author)
|
|
290
|
+
args.push('--author', options.author);
|
|
291
|
+
if (options.label)
|
|
292
|
+
args.push('--label', options.label);
|
|
293
|
+
const items = await deps.cli.json(cwd, args, context?.signal);
|
|
294
|
+
const rendered = renderList(scheme, repo, options, items);
|
|
295
|
+
return {
|
|
296
|
+
url: url.href,
|
|
297
|
+
content: rendered,
|
|
298
|
+
contentType: 'text/markdown',
|
|
299
|
+
size: Buffer.byteLength(rendered, 'utf-8'),
|
|
300
|
+
notes: [`Live \`gh ${scheme} list\` for ${repo}`],
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
async function fetchAndRenderSingle(deps, scheme, parsed, url, context) {
|
|
304
|
+
const repo = await resolveRepo(deps, scheme, parsed.repo, context);
|
|
305
|
+
const cwd = cwdOf(context);
|
|
306
|
+
if (cwd === undefined)
|
|
307
|
+
throw new Error(`${scheme}:// needs a working directory`);
|
|
308
|
+
// `gh view` renders comments by default; the `--json comments` field is what
|
|
309
|
+
// brings them into the payload, so disabling comments just drops the field.
|
|
310
|
+
const baseFields = 'number,title,state,isDraft,author,body,baseRefName,headRefName,createdAt,updatedAt';
|
|
311
|
+
const fields = parsed.comments ? `${baseFields},comments` : baseFields;
|
|
312
|
+
const args = [scheme, 'view', String(parsed.number), '--repo', repo, '--json', fields];
|
|
313
|
+
const item = await deps.cli.json(cwd, args, context?.signal);
|
|
314
|
+
// `gh view` renders comments by default; the `--json comments` field is what
|
|
315
|
+
// brings them into the payload, so disabling comments drops the field and
|
|
316
|
+
// strips whatever the payload carried anyway.
|
|
317
|
+
const { comments: _omitted, ...body } = item;
|
|
318
|
+
const rendered = renderSingle(scheme, parsed.repo ?? repo, parsed.comments ? { ...item } : { ...body });
|
|
319
|
+
const notes = [];
|
|
320
|
+
if (!parsed.comments)
|
|
321
|
+
notes.push('Comments disabled');
|
|
322
|
+
if (scheme === 'pr') {
|
|
323
|
+
const repoSegment = parsed.repo ?? repo;
|
|
324
|
+
notes.push(`Diff: pr://${repoSegment}/${parsed.number}/diff`);
|
|
325
|
+
}
|
|
326
|
+
return {
|
|
327
|
+
url: url.href,
|
|
328
|
+
content: rendered,
|
|
329
|
+
contentType: 'text/markdown',
|
|
330
|
+
size: Buffer.byteLength(rendered, 'utf-8'),
|
|
331
|
+
notes,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
async function fetchAndRenderPrDiff(deps, parsed, url, context) {
|
|
335
|
+
const repo = await resolveRepo(deps, 'pr', parsed.repo, context);
|
|
336
|
+
const cwd = cwdOf(context);
|
|
337
|
+
if (cwd === undefined)
|
|
338
|
+
throw new Error('pr:// diff needs a working directory');
|
|
339
|
+
const unified = await deps.cli.output(cwd, ['pr', 'diff', String(parsed.number), '--repo', repo], context?.signal);
|
|
340
|
+
const files = splitPrDiff(unified);
|
|
341
|
+
if (parsed.mode === 'all') {
|
|
342
|
+
return {
|
|
343
|
+
url: url.href,
|
|
344
|
+
content: unified,
|
|
345
|
+
contentType: 'text/plain',
|
|
346
|
+
size: Buffer.byteLength(unified, 'utf-8'),
|
|
347
|
+
notes: [`Full diff for pr://${repo}/${parsed.number} (${files.length} file${files.length === 1 ? '' : 's'})`],
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
if (parsed.mode === 'slice') {
|
|
351
|
+
const index = parsed.index ?? 1;
|
|
352
|
+
if (index < 1 || index > files.length) {
|
|
353
|
+
throw new Error(`pr://${repo}/${parsed.number}/diff/${index} is out of range; PR has ${files.length} file${files.length === 1 ? '' : 's'}. Use pr://${repo}/${parsed.number}/diff to list available indices.`);
|
|
354
|
+
}
|
|
355
|
+
const file = files[index - 1];
|
|
356
|
+
if (!file)
|
|
357
|
+
throw new Error(`pr://${repo}/${parsed.number}/diff/${index} resolved to a missing slice (parser bug).`);
|
|
358
|
+
// startOffset/endOffset are line indices over the unified text.
|
|
359
|
+
const content = unified.split('\n').slice(file.startOffset, file.endOffset).join('\n');
|
|
360
|
+
return {
|
|
361
|
+
url: url.href,
|
|
362
|
+
content,
|
|
363
|
+
contentType: 'text/plain',
|
|
364
|
+
size: Buffer.byteLength(content, 'utf-8'),
|
|
365
|
+
notes: [`Showing file ${index}/${files.length}: ${file.path}`, `Read all: pr://${repo}/${parsed.number}/diff/all`],
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
// mode === 'list'
|
|
369
|
+
const header = `# Pull Request Diff: ${repo}#${parsed.number} (${files.length} file${files.length === 1 ? '' : 's'})`;
|
|
370
|
+
const body = files.length === 0
|
|
371
|
+
? '_No file changes._'
|
|
372
|
+
: files.map((file, i) => {
|
|
373
|
+
const stats = file.changeType === 'binary' ? '(binary)' : `+${file.additions} -${file.deletions}`;
|
|
374
|
+
const rename = file.oldPath ? ` (renamed from ${file.oldPath})` : '';
|
|
375
|
+
return `${i + 1}. ${file.path} ${stats} [${file.changeType}]${rename}\n pr://${repo}/${parsed.number}/diff/${i + 1}`;
|
|
376
|
+
}).join('\n\n');
|
|
377
|
+
const footer = `\n\n---\nRead all: \`pr://${repo}/${parsed.number}/diff/all\`. Each file is also available as \`pr://${repo}/${parsed.number}/diff/<i>\`.`;
|
|
378
|
+
const content = `${header}\n\n${body}${footer}`;
|
|
379
|
+
return {
|
|
380
|
+
url: url.href,
|
|
381
|
+
content,
|
|
382
|
+
contentType: 'text/markdown',
|
|
383
|
+
size: Buffer.byteLength(content, 'utf-8'),
|
|
384
|
+
notes: [`File listing for pr://${repo}/${parsed.number}`],
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
/** Shared resolve: parse the URL, branch list / pr-diff / single. */
|
|
388
|
+
async function handleResolve(deps, scheme, url, context) {
|
|
389
|
+
if (context?.signal?.aborted)
|
|
390
|
+
throw new Error('aborted');
|
|
391
|
+
const parsed = parseIssuePrUrl(url, scheme);
|
|
392
|
+
try {
|
|
393
|
+
if (parsed.kind === 'list')
|
|
394
|
+
return await fetchAndRenderList(deps, scheme, parsed, url, context);
|
|
395
|
+
if (parsed.kind === 'pr-diff')
|
|
396
|
+
return await fetchAndRenderPrDiff(deps, parsed, url, context);
|
|
397
|
+
return await fetchAndRenderSingle(deps, scheme, parsed, url, context);
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
if (error instanceof Error && error.message.startsWith('Invalid'))
|
|
401
|
+
throw error;
|
|
402
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
403
|
+
throw new Error(`${scheme}:// resolution failed: ${message}`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/** Handler for `issue://` URLs. Immutable — issues are read-only surfaces. */
|
|
407
|
+
export class IssueProtocolHandler {
|
|
408
|
+
deps;
|
|
409
|
+
scheme = 'issue';
|
|
410
|
+
immutable = true;
|
|
411
|
+
constructor(deps) {
|
|
412
|
+
this.deps = deps;
|
|
413
|
+
}
|
|
414
|
+
resolve(url, context) {
|
|
415
|
+
return handleResolve(this.deps, 'issue', url, context);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
/** Handler for `pr://` URLs, including the diff family. Immutable. */
|
|
419
|
+
export class PrProtocolHandler {
|
|
420
|
+
deps;
|
|
421
|
+
scheme = 'pr';
|
|
422
|
+
immutable = true;
|
|
423
|
+
constructor(deps) {
|
|
424
|
+
this.deps = deps;
|
|
425
|
+
}
|
|
426
|
+
resolve(url, context) {
|
|
427
|
+
return handleResolve(this.deps, 'pr', url, context);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
//# sourceMappingURL=issue-pr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issue-pr.js","sourceRoot":"","sources":["../src/issue-pr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAW1C,0DAA0D;AAC1D,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,OAAO;QACL,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC;QAC3D,MAAM,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC;KAChE,CAAA;AACH,CAAC;AA+BD,MAAM,kBAAkB,GAAG,EAAE,CAAA;AAC7B,MAAM,cAAc,GAAG,GAAG,CAAA;AAE1B,SAAS,uBAAuB,CAAC,KAAa;IAC5C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAA;IAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IACzC,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAA;AACpE,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAsB,EAAE,MAAc,EAAE,IAAwB;IACxF,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAC9C,MAAM,aAAa,GAAa,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAA;IACjH,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,WAAW,MAAM,mBAAmB,QAAQ,uBAAuB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACjH,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ,IAAI,MAAM,CAAA;IAEhC,IAAI,KAAK,GAAG,kBAAkB,CAAA;IAC9B,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAC9C,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,MAAM,MAAM,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAA;QAChD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,WAAW,MAAM,mBAAmB,QAAQ,uCAAuC,cAAc,IAAI,CAAC,CAAA;QACxH,CAAC;QACD,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;IAC1C,CAAC;IACD,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE;QACrC,KAAK;QACL,KAAK;QACL,MAAM,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,SAAS;QACnD,KAAK,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,SAAS;KAClD,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,GAAsB,EAAE,MAAc;IACpE,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAA;IACxB,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAA;IAE9B,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,CAAA;IACjD,CAAC;IACD,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,yDAAyD;QACzD,MAAM,GAAG,GAAG,uBAAuB,CAAC,IAAI,CAAC,CAAA;QACzC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,WAAW,MAAM,eAAe,IAAI,EAAE,CAAC,CAAA;QACzD,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,UAAU,CAAC,GAAG,CAAC,EAAE,CAAA;IACnE,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;QAChE,2EAA2E;QAC3E,MAAM,GAAG,GAAG,uBAAuB,CAAC,IAAI,CAAC,CAAA;QACzC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAA;QAClD,CAAC;QACD,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAA;IAC9E,CAAC;IACD,IAAI,MAAM,KAAK,OAAO,IAAI,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,2GAA2G,CAAC,CAAA;IAC9H,CAAC;IACD,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,6BAA6B;QAC7B,OAAO,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAC7D,CAAC;IACD,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QAC9B,uCAAuC;QACvC,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;QAClC,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QAC3B,MAAM,GAAG,GAAG,uBAAuB,CAAC,UAAU,IAAI,EAAE,CAAC,CAAA;QACrD,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,WAAW,MAAM,eAAe,UAAU,IAAI,WAAW,EAAE,CAAC,CAAA;QAC9E,CAAC;QACD,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QAChC,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CAAC,2GAA2G,CAAC,CAAA;YAC9H,CAAC;YACD,IAAI,SAAS,CAAC,CAAC,CAAC,KAAK,MAAM,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpD,MAAM,IAAI,KAAK,CAAC,oFAAoF,CAAC,CAAA;YACvG,CAAC;YACD,OAAO,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAA;QACxF,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,UAAU,CAAC,GAAG,CAAC,EAAE,CAAA;IACzE,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,WAAW,MAAM,qBAAqB,MAAM,QAAQ,MAAM,gBAAgB,MAAM,yBAAyB,MAAM,4BAA4B,CAAC,CAAA;AAC9J,CAAC;AAED,+DAA+D;AAC/D,SAAS,WAAW,CAAC,GAAuB,EAAE,IAAkB;IAC9D,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,KAAK;QAAE,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,CAAA;IACpG,MAAM,GAAG,GAAG,uBAAuB,CAAC,GAAG,CAAC,CAAA;IACxC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,gCAAgC,GAAG,0CAA0C,CAAC,CAAA;IAChG,CAAC;IACD,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAA;AAC/C,CAAC;AAED,SAAS,UAAU,CAAC,GAAsB;IACxC,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;IAC5C,OAAO,GAAG,KAAK,IAAI,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC,CAAA;AACxE,CAAC;AAaD,SAAS,cAAc,CAAC,MAAc,EAAE,IAAY,EAAE,IAAc;IAClE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,GAAG,CAAA;IACjC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,YAAY,CAAA;IACxC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,GAAG,CAAA;IAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,GAAG,CAAA;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,IAAI,EAAE,CAAA;IACtD,MAAM,WAAW,GAAG,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAA;IACrE,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC9E,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,aAAa,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IACvD,MAAM,OAAO,GAAG,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,MAAM,IAAI,IAAI,MAAM,EAAE,CAAA;IACxF,OAAO,MAAM,KAAK,GAAG,WAAW,MAAM,MAAM,MAAM,MAAM,KAAK,OAAO,SAAS,KAAK,GAAG,WAAW,SAAS,OAAO,EAAE,CAAA;AACpH,CAAC;AAED,0FAA0F;AAC1F,SAAS,YAAY,CAAC,MAAc,EAAE,IAAY,EAAE,IAYnD;IACC,MAAM,GAAG,GAAa,EAAE,CAAA;IACxB,MAAM,IAAI,GAAG,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAA;IAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,GAAG,CAAA;IACjC,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,KAAK,MAAM,KAAK,IAAI,CAAC,KAAK,IAAI,YAAY,EAAE,CAAC,CAAA;IAC/D,MAAM,IAAI,GAAa,EAAE,CAAA;IACzB,IAAI,IAAI,CAAC,KAAK;QAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACrC,IAAI,IAAI,CAAC,OAAO;QAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACpC,IAAI,IAAI,CAAC,MAAM,EAAE,KAAK;QAAE,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAA;IAC7D,IAAI,IAAI,CAAC,SAAS;QAAE,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;IAC1D,IAAI,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW;QAAE,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,WAAW,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC,CAAA;IACnH,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACnC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACZ,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,mBAAmB,CAAC,CAAA;IACzD,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACZ,GAAG,CAAC,IAAI,CAAC,gBAAgB,IAAI,sBAAsB,MAAM,MAAM,IAAI,IAAI,MAAM,IAAI,CAAC,CAAA;IAClF,IAAI,MAAM,KAAK,IAAI;QAAE,GAAG,CAAC,IAAI,CAAC,gBAAgB,IAAI,IAAI,MAAM,SAAS,CAAC,CAAA;IACtE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAA;IACpC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,CAAA;QAC3B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,SAAS,KAAK,EAAE,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;QAC1F,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;AAC9B,CAAC;AAED,qEAAqE;AACrE,SAAS,UAAU,CAAC,MAAc,EAAE,IAAY,EAAE,OAAmB,EAAE,KAAiB;IACtF,MAAM,MAAM,GAAG,MAAM,KAAK,OAAO;QAC/B,CAAC,CAAC,eAAe,IAAI,KAAK,OAAO,CAAC,KAAK,WAAW,OAAO,CAAC,KAAK,GAAG;QAClE,CAAC,CAAC,sBAAsB,IAAI,KAAK,OAAO,CAAC,KAAK,WAAW,OAAO,CAAC,KAAK,GAAG,CAAA;IAC3E,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC;QAC7B,CAAC,CAAC,eAAe;QACjB,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACtE,MAAM,MAAM,GAAG,oCAAoC,MAAM,MAAM,IAAI,gBAAgB,MAAM,iCAAiC,CAAA;IAC1H,OAAO,GAAG,MAAM,OAAO,IAAI,GAAG,MAAM,EAAE,CAAA;AACxC,CAAC;AAaD;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACjC,MAAM,KAAK,GAAiB,EAAE,CAAA;IAC9B,IAAI,OAAO,GAQA,IAAI,CAAA;IAEf,MAAM,KAAK,GAAG,CAAC,OAAe,EAAQ,EAAE;QACtC,IAAI,OAAO,KAAK,IAAI;YAAE,OAAM;QAC5B,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,GAAG,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE;YACpE,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,wEAAwE;YACxE,WAAW,EAAE,OAAO,CAAC,KAAK;YAC1B,SAAS,EAAE,OAAO;SACnB,CAAC,CAAA;QACF,OAAO,GAAG,IAAI,CAAA;IAChB,CAAC,CAAA;IAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QAC3B,IAAI,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YACnC,KAAK,CAAC,CAAC,CAAC,CAAA;YACR,MAAM,KAAK,GAAG,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACxD,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;YACvD,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;YACvD,OAAO,GAAG;gBACR,IAAI,EAAE,OAAO,IAAI,OAAO;gBACxB,GAAG,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE;gBAC3D,KAAK,EAAE,CAAC;gBACR,SAAS,EAAE,CAAC;gBACZ,SAAS,EAAE,CAAC;gBACZ,UAAU,EAAE,UAAU;gBACtB,MAAM,EAAE,KAAK;aACd,CAAA;YACD,SAAQ;QACV,CAAC;QACD,IAAI,OAAO,KAAK,IAAI;YAAE,SAAQ;QAC9B,IAAI,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC;YAAE,OAAO,CAAC,UAAU,GAAG,OAAO,CAAA;aAC9D,IAAI,IAAI,CAAC,UAAU,CAAC,oBAAoB,CAAC;YAAE,OAAO,CAAC,UAAU,GAAG,SAAS,CAAA;aACzE,IAAI,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC;YAAE,OAAO,CAAC,UAAU,GAAG,SAAS,CAAA;aACnE,IAAI,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC;YAAE,OAAO,CAAC,UAAU,GAAG,SAAS,CAAA;aACxE,IAAI,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC;YAAE,OAAO,CAAC,UAAU,GAAG,QAAQ,CAAA;QACxE,MAAM,IAAI,GAAG,6CAA6C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACrE,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAA;YACrB,SAAQ;QACV,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,SAAS,IAAI,CAAC,CAAA;aAC7D,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,SAAS,IAAI,CAAC,CAAA;IACzE,CAAC;IACD,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IACnB,OAAO,KAAK,CAAA;AACd,CAAC;AAOD,SAAS,KAAK,CAAC,OAAmC;IAChD,OAAO,OAAO,EAAE,GAAG,CAAA;AACrB,CAAC;AAED,yEAAyE;AACzE,KAAK,UAAU,WAAW,CACxB,IAAiB,EACjB,MAAc,EACd,UAA8B,EAC9B,OAAmC;IAEnC,IAAI,UAAU;QAAE,OAAO,UAAU,CAAA;IACjC,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,CAAA;IAC1B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,yBAAyB,MAAM,sFAAsF,CAAC,CAAA;IACjJ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;QACzD,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,IAAI,CAAA;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,qCAAqC;IACvC,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,sEAAsE,MAAM,4BAA4B,CAAC,CAAA;AACpI,CAAC;AAED,KAAK,UAAU,kBAAkB,CAC/B,IAAiB,EACjB,MAAc,EACd,OAAmB,EACnB,GAAsB,EACtB,OAAmC;IAEnC,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACnE,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,CAAA;IAC1B,IAAI,GAAG,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,uCAAuC,CAAC,CAAA;IACxF,MAAM,MAAM,GAAG,MAAM,KAAK,OAAO;QAC/B,CAAC,CAAC,sDAAsD;QACxD,CAAC,CAAC,8DAA8D,CAAA;IAClE,MAAM,IAAI,GAAG;QACX,MAAM;QACN,MAAM;QACN,QAAQ,EAAE,IAAI;QACd,SAAS,EAAE,OAAO,CAAC,KAAK;QACxB,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;QAChC,QAAQ,EAAE,MAAM;KACjB,CAAA;IACD,IAAI,OAAO,CAAC,MAAM;QAAE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IACzD,IAAI,OAAO,CAAC,KAAK;QAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;IACtD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAe,CAAA;IAC3E,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAA;IACzD,OAAO;QACL,GAAG,EAAE,GAAG,CAAC,IAAI;QACb,OAAO,EAAE,QAAQ;QACjB,WAAW,EAAE,eAAe;QAC5B,IAAI,EAAE,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC;QAC1C,KAAK,EAAE,CAAC,aAAa,MAAM,eAAe,IAAI,EAAE,CAAC;KAClD,CAAA;AACH,CAAC;AAED,KAAK,UAAU,oBAAoB,CACjC,IAAiB,EACjB,MAAc,EACd,MAAoB,EACpB,GAAsB,EACtB,OAAmC;IAEnC,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAClE,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,CAAA;IAC1B,IAAI,GAAG,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,+BAA+B,CAAC,CAAA;IAChF,6EAA6E;IAC7E,4EAA4E;IAC5E,MAAM,UAAU,GAAG,oFAAoF,CAAA;IACvG,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,UAAU,WAAW,CAAC,CAAC,CAAC,UAAU,CAAA;IACtE,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;IACtF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAW1D,CAAA;IACD,6EAA6E;IAC7E,0EAA0E;IAC1E,8CAA8C;IAC9C,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAA;IAC5C,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,IAAI,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAA;IACvG,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,IAAI,CAAC,MAAM,CAAC,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAA;IACrD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,IAAI,IAAI,CAAA;QACvC,KAAK,CAAC,IAAI,CAAC,cAAc,WAAW,IAAI,MAAM,CAAC,MAAM,OAAO,CAAC,CAAA;IAC/D,CAAC;IACD,OAAO;QACL,GAAG,EAAE,GAAG,CAAC,IAAI;QACb,OAAO,EAAE,QAAQ;QACjB,WAAW,EAAE,eAAe;QAC5B,IAAI,EAAE,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC;QAC1C,KAAK;KACN,CAAA;AACH,CAAC;AAED,KAAK,UAAU,oBAAoB,CACjC,IAAiB,EACjB,MAAoB,EACpB,GAAsB,EACtB,OAAmC;IAEnC,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAChE,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,CAAA;IAC1B,IAAI,GAAG,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAA;IAC9E,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;IAClH,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;IAElC,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC1B,OAAO;YACL,GAAG,EAAE,GAAG,CAAC,IAAI;YACb,OAAO,EAAE,OAAO;YAChB,WAAW,EAAE,YAAY;YACzB,IAAI,EAAE,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC;YACzC,KAAK,EAAE,CAAC,sBAAsB,IAAI,IAAI,MAAM,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,QAAQ,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;SAC9G,CAAA;IACH,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC,CAAA;QAC/B,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;YACtC,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,IAAI,MAAM,CAAC,MAAM,SAAS,KAAK,4BAA4B,KAAK,CAAC,MAAM,QAAQ,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,cAAc,IAAI,IAAI,MAAM,CAAC,MAAM,kCAAkC,CAAC,CAAA;QAChN,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;QAC7B,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,IAAI,MAAM,CAAC,MAAM,SAAS,KAAK,4CAA4C,CAAC,CAAA;QACnH,gEAAgE;QAChE,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACtF,OAAO;YACL,GAAG,EAAE,GAAG,CAAC,IAAI;YACb,OAAO;YACP,WAAW,EAAE,YAAY;YACzB,IAAI,EAAE,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC;YACzC,KAAK,EAAE,CAAC,gBAAgB,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC,IAAI,EAAE,EAAE,kBAAkB,IAAI,IAAI,MAAM,CAAC,MAAM,WAAW,CAAC;SACnH,CAAA;IACH,CAAC;IACD,kBAAkB;IAClB,MAAM,MAAM,GAAG,wBAAwB,IAAI,IAAI,MAAM,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,QAAQ,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAA;IACrH,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC;QAC7B,CAAC,CAAC,oBAAoB;QACtB,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;YACtB,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS,EAAE,CAAA;YACjG,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;YACrE,OAAO,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,IAAI,KAAK,KAAK,MAAM,IAAI,CAAC,UAAU,IAAI,MAAM,aAAa,IAAI,IAAI,MAAM,CAAC,MAAM,SAAS,CAAC,GAAG,CAAC,EAAE,CAAA;QAC1H,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACjB,MAAM,MAAM,GAAG,6BAA6B,IAAI,IAAI,MAAM,CAAC,MAAM,sDAAsD,IAAI,IAAI,MAAM,CAAC,MAAM,cAAc,CAAA;IAC1J,MAAM,OAAO,GAAG,GAAG,MAAM,OAAO,IAAI,GAAG,MAAM,EAAE,CAAA;IAC/C,OAAO;QACL,GAAG,EAAE,GAAG,CAAC,IAAI;QACb,OAAO;QACP,WAAW,EAAE,eAAe;QAC5B,IAAI,EAAE,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC;QACzC,KAAK,EAAE,CAAC,yBAAyB,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;KAC1D,CAAA;AACH,CAAC;AAED,qEAAqE;AACrE,KAAK,UAAU,aAAa,CAC1B,IAAiB,EACjB,MAAc,EACd,GAAsB,EACtB,OAAmC;IAEnC,IAAI,OAAO,EAAE,MAAM,EAAE,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,SAAS,CAAC,CAAA;IACxD,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;IAC3C,IAAI,CAAC;QACH,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO,MAAM,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,CAAA;QAC/F,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,MAAM,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,CAAA;QAC5F,OAAO,MAAM,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,CAAA;IACvE,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,MAAM,KAAK,CAAA;QAC9E,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACtE,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,0BAA0B,OAAO,EAAE,CAAC,CAAA;IAC/D,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,OAAO,oBAAoB;IAGF;IAFpB,MAAM,GAAG,OAAO,CAAA;IAChB,SAAS,GAAG,IAAI,CAAA;IACzB,YAA6B,IAAiB;QAAjB,SAAI,GAAJ,IAAI,CAAa;IAAG,CAAC;IAElD,OAAO,CAAC,GAAsB,EAAE,OAAwB;QACtD,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAA;IACxD,CAAC;CACF;AAED,sEAAsE;AACtE,MAAM,OAAO,iBAAiB;IAGC;IAFpB,MAAM,GAAG,IAAI,CAAA;IACb,SAAS,GAAG,IAAI,CAAA;IACzB,YAA6B,IAAiB;QAAjB,SAAI,GAAJ,IAAI,CAAa;IAAG,CAAC;IAElD,OAAO,CAAC,GAAsB,EAAE,OAAwB;QACtD,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,CAAA;IACrD,CAAC;CACF"}
|
package/dist/parse.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC 3986 scheme detection and internal-URL parsing that preserves the raw
|
|
3
|
+
* host/pathname bytes before URL normalization.
|
|
4
|
+
* @module @hy-sde-org/dsh-internal-urls/parse
|
|
5
|
+
*/
|
|
6
|
+
import type { ParsedInternalUrl } from './types.ts';
|
|
7
|
+
/**
|
|
8
|
+
* The scheme of an input that looks like a hierarchical `scheme://` URL, or
|
|
9
|
+
* `undefined` when the input is not of that shape at all (so filesystem paths
|
|
10
|
+
* like `a:/b` or `scheme:x` never count as internal URLs).
|
|
11
|
+
* @param input - the raw model-supplied path.
|
|
12
|
+
* @returns the lowercased scheme, or `undefined`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function extractUriScheme(input: string): string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Parse a hierarchical internal URL preserving the raw host and pathname.
|
|
17
|
+
* Only the `scheme://…` form routes through the registry — opaque inputs
|
|
18
|
+
* (`urn:…`) never parse here.
|
|
19
|
+
*
|
|
20
|
+
* Segment handling follows the router's strictness: an empty, `.`, or `..`
|
|
21
|
+
* path segment throws, and a malformed URL throws with the input echoed back.
|
|
22
|
+
*
|
|
23
|
+
* @param input - the raw model-supplied path (`conflict://3/ours`, `pr://1428`).
|
|
24
|
+
* @returns the parsed URL with raw segments, decoded segments, query params, and exact input.
|
|
25
|
+
* @throws when the input is not a parseable hierarchical `scheme://` URL or contains a bad segment.
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseInternalUrl(input: string): ParsedInternalUrl;
|
|
28
|
+
/**
|
|
29
|
+
* Parse the `<path>:conflict://<id>` selector form — a filesystem path glued to
|
|
30
|
+
* a conflict URL by a colon (sibling of the `path:conflicts` read selector).
|
|
31
|
+
* The conflict handler reads `.rawHref` directly and strips the prefix itself,
|
|
32
|
+
* so the returned URL keeps the original bytes as `rawHref` while the rest of
|
|
33
|
+
* the shape describes the conflict URL proper.
|
|
34
|
+
*
|
|
35
|
+
* @returns the parsed conflict URL, or `null` when `input` is not of that shape.
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseConflictReference(input: string): ParsedInternalUrl | null;
|
|
38
|
+
//# sourceMappingURL=parse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAKnD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGlE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,CA+CjE;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAM9E"}
|
package/dist/parse.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC 3986 scheme detection and internal-URL parsing that preserves the raw
|
|
3
|
+
* host/pathname bytes before URL normalization.
|
|
4
|
+
* @module @hy-sde-org/dsh-internal-urls/parse
|
|
5
|
+
*/
|
|
6
|
+
/** A hierarchical `scheme://` prefix with an RFC 3986 scheme (first char alpha, then alnum/+/./-). */
|
|
7
|
+
const URI_SCHEME_RE = /^([a-z][a-z0-9+.-]*):\/\//i;
|
|
8
|
+
/**
|
|
9
|
+
* The scheme of an input that looks like a hierarchical `scheme://` URL, or
|
|
10
|
+
* `undefined` when the input is not of that shape at all (so filesystem paths
|
|
11
|
+
* like `a:/b` or `scheme:x` never count as internal URLs).
|
|
12
|
+
* @param input - the raw model-supplied path.
|
|
13
|
+
* @returns the lowercased scheme, or `undefined`.
|
|
14
|
+
*/
|
|
15
|
+
export function extractUriScheme(input) {
|
|
16
|
+
const match = URI_SCHEME_RE.exec(input);
|
|
17
|
+
return match?.[1]?.toLowerCase();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Parse a hierarchical internal URL preserving the raw host and pathname.
|
|
21
|
+
* Only the `scheme://…` form routes through the registry — opaque inputs
|
|
22
|
+
* (`urn:…`) never parse here.
|
|
23
|
+
*
|
|
24
|
+
* Segment handling follows the router's strictness: an empty, `.`, or `..`
|
|
25
|
+
* path segment throws, and a malformed URL throws with the input echoed back.
|
|
26
|
+
*
|
|
27
|
+
* @param input - the raw model-supplied path (`conflict://3/ours`, `pr://1428`).
|
|
28
|
+
* @returns the parsed URL with raw segments, decoded segments, query params, and exact input.
|
|
29
|
+
* @throws when the input is not a parseable hierarchical `scheme://` URL or contains a bad segment.
|
|
30
|
+
*/
|
|
31
|
+
export function parseInternalUrl(input) {
|
|
32
|
+
let url;
|
|
33
|
+
try {
|
|
34
|
+
url = new URL(input);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
throw new Error(`Invalid internal URL: ${input}`);
|
|
38
|
+
}
|
|
39
|
+
const scheme = extractUriScheme(input);
|
|
40
|
+
if (scheme === undefined) {
|
|
41
|
+
throw new Error(`Invalid internal URL: ${input}`);
|
|
42
|
+
}
|
|
43
|
+
const afterScheme = input.slice(input.indexOf('://') + 3);
|
|
44
|
+
// The host ends at the first `/`, `?`, or `#` — everything else belongs to it.
|
|
45
|
+
const hostileStart = afterScheme.search(/[/?#]/);
|
|
46
|
+
const rawHost = hostileStart === -1 ? afterScheme : afterScheme.slice(0, hostileStart);
|
|
47
|
+
const tail = hostileStart === -1 ? '' : afterScheme.slice(hostileStart);
|
|
48
|
+
// The raw pathname is everything from the first `/` up to the first `?`/`#`.
|
|
49
|
+
const pathEnd = tail.search(/[?#]/);
|
|
50
|
+
const rawPathname = pathEnd === -1 ? tail : tail.slice(0, pathEnd);
|
|
51
|
+
const stripped = rawPathname.startsWith('/') ? rawPathname.slice(1) : rawPathname;
|
|
52
|
+
const pathSegments = [];
|
|
53
|
+
if (stripped !== '') {
|
|
54
|
+
for (const segment of stripped.split('/')) {
|
|
55
|
+
let decoded;
|
|
56
|
+
try {
|
|
57
|
+
decoded = decodeURIComponent(segment);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
throw new Error(`Invalid internal URL: ${input}`);
|
|
61
|
+
}
|
|
62
|
+
if (decoded === '' || decoded === '.' || decoded === '..') {
|
|
63
|
+
throw new Error(`Invalid internal URL: ${input}`);
|
|
64
|
+
}
|
|
65
|
+
pathSegments.push(decoded);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
scheme,
|
|
70
|
+
rawHost,
|
|
71
|
+
rawPathname,
|
|
72
|
+
pathSegments,
|
|
73
|
+
searchParams: url.searchParams,
|
|
74
|
+
href: url.href,
|
|
75
|
+
rawHref: input,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Parse the `<path>:conflict://<id>` selector form — a filesystem path glued to
|
|
80
|
+
* a conflict URL by a colon (sibling of the `path:conflicts` read selector).
|
|
81
|
+
* The conflict handler reads `.rawHref` directly and strips the prefix itself,
|
|
82
|
+
* so the returned URL keeps the original bytes as `rawHref` while the rest of
|
|
83
|
+
* the shape describes the conflict URL proper.
|
|
84
|
+
*
|
|
85
|
+
* @returns the parsed conflict URL, or `null` when `input` is not of that shape.
|
|
86
|
+
*/
|
|
87
|
+
export function parseConflictReference(input) {
|
|
88
|
+
const marker = input.toLowerCase().lastIndexOf(':conflict://');
|
|
89
|
+
if (marker <= 0)
|
|
90
|
+
return null;
|
|
91
|
+
const tail = input.slice(marker + ':conflict://'.length);
|
|
92
|
+
const inner = parseInternalUrl(`conflict://${tail}`);
|
|
93
|
+
return { ...inner, href: `conflict://${tail}`, rawHref: input };
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"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"}
|
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The internal-URL resolver registry: one handler per scheme, reachable by the
|
|
3
|
+
* read/grep/write tools as `ctx.internalUrls`. Handlers are stateless where
|
|
4
|
+
* possible; per-session shared state (ConflictHistory) lives in the service.
|
|
5
|
+
* @module @hy-sde-org/dsh-internal-urls/router
|
|
6
|
+
*/
|
|
7
|
+
import type { InternalResource, ProtocolHandler, ResolveContext, UrlCompletion, WriteContext } from './types.ts';
|
|
8
|
+
/**
|
|
9
|
+
* Validates a handler registration: a non-empty alphabetic scheme, and a
|
|
10
|
+
* conflict with an existing registration is a programming error (each scheme
|
|
11
|
+
* may have exactly one handler).
|
|
12
|
+
*/
|
|
13
|
+
export declare function validateHandler(handler: ProtocolHandler): string;
|
|
14
|
+
/**
|
|
15
|
+
* One resolver registry. `register`/`unregister` manage the handler table;
|
|
16
|
+
* `canHandle` decides whether a model-supplied path routes away from the
|
|
17
|
+
* filesystem; `resolve`/`write` dispatch to the matching handler.
|
|
18
|
+
*/
|
|
19
|
+
export declare class InternalUrlRouter {
|
|
20
|
+
private readonly handlers;
|
|
21
|
+
/** Register (or replace) the handler for its scheme. Returns a disposer. */
|
|
22
|
+
register(handler: ProtocolHandler): () => void;
|
|
23
|
+
/** Remove the handler for `scheme`; returns false when none was registered. */
|
|
24
|
+
unregister(scheme: string): boolean;
|
|
25
|
+
/** The registered handler for a scheme, or `undefined`. */
|
|
26
|
+
getHandler(scheme: string): ProtocolHandler | undefined;
|
|
27
|
+
/** Every registered scheme. */
|
|
28
|
+
schemes(): string[];
|
|
29
|
+
/**
|
|
30
|
+
* Whether `input` is a hierarchical `scheme://` URL (or the `<path>:conflict://`
|
|
31
|
+
* selector form) with a registered handler. This is the single gate the
|
|
32
|
+
* read/grep/write tools consult before touching the filesystem.
|
|
33
|
+
*/
|
|
34
|
+
canHandle(input: string): boolean;
|
|
35
|
+
/** Resolve an internal URL through its registered handler. */
|
|
36
|
+
resolve(input: string, context?: ResolveContext): Promise<InternalResource>;
|
|
37
|
+
/** Write an internal URL through its registered handler. */
|
|
38
|
+
write(input: string, content: string, context?: WriteContext): Promise<void>;
|
|
39
|
+
/** Candidate completions for `scheme://<query>`, or `null` when the scheme has no completer. */
|
|
40
|
+
complete(scheme: string, query: string, context?: ResolveContext): Promise<UrlCompletion[] | null>;
|
|
41
|
+
private route;
|
|
42
|
+
private unknownScheme;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=router.d.ts.map
|