@metamesh/generator-sdk 0.5.2 → 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.
Files changed (90) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +156 -152
  3. package/dist/authoring.d.ts +57 -0
  4. package/dist/authoring.d.ts.map +1 -0
  5. package/dist/authoring.js +33 -0
  6. package/dist/authoring.js.map +1 -0
  7. package/dist/contracts.d.ts +34 -99
  8. package/dist/contracts.d.ts.map +1 -1
  9. package/dist/execute-generator.d.ts +22 -28
  10. package/dist/execute-generator.d.ts.map +1 -1
  11. package/dist/execute-generator.js +27 -32
  12. package/dist/execute-generator.js.map +1 -1
  13. package/dist/generated-parts.d.ts +1 -2
  14. package/dist/generated-parts.d.ts.map +1 -1
  15. package/dist/generated-parts.js +7 -31
  16. package/dist/generated-parts.js.map +1 -1
  17. package/dist/generator-module.d.ts +18 -40
  18. package/dist/generator-module.d.ts.map +1 -1
  19. package/dist/generator-module.js +4 -111
  20. package/dist/generator-module.js.map +1 -1
  21. package/dist/generator-package/constants.d.ts +2 -2
  22. package/dist/generator-package/constants.js +2 -2
  23. package/dist/generator-package/schemas/definition.schema.d.ts +3 -0
  24. package/dist/generator-package/schemas/definition.schema.d.ts.map +1 -1
  25. package/dist/generator-package/schemas/manifest.schema.d.ts +5 -8422
  26. package/dist/generator-package/schemas/manifest.schema.d.ts.map +1 -1
  27. package/dist/generator-package/schemas/parameter.schema.d.ts +7 -0
  28. package/dist/generator-package/schemas/parameter.schema.d.ts.map +1 -1
  29. package/dist/generator-package/schemas/parameter.schema.js +2 -0
  30. package/dist/generator-package/schemas/parameter.schema.js.map +1 -1
  31. package/dist/generator-package/schemas/preset.schema.d.ts +2 -13
  32. package/dist/generator-package/schemas/preset.schema.d.ts.map +1 -1
  33. package/dist/generator-package/schemas/preset.schema.js.map +1 -1
  34. package/dist/generator-package/validate-definition.d.ts.map +1 -1
  35. package/dist/generator-package/validate-definition.js +18 -0
  36. package/dist/generator-package/validate-definition.js.map +1 -1
  37. package/dist/generator-package/validate-manifest.d.ts.map +1 -1
  38. package/dist/generator-package/validate-manifest.js +2 -13
  39. package/dist/generator-package/validate-manifest.js.map +1 -1
  40. package/dist/generator-services.d.ts +1 -1
  41. package/dist/generator-services.d.ts.map +1 -1
  42. package/dist/index.d.ts +8 -15
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +2 -6
  45. package/dist/index.js.map +1 -1
  46. package/dist/parameter-contracts.d.ts +121 -0
  47. package/dist/parameter-contracts.d.ts.map +1 -0
  48. package/dist/parameter-contracts.js +2 -0
  49. package/dist/parameter-contracts.js.map +1 -0
  50. package/dist/parameters/issues.d.ts +14 -0
  51. package/dist/parameters/issues.d.ts.map +1 -0
  52. package/dist/parameters/issues.js +262 -0
  53. package/dist/parameters/issues.js.map +1 -0
  54. package/dist/parameters/preparation.d.ts +13 -0
  55. package/dist/parameters/preparation.d.ts.map +1 -0
  56. package/dist/parameters/preparation.js +35 -0
  57. package/dist/parameters/preparation.js.map +1 -0
  58. package/dist/parameters/recovery.d.ts +10 -0
  59. package/dist/parameters/recovery.d.ts.map +1 -0
  60. package/dist/parameters/recovery.js +124 -0
  61. package/dist/parameters/recovery.js.map +1 -0
  62. package/dist/parameters/strict.d.ts +30 -0
  63. package/dist/parameters/strict.d.ts.map +1 -0
  64. package/dist/parameters/strict.js +116 -0
  65. package/dist/parameters/strict.js.map +1 -0
  66. package/dist/parameters/values.d.ts +13 -0
  67. package/dist/parameters/values.d.ts.map +1 -0
  68. package/dist/parameters/values.js +73 -0
  69. package/dist/parameters/values.js.map +1 -0
  70. package/dist/parameters.d.ts +7 -69
  71. package/dist/parameters.d.ts.map +1 -1
  72. package/dist/parameters.js +5 -592
  73. package/dist/parameters.js.map +1 -1
  74. package/dist/parse-generator-module.d.ts +29 -0
  75. package/dist/parse-generator-module.d.ts.map +1 -0
  76. package/dist/parse-generator-module.js +137 -0
  77. package/dist/parse-generator-module.js.map +1 -0
  78. package/dist/runtime.d.ts +10 -0
  79. package/dist/runtime.d.ts.map +1 -0
  80. package/dist/runtime.js +5 -0
  81. package/dist/runtime.js.map +1 -0
  82. package/dist/worker-contracts.d.ts +80 -0
  83. package/dist/worker-contracts.d.ts.map +1 -0
  84. package/dist/worker-contracts.js +12 -0
  85. package/dist/worker-contracts.js.map +1 -0
  86. package/package.json +14 -1
  87. package/dist/prepared-generator-module.d.ts +0 -24
  88. package/dist/prepared-generator-module.d.ts.map +0 -1
  89. package/dist/prepared-generator-module.js +0 -124
  90. package/dist/prepared-generator-module.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @metamesh/generator-sdk
