@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.
Files changed (74) hide show
  1. package/README.md +396 -0
  2. package/package.json +44 -0
  3. package/src/index.d.ts +18 -0
  4. package/src/index.js +19 -0
  5. package/src/lib/clients/firebase-auth-client.d.ts +24 -0
  6. package/src/lib/clients/firebase-auth-client.js +76 -0
  7. package/src/lib/clients/firebase-firestore-client.d.ts +22 -0
  8. package/src/lib/clients/firebase-firestore-client.js +79 -0
  9. package/src/lib/config/api-config.d.ts +20 -0
  10. package/src/lib/config/api-config.js +0 -0
  11. package/src/lib/constants/element-defaults.d.ts +15 -0
  12. package/src/lib/constants/element-defaults.js +19 -0
  13. package/src/lib/errors/api-errors.d.ts +33 -0
  14. package/src/lib/errors/api-errors.js +51 -0
  15. package/src/lib/graph-knowledge-api.d.ts +106 -0
  16. package/src/lib/graph-knowledge-api.js +154 -0
  17. package/src/lib/interfaces/auth-client.interface.d.ts +35 -0
  18. package/src/lib/interfaces/auth-client.interface.js +0 -0
  19. package/src/lib/interfaces/batch-operations.interface.d.ts +46 -0
  20. package/src/lib/interfaces/batch-operations.interface.js +0 -0
  21. package/src/lib/interfaces/document-operations.interface.d.ts +51 -0
  22. package/src/lib/interfaces/document-operations.interface.js +0 -0
  23. package/src/lib/interfaces/element-operations.interface.d.ts +57 -0
  24. package/src/lib/interfaces/element-operations.interface.js +0 -0
  25. package/src/lib/interfaces/element-validator.interface.d.ts +42 -0
  26. package/src/lib/interfaces/element-validator.interface.js +0 -0
  27. package/src/lib/interfaces/firestore-client.interface.d.ts +62 -0
  28. package/src/lib/interfaces/firestore-client.interface.js +0 -0
  29. package/src/lib/interfaces/node-operations.interface.d.ts +62 -0
  30. package/src/lib/interfaces/node-operations.interface.js +0 -0
  31. package/src/lib/models/document.model.d.ts +73 -0
  32. package/src/lib/models/document.model.js +13 -0
  33. package/src/lib/models/index.d.ts +6 -0
  34. package/src/lib/models/index.js +5 -0
  35. package/src/lib/operations/batch-operations.d.ts +30 -0
  36. package/src/lib/operations/batch-operations.js +181 -0
  37. package/src/lib/operations/document-operations.d.ts +20 -0
  38. package/src/lib/operations/document-operations.js +108 -0
  39. package/src/lib/operations/element-operations.d.ts +33 -0
  40. package/src/lib/operations/element-operations.js +175 -0
  41. package/src/lib/operations/node-operations.d.ts +22 -0
  42. package/src/lib/operations/node-operations.js +89 -0
  43. package/src/lib/testing/index.d.ts +2 -0
  44. package/src/lib/testing/index.js +3 -0
  45. package/src/lib/testing/mock-auth-client.d.ts +26 -0
  46. package/src/lib/testing/mock-auth-client.js +49 -0
  47. package/src/lib/testing/mock-firestore-client.d.ts +32 -0
  48. package/src/lib/testing/mock-firestore-client.js +126 -0
  49. package/src/lib/types/api-types.d.ts +61 -0
  50. package/src/lib/types/api-types.js +0 -0
  51. package/src/lib/types/element-input-types.d.ts +237 -0
  52. package/src/lib/types/element-input-types.js +3 -0
  53. package/src/lib/utils/link-level-manager.d.ts +66 -0
  54. package/src/lib/utils/link-level-manager.js +200 -0
  55. package/src/lib/utils/uuid.d.ts +5 -0
  56. package/src/lib/utils/uuid.js +16 -0
  57. package/src/lib/validators/document-validator.d.ts +22 -0
  58. package/src/lib/validators/document-validator.js +68 -0
  59. package/src/lib/validators/element-type-validators/base-element-validator.d.ts +35 -0
  60. package/src/lib/validators/element-type-validators/base-element-validator.js +96 -0
  61. package/src/lib/validators/element-type-validators/connector-validator.d.ts +13 -0
  62. package/src/lib/validators/element-type-validators/connector-validator.js +66 -0
  63. package/src/lib/validators/element-type-validators/custom-shape-validator.d.ts +22 -0
  64. package/src/lib/validators/element-type-validators/custom-shape-validator.js +44 -0
  65. package/src/lib/validators/element-type-validators/rectangle-validator.d.ts +11 -0
  66. package/src/lib/validators/element-type-validators/rectangle-validator.js +31 -0
  67. package/src/lib/validators/element-type-validators/text-validator.d.ts +14 -0
  68. package/src/lib/validators/element-type-validators/text-validator.js +66 -0
  69. package/src/lib/validators/element-type-validators/uml-validators.d.ts +58 -0
  70. package/src/lib/validators/element-type-validators/uml-validators.js +123 -0
  71. package/src/lib/validators/element-validator-registry.d.ts +18 -0
  72. package/src/lib/validators/element-validator-registry.js +58 -0
  73. package/src/lib/validators/node-validator.d.ts +26 -0
  74. package/src/lib/validators/node-validator.js +90 -0
