@toon-protocol/core 1.1.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.
@@ -0,0 +1,562 @@
1
+ // src/nip34/constants.ts
2
+ var REPOSITORY_ANNOUNCEMENT_KIND = 30617;
3
+ var PATCH_KIND = 1617;
4
+ var PULL_REQUEST_KIND = 1618;
5
+ var PR_STATUS_UPDATE_KIND = 1619;
6
+ var ISSUE_KIND = 1621;
7
+ var STATUS_OPEN_KIND = 1630;
8
+ var STATUS_APPLIED_KIND = 1631;
9
+ var STATUS_CLOSED_KIND = 1632;
10
+ var STATUS_DRAFT_KIND = 1633;
11
+ var NIP34_EVENT_KINDS = [
12
+ REPOSITORY_ANNOUNCEMENT_KIND,
13
+ PATCH_KIND,
14
+ PULL_REQUEST_KIND,
15
+ PR_STATUS_UPDATE_KIND,
16
+ ISSUE_KIND,
17
+ STATUS_OPEN_KIND,
18
+ STATUS_APPLIED_KIND,
19
+ STATUS_CLOSED_KIND,
20
+ STATUS_DRAFT_KIND
21
+ ];
22
+ function isNIP34Event(kind) {
23
+ return NIP34_EVENT_KINDS.includes(kind);
24
+ }
25
+
26
+ // src/nip34/types.ts
27
+ function getTag(event, tagName) {
28
+ return event.tags.find((t) => t[0] === tagName)?.[1];
29
+ }
30
+ function getTags(event, tagName) {
31
+ return event.tags.filter((t) => t[0] === tagName).map((t) => t[1]).filter((v) => v !== void 0);
32
+ }
33
+ function parseRepositoryReference(aTag) {
34
+ const parts = aTag.split(":");
35
+ if (parts.length !== 3 || parts[0] !== "30617" || !parts[1] || !parts[2]) {
36
+ throw new Error(`Invalid repository reference format: ${aTag}`);
37
+ }
38
+ return {
39
+ kind: 30617,
40
+ pubkey: parts[1],
41
+ repoId: parts[2]
42
+ };
43
+ }
44
+ function extractCommitMessage(patchContent) {
45
+ const lines = patchContent.split("\n");
46
+ const subjectLine = lines.find((line) => line.startsWith("Subject:"));
47
+ if (!subjectLine) {
48
+ return "Applied patch from Nostr";
49
+ }
50
+ return subjectLine.replace(/^Subject:\s*\[PATCH\]\s*/, "").replace(/^Subject:\s*/, "");
51
+ }
52
+
53
+ // src/nip34/ForgejoClient.ts
54
+ var ForgejoClient = class {
55
+ baseUrl;
56
+ token;
57
+ defaultOwner;
58
+ constructor(config) {
59
+ this.baseUrl = config.baseUrl.replace(/\/$/, "");
60
+ this.token = config.token;
61
+ this.defaultOwner = config.defaultOwner;
62
+ }
63
+ /**
64
+ * Make an authenticated API request
65
+ */
66
+ async request(method, path, body) {
67
+ const url = `${this.baseUrl}/api/v1${path}`;
68
+ const headers = {
69
+ Authorization: `token ${this.token}`,
70
+ "Content-Type": "application/json"
71
+ };
72
+ const response = await fetch(url, {
73
+ method,
74
+ headers,
75
+ body: body ? JSON.stringify(body) : void 0
76
+ });
77
+ if (!response.ok) {
78
+ const errorText = await response.text();
79
+ throw new Error(`Forgejo API error (${response.status}): ${errorText}`);
80
+ }
81
+ return response.json();
82
+ }
83
+ /**
84
+ * Create a new repository
85
+ */
86
+ async createRepository(options) {
87
+ const owner = this.defaultOwner;
88
+ if (!owner) {
89
+ throw new Error("No default owner configured for repository creation");
90
+ }
91
+ return this.request("POST", "/user/repos", {
92
+ name: options.name,
93
+ description: options.description || "",
94
+ private: options.private ?? false,
95
+ auto_init: options.auto_init ?? true,
96
+ default_branch: options.default_branch || "main"
97
+ });
98
+ }
99
+ /**
100
+ * Create a pull request
101
+ */
102
+ async createPullRequest(options) {
103
+ return this.request(
104
+ "POST",
105
+ `/repos/${options.owner}/${options.repo}/pulls`,
106
+ {
107
+ title: options.title,
108
+ head: options.head,
109
+ base: options.base,
110
+ body: options.body || ""
111
+ }
112
+ );
113
+ }
114
+ /**
115
+ * Create an issue
116
+ */
117
+ async createIssue(options) {
118
+ return this.request(
119
+ "POST",
120
+ `/repos/${options.owner}/${options.repo}/issues`,
121
+ {
122
+ title: options.title,
123
+ body: options.body || "",
124
+ labels: options.labels
125
+ }
126
+ );
127
+ }
128
+ /**
129
+ * Get repository information
130
+ */
131
+ async getRepository(owner, repo) {
132
+ return this.request("GET", `/repos/${owner}/${repo}`);
133
+ }
134
+ /**
135
+ * Check if repository exists
136
+ */
137
+ async repositoryExists(owner, repo) {
138
+ try {
139
+ await this.getRepository(owner, repo);
140
+ return true;
141
+ } catch {
142
+ return false;
143
+ }
144
+ }
145
+ /**
146
+ * Get clone URL for internal use (within Docker network)
147
+ */
148
+ getInternalCloneUrl(owner, repo) {
149
+ return `${this.baseUrl}/${owner}/${repo}.git`;
150
+ }
151
+ /**
152
+ * Get clone URL for external use
153
+ */
154
+ getExternalCloneUrl(repo) {
155
+ return repo.clone_url;
156
+ }
157
+ /**
158
+ * Create or update a file in a repository
159
+ */
160
+ async createOrUpdateFile(options) {
161
+ const path = `/repos/${options.owner}/${options.repo}/contents/${options.filepath}`;
162
+ const body = {
163
+ content: options.content,
164
+ message: options.message
165
+ };
166
+ if (options.branch) {
167
+ body["branch"] = options.branch;
168
+ }
169
+ if (options.sha) {
170
+ body["sha"] = options.sha;
171
+ }
172
+ const method = options.sha ? "PUT" : "POST";
173
+ return this.request(method, path, body);
174
+ }
175
+ /**
176
+ * Create a new branch
177
+ */
178
+ async createBranch(owner, repo, branchName, fromBranch = "main") {
179
+ await this.request("POST", `/repos/${owner}/${repo}/branches`, {
180
+ new_branch_name: branchName,
181
+ old_branch_name: fromBranch
182
+ });
183
+ }
184
+ /**
185
+ * Get file content from repository
186
+ */
187
+ async getFileContent(owner, repo, filepath, branch) {
188
+ try {
189
+ const path = `/repos/${owner}/${repo}/contents/${filepath}${branch ? `?ref=${branch}` : ""}`;
190
+ const response = await this.request("GET", path);
191
+ return response;
192
+ } catch {
193
+ return null;
194
+ }
195
+ }
196
+ };
197
+
198
+ // src/nip34/NIP34Handler.ts
199
+ var NIP34Handler = class {
200
+ forgejo;
201
+ verbose;
202
+ defaultOwner;
203
+ constructor(config) {
204
+ this.forgejo = new ForgejoClient({
205
+ baseUrl: config.forgejoUrl,
206
+ token: config.forgejoToken,
207
+ defaultOwner: config.defaultOwner
208
+ });
209
+ this.defaultOwner = config.defaultOwner;
210
+ this.verbose = config.verbose ?? false;
211
+ }
212
+ /**
213
+ * Handle a NIP-34 event
214
+ *
215
+ * This is the main entry point called by the BLS after storing an event.
216
+ */
217
+ async handleEvent(event) {
218
+ if (!isNIP34Event(event.kind)) {
219
+ return {
220
+ success: false,
221
+ operation: "unsupported",
222
+ message: `Not a NIP-34 event (kind ${event.kind})`
223
+ };
224
+ }
225
+ this.log(
226
+ `Handling NIP-34 event: kind=${event.kind} id=${event.id.substring(0, 8)}`
227
+ );
228
+ try {
229
+ switch (event.kind) {
230
+ case REPOSITORY_ANNOUNCEMENT_KIND:
231
+ return await this.handleRepositoryAnnouncement(event);
232
+ case PATCH_KIND:
233
+ return await this.handlePatch(event);
234
+ case PULL_REQUEST_KIND:
235
+ return await this.handlePullRequest(event);
236
+ case ISSUE_KIND:
237
+ return await this.handleIssue(event);
238
+ default:
239
+ return {
240
+ success: true,
241
+ operation: "status",
242
+ message: `Status event kind ${event.kind} received (not yet implemented)`
243
+ };
244
+ }
245
+ } catch (error) {
246
+ const message = error instanceof Error ? error.message : "Unknown error";
247
+ this.log(`Error handling event: ${message}`);
248
+ return {
249
+ success: false,
250
+ operation: this.getOperationType(event.kind),
251
+ message: `Failed to process event: ${message}`
252
+ };
253
+ }
254
+ }
255
+ /**
256
+ * Handle Repository Announcement (kind 30617)
257
+ *
258
+ * Creates a new repository in Forgejo.
259
+ */
260
+ async handleRepositoryAnnouncement(event) {
261
+ const repoId = getTag(event, "d");
262
+ const name = getTag(event, "name") || repoId;
263
+ const description = getTag(event, "description");
264
+ if (!repoId) {
265
+ return {
266
+ success: false,
267
+ operation: "repository",
268
+ message: 'Missing required "d" tag (repository identifier)'
269
+ };
270
+ }
271
+ const repoName = repoId.includes("/") ? repoId.split("/").pop() ?? repoId : repoId;
272
+ this.log(`Creating repository: ${repoName}`);
273
+ const options = {
274
+ name: repoName,
275
+ description: description || name,
276
+ private: false,
277
+ auto_init: true
278
+ };
279
+ const repo = await this.forgejo.createRepository(options);
280
+ this.log(`Repository created: ${repo.html_url}`);
281
+ return {
282
+ success: true,
283
+ operation: "repository",
284
+ message: `Repository "${repoName}" created`,
285
+ metadata: {
286
+ repoId: repoName,
287
+ htmlUrl: repo.html_url,
288
+ cloneUrl: repo.clone_url
289
+ }
290
+ };
291
+ }
292
+ /**
293
+ * Handle Patch (kind 1617)
294
+ *
295
+ * Applies a patch to a repository via Forgejo API and creates a pull request.
296
+ */
297
+ async handlePatch(event) {
298
+ const aTag = getTag(event, "a");
299
+ const patchContent = event.content;
300
+ if (!aTag) {
301
+ return {
302
+ success: false,
303
+ operation: "patch",
304
+ message: 'Missing required "a" tag (repository reference)'
305
+ };
306
+ }
307
+ const repoRef = parseRepositoryReference(aTag);
308
+ const owner = this.defaultOwner;
309
+ const repoName = repoRef.repoId.includes("/") ? repoRef.repoId.split("/").pop() ?? repoRef.repoId : repoRef.repoId;
310
+ const exists = await this.forgejo.repositoryExists(owner, repoName);
311
+ if (!exists) {
312
+ return {
313
+ success: false,
314
+ operation: "patch",
315
+ message: `Repository ${owner}/${repoName} does not exist`
316
+ };
317
+ }
318
+ this.log(`Applying patch to ${owner}/${repoName}`);
319
+ const patchInfo = this.parsePatch(patchContent);
320
+ if (!patchInfo) {
321
+ return {
322
+ success: false,
323
+ operation: "patch",
324
+ message: "Failed to parse patch content"
325
+ };
326
+ }
327
+ const patchBranch = `patch-${event.id.substring(0, 8)}`;
328
+ const commitMessage = extractCommitMessage(patchContent);
329
+ try {
330
+ await this.forgejo.createBranch(owner, repoName, patchBranch, "main");
331
+ for (const file of patchInfo.files) {
332
+ const content = Buffer.from(file.content).toString("base64");
333
+ const existingFile = await this.forgejo.getFileContent(
334
+ owner,
335
+ repoName,
336
+ file.path,
337
+ patchBranch
338
+ );
339
+ await this.forgejo.createOrUpdateFile({
340
+ owner,
341
+ repo: repoName,
342
+ filepath: file.path,
343
+ content,
344
+ message: commitMessage,
345
+ branch: patchBranch,
346
+ sha: existingFile?.sha
347
+ // Include SHA if file exists (for update)
348
+ });
349
+ }
350
+ const pr = await this.forgejo.createPullRequest({
351
+ owner,
352
+ repo: repoName,
353
+ title: commitMessage,
354
+ head: patchBranch,
355
+ base: "main",
356
+ body: `Patch from Nostr event: ${event.id}
357
+
358
+ Author: ${event.pubkey}`
359
+ });
360
+ this.log(`Pull request created: ${pr.html_url}`);
361
+ return {
362
+ success: true,
363
+ operation: "patch",
364
+ message: `Patch applied and PR #${pr.number} created`,
365
+ metadata: {
366
+ branch: patchBranch,
367
+ prNumber: pr.number,
368
+ prUrl: pr.html_url
369
+ }
370
+ };
371
+ } catch (error) {
372
+ const message = error instanceof Error ? error.message : "Unknown error";
373
+ return {
374
+ success: false,
375
+ operation: "patch",
376
+ message: `Failed to apply patch: ${message}`
377
+ };
378
+ }
379
+ }
380
+ /**
381
+ * Handle Pull Request (kind 1618)
382
+ *
383
+ * Creates an issue documenting the pull request (simplified approach without git).
384
+ */
385
+ async handlePullRequest(event) {
386
+ const aTag = getTag(event, "a");
387
+ const cloneUrl = getTag(event, "clone");
388
+ const commitTip = getTag(event, "c");
389
+ const subject = getTag(event, "subject");
390
+ if (!aTag || !cloneUrl || !commitTip) {
391
+ return {
392
+ success: false,
393
+ operation: "pull_request",
394
+ message: "Missing required tags: a, clone, c"
395
+ };
396
+ }
397
+ const repoRef = parseRepositoryReference(aTag);
398
+ const owner = this.defaultOwner;
399
+ const repoName = repoRef.repoId.includes("/") ? repoRef.repoId.split("/").pop() ?? repoRef.repoId : repoRef.repoId;
400
+ this.log(`Creating PR issue for ${owner}/${repoName} from ${cloneUrl}`);
401
+ const issueBody = `
402
+ **Pull Request from Nostr**
403
+
404
+ A pull request was submitted via NIP-34 event.
405
+
406
+ **Details:**
407
+ - Source Repository: ${cloneUrl}
408
+ - Commit: ${commitTip}
409
+ - Nostr Event: ${event.id}
410
+ - Author: ${event.pubkey}
411
+
412
+ **To apply this pull request:**
413
+ 1. Clone the source repository: \`git clone ${cloneUrl}\`
414
+ 2. Fetch the specific commit: \`git fetch origin ${commitTip}\`
415
+ 3. Create a branch: \`git checkout -b pr-${event.id.substring(0, 8)} ${commitTip}\`
416
+ 4. Push to this repository and create a PR manually
417
+
418
+ ${event.content}
419
+ `;
420
+ const issue = await this.forgejo.createIssue({
421
+ owner,
422
+ repo: repoName,
423
+ title: subject || `Pull Request from ${event.pubkey.substring(0, 8)}`,
424
+ body: issueBody
425
+ });
426
+ this.log(`PR issue created: ${issue.html_url}`);
427
+ return {
428
+ success: true,
429
+ operation: "pull_request",
430
+ message: `PR documented as issue #${issue.number}`,
431
+ metadata: {
432
+ issueNumber: issue.number,
433
+ issueUrl: issue.html_url
434
+ }
435
+ };
436
+ }
437
+ /**
438
+ * Handle Issue (kind 1621)
439
+ *
440
+ * Creates an issue in Forgejo.
441
+ */
442
+ async handleIssue(event) {
443
+ const aTag = getTag(event, "a");
444
+ const subject = getTag(event, "subject");
445
+ const body = event.content;
446
+ if (!aTag || !subject) {
447
+ return {
448
+ success: false,
449
+ operation: "issue",
450
+ message: "Missing required tags: a, subject"
451
+ };
452
+ }
453
+ const repoRef = parseRepositoryReference(aTag);
454
+ const owner = this.defaultOwner;
455
+ const repoName = repoRef.repoId.includes("/") ? repoRef.repoId.split("/").pop() ?? repoRef.repoId : repoRef.repoId;
456
+ this.log(`Creating issue in ${owner}/${repoName}`);
457
+ const issue = await this.forgejo.createIssue({
458
+ owner,
459
+ repo: repoName,
460
+ title: subject,
461
+ body: `${body}
462
+
463
+ ---
464
+ Submitted via Nostr event: ${event.id}
465
+ Author: ${event.pubkey}`
466
+ });
467
+ this.log(`Issue created: ${issue.html_url}`);
468
+ return {
469
+ success: true,
470
+ operation: "issue",
471
+ message: `Issue #${issue.number} created`,
472
+ metadata: {
473
+ issueNumber: issue.number,
474
+ issueUrl: issue.html_url
475
+ }
476
+ };
477
+ }
478
+ /**
479
+ * Get operation type from event kind
480
+ */
481
+ getOperationType(kind) {
482
+ switch (kind) {
483
+ case REPOSITORY_ANNOUNCEMENT_KIND:
484
+ return "repository";
485
+ case PATCH_KIND:
486
+ return "patch";
487
+ case PULL_REQUEST_KIND:
488
+ return "pull_request";
489
+ case ISSUE_KIND:
490
+ return "issue";
491
+ case 1630:
492
+ case 1631:
493
+ case 1632:
494
+ case 1633:
495
+ return "status";
496
+ default:
497
+ return "unsupported";
498
+ }
499
+ }
500
+ /**
501
+ * Parse git format-patch output to extract file changes
502
+ */
503
+ parsePatch(patchContent) {
504
+ try {
505
+ const files = [];
506
+ const diffPattern = /diff --git a\/(.*?) b\/(.*?)$/gm;
507
+ const matches = [...patchContent.matchAll(diffPattern)];
508
+ if (matches.length === 0) {
509
+ return null;
510
+ }
511
+ for (const match of matches) {
512
+ const filePath = match[2];
513
+ if (!filePath) continue;
514
+ const fileStart = match.index ?? 0;
515
+ const nextMatch = matches[matches.indexOf(match) + 1];
516
+ const fileEnd = nextMatch?.index ?? patchContent.length;
517
+ const filePatch = patchContent.substring(fileStart, fileEnd);
518
+ const contentLines = filePatch.split("\n").filter((line) => line.startsWith("+") && !line.startsWith("+++")).map((line) => line.substring(1));
519
+ if (contentLines.length > 0) {
520
+ files.push({
521
+ path: filePath,
522
+ content: contentLines.join("\n")
523
+ });
524
+ }
525
+ }
526
+ return files.length > 0 ? { files } : null;
527
+ } catch (error) {
528
+ this.log(
529
+ `Error parsing patch: ${error instanceof Error ? error.message : "Unknown"}`
530
+ );
531
+ return null;
532
+ }
533
+ }
534
+ /**
535
+ * Log message if verbose mode is enabled
536
+ */
537
+ log(message) {
538
+ if (this.verbose) {
539
+ console.log(`[NIP34] ${message}`);
540
+ }
541
+ }
542
+ };
543
+ export {
544
+ ForgejoClient,
545
+ ISSUE_KIND,
546
+ NIP34Handler,
547
+ NIP34_EVENT_KINDS,
548
+ PATCH_KIND,
549
+ PR_STATUS_UPDATE_KIND,
550
+ PULL_REQUEST_KIND,
551
+ REPOSITORY_ANNOUNCEMENT_KIND,
552
+ STATUS_APPLIED_KIND,
553
+ STATUS_CLOSED_KIND,
554
+ STATUS_DRAFT_KIND,
555
+ STATUS_OPEN_KIND,
556
+ extractCommitMessage,
557
+ getTag,
558
+ getTags,
559
+ isNIP34Event,
560
+ parseRepositoryReference
561
+ };
562
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/nip34/constants.ts","../../src/nip34/types.ts","../../src/nip34/ForgejoClient.ts","../../src/nip34/NIP34Handler.ts"],"sourcesContent":["/**\n * NIP-34: Git Stuff\n * https://github.com/nostr-protocol/nips/blob/master/34.md\n *\n * Event kinds for decentralized code collaboration via Nostr.\n */\n\n/**\n * Repository Announcement (kind 30617)\n * Replaceable event announcing a Git repository's existence.\n * Contains clone URLs, maintainers, and metadata.\n */\nexport const REPOSITORY_ANNOUNCEMENT_KIND = 30617;\n\n/**\n * Patch (kind 1617)\n * Direct patch submission for files under 60KB.\n * Contains git format-patch output and commit metadata.\n */\nexport const PATCH_KIND = 1617;\n\n/**\n * Pull Request (kind 1618)\n * Larger submissions or branch-based changes.\n * References remote repository and commit range.\n */\nexport const PULL_REQUEST_KIND = 1618;\n\n/**\n * Pull Request Status Update (kind 1619)\n * Updates to existing pull requests.\n */\nexport const PR_STATUS_UPDATE_KIND = 1619;\n\n/**\n * Issue (kind 1621)\n * Bug reports and feature requests in Markdown format.\n */\nexport const ISSUE_KIND = 1621;\n\n/**\n * Status: Open (kind 1630)\n */\nexport const STATUS_OPEN_KIND = 1630;\n\n/**\n * Status: Applied/Merged (kind 1631)\n */\nexport const STATUS_APPLIED_KIND = 1631;\n\n/**\n * Status: Closed (kind 1632)\n */\nexport const STATUS_CLOSED_KIND = 1632;\n\n/**\n * Status: Draft (kind 1633)\n */\nexport const STATUS_DRAFT_KIND = 1633;\n\n/**\n * All NIP-34 event kinds\n */\nexport const NIP34_EVENT_KINDS = [\n REPOSITORY_ANNOUNCEMENT_KIND,\n PATCH_KIND,\n PULL_REQUEST_KIND,\n PR_STATUS_UPDATE_KIND,\n ISSUE_KIND,\n STATUS_OPEN_KIND,\n STATUS_APPLIED_KIND,\n STATUS_CLOSED_KIND,\n STATUS_DRAFT_KIND,\n] as const;\n\n/**\n * Check if an event kind is a NIP-34 event\n */\nexport function isNIP34Event(kind: number): boolean {\n return (NIP34_EVENT_KINDS as readonly number[]).includes(kind);\n}\n","import type { Event as NostrEvent } from 'nostr-tools/pure';\n\n/**\n * Helper to get tag value by name\n */\nexport function getTag(event: NostrEvent, tagName: string): string | undefined {\n return event.tags.find((t) => t[0] === tagName)?.[1];\n}\n\n/**\n * Helper to get all tag values by name\n */\nexport function getTags(event: NostrEvent, tagName: string): string[] {\n return event.tags\n .filter((t) => t[0] === tagName)\n .map((t) => t[1])\n .filter((v): v is string => v !== undefined);\n}\n\n/**\n * Repository Announcement Event (Kind 30617)\n */\nexport interface RepositoryAnnouncement extends NostrEvent {\n kind: 30617;\n tags: (\n | ['d', string] // repository identifier\n | ['name', string] // human-readable name\n | ['description', string] // repository description\n | ['web', string] // browsing URL\n | ['clone', string] // clone URL\n | ['relays', ...string[]] // preferred relays\n | ['r', string, 'euc'] // earliest unique commit\n | ['maintainers', ...string[]]\n )[];\n}\n\n/**\n * Patch Event (Kind 1617)\n */\nexport interface PatchEvent extends NostrEvent {\n kind: 1617;\n content: string; // git format-patch output\n tags: (\n | ['a', string] // repository reference (30617:pubkey:repo-id)\n | ['r', string] // earliest unique commit\n | ['p', string] // repository owner pubkey\n | ['commit', string] // current commit SHA\n | ['parent-commit', string] // parent commit SHA\n | ['commit-pgp-sig', string] // optional PGP signature\n | ['committer', string] // optional committer info\n | ['t', 'root' | 'reply']\n )[];\n}\n\n/**\n * Pull Request Event (Kind 1618)\n */\nexport interface PullRequestEvent extends NostrEvent {\n kind: 1618;\n tags: (\n | ['a', string] // repository reference (30617:pubkey:repo-id)\n | ['r', string] // earliest unique commit\n | ['p', string] // repository owner pubkey\n | ['clone', string] // contributor's clone URL\n | ['c', string] // commit tip SHA\n | ['merge-base', string] // merge base commit\n | ['subject', string] // PR title\n | ['t', 'root' | 'reply']\n )[];\n}\n\n/**\n * Issue Event (Kind 1621)\n */\nexport interface IssueEvent extends NostrEvent {\n kind: 1621;\n content: string; // Markdown issue body\n tags: (\n | ['a', string] // repository reference (30617:pubkey:repo-id)\n | ['p', string] // repository owner pubkey\n | ['subject', string] // issue title\n | ['t', string]\n )[];\n}\n\n/**\n * Status Event (Kinds 1630-1633)\n */\nexport interface StatusEvent extends NostrEvent {\n kind: 1630 | 1631 | 1632 | 1633; // open, applied, closed, draft\n tags: (\n | ['e', string] // event being updated (patch, PR, issue)\n | ['p', string]\n )[];\n}\n\n/**\n * Union type of all NIP-34 events\n */\nexport type NIP34Event =\n | RepositoryAnnouncement\n | PatchEvent\n | PullRequestEvent\n | IssueEvent\n | StatusEvent;\n\n/**\n * Parse repository reference from 'a' tag\n * Format: \"30617:pubkey:repo-id\"\n */\nexport interface RepositoryReference {\n kind: 30617;\n pubkey: string;\n repoId: string;\n}\n\nexport function parseRepositoryReference(aTag: string): RepositoryReference {\n const parts = aTag.split(':');\n if (parts.length !== 3 || parts[0] !== '30617' || !parts[1] || !parts[2]) {\n throw new Error(`Invalid repository reference format: ${aTag}`);\n }\n return {\n kind: 30617,\n pubkey: parts[1],\n repoId: parts[2],\n };\n}\n\n/**\n * Extract commit message from git format-patch content\n */\nexport function extractCommitMessage(patchContent: string): string {\n const lines = patchContent.split('\\n');\n const subjectLine = lines.find((line) => line.startsWith('Subject:'));\n if (!subjectLine) {\n return 'Applied patch from Nostr';\n }\n // Remove \"Subject: [PATCH]\" prefix\n return subjectLine\n .replace(/^Subject:\\s*\\[PATCH\\]\\s*/, '')\n .replace(/^Subject:\\s*/, '');\n}\n","/**\n * Forgejo API Client\n *\n * Wrapper around Forgejo REST API for repository and issue management.\n * Uses fetch API for HTTP requests (compatible with gitea-js SDK structure).\n */\n\nexport interface ForgejoConfig {\n /** Base URL of Forgejo instance (e.g., \"http://forgejo:3000\") */\n baseUrl: string;\n /** API token for authentication */\n token: string;\n /** Default owner/organization for repositories */\n defaultOwner?: string;\n}\n\nexport interface CreateRepositoryOptions {\n name: string;\n description?: string;\n private?: boolean;\n auto_init?: boolean;\n default_branch?: string;\n}\n\nexport interface CreatePullRequestOptions {\n owner: string;\n repo: string;\n title: string;\n head: string; // source branch\n base: string; // target branch\n body?: string;\n}\n\nexport interface CreateIssueOptions {\n owner: string;\n repo: string;\n title: string;\n body?: string;\n labels?: number[]; // label IDs\n}\n\nexport interface ForgejoRepository {\n id: number;\n name: string;\n full_name: string;\n description: string;\n html_url: string;\n clone_url: string;\n ssh_url: string;\n}\n\nexport interface ForgejoPullRequest {\n id: number;\n number: number;\n title: string;\n html_url: string;\n state: 'open' | 'closed';\n}\n\nexport interface ForgejoIssue {\n id: number;\n number: number;\n title: string;\n html_url: string;\n state: 'open' | 'closed';\n}\n\nexport interface CreateOrUpdateFileOptions {\n owner: string;\n repo: string;\n filepath: string;\n content: string; // base64 encoded\n message: string;\n branch?: string;\n sha?: string; // required for updates\n}\n\nexport interface ForgejoFileResponse {\n content: {\n name: string;\n path: string;\n sha: string;\n };\n commit: {\n sha: string;\n };\n}\n\n/**\n * Forgejo API Client\n */\nexport class ForgejoClient {\n private baseUrl: string;\n private token: string;\n private defaultOwner?: string;\n\n constructor(config: ForgejoConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, ''); // Remove trailing slash\n this.token = config.token;\n this.defaultOwner = config.defaultOwner;\n }\n\n /**\n * Make an authenticated API request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}/api/v1${path}`;\n const headers: Record<string, string> = {\n Authorization: `token ${this.token}`,\n 'Content-Type': 'application/json',\n };\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Forgejo API error (${response.status}): ${errorText}`);\n }\n\n return response.json() as Promise<T>;\n }\n\n /**\n * Create a new repository\n */\n async createRepository(\n options: CreateRepositoryOptions\n ): Promise<ForgejoRepository> {\n const owner = this.defaultOwner;\n if (!owner) {\n throw new Error('No default owner configured for repository creation');\n }\n\n return this.request<ForgejoRepository>('POST', '/user/repos', {\n name: options.name,\n description: options.description || '',\n private: options.private ?? false,\n auto_init: options.auto_init ?? true,\n default_branch: options.default_branch || 'main',\n });\n }\n\n /**\n * Create a pull request\n */\n async createPullRequest(\n options: CreatePullRequestOptions\n ): Promise<ForgejoPullRequest> {\n return this.request<ForgejoPullRequest>(\n 'POST',\n `/repos/${options.owner}/${options.repo}/pulls`,\n {\n title: options.title,\n head: options.head,\n base: options.base,\n body: options.body || '',\n }\n );\n }\n\n /**\n * Create an issue\n */\n async createIssue(options: CreateIssueOptions): Promise<ForgejoIssue> {\n return this.request<ForgejoIssue>(\n 'POST',\n `/repos/${options.owner}/${options.repo}/issues`,\n {\n title: options.title,\n body: options.body || '',\n labels: options.labels,\n }\n );\n }\n\n /**\n * Get repository information\n */\n async getRepository(owner: string, repo: string): Promise<ForgejoRepository> {\n return this.request<ForgejoRepository>('GET', `/repos/${owner}/${repo}`);\n }\n\n /**\n * Check if repository exists\n */\n async repositoryExists(owner: string, repo: string): Promise<boolean> {\n try {\n await this.getRepository(owner, repo);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Get clone URL for internal use (within Docker network)\n */\n getInternalCloneUrl(owner: string, repo: string): string {\n // Use internal Docker network URL\n return `${this.baseUrl}/${owner}/${repo}.git`;\n }\n\n /**\n * Get clone URL for external use\n */\n getExternalCloneUrl(repo: ForgejoRepository): string {\n return repo.clone_url;\n }\n\n /**\n * Create or update a file in a repository\n */\n async createOrUpdateFile(\n options: CreateOrUpdateFileOptions\n ): Promise<ForgejoFileResponse> {\n const path = `/repos/${options.owner}/${options.repo}/contents/${options.filepath}`;\n const body: Record<string, unknown> = {\n content: options.content,\n message: options.message,\n };\n\n if (options.branch) {\n body['branch'] = options.branch;\n }\n if (options.sha) {\n body['sha'] = options.sha;\n }\n\n // Use PUT for updates (when SHA provided), POST for creates\n const method = options.sha ? 'PUT' : 'POST';\n return this.request<ForgejoFileResponse>(method, path, body);\n }\n\n /**\n * Create a new branch\n */\n async createBranch(\n owner: string,\n repo: string,\n branchName: string,\n fromBranch = 'main'\n ): Promise<void> {\n // Create the new branch from the source branch\n // Forgejo API: POST /repos/{owner}/{repo}/branches\n await this.request('POST', `/repos/${owner}/${repo}/branches`, {\n new_branch_name: branchName,\n old_branch_name: fromBranch,\n });\n }\n\n /**\n * Get file content from repository\n */\n async getFileContent(\n owner: string,\n repo: string,\n filepath: string,\n branch?: string\n ): Promise<{ content: string; sha: string } | null> {\n try {\n const path = `/repos/${owner}/${repo}/contents/${filepath}${branch ? `?ref=${branch}` : ''}`;\n const response = await this.request<{\n content: string;\n sha: string;\n }>('GET', path);\n return response;\n } catch {\n return null;\n }\n }\n}\n","/**\n * NIP-34 Handler\n *\n * Processes NIP-34 events (Git stuff on Nostr) and executes corresponding\n * Git operations on a Forgejo instance.\n *\n * Flow:\n * 1. TOON receives NIP-34 event via ILP payment\n * 2. BLS validates payment and stores event\n * 3. BLS calls NIP34Handler.handleEvent()\n * 4. Handler maps event to Git operation\n * 5. Operation executes on Forgejo\n */\n\nimport type { Event as NostrEvent } from 'nostr-tools/pure';\nimport {\n ForgejoClient,\n type CreateRepositoryOptions,\n} from './ForgejoClient.js';\nimport {\n isNIP34Event,\n REPOSITORY_ANNOUNCEMENT_KIND,\n PATCH_KIND,\n PULL_REQUEST_KIND,\n ISSUE_KIND,\n} from './constants.js';\nimport {\n getTag,\n parseRepositoryReference,\n extractCommitMessage,\n type NIP34Event,\n} from './types.js';\n\nexport interface NIP34Config {\n /** Forgejo base URL (e.g., \"http://forgejo:3000\") */\n forgejoUrl: string;\n /** Forgejo API token */\n forgejoToken: string;\n /** Default owner/org for repositories */\n defaultOwner: string;\n /** Git commit identity configuration */\n gitConfig?: {\n userName: string;\n userEmail: string;\n };\n /** Enable verbose logging */\n verbose?: boolean;\n}\n\nexport interface HandleEventResult {\n success: boolean;\n operation:\n | 'repository'\n | 'patch'\n | 'pull_request'\n | 'issue'\n | 'status'\n | 'unsupported';\n message: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * NIP-34 Event Handler\n *\n * Maps Nostr events to Git operations on Forgejo.\n */\nexport class NIP34Handler {\n private forgejo: ForgejoClient;\n private verbose: boolean;\n private defaultOwner: string;\n\n constructor(config: NIP34Config) {\n this.forgejo = new ForgejoClient({\n baseUrl: config.forgejoUrl,\n token: config.forgejoToken,\n defaultOwner: config.defaultOwner,\n });\n\n this.defaultOwner = config.defaultOwner;\n this.verbose = config.verbose ?? false;\n }\n\n /**\n * Handle a NIP-34 event\n *\n * This is the main entry point called by the BLS after storing an event.\n */\n async handleEvent(event: NostrEvent): Promise<HandleEventResult> {\n // Check if this is a NIP-34 event\n if (!isNIP34Event(event.kind)) {\n return {\n success: false,\n operation: 'unsupported',\n message: `Not a NIP-34 event (kind ${event.kind})`,\n };\n }\n\n this.log(\n `Handling NIP-34 event: kind=${event.kind} id=${event.id.substring(0, 8)}`\n );\n\n try {\n switch (event.kind) {\n case REPOSITORY_ANNOUNCEMENT_KIND:\n return await this.handleRepositoryAnnouncement(event as NIP34Event);\n\n case PATCH_KIND:\n return await this.handlePatch(event as NIP34Event);\n\n case PULL_REQUEST_KIND:\n return await this.handlePullRequest(event as NIP34Event);\n\n case ISSUE_KIND:\n return await this.handleIssue(event as NIP34Event);\n\n default:\n // Status events (1630-1633) - not yet implemented\n return {\n success: true,\n operation: 'status',\n message: `Status event kind ${event.kind} received (not yet implemented)`,\n };\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n this.log(`Error handling event: ${message}`);\n return {\n success: false,\n operation: this.getOperationType(event.kind),\n message: `Failed to process event: ${message}`,\n };\n }\n }\n\n /**\n * Handle Repository Announcement (kind 30617)\n *\n * Creates a new repository in Forgejo.\n */\n private async handleRepositoryAnnouncement(\n event: NIP34Event\n ): Promise<HandleEventResult> {\n const repoId = getTag(event, 'd');\n const name = getTag(event, 'name') || repoId;\n const description = getTag(event, 'description');\n\n if (!repoId) {\n return {\n success: false,\n operation: 'repository',\n message: 'Missing required \"d\" tag (repository identifier)',\n };\n }\n\n // Extract just the repository name (remove owner prefix if present)\n // e.g., \"admin/repo-name\" -> \"repo-name\"\n const repoName = repoId.includes('/')\n ? (repoId.split('/').pop() ?? repoId)\n : repoId;\n\n this.log(`Creating repository: ${repoName}`);\n\n const options: CreateRepositoryOptions = {\n name: repoName,\n description: description || name,\n private: false,\n auto_init: true,\n };\n\n const repo = await this.forgejo.createRepository(options);\n\n this.log(`Repository created: ${repo.html_url}`);\n\n return {\n success: true,\n operation: 'repository',\n message: `Repository \"${repoName}\" created`,\n metadata: {\n repoId: repoName,\n htmlUrl: repo.html_url,\n cloneUrl: repo.clone_url,\n },\n };\n }\n\n /**\n * Handle Patch (kind 1617)\n *\n * Applies a patch to a repository via Forgejo API and creates a pull request.\n */\n private async handlePatch(event: NIP34Event): Promise<HandleEventResult> {\n const aTag = getTag(event, 'a');\n const patchContent = event.content;\n\n if (!aTag) {\n return {\n success: false,\n operation: 'patch',\n message: 'Missing required \"a\" tag (repository reference)',\n };\n }\n\n const repoRef = parseRepositoryReference(aTag);\n const owner = this.defaultOwner;\n // Extract just the repository name (remove owner prefix if present)\n const repoName = repoRef.repoId.includes('/')\n ? (repoRef.repoId.split('/').pop() ?? repoRef.repoId)\n : repoRef.repoId;\n\n // Check if repository exists\n const exists = await this.forgejo.repositoryExists(owner, repoName);\n if (!exists) {\n return {\n success: false,\n operation: 'patch',\n message: `Repository ${owner}/${repoName} does not exist`,\n };\n }\n\n this.log(`Applying patch to ${owner}/${repoName}`);\n\n // Parse patch to extract file changes\n const patchInfo = this.parsePatch(patchContent);\n if (!patchInfo) {\n return {\n success: false,\n operation: 'patch',\n message: 'Failed to parse patch content',\n };\n }\n\n const patchBranch = `patch-${event.id.substring(0, 8)}`;\n const commitMessage = extractCommitMessage(patchContent);\n\n try {\n // Create a new branch via API\n await this.forgejo.createBranch(owner, repoName, patchBranch, 'main');\n\n // Apply each file change via API\n for (const file of patchInfo.files) {\n const content = Buffer.from(file.content).toString('base64');\n\n // Check if file exists on the branch to get its SHA for updates\n const existingFile = await this.forgejo.getFileContent(\n owner,\n repoName,\n file.path,\n patchBranch\n );\n\n await this.forgejo.createOrUpdateFile({\n owner,\n repo: repoName,\n filepath: file.path,\n content,\n message: commitMessage,\n branch: patchBranch,\n sha: existingFile?.sha, // Include SHA if file exists (for update)\n });\n }\n\n // Create pull request via API\n const pr = await this.forgejo.createPullRequest({\n owner,\n repo: repoName,\n title: commitMessage,\n head: patchBranch,\n base: 'main',\n body: `Patch from Nostr event: ${event.id}\\n\\nAuthor: ${event.pubkey}`,\n });\n\n this.log(`Pull request created: ${pr.html_url}`);\n\n return {\n success: true,\n operation: 'patch',\n message: `Patch applied and PR #${pr.number} created`,\n metadata: {\n branch: patchBranch,\n prNumber: pr.number,\n prUrl: pr.html_url,\n },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return {\n success: false,\n operation: 'patch',\n message: `Failed to apply patch: ${message}`,\n };\n }\n }\n\n /**\n * Handle Pull Request (kind 1618)\n *\n * Creates an issue documenting the pull request (simplified approach without git).\n */\n private async handlePullRequest(\n event: NIP34Event\n ): Promise<HandleEventResult> {\n const aTag = getTag(event, 'a');\n const cloneUrl = getTag(event, 'clone');\n const commitTip = getTag(event, 'c');\n const subject = getTag(event, 'subject');\n\n if (!aTag || !cloneUrl || !commitTip) {\n return {\n success: false,\n operation: 'pull_request',\n message: 'Missing required tags: a, clone, c',\n };\n }\n\n const repoRef = parseRepositoryReference(aTag);\n const owner = this.defaultOwner;\n // Extract just the repository name (remove owner prefix if present)\n const repoName = repoRef.repoId.includes('/')\n ? (repoRef.repoId.split('/').pop() ?? repoRef.repoId)\n : repoRef.repoId;\n\n this.log(`Creating PR issue for ${owner}/${repoName} from ${cloneUrl}`);\n\n // Create an issue documenting the pull request\n const issueBody = `\n**Pull Request from Nostr**\n\nA pull request was submitted via NIP-34 event.\n\n**Details:**\n- Source Repository: ${cloneUrl}\n- Commit: ${commitTip}\n- Nostr Event: ${event.id}\n- Author: ${event.pubkey}\n\n**To apply this pull request:**\n1. Clone the source repository: \\`git clone ${cloneUrl}\\`\n2. Fetch the specific commit: \\`git fetch origin ${commitTip}\\`\n3. Create a branch: \\`git checkout -b pr-${event.id.substring(0, 8)} ${commitTip}\\`\n4. Push to this repository and create a PR manually\n\n${event.content}\n`;\n\n const issue = await this.forgejo.createIssue({\n owner,\n repo: repoName,\n title: subject || `Pull Request from ${event.pubkey.substring(0, 8)}`,\n body: issueBody,\n });\n\n this.log(`PR issue created: ${issue.html_url}`);\n\n return {\n success: true,\n operation: 'pull_request',\n message: `PR documented as issue #${issue.number}`,\n metadata: {\n issueNumber: issue.number,\n issueUrl: issue.html_url,\n },\n };\n }\n\n /**\n * Handle Issue (kind 1621)\n *\n * Creates an issue in Forgejo.\n */\n private async handleIssue(event: NIP34Event): Promise<HandleEventResult> {\n const aTag = getTag(event, 'a');\n const subject = getTag(event, 'subject');\n const body = event.content;\n\n if (!aTag || !subject) {\n return {\n success: false,\n operation: 'issue',\n message: 'Missing required tags: a, subject',\n };\n }\n\n const repoRef = parseRepositoryReference(aTag);\n const owner = this.defaultOwner;\n // Extract just the repository name (remove owner prefix if present)\n const repoName = repoRef.repoId.includes('/')\n ? (repoRef.repoId.split('/').pop() ?? repoRef.repoId)\n : repoRef.repoId;\n\n this.log(`Creating issue in ${owner}/${repoName}`);\n\n const issue = await this.forgejo.createIssue({\n owner,\n repo: repoName,\n title: subject,\n body: `${body}\\n\\n---\\nSubmitted via Nostr event: ${event.id}\\nAuthor: ${event.pubkey}`,\n });\n\n this.log(`Issue created: ${issue.html_url}`);\n\n return {\n success: true,\n operation: 'issue',\n message: `Issue #${issue.number} created`,\n metadata: {\n issueNumber: issue.number,\n issueUrl: issue.html_url,\n },\n };\n }\n\n /**\n * Get operation type from event kind\n */\n private getOperationType(\n kind: number\n ):\n | 'repository'\n | 'patch'\n | 'pull_request'\n | 'issue'\n | 'status'\n | 'unsupported' {\n switch (kind) {\n case REPOSITORY_ANNOUNCEMENT_KIND:\n return 'repository';\n case PATCH_KIND:\n return 'patch';\n case PULL_REQUEST_KIND:\n return 'pull_request';\n case ISSUE_KIND:\n return 'issue';\n case 1630:\n case 1631:\n case 1632:\n case 1633:\n return 'status';\n default:\n return 'unsupported';\n }\n }\n\n /**\n * Parse git format-patch output to extract file changes\n */\n private parsePatch(\n patchContent: string\n ): { files: { path: string; content: string }[] } | null {\n try {\n const files: { path: string; content: string }[] = [];\n\n // Simple parser for git format-patch\n // Look for diff --git lines to identify files\n const diffPattern = /diff --git a\\/(.*?) b\\/(.*?)$/gm;\n const matches = [...patchContent.matchAll(diffPattern)];\n\n if (matches.length === 0) {\n return null;\n }\n\n for (const match of matches) {\n const filePath = match[2]; // Use the \"b/\" path (new file)\n if (!filePath) continue; // Skip if no path found\n\n // Extract the patch content for this file\n // For simplicity, we'll extract everything after the diff header\n // In a full implementation, we'd properly parse hunks and apply them\n const fileStart = match.index ?? 0;\n const nextMatch = matches[matches.indexOf(match) + 1];\n const fileEnd = nextMatch?.index ?? patchContent.length;\n const filePatch = patchContent.substring(fileStart, fileEnd);\n\n // For now, extract content lines (lines starting with +)\n // This is a simplified approach - a full parser would handle hunks properly\n const contentLines = filePatch\n .split('\\n')\n .filter((line) => line.startsWith('+') && !line.startsWith('+++'))\n .map((line) => line.substring(1));\n\n if (contentLines.length > 0) {\n files.push({\n path: filePath,\n content: contentLines.join('\\n'),\n });\n }\n }\n\n return files.length > 0 ? { files } : null;\n } catch (error) {\n this.log(\n `Error parsing patch: ${error instanceof Error ? error.message : 'Unknown'}`\n );\n return null;\n }\n }\n\n /**\n * Log message if verbose mode is enabled\n */\n private log(message: string): void {\n if (this.verbose) {\n console.log(`[NIP34] ${message}`);\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,2 @@
1
+ export { a as ToonDecodeError, b as ToonEncodeError, c as ToonRoutingMeta, d as decodeEventFromToon, e as encodeEventToToon, f as encodeEventToToonString, s as shallowParseToon } from '../index-DSgMeweu.js';
2
+ import 'nostr-tools/pure';
@@ -0,0 +1,17 @@
1
+ import {
2
+ ToonDecodeError,
3
+ ToonEncodeError,
4
+ decodeEventFromToon,
5
+ encodeEventToToon,
6
+ encodeEventToToonString,
7
+ shallowParseToon
8
+ } from "../chunk-7AHDGE4H.js";
9
+ export {
10
+ ToonDecodeError,
11
+ ToonEncodeError,
12
+ decodeEventFromToon,
13
+ encodeEventToToon,
14
+ encodeEventToToonString,
15
+ shallowParseToon
16
+ };
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}