-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathadapter-implementation.ts
More file actions
181 lines (148 loc) · 6.1 KB
/
adapter-implementation.ts
File metadata and controls
181 lines (148 loc) · 6.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/**
* Custom adapter implementation example.
*
* Demonstrates implementing the IssueTracker interface from scratch,
* registering it, and using the webhook bridge.
*
* Spec reference: RFC-0003 (Infrastructure Provider Adapters) §3 — Sandbox
* and the broader adapter interface enum extension. The IssueTracker shown
* here is one of the SDLC interfaces that AdapterBinding has covered since
* v1alpha1; RFC-0003 extends the same pattern to the five infrastructure
* concerns (AuditSink, Sandbox, SecretStore, MemoryStore, EventBus).
*
* Run: npx tsx docs/examples/adapter-implementation.ts
*/
import type { IssueTracker, Issue, IssueFilter, EventStream } from '@ai-sdlc/reference';
import {
createAdapterRegistry,
createWebhookBridge,
createInProcessEventBus,
AdapterBindingBuilder,
validateResource,
} from '@ai-sdlc/reference';
// ── 1. Implement the IssueTracker interface ───────────────────────────
interface InMemoryConfig {
projectKey: string;
}
function createInMemoryIssueTracker(config: InMemoryConfig): IssueTracker {
const issues = new Map<string, Issue>();
let counter = 0;
return {
async listIssues(filter: IssueFilter): Promise<Issue[]> {
return Array.from(issues.values()).filter((issue) => {
if (filter.status && issue.status !== filter.status) return false;
if (filter.assignee && issue.assignee !== filter.assignee) return false;
if (filter.labels?.length) {
const issueLabels = issue.labels ?? [];
if (!filter.labels.some((l) => issueLabels.includes(l))) return false;
}
return true;
});
},
async getIssue(id: string): Promise<Issue> {
const issue = issues.get(id);
if (!issue) throw new Error(`Issue ${id} not found`);
return issue;
},
async createIssue(input): Promise<Issue> {
const id = `${config.projectKey}-${++counter}`;
const issue: Issue = {
id,
title: input.title,
description: input.description,
status: 'open',
labels: input.labels,
assignee: input.assignee,
url: `https://example.com/issues/${id}`,
};
issues.set(id, issue);
return issue;
},
async updateIssue(id: string, input): Promise<Issue> {
const issue = issues.get(id);
if (!issue) throw new Error(`Issue ${id} not found`);
const updated: Issue = {
...issue,
...(input.title && { title: input.title }),
...(input.description && { description: input.description }),
...(input.labels && { labels: input.labels }),
...(input.assignee && { assignee: input.assignee }),
};
issues.set(id, updated);
return updated;
},
async transitionIssue(id: string, transition: string): Promise<Issue> {
const issue = issues.get(id);
if (!issue) throw new Error(`Issue ${id} not found`);
const updated = { ...issue, status: transition };
issues.set(id, updated);
return updated;
},
async addComment(_id: string, _body: string): Promise<void> {
// No-op for in-memory adapter
},
async getComments(_id: string) {
return [];
},
watchIssues(_filter: IssueFilter): EventStream<any> {
throw new Error('Watch not supported in in-memory adapter');
},
};
}
// ── 2. Register the adapter ───────────────────────────────────────────
const registry = createAdapterRegistry();
registry.register(
{
name: 'in-memory',
interface: 'IssueTracker',
type: 'in-memory',
version: '1.0.0',
stability: 'experimental',
description: 'In-memory issue tracker for testing and examples',
},
(config) => createInMemoryIssueTracker(config as InMemoryConfig),
);
// List registered adapters
console.log(
'Registered adapters:',
registry.list().map((a) => `${a.interface}/${a.name}`),
);
// ── 3. Instantiate and use ────────────────────────────────────────────
const factory = registry.get('IssueTracker', 'in-memory')!;
const tracker = factory({ projectKey: 'TEST' }) as IssueTracker;
const created = await tracker.createIssue({
title: 'Add user authentication',
description: 'Implement JWT-based auth with login/logout endpoints',
labels: ['feature', 'ai-eligible'],
});
console.log('Created:', created.id, '-', created.title);
await tracker.transitionIssue(created.id, 'in-progress');
const updated = await tracker.getIssue(created.id);
console.log('Status:', updated.status);
const openIssues = await tracker.listIssues({ status: 'in-progress' });
console.log('In-progress issues:', openIssues.length);
// ── 4. Webhook bridge ─────────────────────────────────────────────────
const bridge = createWebhookBridge();
bridge.transform('custom:issue_updated', (payload: any) => ({
type: 'updated',
issue: payload.issue,
timestamp: new Date().toISOString(),
}));
bridge.on('custom:issue_updated', (event) => {
console.log('Webhook event received:', (event as any).type);
});
bridge.emit('custom:issue_updated', { issue: updated });
// ── 5. In-process EventBus ────────────────────────────────────────────
const bus = createInProcessEventBus();
const unsub = bus.subscribe('issue.created', (payload) => {
console.log('EventBus — issue.created:', (payload as any).id);
});
await bus.publish('issue.created', { id: created.id, title: created.title });
unsub();
// ── 6. AdapterBinding resource ────────────────────────────────────────
const binding = new AdapterBindingBuilder('in-memory-tracker', 'IssueTracker', 'in-memory', '1.0.0')
.label('adapter', 'in-memory')
.config({ projectKey: 'TEST' })
.build();
const result = validateResource(binding);
console.log('AdapterBinding valid:', result.valid);