@chestnutlabs/gcode-parser 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 ADDED
@@ -0,0 +1,33 @@
1
+ # @chestnutlabs/gcode-parser
2
+
3
+ Worker-safe G-code parse core and session client for the Chestnut Labs G-code Preview stack
4
+ (design: DD-003). Parses `.gcode` / `.gcode.3mf` **off the main thread** into
5
+ [`ToolpathIR`](../toolpath-core), with streaming input, progressive partial previews for large
6
+ files, resource limits, cancellation, and an adversarial-input corpus behind it.
7
+
8
+ ```ts
9
+ import { GcodeParseSession } from '@chestnutlabs/gcode-parser';
10
+
11
+ const session = new GcodeParseSession(); // batteries worker: all dialects + .gcode.3mf
12
+ const result = await session.parse(bytes, {
13
+ onPartial: (ir) => render(ir), // progressive preview
14
+ onProgress: (p) => bar(p)
15
+ });
16
+ render(result.ir);
17
+ session.dispose();
18
+ ```
19
+
20
+ ## Workers
21
+
22
+ - **Batteries default (zero setup):** the session creates its own worker via the bundler-native
23
+ `new Worker(new URL('./worker.js', import.meta.url))` pattern — all supported dialect adapters
24
+ plus `.gcode.3mf` container support. Vite resolves it out of the box (CI-tested).
25
+ - **Slim / custom:** pass a worker factory for the slim build (no dialects/containers), your own
26
+ adapter set via `createWorkerHandler(...)`, other bundlers, or strict-CSP environments.
27
+
28
+ Wire options accept dialect selection, container/plate selection (`plate` for multi-plate
29
+ `.gcode.3mf`), and resource limits. Most applications should consume this through
30
+ [`@chestnutlabs/gcode-preview-core`](../gcode-preview-core) or a framework adapter rather than
31
+ directly.
32
+
33
+ Part of [Chestnut Labs G-code Preview](../../README.md) · MIT
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Budget-aware growable SoA segment writer (DD-003 §7.2/§7.3).
3
+ *
4
+ * Accumulates segments directly into growable typed arrays (no intermediate object
5
+ * graphs) and keeps a byte-accurate account of every live working allocation. Any
6
+ * projected allocation — growth steps and the final right-size compaction, during
7
+ * which old and new capacity coexist — is checked against `maxBufferBytes` BEFORE
8
+ * it is performed. On budget exhaustion the writer reports failure and the parse
9
+ * stops at the current command boundary with a structured bounded result; it never
10
+ * throws an unhandled allocation error.
11
+ */
12
+ export declare class BudgetExceededError extends Error {
13
+ readonly requestedBytes: number;
14
+ readonly accountedBytes: number;
15
+ readonly maxBufferBytes: number;
16
+ constructor(requestedBytes: number, accountedBytes: number, maxBufferBytes: number);
17
+ }
18
+ export interface SegmentRecord {
19
+ x0: number;
20
+ y0: number;
21
+ z0: number;
22
+ x1: number;
23
+ y1: number;
24
+ z1: number;
25
+ e: number;
26
+ feedrate: number;
27
+ kind: number;
28
+ tool: number;
29
+ layer: number;
30
+ srcByte: number;
31
+ }
32
+ export interface FinalChannels {
33
+ count: number;
34
+ x0: Float32Array;
35
+ y0: Float32Array;
36
+ z0: Float32Array;
37
+ x1: Float32Array;
38
+ y1: Float32Array;
39
+ z1: Float32Array;
40
+ e: Float32Array;
41
+ feedrate: Float32Array;
42
+ kind: Uint8Array;
43
+ tool: Uint16Array;
44
+ layer: Uint32Array;
45
+ feature: Uint8Array;
46
+ object: Uint32Array;
47
+ srcByte: Uint32Array;
48
+ }
49
+ export declare class SegmentWriter {
50
+ private readonly maxBufferBytes;
51
+ private capacity;
52
+ private _count;
53
+ private x0;
54
+ private y0;
55
+ private z0;
56
+ private x1;
57
+ private y1;
58
+ private z1;
59
+ private e;
60
+ private feedrate;
61
+ private kind;
62
+ private tool;
63
+ private layer;
64
+ private feature;
65
+ private object;
66
+ private srcByte;
67
+ /** Bytes of live typed-array capacity currently held by this writer. */
68
+ private accountedBytes;
69
+ /** Extra live bytes the caller wants included in the budget (line buffer, warnings). */
70
+ private externalBytes;
71
+ constructor(maxBufferBytes: number);
72
+ get count(): number;
73
+ get accounted(): number;
74
+ /** Report additional live working bytes (e.g., the line-drain buffer) into the budget. */
75
+ setExternalBytes(bytes: number): void;
76
+ /** Throws BudgetExceededError if `requested` more bytes would break the budget. */
77
+ private ensureBudget;
78
+ private allocate;
79
+ /** Append one segment. Throws BudgetExceededError when growth would break the budget. */
80
+ push(s: SegmentRecord): void;
81
+ /** Overwrite the layer channel for a segment range (used for post-hoc layer resolution). */
82
+ setLayerRange(start: number, end: number, layerIndex: number): void;
83
+ /** Zero the whole layer channel (non-planar clearing semantics). */
84
+ clearLayers(): void;
85
+ /**
86
+ * Copy a segment range into fresh right-sized arrays (progressive-preview
87
+ * snapshots, DD-004 §5.4 / issue #60). Budget-checked: the copies coexist with
88
+ * the live buffers until they are transferred away, so they count against
89
+ * `maxBufferBytes` at creation time. Throws BudgetExceededError when the copy
90
+ * would break the budget — callers skip the snapshot, never the parse.
91
+ */
92
+ snapshotRange(start: number, end: number): FinalChannels;
93
+ /** Right-size to exact count. Budget-checked (old + new coexist during the copy). */
94
+ finalize(): FinalChannels;
95
+ }
96
+ //# sourceMappingURL=growable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"growable.d.ts","sourceRoot":"","sources":["../src/growable.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,qBAAa,mBAAoB,SAAQ,KAAK;aAE1B,cAAc,EAAE,MAAM;aACtB,cAAc,EAAE,MAAM;aACtB,cAAc,EAAE,MAAM;gBAFtB,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM;CAOzC;AAuBD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,EAAE,MAAM,CAAC;IACV,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,YAAY,CAAC;IACjB,EAAE,EAAE,YAAY,CAAC;IACjB,EAAE,EAAE,YAAY,CAAC;IACjB,EAAE,EAAE,YAAY,CAAC;IACjB,EAAE,EAAE,YAAY,CAAC;IACjB,EAAE,EAAE,YAAY,CAAC;IACjB,CAAC,EAAE,YAAY,CAAC;IAChB,QAAQ,EAAE,YAAY,CAAC;IACvB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,UAAU,CAAC;IACpB,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,WAAW,CAAC;CACtB;AAID,qBAAa,aAAa;IAwBZ,OAAO,CAAC,QAAQ,CAAC,cAAc;IAvB3C,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,MAAM,CAAK;IAEnB,OAAO,CAAC,EAAE,CAAgB;IAC1B,OAAO,CAAC,EAAE,CAAgB;IAC1B,OAAO,CAAC,EAAE,CAAgB;IAC1B,OAAO,CAAC,EAAE,CAAgB;IAC1B,OAAO,CAAC,EAAE,CAAgB;IAC1B,OAAO,CAAC,EAAE,CAAgB;IAC1B,OAAO,CAAC,CAAC,CAAgB;IACzB,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,IAAI,CAAc;IAC1B,OAAO,CAAC,IAAI,CAAe;IAC3B,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,OAAO,CAAe;IAE9B,wEAAwE;IACxE,OAAO,CAAC,cAAc,CAAK;IAC3B,wFAAwF;IACxF,OAAO,CAAC,aAAa,CAAK;gBAEG,cAAc,EAAE,MAAM;IAInD,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,0FAA0F;IAC1F,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIrC,mFAAmF;IACnF,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,QAAQ;IAwDhB,yFAAyF;IACzF,IAAI,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI;IAsB5B,4FAA4F;IAC5F,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAInE,oEAAoE;IACpE,WAAW,IAAI,IAAI;IAInB;;;;;;OAMG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa;IAsBxD,qFAAqF;IACrF,QAAQ,IAAI,aAAa;CAsB1B"}
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Budget-aware growable SoA segment writer (DD-003 §7.2/§7.3).
3
+ *
4
+ * Accumulates segments directly into growable typed arrays (no intermediate object
5
+ * graphs) and keeps a byte-accurate account of every live working allocation. Any
6
+ * projected allocation — growth steps and the final right-size compaction, during
7
+ * which old and new capacity coexist — is checked against `maxBufferBytes` BEFORE
8
+ * it is performed. On budget exhaustion the writer reports failure and the parse
9
+ * stops at the current command boundary with a structured bounded result; it never
10
+ * throws an unhandled allocation error.
11
+ */
12
+ export class BudgetExceededError extends Error {
13
+ requestedBytes;
14
+ accountedBytes;
15
+ maxBufferBytes;
16
+ constructor(requestedBytes, accountedBytes, maxBufferBytes) {
17
+ super(`allocation of ${requestedBytes} B would exceed maxBufferBytes=${maxBufferBytes} B (accounted: ${accountedBytes} B)`);
18
+ this.requestedBytes = requestedBytes;
19
+ this.accountedBytes = accountedBytes;
20
+ this.maxBufferBytes = maxBufferBytes;
21
+ this.name = 'BudgetExceededError';
22
+ }
23
+ }
24
+ const CHANNELS = [
25
+ { name: 'x0', bytesPerElement: 4 },
26
+ { name: 'y0', bytesPerElement: 4 },
27
+ { name: 'z0', bytesPerElement: 4 },
28
+ { name: 'x1', bytesPerElement: 4 },
29
+ { name: 'y1', bytesPerElement: 4 },
30
+ { name: 'z1', bytesPerElement: 4 },
31
+ { name: 'e', bytesPerElement: 4 },
32
+ { name: 'feedrate', bytesPerElement: 4 },
33
+ { name: 'kind', bytesPerElement: 1 },
34
+ { name: 'tool', bytesPerElement: 2 },
35
+ { name: 'layer', bytesPerElement: 4 },
36
+ { name: 'feature', bytesPerElement: 1 },
37
+ { name: 'object', bytesPerElement: 4 },
38
+ { name: 'srcByte', bytesPerElement: 4 }
39
+ ];
40
+ const BYTES_PER_SEGMENT = CHANNELS.reduce((a, c) => a + c.bytesPerElement, 0); // 40 B core set
41
+ const INITIAL_CAPACITY = 4096;
42
+ export class SegmentWriter {
43
+ maxBufferBytes;
44
+ capacity = 0;
45
+ _count = 0;
46
+ x0;
47
+ y0;
48
+ z0;
49
+ x1;
50
+ y1;
51
+ z1;
52
+ e;
53
+ feedrate;
54
+ kind;
55
+ tool;
56
+ layer;
57
+ feature;
58
+ object;
59
+ srcByte;
60
+ /** Bytes of live typed-array capacity currently held by this writer. */
61
+ accountedBytes = 0;
62
+ /** Extra live bytes the caller wants included in the budget (line buffer, warnings). */
63
+ externalBytes = 0;
64
+ constructor(maxBufferBytes) {
65
+ this.maxBufferBytes = maxBufferBytes;
66
+ this.allocate(INITIAL_CAPACITY);
67
+ }
68
+ get count() {
69
+ return this._count;
70
+ }
71
+ get accounted() {
72
+ return this.accountedBytes + this.externalBytes;
73
+ }
74
+ /** Report additional live working bytes (e.g., the line-drain buffer) into the budget. */
75
+ setExternalBytes(bytes) {
76
+ this.externalBytes = bytes;
77
+ }
78
+ /** Throws BudgetExceededError if `requested` more bytes would break the budget. */
79
+ ensureBudget(requested) {
80
+ if (this.accounted + requested > this.maxBufferBytes) {
81
+ throw new BudgetExceededError(requested, this.accounted, this.maxBufferBytes);
82
+ }
83
+ }
84
+ allocate(newCapacity) {
85
+ // During a grow/compact, old and new capacity coexist until copies complete.
86
+ const newBytes = newCapacity * BYTES_PER_SEGMENT;
87
+ this.ensureBudget(newBytes);
88
+ const nx0 = new Float32Array(newCapacity);
89
+ const ny0 = new Float32Array(newCapacity);
90
+ const nz0 = new Float32Array(newCapacity);
91
+ const nx1 = new Float32Array(newCapacity);
92
+ const ny1 = new Float32Array(newCapacity);
93
+ const nz1 = new Float32Array(newCapacity);
94
+ const ne = new Float32Array(newCapacity);
95
+ const nfeed = new Float32Array(newCapacity);
96
+ const nkind = new Uint8Array(newCapacity);
97
+ const ntool = new Uint16Array(newCapacity);
98
+ const nlayer = new Uint32Array(newCapacity);
99
+ const nfeature = new Uint8Array(newCapacity);
100
+ const nobject = new Uint32Array(newCapacity);
101
+ const nsrc = new Uint32Array(newCapacity);
102
+ if (this.capacity > 0) {
103
+ nx0.set(this.x0.subarray(0, this._count));
104
+ ny0.set(this.y0.subarray(0, this._count));
105
+ nz0.set(this.z0.subarray(0, this._count));
106
+ nx1.set(this.x1.subarray(0, this._count));
107
+ ny1.set(this.y1.subarray(0, this._count));
108
+ nz1.set(this.z1.subarray(0, this._count));
109
+ ne.set(this.e.subarray(0, this._count));
110
+ nfeed.set(this.feedrate.subarray(0, this._count));
111
+ nkind.set(this.kind.subarray(0, this._count));
112
+ ntool.set(this.tool.subarray(0, this._count));
113
+ nlayer.set(this.layer.subarray(0, this._count));
114
+ nfeature.set(this.feature.subarray(0, this._count));
115
+ nobject.set(this.object.subarray(0, this._count));
116
+ nsrc.set(this.srcByte.subarray(0, this._count));
117
+ }
118
+ const oldBytes = this.capacity * BYTES_PER_SEGMENT;
119
+ this.x0 = nx0;
120
+ this.y0 = ny0;
121
+ this.z0 = nz0;
122
+ this.x1 = nx1;
123
+ this.y1 = ny1;
124
+ this.z1 = nz1;
125
+ this.e = ne;
126
+ this.feedrate = nfeed;
127
+ this.kind = nkind;
128
+ this.tool = ntool;
129
+ this.layer = nlayer;
130
+ this.feature = nfeature;
131
+ this.object = nobject;
132
+ this.srcByte = nsrc;
133
+ this.capacity = newCapacity;
134
+ this.accountedBytes = this.accountedBytes - oldBytes + newBytes;
135
+ }
136
+ /** Append one segment. Throws BudgetExceededError when growth would break the budget. */
137
+ push(s) {
138
+ if (this._count === this.capacity) {
139
+ this.allocate(this.capacity * 2);
140
+ }
141
+ const i = this._count;
142
+ this.x0[i] = s.x0;
143
+ this.y0[i] = s.y0;
144
+ this.z0[i] = s.z0;
145
+ this.x1[i] = s.x1;
146
+ this.y1[i] = s.y1;
147
+ this.z1[i] = s.z1;
148
+ this.e[i] = s.e;
149
+ this.feedrate[i] = s.feedrate;
150
+ this.kind[i] = s.kind;
151
+ this.tool[i] = s.tool;
152
+ this.layer[i] = s.layer;
153
+ this.feature[i] = 0;
154
+ this.object[i] = 0;
155
+ this.srcByte[i] = s.srcByte;
156
+ this._count++;
157
+ }
158
+ /** Overwrite the layer channel for a segment range (used for post-hoc layer resolution). */
159
+ setLayerRange(start, end, layerIndex) {
160
+ this.layer.fill(layerIndex, start, end + 1);
161
+ }
162
+ /** Zero the whole layer channel (non-planar clearing semantics). */
163
+ clearLayers() {
164
+ this.layer.fill(0, 0, this._count);
165
+ }
166
+ /**
167
+ * Copy a segment range into fresh right-sized arrays (progressive-preview
168
+ * snapshots, DD-004 §5.4 / issue #60). Budget-checked: the copies coexist with
169
+ * the live buffers until they are transferred away, so they count against
170
+ * `maxBufferBytes` at creation time. Throws BudgetExceededError when the copy
171
+ * would break the budget — callers skip the snapshot, never the parse.
172
+ */
173
+ snapshotRange(start, end) {
174
+ const count = Math.max(0, end - start);
175
+ this.ensureBudget(count * BYTES_PER_SEGMENT);
176
+ return {
177
+ count,
178
+ x0: this.x0.slice(start, end),
179
+ y0: this.y0.slice(start, end),
180
+ z0: this.z0.slice(start, end),
181
+ x1: this.x1.slice(start, end),
182
+ y1: this.y1.slice(start, end),
183
+ z1: this.z1.slice(start, end),
184
+ e: this.e.slice(start, end),
185
+ feedrate: this.feedrate.slice(start, end),
186
+ kind: this.kind.slice(start, end),
187
+ tool: this.tool.slice(start, end),
188
+ layer: this.layer.slice(start, end),
189
+ feature: this.feature.slice(start, end),
190
+ object: this.object.slice(start, end),
191
+ srcByte: this.srcByte.slice(start, end)
192
+ };
193
+ }
194
+ /** Right-size to exact count. Budget-checked (old + new coexist during the copy). */
195
+ finalize() {
196
+ if (this._count < this.capacity) {
197
+ this.allocate(Math.max(this._count, 1));
198
+ }
199
+ return {
200
+ count: this._count,
201
+ x0: this.x0.subarray(0, this._count),
202
+ y0: this.y0.subarray(0, this._count),
203
+ z0: this.z0.subarray(0, this._count),
204
+ x1: this.x1.subarray(0, this._count),
205
+ y1: this.y1.subarray(0, this._count),
206
+ z1: this.z1.subarray(0, this._count),
207
+ e: this.e.subarray(0, this._count),
208
+ feedrate: this.feedrate.subarray(0, this._count),
209
+ kind: this.kind.subarray(0, this._count),
210
+ tool: this.tool.subarray(0, this._count),
211
+ layer: this.layer.subarray(0, this._count),
212
+ feature: this.feature.subarray(0, this._count),
213
+ object: this.object.subarray(0, this._count),
214
+ srcByte: this.srcByte.subarray(0, this._count)
215
+ };
216
+ }
217
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @chestnutlabs/gcode-parser — worker-safe G-code parse core (DD-003).
3
+ *
4
+ * Phase 1 (#44): pure parse core. Phase 2 (#45): cooperative async driver,
5
+ * worker protocol v1, and the GcodeParseSession client. Streaming inputs and
6
+ * full limit wiring land in phase 3 (#46). Depends only on
7
+ * @chestnutlabs/toolpath-core.
8
+ */
9
+ export { parseGcodeToIR, parseGcodeToIRAsync, DEFAULT_LIMITS } from './parse.js';
10
+ export type { AsyncParseHooks, AsyncParseResult, CommandEvent, ParseLimits, ParseOptions, ParseResult, ParseStats, StopReason } from './parse.js';
11
+ export { BudgetExceededError } from './growable.js';
12
+ export { parseGcodeStreamToIR, isBlobLike, isReadableStreamLike } from './streaming.js';
13
+ export type { StreamInput, BlobLike, ReadableStreamLike } from './streaming.js';
14
+ export { PROTOCOL_VERSION, PARTIAL_PREVIEW_MIN_BYTES, irTransferList } from './protocol.js';
15
+ export type { WireParseOptions, WorkerRequest, WorkerResponse, PartialMessage } from './protocol.js';
16
+ export { createWorkerHandler } from './worker-core.js';
17
+ export type { PostFn, DialectRunLike, DialectRunnerFactoryLike, WorkerHandlerOptions } from './worker-core.js';
18
+ export { GcodeParseSession, ParseSessionError, CancelledError } from './session.js';
19
+ export type { SessionOptions, WorkerLike, ParseProgress } from './session.js';
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjF,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,WAAW,EACX,UAAU,EACV,UAAU,EACX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACxF,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC5F,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACrG,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC/G,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACpF,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @chestnutlabs/gcode-parser — worker-safe G-code parse core (DD-003).
3
+ *
4
+ * Phase 1 (#44): pure parse core. Phase 2 (#45): cooperative async driver,
5
+ * worker protocol v1, and the GcodeParseSession client. Streaming inputs and
6
+ * full limit wiring land in phase 3 (#46). Depends only on
7
+ * @chestnutlabs/toolpath-core.
8
+ */
9
+ export { parseGcodeToIR, parseGcodeToIRAsync, DEFAULT_LIMITS } from './parse.js';
10
+ export { BudgetExceededError } from './growable.js';
11
+ export { parseGcodeStreamToIR, isBlobLike, isReadableStreamLike } from './streaming.js';
12
+ export { PROTOCOL_VERSION, PARTIAL_PREVIEW_MIN_BYTES, irTransferList } from './protocol.js';
13
+ export { createWorkerHandler } from './worker-core.js';
14
+ export { GcodeParseSession, ParseSessionError, CancelledError } from './session.js';
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Pure G-code parse core (DD-003 §14 phase 1, issue #44; async driver added in phase 2, #45).
3
+ *
4
+ * Ports the inherited xyz-tools interpreter semantics — motion handling (G0/G1),
5
+ * arc flattening (G2/G3 incl. R-mode and full circles), units (G20/G21), homing
6
+ * (G28), tool selection (T0–T7), travel/extrusion path splitting, and the
7
+ * layer-indexing rules (0.05 tolerance, non-planar clearing) — but writes the
8
+ * DD-001 `ToolpathIR` SoA buffers **directly** through a budget-aware growable
9
+ * writer, with **real** per-segment `srcByte`, extrusion deltas, and modal
10
+ * feed rate (the #29 adapter had to report those `unavailable`).
11
+ *
12
+ * Provenance: machine-state semantics adapted from the inherited MIT source
13
+ * (`src/interpreter.ts`, `src/gcode-parser.ts`, `src/indexers.ts` @ the founding
14
+ * baseline) — recorded in docs/UPSTREAM_PROVENANCE.md. Deliberate quirk-for-quirk
15
+ * fidelity (e.g. truthy `x || state.x` in arcs, tool captured at path start) is
16
+ * required by the golden-equivalence gate; do not "fix" behavior here without a
17
+ * reviewed golden regeneration.
18
+ *
19
+ * Two drivers share one engine:
20
+ * - `parseGcodeToIR` — synchronous (tests, Node tooling).
21
+ * - `parseGcodeToIRAsync` — the DD-003 §5.2 cooperative driver: processes at most
22
+ * `yieldIntervalMs` of CPU work between explicit MessageChannel macrotask yields
23
+ * so a queued worker `cancel` message can actually be dispatched; reports
24
+ * progress and honors a cancellation hook.
25
+ *
26
+ * Byte offsets assume single-byte (ASCII/UTF-8) G-code, which holds for slicer
27
+ * output; multi-byte comment bytes would skew offsets and are a documented
28
+ * limitation until the streaming decoder lands (phase 3).
29
+ */
30
+ import { type ToolpathIR } from '@chestnutlabs/toolpath-core';
31
+ export interface ParseLimits {
32
+ maxInputBytes: number;
33
+ maxSegments: number;
34
+ maxBufferBytes: number;
35
+ maxLineLength: number;
36
+ maxWarnings: number;
37
+ }
38
+ export declare const DEFAULT_LIMITS: ParseLimits;
39
+ /**
40
+ * Read-only normalized command event (DD-005 §4.3, amendment 1): observers see
41
+ * each lexed command BEFORE dispatch and cannot alter parsing.
42
+ */
43
+ export interface CommandEvent {
44
+ /** Normalized lowercase word+number, e.g. 'm486'. */
45
+ gcode: string;
46
+ params: Readonly<Record<string, number>>;
47
+ /** The raw line, for vendor syntaxes the lexer's params don't capture. */
48
+ rawLine: string;
49
+ srcByte: number;
50
+ /** Segment count at event time — maps events to segment ranges. */
51
+ segIndex: number;
52
+ }
53
+ export interface ParseOptions {
54
+ parserVersion?: string;
55
+ sourceId?: string;
56
+ limits?: Partial<ParseLimits>;
57
+ /** Layer-detection tolerance (inherited default 0.05). */
58
+ minLayerThreshold?: number;
59
+ /**
60
+ * DD-005 §4.3 read-only hooks: observe comments/commands during the parse.
61
+ * Inert when unset (one branch per line); they cannot alter lexing, dispatch,
62
+ * or machine state — the golden-gated semantics are untouched.
63
+ */
64
+ onComment?: (text: string, srcByte: number) => void;
65
+ onCommand?: (event: CommandEvent) => void;
66
+ }
67
+ export interface StopReason {
68
+ code: 'E_LIMIT_INPUT_BYTES' | 'E_LIMIT_SEGMENTS' | 'E_LIMIT_BUFFER_BYTES';
69
+ message: string;
70
+ srcByte?: number;
71
+ }
72
+ export interface ParseStats {
73
+ bytes: number;
74
+ lines: number;
75
+ commands: number;
76
+ segments: number;
77
+ points: number;
78
+ retractions: number;
79
+ deretractions: number;
80
+ feedrateChanges: number;
81
+ others: number;
82
+ extrusionDistance: number;
83
+ warningsByCode: Record<string, number>;
84
+ stopReason?: StopReason;
85
+ }
86
+ export interface ParseResult {
87
+ ir: ToolpathIR;
88
+ stats: ParseStats;
89
+ /** Dialect/machine metadata (DD-005 §4.2) — populated by the worker pipeline
90
+ * when dialect adapters are registered and matched; absent otherwise. */
91
+ metadata?: import('@chestnutlabs/toolpath-core').DialectMetadata;
92
+ }
93
+ export interface AsyncParseHooks {
94
+ /** Called after each cooperative yield with bytes processed so far. */
95
+ onProgress?: (bytesProcessed: number, totalBytes: number, segments: number) => void;
96
+ /** Checked after each yield; returning true stops the parse with a partial result. */
97
+ shouldCancel?: () => boolean;
98
+ /** Max CPU ms between yields (DD-003 §5.2 default 50). */
99
+ yieldIntervalMs?: number;
100
+ /**
101
+ * Progressive preview (DD-004 §5.4, issue #60): receives path-aligned delta
102
+ * snapshots of the IR built so far. Emission starts once bytesProcessed ≥
103
+ * `partialMinBytes` and repeats at most every `partialIntervalMs`.
104
+ */
105
+ onPartial?: (slice: ToolpathIR, cumulativeSegments: number) => void;
106
+ /** Bytes processed before the first partial may be emitted (default 0 when onPartial set). */
107
+ partialMinBytes?: number;
108
+ /** Minimum ms between partial snapshots (default 1000). */
109
+ partialIntervalMs?: number;
110
+ /**
111
+ * DD-005 §4.5 stream detection windows: when set, the STREAMING driver fills
112
+ * `head` with the first ≤64 KB of decoded text and keeps `tail` as a bounded
113
+ * rolling window (last ≤16 KB) — read after the parse for late dialect
114
+ * detection on non-seekable streams. In-memory drivers ignore this (their
115
+ * windows are sliceable up front).
116
+ */
117
+ captureWindows?: {
118
+ head: string;
119
+ tail: string;
120
+ };
121
+ }
122
+ export interface AsyncParseResult extends ParseResult {
123
+ cancelled: boolean;
124
+ }
125
+ /** Internal engine surface shared by the sync, async, and streaming drivers. Not public API. */
126
+ export interface Engine {
127
+ text: string;
128
+ limits: ParseLimits;
129
+ processLine(rawLine: string, offset: number): void;
130
+ stopped(): boolean;
131
+ markInputTooBig(): void;
132
+ /** Streaming driver: record the true source byte count as it becomes known. */
133
+ setSourceBytes(bytes: number): void;
134
+ /**
135
+ * Progressive-preview delta (issue #60): copy segments [fromSeg, cut) where
136
+ * `cut` is the current path boundary — every included segment has a resolved
137
+ * layer. Returns null when nothing new is ready or the copy would break the
138
+ * §7.2 buffer budget (snapshots are skippable; the parse never is).
139
+ */
140
+ snapshot(fromSeg: number): {
141
+ slice: ToolpathIR;
142
+ upTo: number;
143
+ } | null;
144
+ finish(cancelledAtByte?: number): ParseResult;
145
+ }
146
+ /** Internal: build an engine over decoded text (streaming passes '' and feeds lines itself). */
147
+ export declare function createEngine(input: string | Uint8Array, opts: ParseOptions): Engine;
148
+ /** Parse a whole G-code text into a ToolpathIR. Pure and synchronous. */
149
+ export declare function parseGcodeToIR(input: string | Uint8Array, opts?: ParseOptions): ParseResult;
150
+ /**
151
+ * Cooperative async driver (DD-003 §5.2): identical parsing semantics, but the
152
+ * loop processes at most `yieldIntervalMs` (default 50) of CPU work between
153
+ * explicit macrotask yields so queued `cancel` messages are dispatched. Progress
154
+ * and cancellation are surfaced through hooks.
155
+ */
156
+ export declare function parseGcodeToIRAsync(input: string | Uint8Array, opts?: ParseOptions, hooks?: AsyncParseHooks): Promise<AsyncParseResult>;
157
+ /**
158
+ * Shared partial-snapshot emitter for the async and streaming drivers (#60):
159
+ * called at cooperative yield points; emits a delta once bytesProcessed passes
160
+ * `partialMinBytes` and at most every `partialIntervalMs`.
161
+ */
162
+ export declare function makePartialEmitter(engine: Engine, hooks: AsyncParseHooks): (bytesProcessed: number) => void;
163
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"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"}