2
2
 
3
+ ## 0.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - b1b97b9: Replace `ModelTimings.renderUploadMs` with optional `viewportPrepareMs` and `firstDrawMs` to
8
+ distinguish viewport preparation from the elapsed time to first draw submission. Consumers must
9
+ read the new fields; the combined metric is removed.
10
+
11
+ ## 0.6.0
12
+
13
+ ### Minor Changes
14
+
15
+ - Replace format-1 validation/generation with exact format-2 prepare/build modules targeting runtime 2.0.0. Add opaque one-use preparation capture, lean authoring and host subpaths, integer numeric declarations, and canonical manufacturing output without normals.
16
+
3
17
  ## 0.5.2
4
18
 
5
19
  ### Patch Changes
package/README.md CHANGED
@@ -1,51 +1,43 @@
1
1
  # `@metamesh/generator-sdk`
2
2
 
3
- Portable contracts and helpers shared by Studio, built-in generators, and workers.
3
+ Runtime-neutral contracts and helpers for Generators, hosts, and workers.
4
4
 
5
- > **Preview:** `0.x` releases may contain breaking changes in a minor release. Patch releases are
6
- > intended to remain compatible within the same minor line. A stable support commitment begins only
7
- > after an explicit `1.0.0` review.
5
+ > **Preview:** `0.x` minor releases may break compatibility. Patches remain compatible within the
6
+ > same minor line. Stable support requires an explicit `1.0.0` review.
8
7
 
9
- Install the public package from npm:
8
+ Install a published release from npm:
10
9
 
11
10
  ```bash
12
11
  pnpm add @metamesh/generator-sdk
13
12
  ```
14
13
 
15
- The package root exposes the Generator API. Use `@metamesh/generator-sdk/fonts` for only
16
- `FontSelection` and `isFontSelection`, without loading Generator services or Zod. Both exports
17
- remain available from the root.
14
+ The root provides lightweight authoring contracts and helpers with explicit TypeScript interfaces.
15
+ It has no Zod, modeling, renderer, browser, or Worker dependencies.
16
+ Use the supported subpaths for host responsibilities:
18
17
 
19
- The package owns parameter/group definitions, serializable conditions, default derivation and
20
- normalization, structured validation, runtime envelopes, normalized generated-model results, and
21
- serializable catalog/package/install contracts. Project persistence belongs to Studio;
22
- release-state transitions belong to trusted database/tooling boundaries.
18
+ | Entry | Responsibility |
19
+ | ------------- | ------------------------------------------------------------------------------- |
20
+ | Root | Definitions, presets, preparation/module authoring, parameter and service types |
21
+ | `/parameters` | Defaults, normalization, strict codecs, schema issues |
22
+ | `/runtime` | Evaluated module parsing, execution, mesh DTO parsing, Worker envelopes |
23
+ | `/packages` | Package, catalog and installation trust parsing |
24
+ | `/fonts` | Standalone font selection contract |
23
25
 
