@drawtonomy/sdk 0.5.0 → 0.7.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.
@@ -0,0 +1,370 @@
1
+ // Convert parsed Lanelet2 OSM data into the shape primitives that the
2
+ // drawtonomy editor consumes (points, linestrings, lanes).
3
+ //
4
+ // The output is the intermediate `ImportedShapes` structure used by the
5
+ // editor's import flow — not a full DrawtonomySnapshot — because the editor
6
+ // needs per-shape positioning + bounds to drive its chunked import (camera
7
+ // alignment, progress reporting, etc.). Callers can wrap the result into
8
+ // shapes themselves.
9
+ import { latLonToCanvas } from './osmParser';
10
+ /** Default allocator: monotonic counter per shape kind. */
11
+ export function createShapeIdAllocator() {
12
+ const counters = { point: 0, linestring: 0, lane: 0 };
13
+ return {
14
+ next(kind) {
15
+ const id = `shape:${kind}_${counters[kind]}`;
16
+ counters[kind] += 1;
17
+ return id;
18
+ },
19
+ };
20
+ }
21
+ function getMiddlePoint(points) {
22
+ if (points.length === 0)
23
+ return { x: 0, y: 0 };
24
+ if (points.length === 1)
25
+ return points[0];
26
+ if (points.length === 2) {
27
+ return {
28
+ x: (points[0].x + points[1].x) / 2,
29
+ y: (points[0].y + points[1].y) / 2,
30
+ };
31
+ }
32
+ return points[Math.floor(points.length / 2)];
33
+ }
34
+ /**
35
+ * Decide whether to reverse the left and/or right boundary so the lane is
36
+ * interpreted consistently by the editor.
37
+ *
38
+ * In Lanelet2, the way's node order defines the lane direction (the left
39
+ * boundary's direction is the lane's direction). This routine preserves that
40
+ * direction whenever possible, only flipping a boundary when needed to keep
41
+ * the right-of-left invariant.
42
+ */
43
+ export function alignBoundaries(leftPoints, rightPoints) {
44
+ if (leftPoints.length < 2 || rightPoints.length < 2) {
45
+ return { invertLeft: false, invertRight: false };
46
+ }
47
+ const getEffective = (points, invert) => invert ? [...points].reverse() : points;
48
+ const areParallel = (left, right) => {
49
+ const ls = left[0], le = left[left.length - 1];
50
+ const rs = right[0], re = right[right.length - 1];
51
+ const parallelDist = Math.hypot(ls.x - rs.x, ls.y - rs.y) + Math.hypot(le.x - re.x, le.y - re.y);
52
+ const crossDist = Math.hypot(ls.x - re.x, ls.y - re.y) + Math.hypot(le.x - rs.x, le.y - rs.y);
53
+ return parallelDist <= crossDist;
54
+ };
55
+ const isRightOnRightSide = (left, right) => {
56
+ if (left.length < 2 || right.length < 1)
57
+ return false;
58
+ const leftDir = {
59
+ x: left[left.length - 1].x - left[0].x,
60
+ y: left[left.length - 1].y - left[0].y,
61
+ };
62
+ const leftMid = getMiddlePoint(left);
63
+ const rightMid = getMiddlePoint(right);
64
+ const leftToRight = {
65
+ x: rightMid.x - leftMid.x,
66
+ y: rightMid.y - leftMid.y,
67
+ };
68
+ // Cross product. In screen coordinates (Y points down), the sign is
69
+ // inverted vs. standard math convention; positive means "right is on the
70
+ // right of left".
71
+ const cross = leftDir.x * leftToRight.y - leftDir.y * leftToRight.x;
72
+ if (Math.abs(cross) < 1e-6) {
73
+ // Converging boundaries: fall back to comparing start-to-start.
74
+ const leftToRightStart = {
75
+ x: right[0].x - left[0].x,
76
+ y: right[0].y - left[0].y,
77
+ };
78
+ const crossStart = leftDir.x * leftToRightStart.y - leftDir.y * leftToRightStart.x;
79
+ return crossStart > 0;
80
+ }
81
+ return cross > 0;
82
+ };
83
+ if (areParallel(leftPoints, rightPoints) && isRightOnRightSide(leftPoints, rightPoints)) {
84
+ return { invertLeft: false, invertRight: false };
85
+ }
86
+ const rightInverted = getEffective(rightPoints, true);
87
+ if (areParallel(leftPoints, rightInverted) && isRightOnRightSide(leftPoints, rightInverted)) {
88
+ return { invertLeft: false, invertRight: true };
89
+ }
90
+ const leftInverted = getEffective(leftPoints, true);
91
+ if (areParallel(leftInverted, rightPoints) && isRightOnRightSide(leftInverted, rightPoints)) {
92
+ return { invertLeft: true, invertRight: false };
93
+ }
94
+ if (areParallel(leftInverted, rightInverted) && isRightOnRightSide(leftInverted, rightInverted)) {
95
+ return { invertLeft: true, invertRight: true };
96
+ }
97
+ if (areParallel(leftPoints, rightPoints)) {
98
+ return { invertLeft: false, invertRight: false };
99
+ }
100
+ if (areParallel(leftPoints, rightInverted)) {
101
+ return { invertLeft: false, invertRight: true };
102
+ }
103
+ return { invertLeft: false, invertRight: false };
104
+ }
105
+ /**
106
+ * Detect lane connectivity (next/prev) by matching the boundary endpoints of
107
+ * one lane to the start of another. Runs in O(n) using start/end node hash
108
+ * maps.
109
+ */
110
+ function detectLaneConnections(laneInfos) {
111
+ const connections = new Map();
112
+ laneInfos.forEach(info => {
113
+ connections.set(info.laneId, { next: [], prev: [] });
114
+ });
115
+ const getEffectiveNodes = (lane) => {
116
+ const leftStart = lane.invertLeft ? lane.leftLastNode : lane.leftFirstNode;
117
+ const leftEnd = lane.invertLeft ? lane.leftFirstNode : lane.leftLastNode;
118
+ const rightStart = lane.invertRight ? lane.rightLastNode : lane.rightFirstNode;
119
+ const rightEnd = lane.invertRight ? lane.rightFirstNode : lane.rightLastNode;
120
+ return { leftStart, leftEnd, rightStart, rightEnd };
121
+ };
122
+ const startNodeMap = new Map();
123
+ const endNodeMap = new Map();
124
+ for (const lane of laneInfos) {
125
+ const nodes = getEffectiveNodes(lane);
126
+ const startKey = `${nodes.leftStart}|${nodes.rightStart}`;
127
+ const endKey = `${nodes.leftEnd}|${nodes.rightEnd}`;
128
+ if (!startNodeMap.has(startKey))
129
+ startNodeMap.set(startKey, []);
130
+ startNodeMap.get(startKey).push(lane.laneId);
131
+ if (!endNodeMap.has(endKey))
132
+ endNodeMap.set(endKey, []);
133
+ endNodeMap.get(endKey).push(lane.laneId);
134
+ }
135
+ for (const lane of laneInfos) {
136
+ const nodes = getEffectiveNodes(lane);
137
+ const endKey = `${nodes.leftEnd}|${nodes.rightEnd}`;
138
+ const nextLaneIds = startNodeMap.get(endKey);
139
+ if (!nextLaneIds)
140
+ continue;
141
+ const conn = connections.get(lane.laneId);
142
+ for (const nextLaneId of nextLaneIds) {
143
+ if (nextLaneId === lane.laneId)
144
+ continue;
145
+ if (!conn.next.includes(nextLaneId)) {
146
+ conn.next.push(nextLaneId);
147
+ const nextConn = connections.get(nextLaneId);
148
+ if (!nextConn.prev.includes(lane.laneId)) {
149
+ nextConn.prev.push(lane.laneId);
150
+ }
151
+ }
152
+ }
153
+ }
154
+ return connections;
155
+ }
156
+ function emptyBounds() {
157
+ return {
158
+ minX: Infinity,
159
+ maxX: -Infinity,
160
+ minY: Infinity,
161
+ maxY: -Infinity,
162
+ centerX: 0,
163
+ centerY: 0,
164
+ width: 0,
165
+ height: 0,
166
+ };
167
+ }
168
+ /**
169
+ * Convert parsed OSM data into editor-ready point/linestring/lane records.
170
+ *
171
+ * Pass `selectedLaneIds` to restrict to a subset of lanelet relations
172
+ * (selective import); leave it `undefined` to import every `type=lanelet`
173
+ * relation.
174
+ */
175
+ export function osmToShapes(osmData, options = {}) {
176
+ const idAllocator = options.idAllocator ?? createShapeIdAllocator();
177
+ const result = {
178
+ points: [],
179
+ linestrings: [],
180
+ lanes: [],
181
+ bounds: emptyBounds(),
182
+ };
183
+ // Compute the projection center.
184
+ let minLat = Infinity, maxLat = -Infinity;
185
+ let minLon = Infinity, maxLon = -Infinity;
186
+ osmData.nodes.forEach(node => {
187
+ if (node.lat < minLat)
188
+ minLat = node.lat;
189
+ if (node.lat > maxLat)
190
+ maxLat = node.lat;
191
+ if (node.lon < minLon)
192
+ minLon = node.lon;
193
+ if (node.lon > maxLon)
194
+ maxLon = node.lon;
195
+ });
196
+ // For files emitted by drawtonomy, honor the embedded origin so page
197
+ // coordinates round-trip exactly. Otherwise center on the bbox.
198
+ const centerLat = osmData.drawtonomyOrigin?.lat ?? (minLat + maxLat) / 2;
199
+ const centerLon = osmData.drawtonomyOrigin?.lon ?? (minLon + maxLon) / 2;
200
+ result.originLatLon = { lat: centerLat, lon: centerLon };
201
+ const osmNodeToPointId = new Map();
202
+ const osmWayToLinestringId = new Map();
203
+ const pointIdToPoint = new Map();
204
+ const linestringIdToLinestring = new Map();
205
+ // Only `type=lanelet` relations become lanes. Other relation types
206
+ // (regulatory_element, multipolygon, etc.) are preserved verbatim via the
207
+ // OSM sidecar.
208
+ let laneletRelations = osmData.relations.filter(r => r.tags.type === 'lanelet');
209
+ if (options.selectedLaneIds) {
210
+ const selected = new Set(options.selectedLaneIds);
211
+ laneletRelations = laneletRelations.filter(r => selected.has(r.id));
212
+ }
213
+ // Collect ways and nodes referenced by the chosen lanelets.
214
+ const usedWayIds = new Set();
215
+ const usedNodeIds = new Set();
216
+ for (const relation of laneletRelations) {
217
+ for (const member of relation.members) {
218
+ if (member.type === 'way')
219
+ usedWayIds.add(member.ref);
220
+ }
221
+ }
222
+ for (const wayId of usedWayIds) {
223
+ const way = osmData.ways.get(wayId);
224
+ if (!way)
225
+ continue;
226
+ for (const ref of way.nodeRefs)
227
+ usedNodeIds.add(ref);
228
+ }
229
+ // Materialize points (one per OSM node, shared across boundaries).
230
+ for (const nodeId of usedNodeIds) {
231
+ const node = osmData.nodes.get(nodeId);
232
+ if (!node)
233
+ continue;
234
+ const { x, y } = latLonToCanvas(node.lat, node.lon, centerLat, centerLon);
235
+ const pointId = idAllocator.next('point');
236
+ osmNodeToPointId.set(nodeId, pointId);
237
+ const data = { id: pointId, x, y, osmId: nodeId };
238
+ result.points.push(data);
239
+ pointIdToPoint.set(pointId, data);
240
+ }
241
+ // Materialize linestrings (one per used way).
242
+ for (const wayId of usedWayIds) {
243
+ const way = osmData.ways.get(wayId);
244
+ if (!way)
245
+ continue;
246
+ const pointIds = [];
247
+ for (const nodeRef of way.nodeRefs) {
248
+ const pid = osmNodeToPointId.get(nodeRef);
249
+ if (pid)
250
+ pointIds.push(pid);
251
+ }
252
+ if (pointIds.length < 2)
253
+ continue;
254
+ const firstPoint = pointIdToPoint.get(pointIds[0]);
255
+ if (!firstPoint)
256
+ continue;
257
+ const linestringId = idAllocator.next('linestring');
258
+ osmWayToLinestringId.set(wayId, linestringId);
259
+ const data = {
260
+ id: linestringId,
261
+ x: firstPoint.x,
262
+ y: firstPoint.y,
263
+ pointIds,
264
+ osmId: wayId,
265
+ attributes: {
266
+ type: way.tags.type || 'line_thin',
267
+ subtype: way.tags.subtype || 'solid',
268
+ width: way.tags.width || '0.2',
269
+ },
270
+ };
271
+ result.linestrings.push(data);
272
+ linestringIdToLinestring.set(linestringId, data);
273
+ }
274
+ // Materialize lanes and collect info for connectivity detection.
275
+ const laneInfos = [];
276
+ for (const relation of laneletRelations) {
277
+ let leftWayId = null;
278
+ let rightWayId = null;
279
+ let leftBoundaryId = null;
280
+ let rightBoundaryId = null;
281
+ for (const member of relation.members) {
282
+ if (member.type !== 'way')
283
+ continue;
284
+ const linestringId = osmWayToLinestringId.get(member.ref);
285
+ if (!linestringId)
286
+ continue;
287
+ if (member.role === 'left') {
288
+ leftBoundaryId = linestringId;
289
+ leftWayId = member.ref;
290
+ }
291
+ else if (member.role === 'right') {
292
+ rightBoundaryId = linestringId;
293
+ rightWayId = member.ref;
294
+ }
295
+ }
296
+ if (!leftBoundaryId || !rightBoundaryId || !leftWayId || !rightWayId)
297
+ continue;
298
+ const leftWay = osmData.ways.get(leftWayId);
299
+ const rightWay = osmData.ways.get(rightWayId);
300
+ const leftLinestring = linestringIdToLinestring.get(leftBoundaryId);
301
+ const rightLinestring = linestringIdToLinestring.get(rightBoundaryId);
302
+ if (!leftWay || !rightWay || !leftLinestring || !rightLinestring)
303
+ continue;
304
+ const getPointCoords = (pointIds) => pointIds
305
+ .map(pid => pointIdToPoint.get(pid))
306
+ .filter((p) => p !== undefined)
307
+ .map(p => ({ x: p.x, y: p.y }));
308
+ const { invertLeft, invertRight } = alignBoundaries(getPointCoords(leftLinestring.pointIds), getPointCoords(rightLinestring.pointIds));
309
+ const laneId = idAllocator.next('lane');
310
+ laneInfos.push({
311
+ osmId: relation.id,
312
+ laneId,
313
+ leftWayId,
314
+ rightWayId,
315
+ leftFirstNode: leftWay.nodeRefs[0],
316
+ leftLastNode: leftWay.nodeRefs[leftWay.nodeRefs.length - 1],
317
+ rightFirstNode: rightWay.nodeRefs[0],
318
+ rightLastNode: rightWay.nodeRefs[rightWay.nodeRefs.length - 1],
319
+ invertLeft,
320
+ invertRight,
321
+ });
322
+ result.lanes.push({
323
+ id: laneId,
324
+ x: leftLinestring.x,
325
+ y: leftLinestring.y,
326
+ leftBoundaryId,
327
+ rightBoundaryId,
328
+ invertLeft,
329
+ invertRight,
330
+ osmId: relation.id,
331
+ attributes: {
332
+ type: 'lanelet',
333
+ subtype: relation.tags.subtype || 'road',
334
+ location: relation.tags.location || 'urban',
335
+ one_way: relation.tags.one_way || 'yes',
336
+ speed_limit: relation.tags.speed_limit || '30',
337
+ turn_direction: relation.tags.turn_direction || 'straight',
338
+ },
339
+ next: [],
340
+ prev: [],
341
+ });
342
+ }
343
+ const connections = detectLaneConnections(laneInfos);
344
+ for (const lane of result.lanes) {
345
+ const conn = connections.get(lane.id);
346
+ if (conn) {
347
+ lane.next = conn.next;
348
+ lane.prev = conn.prev;
349
+ }
350
+ }
351
+ // Bounds across all materialized points.
352
+ for (const point of result.points) {
353
+ if (point.x < result.bounds.minX)
354
+ result.bounds.minX = point.x;
355
+ if (point.x > result.bounds.maxX)
356
+ result.bounds.maxX = point.x;
357
+ if (point.y < result.bounds.minY)
358
+ result.bounds.minY = point.y;
359
+ if (point.y > result.bounds.maxY)
360
+ result.bounds.maxY = point.y;
361
+ }
362
+ if (result.points.length > 0) {
363
+ result.bounds.width = result.bounds.maxX - result.bounds.minX;
364
+ result.bounds.height = result.bounds.maxY - result.bounds.minY;
365
+ result.bounds.centerX = result.bounds.minX + result.bounds.width / 2;
366
+ result.bounds.centerY = result.bounds.minY + result.bounds.height / 2;
367
+ }
368
+ return result;
369
+ }
370
+ //# sourceMappingURL=osmToShapes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"osmToShapes.js","sourceRoot":"","sources":["../../src/exporter/osmToShapes.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -2,5 +2,6 @@ export * from './types';
2
2
  export * from './helpers';
