@xom11/whiteboard 0.24.2 → 0.25.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 (84) hide show
  1. package/README.md +84 -11
  2. package/dist/ai.d.mts +422 -566
  3. package/dist/ai.d.ts +422 -566
  4. package/dist/ai.js +1527 -407
  5. package/dist/ai.js.map +1 -1
  6. package/dist/ai.mjs +1008 -512
  7. package/dist/ai.mjs.map +1 -1
  8. package/dist/catalog.json +4 -4
  9. package/dist/{chunk-BKSXPNPQ.mjs → chunk-AYSFWUPK.mjs} +4 -3
  10. package/dist/chunk-AYSFWUPK.mjs.map +1 -0
  11. package/dist/chunk-B4NJJZFR.mjs +18 -0
  12. package/dist/chunk-B4NJJZFR.mjs.map +1 -0
  13. package/dist/{chunk-LVNCYP4J.mjs → chunk-CJBLJUWG.mjs} +5 -5
  14. package/dist/{chunk-LVNCYP4J.mjs.map → chunk-CJBLJUWG.mjs.map} +1 -1
  15. package/dist/{chunk-7WQXXEVR.mjs → chunk-ESVPQWHX.mjs} +5 -5
  16. package/dist/{chunk-7WQXXEVR.mjs.map → chunk-ESVPQWHX.mjs.map} +1 -1
  17. package/dist/{chunk-KRC2XOIG.mjs → chunk-I24QOHPU.mjs} +3 -3
  18. package/dist/{chunk-KRC2XOIG.mjs.map → chunk-I24QOHPU.mjs.map} +1 -1
  19. package/dist/{chunk-ZBJBQKJ2.mjs → chunk-IHUFOV7L.mjs} +4 -19
  20. package/dist/chunk-IHUFOV7L.mjs.map +1 -0
  21. package/dist/{chunk-AZIARTGX.mjs → chunk-M42TGYT6.mjs} +3 -3
  22. package/dist/{chunk-AZIARTGX.mjs.map → chunk-M42TGYT6.mjs.map} +1 -1
  23. package/dist/{chunk-45CGKJ7S.mjs → chunk-NDEZJKNY.mjs} +4 -4
  24. package/dist/{chunk-45CGKJ7S.mjs.map → chunk-NDEZJKNY.mjs.map} +1 -1
  25. package/dist/{chunk-BEZSQKPY.mjs → chunk-ONBCUWVI.mjs} +5 -4
  26. package/dist/chunk-ONBCUWVI.mjs.map +1 -0
  27. package/dist/{chunk-WM2VDYQA.mjs → chunk-REIJZDVZ.mjs} +4 -3
  28. package/dist/chunk-REIJZDVZ.mjs.map +1 -0
  29. package/dist/{chunk-2WF6KIGF.mjs → chunk-TB4CL25L.mjs} +9 -8
  30. package/dist/chunk-TB4CL25L.mjs.map +1 -0
  31. package/dist/chunk-VNCCIV6O.mjs +938 -0
  32. package/dist/chunk-VNCCIV6O.mjs.map +1 -0
  33. package/dist/{chunk-CGZZO4BX.mjs → chunk-VRHWDZ66.mjs} +5 -5
  34. package/dist/{chunk-CGZZO4BX.mjs.map → chunk-VRHWDZ66.mjs.map} +1 -1
  35. package/dist/{chunk-4DS3MKID.mjs → chunk-YSJOVBCD.mjs} +4 -4
  36. package/dist/{chunk-4DS3MKID.mjs.map → chunk-YSJOVBCD.mjs.map} +1 -1
  37. package/dist/geometry-2d.d.mts +2 -2
  38. package/dist/geometry-2d.d.ts +2 -2
  39. package/dist/geometry-2d.js +1383 -23
  40. package/dist/geometry-2d.js.map +1 -1
  41. package/dist/geometry-2d.mjs +6 -5
  42. package/dist/geometry-3d.d.mts +2 -2
  43. package/dist/geometry-3d.d.ts +2 -2
  44. package/dist/geometry-3d.js +2 -2
  45. package/dist/geometry-3d.js.map +1 -1
  46. package/dist/geometry-3d.mjs +5 -4
  47. package/dist/graph-2d.d.mts +2 -2
  48. package/dist/graph-2d.d.ts +2 -2
  49. package/dist/graph-2d.js +2 -2
  50. package/dist/graph-2d.js.map +1 -1
  51. package/dist/graph-2d.mjs +8 -7
  52. package/dist/{host-ZIQ77W33.mjs → host-A64ITWVX.mjs} +7 -6
  53. package/dist/host-A64ITWVX.mjs.map +1 -0
  54. package/dist/{host-EPZCNFLH.mjs → host-L7FMFZUW.mjs} +226 -29
  55. package/dist/host-L7FMFZUW.mjs.map +1 -0
  56. package/dist/{host-LKCMYEAV.mjs → host-QK53UYMD.mjs} +11 -10
  57. package/dist/host-QK53UYMD.mjs.map +1 -0
  58. package/dist/index.d.mts +3 -3
  59. package/dist/index.d.ts +3 -3
  60. package/dist/index.js +1414 -54
  61. package/dist/index.js.map +1 -1
  62. package/dist/index.mjs +18 -17
  63. package/dist/index.mjs.map +1 -1
  64. package/dist/latex.d.mts +2 -2
  65. package/dist/latex.d.ts +2 -2
  66. package/dist/render-3WTY7NZB.mjs +9 -0
  67. package/dist/{render-SA4JTOW3.mjs.map → render-3WTY7NZB.mjs.map} +1 -1
  68. package/dist/serialize-SRJVKYUG.mjs +8 -0
  69. package/dist/{serialize-JAVOU22E.mjs.map → serialize-SRJVKYUG.mjs.map} +1 -1
  70. package/dist/{types-vtvyKGAA.d.mts → types-DWRyCa2m.d.mts} +187 -2
  71. package/dist/{types-Crbefnfe.d.ts → types-DWRyCa2m.d.ts} +187 -2
  72. package/package.json +1 -1
  73. package/dist/chunk-2WF6KIGF.mjs.map +0 -1
  74. package/dist/chunk-BEZSQKPY.mjs.map +0 -1
  75. package/dist/chunk-BKSXPNPQ.mjs.map +0 -1
  76. package/dist/chunk-WM2VDYQA.mjs.map +0 -1
  77. package/dist/chunk-ZBJBQKJ2.mjs.map +0 -1
  78. package/dist/host-EPZCNFLH.mjs.map +0 -1
  79. package/dist/host-LKCMYEAV.mjs.map +0 -1
  80. package/dist/host-ZIQ77W33.mjs.map +0 -1
  81. package/dist/render-SA4JTOW3.mjs +0 -8
  82. package/dist/serialize-JAVOU22E.mjs +0 -7
  83. package/dist/types-DxlMPh-6.d.mts +0 -49
  84. package/dist/types-DxlMPh-6.d.ts +0 -49