@@ -0,0 +1,175 @@
1
+ import { generateUuid } from "../utils/uuid";
2
+ import { NotFoundError } from "../errors/api-errors";
3
+ import { DEFAULT_ELEMENT_DIMENSIONS, DEFAULT_CUSTOM_SHAPE_DIMENSIONS } from "../constants/element-defaults";
4
+ /**
5
+ * Implementation of element CRUD operations.
6
+ */
7
+ export class ElementOperations {
8
+ firestore;
9
+ auth;
10
+ validatorRegistry;
11
+ constructor(firestore, auth, validatorRegistry) {
12
+ this.firestore = firestore;
13
+ this.auth = auth;
14
+ this.validatorRegistry = validatorRegistry;
15
+ }
16
+ async get(documentId, nodeId, elementId) {
17
+ this.auth.requireAuth();
18
+ const nodePath = `documents/${documentId}/nodes/${nodeId}`;
19
+ const node = await this.firestore.getDocument(nodePath);
20
+ if (!node) {
21
+ throw new NotFoundError(`Node ${nodeId} not found`);
22
+ }
23
+ const element = node.elements.find((e) => e.id === elementId);
24
+ if (!element) {
25
+ throw new NotFoundError(`Element ${elementId} not found`);
26
+ }
27
+ return element;
28
+ }
29
+ async list(documentId, nodeId) {
30
+ this.auth.requireAuth();
31
+ const nodePath = `documents/${documentId}/nodes/${nodeId}`;
32
+ const node = await this.firestore.getDocument(nodePath);
33
+ if (!node) {
34
+ throw new NotFoundError(`Node ${nodeId} not found`);
35
+ }
36
+ return node.elements || [];
37
+ }
38
+ async create(documentId, nodeId, input) {
39
+ this.auth.requireAuth();
40
+ // Custom shapes require premium
41
+ if (input.type.startsWith("custom:")) {
42
+ await this.auth.requirePremium();
43
+ }
44
+ this.validatorRegistry.validate(input);
45
+ const nodePath = `documents/${documentId}/nodes/${nodeId}`;
46
+ const node = await this.firestore.getDocument(nodePath);
47
+ if (!node) {
48
+ throw new NotFoundError(`Node ${nodeId} not found`);
49
+ }
50
+ const defaults = this.getDefaultDimensions(input.type);
51
+ // Connectors have optional x/y since their position is derived from connected elements
52
+ const x = input.x ?? 0;
53
+ const y = input.y ?? 0;
54
+ const element = {
55
+ id: generateUuid(),
56
+ elementType: input.type,
57
+ x,
58
+ y,
59
+ width: input.width ?? defaults.width,
60
+ height: input.height ?? defaults.height,
61
+ rotation: this.normalizeRotation(input.rotation ?? 0),
62
+ properties: this.buildProperties(input)
63
+ };
64
+ // Only add link properties if they have values (Firestore rejects undefined)
65
+ if (input.isLink !== undefined) {
66
+ element.isLink = input.isLink;
67
+ }
68
+ if (input.linkTarget !== undefined) {
69
+ element.linkTarget = input.linkTarget;
70
+ }
71
+ const updatedElements = [...(node.elements || []), element];
72
+ await this.firestore.updateDocument(nodePath, {
73
+ elements: updatedElements
74
+ });
75
+ return element;
76
+ }
77
+ async update(documentId, nodeId, elementId, input) {
78
+ this.auth.requireAuth();
79
+ const nodePath = `documents/${documentId}/nodes/${nodeId}`;
80
+ const node = await this.firestore.getDocument(nodePath);
81
+ if (!node) {
82
+ throw new NotFoundError(`Node ${nodeId} not found`);
83
+ }
84
+ const elementIndex = node.elements.findIndex((e) => e.id === elementId);
85
+ if (elementIndex === -1) {
86
+ throw new NotFoundError(`Element ${elementId} not found`);
87
+ }
88
+ const existingElement = node.elements[elementIndex];
89
+ const updatedElement = {
90
+ ...existingElement,
91
+ x: input.x ?? existingElement.x,
92
+ y: input.y ?? existingElement.y,
93
+ width: input.width ?? existingElement.width,
94
+ height: input.height ?? existingElement.height,
95
+ rotation: input.rotation !== undefined
96
+ ? this.normalizeRotation(input.rotation)
97
+ : existingElement.rotation,
98
+ properties: {
99
+ ...existingElement.properties,
100
+ ...(input.properties ?? {})
101
+ }
102
+ };
103
+ // Handle link properties - only set if defined (Firestore rejects undefined)
104
+ const newIsLink = input.isLink ?? existingElement.isLink;
105
+ const newLinkTarget = input.linkTarget ?? existingElement.linkTarget;
106
+ if (newIsLink !== undefined) {
107
+ updatedElement.isLink = newIsLink;
108
+ }
109
+ if (newLinkTarget !== undefined) {
110
+ updatedElement.linkTarget = newLinkTarget;
111
+ }
112
+ const updatedElements = [...node.elements];
113
+ updatedElements[elementIndex] = updatedElement;
114
+ await this.firestore.updateDocument(nodePath, {
115
+ elements: updatedElements
116
+ });
117
+ }
118
+ async delete(documentId, nodeId, elementId) {
119
+ this.auth.requireAuth();
120
+ const nodePath = `documents/${documentId}/nodes/${nodeId}`;
121
+ const node = await this.firestore.getDocument(nodePath);
122
+ if (!node) {
123
+ throw new NotFoundError(`Node ${nodeId} not found`);
124
+ }
125
+ const updatedElements = node.elements.filter((e) => e.id !== elementId);
126
+ await this.firestore.updateDocument(nodePath, {
127
+ elements: updatedElements
128
+ });
129
+ }
130
+ /**
131
+ * Gets default dimensions for an element type.
132
+ */
133
+ getDefaultDimensions(type) {
134
+ if (type.startsWith("custom:")) {
135
+ return DEFAULT_CUSTOM_SHAPE_DIMENSIONS;
136
+ }
137
+ return (DEFAULT_ELEMENT_DIMENSIONS[type] ?? DEFAULT_CUSTOM_SHAPE_DIMENSIONS);
138
+ }
139
+ /**
140
+ * Normalizes rotation to integer in range [0, 360).
141
+ */
142
+ normalizeRotation(rotation) {
143
+ let normalized = rotation % 360;
144
+ if (normalized < 0) {
145
+ normalized += 360;
146
+ }
147
+ return Math.round(normalized);
148
+ }
149
+ /**
150
+ * Builds properties object from element input.
151
+ * Extracts type-specific properties, excluding common fields.
152
+ */
153
+ buildProperties(input) {
154
+ const props = {
155
+ __schemaVersion: 1
156
+ };
157
+ // Exclude common fields, copy the rest as properties
158
+ const commonFields = new Set([
159
+ "type",
160
+ "x",
161
+ "y",
162
+ "width",
163
+ "height",
164
+ "rotation",
165
+ "isLink",
166
+ "linkTarget"
167
+ ]);
168
+ for (const [key, value] of Object.entries(input)) {
169
+ if (!commonFields.has(key) && value !== undefined) {
170
+ props[key] = value;
171
+ }
172
+ }
173
+ return props;
174
+ }
175
+ }
@@ -0,0 +1,22 @@
1
+ import { GraphNode } from "@graph-knowledge/domain";
2
+ import { INodeOperations } from "../interfaces/node-operations.interface";
3
+ import { IFirestoreClient } from "../interfaces/firestore-client.interface";
4
+ import { IAuthClient } from "../interfaces/auth-client.interface";
5
+ import { NodeValidator } from "../validators/node-validator";
6
+ import { CreateNodeInput, UpdateNodeInput } from "../types/api-types";
7
+ /**
8
+ * Implementation of node CRUD operations.
9
+ */
10
+ export declare class NodeOperations implements INodeOperations {
11
+ private readonly firestore;
12
+ private readonly auth;
13
+ private readonly validator;
14
+ constructor(firestore: IFirestoreClient, auth: IAuthClient, validator: NodeValidator);
15
+ create(documentId: string, input: CreateNodeInput): Promise<GraphNode>;
16
+ createWithId(documentId: string, nodeId: string, input: CreateNodeInput): Promise<GraphNode>;
17
+ private createNodeWithId;
18
+ get(documentId: string, nodeId: string): Promise<GraphNode>;
19
+ list(documentId: string): Promise<GraphNode[]>;
20
+ update(documentId: string, nodeId: string, input: UpdateNodeInput): Promise<void>;
21
+ delete(documentId: string, nodeId: string): Promise<void>;
22
+ }
@@ -0,0 +1,89 @@
1
+ import { generateUuid } from "../utils/uuid";
2
+ import { NotFoundError } from "../errors/api-errors";
3
+ // Default canvas dimensions
4
+ const DEFAULT_CANVAS_WIDTH = 1920;
5
+ const DEFAULT_CANVAS_HEIGHT = 1080;
6
+ /**
7
+ * Implementation of node CRUD operations.
8
+ */
9
+ export class NodeOperations {
10
+ firestore;
11
+ auth;
12
+ validator;
13
+ constructor(firestore, auth, validator) {
14
+ this.firestore = firestore;
15
+ this.auth = auth;
16
+ this.validator = validator;
17
+ }
18
+ async create(documentId, input) {
19
+ const nodeId = generateUuid();
20
+ return this.createNodeWithId(documentId, nodeId, input);
21
+ }
22
+ async createWithId(documentId, nodeId, input) {
23
+ return this.createNodeWithId(documentId, nodeId, input);
24
+ }
25
+ async createNodeWithId(documentId, nodeId, input) {
26
+ const userId = this.auth.requireAuth();
27
+ this.validator.validateCreate(input);
28
+ // Determine level from parent if provided
29
+ let level = input.level ?? 0;
30
+ if (input.parentNodeId) {
31
+ const parentNode = await this.firestore.getDocument(`documents/${documentId}/nodes/${input.parentNodeId}`);
32
+ if (parentNode) {
33
+ level = (parentNode.level ?? 0) + 1;
34
+ }
35
+ }
36
+ const node = {
37
+ id: nodeId,
38
+ title: input.title,
39
+ content: input.content ?? "",
40
+ elements: [],
41
+ canvasWidth: input.canvasWidth ?? DEFAULT_CANVAS_WIDTH,
42
+ canvasHeight: input.canvasHeight ?? DEFAULT_CANVAS_HEIGHT,
43
+ level,
44
+ owner: userId
45
+ };
46
+ // Only set parentNodeId if defined (Firestore rejects undefined)
47
+ if (input.parentNodeId !== undefined) {
48
+ node.parentNodeId = input.parentNodeId;
49
+ }
50
+ await this.firestore.setDocument(`documents/${documentId}/nodes/${nodeId}`, node);
51
+ return node;
52
+ }
53
+ async get(documentId, nodeId) {
54
+ this.auth.requireAuth();
55
+ const node = await this.firestore.getDocument(`documents/${documentId}/nodes/${nodeId}`);
56
+ if (!node) {
57
+ throw new NotFoundError(`Node ${nodeId} not found`);
58
+ }
59
+ return node;
60
+ }
61
+ async list(documentId) {
62
+ this.auth.requireAuth();
63
+ return this.firestore.getCollection(`documents/${documentId}/nodes`);
64
+ }
65
+ async update(documentId, nodeId, input) {
66
+ this.auth.requireAuth();
67
+ this.validator.validateUpdate(input);
68
+ const node = await this.firestore.getDocument(`documents/${documentId}/nodes/${nodeId}`);
69
+ if (!node) {
70
+ throw new NotFoundError(`Node ${nodeId} not found`);
71
+ }
72
+ const updates = {};
73
+ if (input.title !== undefined)
74
+ updates.title = input.title;
75
+ if (input.content !== undefined)
76
+ updates.content = input.content;
77
+ if (input.canvasWidth !== undefined)
78
+ updates.canvasWidth = input.canvasWidth;
79
+ if (input.canvasHeight !== undefined)
80
+ updates.canvasHeight = input.canvasHeight;
81
+ if (Object.keys(updates).length > 0) {
82
+ await this.firestore.updateDocument(`documents/${documentId}/nodes/${nodeId}`, updates);
83
+ }
84
+ }
85
+ async delete(documentId, nodeId) {
86
+ this.auth.requireAuth();
87
+ await this.firestore.deleteDocument(`documents/${documentId}/nodes/${nodeId}`);
88
+ }
89
+ }
@@ -0,0 +1,2 @@
1
+ export { MockAuthClient } from "./mock-auth-client";
2
+ export { MockFirestoreClient } from "./mock-firestore-client";
@@ -0,0 +1,3 @@
1
+ // Testing utilities for @graph-knowledge/api
2
+ export { MockAuthClient } from "./mock-auth-client";
3
+ export { MockFirestoreClient } from "./mock-firestore-client";
@@ -0,0 +1,26 @@
1
+ import { IAuthClient } from "../interfaces/auth-client.interface";
2
+ /**
3
+ * Mock authentication client for testing.
4
+ */
5
+ export declare class MockAuthClient implements IAuthClient {
6
+ private _currentUserId;
7
+ private _isPremium;
8
+ constructor(options?: {
9
+ userId?: string;
10
+ isPremium?: boolean;
11
+ });
12
+ signIn(email: string, _password: string): Promise<void>;
13
+ signOut(): Promise<void>;
14
+ get currentUserId(): string | null;
15
+ requireAuth(): string;
16
+ /**
17
+ * Sets the current user ID for testing.
18
+ */
19
+ setUserId(userId: string | null): void;
20
+ isPremium(): Promise<boolean>;
21
+ requirePremium(): Promise<void>;
22
+ /**
23
+ * Sets the premium status for testing.
24
+ */
25
+ setPremium(isPremium: boolean): void;
26
+ }
@@ -0,0 +1,49 @@
1
+ import { AuthenticationError, PermissionError } from "../errors/api-errors";
2
+ /**
3
+ * Mock authentication client for testing.
4
+ */
5
+ export class MockAuthClient {
6
+ _currentUserId = null;
7
+ _isPremium = false;
8
+ constructor(options) {
9
+ this._currentUserId = options?.userId ?? null;
10
+ this._isPremium = options?.isPremium ?? false;
11
+ }
12
+ async signIn(email, _password) {
13
+ // Simulate successful sign-in
14
+ this._currentUserId = `mock-user-${email.split("@")[0]}`;
15
+ }
16
+ async signOut() {
17
+ this._currentUserId = null;
18
+ }
19
+ get currentUserId() {
20
+ return this._currentUserId;
21
+ }
22
+ requireAuth() {
23
+ if (!this._currentUserId) {
24
+ throw new AuthenticationError("Not authenticated");
25
+ }
26
+ return this._currentUserId;
27
+ }
28
+ /**
29
+ * Sets the current user ID for testing.
30
+ */
31
+ setUserId(userId) {
32
+ this._currentUserId = userId;
33
+ }
34
+ async isPremium() {
35
+ return this._isPremium;
36
+ }
37
+ async requirePremium() {
38
+ this.requireAuth();
39
+ if (!this._isPremium) {
40
+ throw new PermissionError("Premium subscription required");
41
+ }
42
+ }
43
+ /**
44
+ * Sets the premium status for testing.
45
+ */
46
+ setPremium(isPremium) {
47
+ this._isPremium = isPremium;
48
+ }
49
+ }
@@ -0,0 +1,32 @@
1
+ import { IFirestoreClient, IBatchWriter } from "../interfaces/firestore-client.interface";
2
+ /**
3
+ * Mock Firestore client for testing.
4
+ */
5
+ export declare class MockFirestoreClient implements IFirestoreClient {
6
+ private readonly store;
7
+ getDocument<T>(path: string): Promise<(T & {
8
+ id: string;
9
+ }) | null>;
10
+ setDocument<T extends object>(path: string, data: T): Promise<void>;
11
+ updateDocument<T extends object>(path: string, data: Partial<T>): Promise<void>;
12
+ deleteDocument(path: string): Promise<void>;
13
+ getCollection<T>(path: string): Promise<(T & {
14
+ id: string;
15
+ })[]>;
16
+ queryCollection<T>(path: string, field: string, value: string): Promise<(T & {
17
+ id: string;
18
+ })[]>;
19
+ batch(): IBatchWriter;
20
+ /**
21
+ * Clears all data from the mock store.
22
+ */
23
+ clear(): void;
24
+ /**
25
+ * Gets all stored data for testing assertions.
26
+ */
27
+ getAllData(): Map<string, unknown>;
28
+ /**
29
+ * Seeds data into the store for testing.
30
+ */
31
+ seed(path: string, data: Record<string, unknown>): void;
32
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Mock batch writer for testing.
3
+ */
4
+ class MockBatchWriter {
5
+ store;
6
+ operations = [];
7
+ constructor(store) {
8
+ this.store = store;
9
+ }
10
+ set(path, data) {
11
+ this.operations.push({
12
+ type: "set",
13
+ path,
14
+ data: data
15
+ });
16
+ }
17
+ update(path, data) {
18
+ this.operations.push({
19
+ type: "update",
20
+ path,
21
+ data: data
22
+ });
23
+ }
24
+ delete(path) {
25
+ this.operations.push({ type: "delete", path });
26
+ }
27
+ async commit() {
28
+ for (const op of this.operations) {
29
+ const id = op.path.split("/").pop() ?? "";
30
+ switch (op.type) {
31
+ case "set":
32
+ this.store.set(op.path, { id, ...(op.data ?? {}) });
33
+ break;
34
+ case "update": {
35
+ const existing = this.store.get(op.path);
36
+ if (existing) {
37
+ this.store.set(op.path, {
38
+ ...existing,
39
+ ...(op.data ?? {})
40
+ });
41
+ }
42
+ break;
43
+ }
44
+ case "delete":
45
+ this.store.delete(op.path);
46
+ break;
47
+ }
48
+ }
49
+ this.operations = [];
50
+ }
51
+ /**
52
+ * Gets the pending operations for testing assertions.
53
+ */
54
+ getPendingOperations() {
55
+ return [...this.operations];
56
+ }
57
+ }
58
+ /**
59
+ * Mock Firestore client for testing.
60
+ */
61
+ export class MockFirestoreClient {
62
+ store = new Map();
63
+ async getDocument(path) {
64
+ const data = this.store.get(path);
65
+ if (!data) {
66
+ return null;
67
+ }
68
+ return data;
69
+ }
70
+ async setDocument(path, data) {
71
+ const id = path.split("/").pop() ?? "";
72
+ this.store.set(path, { id, ...data });
73
+ }
74
+ async updateDocument(path, data) {
75
+ const existing = this.store.get(path);
76
+ if (existing) {
77
+ this.store.set(path, { ...existing, ...data });
78
+ }
79
+ }
80
+ async deleteDocument(path) {
81
+ this.store.delete(path);
82
+ }
83
+ async getCollection(path) {
84
+ const results = [];
85
+ const prefix = path + "/";
86
+ for (const [key, value] of this.store) {
87
+ if (key.startsWith(prefix)) {
88
+ // Only include direct children (not nested sub-collections)
89
+ const remainder = key.substring(prefix.length);
90
+ if (!remainder.includes("/")) {
91
+ results.push(value);
92
+ }
93
+ }
94
+ }
95
+ return results;
96
+ }
97
+ async queryCollection(path, field, value) {
98
+ const all = await this.getCollection(path);
99
+ return all.filter((item) => {
100
+ const record = item;
101
+ return record[field] === value;
102
+ });
103
+ }
104
+ batch() {
105
+ return new MockBatchWriter(this.store);
106
+ }
107
+ /**
108
+ * Clears all data from the mock store.
109
+ */
110
+ clear() {
111
+ this.store.clear();
112
+ }
113
+ /**
114
+ * Gets all stored data for testing assertions.
115
+ */
116
+ getAllData() {
117
+ return new Map(this.store);
118
+ }
119
+ /**
120
+ * Seeds data into the store for testing.
121
+ */
122
+ seed(path, data) {
123
+ const id = path.split("/").pop() ?? "";
124
+ this.store.set(path, { id, ...data });
125
+ }
126
+ }
@@ -0,0 +1,61 @@
1
+ import { Document, GraphNode } from "@graph-knowledge/domain";
2
+ /**
3
+ * Input for creating a new document.
4
+ */
5
+ export interface CreateDocumentInput {
6
+ /** Document title (required) */
7
+ title: string;
8
+ /** Document description/content */
9
+ content?: string;
10
+ /** Root node canvas width (defaults to 1920) */
11
+ canvasWidth?: number;
12
+ /** Root node canvas height (defaults to 1080) */
13
+ canvasHeight?: number;
14
+ /** Mark as premium content */
15
+ isPremiumContent?: boolean;
16
+ }
17
+ /**
18
+ * Input for updating an existing document.
19
+ */
20
+ export interface UpdateDocumentInput {
21
+ title?: string;
22
+ content?: string;
23
+ }
24
+ /**
25
+ * Result of document operations, extends Document with required id.
26
+ */
27
+ export type DocumentResult = Document & {
28
+ id: string;
29
+ };
30
+ /**
31
+ * Input for creating a new node.
32
+ */
33
+ export interface CreateNodeInput {
34
+ /** Node title (required) */
35
+ title: string;
36
+ /** Node description/content */
37
+ content?: string;
38
+ /** Parent node ID for hierarchical structure */
39
+ parentNodeId?: string;
40
+ /** Canvas width (defaults to 1920) */
41
+ canvasWidth?: number;
42
+ /** Canvas height (defaults to 1080) */
43
+ canvasHeight?: number;
44
+ /** Hierarchical level (auto-calculated from parent) */
45
+ level?: number;
46
+ }
47
+ /**
48
+ * Input for updating an existing node.
49
+ */
50
+ export interface UpdateNodeInput {
51
+ title?: string;
52
+ content?: string;
53
+ canvasWidth?: number;
54
+ canvasHeight?: number;
55
+ }
56
+ /**
57
+ * Result of node operations.
58
+ */
59
+ export type NodeResult = GraphNode & {
60
+ id: string;
61
+ };
File without changes