Skip to content

Commit

Permalink
begin fixing agents
Browse files Browse the repository at this point in the history
  • Loading branch information
dskvr committed Jan 24, 2025
1 parent 3bf9774 commit 79807c1
Show file tree
Hide file tree
Showing 13 changed files with 1,127 additions and 0 deletions.
Empty file.
69 changes: 69 additions & 0 deletions libraries/nocap-route66/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"name": "@nostrwatch/nocap-route66",
"type": "module",
"version": "0.0.1",
"description": "A library for transforming NOCAP output into Nostr events of kind 30166",
"entry": "src/index.ts",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/node/index.js",
"types": "./dist/index.d.ts"
},
"./web": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./umd": {
"import": "./dist/umd/index.js",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"build": "tsc && tsc-alias && webpack --config webpack.config.js",
"build:browser": "webpack --config webpack.config.js --env target=web",
"build:node": "webpack --config webpack.config.js --env target=node",
"test": "vitest",
"lint": "eslint . --ext .ts",
"clean": "rimraf dist"
},
"repository": {
"type": "git",
"url": "https://github.com/yourusername/nocapd-route66.git"
},
"keywords": [
"nostr",
"route66",
"nocap",
"event",
"library"
],
"author": "Your Name <[email protected]>",
"license": "MIT",
"dependencies": {
"@nostrwatch/logger": "*",
"@nostrwatch/nocap": "workspace:^",
"nostr-geotags": "0.7.1",
"nostr-tools": "2.7.2"
},
"devDependencies": {
"@types/node": "^20.0.0",
"eslint": "^8.0.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"prettier": "^3.4.2",
"rimraf": "^5.0.0",
"ts-loader": "^9.0.0",
"tsc-alias": "^1.8.10",
"typescript": "^5.0.0",
"vitest": "^0.34.1",
"webpack": "^5.0.0",
"webpack-cli": "^5.0.0"
},
"files": [
"dist/**/*"
],
"engines": {
"node": ">=14.0.0"
}
}
180 changes: 180 additions & 0 deletions libraries/nocap-route66/src/Transform.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// Transform.test.ts
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { Transform } from './Transform.js';

// Mock dependencies if necessary
vi.mock('@nostrwatch/logger', () => ({
Logger: class {
err = vi.fn();
info = vi.fn();
warn = vi.fn();
},
default: class {
err = vi.fn();
info = vi.fn();
warn = vi.fn();
}
}));

vi.mock('nostr-tools', () => ({
getEventHash: vi.fn().mockReturnValue('mocked_event_hash'),
}));

describe('Transform Class', () => {
const pubkey = 'abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890';
let baseInstance: Transform;

// Create a concrete subclass for testing
class TestBase extends Transform {
constructor(kind: number, pubkey: string) {
super(kind, pubkey);
}

generateTags(check: any): string[][] {
return [['tag1', 'value1'], ['tag2', 'value2']];
}
}

beforeEach(() => {
baseInstance = new TestBase(9999, pubkey);
});

describe('Initialization', () => {
it('should create an instance of Transform', () => {
expect(baseInstance).toBeInstanceOf(Transform);
expect(baseInstance.kind).toBe(9999);
expect(baseInstance.pubkey).toBe(pubkey);
expect(baseInstance.logger).toBeDefined();
});

it('should throw an error if kind is undefined', () => {
expect(() => new TestBase(undefined as unknown as number, pubkey)).toThrow(
'Kind must be defined'
);
});

it('should throw an error if pubkey is undefined', () => {
expect(() => new TestBase(9999, undefined as unknown as string)).toThrow(
'DAEMON_PUBKEY must be defined'
);
});
});

describe('tpl()', () => {
it('should return a template with default values', () => {
const tpl = baseInstance.tpl();
expect(tpl).toEqual({
id: null,
pubkey,
kind: 9999,
created_at: expect.any(Number),
tags: [],
content: '',
});
});

it('should include data if provided', () => {
const data = {
checked_at: 1620000000,
content: 'Test Content',
tags: [['test', 'tag']],
};
const tpl = baseInstance.tpl(data);
expect(tpl).toEqual({
id: null,
pubkey,
kind: 9999,
created_at: 1620000000,
tags: [['test', 'tag']],
content: 'Test Content',
});
});
});

describe('generateEvent()', () => {
it('should generate an event with correct properties', () => {
const data = {
url: 'wss://example.com',
info: { data: { key: 'value' } },
};
const event = baseInstance.generateEvent(data);
expect(event.id).toBe('mocked_event_hash');
expect(event.pubkey).toBe(pubkey);
expect(event.kind).toBe(9999);
expect(event.created_at).toBeGreaterThan(0);
expect(event.tags).toEqual([['tag1', 'value1'], ['tag2', 'value2']]);
expect(event.content).toBe(JSON.stringify({ key: 'value' }));
});

it('should handle errors in content serialization', () => {
const data = {
url: "wss://someurl.xyz",
info: { data: undefined },
};
// Simulate error in JSON.stringify
const originalStringify = JSON.stringify;
JSON.stringify = () => {
throw new Error('Serialization error');
};

const event = baseInstance.generateEvent(data);
expect(event.content).toBe('{}');
// Restore JSON.stringify
JSON.stringify = originalStringify;
});
});

describe('dedupLabels()', () => {
it('should deduplicate labels correctly', () => {
const tags = [
['L', 'label1'],
['L', 'label2'],
['l', 'value1', 'label1'],
['l', 'value2', 'label1'],
['l', 'value1', 'label1'], // Duplicate
['L', 'label2'],
['l', 'value3', 'label2'],
];
const dedupedTags = baseInstance.dedupLabels(tags);
console.log('dedupedTags', dedupedTags);
expect(dedupedTags).toEqual(expect.arrayContaining([
['L', 'label1'],
['L', 'label2'],
['l', 'value1', 'label1'],
['l', 'value2', 'label1'],
['l', 'value3', 'label2'],
]));
});
});


describe('removeLabels()', () => {
it('should remove labels correctly', () => {
const tags = [
['L', 'label1'],
['l', 'value1', 'label1'],
['t', 'tag1'],
['l', 'value2', 'label1'],
['L', 'label2'],
['p', 'pubkey'],
];
const filteredTags = baseInstance.removeLabels(tags);
expect(filteredTags).toEqual([
['t', 'tag1'],
['p', 'pubkey'],
]);
});
});

describe('json()', () => {
it('should return the current event', () => {
const data = {
url: 'wss://example.com',
};
baseInstance.generateEvent(data);
const event = baseInstance.json();
expect(event).toBeDefined();
expect(event.id).toBe('mocked_event_hash');
});
});
});
126 changes: 126 additions & 0 deletions libraries/nocap-route66/src/Transform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// KindBase.ts
import {
getEventHash,
} from 'nostr-tools';
import Logger from '@nostrwatch/logger';
import { type IResult } from '@nostrwatch/nocap';


