@mylasertools/kerfscript 0.2.1 → 0.3.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/dist/lexer.d.cts CHANGED
@@ -3,7 +3,9 @@ export interface Pos {
3
3
  line: number;
4
4
  col: number;
5
5
  offset: number;
6
+ file?: string;
6
7
  }
8
+ export declare const where: (pos: Pos, col?: boolean) => string;
7
9
  export interface Token {
8
10
  type: TokenType;
9
11
  value: string;
@@ -20,9 +22,10 @@ export declare class KerfError extends Error {
20
22
  col?: number;
21
23
  echoes: string[];
22
24
  missingFonts?: string[];
25
+ file?: string;
23
26
  constructor(message: string, pos?: Pos);
24
27
  }
25
- export declare function tokenize(src: string): {
28
+ export declare function tokenize(src: string, file?: string): {
26
29
  tokens: Token[];
27
30
  comments: Comment[];
28
31
  };
package/dist/lexer.d.ts CHANGED
@@ -3,7 +3,9 @@ export interface Pos {
3
3
  line: number;
4
4
  col: number;
5
5
  offset: number;
6
+ file?: string;
6
7
  }
8
+ export declare const where: (pos: Pos, col?: boolean) => string;
7
9
  export interface Token {
8
10
  type: TokenType;
9
11
  value: string;
@@ -20,9 +22,10 @@ export declare class KerfError extends Error {
20
22
  col?: number;
21
23
  echoes: string[];
22
24
  missingFonts?: string[];
25
+ file?: string;
23
26
  constructor(message: string, pos?: Pos);
24
27
  }
25
- export declare function tokenize(src: string): {
28
+ export declare function tokenize(src: string, file?: string): {
26
29
  tokens: Token[];
27
30
  comments: Comment[];
28
31
  };
package/dist/parser.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { Program } from './ast.cjs';
2
2
  import { type Comment } from './lexer.cjs';
3
- export declare function parse(src: string): {
3
+ export declare function parse(src: string, file?: string): {
4
4
  program: Program;
5
5
  comments: Comment[];
6
6
  };
package/dist/parser.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { Program } from './ast.js';
2
2
  import { type Comment } from './lexer.js';
3
- export declare function parse(src: string): {
3
+ export declare function parse(src: string, file?: string): {
4
4
  program: Program;
5
5
  comments: Comment[];
6
6
  };
package/dist/render.d.cts CHANGED
@@ -2,7 +2,7 @@ import { type LayerDef, type Limits } from './evaluator.cjs';
2
2
  import { type LayerPath } from './export/svg.cjs';
3
3
  import { type Layer } from './geometry.cjs';
4
4
  import { type FontSource } from './text.cjs';
5
- import type { Value } from './values.cjs';
5
+ import { type Value } from './values.cjs';
6
6
  export interface RenderOptions {
7
7
  params?: Record<string, Value>;
8
8
  fonts?: Record<string, FontSource>;
package/dist/render.d.ts CHANGED
@@ -2,7 +2,7 @@ import { type LayerDef, type Limits } from './evaluator.js';
2
2
  import { type LayerPath } from './export/svg.js';
3
3
  import { type Layer } from './geometry.js';
4
4
  import { type FontSource } from './text.js';
5
- import type { Value } from './values.js';
5
+ import { type Value } from './values.js';
6
6
  export interface RenderOptions {
7
7
  params?: Record<string, Value>;
8
8
  fonts?: Record<string, FontSource>;
package/dist/values.d.cts CHANGED
@@ -16,10 +16,18 @@ export type Value = number | string | boolean | undefined | Value[] | Range | Cl
16
16
  export declare const isRange: (v: Value) => v is Range;
17
17
  export declare const isFn: (v: Value) => v is Closure;
18
18
  export declare const isNum: (v: Value) => v is number;
19
+ export declare const own: <T>(o: Record<string, T> | undefined, key: string) => T | undefined;
20
+ export declare function checkSetting(name: string, v: unknown, depth?: number): void;
19
21
  export declare function rangeValues(r: Range, limit: number): number[];
20
22
  export declare function truthy(v: Value): boolean;
21
- export declare function equals(a: Value, b: Value): boolean;
23
+ export declare function equals(a: Value, b: Value, step?: () => void): boolean;
22
24
  export declare function typeName(v: Value): string;
25
+ export declare function write(v: Value, max: number, quote?: boolean): {
26
+ text: string;
27
+ whole: boolean;
28
+ };
29
+ export declare function joinText(vs: Value[], sep: string, max: number): string | undefined;
23
30
  export declare function show(v: Value): string;
24
- export declare function str(v: Value): string;
31
+ export declare const toText: (v: Value) => string;
32
+ export declare function toNumber(v: Value): number;
25
33
  export declare function fmt(n: number): string;
package/dist/values.d.ts CHANGED
@@ -16,10 +16,18 @@ export type Value = number | string | boolean | undefined | Value[] | Range | Cl
16
16
  export declare const isRange: (v: Value) => v is Range;
17
17
  export declare const isFn: (v: Value) => v is Closure;
18
18
  export declare const isNum: (v: Value) => v is number;
19
+ export declare const own: <T>(o: Record<string, T> | undefined, key: string) => T | undefined;
20
+ export declare function checkSetting(name: string, v: unknown, depth?: number): void;
19
21
  export declare function rangeValues(r: Range, limit: number): number[];
20
22
  export declare function truthy(v: Value): boolean;
21
- export declare function equals(a: Value, b: Value): boolean;
23
+ export declare function equals(a: Value, b: Value, step?: () => void): boolean;
22
24
  export declare function typeName(v: Value): string;
25
+ export declare function write(v: Value, max: number, quote?: boolean): {
26
+ text: string;
27
+ whole: boolean;
28
+ };
29
+ export declare function joinText(vs: Value[], sep: string, max: number): string | undefined;
23
30
  export declare function show(v: Value): string;
24
- export declare function str(v: Value): string;
31
+ export declare const toText: (v: Value) => string;
32
+ export declare function toNumber(v: Value): number;
25
33
  export declare function fmt(n: number): string;
@@ -0,0 +1 @@
1
+ export {};
package/dist/wasm.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/docs/cli.md CHANGED
@@ -14,6 +14,30 @@ You can also run it through `npx`:
14
14
  npx @mylasertools/kerfscript render box.kerf -o box.svg
15
15
  ```
16
16
 
17
+ ## Without Node.js
18
+
19
+ The [releases](https://github.com/mylasertools/kerfscript/releases) page has a
20
+ standalone `kerf` for Windows, macOS and Linux (x64 and arm64) that needs no
21
+ Node.js: `kerf-<version>-<os>-<arch>.zip` or `.tar.gz`, and
22
+ `kerfscript-cli-<version>-linux-<arch>.deb` or `.rpm`. It has the
23
+ Atkinson Hyperlegible font built in, like the npm package.
24
+
25
+ | System | Install |
26
+ | ---------------- | ------------------------------------------------------------------------------------------------------------- |
27
+ | macOS, Linux | `brew install mylasertools/tap/kerfscript` |
28
+ | Windows | `winget install MyLaserTools.KerfScript.CLI`, or `choco install kerfscript` |
29
+ | Windows (Scoop) | `scoop bucket add mylasertools https://github.com/mylasertools/scoop-bucket`, then `scoop install kerfscript` |
30
+ | Linux (Snap) | `sudo snap install kerfscript`; the command is `kerfscript.kerf` |
31
+ | Arch Linux (AUR) | `kerfscript-bin` |
32
+ | macOS (MacPorts) | `sudo port install kerfscript` |
33
+ | Docker | `docker run --rm -v "$PWD:/work" --user "$(id -u):$(id -g)" ghcr.io/mylasertools/kerfscript render box.kerf` |
34
+
35
+ Package manager listings follow each release as they are approved. The
36
+ binaries are not signed yet. On macOS, Homebrew and MacPorts install them
37
+ without a warning; a downloaded `kerf` needs
38
+ `xattr -d com.apple.quarantine kerf` once. On Windows, SmartScreen may warn
39
+ the first time.
40
+
17
41
  Save a `.kerf` file from the [models](https://kerfscript.mylasertools.com/models/)
18
42
  to try these commands.
19
43
 
@@ -31,21 +55,21 @@ reports parts, dimensions, cut lengths, sheets and warnings.
31
55
 
32
56
  ## Options
33
57
 
34
- | Option | Use |
35
- | --- | --- |
36
- | `-o, --out <file>` | Output path ending in `.svg`, `.dxf` or `.json`; defaults to `<file>.svg` |
37
- | `-D name=value` | Override a parameter; repeat for multiple values |
38
- | `-p, --params <file>` | Load parameter values from a JSON file |
39
- | `--font <file.ttf>` | Supply a font file; repeat for multiple fonts |
40
- | `--kerf <mm>` | Override kerf for every part |
41
- | `--sheet <W>x<H>` | Set the sheet width and height in millimetres |
42
- | `--gap <mm>` | Set spacing between parts |
43
- | `--margin <mm>` | Set spacing at sheet edges; defaults to the gap |
44
- | `--no-pack` | Keep the positions defined in the program |
45
- | `--guides` | Include the guide layer |
46
- | `--split` | Export one file per sheet |
47
- | `-h, --help` | Show command help |
48
- | `-v, --version` | Show version numbers |
58
+ | Option | Use |
59
+ | --------------------- | ------------------------------------------------------------------------- |
60
+ | `-o, --out <file>` | Output path ending in `.svg`, `.dxf` or `.json`; defaults to `<file>.svg` |
61
+ | `-D name=value` | Override a parameter; repeat for multiple values |
62
+ | `-p, --params <file>` | Load parameter values from a JSON file |
63
+ | `--font <file.ttf>` | Supply a font file; repeat for multiple fonts. `Atkinson` is built in |
64
+ | `--kerf <mm>` | Override kerf for every part |
65
+ | `--sheet <W>x<H>` | Set the sheet width and height in millimetres |
66
+ | `--gap <mm>` | Set spacing between parts |
67
+ | `--margin <mm>` | Set spacing at sheet edges; defaults to the gap |
68
+ | `--no-pack` | Keep the positions defined in the program |
69
+ | `--guides` | Include the guide layer |
70
+ | `--split` | Export one file per sheet |
71
+ | `-h, --help` | Show command help |
72
+ | `-v, --version` | Show version numbers |
49
73
 
50
74
  `-D` accepts KerfScript values, including numbers, quoted strings, booleans
51
75
  and lists. For example: `-D width=200` or `-D 'name="Ada"'`.
@@ -68,9 +92,11 @@ a non-zero exit code.
68
92
 
69
93
  ## Fonts
70
94
 
71
- Supply font files with `--font` for designs that use text. The CLI does not
72
- look up fonts installed on your system.
95
+ The CLI has one font built in, Atkinson Hyperlegible, as `Atkinson`: the same
96
+ font and name as the playground. Text that names no font uses it.
73
97
 
74
- `--font Inter-Bold.ttf` makes the font available as `Inter-Bold`. A design
75
- can request it with `font = "Inter-Bold"`. The first supplied font is the
76
- default for text that does not name one.
98
+ Supply other font files with `--font`. The CLI does not look up fonts
99
+ installed on your system. `--font Inter-Bold.ttf` makes the font available
100
+ as `Inter-Bold`. A design can request it with `font = "Inter-Bold"`. The
101
+ first supplied font becomes the default for text that does not name one, and
102
+ `Atkinson` stays available.
@@ -22,9 +22,9 @@ and operation order.
22
22
 
23
23
  ## Checked
24
24
 
25
- | Program | Version | SVG | DXF | Checks |
26
- | --- | --- | --- | --- | --- |
27
- | Inkscape | 1.4.2 | Passed | Passed | Dimensions, layers and SVG fills |
25
+ | Program | Version | SVG | DXF | Checks |
26
+ | -------- | ------- | ------ | ------ | -------------------------------- |
27
+ | Inkscape | 1.4.2 | Passed | Passed | Dimensions, layers and SVG fills |
28
28
 
29
29
  Six reference models passed these import checks on 28 October 2026.
30
30
  Drawing dimensions matched within 0.02 mm, including kerf compensation.
@@ -38,15 +38,15 @@ do not get their own controls.
38
38
 
39
39
  Put a range or choice list after the assignment:
40
40
 
41
- | Comment after the value | Control |
42
- | --- | --- |
43
- | `// [40:5:400]` | Slider from 40 to 400, in steps of 5 |
44
- | `// [40:400]` | Slider from 40 to 400 |
45
- | `// [200]` | Number with an upper limit of 200 |
46
- | `// [2, 3, 4]` | Choice of 2, 3 or 4 |
47
- | `// [round:Round, square:Square]` | Menu with a label for each value |
48
- | No comment on a boolean | Checkbox |
49
- | No comment on a string | Text field |
41
+ | Comment after the value | Control |
42
+ | --------------------------------- | ------------------------------------ |
43
+ | `// [40:5:400]` | Slider from 40 to 400, in steps of 5 |
44
+ | `// [40:400]` | Slider from 40 to 400 |
45
+ | `// [200]` | Number with an upper limit of 200 |
46
+ | `// [2, 3, 4]` | Choice of 2, 3 or 4 |
47
+ | `// [round:Round, square:Square]` | Menu with a label for each value |
48
+ | No comment on a boolean | Checkbox |
49
+ | No comment on a string | Text field |
50
50
 
51
51
  `/* [Size] */` starts a group named Size. `/* [Hidden] */` hides the
52
52
  settings that follow it, until another group begins.
@@ -0,0 +1,96 @@
1
+ # Desktop app
2
+
3
+ The KerfScript app is the [playground](https://kerfscript.mylasertools.com/playground/)
4
+ on your computer: the same editor, settings and preview, with your designs
5
+ saved as `.kerf` files. It works offline.
6
+
7
+ ## Install
8
+
9
+ Download the app for your system from the
10
+ [releases](https://github.com/mylasertools/kerfscript/releases):
11
+
12
+ | System | File |
13
+ | ------- | ---------------------------------------------------------------------------- |
14
+ | Windows | `kerfscript-desktop-<version>-windows-x64-setup.exe`, or the `.msi` |
15
+ | macOS | `kerfscript-desktop-<version>-macos-universal.dmg` (Apple silicon and Intel) |
16
+ | Linux | `.AppImage`, `.deb` or `.rpm`, for x64 or arm64 |
17
+
18
+ Package manager listings (winget, Chocolatey, Flathub and the AUR) follow as
19
+ each one is approved.
20
+
21
+ The app is not signed with an Apple or Microsoft certificate yet:
22
+
23
+ - **macOS** says the app cannot be checked for malicious software. Open it
24
+ once with right click → **Open**, or run
25
+ `xattr -d com.apple.quarantine /Applications/KerfScript.app`.
26
+ - **Windows** SmartScreen may say it protected your PC. Choose
27
+ **More info** → **Run anyway**.
28
+
29
+ ## Files
30
+
31
+ | Action | Keys |
32
+ | --------------------- | --------------------------------------------- |
33
+ | Open a `.kerf` file | Ctrl+O (⌘O on a Mac), or the **Open…** button |
34
+ | Save | Ctrl+S |
35
+ | Save under a new name | Ctrl+Shift+S, or **Save as…** |
36
+
37
+ Double-click a `.kerf` file to open it in the app. The title bar shows the
38
+ file name, or Untitled, and a dot when there are unsaved changes. The app asks
39
+ before it closes, or opens another design or an example over them, also for a
40
+ design you have not saved yet. A design can be up to 1,000,000 characters
41
+ long, the same as in the playground. Saving writes the whole file before it
42
+ replaces the old one, so a full disk or a crash cannot leave half a design.
43
+
44
+ SVG, DXF and sheet ZIP exports open a save dialog in the design's folder.
45
+ On Linux, a name typed without `.kerf`, `.svg`, `.dxf` or `.zip` gets it, and
46
+ the app asks before replacing a file of that name.
47
+
48
+ ## LightBurn
49
+
50
+ **Open in LightBurn** sends the cut file straight to
51
+ [LightBurn](https://lightburnsoftware.com) 2.2 or later, through LightBurn's
52
+ [REST API](https://github.com/LightBurnSoftware/LightBurn-REST). With several
53
+ sheets, each sheet in the cut files list has its own **LightBurn** link.
54
+
55
+ LightBurn has to be open on the same computer. The first time, LightBurn
56
+ asks whether to allow KerfScript; choose **Allow** within 30 seconds. The
57
+ window may open behind KerfScript. KerfScript asks only to send files: it
58
+ cannot read your projects, change cut settings or start the laser.
59
+
60
+ The file opens as a new LightBurn project, as if you had used File → Open,
61
+ and LightBurn asks first if your current project has unsaved changes. Check
62
+ the layers and cut settings in LightBurn before you run the job.
63
+
64
+ To stop KerfScript sending files, remove it in LightBurn under
65
+ Settings → Extensions → Manage API Authorizations.
66
+
67
+ LightBurn is a trademark of LightBurn Software, Inc. KerfScript is not made,
68
+ endorsed or certified by LightBurn Software.
69
+
70
+ ## Libraries
71
+
72
+ `use <path>` and `include <path>` find files in the open design's folder and
73
+ the folders below it: with the design at `projects/box.kerf`,
74
+ `use <lib/tabs>` reads `projects/lib/tabs.kerf`. Unlike the
75
+ [command line](cli.md), the app reads only `.kerf` files, and not from folders
76
+ above the design's, such as `use <../shared/tabs>`; keep them next to the
77
+ design or in a folder below it. That way a design you download cannot read
78
+ other files on your computer. Standard library paths such as `use <kerf/box>` work
79
+ everywhere. A design that has not been saved yet can only use the standard
80
+ library.
81
+
82
+ ## Fonts
83
+
84
+ `text()` uses Atkinson Hyperlegible, as in the playground. The app does not
85
+ read fonts installed on your system yet; use the [command line](cli.md) with
86
+ `--font` for other fonts.
87
+
88
+ ## About and licences
89
+
90
+ **About** shows the app's version, a link to its source code, and the
91
+ licences of KerfScript and of everything built into the app.
92
+
93
+ ## Sharing
94
+
95
+ **Copy web link** copies a link that opens your code in the playground on the
96
+ website, so people without the app can see it.
@@ -31,20 +31,20 @@ changing the source string.
31
31
 
32
32
  ### Options
33
33
 
34
- | Option | Value |
35
- | --- | --- |
36
- | `params` | Parameter values keyed by name |
37
- | `fonts` | Font files keyed by name, as `ArrayBuffer` or `Uint8Array` |
38
- | `defaultFont` | Name to use when `text()` does not specify a font |
39
- | `resolve` | `(path) => source` callback for libraries outside the standard library |
40
- | `kerf` | Kerf override for every part, in millimetres |
41
- | `layout` | `"pack"` or `"none"` |
42
- | `sheet` | `[width, height]` in millimetres |
43
- | `gap` | Space between parts, in millimetres |
44
- | `margin` | Space at sheet edges, in millimetres; defaults to `gap` |
45
- | `guides` | Whether to include guide geometry |
46
- | `maxSteps` | Evaluation step limit |
47
- | `limits` | Memory limits, see below; any you leave out keep their default |
34
+ | Option | Value |
35
+ | ------------- | ---------------------------------------------------------------------- |
36
+ | `params` | Parameter values keyed by name |
37
+ | `fonts` | Font files keyed by name, as `ArrayBuffer` or `Uint8Array` |
38
+ | `defaultFont` | Name to use when `text()` does not specify a font |
39
+ | `resolve` | `(path) => source` callback for libraries outside the standard library |
40
+ | `kerf` | Kerf override for every part, in millimetres |
41
+ | `layout` | `"pack"` or `"none"` |
42
+ | `sheet` | `[width, height]` in millimetres |
43
+ | `gap` | Space between parts, in millimetres |
44
+ | `margin` | Space at sheet edges, in millimetres; defaults to `gap` |
45
+ | `guides` | Whether to include guide geometry |
46
+ | `maxSteps` | Evaluation step limit |
47
+ | `limits` | Memory limits, see below; any you leave out keep their default |
48
48
 
49
49
  Supply a font for programs that use text:
50
50
 
@@ -61,16 +61,16 @@ The URL should point to a font file provided by your application.
61
61
 
62
62
  ### Result
63
63
 
64
- | Field | Contents |
65
- | --- | --- |
66
- | `svg` | Combined SVG containing all sheets |
67
- | `width`, `height` | Combined SVG dimensions in millimetres |
68
- | `sheets` | Each sheet's dimensions, material, thickness, SVG, DXF, paths and placed items |
69
- | `parts` | Part names, quantities, dimensions, piece counts, cut lengths and material properties |
70
- | `paths` | Layer paths with `name`, `mode`, `color`, `d`, `regions` and `lines` |
71
- | `layers` | Layer definitions in output order |
72
- | `warnings` | Problems that did not stop the render |
73
- | `echoes` | Output from the program's `echo()` calls |
64
+ | Field | Contents |
65
+ | ----------------- | ------------------------------------------------------------------------------------- |
66
+ | `svg` | Combined SVG containing all sheets |
67
+ | `width`, `height` | Combined SVG dimensions in millimetres |
68
+ | `sheets` | Each sheet's dimensions, material, thickness, SVG, DXF, paths and placed items |
69
+ | `parts` | Part names, quantities, dimensions, piece counts, cut lengths and material properties |
70
+ | `paths` | Layer paths with `name`, `mode`, `color`, `d`, `regions` and `lines` |
71
+ | `layers` | Layer definitions in output order |
72
+ | `warnings` | Problems that did not stop the render |
73
+ | `echoes` | Output from the program's `echo()` calls |
74
74
 
75
75
  Use `result.svg` for a combined preview. Use `result.sheets[i].svg` or
76
76
  `.dxf` to export individual sheets, particularly when they use different
@@ -136,9 +136,11 @@ Run browser renders in a web worker so they do not block the page. Set a
136
136
  timeout and terminate the worker if it takes too long. Evaluation limits
137
137
  bound program steps and recursion, but geometry work also takes time.
138
138
 
139
- Memory limits stop a program before it asks for more memory than a browser
140
- tab can give: part copies, circle segments, list sizes, text length, shape
141
- points, exported points and sheets. The defaults are in
139
+ Size limits stop a program before it builds more than a browser tab can
140
+ usually hold: part copies, circle segments, list sizes, text length, shape
141
+ points, exported points and sheets. They bound what a program makes, not
142
+ every byte the renderer uses, so keep the worker's timeout as well, and run
143
+ programs you did not write where the page can end them. The defaults are in
142
144
  [Limits](../LANGUAGE.md#limits) and in `DEFAULT_LIMITS`. Lower them with the
143
145
  `limits` option if your worker has little memory:
144
146
 
package/docs/layers.md CHANGED
@@ -6,12 +6,12 @@ software. Unassigned shapes go on the `cut` layer.
6
6
 
7
7
  ## The built-in layers
8
8
 
9
- | Layer | Wrap shapes in | Colour in the file | Intended operation |
10
- | --- | --- | --- | --- |
11
- | `engrave` | `engrave()` | black `#000000` | Fill an area |
12
- | `score` | `score()` | blue `#0000ff` | Mark a line or outline |
13
- | `cut` | `cut()` | red `#ff0000` | Cut through the material |
14
- | `guide` | `guide()` | light blue | Show construction geometry |
9
+ | Layer | Wrap shapes in | Colour in the file | Intended operation |
10
+ | --------- | -------------- | ------------------ | -------------------------- |
11
+ | `engrave` | `engrave()` | black `#000000` | Fill an area |
12
+ | `score` | `score()` | blue `#0000ff` | Mark a line or outline |
13
+ | `cut` | `cut()` | red `#ff0000` | Cut through the material |
14
+ | `guide` | `guide()` | light blue | Show construction geometry |
15
15
 
16
16
  This coaster has a cut outline and hole, an engraved ring and a scored border:
17
17
 
package/docs/openscad.md CHANGED
@@ -23,15 +23,15 @@ See the [language reference](../LANGUAGE.md) for supported arguments.
23
23
 
24
24
  ## Different
25
25
 
26
- | Feature | OpenSCAD | KerfScript |
27
- | --- | --- | --- |
28
- | Geometry | 3D and 2D | 2D parts for laser cutting |
29
- | Output | 3D formats and 2D exports | SVG and DXF with operation layers |
30
- | Assignments | The last assignment in a scope applies throughout that scope | Assignments take effect in order |
31
- | Type errors | Many produce `undef` and a warning | Stop evaluation with an error |
32
- | `color()` | Sets a shape's colour | Use `cut()`, `score()`, `engrave()` or `layer()` |
33
- | Fonts | Looks up system fonts | Uses fonts supplied by the application or CLI |
34
- | Libraries | Can load local files | Loads the standard library and sources allowed by the host |
26
+ | Feature | OpenSCAD | KerfScript |
27
+ | ----------- | ------------------------------------------------------------ | ---------------------------------------------------------- |
28
+ | Geometry | 3D and 2D | 2D parts for laser cutting |
29
+ | Output | 3D formats and 2D exports | SVG and DXF with operation layers |
30
+ | Assignments | The last assignment in a scope applies throughout that scope | Assignments take effect in order |
31
+ | Type errors | Many produce `undef` and a warning | Stop evaluation with an error |
32
+ | `color()` | Sets a shape's colour | Use `cut()`, `score()`, `engrave()` or `layer()` |
33
+ | Fonts | Looks up system fonts | Uses fonts supplied by the application or CLI |
34
+ | Libraries | Can load local files | Loads the standard library and sources allowed by the host |
35
35
 
36
36
  Assignment order is especially important when adapting a design. A value
37
37
  assigned later in a KerfScript program does not change earlier geometry.
package/docs/versions.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Versions
2
2
 
3
- KerfScript 0.2.0 implements language version 0.1. The package version tracks
3
+ KerfScript 0.3.0 implements language version 0.1. The package version tracks
4
4
  the software release; the language version tracks the syntax and behavior
5
5
  of `.kerf` programs.
6
6
 
@@ -30,4 +30,4 @@ files used with a design. Applications that store designs should retain
30
30
  that information and compare the output before upgrading the interpreter.
31
31
 
32
32
  Pin the npm package to an exact version when your application needs a fixed
33
- renderer, for example `@mylasertools/kerfscript@0.2.0`.
33
+ renderer, for example `@mylasertools/kerfscript@0.3.0`.
@@ -0,0 +1,131 @@
1
+ # WebAssembly
2
+
3
+ `kerfscript.wasm` runs KerfScript from any language that can host a
4
+ WebAssembly module: Python, Rust, Go, C#, Java, Ruby and others. It is a
5
+ WASI (preview 1) module built from the same source as the JavaScript package,
6
+ so it makes the same SVG and DXF files, byte for byte.
7
+
8
+ For JavaScript applications, use the [JavaScript API](javascript.md)
9
+ instead: it is smaller and faster.
10
+
11
+ ## Getting the module
12
+
13
+ Download `kerfscript-<version>.wasm` from the
14
+ [releases](https://github.com/mylasertools/kerfscript/releases) page and save
15
+ it as `kerfscript.wasm`, or get `kerfscript-wasm-<version>.zip`, which has the
16
+ module with this guide and its licences. You can also build it from the
17
+ repository with [Javy](https://github.com/bytecodealliance/javy):
18
+
19
+ ```sh
20
+ npm run build
21
+ npm run wasm # writes dist/kerfscript.wasm
22
+ ```
23
+
24
+ Set `JAVY` to the javy binary if it is not on the `PATH`.
25
+
26
+ ## How it works
27
+
28
+ The module reads one JSON request from standard input, writes one JSON
29
+ response to standard output and exits. Start a fresh instance for each
30
+ request. From a shell, with [Wasmtime](https://wasmtime.dev):
31
+
32
+ ```sh
33
+ echo '{"source": "rect([80, 50]);"}' | wasmtime run kerfscript.wasm
34
+ ```
35
+
36
+ ### Request
37
+
38
+ | Field | Value |
39
+ | ------------- | --------------------------------------------------------------------------- |
40
+ | `command` | `"render"` (default), `"params"`, `"version"` or `"licenses"` |
41
+ | `source` | The program, as a string |
42
+ | `params` | Parameter values keyed by name |
43
+ | `files` | Libraries for `include` and `use`, keyed by path: `{"lib/tab.kerf": "..."}` |
44
+ | `fonts` | Font files keyed by name, base64 encoded. `Atkinson` is built in |
45
+ | `defaultFont` | Name to use when `text()` does not specify a font |
46
+ | `kerf` | Kerf override for every part, in millimetres |
47
+ | `layout` | `"pack"` (default) or `"none"` |
48
+ | `sheet` | Sheet size `[width, height]` in millimetres |
49
+ | `gap` | Space between packed parts |
50
+ | `margin` | Space between the parts and the sheet edge |
51
+ | `guides` | `true` to include guide layer geometry |
52
+
53
+ These match the [render() options](javascript.md#options). A path in `files`
54
+ can leave out `.kerf`, as in the program.
55
+
56
+ ### Response
57
+
58
+ Every response has `ok`. When it is `false`, `error` holds the message and
59
+ `echoes` holds what the program echoed before it stopped. The module never
60
+ exits with an error code for a bad program.
61
+
62
+ - `render`: `svg` (all sheets, for preview), `width`, `height`, `parts`,
63
+ `warnings`, `echoes` and `sheets`. Each sheet has `width`, `height`,
64
+ `material`, `thickness`, `svg` and `dxf`; write one file per sheet to cut.
65
+ - `params`: `parameters`, as from [`parameters()`](javascript.md).
66
+ - `version`: `version` (package) and `language`.
67
+ - `licenses`: `files`, the licence texts of the module and of everything
68
+ built into it, keyed by file name: `LICENSE`, `COMMERCIAL-LICENSE.md`,
69
+ `THIRD-PARTY-NOTICES.md`, `THIRD-PARTY-LICENSES.md` (the JavaScript engine
70
+ and its Rust crates) and `OFL.txt`. The release ZIP has the same files.
71
+
72
+ ## Python
73
+
74
+ With the [`wasmtime`](https://pypi.org/project/wasmtime/) package:
75
+
76
+ ```python
77
+ import json, os, tempfile
78
+ from wasmtime import Engine, Linker, Module, Store, WasiConfig
79
+
80
+ engine = Engine()
81
+ module = Module.from_file(engine, "kerfscript.wasm")
82
+ linker = Linker(engine)
83
+ linker.define_wasi()
84
+
85
+ def kerf(request):
86
+ with tempfile.TemporaryDirectory() as d:
87
+ inp, out = os.path.join(d, "in.json"), os.path.join(d, "out.json")
88
+ with open(inp, "w", encoding="utf-8") as f:
89
+ json.dump(request, f)
90
+ wasi = WasiConfig()
91
+ wasi.stdin_file = inp
92
+ wasi.stdout_file = out
93
+ store = Store(engine)
94
+ store.set_wasi(wasi)
95
+ linker.instantiate(store, module).exports(store)["_start"](store)
96
+ with open(out, encoding="utf-8") as f:
97
+ return json.load(f)
98
+
99
+ result = kerf({"source": "w = 80; rect([w, 50]);", "params": {"w": 120}})
100
+ for i, sheet in enumerate(result["sheets"]):
101
+ with open(f"sheet-{i + 1}.svg", "w", encoding="utf-8", newline="") as f:
102
+ f.write(sheet["svg"])
103
+ ```
104
+
105
+ Compile the module once and reuse it; each request only needs a new `Store`.
106
+
107
+ ## Node.js
108
+
109
+ The JavaScript package is the better fit in Node.js, but the module also runs
110
+ under `node:wasi`, which is how the test suite checks it:
111
+
112
+ ```js
113
+ import { closeSync, openSync, readFileSync, writeFileSync } from 'node:fs';
114
+ import { WASI } from 'node:wasi';
115
+
116
+ const module = await WebAssembly.compile(readFileSync('kerfscript.wasm'));
117
+ writeFileSync('in.json', JSON.stringify({ source: 'rect([80, 50]);' }));
118
+ const stdin = openSync('in.json', 'r');
119
+ const stdout = openSync('out.json', 'w');
120
+ const wasi = new WASI({ version: 'preview1', stdin, stdout, returnOnExit: true });
121
+ wasi.start(await WebAssembly.instantiate(module, wasi.getImportObject()));
122
+ closeSync(stdin);
123
+ closeSync(stdout);
124
+ console.log(JSON.parse(readFileSync('out.json', 'utf8')).svg);
125
+ ```
126
+
127
+ ## Performance
128
+
129
+ The module carries its own JavaScript engine (QuickJS), so it is about 2 MB
130
+ and renders roughly three to five times slower than Node.js. The example
131
+ designs take well under a second.