3
3
  export * from './geometry';
4
4
  export { ExtensionClient } from './ExtensionClient';
5
+ export { parseDrawtonomySvg } from './snapshot';
5
6
  export * as exporter from './exporter';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAEnD,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAE/C,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAA"}
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ export * from './types';
3
3
  export * from './helpers';
4
4
  export * from './geometry';
5
5
  export { ExtensionClient } from './ExtensionClient';
6
+ export { parseDrawtonomySvg } from './snapshot';
6
7
  // Exporter sub-module is also available via "@drawtonomy/sdk/exporter".
7
8
  export * as exporter from './exporter';
8
9
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,wEAAwE;AACxE,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/C,wEAAwE;AACxE,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAA"}
@@ -0,0 +1,9 @@
1
+ import type { DrawtonomySnapshot } from './types';
2
+ /**
3
+ * Parse a `.drawtonomy.svg` source string and return the embedded
4
+ * DrawtonomySnapshot. Returns `null` if the file is not a drawtonomy SVG
5
+ * (e.g. plain SVG without an embedded snapshot) or the embedded payload is
6
+ * malformed.
7
+ */
8
+ export declare function parseDrawtonomySvg(svgContent: string): DrawtonomySnapshot | null;
9
+ //# sourceMappingURL=snapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAkCjD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAgBhF"}
@@ -0,0 +1,60 @@
1
+ // Parse a `.drawtonomy.svg` file (a regular SVG with a base64-encoded
2
+ // snapshot embedded in `data-drawtonomy-snapshot`) back into a
3
+ // DrawtonomySnapshot. The legacy attribute name `data-drawauto-snapshot`
4
+ // is also accepted for backwards compatibility.
5
+ /**
6
+ * Decode a base64-encoded UTF-8 string. Mirrors the encoder used when the
7
+ * editor produces .drawtonomy.svg files: `btoa(unescape(encodeURIComponent(json)))`.
8
+ */
9
+ function base64DecodeUtf8(encoded) {
10
+ if (typeof atob === 'function') {
11
+ return decodeURIComponent(escape(atob(encoded)));
12
+ }
13
+ // Node fallback (Buffer is global in Node 18+).
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ const buf = globalThis.Buffer;
16
+ if (buf) {
17
+ return buf.from(encoded, 'base64').toString('utf-8');
18
+ }
19
+ throw new Error('No base64 decoder available in this environment');
20
+ }
21
+ /**
22
+ * Extract the value of an attribute from the root `<svg>` element of an SVG
23
+ * source string. Used instead of DOMParser so the parser works in plain
24
+ * Node without jsdom.
25
+ */
26
+ function extractRootSvgAttribute(svg, attrName) {
27
+ // Match the opening <svg ...> tag (allow line breaks inside).
28
+ const openTagMatch = svg.match(/<svg\b[^>]*>/);
29
+ if (!openTagMatch)
30
+ return null;
31
+ const openTag = openTagMatch[0];
32
+ const attrRegex = new RegExp(`\\b${attrName}="([^"]*)"`);
33
+ const m = openTag.match(attrRegex);
34
+ return m ? m[1] : null;
35
+ }
36
+ /**
37
+ * Parse a `.drawtonomy.svg` source string and return the embedded
38
+ * DrawtonomySnapshot. Returns `null` if the file is not a drawtonomy SVG
39
+ * (e.g. plain SVG without an embedded snapshot) or the embedded payload is
40
+ * malformed.
41
+ */
42
+ export function parseDrawtonomySvg(svgContent) {
43
+ if (!svgContent || typeof svgContent !== 'string')
44
+ return null;
45
+ const encoded = extractRootSvgAttribute(svgContent, 'data-drawtonomy-snapshot') ??
46
+ extractRootSvgAttribute(svgContent, 'data-drawauto-snapshot');
47
+ if (!encoded)
48
+ return null;
49
+ try {
50
+ const jsonString = base64DecodeUtf8(encoded);
51
+ const parsed = JSON.parse(jsonString);
52
+ if (!parsed || !Array.isArray(parsed.shapes))
53
+ return null;
54
+ return parsed;
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ }
60
+ //# sourceMappingURL=snapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,+DAA+D;AAC/D,yEAAyE;AACzE,gDAAgD;AAIhD;;;GAGG;AACH,SAAS,gBAAgB,CAAC,OAAe;IACvC,IAAI,OAAO,IAAI,KAAK,UAAU,EAAE,CAAC;QAC/B,OAAO,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAClD,CAAC;IACD,gDAAgD;IAChD,8DAA8D;IAC9D,MAAM,GAAG,GAAI,UAAkB,CAAC,MAAM,CAAA;IACtC,IAAI,GAAG,EAAE,CAAC;QACR,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;IACtD,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAA;AACpE,CAAC;AAED;;;;GAIG;AACH,SAAS,uBAAuB,CAAC,GAAW,EAAE,QAAgB;IAC5D,8DAA8D;IAC9D,MAAM,YAAY,GAAG,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,CAAA;IAC9C,IAAI,CAAC,YAAY;QAAE,OAAO,IAAI,CAAA;IAC9B,MAAM,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC,CAAA;IAC/B,MAAM,SAAS,GAAG,IAAI,MAAM,CAAC,MAAM,QAAQ,YAAY,CAAC,CAAA;IACxD,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IAClC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACxB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IACnD,IAAI,CAAC,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAE9D,MAAM,OAAO,GACX,uBAAuB,CAAC,UAAU,EAAE,0BAA0B,CAAC;QAC/D,uBAAuB,CAAC,UAAU,EAAE,wBAAwB,CAAC,CAAA;IAC/D,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IAEzB,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;QAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAuB,CAAA;QAC3D,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAA;QACzD,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawtonomy/sdk",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "SDK for building drawtonomy extensions",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -15,6 +15,11 @@
15
15
  "dist",
16
16
  "README.md"
17
17
  ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "test": "vitest run",
21
+ "prepublishOnly": "pnpm build"
22
+ },
18
23
  "keywords": [
19
24
  "drawtonomy",
20
25
  "extension",
@@ -27,9 +32,5 @@
27
32
  "jsdom": "29.0.1",
28
33
  "typescript": "5.9.3",
29
34
  "vitest": "4.1.2"
30
- },
31
- "scripts": {
32
- "build": "tsc",
33
- "test": "vitest run"
34
35
  }
35
- }
36
+ }