package/dist/latex.d.mts CHANGED
@@ -1,7 +1,7 @@
1
- import { B as BaseStampCustomData, S as StampType } from './types-vtvyKGAA.mjs';
1
+ import { B as BaseStampCustomData, S as StampType } from './types-DWRyCa2m.mjs';
2
+ import 'zod';
2
3
  import 'react';
3
4
  import '@excalidraw/excalidraw/element/types';
4
- import './types-DxlMPh-6.mjs';
5
5
 
6
6
  interface LatexCustomData extends BaseStampCustomData {
7
7
  kind: 'latex';
package/dist/latex.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { B as BaseStampCustomData, S as StampType } from './types-Crbefnfe.js';
1
+ import { B as BaseStampCustomData, S as StampType } from './types-DWRyCa2m.js';
2
+ import 'zod';
2
3
  import 'react';
3
4
  import '@excalidraw/excalidraw/element/types';
4
- import './types-DxlMPh-6.js';
5
5
 
6
6
  interface LatexCustomData extends BaseStampCustomData {
7
7
  kind: 'latex';
@@ -0,0 +1,9 @@
1
+ "use client";
2
+ export { renderGraphSvgFromState } from './chunk-CJBLJUWG.mjs';
3
+ import './chunk-M42TGYT6.mjs';
4
+ import './chunk-AYSFWUPK.mjs';
5
+ import './chunk-ICR4CVOE.mjs';
6
+ import './chunk-IHUFOV7L.mjs';
7
+ import './chunk-B4NJJZFR.mjs';
8
+ //# sourceMappingURL=render-3WTY7NZB.mjs.map
9
+ //# sourceMappingURL=render-3WTY7NZB.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"render-SA4JTOW3.mjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"render-3WTY7NZB.mjs"}
@@ -0,0 +1,8 @@
1
+ "use client";
2
+ export { parseSceneState, stringifySceneState } from './chunk-I24QOHPU.mjs';
3
+ import './chunk-REIJZDVZ.mjs';
4
+ import './chunk-IHUFOV7L.mjs';
5
+ import './chunk-73Q7ADVL.mjs';
6
+ import './chunk-B4NJJZFR.mjs';
7
+ //# sourceMappingURL=serialize-SRJVKYUG.mjs.map
8
+ //# sourceMappingURL=serialize-SRJVKYUG.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"serialize-JAVOU22E.mjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"serialize-SRJVKYUG.mjs"}
@@ -1,6 +1,174 @@
1
+ import { z } from 'zod';
1
2
  import { ReactNode, ComponentType, RefAttributes } from 'react';
2
3
  import { ExcalidrawElement } from '@excalidraw/excalidraw/element/types';
3
- import { S as State } from './types-DxlMPh-6.mjs';
4
+
5
+ declare const NameZ: z.ZodString;
6
+
7
+ type Name = z.infer<typeof NameZ>;
8
+ type DslPointT = {
9
+ name: Name;
10
+ kind: 'free';
11
+ x: number;
12
+ y: number;
13
+ } | {
14
+ name: Name;
15
+ kind: 'midpoint';
16
+ p1: Name;
17
+ p2: Name;
18
+ } | {
19
+ name: Name;
20
+ kind: 'onSegment';
21
+ segmentId: Name;
22
+ t: number;
23
+ } | {
24
+ name: Name;
25
+ kind: 'onLine';
26
+ lineId: Name;
27
+ t: number;
28
+ } | {
29
+ name: Name;
30
+ kind: 'onCircle';
31
+ circleId: Name;
32
+ theta: number;
33
+ } | {
34
+ name: Name;
35
+ kind: 'perpFoot';
36
+ from: Name;
37
+ onLine: Name;
38
+ } | {
39
+ name: Name;
40
+ kind: 'circumcenter';
41
+ vertices: [Name, Name, Name];
42
+ } | {
43
+ name: Name;
44
+ kind: 'incenter';
45
+ vertices: [Name, Name, Name];
46
+ } | {
47
+ name: Name;
48
+ kind: 'centroid';
49
+ vertices: [Name, Name, Name];
50
+ } | {
51
+ name: Name;
52
+ kind: 'orthocenter';
53
+ vertices: [Name, Name, Name];
54
+ } | {
55
+ name: Name;
56
+ kind: 'intersection';
57
+ ref1: Name;
58
+ ref2: Name;
59
+ branch?: 0 | 1;
60
+ };
61
+ type DslShapeT = {
62
+ name: Name;
63
+ kind: 'segment';
64
+ p1: Name;
65
+ p2: Name;
66
+ } | {
67
+ name: Name;
68
+ kind: 'line';
69
+ p1: Name;
70
+ p2: Name;
71
+ } | {
72
+ name: Name;
73
+ kind: 'ray';
74
+ origin: Name;
75
+ through: Name;
76
+ } | {
77
+ name: Name;
78
+ kind: 'polygon';
79
+ vertices: Name[];
80
+ } | {
81
+ name: Name;
82
+ kind: 'perpendicular';
83
+ throughPoint: Name;
84
+ toLine: Name;
85
+ } | {
86
+ name: Name;
87
+ kind: 'parallel';
88
+ throughPoint: Name;
89
+ toLine: Name;
90
+ } | {
91
+ name: Name;
92
+ kind: 'perpBisector';
93
+ p1: Name;
94
+ p2: Name;
95
+ } | {
96
+ name: Name;
97
+ kind: 'angleBisector';
98
+ p1: Name;
99
+ vertex: Name;
100
+ p2: Name;
101
+ } | {
102
+ name: Name;
103
+ kind: 'tangent';
104
+ throughPoint: Name;
105
+ toCircle: Name;
106
+ branch?: 0 | 1 | 'on';
107
+ } | {
108
+ name: Name;
109
+ kind: 'circleCP';
110
+ center: Name;
111
+ surfacePoint: Name;
112
+ } | {
113
+ name: Name;
114
+ kind: 'circle3';
115
+ p1: Name;
116
+ p2: Name;
117
+ p3: Name;
118
+ };
119
+ type DslInputT = {
120
+ version: 1;
121
+ points: DslPointT[];
122
+ shapes: DslShapeT[];
123
+ };
124
+
125
+ type SceneObject<A = Record<string, unknown>> = {
126
+ id: string;
127
+ kind: string;
128
+ label: string;
129
+ visible: boolean;
130
+ locked: boolean;
131
+ layer: string;
132
+ schemaVersion: number;
133
+ attrs: A;
134
+ };
135
+ type View2D = {
136
+ readonly bbox: readonly [number, number, number, number];
137
+ readonly showAxis: boolean;
138
+ readonly showGrid: boolean;
139
+ };
140
+ type View3D = {
141
+ readonly bbox3D: readonly [number, number, number, number, number, number];
142
+ readonly azimuth: number;
143
+ readonly elevation: number;
144
+ };
145
+ type ViewGraph2D = {
146
+ readonly xMin: number;
147
+ readonly xMax: number;
148
+ readonly yMin: number;
149
+ readonly yMax: number;
150
+ readonly showAxis: boolean;
151
+ readonly showGrid: boolean;
152
+ };
153
+ type StateMeta = {
154
+ readonly domain: '2d';
155
+ readonly version: number;
156
+ readonly view: View2D;
157
+ } | {
158
+ readonly domain: '3d';
159
+ readonly version: number;
160
+ readonly view: View3D;
161
+ } | {
162
+ readonly domain: 'graph2d';
163
+ readonly version: number;
164
+ readonly view: ViewGraph2D;
165
+ };
166
+ type State = {
167
+ readonly objects: Readonly<Record<string, SceneObject>>;
168
+ readonly order: readonly string[];
169
+ readonly counter: number;
170
+ readonly meta: StateMeta;
171
+ };
4
172
 
5
173
  /** Minimal client-safe response required by the geometry AI editor. */
6
174
  type AiFigureUiResult = {
@@ -10,12 +178,29 @@ type AiFigureUiResult = {
10
178
  ok: false;
11
179
  message: string;
12
180
  };
181
+ /**
182
+ * Progress event emitted bởi streaming generator. Số token output đã sinh.
183
+ * Editor dùng để hiển thị "Đang nhận N token..." realtime.
184
+ */
185
+ interface AiFigureProgress {
186
+ /** Số token output đã sinh tính đến lúc emit. */
187
+ tokens: number;
188
+ }
13
189
  /**
14
190
  * Consumer-provided bridge to a server-side `generateFigure()` call.
15
191
  * Implementations must keep API credentials outside the browser bundle.
192
+ *
193
+ * `onProgress` là optional: nếu consumer dùng streaming endpoint (SSE),
194
+ * forward chunk events vào đây. Non-streaming impl bỏ qua.
195
+ *
196
+ * `currentDsl` (MỚI cho multi-step refine): khi caller có hình hiện tại
197
+ * (state.order.length > 0) và muốn AI sửa/thêm → pass currentDsl. Consumer
198
+ * branch sang refine endpoint. Không pass → build endpoint cũ.
16
199
  */
17
200
  type GenerateGeometryFigure = (problem: string, options: {
18
201
  signal: AbortSignal;
202
+ onProgress?: (info: AiFigureProgress) => void;
203
+ currentDsl?: DslInputT;
19
204
  }) => Promise<AiFigureUiResult>;
20
205
  /**
21
206
  * Kết quả trả về từ `restoreFileFromCustomData`. Chứa đủ thông tin để
@@ -125,4 +310,4 @@ interface StampType<TCustomData extends BaseStampCustomData = BaseStampCustomDat
125
310
  experimental?: boolean;
126
311
  }
127
312
 
128
- export type { AiFigureUiResult as A, BaseStampCustomData as B, GenerateGeometryFigure as G, StampType as S };
313
+ export type { AiFigureProgress as A, BaseStampCustomData as B, DslInputT as D, GenerateGeometryFigure as G, StampType as S, AiFigureUiResult as a, State as b };
@@ -1,6 +1,174 @@
1
+ import { z } from 'zod';
1
2
  import { ReactNode, ComponentType, RefAttributes } from 'react';
2
3
  import { ExcalidrawElement } from '@excalidraw/excalidraw/element/types';
3
- import { S as State } from './types-DxlMPh-6.js';
4
+
5
+ declare const NameZ: z.ZodString;
6
+
7
+ type Name = z.infer<typeof NameZ>;
8
+ type DslPointT = {
9
+ name: Name;
10
+ kind: 'free';
11
+ x: number;
12
+ y: number;
13
+ } | {
14
+ name: Name;
15
+ kind: 'midpoint';
16
+ p1: Name;
17
+ p2: Name;
18
+ } | {
19
+ name: Name;
20
+ kind: 'onSegment';
21
+ segmentId: Name;
22
+ t: number;
23
+ } | {
24
+ name: Name;
25
+ kind: 'onLine';
26
+ lineId: Name;
27
+ t: number;
28
+ } | {
29
+ name: Name;
30
+ kind: 'onCircle';
31
+ circleId: Name;
32
+ theta: number;
33
+ } | {
34
+ name: Name;
35
+ kind: 'perpFoot';
36
+ from: Name;
37
+ onLine: Name;
38
+ } | {
39
+ name: Name;
40
+ kind: 'circumcenter';
41
+ vertices: [Name, Name, Name];
42
+ } | {
43
+ name: Name;
44
+ kind: 'incenter';
45
+ vertices: [Name, Name, Name];
46
+ } | {
47
+ name: Name;
48
+ kind: 'centroid';
49
+ vertices: [Name, Name, Name];
50
+ } | {
51
+ name: Name;
52
+ kind: 'orthocenter';
53
+ vertices: [Name, Name, Name];
54
+ } | {
55
+ name: Name;
56
+ kind: 'intersection';
57
+ ref1: Name;
58
+ ref2: Name;
59
+ branch?: 0 | 1;
60
+ };
61
+ type DslShapeT = {
62
+ name: Name;
63
+ kind: 'segment';
64
+ p1: Name;
65
+ p2: Name;
66
+ } | {
67
+ name: Name;
68
+ kind: 'line';
69
+ p1: Name;
70
+ p2: Name;
71
+ } | {
72
+ name: Name;
73
+ kind: 'ray';
74
+ origin: Name;
75
+ through: Name;
76
+ } | {
77
+ name: Name;
78
+ kind: 'polygon';
79
+ vertices: Name[];
80
+ } | {
81
+ name: Name;
82
+ kind: 'perpendicular';
83
+ throughPoint: Name;
84
+ toLine: Name;
85
+ } | {
86
+ name: Name;
87
+ kind: 'parallel';
88
+ throughPoint: Name;
89
+ toLine: Name;
90
+ } | {
91
+ name: Name;
92
+ kind: 'perpBisector';
93
+ p1: Name;
94
+ p2: Name;
95
+ } | {
96
+ name: Name;
97
+ kind: 'angleBisector';
98
+ p1: Name;
99
+ vertex: Name;
100
+ p2: Name;
101
+ } | {
102
+ name: Name;
103
+ kind: 'tangent';
104
+ throughPoint: Name;
105
+ toCircle: Name;
106
+ branch?: 0 | 1 | 'on';
107
+ } | {
108
+ name: Name;
109
+ kind: 'circleCP';
110
+ center: Name;
111
+ surfacePoint: Name;
112
+ } | {
113
+ name: Name;
114
+ kind: 'circle3';
115
+ p1: Name;
116
+ p2: Name;
117
+ p3: Name;
118
+ };
119
+ type DslInputT = {
120
+ version: 1;
121
+ points: DslPointT[];
122
+ shapes: DslShapeT[];
123
+ };
124
+
125
+ type SceneObject<A = Record<string, unknown>> = {
126
+ id: string;
127
+ kind: string;
128
+ label: string;
129
+ visible: boolean;
130
+ locked: boolean;
131
+ layer: string;
132
+ schemaVersion: number;
133
+ attrs: A;
134
+ };
135
+ type View2D = {
136
+ readonly bbox: readonly [number, number, number, number];
137
+ readonly showAxis: boolean;
138
+ readonly showGrid: boolean;
139
+ };
140
+ type View3D = {
141
+ readonly bbox3D: readonly [number, number, number, number, number, number];
142
+ readonly azimuth: number;
143
+ readonly elevation: number;
144
+ };
145
+ type ViewGraph2D = {
146
+ readonly xMin: number;
147
+ readonly xMax: number;
148
+ readonly yMin: number;
149
+ readonly yMax: number;
150
+ readonly showAxis: boolean;
151
+ readonly showGrid: boolean;
152
+ };
153
+ type StateMeta = {
154
+ readonly domain: '2d';
155
+ readonly version: number;
156
+ readonly view: View2D;
157
+ } | {
158
+ readonly domain: '3d';
159
+ readonly version: number;
160
+ readonly view: View3D;
161
+ } | {
162
+ readonly domain: 'graph2d';
163
+ readonly version: number;
164
+ readonly view: ViewGraph2D;
165
+ };
166
+ type State = {
167
+ readonly objects: Readonly<Record<string, SceneObject>>;
168
+ readonly order: readonly string[];
169
+ readonly counter: number;
170
+ readonly meta: StateMeta;
171
+ };
4
172
 
5
173
  /** Minimal client-safe response required by the geometry AI editor. */
6
174
  type AiFigureUiResult = {
@@ -10,12 +178,29 @@ type AiFigureUiResult = {
10
178
  ok: false;
11
179
  message: string;
12
180
  };
181
+ /**
182
+ * Progress event emitted bởi streaming generator. Số token output đã sinh.
183
+ * Editor dùng để hiển thị "Đang nhận N token..." realtime.
184
+ */
185
+ interface AiFigureProgress {
186
+ /** Số token output đã sinh tính đến lúc emit. */
187
+ tokens: number;
188
+ }
13
189
  /**
14
190
  * Consumer-provided bridge to a server-side `generateFigure()` call.
15
191
  * Implementations must keep API credentials outside the browser bundle.
192
+ *
193
+ * `onProgress` là optional: nếu consumer dùng streaming endpoint (SSE),
194
+ * forward chunk events vào đây. Non-streaming impl bỏ qua.
195
+ *
196
+ * `currentDsl` (MỚI cho multi-step refine): khi caller có hình hiện tại
197
+ * (state.order.length > 0) và muốn AI sửa/thêm → pass currentDsl. Consumer
198
+ * branch sang refine endpoint. Không pass → build endpoint cũ.
16
199
  */
17
200
  type GenerateGeometryFigure = (problem: string, options: {
18
201
  signal: AbortSignal;
202
+ onProgress?: (info: AiFigureProgress) => void;
203
+ currentDsl?: DslInputT;
19
204
  }) => Promise<AiFigureUiResult>;
20
205
  /**
21
206
  * Kết quả trả về từ `restoreFileFromCustomData`. Chứa đủ thông tin để
@@ -125,4 +310,4 @@ interface StampType<TCustomData extends BaseStampCustomData = BaseStampCustomDat
125
310
  experimental?: boolean;
126
311
  }
127
312
 
128
- export type { AiFigureUiResult as A, BaseStampCustomData as B, GenerateGeometryFigure as G, StampType as S };
313
+ export type { AiFigureProgress as A, BaseStampCustomData as B, DslInputT as D, GenerateGeometryFigure as G, StampType as S, AiFigureUiResult as a, State as b };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xom11/whiteboard",
3
- "version": "0.24.2",
3
+ "version": "0.25.0",
4
4
  "private": false,
5
5
  "description": "Excalidraw + JSXGraph + KaTeX whiteboard component (drawing, geometry stamps, LaTeX stamps).",
6
6
  "repository": {