export interface ITransform {
generateEvent(data: IResult): any;
generateTags(data: IResult): string[][];
dedupLabels(tags: string[][]): string[][];
removeLabels(tags: string[][]): string[][];
}

export abstract class Transform {
logger: Logger;
event: any;

constructor(public kind: number, public pubkey: string) {
if (!kind) {
throw new Error('Kind must be defined');
}
if (!pubkey) {
throw new Error('DAEMON_PUBKEY must be defined');
}
this.logger = new Logger(`@nostrwatch/publisher/event: ${kind}`);
}

tpl(data?: any) {
const id = null;
const pubkey = this.pubkey;
const kind = this.kind;
const created_at = data?.checked_at
? data.checked_at
: Math.round(Date.now() / 1000);
const content = data?.content ?? '';
const tags = data?.tags ?? [];

return { id, pubkey, kind, created_at, tags, content };
}

json() {
return this.event;
}

dedupLabels(tags: string[][]) {
const labels = new Set();
let deduped = tags.filter((tag) => {
if (tag[0] === 'L') {
if (labels.has(tag[1])) {
return false;
}
labels.add(tag[1]);
}
return true;
});

const lTags = new Map();
deduped.forEach((tag) => {
if (tag[0] === 'l') {
const label = tag[2];
const value = tag[1];
if (!lTags.has(label)) {
lTags.set(label, new Set());
}
const labelMap = lTags.get(label)
if (labelMap.has(value)) {
return;
}
labelMap.add(value);
lTags.set(label, labelMap);
}
});

deduped = deduped.filter( tag => tag[0] !== 'l');

deduped.push(
...Array.from(lTags.entries()).flatMap(([key, values]) => {
return Array.from(values).map((value) => ['l', value, key]);
})
);

return deduped;
}

removeLabels(tags: string[][]) {
const labels = new Set();
return tags.filter((tag) => {
if (tag[0] === 'L' || tag[0] === 'l') {
return false
}
return true;
});
}


generateEvent(data: IResult) {
this.event = this._generateEvent(data);
this.event.id = getEventHash(this.event);
return this.event;
}

protected _generateEvent(data: IResult) {
let content = '{}';
const tags = this.generateTags(data);
const nip11 = data?.info?.data;

if (nip11) {
try {
content = JSON.stringify(nip11);
} catch (e) {
this.logger.err(`generateEvent(): Error: ${e}`);
this.logger.info(nip11);
}
}

return {
...this.tpl(),
content,
tags,
};
}

abstract generateTags(check: IResult): string[][];
}
2 changes: 2 additions & 0 deletions libraries/nocap-route66/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Transform } from './Transform.js';
export { Kind30166 } from './kinds/index.js';
Loading

0 comments on commit 79807c1

Please sign in to comment.