@chestnutlabs/gcode-dialects 0.16.0 → 0.17.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.
@@ -19,24 +19,38 @@ export interface RangeMarker {
19
19
  * value of 0 acts as a range terminator (back to unknown/none).
20
20
  */
21
21
  export declare function applyMarkerRanges(ir: ToolpathIR, markers: RangeMarker[], write: (segStart: number, segEnd: number, value: number) => void): number;
22
- /** One end of a wipe bracket (DD-016, #182): the source byte and whether it OPENED the bracket. */
23
- export interface WipeMark {
22
+ /** One end of a housekeeping bracket (DD-016/DD-026 D3): source byte + whether it OPENED the bracket. */
23
+ export interface BracketMark {
24
24
  srcByte: number;
25
25
  open: boolean;
26
26
  }
27
+ /** One end of a wipe bracket (DD-016, #182). Structurally a {@link BracketMark}; kept for compatibility. */
28
+ export type WipeMark = BracketMark;
27
29
  /**
28
30
  * Classify a comment as a PrusaSlicer/OrcaSlicer wipe-bracket marker. The convention is bare
29
31
  * `;WIPE_START` / `;WIPE_END` (the semicolon is already stripped before onComment); tolerant of a
30
32
  * leading space and casing. Returns null for any other comment.
31
33
  */
32
34
  export declare function matchWipeComment(comment: string): 'start' | 'end' | null;
35
+ /**
36
+ * Classify a comment as a bare `<NAME>_START` / `<NAME>_END` bracket marker (semicolon already stripped
37
+ * before onComment; case/space tolerant). Used for wipe (DD-016) and the DD-026 D3 housekeeping brackets
38
+ * (`FLUSH_START/END`, `WIPE_TOWER_START/END`). Returns null for any other comment.
39
+ */
40
+ export declare function matchBracketComment(comment: string, name: string): 'start' | 'end' | null;
33
41
  /**
34
42
  * Resolve WIPE_START/END byte marks to segment ranges and add `MoveKind.Wipe` additively via the sink
35
- * (DD-016 §4.2). Segments emitted between a START and its END are the wipe moves. Unbalanced or nested
36
- * markers degrade safely: a stray END is ignored, a second START before an END is folded into the open
37
- * bracket, and a START left open at end-of-file closes at the last segment. Returns ranges applied.
43
+ * (DD-016 §4.2). Segments emitted between a START and its END are the wipe moves. Returns ranges applied.
38
44
  */
39
45
  export declare function applyWipeRanges(ir: ToolpathIR, marks: WipeMark[], sink: AnnotationSink): number;
46
+ /**
47
+ * Resolve a housekeeping bracket (DD-026 D3) — e.g. Bambu `FLUSH_START/END` → `FeatureRole.Purge`,
48
+ * `WIPE_TOWER_START/END` → `FeatureRole.WipeTower` — to segment ranges and set `role` on the enclosed
49
+ * segments. Apply this AFTER the `;TYPE:`/`; FEATURE:` markers so the explicit bracket wins over the
50
+ * surrounding role (the classifier then excludes the whole bracket from `modelBounds`). Returns ranges
51
+ * applied.
52
+ */
53
+ export declare function applyFeatureBracketRanges(ir: ToolpathIR, marks: BracketMark[], role: number, sink: AnnotationSink): number;
40
54
  export interface SegMarker {
41
55
  segIndex: number;
42
56
  value: number;
@@ -1 +1 @@
1
- {"version":3,"file":"annotate.d.ts","sourceRoot":"","sources":["../src/annotate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAY,KAAK,eAAe,EAAE,KAAK,MAAM,EAAE,KAAK,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC3G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAUrE;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,WAAW,EAAE,EACtB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAC/D,MAAM,CAYR;AAED,mGAAmG;AACnG,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,IAAI,CAKxE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,cAAc,GAAG,MAAM,CAwB/F;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,EAAE,EACpB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAC/D,MAAM,CAYR;AAED,uGAAuG;AACvG,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAGpF;AAED,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAU9D;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EAAE,EAChB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,OAAO,EAAE,MAAM,GAAG,SAAS,GAC1B,eAAe,CAejB;AASD,yFAAyF;AACzF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAkB5D;AAED,mGAAmG;AACnG,qBAAa,kBAAkB;IAEjB,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IADjD,OAAO,CAAC,MAAM,CAAgF;gBACjE,oBAAoB,SAAa;IAE9D,uFAAuF;IACvF,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,mFAAmF;IACnF,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI;CA8BlD"}
1
+ {"version":3,"file":"annotate.d.ts","sourceRoot":"","sources":["../src/annotate.ts"],"names":[],"mappings":"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"}
package/dist/annotate.js CHANGED
@@ -45,20 +45,29 @@ export function applyMarkerRanges(ir, markers, write) {
45
45
  * leading space and casing. Returns null for any other comment.
46
46
  */
47
47
  export function matchWipeComment(comment) {
48
+ return matchBracketComment(comment, 'WIPE');
49
+ }
50
+ /**
51
+ * Classify a comment as a bare `<NAME>_START` / `<NAME>_END` bracket marker (semicolon already stripped
52
+ * before onComment; case/space tolerant). Used for wipe (DD-016) and the DD-026 D3 housekeeping brackets
53
+ * (`FLUSH_START/END`, `WIPE_TOWER_START/END`). Returns null for any other comment.
54
+ */
55
+ export function matchBracketComment(comment, name) {
48
56
  const t = comment.trim().toUpperCase();
49
- if (t === 'WIPE_START')
57
+ const n = name.toUpperCase();
58
+ if (t === `${n}_START`)
50
59
  return 'start';
51
- if (t === 'WIPE_END')
60
+ if (t === `${n}_END`)
52
61
  return 'end';
53
62
  return null;
54
63
  }
55
64
  /**
56
- * Resolve WIPE_START/END byte marks to segment ranges and add `MoveKind.Wipe` additively via the sink
57
- * (DD-016 §4.2). Segments emitted between a START and its END are the wipe moves. Unbalanced or nested
58
- * markers degrade safely: a stray END is ignored, a second START before an END is folded into the open
59
- * bracket, and a START left open at end-of-file closes at the last segment. Returns ranges applied.
65
+ * Resolve balanced open/close bracket marks to inclusive segment ranges and hand each to `emit`. Shared
66
+ * by the wipe (MoveKind) and DD-026 housekeeping-role (FeatureRole) brackets. Unbalanced or nested
67
+ * markers degrade safely: a stray END is ignored, a second START before an END folds into the open
68
+ * bracket, and a START left open at end-of-file closes at the last segment. Returns ranges emitted.
60
69
  */
61
- export function applyWipeRanges(ir, marks, sink) {
70
+ function forEachBracketRange(ir, marks, emit) {
62
71
  let applied = 0;
63
72
  let openByte = -1;
64
73
  for (const mark of marks) {
@@ -68,9 +77,9 @@ export function applyWipeRanges(ir, marks, sink) {
68
77
  }
69
78
  else if (openByte >= 0) {
70
79
  const start = segAtOrAfterByte(ir, openByte);
71
- const end = segAtOrAfterByte(ir, mark.srcByte); // first segment AFTER the wipe block
80
+ const end = segAtOrAfterByte(ir, mark.srcByte); // first segment AFTER the block
72
81
  if (end - 1 >= start) {
73
- sink.addMoveKind(start, end - 1, MoveKind.Wipe);
82
+ emit(start, end - 1);
74
83
  applied++;
75
84
  }
76
85
  openByte = -1;
@@ -79,12 +88,29 @@ export function applyWipeRanges(ir, marks, sink) {
79
88
  if (openByte >= 0) {
80
89
  const start = segAtOrAfterByte(ir, openByte);
81
90
  if (ir.segments.count - 1 >= start) {
82
- sink.addMoveKind(start, ir.segments.count - 1, MoveKind.Wipe);
91
+ emit(start, ir.segments.count - 1);
83
92
  applied++;
84
93
  }
85
94
  }
86
95
  return applied;
87
96
  }
97
+ /**
98
+ * Resolve WIPE_START/END byte marks to segment ranges and add `MoveKind.Wipe` additively via the sink
99
+ * (DD-016 §4.2). Segments emitted between a START and its END are the wipe moves. Returns ranges applied.
100
+ */
101
+ export function applyWipeRanges(ir, marks, sink) {
102
+ return forEachBracketRange(ir, marks, (start, end) => sink.addMoveKind(start, end, MoveKind.Wipe));
103
+ }
104
+ /**
105
+ * Resolve a housekeeping bracket (DD-026 D3) — e.g. Bambu `FLUSH_START/END` → `FeatureRole.Purge`,
106
+ * `WIPE_TOWER_START/END` → `FeatureRole.WipeTower` — to segment ranges and set `role` on the enclosed
107
+ * segments. Apply this AFTER the `;TYPE:`/`; FEATURE:` markers so the explicit bracket wins over the
108
+ * surrounding role (the classifier then excludes the whole bracket from `modelBounds`). Returns ranges
109
+ * applied.
110
+ */
111
+ export function applyFeatureBracketRanges(ir, marks, role, sink) {
112
+ return forEachBracketRange(ir, marks, (start, end) => sink.setFeature(start, end, role));
113
+ }
88
114
  /**
89
115
  * Apply SEGMENT-indexed markers (CommandEvent.segIndex — exact, no byte
90
116
  * resolution needed) as contiguous ranges; value 0 terminates (DD-005 phase 5).
@@ -1 +1 @@
1
- {"version":3,"file":"orca-bambu.d.ts","sourceRoot":"","sources":["../src/orca-bambu.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAkB,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAoErE,wBAAgB,SAAS,IAAI,cAAc,CA2G1C"}
1
+ {"version":3,"file":"orca-bambu.d.ts","sourceRoot":"","sources":["../src/orca-bambu.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAkB,cAAc,EAAE,MAAM,gBAAgB,CAAC;AA8ErE,wBAAgB,SAAS,IAAI,cAAc,CA0H1C"}
@@ -14,7 +14,7 @@
14
14
  import { FeatureRole } from '@chestnutlabs/toolpath-core';
15
15
  import { parseFilamentTotals, parseHmsToSeconds } from './prusaslicer.js';
16
16
  import { PrintingObjectTracker } from './object-markers.js';
17
- import { ThumbnailCollector, applyMarkerRanges, applyWipeRanges, bedFromPoints, matchWipeComment, parseAreaPoints, parseKeyValue } from './annotate.js';
17
+ import { ThumbnailCollector, applyFeatureBracketRanges, applyMarkerRanges, applyWipeRanges, bedFromPoints, matchBracketComment, matchWipeComment, parseAreaPoints, parseKeyValue } from './annotate.js';
18
18
  /** Orca/Bambu `; FEATURE:` names → DD-001 FeatureRole. */
19
19
  const FEATURE_MAP = {
20
20
  'inner wall': FeatureRole.Perimeter,
@@ -44,6 +44,8 @@ function stateFor(sink) {
44
44
  s = {
45
45
  features: [],
46
46
  wipes: [],
47
+ flushes: [],
48
+ wipeTowers: [],
47
49
  objectTracker: new PrintingObjectTracker(),
48
50
  bedPoints: null,
49
51
  thumbs: new ThumbnailCollector()
@@ -95,6 +97,17 @@ export function orcaBambu() {
95
97
  s.wipes.push({ srcByte, open: wipe === 'start' });
96
98
  return;
97
99
  }
100
+ // DD-026 D3 housekeeping brackets (checked after WIPE — WIPE_TOWER is a distinct token).
101
+ const flush = matchBracketComment(comment, 'FLUSH');
102
+ if (flush !== null) {
103
+ s.flushes.push({ srcByte, open: flush === 'start' });
104
+ return;
105
+ }
106
+ const wipeTower = matchBracketComment(comment, 'WIPE_TOWER');
107
+ if (wipeTower !== null) {
108
+ s.wipeTowers.push({ srcByte, open: wipeTower === 'start' });
109
+ return;
110
+ }
98
111
  if (s.thumbs.isActive || trimmed.toLowerCase().startsWith('thumbnail')) {
99
112
  s.thumbs.feed(comment, sink);
100
113
  return;
@@ -138,7 +151,11 @@ export function orcaBambu() {
138
151
  finalize(ir, sink) {
139
152
  const s = stateFor(sink);
140
153
  const features = applyMarkerRanges(ir, s.features, (a, b, v) => sink.setFeature(a, b, v));
141
- if (features > 0)
154
+ // Housekeeping brackets (DD-026 D3) override the surrounding ;TYPE: role — apply them AFTER the
155
+ // feature markers so the bracket range wins (setFeature fills, last write per segment holds).
156
+ const flush = applyFeatureBracketRanges(ir, s.flushes, FeatureRole.Purge, sink);
157
+ const wipeTower = applyFeatureBracketRanges(ir, s.wipeTowers, FeatureRole.WipeTower, sink);
158
+ if (features + flush + wipeTower > 0)
142
159
  sink.upgradeCapability('featureRoles', 'known');
143
160
  s.objectTracker.apply(ir, sink);
144
161
  if (applyWipeRanges(ir, s.wipes, sink) > 0)
@@ -1,16 +1,3 @@
1
- /**
2
- * Dialect registry & run composition (DD-005 §4.1/§4.5, as amended).
3
- *
4
- * Explicit, tree-shakeable: `createDialectRunner(adapters)` — no global
5
- * side-effect registry. The returned factory matches the parser's structural
6
- * `DialectRunnerFactory` shape (the parser never imports this package;
7
- * worker entries compose the two).
8
- *
9
- * Composition (amendment 1): detection picks at most ONE adapter per `kind`
10
- * (highest confidence wins; ties select none for that kind — never a guess),
11
- * and all selected adapters run together. Adapter exceptions are contained:
12
- * a throwing adapter is disabled for the run with an `adapter-failed` warning.
13
- */
14
1
  import type { DialectDetection, DialectMetadata, ToolpathIR, Warning } from '@chestnutlabs/toolpath-core';
15
2
  import type { CommandEvent, DialectAdapter } from './contracts.js';
16
3
  export interface DialectRunInput {
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAC1G,OAAO,KAAK,EAAE,YAAY,EAAe,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAKhF,MAAM,WAAW,eAAe;IAC9B,oFAAoF;IACpF,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC/B,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,SAAS,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,EAAE,EAAE,UAAU,GAAG,gBAAgB,CAAC;CAC5C;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,CAAC,KAAK,EAAE,eAAe,GAAG,UAAU,GAAG,IAAI,CAAC;CACtD;AAUD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,oBAAoB,CAsFpF"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAC1G,OAAO,KAAK,EAAE,YAAY,EAAe,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAKhF,MAAM,WAAW,eAAe;IAC9B,oFAAoF;IACpF,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC/B,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,SAAS,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,EAAE,EAAE,UAAU,GAAG,gBAAgB,CAAC;CAC5C;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,CAAC,KAAK,EAAE,eAAe,GAAG,UAAU,GAAG,IAAI,CAAC;CACtD;AAUD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,oBAAoB,CA4FpF"}
package/dist/registry.js CHANGED
@@ -1,3 +1,17 @@
1
+ /**
2
+ * Dialect registry & run composition (DD-005 §4.1/§4.5, as amended).
3
+ *
4
+ * Explicit, tree-shakeable: `createDialectRunner(adapters)` — no global
5
+ * side-effect registry. The returned factory matches the parser's structural
6
+ * `DialectRunnerFactory` shape (the parser never imports this package;
7
+ * worker entries compose the two).
8
+ *
9
+ * Composition (amendment 1): detection picks at most ONE adapter per `kind`
10
+ * (highest confidence wins; ties select none for that kind — never a guess),
11
+ * and all selected adapters run together. Adapter exceptions are contained:
12
+ * a throwing adapter is disabled for the run with an `adapter-failed` warning.
13
+ */
14
+ import { classifyModelBounds } from '@chestnutlabs/toolpath-core';
1
15
  import { BufferedAnnotationSink } from './sink.js';
2
16
  const CONFIDENCE_RANK = { known: 3, inferred: 2, approximated: 1, unavailable: 0 };
3
17
  export function createDialectRunner(adapters) {
@@ -76,6 +90,12 @@ export function createDialectRunner(adapters) {
76
90
  contain(a, () => a.adapter.finalize?.(ir, a.sink, a.config));
77
91
  a.sink.apply(ir, metadata, (code, message) => conflicts.push({ code, message, severity: 'warn', count: 1 }));
78
92
  }
93
+ // Now that every adapter has settled the object/feature channels, refresh the non-model
94
+ // classification authoritatively (DD-026 D4/D5/D7, lifecycle §5). Parse time saw empty
95
+ // channels; this is where membership/roles actually exist.
96
+ const { modelBounds, classification } = classifyModelBounds(ir.segments, ir.header.originOffset);
97
+ ir.modelBounds = modelBounds;
98
+ ir.header.capabilities.nonModelClassification = classification;
79
99
  for (const w of conflicts)
80
100
  ir.header.warnings.push(w);
81
101
  ir.header.dialects.push(...active.map((a) => ({ id: a.detection.dialectId, confidence: a.detection.confidence })));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chestnutlabs/gcode-dialects",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Slicer/firmware dialect adapters for ToolpathIR annotation (DD-005). Adapters annotate metadata and optional channels — they can never alter geometry.",
5
5
  "keywords": [
6
6
  "gcode",
@@ -45,6 +45,6 @@
45
45
  "test": "vitest run"
46
46
  },
47
47
  "dependencies": {
48
- "@chestnutlabs/toolpath-core": "0.16.0"
48
+ "@chestnutlabs/toolpath-core": "0.17.0"
49
49
  }
50
50
  }