@objectstack/core 0.8.2 → 0.9.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.
- package/API_REGISTRY.md +392 -0
- package/CHANGELOG.md +8 -0
- package/README.md +36 -0
- package/dist/api-registry-plugin.d.ts +54 -0
- package/dist/api-registry-plugin.d.ts.map +1 -0
- package/dist/api-registry-plugin.js +53 -0
- package/dist/api-registry-plugin.test.d.ts +2 -0
- package/dist/api-registry-plugin.test.d.ts.map +1 -0
- package/dist/api-registry-plugin.test.js +332 -0
- package/dist/api-registry.d.ts +259 -0
- package/dist/api-registry.d.ts.map +1 -0
- package/dist/api-registry.js +599 -0
- package/dist/api-registry.test.d.ts +2 -0
- package/dist/api-registry.test.d.ts.map +1 -0
- package/dist/api-registry.test.js +957 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +35 -11
- package/dist/plugin-loader.d.ts +3 -2
- package/dist/plugin-loader.d.ts.map +1 -1
- package/dist/plugin-loader.js +13 -11
- package/dist/qa/adapter.d.ts +14 -0
- package/dist/qa/adapter.d.ts.map +1 -0
- package/dist/qa/adapter.js +1 -0
- package/dist/qa/http-adapter.d.ts +16 -0
- package/dist/qa/http-adapter.d.ts.map +1 -0
- package/dist/qa/http-adapter.js +107 -0
- package/dist/qa/index.d.ts +4 -0
- package/dist/qa/index.d.ts.map +1 -0
- package/dist/qa/index.js +3 -0
- package/dist/qa/runner.d.ts +27 -0
- package/dist/qa/runner.d.ts.map +1 -0
- package/dist/qa/runner.js +157 -0
- package/dist/security/index.d.ts +14 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +13 -0
- package/dist/security/plugin-config-validator.d.ts +79 -0
- package/dist/security/plugin-config-validator.d.ts.map +1 -0
- package/dist/security/plugin-config-validator.js +166 -0
- package/dist/security/plugin-config-validator.test.d.ts +2 -0
- package/dist/security/plugin-config-validator.test.d.ts.map +1 -0
- package/dist/security/plugin-config-validator.test.js +223 -0
- package/dist/security/plugin-permission-enforcer.d.ts +154 -0
- package/dist/security/plugin-permission-enforcer.d.ts.map +1 -0
- package/dist/security/plugin-permission-enforcer.js +323 -0
- package/dist/security/plugin-permission-enforcer.test.d.ts +2 -0
- package/dist/security/plugin-permission-enforcer.test.d.ts.map +1 -0
- package/dist/security/plugin-permission-enforcer.test.js +205 -0
- package/dist/security/plugin-signature-verifier.d.ts +96 -0
- package/dist/security/plugin-signature-verifier.d.ts.map +1 -0
- package/dist/security/plugin-signature-verifier.js +250 -0
- package/examples/api-registry-example.ts +557 -0
- package/package.json +2 -2
- package/src/api-registry-plugin.test.ts +391 -0
- package/src/api-registry-plugin.ts +86 -0
- package/src/api-registry.test.ts +1089 -0
- package/src/api-registry.ts +736 -0
- package/src/index.ts +6 -0
- package/src/logger.ts +36 -11
- package/src/plugin-loader.ts +17 -13
- package/src/qa/adapter.ts +14 -0
- package/src/qa/http-adapter.ts +114 -0
- package/src/qa/index.ts +3 -0
- package/src/qa/runner.ts +179 -0
- package/src/security/index.ts +29 -0
- package/src/security/plugin-config-validator.test.ts +276 -0
- package/src/security/plugin-config-validator.ts +191 -0
- package/src/security/plugin-permission-enforcer.test.ts +251 -0
- package/src/security/plugin-permission-enforcer.ts +408 -0
- package/src/security/plugin-signature-verifier.ts +359 -0
package/API_REGISTRY.md
ADDED
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
# API Registry Implementation
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The API Registry is a centralized service in the ObjectStack kernel that manages API endpoint registration, discovery, and conflict resolution across different protocols and plugins.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
✅ **Multi-Protocol Support** - REST, GraphQL, OData, WebSocket, Plugin APIs, and more
|
|
10
|
+
✅ **Route Conflict Detection** - Configurable strategies (error, priority, first-wins, last-wins)
|
|
11
|
+
✅ **RBAC Integration** - Endpoints can specify required permissions
|
|
12
|
+
✅ **Dynamic Schema Linking** - Reference ObjectQL objects for auto-updating schemas
|
|
13
|
+
✅ **Protocol Extensions** - Support for gRPC, tRPC, and custom protocols
|
|
14
|
+
✅ **API Discovery** - Filter and search APIs by type, status, tags, and more
|
|
15
|
+
|
|
16
|
+
## Architecture
|
|
17
|
+
|
|
18
|
+
The API Registry follows the ObjectStack microkernel pattern:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
┌─────────────────────────────────────────────────────┐
|
|
22
|
+
│ ObjectKernel (Core) │
|
|
23
|
+
│ ┌───────────────────────────────────────────────┐ │
|
|
24
|
+
│ │ Service Registry (DI Container) │ │
|
|
25
|
+
│ │ ┌─────────────────────────────────────────┐ │ │
|
|
26
|
+
│ │ │ API Registry Service │ │ │
|
|
27
|
+
│ │ │ • registerApi() │ │ │
|
|
28
|
+
│ │ │ • unregisterApi() │ │ │
|
|
29
|
+
│ │ │ • findApis() │ │ │
|
|
30
|
+
│ │ │ • getRegistry() │ │ │
|
|
31
|
+
│ │ └─────────────────────────────────────────┘ │ │
|
|
32
|
+
│ └───────────────────────────────────────────────┘ │
|
|
33
|
+
└─────────────────────────────────────────────────────┘
|
|
34
|
+
│
|
|
35
|
+
┌─────────┴─────────┬──────────┬──────────┐
|
|
36
|
+
│ │ │ │
|
|
37
|
+
┌───▼────┐ ┌───────▼──┐ ┌──▼───┐ ┌───▼────┐
|
|
38
|
+
│ REST │ │ GraphQL │ │WebSkt│ │ Plugin │
|
|
39
|
+
│ Plugin │ │ Plugin │ │Plugin│ │ APIs │
|
|
40
|
+
└────────┘ └──────────┘ └──────┘ └────────┘
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
### 1. Register the API Registry Plugin
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { ObjectKernel, createApiRegistryPlugin } from '@objectstack/core';
|
|
49
|
+
|
|
50
|
+
const kernel = new ObjectKernel();
|
|
51
|
+
|
|
52
|
+
// Register with default settings (error on conflicts)
|
|
53
|
+
kernel.use(createApiRegistryPlugin());
|
|
54
|
+
|
|
55
|
+
// Or with custom configuration
|
|
56
|
+
kernel.use(
|
|
57
|
+
createApiRegistryPlugin({
|
|
58
|
+
conflictResolution: 'priority', // priority, first-wins, last-wins
|
|
59
|
+
version: '1.0.0',
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
await kernel.bootstrap();
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 2. Register APIs in Plugins
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import type { Plugin } from '@objectstack/core';
|
|
70
|
+
import type { ApiRegistry } from '@objectstack/core';
|
|
71
|
+
import type { ApiRegistryEntry } from '@objectstack/spec/api';
|
|
72
|
+
|
|
73
|
+
const myPlugin: Plugin = {
|
|
74
|
+
name: 'my-plugin',
|
|
75
|
+
version: '1.0.0',
|
|
76
|
+
|
|
77
|
+
init: async (ctx) => {
|
|
78
|
+
// Get the API Registry service
|
|
79
|
+
const registry = ctx.getService<ApiRegistry>('api-registry');
|
|
80
|
+
|
|
81
|
+
// Register your API
|
|
82
|
+
const api: ApiRegistryEntry = {
|
|
83
|
+
id: 'customer_api',
|
|
84
|
+
name: 'Customer API',
|
|
85
|
+
type: 'rest',
|
|
86
|
+
version: 'v1',
|
|
87
|
+
basePath: '/api/v1/customers',
|
|
88
|
+
endpoints: [
|
|
89
|
+
{
|
|
90
|
+
id: 'get_customer',
|
|
91
|
+
method: 'GET',
|
|
92
|
+
path: '/api/v1/customers/:id',
|
|
93
|
+
summary: 'Get customer by ID',
|
|
94
|
+
requiredPermissions: ['customer.read'], // RBAC
|
|
95
|
+
parameters: [
|
|
96
|
+
{
|
|
97
|
+
name: 'id',
|
|
98
|
+
in: 'path',
|
|
99
|
+
required: true,
|
|
100
|
+
schema: { type: 'string', format: 'uuid' },
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
responses: [
|
|
104
|
+
{
|
|
105
|
+
statusCode: 200,
|
|
106
|
+
description: 'Customer found',
|
|
107
|
+
schema: {
|
|
108
|
+
$ref: {
|
|
109
|
+
objectId: 'customer', // Dynamic ObjectQL reference
|
|
110
|
+
excludeFields: ['password_hash'],
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
metadata: {
|
|
118
|
+
status: 'active',
|
|
119
|
+
tags: ['customer', 'crm'],
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
registry.registerApi(api);
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 3. Discover APIs
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
const registry = kernel.getService<ApiRegistry>('api-registry');
|
|
132
|
+
|
|
133
|
+
// Get all APIs
|
|
134
|
+
const allApis = registry.getAllApis();
|
|
135
|
+
|
|
136
|
+
// Find REST APIs
|
|
137
|
+
const restApis = registry.findApis({ type: 'rest' });
|
|
138
|
+
|
|
139
|
+
// Find active APIs with specific tags
|
|
140
|
+
const crmApis = registry.findApis({
|
|
141
|
+
status: 'active',
|
|
142
|
+
tags: ['crm'],
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Search by name
|
|
146
|
+
const searchResults = registry.findApis({
|
|
147
|
+
search: 'customer',
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Get endpoint by route
|
|
151
|
+
const endpoint = registry.findEndpointByRoute('GET', '/api/v1/customers/:id');
|
|
152
|
+
console.log(endpoint?.api.name); // "Customer API"
|
|
153
|
+
console.log(endpoint?.endpoint.summary); // "Get customer by ID"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 4. Get Registry Snapshot
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const registry = kernel.getService<ApiRegistry>('api-registry');
|
|
160
|
+
const snapshot = registry.getRegistry();
|
|
161
|
+
|
|
162
|
+
console.log(`Total APIs: ${snapshot.totalApis}`);
|
|
163
|
+
console.log(`Total Endpoints: ${snapshot.totalEndpoints}`);
|
|
164
|
+
console.log(`Conflict Resolution: ${snapshot.conflictResolution}`);
|
|
165
|
+
|
|
166
|
+
// APIs grouped by type
|
|
167
|
+
snapshot.byType?.rest.forEach((api) => {
|
|
168
|
+
console.log(`REST API: ${api.name}`);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// APIs grouped by status
|
|
172
|
+
snapshot.byStatus?.active.forEach((api) => {
|
|
173
|
+
console.log(`Active API: ${api.name}`);
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Conflict Resolution Strategies
|
|
178
|
+
|
|
179
|
+
### 1. Error (Default)
|
|
180
|
+
|
|
181
|
+
Throws an error when a route conflict is detected.
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
kernel.use(createApiRegistryPlugin({ conflictResolution: 'error' }));
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Best for:** Production environments where conflicts should be caught early.
|
|
188
|
+
|
|
189
|
+
### 2. Priority
|
|
190
|
+
|
|
191
|
+
Uses the `priority` field on endpoints to resolve conflicts. Higher priority wins.
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
kernel.use(createApiRegistryPlugin({ conflictResolution: 'priority' }));
|
|
195
|
+
|
|
196
|
+
// In your plugin
|
|
197
|
+
registry.registerApi({
|
|
198
|
+
endpoints: [
|
|
199
|
+
{
|
|
200
|
+
path: '/api/data/:object',
|
|
201
|
+
priority: 900, // Core API (high priority)
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Priority Ranges:**
|
|
208
|
+
- **900-1000**: Core system endpoints
|
|
209
|
+
- **500-900**: Custom/override endpoints
|
|
210
|
+
- **100-500**: Plugin endpoints
|
|
211
|
+
- **0-100**: Fallback routes
|
|
212
|
+
|
|
213
|
+
### 3. First-Wins
|
|
214
|
+
|
|
215
|
+
First registered endpoint wins. Subsequent registrations are ignored.
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
kernel.use(createApiRegistryPlugin({ conflictResolution: 'first-wins' }));
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Best for:** Stable, predictable routing where load order matters.
|
|
222
|
+
|
|
223
|
+
### 4. Last-Wins
|
|
224
|
+
|
|
225
|
+
Last registered endpoint wins. Previous registrations are overwritten.
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
kernel.use(createApiRegistryPlugin({ conflictResolution: 'last-wins' }));
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Best for:** Development/testing where you want to override defaults.
|
|
232
|
+
|
|
233
|
+
## RBAC Integration
|
|
234
|
+
|
|
235
|
+
Endpoints can specify required permissions that are automatically validated at the gateway level:
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
{
|
|
239
|
+
id: 'delete_customer',
|
|
240
|
+
method: 'DELETE',
|
|
241
|
+
path: '/api/v1/customers/:id',
|
|
242
|
+
requiredPermissions: [
|
|
243
|
+
'customer.delete',
|
|
244
|
+
'api_enabled',
|
|
245
|
+
],
|
|
246
|
+
responses: [],
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Permission Format:**
|
|
251
|
+
- **Object Permissions:** `<object>.<operation>` (e.g., `customer.read`, `order.delete`)
|
|
252
|
+
- **System Permissions:** `<permission_name>` (e.g., `manage_users`, `api_enabled`)
|
|
253
|
+
|
|
254
|
+
## Dynamic Schema Linking
|
|
255
|
+
|
|
256
|
+
Reference ObjectQL objects instead of static schemas:
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
{
|
|
260
|
+
statusCode: 200,
|
|
261
|
+
description: 'Customer retrieved',
|
|
262
|
+
schema: {
|
|
263
|
+
$ref: {
|
|
264
|
+
objectId: 'customer', // ObjectQL object name
|
|
265
|
+
excludeFields: ['password_hash'], // Exclude sensitive fields
|
|
266
|
+
includeFields: ['id', 'name'], // Or whitelist specific fields
|
|
267
|
+
includeRelated: ['account'], // Include related objects
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Benefits:**
|
|
274
|
+
- API documentation auto-updates when object schemas change
|
|
275
|
+
- No schema duplication between API and data model
|
|
276
|
+
- Consistent type definitions across API and database
|
|
277
|
+
|
|
278
|
+
## Protocol-Specific Configuration
|
|
279
|
+
|
|
280
|
+
Support custom protocols with `protocolConfig`:
|
|
281
|
+
|
|
282
|
+
### WebSocket
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
{
|
|
286
|
+
id: 'customer_updates',
|
|
287
|
+
path: '/ws/customers',
|
|
288
|
+
protocolConfig: {
|
|
289
|
+
subProtocol: 'websocket',
|
|
290
|
+
eventName: 'customer.updated',
|
|
291
|
+
direction: 'server-to-client',
|
|
292
|
+
},
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### gRPC
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
{
|
|
300
|
+
id: 'grpc_method',
|
|
301
|
+
path: '/grpc/CustomerService/GetCustomer',
|
|
302
|
+
protocolConfig: {
|
|
303
|
+
subProtocol: 'grpc',
|
|
304
|
+
serviceName: 'CustomerService',
|
|
305
|
+
methodName: 'GetCustomer',
|
|
306
|
+
streaming: false,
|
|
307
|
+
},
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### tRPC
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
{
|
|
315
|
+
id: 'trpc_query',
|
|
316
|
+
path: '/trpc/customer.get',
|
|
317
|
+
protocolConfig: {
|
|
318
|
+
subProtocol: 'trpc',
|
|
319
|
+
procedureType: 'query',
|
|
320
|
+
router: 'customer',
|
|
321
|
+
},
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## API Registry Methods
|
|
326
|
+
|
|
327
|
+
### Registration
|
|
328
|
+
|
|
329
|
+
- `registerApi(api: ApiRegistryEntry): void` - Register an API
|
|
330
|
+
- `unregisterApi(apiId: string): void` - Unregister an API
|
|
331
|
+
|
|
332
|
+
### Discovery
|
|
333
|
+
|
|
334
|
+
- `getApi(apiId: string): ApiRegistryEntry | undefined` - Get API by ID
|
|
335
|
+
- `getAllApis(): ApiRegistryEntry[]` - Get all registered APIs
|
|
336
|
+
- `findApis(query: ApiDiscoveryQuery): ApiDiscoveryResponse` - Search/filter APIs
|
|
337
|
+
- `getEndpoint(apiId: string, endpointId: string): ApiEndpointRegistration | undefined` - Get specific endpoint
|
|
338
|
+
- `findEndpointByRoute(method: string, path: string): { api, endpoint } | undefined` - Find endpoint by route
|
|
339
|
+
|
|
340
|
+
### Registry Info
|
|
341
|
+
|
|
342
|
+
- `getRegistry(): ApiRegistry` - Get complete registry snapshot
|
|
343
|
+
- `getStats(): RegistryStats` - Get registry statistics
|
|
344
|
+
- `clear(): void` - Clear all registered APIs (for testing)
|
|
345
|
+
|
|
346
|
+
## Examples
|
|
347
|
+
|
|
348
|
+
See [api-registry-example.ts](./examples/api-registry-example.ts) for comprehensive examples:
|
|
349
|
+
|
|
350
|
+
1. **Basic API Registration** - Simple REST API with CRUD endpoints
|
|
351
|
+
2. **Multi-Plugin Discovery** - Multiple plugins registering different API types
|
|
352
|
+
3. **Route Conflict Resolution** - Priority-based conflict handling
|
|
353
|
+
4. **Custom Protocol Support** - WebSocket API with protocol config
|
|
354
|
+
5. **Dynamic Schema Linking** - ObjectQL reference in API responses
|
|
355
|
+
|
|
356
|
+
## Testing
|
|
357
|
+
|
|
358
|
+
Run the API Registry tests:
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
pnpm --filter @objectstack/core test api-registry.test.ts
|
|
362
|
+
pnpm --filter @objectstack/core test api-registry-plugin.test.ts
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
**Test Coverage:**
|
|
366
|
+
- ✅ 32 tests for ApiRegistry service
|
|
367
|
+
- ✅ 9 tests for API Registry plugin
|
|
368
|
+
- ✅ All conflict resolution strategies
|
|
369
|
+
- ✅ Multi-protocol support
|
|
370
|
+
- ✅ API discovery and filtering
|
|
371
|
+
- ✅ Integration with kernel lifecycle
|
|
372
|
+
|
|
373
|
+
## Next Steps
|
|
374
|
+
|
|
375
|
+
Based on [API_REGISTRY_ENHANCEMENTS.md](../../API_REGISTRY_ENHANCEMENTS.md), recommended next implementations:
|
|
376
|
+
|
|
377
|
+
1. **API Explorer Plugin** - UI to visualize the registry
|
|
378
|
+
2. **Gateway Integration** - Implement permission checking in API gateway
|
|
379
|
+
3. **Schema Resolution** - Build engine to resolve ObjectQL references to JSON schemas
|
|
380
|
+
4. **Conflict Detection UI** - Visualization of route conflicts and priorities
|
|
381
|
+
5. **Plugin Examples** - Reference implementations for gRPC and tRPC plugins
|
|
382
|
+
|
|
383
|
+
## Related Documentation
|
|
384
|
+
|
|
385
|
+
- [API Registry Schema](../spec/src/api/registry.zod.ts) - Zod schema definitions
|
|
386
|
+
- [API Registry Tests](./src/api-registry.test.ts) - Comprehensive test suite
|
|
387
|
+
- [Plugin System](./README.md) - ObjectStack plugin architecture
|
|
388
|
+
- [Microkernel Design](../../ARCHITECTURE.md) - Overall architecture
|
|
389
|
+
|
|
390
|
+
## License
|
|
391
|
+
|
|
392
|
+
MIT
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @objectstack/core
|
|
2
2
|
|
|
3
|
+
## 0.9.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Patch release for maintenance and stability improvements. All packages updated with unified versioning.
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @objectstack/spec@0.9.1
|
|
10
|
+
|
|
3
11
|
## 0.8.2
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -82,6 +82,42 @@ const service = kernel.getService('my-service');
|
|
|
82
82
|
await kernel.shutdown();
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
+
## 🤖 AI Quick Reference
|
|
86
|
+
|
|
87
|
+
**For AI Agents:** This package implements a microkernel architecture. Key concepts:
|
|
88
|
+
|
|
89
|
+
1. **Plugin Lifecycle**: `init()` → `start()` → `destroy()`
|
|
90
|
+
2. **Service Registry**: Share functionality via `ctx.registerService(name, service)` and `ctx.getService(name)`
|
|
91
|
+
3. **Dependencies**: Declare plugin dependencies for automatic load ordering
|
|
92
|
+
4. **Hooks/Events**: Decouple plugins with `ctx.hook(event, handler)` and `ctx.trigger(event, ...args)`
|
|
93
|
+
5. **Logger**: Always use `ctx.logger` for consistent, structured logging
|
|
94
|
+
|
|
95
|
+
**Common Plugin Pattern:**
|
|
96
|
+
```typescript
|
|
97
|
+
const plugin: Plugin = {
|
|
98
|
+
name: 'my-plugin',
|
|
99
|
+
dependencies: ['other-plugin'], // Load after these plugins
|
|
100
|
+
|
|
101
|
+
async init(ctx: PluginContext) {
|
|
102
|
+
// Register services and hooks
|
|
103
|
+
const otherService = ctx.getService('other-service');
|
|
104
|
+
ctx.registerService('my-service', new MyService(otherService));
|
|
105
|
+
ctx.hook('data:created', async (data) => { /* ... */ });
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
async start(ctx: PluginContext) {
|
|
109
|
+
// Execute business logic
|
|
110
|
+
const service = ctx.getService('my-service');
|
|
111
|
+
await service.initialize();
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
async destroy() {
|
|
115
|
+
// Cleanup resources
|
|
116
|
+
await service.close();
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
```
|
|
120
|
+
|
|
85
121
|
## Configurable Logger
|
|
86
122
|
|
|
87
123
|
The logger uses **[Pino](https://github.com/pinojs/pino)** for Node.js environments (high-performance, low-overhead) and a simple console-based logger for browsers. It automatically detects the runtime environment.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Plugin } from './types.js';
|
|
2
|
+
import type { ConflictResolutionStrategy } from '@objectstack/spec/api';
|
|
3
|
+
/**
|
|
4
|
+
* API Registry Plugin Configuration
|
|
5
|
+
*/
|
|
6
|
+
export interface ApiRegistryPluginConfig {
|
|
7
|
+
/**
|
|
8
|
+
* Conflict resolution strategy for route conflicts
|
|
9
|
+
* @default 'error'
|
|
10
|
+
*/
|
|
11
|
+
conflictResolution?: ConflictResolutionStrategy;
|
|
12
|
+
/**
|
|
13
|
+
* Registry version
|
|
14
|
+
* @default '1.0.0'
|
|
15
|
+
*/
|
|
16
|
+
version?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* API Registry Plugin
|
|
20
|
+
*
|
|
21
|
+
* Registers the API Registry service in the kernel, making it available
|
|
22
|
+
* to all plugins for endpoint registration and discovery.
|
|
23
|
+
*
|
|
24
|
+
* **Usage:**
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const kernel = new ObjectKernel();
|
|
27
|
+
*
|
|
28
|
+
* // Register API Registry Plugin
|
|
29
|
+
* kernel.use(createApiRegistryPlugin({ conflictResolution: 'priority' }));
|
|
30
|
+
*
|
|
31
|
+
* // In other plugins, access the API Registry
|
|
32
|
+
* const plugin: Plugin = {
|
|
33
|
+
* name: 'my-plugin',
|
|
34
|
+
* init: async (ctx) => {
|
|
35
|
+
* const registry = ctx.getService<ApiRegistry>('api-registry');
|
|
36
|
+
*
|
|
37
|
+
* // Register plugin APIs
|
|
38
|
+
* registry.registerApi({
|
|
39
|
+
* id: 'my_plugin_api',
|
|
40
|
+
* name: 'My Plugin API',
|
|
41
|
+
* type: 'rest',
|
|
42
|
+
* version: 'v1',
|
|
43
|
+
* basePath: '/api/v1/my-plugin',
|
|
44
|
+
* endpoints: [...]
|
|
45
|
+
* });
|
|
46
|
+
* }
|
|
47
|
+
* };
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* @param config - Plugin configuration
|
|
51
|
+
* @returns Plugin instance
|
|
52
|
+
*/
|
|
53
|
+
export declare function createApiRegistryPlugin(config?: ApiRegistryPluginConfig): Plugin;
|
|
54
|
+
//# sourceMappingURL=api-registry-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-registry-plugin.d.ts","sourceRoot":"","sources":["../src/api-registry-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,YAAY,CAAC;AAExD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AAExE;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,0BAA0B,CAAC;IAEhD;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,GAAE,uBAA4B,GACnC,MAAM,CA2BR"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ApiRegistry } from './api-registry.js';
|
|
2
|
+
/**
|
|
3
|
+
* API Registry Plugin
|
|
4
|
+
*
|
|
5
|
+
* Registers the API Registry service in the kernel, making it available
|
|
6
|
+
* to all plugins for endpoint registration and discovery.
|
|
7
|
+
*
|
|
8
|
+
* **Usage:**
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const kernel = new ObjectKernel();
|
|
11
|
+
*
|
|
12
|
+
* // Register API Registry Plugin
|
|
13
|
+
* kernel.use(createApiRegistryPlugin({ conflictResolution: 'priority' }));
|
|
14
|
+
*
|
|
15
|
+
* // In other plugins, access the API Registry
|
|
16
|
+
* const plugin: Plugin = {
|
|
17
|
+
* name: 'my-plugin',
|
|
18
|
+
* init: async (ctx) => {
|
|
19
|
+
* const registry = ctx.getService<ApiRegistry>('api-registry');
|
|
20
|
+
*
|
|
21
|
+
* // Register plugin APIs
|
|
22
|
+
* registry.registerApi({
|
|
23
|
+
* id: 'my_plugin_api',
|
|
24
|
+
* name: 'My Plugin API',
|
|
25
|
+
* type: 'rest',
|
|
26
|
+
* version: 'v1',
|
|
27
|
+
* basePath: '/api/v1/my-plugin',
|
|
28
|
+
* endpoints: [...]
|
|
29
|
+
* });
|
|
30
|
+
* }
|
|
31
|
+
* };
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @param config - Plugin configuration
|
|
35
|
+
* @returns Plugin instance
|
|
36
|
+
*/
|
|
37
|
+
export function createApiRegistryPlugin(config = {}) {
|
|
38
|
+
const { conflictResolution = 'error', version = '1.0.0', } = config;
|
|
39
|
+
return {
|
|
40
|
+
name: 'com.objectstack.core.api-registry',
|
|
41
|
+
version: '1.0.0',
|
|
42
|
+
init: async (ctx) => {
|
|
43
|
+
// Create API Registry instance
|
|
44
|
+
const registry = new ApiRegistry(ctx.logger, conflictResolution, version);
|
|
45
|
+
// Register as a service
|
|
46
|
+
ctx.registerService('api-registry', registry);
|
|
47
|
+
ctx.logger.info('API Registry plugin initialized', {
|
|
48
|
+
conflictResolution,
|
|
49
|
+
version,
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-registry-plugin.test.d.ts","sourceRoot":"","sources":["../src/api-registry-plugin.test.ts"],"names":[],"mappings":""}
|