@relazio/plugin-sdk 0.1.1 → 0.2.0
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/CHANGELOG.md +96 -0
- package/README.md +121 -12
- package/dist/core/types.d.ts +8 -8
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -1
- package/dist/utils/builders.d.ts +147 -0
- package/dist/utils/builders.d.ts.map +1 -0
- package/dist/utils/builders.js +316 -0
- package/dist/utils/id-generator.d.ts +81 -0
- package/dist/utils/id-generator.d.ts.map +1 -0
- package/dist/utils/id-generator.js +122 -0
- package/dist/utils/result-builder.d.ts +109 -0
- package/dist/utils/result-builder.d.ts.map +1 -0
- package/dist/utils/result-builder.js +185 -0
- package/docs/builders-guide.md +520 -0
- package/docs/examples.md +238 -0
- package/docs/quick-start.md +287 -0
- package/docs/response-format.md +515 -0
- package/package.json +4 -2
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResultBuilder = void 0;
|
|
4
|
+
exports.emptyResult = emptyResult;
|
|
5
|
+
exports.errorResult = errorResult;
|
|
6
|
+
exports.singleEntityResult = singleEntityResult;
|
|
7
|
+
exports.multiEntityResult = multiEntityResult;
|
|
8
|
+
const builders_1 = require("./builders");
|
|
9
|
+
/**
|
|
10
|
+
* Builder per costruire facilmente un TransformResult
|
|
11
|
+
* Gestisce automaticamente gli edge dall'entità di input alle nuove entità
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const result = new ResultBuilder(input)
|
|
16
|
+
* .addEntity(createIP('8.8.8.8'), 'resolves to')
|
|
17
|
+
* .addEntity(createLocation('Mountain View, CA'), 'located in')
|
|
18
|
+
* .setMessage('Found IP information')
|
|
19
|
+
* .build();
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
class ResultBuilder {
|
|
23
|
+
constructor(input) {
|
|
24
|
+
this.entities = [];
|
|
25
|
+
this.edges = [];
|
|
26
|
+
this.inputEntityId = input.entity.id;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Aggiungi un'entità con edge automatico dall'input
|
|
30
|
+
*
|
|
31
|
+
* @param entity - Entità da aggiungere
|
|
32
|
+
* @param edgeLabel - Label dell'edge (opzionale)
|
|
33
|
+
* @param edgeOptions - Opzioni per l'edge
|
|
34
|
+
*/
|
|
35
|
+
addEntity(entity, edgeLabel, edgeOptions) {
|
|
36
|
+
this.entities.push(entity);
|
|
37
|
+
// Se viene specificata una label, crea automaticamente l'edge
|
|
38
|
+
if (edgeLabel) {
|
|
39
|
+
const edge = (0, builders_1.createEdge)(this.inputEntityId, entity.id, edgeLabel, edgeOptions);
|
|
40
|
+
this.edges.push(edge);
|
|
41
|
+
}
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Aggiungi multiple entità con lo stesso tipo di edge
|
|
46
|
+
*
|
|
47
|
+
* @param entities - Array di entità da aggiungere
|
|
48
|
+
* @param edgeLabel - Label dell'edge (opzionale)
|
|
49
|
+
* @param edgeOptions - Opzioni per l'edge
|
|
50
|
+
*/
|
|
51
|
+
addEntities(entities, edgeLabel, edgeOptions) {
|
|
52
|
+
for (const entity of entities) {
|
|
53
|
+
this.addEntity(entity, edgeLabel, edgeOptions);
|
|
54
|
+
}
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Aggiungi un'entità senza edge automatico
|
|
59
|
+
*/
|
|
60
|
+
addEntityOnly(entity) {
|
|
61
|
+
this.entities.push(entity);
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Aggiungi un edge manualmente
|
|
66
|
+
*/
|
|
67
|
+
addEdge(edge) {
|
|
68
|
+
this.edges.push(edge);
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Aggiungi multiple edges
|
|
73
|
+
*/
|
|
74
|
+
addEdges(edges) {
|
|
75
|
+
this.edges.push(...edges);
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Imposta il messaggio di successo
|
|
80
|
+
*/
|
|
81
|
+
setMessage(message) {
|
|
82
|
+
this.message = message;
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Imposta metadata addizionali
|
|
87
|
+
*/
|
|
88
|
+
setMetadata(metadata) {
|
|
89
|
+
this.metadata = metadata;
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Aggiungi metadata
|
|
94
|
+
*/
|
|
95
|
+
addMetadata(key, value) {
|
|
96
|
+
if (!this.metadata) {
|
|
97
|
+
this.metadata = {};
|
|
98
|
+
}
|
|
99
|
+
this.metadata[key] = value;
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Costruisci il risultato finale
|
|
104
|
+
*/
|
|
105
|
+
build() {
|
|
106
|
+
return {
|
|
107
|
+
success: true,
|
|
108
|
+
entities: this.entities,
|
|
109
|
+
edges: this.edges,
|
|
110
|
+
...(this.message && { message: this.message }),
|
|
111
|
+
...(this.metadata && { metadata: this.metadata }),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Ottieni il numero di entità aggiunte
|
|
116
|
+
*/
|
|
117
|
+
getEntityCount() {
|
|
118
|
+
return this.entities.length;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Ottieni il numero di edge aggiunti
|
|
122
|
+
*/
|
|
123
|
+
getEdgeCount() {
|
|
124
|
+
return this.edges.length;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Verifica se ci sono risultati
|
|
128
|
+
*/
|
|
129
|
+
hasResults() {
|
|
130
|
+
return this.entities.length > 0;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
exports.ResultBuilder = ResultBuilder;
|
|
134
|
+
/**
|
|
135
|
+
* Helper veloce per creare un risultato vuoto (no results)
|
|
136
|
+
*/
|
|
137
|
+
function emptyResult(message) {
|
|
138
|
+
return {
|
|
139
|
+
success: true,
|
|
140
|
+
entities: [],
|
|
141
|
+
edges: [],
|
|
142
|
+
...(message && { message }),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Helper veloce per creare un risultato di errore
|
|
147
|
+
*/
|
|
148
|
+
function errorResult(error) {
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
entities: [],
|
|
152
|
+
edges: [],
|
|
153
|
+
message: error instanceof Error ? error.message : error,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Helper per creare un risultato con una singola entità
|
|
158
|
+
*/
|
|
159
|
+
function singleEntityResult(inputEntityId, entity, edgeLabel, options) {
|
|
160
|
+
const edge = (0, builders_1.createEdge)(inputEntityId, entity.id, edgeLabel, {
|
|
161
|
+
relationship: options?.relationship,
|
|
162
|
+
});
|
|
163
|
+
return {
|
|
164
|
+
success: true,
|
|
165
|
+
entities: [entity],
|
|
166
|
+
edges: [edge],
|
|
167
|
+
...(options?.message && { message: options.message }),
|
|
168
|
+
...(options?.metadata && { metadata: options.metadata }),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Helper per creare un risultato con multiple entità dello stesso tipo
|
|
173
|
+
*/
|
|
174
|
+
function multiEntityResult(inputEntityId, entities, edgeLabel, options) {
|
|
175
|
+
const edges = entities.map(entity => (0, builders_1.createEdge)(inputEntityId, entity.id, edgeLabel, {
|
|
176
|
+
relationship: options?.relationship,
|
|
177
|
+
}));
|
|
178
|
+
return {
|
|
179
|
+
success: true,
|
|
180
|
+
entities,
|
|
181
|
+
edges,
|
|
182
|
+
...(options?.message && { message: options.message }),
|
|
183
|
+
...(options?.metadata && { metadata: options.metadata }),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
# Builders Guide
|
|
2
|
+
|
|
3
|
+
This guide provides detailed documentation on using the SDK's builder utilities to create entities, edges, and transform results.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The SDK provides a scalable, type-agnostic approach to creating plugin responses:
|
|
8
|
+
|
|
9
|
+
- **createEntity()**: Universal entity creation function
|
|
10
|
+
- **createEdge()**: Edge creation with validation
|
|
11
|
+
- **ResultBuilder**: Fluent builder for complex results
|
|
12
|
+
- **EntityBuilder**: Advanced entity construction
|
|
13
|
+
- **EdgeBuilder**: Advanced edge construction
|
|
14
|
+
|
|
15
|
+
## createEntity() - Universal Approach
|
|
16
|
+
|
|
17
|
+
The `createEntity()` function works with any entity type, present or future, eliminating the need for type-specific helpers.
|
|
18
|
+
|
|
19
|
+
### Signature
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
createEntity(
|
|
23
|
+
type: EntityType,
|
|
24
|
+
value: string,
|
|
25
|
+
options?: {
|
|
26
|
+
id?: string;
|
|
27
|
+
label?: string;
|
|
28
|
+
metadata?: Record<string, any>;
|
|
29
|
+
}
|
|
30
|
+
): OSINTEntity
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Examples
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { createEntity } from '@relazio/plugin-sdk';
|
|
37
|
+
|
|
38
|
+
// IP address
|
|
39
|
+
const ip = createEntity('ip', '8.8.8.8', {
|
|
40
|
+
label: 'Google DNS',
|
|
41
|
+
metadata: { country: 'US', isp: 'Google LLC' }
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Domain
|
|
45
|
+
const domain = createEntity('domain', 'google.com', {
|
|
46
|
+
metadata: { tld: 'com', registrar: 'MarkMonitor' }
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Email
|
|
50
|
+
const email = createEntity('email', 'user@example.com', {
|
|
51
|
+
metadata: { verified: true }
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Location
|
|
55
|
+
const location = createEntity('location', 'New York, NY', {
|
|
56
|
+
metadata: {
|
|
57
|
+
latitude: 40.7128,
|
|
58
|
+
longitude: -74.0060,
|
|
59
|
+
country: 'United States'
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Organization
|
|
64
|
+
const org = createEntity('organization', 'Google LLC', {
|
|
65
|
+
metadata: {
|
|
66
|
+
industry: 'Technology',
|
|
67
|
+
website: 'google.com'
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Note with Markdown
|
|
72
|
+
const note = createEntity('note', 'Analysis Results', {
|
|
73
|
+
label: '## Analysis\n\n**Result**: Success',
|
|
74
|
+
metadata: {
|
|
75
|
+
format: 'markdown',
|
|
76
|
+
tags: ['analysis', 'report']
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Custom entity type
|
|
81
|
+
const customEntity = createEntity('my-custom-type', 'value', {
|
|
82
|
+
metadata: { custom: 'data' }
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Advantages
|
|
87
|
+
|
|
88
|
+
- **Scalable**: No SDK updates needed for new entity types
|
|
89
|
+
- **Flexible**: Works with custom entity types
|
|
90
|
+
- **Consistent**: Same syntax for all types
|
|
91
|
+
- **Type-safe**: TypeScript validation for known types
|
|
92
|
+
- **Future-proof**: Compatible with future entity types
|
|
93
|
+
|
|
94
|
+
## EntityBuilder - Advanced Construction
|
|
95
|
+
|
|
96
|
+
For complex entities requiring fine-grained control, use the EntityBuilder class.
|
|
97
|
+
|
|
98
|
+
### Example
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { EntityBuilder } from '@relazio/plugin-sdk';
|
|
102
|
+
|
|
103
|
+
const entity = EntityBuilder
|
|
104
|
+
.create('ip', '8.8.8.8')
|
|
105
|
+
.withLabel('Google DNS')
|
|
106
|
+
.withMetadata({ country: 'US' })
|
|
107
|
+
.addMetadata('isp', 'Google LLC')
|
|
108
|
+
.addMetadata('asn', 'AS15169')
|
|
109
|
+
.build();
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## createEdge() - Edge Creation
|
|
113
|
+
|
|
114
|
+
Creates edges between entities with automatic ID generation.
|
|
115
|
+
|
|
116
|
+
### Signature
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
createEdge(
|
|
120
|
+
sourceId: string,
|
|
121
|
+
targetId: string,
|
|
122
|
+
label: string,
|
|
123
|
+
options?: {
|
|
124
|
+
id?: string;
|
|
125
|
+
relationship?: string;
|
|
126
|
+
metadata?: Record<string, any>;
|
|
127
|
+
}
|
|
128
|
+
): OSINTEdge
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Examples
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { createEdge } from '@relazio/plugin-sdk';
|
|
135
|
+
|
|
136
|
+
// Simple edge
|
|
137
|
+
const edge = createEdge(
|
|
138
|
+
'ip-abc123',
|
|
139
|
+
'location-xyz',
|
|
140
|
+
'located in'
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// Edge with relationship and metadata
|
|
144
|
+
const edge = createEdge(
|
|
145
|
+
'ip-abc123',
|
|
146
|
+
'org-google',
|
|
147
|
+
'assigned by',
|
|
148
|
+
{
|
|
149
|
+
relationship: 'isp_assignment',
|
|
150
|
+
metadata: { confidence: 0.98 }
|
|
151
|
+
}
|
|
152
|
+
);
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## ResultBuilder - Automated Result Construction
|
|
156
|
+
|
|
157
|
+
ResultBuilder simplifies creating transform results by automatically generating edges from the input entity to created entities.
|
|
158
|
+
|
|
159
|
+
### Basic Usage
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import { ResultBuilder } from '@relazio/plugin-sdk';
|
|
163
|
+
|
|
164
|
+
handler: async (input) => {
|
|
165
|
+
const entity = createEntity('ip', '8.8.8.8');
|
|
166
|
+
|
|
167
|
+
return new ResultBuilder(input)
|
|
168
|
+
.addEntity(entity, 'resolves to')
|
|
169
|
+
.setMessage('Resolution successful')
|
|
170
|
+
.build();
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Adding Single Entities
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
const result = new ResultBuilder(input);
|
|
178
|
+
|
|
179
|
+
result.addEntity(entity, 'edge label', {
|
|
180
|
+
relationship: 'custom_relationship',
|
|
181
|
+
metadata: { confidence: 0.95 }
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
return result
|
|
185
|
+
.setMessage('Operation completed')
|
|
186
|
+
.build();
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Adding Multiple Entities
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
const result = new ResultBuilder(input);
|
|
193
|
+
|
|
194
|
+
const entities = ['1.1.1.1', '8.8.8.8'].map(ip =>
|
|
195
|
+
createEntity('ip', ip)
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
result.addEntities(entities, 'resolves to', {
|
|
199
|
+
relationship: 'dns_resolution'
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
return result.build();
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Adding Entities Without Edges
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
const result = new ResultBuilder(input);
|
|
209
|
+
|
|
210
|
+
// Add entity without creating an edge
|
|
211
|
+
result.addEntityOnly(entity);
|
|
212
|
+
|
|
213
|
+
return result.build();
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Adding Custom Edges
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
const result = new ResultBuilder(input);
|
|
220
|
+
|
|
221
|
+
result.addEntityOnly(entity1);
|
|
222
|
+
result.addEntityOnly(entity2);
|
|
223
|
+
|
|
224
|
+
// Create custom edge between entities
|
|
225
|
+
const edge = createEdge(entity1.id, entity2.id, 'related to');
|
|
226
|
+
result.addEdge(edge);
|
|
227
|
+
|
|
228
|
+
return result.build();
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Adding Metadata
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
return new ResultBuilder(input)
|
|
235
|
+
.addEntity(entity, 'label')
|
|
236
|
+
.setMessage('Success')
|
|
237
|
+
.addMetadata('executionTime', 123)
|
|
238
|
+
.addMetadata('source', 'api.example.com')
|
|
239
|
+
.build();
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Common Patterns
|
|
243
|
+
|
|
244
|
+
### Pattern 1: IP Geolocation Lookup
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
handler: async (input) => {
|
|
248
|
+
const ip = input.entity.value;
|
|
249
|
+
const data = await lookupIP(ip);
|
|
250
|
+
|
|
251
|
+
const location = createEntity('location', data.city, {
|
|
252
|
+
metadata: {
|
|
253
|
+
latitude: data.lat,
|
|
254
|
+
longitude: data.lon,
|
|
255
|
+
country: data.country
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
const org = createEntity('organization', data.isp, {
|
|
260
|
+
metadata: { asn: data.asn }
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
return new ResultBuilder(input)
|
|
264
|
+
.addEntity(location, 'located in', {
|
|
265
|
+
relationship: 'geolocation'
|
|
266
|
+
})
|
|
267
|
+
.addEntity(org, 'assigned by', {
|
|
268
|
+
relationship: 'isp_assignment'
|
|
269
|
+
})
|
|
270
|
+
.setMessage(`Analyzed IP ${ip}`)
|
|
271
|
+
.build();
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Pattern 2: DNS Resolution
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
handler: async (input) => {
|
|
279
|
+
const domain = input.entity.value;
|
|
280
|
+
const ips = await resolveDomain(domain);
|
|
281
|
+
|
|
282
|
+
const ipEntities = ips.map(ip => createEntity('ip', ip, {
|
|
283
|
+
metadata: { recordType: 'A' }
|
|
284
|
+
}));
|
|
285
|
+
|
|
286
|
+
return new ResultBuilder(input)
|
|
287
|
+
.addEntities(ipEntities, 'resolves to', {
|
|
288
|
+
relationship: 'dns_resolution'
|
|
289
|
+
})
|
|
290
|
+
.setMessage(`Found ${ips.length} IP address(es)`)
|
|
291
|
+
.build();
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Pattern 3: Analysis with Markdown Notes
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
handler: async (input) => {
|
|
299
|
+
const data = await analyze(input.entity.value);
|
|
300
|
+
|
|
301
|
+
const markdown = `
|
|
302
|
+
## Analysis Results
|
|
303
|
+
|
|
304
|
+
**Status**: ${data.status}
|
|
305
|
+
**Score**: ${data.score}/100
|
|
306
|
+
|
|
307
|
+
### Details
|
|
308
|
+
- Finding 1: ${data.finding1}
|
|
309
|
+
- Finding 2: ${data.finding2}
|
|
310
|
+
`;
|
|
311
|
+
|
|
312
|
+
const note = createEntity('note', 'Analysis Report', {
|
|
313
|
+
label: markdown,
|
|
314
|
+
metadata: {
|
|
315
|
+
format: 'markdown',
|
|
316
|
+
tags: ['analysis', 'report'],
|
|
317
|
+
timestamp: new Date().toISOString()
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
return new ResultBuilder(input)
|
|
322
|
+
.addEntity(note, 'analysis')
|
|
323
|
+
.build();
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Pattern 4: Empty Results
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
import { emptyResult } from '@relazio/plugin-sdk';
|
|
331
|
+
|
|
332
|
+
handler: async (input) => {
|
|
333
|
+
const results = await search(input.entity.value);
|
|
334
|
+
|
|
335
|
+
if (results.length === 0) {
|
|
336
|
+
return emptyResult('No results found');
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Process results...
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Pattern 5: Error Handling
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
import { errorResult } from '@relazio/plugin-sdk';
|
|
347
|
+
|
|
348
|
+
handler: async (input) => {
|
|
349
|
+
try {
|
|
350
|
+
// Transform logic
|
|
351
|
+
} catch (error) {
|
|
352
|
+
throw error; // SDK handles automatically
|
|
353
|
+
|
|
354
|
+
// Or use errorResult for custom handling
|
|
355
|
+
return errorResult(error);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## Validation
|
|
361
|
+
|
|
362
|
+
The SDK automatically validates responses, but manual validation is available.
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
import { validateTransformResult } from '@relazio/plugin-sdk';
|
|
366
|
+
|
|
367
|
+
const result = {
|
|
368
|
+
entities: [/* ... */],
|
|
369
|
+
edges: [/* ... */]
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const validation = validateTransformResult(result, input.entity.id);
|
|
373
|
+
|
|
374
|
+
if (!validation.valid) {
|
|
375
|
+
console.error('Validation errors:', validation.errors);
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## ID Generation
|
|
380
|
+
|
|
381
|
+
IDs are generated automatically using deterministic hashing, but can be customized.
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
import { generateEntityId, generateEdgeId } from '@relazio/plugin-sdk';
|
|
385
|
+
|
|
386
|
+
// Deterministic entity ID
|
|
387
|
+
const id = generateEntityId('ip', '8.8.8.8');
|
|
388
|
+
// Returns: "ip-c909e98d"
|
|
389
|
+
|
|
390
|
+
// Deterministic edge ID
|
|
391
|
+
const edgeId = generateEdgeId('source-id', 'target-id', 'label');
|
|
392
|
+
// Returns: "edge-4f8a2d1c"
|
|
393
|
+
|
|
394
|
+
// Custom ID
|
|
395
|
+
const entity = createEntity('ip', '8.8.8.8', {
|
|
396
|
+
id: 'my-custom-id'
|
|
397
|
+
});
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## Best Practices
|
|
401
|
+
|
|
402
|
+
### Recommended Approach
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
// Use createEntity() for scalability
|
|
406
|
+
const ip = createEntity('ip', '8.8.8.8');
|
|
407
|
+
|
|
408
|
+
// Use ResultBuilder for complex results
|
|
409
|
+
return new ResultBuilder(input)
|
|
410
|
+
.addEntity(location, 'located in')
|
|
411
|
+
.build();
|
|
412
|
+
|
|
413
|
+
// Add meaningful metadata
|
|
414
|
+
createEntity('location', 'NYC', {
|
|
415
|
+
metadata: { latitude: 40.7, longitude: -74.0 }
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// Use descriptive labels
|
|
419
|
+
createEntity('domain', 'mx1.google.com', {
|
|
420
|
+
label: 'mx1.google.com (priority: 10)'
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
// Works with custom types
|
|
424
|
+
createEntity('my-custom-type', 'value', {
|
|
425
|
+
metadata: { custom: 'data' }
|
|
426
|
+
});
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Common Mistakes
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
// Incorrect: Creating entities manually without IDs
|
|
433
|
+
const entity = { type: 'ip', value: '8.8.8.8' };
|
|
434
|
+
|
|
435
|
+
// Correct: Use createEntity()
|
|
436
|
+
const entity = createEntity('ip', '8.8.8.8');
|
|
437
|
+
|
|
438
|
+
// Incorrect: Empty edge label
|
|
439
|
+
createEdge('source-id', 'target-id', '');
|
|
440
|
+
|
|
441
|
+
// Correct: Descriptive label
|
|
442
|
+
createEdge('source-id', 'target-id', 'related to');
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
## Helper Functions
|
|
446
|
+
|
|
447
|
+
### Result Helpers
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
// Empty result
|
|
451
|
+
emptyResult(message?: string): TransformResult
|
|
452
|
+
|
|
453
|
+
// Error result
|
|
454
|
+
errorResult(error: string | Error): TransformResult
|
|
455
|
+
|
|
456
|
+
// Single entity result
|
|
457
|
+
singleEntityResult(
|
|
458
|
+
inputEntityId: string,
|
|
459
|
+
entity: OSINTEntity,
|
|
460
|
+
edgeLabel: string,
|
|
461
|
+
options?: {
|
|
462
|
+
relationship?: string;
|
|
463
|
+
message?: string;
|
|
464
|
+
metadata?: Record<string, any>;
|
|
465
|
+
}
|
|
466
|
+
): TransformResult
|
|
467
|
+
|
|
468
|
+
// Multi-entity result
|
|
469
|
+
multiEntityResult(
|
|
470
|
+
inputEntityId: string,
|
|
471
|
+
entities: OSINTEntity[],
|
|
472
|
+
edgeLabel: string,
|
|
473
|
+
options?: {
|
|
474
|
+
relationship?: string;
|
|
475
|
+
message?: string;
|
|
476
|
+
metadata?: Record<string, any>;
|
|
477
|
+
}
|
|
478
|
+
): TransformResult
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Validation Helpers
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
validateEntities(entities: OSINTEntity[]): { valid: boolean; errors: string[] }
|
|
485
|
+
|
|
486
|
+
validateEdges(
|
|
487
|
+
edges: OSINTEdge[],
|
|
488
|
+
entities: OSINTEntity[],
|
|
489
|
+
inputEntityId?: string
|
|
490
|
+
): { valid: boolean; errors: string[] }
|
|
491
|
+
|
|
492
|
+
validateTransformResult(
|
|
493
|
+
result: { entities: OSINTEntity[]; edges: OSINTEdge[] },
|
|
494
|
+
inputEntityId?: string
|
|
495
|
+
): { valid: boolean; errors: string[] }
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### ID Generation Helpers
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
generateEntityId(type: EntityType, value: string): string
|
|
502
|
+
|
|
503
|
+
generateEdgeId(sourceId: string, targetId: string, label?: string): string
|
|
504
|
+
|
|
505
|
+
generateRandomId(prefix: string): string
|
|
506
|
+
|
|
507
|
+
normalizeValue(value: string): string
|
|
508
|
+
|
|
509
|
+
isValidEntityId(id: string): boolean
|
|
510
|
+
|
|
511
|
+
isValidEdgeId(id: string): boolean
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
## Additional Resources
|
|
515
|
+
|
|
516
|
+
- [Quick Start Guide](./quick-start.md)
|
|
517
|
+
- [Response Format Specification](./response-format.md)
|
|
518
|
+
- [Examples Documentation](./examples.md)
|
|
519
|
+
- [Main README](../README.md)
|
|
520
|
+
|