@graph-knowledge/api 0.1.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/README.md +396 -0
- package/package.json +44 -0
- package/src/index.d.ts +18 -0
- package/src/index.js +19 -0
- package/src/lib/clients/firebase-auth-client.d.ts +24 -0
- package/src/lib/clients/firebase-auth-client.js +76 -0
- package/src/lib/clients/firebase-firestore-client.d.ts +22 -0
- package/src/lib/clients/firebase-firestore-client.js +79 -0
- package/src/lib/config/api-config.d.ts +20 -0
- package/src/lib/config/api-config.js +0 -0
- package/src/lib/constants/element-defaults.d.ts +15 -0
- package/src/lib/constants/element-defaults.js +19 -0
- package/src/lib/errors/api-errors.d.ts +33 -0
- package/src/lib/errors/api-errors.js +51 -0
- package/src/lib/graph-knowledge-api.d.ts +106 -0
- package/src/lib/graph-knowledge-api.js +154 -0
- package/src/lib/interfaces/auth-client.interface.d.ts +35 -0
- package/src/lib/interfaces/auth-client.interface.js +0 -0
- package/src/lib/interfaces/batch-operations.interface.d.ts +46 -0
- package/src/lib/interfaces/batch-operations.interface.js +0 -0
- package/src/lib/interfaces/document-operations.interface.d.ts +51 -0
- package/src/lib/interfaces/document-operations.interface.js +0 -0
- package/src/lib/interfaces/element-operations.interface.d.ts +57 -0
- package/src/lib/interfaces/element-operations.interface.js +0 -0
- package/src/lib/interfaces/element-validator.interface.d.ts +42 -0
- package/src/lib/interfaces/element-validator.interface.js +0 -0
- package/src/lib/interfaces/firestore-client.interface.d.ts +62 -0
- package/src/lib/interfaces/firestore-client.interface.js +0 -0
- package/src/lib/interfaces/node-operations.interface.d.ts +62 -0
- package/src/lib/interfaces/node-operations.interface.js +0 -0
- package/src/lib/models/document.model.d.ts +73 -0
- package/src/lib/models/document.model.js +13 -0
- package/src/lib/models/index.d.ts +6 -0
- package/src/lib/models/index.js +5 -0
- package/src/lib/operations/batch-operations.d.ts +30 -0
- package/src/lib/operations/batch-operations.js +181 -0
- package/src/lib/operations/document-operations.d.ts +20 -0
- package/src/lib/operations/document-operations.js +108 -0
- package/src/lib/operations/element-operations.d.ts +33 -0
- package/src/lib/operations/element-operations.js +175 -0
- package/src/lib/operations/node-operations.d.ts +22 -0
- package/src/lib/operations/node-operations.js +89 -0
- package/src/lib/testing/index.d.ts +2 -0
- package/src/lib/testing/index.js +3 -0
- package/src/lib/testing/mock-auth-client.d.ts +26 -0
- package/src/lib/testing/mock-auth-client.js +49 -0
- package/src/lib/testing/mock-firestore-client.d.ts +32 -0
- package/src/lib/testing/mock-firestore-client.js +126 -0
- package/src/lib/types/api-types.d.ts +61 -0
- package/src/lib/types/api-types.js +0 -0
- package/src/lib/types/element-input-types.d.ts +237 -0
- package/src/lib/types/element-input-types.js +3 -0
- package/src/lib/utils/link-level-manager.d.ts +66 -0
- package/src/lib/utils/link-level-manager.js +200 -0
- package/src/lib/utils/uuid.d.ts +5 -0
- package/src/lib/utils/uuid.js +16 -0
- package/src/lib/validators/document-validator.d.ts +22 -0
- package/src/lib/validators/document-validator.js +68 -0
- package/src/lib/validators/element-type-validators/base-element-validator.d.ts +35 -0
- package/src/lib/validators/element-type-validators/base-element-validator.js +96 -0
- package/src/lib/validators/element-type-validators/connector-validator.d.ts +13 -0
- package/src/lib/validators/element-type-validators/connector-validator.js +66 -0
- package/src/lib/validators/element-type-validators/custom-shape-validator.d.ts +22 -0
- package/src/lib/validators/element-type-validators/custom-shape-validator.js +44 -0
- package/src/lib/validators/element-type-validators/rectangle-validator.d.ts +11 -0
- package/src/lib/validators/element-type-validators/rectangle-validator.js +31 -0
- package/src/lib/validators/element-type-validators/text-validator.d.ts +14 -0
- package/src/lib/validators/element-type-validators/text-validator.js +66 -0
- package/src/lib/validators/element-type-validators/uml-validators.d.ts +58 -0
- package/src/lib/validators/element-type-validators/uml-validators.js +123 -0
- package/src/lib/validators/element-validator-registry.d.ts +18 -0
- package/src/lib/validators/element-validator-registry.js +58 -0
- package/src/lib/validators/node-validator.d.ts +26 -0
- package/src/lib/validators/node-validator.js +90 -0
package/README.md
ADDED
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
# @graph-knowledge/api
|
|
2
|
+
|
|
3
|
+
Headless Document API for Graph Knowledge - provides programmatic access to documents, nodes, and elements without requiring the Angular UI.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### npm (for external use)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @graph-knowledge/api firebase
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Monorepo (internal use)
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { GraphKnowledgeAPI } from "@graph-knowledge/api";
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { GraphKnowledgeAPI } from "@graph-knowledge/api";
|
|
23
|
+
|
|
24
|
+
// Initialize with your Firebase config
|
|
25
|
+
const api = new GraphKnowledgeAPI({
|
|
26
|
+
firebaseConfig: {
|
|
27
|
+
apiKey: "your-api-key",
|
|
28
|
+
authDomain: "your-project.firebaseapp.com",
|
|
29
|
+
projectId: "your-project",
|
|
30
|
+
storageBucket: "your-project.appspot.com",
|
|
31
|
+
messagingSenderId: "123456789",
|
|
32
|
+
appId: "1:123456789:web:abc123"
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Sign in
|
|
37
|
+
await api.signIn("user@example.com", "password");
|
|
38
|
+
|
|
39
|
+
// Create a document
|
|
40
|
+
const doc = await api.documents.create({
|
|
41
|
+
title: "My Document",
|
|
42
|
+
content: "Description"
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Create a node in the document
|
|
46
|
+
const node = await api.nodes.create(doc.id, {
|
|
47
|
+
title: "My Node",
|
|
48
|
+
canvasWidth: 1920,
|
|
49
|
+
canvasHeight: 1080
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Add elements to the node
|
|
53
|
+
await api.elements.create(doc.id, node.id, {
|
|
54
|
+
type: "rectangle",
|
|
55
|
+
x: 100,
|
|
56
|
+
y: 100,
|
|
57
|
+
width: 200,
|
|
58
|
+
height: 150,
|
|
59
|
+
fillColor: "#FF5733",
|
|
60
|
+
strokeColor: "#000000"
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
await api.elements.create(doc.id, node.id, {
|
|
64
|
+
type: "text",
|
|
65
|
+
x: 150,
|
|
66
|
+
y: 160,
|
|
67
|
+
text: "Hello World",
|
|
68
|
+
fontSize: 24
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Sign out
|
|
72
|
+
await api.signOut();
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## API Reference
|
|
76
|
+
|
|
77
|
+
### GraphKnowledgeAPI
|
|
78
|
+
|
|
79
|
+
Main entry point for the API.
|
|
80
|
+
|
|
81
|
+
#### Constructor
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
new GraphKnowledgeAPI(config: ApiConfig)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### Methods
|
|
88
|
+
|
|
89
|
+
| Method | Description |
|
|
90
|
+
|--------|-------------|
|
|
91
|
+
| `signIn(email, password)` | Signs in with email and password |
|
|
92
|
+
| `signOut()` | Signs out the current user |
|
|
93
|
+
| `waitForAuthInit()` | Waits for Firebase Auth to initialize |
|
|
94
|
+
|
|
95
|
+
#### Properties
|
|
96
|
+
|
|
97
|
+
| Property | Type | Description |
|
|
98
|
+
|----------|------|-------------|
|
|
99
|
+
| `currentUserId` | `string \| null` | Current user's ID |
|
|
100
|
+
| `documents` | `IDocumentOperations` | Document CRUD operations |
|
|
101
|
+
| `nodes` | `INodeOperations` | Node CRUD operations |
|
|
102
|
+
| `elements` | `IElementOperations` | Element CRUD operations |
|
|
103
|
+
| `batch` | `IBatchOperations` | Batch element operations |
|
|
104
|
+
| `authClient` | `IAuthClient` | Authentication client |
|
|
105
|
+
|
|
106
|
+
### Document Operations
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// Create a document
|
|
110
|
+
const doc = await api.documents.create({
|
|
111
|
+
title: "My Document",
|
|
112
|
+
content: "Optional description",
|
|
113
|
+
canvasWidth: 1920, // Optional, default: 1920
|
|
114
|
+
canvasHeight: 1080 // Optional, default: 1080
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Get a document by ID
|
|
118
|
+
const doc = await api.documents.get(documentId);
|
|
119
|
+
|
|
120
|
+
// List all documents
|
|
121
|
+
const docs = await api.documents.list();
|
|
122
|
+
|
|
123
|
+
// Update a document
|
|
124
|
+
await api.documents.update(documentId, {
|
|
125
|
+
title: "New Title",
|
|
126
|
+
content: "New description"
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Delete a document
|
|
130
|
+
await api.documents.delete(documentId);
|
|
131
|
+
|
|
132
|
+
// Share a document
|
|
133
|
+
await api.documents.share(documentId, ["user-id-1", "user-id-2"]);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Node Operations
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// Create a node
|
|
140
|
+
const node = await api.nodes.create(documentId, {
|
|
141
|
+
title: "My Node",
|
|
142
|
+
content: "Optional description",
|
|
143
|
+
parentNodeId: "parent-node-id", // Optional
|
|
144
|
+
canvasWidth: 1920,
|
|
145
|
+
canvasHeight: 1080
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Get a node
|
|
149
|
+
const node = await api.nodes.get(documentId, nodeId);
|
|
150
|
+
|
|
151
|
+
// List all nodes in a document
|
|
152
|
+
const nodes = await api.nodes.list(documentId);
|
|
153
|
+
|
|
154
|
+
// Update a node
|
|
155
|
+
await api.nodes.update(documentId, nodeId, {
|
|
156
|
+
title: "New Title"
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Delete a node
|
|
160
|
+
await api.nodes.delete(documentId, nodeId);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Element Operations
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
// Get an element by ID
|
|
167
|
+
const element = await api.elements.get(documentId, nodeId, elementId);
|
|
168
|
+
|
|
169
|
+
// List all elements in a node
|
|
170
|
+
const elements = await api.elements.list(documentId, nodeId);
|
|
171
|
+
|
|
172
|
+
// Create a rectangle
|
|
173
|
+
const rect = await api.elements.create(documentId, nodeId, {
|
|
174
|
+
type: "rectangle",
|
|
175
|
+
x: 100,
|
|
176
|
+
y: 100,
|
|
177
|
+
width: 200,
|
|
178
|
+
height: 150,
|
|
179
|
+
fillColor: "#FF5733",
|
|
180
|
+
strokeColor: "#000000",
|
|
181
|
+
strokeWidth: 2,
|
|
182
|
+
cornerRadius: 10
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Create text
|
|
186
|
+
await api.elements.create(documentId, nodeId, {
|
|
187
|
+
type: "text",
|
|
188
|
+
x: 100,
|
|
189
|
+
y: 100,
|
|
190
|
+
text: "Hello World",
|
|
191
|
+
fontSize: 24,
|
|
192
|
+
fontFamily: "Arial",
|
|
193
|
+
fillColor: "#000000",
|
|
194
|
+
textAlign: "center"
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Create a connector
|
|
198
|
+
await api.elements.create(documentId, nodeId, {
|
|
199
|
+
type: "connector",
|
|
200
|
+
x: 0,
|
|
201
|
+
y: 0,
|
|
202
|
+
startElementId: "element-1",
|
|
203
|
+
endElementId: "element-2",
|
|
204
|
+
startAnchor: "right",
|
|
205
|
+
endAnchor: "left",
|
|
206
|
+
lineStyle: "solid",
|
|
207
|
+
endMarker: "arrow"
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Create a UML class
|
|
211
|
+
await api.elements.create(documentId, nodeId, {
|
|
212
|
+
type: "uml-class",
|
|
213
|
+
x: 100,
|
|
214
|
+
y: 100,
|
|
215
|
+
width: 200,
|
|
216
|
+
height: 150,
|
|
217
|
+
name: "MyClass",
|
|
218
|
+
attributes: "+ name: string\n- id: number",
|
|
219
|
+
methods: "+ getName(): string\n+ setName(name: string): void"
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Update an element
|
|
223
|
+
await api.elements.update(documentId, nodeId, elementId, {
|
|
224
|
+
x: 200,
|
|
225
|
+
y: 200,
|
|
226
|
+
properties: {
|
|
227
|
+
fillColor: "#00FF00"
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Delete an element
|
|
232
|
+
await api.elements.delete(documentId, nodeId, elementId);
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Batch Operations
|
|
236
|
+
|
|
237
|
+
For efficient bulk operations (ideal for AI agents and automation):
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
// Create multiple elements atomically
|
|
241
|
+
const elements = await api.batch.createElements(documentId, nodeId, [
|
|
242
|
+
{ type: "rectangle", x: 100, y: 100 },
|
|
243
|
+
{ type: "rectangle", x: 300, y: 100 },
|
|
244
|
+
{ type: "text", x: 200, y: 200, text: "Connected" }
|
|
245
|
+
]);
|
|
246
|
+
|
|
247
|
+
// Update multiple elements atomically
|
|
248
|
+
await api.batch.updateElements(documentId, nodeId, [
|
|
249
|
+
{ elementId: "elem-1", x: 150, y: 150 },
|
|
250
|
+
{ elementId: "elem-2", width: 300, height: 200 }
|
|
251
|
+
]);
|
|
252
|
+
|
|
253
|
+
// Delete multiple elements atomically
|
|
254
|
+
await api.batch.deleteElements(documentId, nodeId, [
|
|
255
|
+
"elem-1",
|
|
256
|
+
"elem-2",
|
|
257
|
+
"elem-3"
|
|
258
|
+
]);
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Supported Element Types
|
|
262
|
+
|
|
263
|
+
| Type | Description |
|
|
264
|
+
|------|-------------|
|
|
265
|
+
| `rectangle` | Rectangle shape with fill, stroke, corner radius |
|
|
266
|
+
| `text` | Text element with font customization |
|
|
267
|
+
| `connector` | Line connecting two elements |
|
|
268
|
+
| `uml-class` | UML class diagram element |
|
|
269
|
+
| `uml-interface` | UML interface element |
|
|
270
|
+
| `uml-component` | UML component element |
|
|
271
|
+
| `uml-package` | UML package element |
|
|
272
|
+
| `uml-artifact` | UML artifact element |
|
|
273
|
+
| `uml-note` | UML note element |
|
|
274
|
+
| `custom:{shapeId}` | Custom SVG shape (requires premium) |
|
|
275
|
+
|
|
276
|
+
## Link Navigation
|
|
277
|
+
|
|
278
|
+
Elements can act as links to other nodes:
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
// Create an element that links to another node
|
|
282
|
+
await api.elements.create(documentId, nodeId, {
|
|
283
|
+
type: "rectangle",
|
|
284
|
+
x: 100,
|
|
285
|
+
y: 100,
|
|
286
|
+
isLink: true,
|
|
287
|
+
linkTarget: "other-node-id" // Node ID to navigate to
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Error Handling
|
|
292
|
+
|
|
293
|
+
The API throws typed errors for different failure cases:
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
import {
|
|
297
|
+
GraphKnowledgeAPI,
|
|
298
|
+
AuthenticationError,
|
|
299
|
+
NotFoundError,
|
|
300
|
+
ValidationError,
|
|
301
|
+
PermissionError
|
|
302
|
+
} from "@graph-knowledge/api";
|
|
303
|
+
|
|
304
|
+
try {
|
|
305
|
+
await api.documents.get("non-existent");
|
|
306
|
+
} catch (error) {
|
|
307
|
+
if (error instanceof NotFoundError) {
|
|
308
|
+
console.log("Document not found");
|
|
309
|
+
} else if (error instanceof AuthenticationError) {
|
|
310
|
+
console.log("Not authenticated");
|
|
311
|
+
} else if (error instanceof ValidationError) {
|
|
312
|
+
console.log("Invalid input:", error.field);
|
|
313
|
+
} else if (error instanceof PermissionError) {
|
|
314
|
+
console.log("Permission denied (e.g., premium required)");
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Testing
|
|
320
|
+
|
|
321
|
+
The library exports mock implementations for testing:
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
import {
|
|
325
|
+
MockAuthClient,
|
|
326
|
+
MockFirestoreClient
|
|
327
|
+
} from "@graph-knowledge/api";
|
|
328
|
+
import { ElementOperations } from "@graph-knowledge/api";
|
|
329
|
+
import { ElementValidatorRegistry } from "@graph-knowledge/api";
|
|
330
|
+
|
|
331
|
+
describe("MyTest", () => {
|
|
332
|
+
let mockAuth: MockAuthClient;
|
|
333
|
+
let mockFirestore: MockFirestoreClient;
|
|
334
|
+
|
|
335
|
+
beforeEach(() => {
|
|
336
|
+
mockAuth = new MockAuthClient({ userId: "test-user" });
|
|
337
|
+
mockFirestore = new MockFirestoreClient();
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it("should work with mocks", async () => {
|
|
341
|
+
// Seed test data
|
|
342
|
+
mockFirestore.seed("documents/doc-1/nodes/node-1", {
|
|
343
|
+
id: "node-1",
|
|
344
|
+
title: "Test Node",
|
|
345
|
+
elements: []
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// Test your code...
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## Architecture
|
|
354
|
+
|
|
355
|
+
The library follows SOLID principles:
|
|
356
|
+
|
|
357
|
+
- **Single Responsibility**: Each class has one job
|
|
358
|
+
- **Open/Closed**: Element validators use registry pattern for extensibility
|
|
359
|
+
- **Liskov Substitution**: All implementations can be swapped via interfaces
|
|
360
|
+
- **Interface Segregation**: Small, focused interfaces
|
|
361
|
+
- **Dependency Inversion**: Operations depend on interfaces, not implementations
|
|
362
|
+
|
|
363
|
+
```
|
|
364
|
+
GraphKnowledgeAPI (Composition Root)
|
|
365
|
+
├── FirebaseAuthClient : IAuthClient
|
|
366
|
+
├── FirebaseFirestoreClient : IFirestoreClient
|
|
367
|
+
├── DocumentOperations : IDocumentOperations
|
|
368
|
+
├── NodeOperations : INodeOperations
|
|
369
|
+
├── ElementOperations : IElementOperations
|
|
370
|
+
├── BatchOperations : IBatchOperations
|
|
371
|
+
└── ElementValidatorRegistry : IElementValidatorRegistry
|
|
372
|
+
├── RectangleValidator
|
|
373
|
+
├── TextValidator
|
|
374
|
+
├── ConnectorValidator
|
|
375
|
+
└── UmlValidators...
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Building
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
# Build
|
|
382
|
+
nx build api
|
|
383
|
+
|
|
384
|
+
# Test
|
|
385
|
+
nx test api
|
|
386
|
+
|
|
387
|
+
# Lint
|
|
388
|
+
nx lint api
|
|
389
|
+
|
|
390
|
+
# Publish to npm (after build)
|
|
391
|
+
nx publish api
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
## License
|
|
395
|
+
|
|
396
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@graph-knowledge/api",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Headless Document API for Graph Knowledge - programmatic access to documents, nodes, and elements",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Graph Knowledge Team",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/iliedanila/knowledge-graph.git",
|
|
10
|
+
"directory": "libs/api"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"graph",
|
|
14
|
+
"knowledge-graph",
|
|
15
|
+
"document-api",
|
|
16
|
+
"firebase",
|
|
17
|
+
"uml",
|
|
18
|
+
"diagram"
|
|
19
|
+
],
|
|
20
|
+
"main": "./src/index.js",
|
|
21
|
+
"module": "./src/index.mjs",
|
|
22
|
+
"types": "./src/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./src/index.d.ts",
|
|
26
|
+
"import": "./src/index.mjs",
|
|
27
|
+
"require": "./src/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"src/**/*.js",
|
|
32
|
+
"src/**/*.mjs",
|
|
33
|
+
"src/**/*.d.ts",
|
|
34
|
+
"README.md"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"firebase": ">=10.0.0 <13.0.0"
|
|
41
|
+
},
|
|
42
|
+
"sideEffects": false,
|
|
43
|
+
"type": "commonjs"
|
|
44
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { GraphKnowledgeAPI } from "./lib/graph-knowledge-api";
|
|
2
|
+
export type { ApiConfig, FirebaseConfig } from "./lib/config/api-config";
|
|
3
|
+
export type { IAuthClient } from "./lib/interfaces/auth-client.interface";
|
|
4
|
+
export type { IFirestoreClient, IBatchWriter } from "./lib/interfaces/firestore-client.interface";
|
|
5
|
+
export type { IDocumentOperations } from "./lib/interfaces/document-operations.interface";
|
|
6
|
+
export type { INodeOperations } from "./lib/interfaces/node-operations.interface";
|
|
7
|
+
export type { IElementOperations } from "./lib/interfaces/element-operations.interface";
|
|
8
|
+
export type { IBatchOperations, BatchUpdateElementInput } from "./lib/interfaces/batch-operations.interface";
|
|
9
|
+
export type { IElementTypeValidator, IElementValidatorRegistry } from "./lib/interfaces/element-validator.interface";
|
|
10
|
+
export type { CreateDocumentInput, UpdateDocumentInput, DocumentResult, CreateNodeInput, UpdateNodeInput, NodeResult } from "./lib/types/api-types";
|
|
11
|
+
export type { BaseElementInput, RectangleInput, TextInput, ConnectorInput, ConnectorAnchor, LineStyle, MarkerType, UmlClassInput, UmlInterfaceInput, UmlComponentInput, UmlPackageInput, UmlArtifactInput, UmlNoteInput, CustomShapeInput, AnyElementInput, UpdateElementInput } from "./lib/types/element-input-types";
|
|
12
|
+
export { GraphKnowledgeAPIError, AuthenticationError, NotFoundError, ValidationError, PermissionError } from "./lib/errors/api-errors";
|
|
13
|
+
export type { Document, GraphNode, GraphElement } from "./lib/models";
|
|
14
|
+
export { CURRENT_DOCUMENT_SCHEMA_VERSION } from "./lib/models";
|
|
15
|
+
export { LinkLevelManager } from "./lib/utils/link-level-manager";
|
|
16
|
+
export type { LevelChange, LinkState } from "./lib/utils/link-level-manager";
|
|
17
|
+
export { MockAuthClient } from "./lib/testing/mock-auth-client";
|
|
18
|
+
export { MockFirestoreClient } from "./lib/testing/mock-firestore-client";
|
package/src/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Graph Knowledge Headless Document API
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// Main API Class
|
|
5
|
+
export { GraphKnowledgeAPI } from "./lib/graph-knowledge-api";
|
|
6
|
+
// =============================================================================
|
|
7
|
+
// Errors
|
|
8
|
+
// =============================================================================
|
|
9
|
+
export { GraphKnowledgeAPIError, AuthenticationError, NotFoundError, ValidationError, PermissionError } from "./lib/errors/api-errors";
|
|
10
|
+
export { CURRENT_DOCUMENT_SCHEMA_VERSION } from "./lib/models";
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// Utilities
|
|
13
|
+
// =============================================================================
|
|
14
|
+
export { LinkLevelManager } from "./lib/utils/link-level-manager";
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// Testing Utilities
|
|
17
|
+
// =============================================================================
|
|
18
|
+
export { MockAuthClient } from "./lib/testing/mock-auth-client";
|
|
19
|
+
export { MockFirestoreClient } from "./lib/testing/mock-firestore-client";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Auth } from "firebase/auth";
|
|
2
|
+
import { Firestore } from "firebase/firestore";
|
|
3
|
+
import { IAuthClient } from "../interfaces/auth-client.interface";
|
|
4
|
+
/**
|
|
5
|
+
* Firebase Auth implementation of IAuthClient.
|
|
6
|
+
*/
|
|
7
|
+
export declare class FirebaseAuthClient implements IAuthClient {
|
|
8
|
+
private readonly auth;
|
|
9
|
+
private readonly firestore;
|
|
10
|
+
private _currentUser;
|
|
11
|
+
private _initialized;
|
|
12
|
+
private _initPromise;
|
|
13
|
+
constructor(auth: Auth, firestore: Firestore);
|
|
14
|
+
/**
|
|
15
|
+
* Waits for auth state to be initialized.
|
|
16
|
+
*/
|
|
17
|
+
waitForInit(): Promise<void>;
|
|
18
|
+
signIn(email: string, password: string): Promise<void>;
|
|
19
|
+
signOut(): Promise<void>;
|
|
20
|
+
get currentUserId(): string | null;
|
|
21
|
+
requireAuth(): string;
|
|
22
|
+
isPremium(): Promise<boolean>;
|
|
23
|
+
requirePremium(): Promise<void>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { signInWithEmailAndPassword, signOut as firebaseSignOut, onAuthStateChanged } from "firebase/auth";
|
|
2
|
+
import { doc, getDoc } from "firebase/firestore";
|
|
3
|
+
import { AuthenticationError, PermissionError } from "../errors/api-errors";
|
|
4
|
+
/**
|
|
5
|
+
* Firebase Auth implementation of IAuthClient.
|
|
6
|
+
*/
|
|
7
|
+
export class FirebaseAuthClient {
|
|
8
|
+
auth;
|
|
9
|
+
firestore;
|
|
10
|
+
_currentUser = null;
|
|
11
|
+
_initialized = false;
|
|
12
|
+
_initPromise;
|
|
13
|
+
constructor(auth, firestore) {
|
|
14
|
+
this.auth = auth;
|
|
15
|
+
this.firestore = firestore;
|
|
16
|
+
this._initPromise = new Promise((resolve) => {
|
|
17
|
+
const unsubscribe = onAuthStateChanged(this.auth, (user) => {
|
|
18
|
+
this._currentUser = user;
|
|
19
|
+
if (!this._initialized) {
|
|
20
|
+
this._initialized = true;
|
|
21
|
+
unsubscribe();
|
|
22
|
+
resolve();
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Waits for auth state to be initialized.
|
|
29
|
+
*/
|
|
30
|
+
async waitForInit() {
|
|
31
|
+
return this._initPromise;
|
|
32
|
+
}
|
|
33
|
+
async signIn(email, password) {
|
|
34
|
+
try {
|
|
35
|
+
const credential = await signInWithEmailAndPassword(this.auth, email, password);
|
|
36
|
+
this._currentUser = credential.user;
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
const message = error instanceof Error ? error.message : "Sign in failed";
|
|
40
|
+
throw new AuthenticationError(message);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async signOut() {
|
|
44
|
+
await firebaseSignOut(this.auth);
|
|
45
|
+
this._currentUser = null;
|
|
46
|
+
}
|
|
47
|
+
get currentUserId() {
|
|
48
|
+
return this._currentUser?.uid ?? null;
|
|
49
|
+
}
|
|
50
|
+
requireAuth() {
|
|
51
|
+
if (!this._currentUser) {
|
|
52
|
+
throw new AuthenticationError("Not authenticated");
|
|
53
|
+
}
|
|
54
|
+
return this._currentUser.uid;
|
|
55
|
+
}
|
|
56
|
+
async isPremium() {
|
|
57
|
+
if (!this._currentUser) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
// Read premium status from user profile in Firestore
|
|
61
|
+
const userDoc = doc(this.firestore, `users/${this._currentUser.uid}`);
|
|
62
|
+
const snapshot = await getDoc(userDoc);
|
|
63
|
+
if (!snapshot.exists()) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
const profile = snapshot.data();
|
|
67
|
+
return profile?.["isPremium"] === true;
|
|
68
|
+
}
|
|
69
|
+
async requirePremium() {
|
|
70
|
+
this.requireAuth();
|
|
71
|
+
const premium = await this.isPremium();
|
|
72
|
+
if (!premium) {
|
|
73
|
+
throw new PermissionError("Premium subscription required");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Firestore } from "firebase/firestore";
|
|
2
|
+
import { IFirestoreClient, IBatchWriter } from "../interfaces/firestore-client.interface";
|
|
3
|
+
/**
|
|
4
|
+
* Firebase Firestore implementation of IFirestoreClient.
|
|
5
|
+
*/
|
|
6
|
+
export declare class FirebaseFirestoreClient implements IFirestoreClient {
|
|
7
|
+
private readonly db;
|
|
8
|
+
constructor(db: Firestore);
|
|
9
|
+
getDocument<T>(path: string): Promise<(T & {
|
|
10
|
+
id: string;
|
|
11
|
+
}) | null>;
|
|
12
|
+
setDocument<T extends object>(path: string, data: T): Promise<void>;
|
|
13
|
+
updateDocument<T extends object>(path: string, data: Partial<T>): Promise<void>;
|
|
14
|
+
deleteDocument(path: string): Promise<void>;
|
|
15
|
+
getCollection<T>(path: string): Promise<(T & {
|
|
16
|
+
id: string;
|
|
17
|
+
})[]>;
|
|
18
|
+
queryCollection<T>(path: string, field: string, value: string): Promise<(T & {
|
|
19
|
+
id: string;
|
|
20
|
+
})[]>;
|
|
21
|
+
batch(): IBatchWriter;
|
|
22
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { doc, getDoc, setDoc, updateDoc, deleteDoc, collection, getDocs, query, where, writeBatch } from "firebase/firestore";
|
|
2
|
+
/**
|
|
3
|
+
* Firebase Firestore batch writer implementation.
|
|
4
|
+
*/
|
|
5
|
+
class FirestoreBatchWriter {
|
|
6
|
+
db;
|
|
7
|
+
batch;
|
|
8
|
+
constructor(db, batch) {
|
|
9
|
+
this.db = db;
|
|
10
|
+
this.batch = batch;
|
|
11
|
+
}
|
|
12
|
+
set(path, data) {
|
|
13
|
+
const docRef = doc(this.db, path);
|
|
14
|
+
this.batch.set(docRef, data);
|
|
15
|
+
}
|
|
16
|
+
update(path, data) {
|
|
17
|
+
const docRef = doc(this.db, path);
|
|
18
|
+
this.batch.update(docRef, data);
|
|
19
|
+
}
|
|
20
|
+
delete(path) {
|
|
21
|
+
const docRef = doc(this.db, path);
|
|
22
|
+
this.batch.delete(docRef);
|
|
23
|
+
}
|
|
24
|
+
async commit() {
|
|
25
|
+
await this.batch.commit();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Firebase Firestore implementation of IFirestoreClient.
|
|
30
|
+
*/
|
|
31
|
+
export class FirebaseFirestoreClient {
|
|
32
|
+
db;
|
|
33
|
+
constructor(db) {
|
|
34
|
+
this.db = db;
|
|
35
|
+
}
|
|
36
|
+
async getDocument(path) {
|
|
37
|
+
const docRef = doc(this.db, path);
|
|
38
|
+
const snapshot = await getDoc(docRef);
|
|
39
|
+
if (!snapshot.exists()) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
id: snapshot.id,
|
|
44
|
+
...snapshot.data()
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async setDocument(path, data) {
|
|
48
|
+
const docRef = doc(this.db, path);
|
|
49
|
+
await setDoc(docRef, data);
|
|
50
|
+
}
|
|
51
|
+
async updateDocument(path, data) {
|
|
52
|
+
const docRef = doc(this.db, path);
|
|
53
|
+
await updateDoc(docRef, data);
|
|
54
|
+
}
|
|
55
|
+
async deleteDocument(path) {
|
|
56
|
+
const docRef = doc(this.db, path);
|
|
57
|
+
await deleteDoc(docRef);
|
|
58
|
+
}
|
|
59
|
+
async getCollection(path) {
|
|
60
|
+
const collectionRef = collection(this.db, path);
|
|
61
|
+
const snapshot = await getDocs(collectionRef);
|
|
62
|
+
return snapshot.docs.map((doc) => ({
|
|
63
|
+
id: doc.id,
|
|
64
|
+
...doc.data()
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
async queryCollection(path, field, value) {
|
|
68
|
+
const collectionRef = collection(this.db, path);
|
|
69
|
+
const q = query(collectionRef, where(field, "==", value));
|
|
70
|
+
const snapshot = await getDocs(q);
|
|
71
|
+
return snapshot.docs.map((doc) => ({
|
|
72
|
+
id: doc.id,
|
|
73
|
+
...doc.data()
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
batch() {
|
|
77
|
+
return new FirestoreBatchWriter(this.db, writeBatch(this.db));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase configuration required to initialize the API.
|
|
3
|
+
*/
|
|
4
|
+
export interface FirebaseConfig {
|
|
5
|
+
apiKey: string;
|
|
6
|
+
authDomain: string;
|
|
7
|
+
projectId: string;
|
|
8
|
+
storageBucket: string;
|
|
9
|
+
messagingSenderId: string;
|
|
10
|
+
appId: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Configuration options for GraphKnowledgeAPI.
|
|
14
|
+
*/
|
|
15
|
+
export interface ApiConfig {
|
|
16
|
+
/** Firebase project configuration */
|
|
17
|
+
firebaseConfig: FirebaseConfig;
|
|
18
|
+
/** Optional app name for multiple Firebase instances */
|
|
19
|
+
appName?: string;
|
|
20
|
+
}
|