24
- The optional `Export3mfRequest.partColors` map carries Project color overrides keyed by semantic
25
- part ID. Studio validates and applies it during export; it does not enter Generator parameters or
26
- change package generation contracts. Omitted overrides preserve generated colors.
27
-
28
- Zod is the only runtime dependency. The [public entry point](https://github.com/JasonKoolman/metamesh/blob/main/packages/generator-sdk/src/index.ts) owns exact exports;
29
- strict schemas own accepted boundary data. Hosts own package delivery and execution.
26
+ Definitions remain callback-free and serializable. Preparation/build state is local to execution;
27
+ Studio owns persistence and the Modeling engine owns modeling and manufacturing finalization.
30
28
 
31
29
  ## Parameter definitions
32
30
 
33
- Definitions use the `type` discriminant with `number`, `slider`, `boolean`, `select`, `choice-set`,
34
- `color`, `text`, `font`, `svg`, `profile`, and homogeneous `array` variants. A `slider` requires `min`, `max`, and
35
- `step`, while a `number` may omit any of those constraints. Visibility uses serializable
36
- `ConditionExpression` values; `evaluateCondition` supports `all`, `any`, `not`,
37
- equality/inequality, numeric comparisons, and `choice-set` membership through `contains`.
31
+ Parameters use a `type` discriminant: `number`, `slider`, `boolean`, `select`, `choice-set`, `color`,
32
+ `text`, `font`, `svg`, `profile`, or homogeneous `array`. Sliders require `min`, `max`, and `step`;
33
+ numbers may omit them. Visibility uses serializable `ConditionExpression` values with `all`, `any`,
34
+ `not`, equality/inequality, numeric comparisons, and choice-set membership (`contains`).
38
35
 
39
36
  Define controls once, then normalize saved values or strictly parse boundary values:
40
37
 
41
38
  ```ts
42
- import {
43
- createParamCodec,
44
- defineGenerator,
45
- EMPTY_SVG_PARAMETER_DEFAULT,
46
- prepareParameters,
47
- type GeneratorParams,
48
- } from "@metamesh/generator-sdk";
39
+ import { defineGenerator, type GeneratorParams } from "@metamesh/generator-sdk";
40
+ import { createParamCodec, prepareParameters } from "@metamesh/generator-sdk/parameters";
49
41
 
50
42
  const definition = defineGenerator({
51
43
  id: "sleeve",
@@ -65,14 +57,16 @@ const workerValues: unknown = { outerDiameter: 24, boreDiameter: 10 };
65
57
  const strictParameters = createParamCodec(definition).parse(workerValues);
66
58
  ```
67
59
 
68
- Normalization drops unknown parameter-value keys, fills missing or invalid values from each
69
- parameter's declared `default`, clamps numbers, and returns a fresh frozen map. Strict codecs instead
70
- reject missing, unknown, invalid, or noncanonical values and return cloned frozen structured values.
71
- `prepareParameters()` normalizes once and returns that same canonical map with the combined schema
72
- and Generator-owned validation result. Definitions accept only canonical parameter types. SVG runtime values remain
73
- standard strings and are limited to 256 KiB each and 512 KiB per normalized project.
60
+ Normalization drops unknown keys, restores missing or invalid values from declared defaults,
61
+ clamps numbers, and returns a fresh frozen map. Strict codecs reject missing, unknown, invalid, or
62
+ noncanonical values and return cloned frozen structured values. `prepareParameters()` normalizes
63
+ once and returns the map with schema issues. Execution aggregates Generator preparation issues
64
+ separately. Definitions accept only
65
+ canonical types. SVG values are strings limited to 256 KiB each and 512 KiB per normalized Project.
74
66
 
75
67
  ```ts
68
+ import { EMPTY_SVG_PARAMETER_DEFAULT } from "@metamesh/generator-sdk/parameters";
69
+
76
70
  const profile = {
77
71
  id: "profile",
78
72
  label: "Profile",
@@ -81,11 +75,12 @@ const profile = {
81
75
  } as const;
82
76
  ```
83
77
 
84
- Choice-set definitions use fixed, uniquely valued options and a default array of selected option
85
- values. Optional `minSelections` and `maxSelections` bound the selection count. Normalized values
86
- are unique and use option declaration order; invalid values fall back to the canonical default.
87
- `{ param: "outputs", contains: "nut" }` may reference only a `choice-set` and one of its declared
88
- option values.
78
+ ### Choice sets and arrays
79
+
80
+ Choice sets have uniquely valued options and a default selection array. `minSelections` and
81
+ `maxSelections` optionally bound its size. Normalization orders valid selections by option
82
+ declaration; duplicate or invalid selections recover to the canonical default. `{ param: "outputs", contains: "nut" }`
83
+ must reference a choice set and one of its declared options.
89
84
 
90
85
  Array definitions declare `items.type` as `number` or `string`. They may constrain `minItems` and
91
86
  `maxItems`; number items may also declare `min`, `max`, `step`, and `unit`. Array-valued controls are
@@ -106,58 +101,49 @@ const points = {
106
101
  Groups are ordered sections with stable IDs, user-facing labels, optional descriptions, and an
107
102
  optional `collapsed` default. Parameters without a group render in Studio's General section.
108
103
 
104
+ ### Fonts and text
105
+
109
106
  Font parameters use a `FontSelection` default with immutable `faceId`, SHA-256 `revision`, and
110
107
  integer `weight`. Structural validation does not require the font to be available locally. Valid
111
- unavailable references survive codecs, presets, and persistence. Studio prepares the
112
- declared fonts before executable validation and generation. It supplies `GeneratorServices` as the
113
- second validator argument and third generation argument. `services.text.layout(text, { font,
114
- fontSize })` returns positioned vector curves and matching metrics in millimetres. Geometry's
115
- `withTextFonts(services.text, callback)` binds these services for `textContours()` and
116
- `textMetrics()` in compiled code. The SDK and Geometry keep their small structural text contracts
117
- compatible without importing each other's runtime. Font parameters and prepared text services require
118
- runtime `1.2.0` or later. Package format remains 1.
119
-
120
- `GeneratorDefinition` is callback-free, JSON-safe metadata used by forms, manifests, persistence,
121
- and normalization. Its `min`, `max`, options, and other field constraints are portable
122
- data. `defineGenerator()` preserves literal IDs and select options so `GeneratorParams`,
123
- `GeneratorParamId`, `GeneratorIssue`, validators, defaults, and presets derive from the definition.
124
- Keep derived-value, cross-parameter, warning, and geometry checks in a separate TypeScript
125
- validator:
126
-
127
- ```ts
128
- import { defineValidator } from "@metamesh/generator-sdk";
108
+ unavailable references survive codecs, presets, and persistence. Studio prepares declared fonts before the Generator's `prepare(parameters, services)` callback.
109
+ `services.text.layout(text, { font, fontSize })` returns exact vector paths and measurements in
110
+ millimetres. Keep that layout in the preparation value when checks and construction both need it.
111
+ Imported `textSection(layout)` consumes it during build. No ambient font provider is bound.
129
112
 
130
- export const validate = defineValidator(definition, (parameters) => {
131
- return parameters.outerDiameter - parameters.boreDiameter < 2
132
- ? [{ severity: "error", parameterId: "boreDiameter", message: "Leave at least a 1 mm wall." }]
133
- : [];
134
- });
135
- ```
113
+ ### Validation and bounds
136
114
 
137
- `defineValidator()` preserves the callback's signature. Pass a parameter-only validator directly
138
- to `prepareParameters()` or `validateParameters()`. For a validator that needs runtime services,
139
- bind the prepared services explicitly: `(parameters) => validate(parameters, services)`. Use
115
+ `GeneratorDefinition` is callback-free, JSON-safe metadata for forms, manifests, persistence, and
116
+ normalization. `defineGenerator()` preserves literal IDs/options; `GeneratorParams` and
117
+ `GeneratorIssue` derive exact parameter types and issue IDs from that definition. Use
140
118
  `definePresets(definition, presets)` to require complete definition-derived preset values.
141
119
 
142
- Schema editors only receive `definition`; they never serialize, parse, or rewrite validators.
143
- Downloaded manifests remain callback-free and do not define a validation rules language. Runtime
144
- validation receives normalized, frozen values. Its issue array is strictly checked and bounded;
145
- malformed output is a runtime error rather than a user-facing validation result.
146
- Combined validation retains at most 100 issues, prioritizing errors over warnings while preserving
147
- the retained issues' original order. Reaching the reporting limit cannot hide a blocking error.
120
+ Per-field constraints belong in the definition. Cross-parameter, warning, manufacturing and geometry
121
+ constraints belong in `prepare`, alongside the dimensions and samples that `build` will consume.
122
+ A successful result is `{ ok: true, value, issues }`; an invalid result is `{ ok: false, issues }`.
123
+ Successful preparation may contain warnings but no errors. Invalid preparation requires an error,
124
+ and prevents build and native shape construction.
125
+
126
+ Numeric parameters and numeric array items may declare `integer: true`. Their defaults and supplied
127
+ bounds must be integers. Strict codecs reject fractions; saved fractions recover to the declared
128
+ default. Numeric array recovery replaces the invalid array with its default. `step` only describes
129
+ the control increment and does not imply integer values.
148
130
 
149
- Use `getDefaultParameters(definition)` to derive a fresh frozen defaults map. IDs are bounded safe keys; package
150
- manifests, definitions, descriptors, conditions, and nested parameter objects reject unknown fields.
131
+ Schema editors receive only the definition and never rewrite validators. Manifests remain
132
+ callback-free. Preparation receives normalized, frozen parameters; malformed issue output is a
133
+ runtime error. Combined validation retains at most 100 issues, prioritizing errors over warnings
134
+ and preserving retained order. The reporting limit cannot hide a blocking error.
135
+
136
+ Use `getDefaultParameters(definition)` for a fresh frozen defaults map. IDs are bounded safe keys.
137
+ Package manifests, definitions, descriptors, conditions, and nested parameter objects reject unknown fields.
151
138
  Definitions are bounded to 64 groups and 256 parameters, arrays to 1,024 items, options to 512,
152
139
  presets to 256, and condition composition to 8 levels with 32 children per node.
153
140
 
154
141
  ### Editable side profiles
155
142
 
156
- A `profile` declares an open side outline as a versioned `ProfileValue`. Its start and each segment
157
- end are anchors with normalized `radius` and upward `height` coordinates, plus `mode` (`auto`,
158
- `smooth`, or `corner`) to retain editing intent. A segment is either `line` or `cubic`; cubic
159
- segments carry explicit `control1` and `control2` points. Explicit coordinates always define the
160
- shape; hosts must write resolved tangents into the controls when editing automatic points.
143
+ A `profile` declares an open side outline as a versioned `ProfileValue`. Start/end anchors carry
144
+ normalized `radius` and upward `height` coordinates, plus editing `mode` (`auto`, `smooth`, or
145
+ `corner`). Segments are `line` or `cubic`, with cubic `control1` and `control2` points. Coordinates
146
+ define the shape; hosts must resolve automatic tangents into those control points.
161
147
 
162
148
  ```ts
163
149
  const sideProfile = {
@@ -197,8 +183,7 @@ bindings leaves the editor in normalized coordinates.
197
183
  `cloneProfileValue()` returns a fresh deeply frozen value, and `profileValuesEqual()` compares both
198
184
  shape and editing intent. Normalization, strict codecs, defaults, and presets clone every nested
199
185
  profile object. Invalid saved profiles recover to the declared default; strict boundaries reject
200
- them. Profiles cannot be condition operands. Profile parameters require runtime `1.3.0` or later;
201
- package format remains `1`.
186
+ them. Profiles cannot be condition operands. All executable packages use format `2` and runtime major `2`.
202
187
 
203
188
  ### Curve resolution
204
189
 
@@ -221,88 +206,107 @@ const curveDeviation = {
221
206
  } as const;
222
207
  ```
223
208
 
224
- Multiple controls per definition may have this role. Each requires `unit: "mm"`, a positive `default`,
225
- and an explicit positive `min`; existing numeric bounds and step rules still apply. Lower values
226
- request closer curve approximation. The Generator consumes the value in its physical
227
- `GeometryResolution` policy and retains local exact counts where topology requires them. Hosts
228
- edit the saved parameters directly and present their authored labels in definition order; preview
229
- and manufacturing use the same values. The Generator decides which curves or parts each parameter
230
- affects, including shared curves across several parts. Studio does not infer part overrides from
231
- mesh IDs. Resolution requests exceeding model complexity
232
- limits must fail explicitly instead of silently coarsening geometry.
233
-
234
- Packages declaring this role require runtime `1.3.0` or later and retain package format `1` and the
235
- existing five module exports. Definitions without the role retain their existing behavior.
209
+ Multiple controls may have this role. Each requires `unit: "mm"`, a positive `default`, and an
210
+ explicit positive `min`; numeric bounds and step rules still apply. Lower values request closer
211
+ approximation through the Generator's `GeometryResolution` policy; exact counts remain available
212
+ for matched topology. Hosts show authored labels in definition order and edit the saved parameters
213
+ used by both preview and manufacturing. Generators determine affected curves and parts; Studio
214
+ does not infer overrides from mesh IDs. Requests beyond complexity limits must fail explicitly.
236
215
 
237
216
  ## Compiled module and output boundary
238
217
 
239
- `defineGeneratorModule()` assembles and freezes the exact five-field format-1 runtime surface from
240
- a typed definition, validator, and generator without coupling the SDK to a modeling library:
218
+ `defineGeneratorModule()` creates the five-field format-2 surface. Preparation resolves the model
219
+ once; build receives that exact value. Imported modeling helpers bind to the host's synchronous build:
241
220
 
242
221
  ```ts
243
- const runtime = defineGeneratorModule<typeof definition, ModelingContext>({
222
+ import { defineGeneratorModule, definePreparation, prepared } from "@metamesh/generator-sdk";
223
+ import { tube } from "@metamesh/modeling";
224
+
225
+ const prepareSleeve = definePreparation(definition, (parameters, { invalid }) => {
226
+ if (parameters.boreDiameter >= parameters.outerDiameter)
227
+ return invalid("boreDiameter", "The bore must be smaller than the outside diameter.");
228
+ return prepared({
229
+ outerRadius: parameters.outerDiameter / 2,
230
+ innerRadius: parameters.boreDiameter / 2,
231
+ });
232
+ });
233
+ const runtime = defineGeneratorModule({
244
234
  definition,
245
- validate,
246
- generate,
235
+ prepare: prepareSleeve,
236
+ build: (dimensions) => tube({ ...dimensions, height: 20 }),
247
237
  });
248
238
 
249
- export const { formatVersion, generate, generatorId, validate, version } = runtime;
239
+ export const { formatVersion, generatorId, version, prepare, build } = runtime;
250
240
  ```
251
241
 
252
- The author helper accepts definition-derived canonical parameters and adds neither runtime checking
253
- nor a parameter decoder. This keeps self-contained releases small and lets a host reuse the exact
254
- frozen map produced by `prepareParameters()`.
255
- `parseGeneratorModule(namespace, definition)` is the corresponding host-side boundary for evaluated
256
- packages: it requires exactly those five exports, verifies identity against the authoritative
257
- definition, wraps callbacks to enforce synchronous array results, and returns two frozen lanes. Use `canonical`
258
- only with host-prepared parameters; use `strict` for raw boundary values that must pass the host's
259
- codec. Extra exports fail closed.
260
-
261
- Hosts always invoke modules with `validate(parameters, services)` and
262
- `generate(parameters, context, services)`. Font-free hosts pass `UNPREPARED_GENERATOR_SERVICES`;
263
- font-dependent hosts prepare and pass the selected fonts before executable validation. Author
264
- callbacks may omit arguments they do not use.
265
-
266
- Generators with expensive SVG import, font layout, encoding, or dimension resolution may use
267
- `definePreparedGeneratorModule({ definition, prepare, generate })`. `prepare(parameters, services)`
268
- returns `{ ok: true, value, issues }` or `{ ok: false, issues }`, with definition-derived structured
269
- issues. Successful results may contain warnings but no errors; invalid results require an error.
270
- The model callback receives `generate(parameters, context, services, preparedValue)`. The returned
271
- module still exports exactly the same five fields and uses the unchanged host call signatures.
272
-
273
- Preparation accepts finite scalar data, plain objects, and arrays, which the helper checks and
274
- freezes. It rejects functions, accessors, cycles, promises, class instances, typed arrays, and live
275
- WASM objects, with traversal bounded to 1,000,000 values and 64 levels.
276
-
277
- The helper may reuse one successful validation result for the next generation when the exact
278
- deeply frozen parameters and frozen services/provider objects match. Different or mutable objects
279
- and direct generation prepare afresh; later calls and failures clear the retained result. Generators
280
- still own geometry complexity limits. See the [helper source](https://github.com/JasonKoolman/metamesh/blob/main/packages/generator-sdk/src/prepared-generator-module.ts) for
281
- the reuse checks.
282
-
283
- Call `parseGeneratedParts()` on executable output before downstream mesh work. It accepts only
284
- non-empty dense arrays of own data values containing uniquely identified parts backed by transferable `Float32Array` and
285
- `Uint32Array` storage, checks finite coordinates and valid indices, normalizes six-digit hex colors
286
- to lowercase, and enforces aggregate part, vertex, triangle, and backing-buffer byte limits. Closed-solid,
287
- manufacturing, and reconstruction checks remain Geometry or Generator responsibilities.
288
-
289
- Hosts should use `executeGenerator()` as the single synchronous execution seam. It normalizes raw
290
- values, runs schema and Generator validation, parses generated output, and returns either an explicit
291
- `invalid` result or a `generated` result. Hosts that prepare asynchronous services first can pass the
292
- `PreparedParameters` result to `executePreparedGenerator()`; malformed executable output remains a
293
- thrown runtime failure.
242
+ `definePreparation(definition, callback)` infers readonly parameter values, prepared data and
243
+ `PreparationServices<typeof definition>`. Its second callback argument retains `services.text`
244
+ and adds `invalid(parameterId, message)` and `invalid(issue)`, with allowed IDs checked directly at
245
+ each argument. Destructure `{ invalid }` or call `services.invalid(...)`. A warning alone cannot
246
+ produce failed preparation.
247
+
248
+ The helper returns a preparation wrapper that accepts parameters and optional host `GeneratorServices`.
249
+ Omitting services uses `UNPREPARED_GENERATOR_SERVICES` for parameter-only probes; requesting
250
+ text through that provider fails explicitly. Hosts pass their prepared services.
251
+ It adds the scoped helper for each callback invocation, preserving the exact callback result and
252
+ prepared value without traversal or freezing. The callback itself is not returned unchanged.
253
+ `prepared(value)` remains the successful-value helper. Standalone global `invalid` accepts literal
254
+ IDs or existing error issues; the host checks their IDs against the definition at the runtime
255
+ boundary.
256
+
257
+ The root helper has no decoder or Modeling runtime dependency. Modeling owns `ModelingContext`
258
+ and `BuildOutput`; SDK context/output generics preserve author typing without importing them.
259
+ Prepared values are opaque local data: the SDK does not traverse, clone, serialize or freeze them.
260
+ Preparation must not acquire live modeling resources. Prepared values are never persisted,
261
+ transferred to the UI, or reused between requests.
262
+
263
+ Hosts import `parseGeneratorModule`, `prepareGenerator` and `executeGenerator` from `/runtime`.
264
+ The parser requires exactly the five exports above and matching manifest identity. Evaluated
265
+ preparation captures an opaque value in a one-use build closure; there is no raw prepared-value
266
+ invocation lane. Callback results must be synchronous. The host checks the own-data preparation
267
+ wrapper and bounded structured issues while leaving the value opaque.
268
+
269
+ Studio's authored source surface accepts `build` and optional `prepare`. Its compiler supplies
270
+ identity and `prepareIdentity(definition)` when preparation is omitted, then passes the resulting
271
+ five-field module through this same parser and executor. Compiled packages always include `prepare`.
272
+
273
+ `prepareGenerator` normalizes once and either returns `invalid` or a prepared result containing
274
+ `build(context)`. Use that closure inside `engine.build()` so one engine owns modeling, mesh
275
+ validation, limits, measurements and cleanup. `executeGenerator` accepts a context directly and
276
+ returns abstract build output; it does not finalize geometry. Hosts preparing asynchronous services
277
+ first may supply their `/parameters` `PreparedParameters` result as `parameterState` to reuse it.
278
+
279
+ `parseGeneratedParts(parts, limits)` strictly checks the final transferable DTO. The host supplies
280
+ its complete limits; the SDK does not own a duplicate default resource policy. Canonical parts have
281
+ unique IDs, Float32 positions, Uint32 indices and optional six-digit colors. Normals belong to
282
+ viewport presentation. Geometric/manufacturing validation belongs to the shared engine.
283
+
284
+ Package format `1`, runtime major `1`, retired `validate/generate` module exports and extra exports
285
+ reject explicitly. No compatibility adapter or preparation cache remains.
294
286
 
295
287
  ## Worker boundary
296
288
 
297
- `ValidateRequest`, `GenerateRequest`, `Export3mfRequest`, and the `WorkerResponse` union carry
298
- monotonic request IDs. Validation errors are structured normal results; only actual worker,
299
- validator, generator, or exporter exceptions use error envelopes. `GeneratedModel` contains only
300
- structured data and transferable typed arrays—never live Manifold objects. A model can contain
301
- multiple `GeneratedPart` entries and includes bounds, warnings, and optional statistics. Studio
302
- supplies export filenames and the `MeshTopologyMode` selected in device preferences; generators supply semantic part
303
- IDs and colors. `original` preserves the generated final triangles, while `optimized` permits
304
- Studio's bounded planar-only topology pass after generation. This runtime choice is not a Generator
305
- parameter, package field, or persisted Project contract.
289
+ Worker requests/responses are `/runtime` contracts containing structured data and transferable
290
+ arrays, never live modeling handles. Validation issues are normal outcomes; callback, Worker and
291
+ exporter exceptions use error envelopes. Finalized previews carry revision identity, bounds,
292
+ warnings and optional statistics. Manufacturing exports serialize the requested finalized revision,
293
+ including appearance overrides, without running preparation/build again.
294
+
295
+ Successful generation envelopes include `sentAt`, the sender's high-resolution epoch milliseconds
296
+ captured immediately before transfer. Hosts use `/runtime` `parseGenerationSentAt()` to reject
297
+ missing, inherited, accessor, or nonfinite timestamps before measuring transfer latency. Worker
298
+ clock construction and receiver timings belong to the host.
299
+
300
+ `ModelTimings.prepareMs` measures Generator preparation; `viewportPrepareMs` measures host viewport
301
+ preparation. `firstDrawMs` starts when viewport preparation ends and measures elapsed time through
302
+ every part's first draw submission, including render scheduling and buffer upload. Viewport fields
303
+ are optional until the host records completion. First draw does not measure GPU completion.
304
+ `totalMs` is request-to-completion elapsed time, including scheduling between stages, rather than
305
+ the sum of stage timings. Studio reports request-to-delivery time until viewport completion.
306
+
307
+ `MeshTopologyMode` is host policy rather than a Generator parameter. Both compiled and authored
308
+ models use the same engine finalization and limits. Studio's runtime contract owns request lifecycle,
309
+ revision retention and export recovery.
306
310
 
307
311
  ## Development
308
312
 
@@ -0,0 +1,57 @@
1
+ import type { GeneratorDefinition, GeneratorIssue, GeneratorParams, GeneratorPreset, ParameterIssue } from "./contracts.js";
2
+ import type { Preparation } from "./generator-module.js";
3
+ import { type GeneratorServices } from "./generator-services.js";
4
+ /** Captures literal IDs and options; the host validates serializable metadata. */
5
+ export declare function defineGenerator<const D extends GeneratorDefinition>(definition: D): D;
6
+ /** Contextually types complete presets from the same definition as the controls. */
7
+ export declare function definePresets<const D extends GeneratorDefinition, const Presets extends readonly GeneratorPreset<NoInfer<D>>[]>(_definition: D, presets: Presets): Presets;
8
+ /** Successful worker-local preparation. The value is never cloned or inspected. */
9
+ export declare function prepared<P>(value: P): {
10
+ readonly ok: true;
11
+ readonly value: P;
12
+ readonly issues: readonly [];
13
+ };
14
+ /** Failed preparation attributed to a literal parameter ID or an existing error issue. */
15
+ export declare function invalid<const Id extends string>(parameterId: Id, message: string): {
16
+ readonly ok: false;
17
+ readonly issues: readonly [
18
+ {
19
+ readonly severity: "error";
20
+ readonly parameterId: Id;
21
+ readonly message: string;
22
+ }
23
+ ];
24
+ };
25
+ export declare function invalid<const Issue extends ParameterIssue & {
26
+ readonly severity: "error";
27
+ }>(issue: Issue): {
28
+ readonly ok: false;
29
+ readonly issues: readonly [Issue];
30
+ };
31
+ /** Definition-bound error reporting plus the host's unchanged text service. */
32
+ export interface PreparationServices<D extends GeneratorDefinition> extends GeneratorServices {
33
+ /** Reports one error using a declared parameter ID or an existing definition-bound issue. */
34
+ readonly invalid: {
35
+ <const Id extends D["parameters"][number]["id"]>(parameterId: Id, message: string): {
36
+ readonly ok: false;
37
+ readonly issues: readonly [
38
+ {
39
+ readonly severity: "error";
40
+ readonly parameterId: Id;
41
+ readonly message: string;
42
+ }
43
+ ];
44
+ };
45
+ <const Issue extends GeneratorIssue<D> & {
46
+ readonly severity: "error";
47
+ }>(issue: Issue): {
48
+ readonly ok: false;
49
+ readonly issues: readonly [Issue];
50
+ };
51
+ };
52
+ }
53
+ /** Wraps preparation with definition-bound services while preserving its exact result and value. */
54
+ export declare function definePreparation<const D extends GeneratorDefinition, Result extends Preparation<NoInfer<D>, unknown>>(_definition: D, callback: (parameters: GeneratorParams<NoInfer<D>>, services: PreparationServices<NoInfer<D>>) => Result): (parameters: GeneratorParams<D>, services?: GeneratorServices) => Result;
55
+ /** Parameter-only preparation is useful for models with no derived dimensions. */
56
+ export declare function prepareIdentity<const D extends GeneratorDefinition>(_definition: D): (parameters: GeneratorParams<D>) => ReturnType<typeof prepared<GeneratorParams<D>>>;
57
+ //# sourceMappingURL=authoring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authoring.d.ts","sourceRoot":"","sources":["../src/authoring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,eAAe,EACf,cAAc,EACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAiC,KAAK,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEhG,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,KAAK,CAAC,CAAC,SAAS,mBAAmB,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,CAErF;AAED,oFAAoF;AACpF,wBAAgB,aAAa,CAC3B,KAAK,CAAC,CAAC,SAAS,mBAAmB,EACnC,KAAK,CAAC,OAAO,SAAS,SAAS,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,EAC5D,WAAW,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAE3C;AAED,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG;IACrC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC;CAC9B,CAEA;AAED,0FAA0F;AAC1F,wBAAgB,OAAO,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAC7C,WAAW,EAAE,EAAE,EACf,OAAO,EAAE,MAAM,GACd;IACD,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,MAAM,EAAE,SAAS;QACxB;YAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;YAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;YAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;SAAE;KACnF,CAAC;CACH,CAAC;AACF,wBAAgB,OAAO,CAAC,KAAK,CAAC,KAAK,SAAS,cAAc,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,EACzF,KAAK,EAAE,KAAK,GACX;IACD,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC;CACnC,CAAC;AAkBF,+EAA+E;AAC/E,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS,mBAAmB,CAAE,SAAQ,iBAAiB;IAC3F,6FAA6F;IAC7F,QAAQ,CAAC,OAAO,EAAE;QAChB,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAC7C,WAAW,EAAE,EAAE,EACf,OAAO,EAAE,MAAM,GACd;YACD,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;YACnB,QAAQ,CAAC,MAAM,EAAE,SAAS;gBACxB;oBAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;oBAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;oBAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;iBAAE;aACnF,CAAC;SACH,CAAC;QACF,CAAC,KAAK,CAAC,KAAK,SAAS,cAAc,CAAC,CAAC,CAAC,GAAG;YAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;SAAE,EACrE,KAAK,EAAE,KAAK,GACX;YACD,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;YACnB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC;SACnC,CAAC;KACH,CAAC;CACH;AAED,oGAAoG;AACpG,wBAAgB,iBAAiB,CAC/B,KAAK,CAAC,CAAC,SAAS,mBAAmB,EACnC,MAAM,SAAS,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAE/C,WAAW,EAAE,CAAC,EACd,QAAQ,EAAE,CACR,UAAU,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EACvC,QAAQ,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KACtC,MAAM,GACV,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,iBAAiB,KAAK,MAAM,CAG1E;AAED,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,KAAK,CAAC,CAAC,SAAS,mBAAmB,EACjE,WAAW,EAAE,CAAC,GACb,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,OAAO,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAGrF"}
@@ -0,0 +1,33 @@
1
+ import { UNPREPARED_GENERATOR_SERVICES } from "./generator-services.js";
2
+ /** Captures literal IDs and options; the host validates serializable metadata. */
3
+ export function defineGenerator(definition) {
4
+ return definition;
5
+ }
6
+ /** Contextually types complete presets from the same definition as the controls. */
7
+ export function definePresets(_definition, presets) {
8
+ return presets;
9
+ }
10
+ /** Successful worker-local preparation. The value is never cloned or inspected. */
11
+ export function prepared(value) {
12
+ return { ok: true, value, issues: [] };
13
+ }
14
+ export function invalid(issueOrId, message) {
15
+ if (typeof issueOrId === "string") {
16
+ if (typeof message !== "string")
17
+ throw new TypeError("Invalid preparation needs an error message.");
18
+ return { ok: false, issues: [{ severity: "error", parameterId: issueOrId, message }] };
19
+ }
20
+ if (issueOrId === null || typeof issueOrId !== "object" || issueOrId.severity !== "error")
21
+ throw new TypeError("Invalid preparation requires an error issue.");
22
+ return { ok: false, issues: [issueOrId] };
23
+ }
24
+ /** Wraps preparation with definition-bound services while preserving its exact result and value. */
25
+ export function definePreparation(_definition, callback) {
26
+ return (parameters, services = UNPREPARED_GENERATOR_SERVICES) => callback(parameters, { ...services, invalid });
27
+ }
28
+ /** Parameter-only preparation is useful for models with no derived dimensions. */
29
+ export function prepareIdentity(_definition) {
30
+ void _definition;
31
+ return prepared;
32
+ }
33
+ //# sourceMappingURL=authoring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authoring.js","sourceRoot":"","sources":["../src/authoring.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,6BAA6B,EAA0B,MAAM,yBAAyB,CAAC;AAEhG,kFAAkF;AAClF,MAAM,UAAU,eAAe,CAAsC,UAAa;IAChF,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,aAAa,CAG3B,WAAc,EAAE,OAAgB;IAChC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,QAAQ,CAAI,KAAQ;IAKlC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AACzC,CAAC;AAkBD,MAAM,UAAU,OAAO,CACrB,SAAqE,EACrE,OAAgB;IAKhB,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QAClC,IAAI,OAAO,OAAO,KAAK,QAAQ;YAC7B,MAAM,IAAI,SAAS,CAAC,6CAA6C,CAAC,CAAC;QACrE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IACzF,CAAC;IACD,IAAI,SAAS,KAAK,IAAI,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,QAAQ,KAAK,OAAO;QACvF,MAAM,IAAI,SAAS,CAAC,8CAA8C,CAAC,CAAC;IACtE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;AAC5C,CAAC;AAwBD,oGAAoG;AACpG,MAAM,UAAU,iBAAiB,CAI/B,WAAc,EACd,QAGW;IAEX,OAAO,CAAC,UAAU,EAAE,QAAQ,GAAG,6BAA6B,EAAE,EAAE,CAC9D,QAAQ,CAAC,UAAU,EAAE,EAAE,GAAG,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,eAAe,CAC7B,WAAc;IAEd,KAAK,WAAW,CAAC;IACjB,OAAO,QAAQ,CAAC;AAClB,CAAC"}