onshape 0.1.0 → 0.1.1

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.
@@ -47,6 +47,34 @@ class PartStudioManager {
47
47
  rollbackIndex: index,
48
48
  });
49
49
  }
50
+ async validateFeature(documentId, workspaceId, elementId, featureId) {
51
+ const features = await this.getFeatures(documentId, workspaceId, elementId);
52
+ const states = isRecord(features) && isRecord(features.featureStates) ? features.featureStates : {};
53
+ const state = isRecord(states[featureId]) ? states[featureId] : {};
54
+ const status = typeof state.featureStatus === "string" ? state.featureStatus : null;
55
+ if (status === "ERROR") {
56
+ throw new Error(`Feature ${featureId} regenerated with status ERROR.`);
57
+ }
58
+ return { featureId, featureStatus: status };
59
+ }
60
+ async validatePartStudio(documentId, workspaceId, elementId, expectations = {}) {
61
+ const partsRaw = await this.getParts(documentId, workspaceId, elementId);
62
+ const parts = Array.isArray(partsRaw) ? partsRaw : [];
63
+ const massRaw = await this.client.get(`/api/v6/partstudios/d/${documentId}/w/${workspaceId}/e/${elementId}/massproperties`);
64
+ const bodiesRecord = isRecord(massRaw) && isRecord(massRaw.bodies) ? massRaw.bodies : {};
65
+ const bodyCount = Object.keys(bodiesRecord).length;
66
+ if (expectations.parts !== undefined && parts.length !== expectations.parts) {
67
+ throw new Error(`Expected ${expectations.parts} part(s), found ${parts.length}.`);
68
+ }
69
+ if (expectations.bodies !== undefined && bodyCount !== expectations.bodies) {
70
+ throw new Error(`Expected ${expectations.bodies} bod(y/ies), found ${bodyCount}.`);
71
+ }
72
+ return {
73
+ parts: parts.length,
74
+ bodies: bodyCount,
75
+ partIds: parts.map((part) => (isRecord(part) ? part.partId : undefined)).filter(Boolean),
76
+ };
77
+ }
50
78
  }
51
79
  exports.PartStudioManager = PartStudioManager;
52
80
  function loadJson(inline, file) {
@@ -55,3 +83,6 @@ function loadJson(inline, file) {
55
83
  throw new Error("Expected JSON via --json or --json-file");
56
84
  return JSON.parse(raw);
57
85
  }
86
+ function isRecord(value) {
87
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
88
+ }
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.planeId = planeId;
4
+ exports.parsePoint2 = parsePoint2;
5
+ exports.buildCircleSketch = buildCircleSketch;
6
+ exports.buildCircleAxisSketch = buildCircleAxisSketch;
7
+ exports.buildExtrude = buildExtrude;
8
+ exports.buildRevolve = buildRevolve;
9
+ exports.buildBooleanUnion = buildBooleanUnion;
10
+ const INCH_TO_METER = 0.0254;
11
+ const PLANE_IDS = {
12
+ front: "JCC",
13
+ top: "JDC",
14
+ right: "JEC",
15
+ };
16
+ function planeId(name) {
17
+ const value = PLANE_IDS[name.toLowerCase()];
18
+ if (!value)
19
+ throw new Error(`Unknown plane '${name}'. Use Front, Top, or Right.`);
20
+ return value;
21
+ }
22
+ function parsePoint2(value) {
23
+ const parts = value.split(",").map((part) => Number(part.trim()));
24
+ if (parts.length !== 2 || parts.some((part) => !Number.isFinite(part))) {
25
+ throw new Error(`Expected a 2D point as x,y; got '${value}'.`);
26
+ }
27
+ return [parts[0], parts[1]];
28
+ }
29
+ function toMeters(value) {
30
+ return value * INCH_TO_METER;
31
+ }
32
+ function circleEntity(id, center, radius) {
33
+ return {
34
+ btType: "BTMSketchCurve-4",
35
+ entityId: id,
36
+ centerId: `${id}.center`,
37
+ geometry: {
38
+ btType: "BTCurveGeometryCircle-115",
39
+ radius: toMeters(radius),
40
+ xCenter: toMeters(center[0]),
41
+ yCenter: toMeters(center[1]),
42
+ xDir: 1,
43
+ yDir: 0,
44
+ clockwise: false,
45
+ },
46
+ isConstruction: false,
47
+ };
48
+ }
49
+ function lineEntity(id, start, end, isConstruction = false) {
50
+ const x1 = toMeters(start[0]);
51
+ const y1 = toMeters(start[1]);
52
+ const x2 = toMeters(end[0]);
53
+ const y2 = toMeters(end[1]);
54
+ const dx = x2 - x1;
55
+ const dy = y2 - y1;
56
+ const length = Math.hypot(dx, dy);
57
+ if (length === 0)
58
+ throw new Error("Line start and end must be different.");
59
+ return {
60
+ btType: "BTMSketchCurveSegment-155",
61
+ entityId: id,
62
+ startPointId: `${id}.start`,
63
+ endPointId: `${id}.end`,
64
+ startParam: 0,
65
+ endParam: length,
66
+ geometry: {
67
+ btType: "BTCurveGeometryLine-117",
68
+ pntX: x1,
69
+ pntY: y1,
70
+ dirX: dx / length,
71
+ dirY: dy / length,
72
+ },
73
+ isConstruction,
74
+ };
75
+ }
76
+ function sketch(name, sketchPlaneId, entities) {
77
+ return {
78
+ feature: {
79
+ btType: "BTMSketch-151",
80
+ featureType: "newSketch",
81
+ name,
82
+ suppressed: false,
83
+ parameters: [
84
+ {
85
+ btType: "BTMParameterQueryList-148",
86
+ queries: [{ btType: "BTMIndividualQuery-138", deterministicIds: [sketchPlaneId] }],
87
+ parameterId: "sketchPlane",
88
+ },
89
+ ],
90
+ entities,
91
+ constraints: [],
92
+ },
93
+ };
94
+ }
95
+ function pEnum(parameterId, enumName, value) {
96
+ return {
97
+ btType: "BTMParameterEnum-145",
98
+ namespace: "",
99
+ enumName,
100
+ value,
101
+ parameterId,
102
+ parameterName: "",
103
+ libraryRelationType: "NONE",
104
+ };
105
+ }
106
+ function pBool(parameterId, value) {
107
+ return {
108
+ btType: "BTMParameterBoolean-144",
109
+ value,
110
+ parameterId,
111
+ parameterName: "",
112
+ libraryRelationType: "NONE",
113
+ };
114
+ }
115
+ function pQuantity(parameterId, value, units) {
116
+ return {
117
+ btType: "BTMParameterQuantity-147",
118
+ isInteger: false,
119
+ value,
120
+ units: "",
121
+ expression: `${value} ${units}`,
122
+ parameterId,
123
+ parameterName: "",
124
+ libraryRelationType: "NONE",
125
+ };
126
+ }
127
+ function pQuery(parameterId, queryString, featureId) {
128
+ return {
129
+ btType: "BTMParameterQueryList-148",
130
+ queries: [
131
+ {
132
+ btType: "BTMIndividualQuery-138",
133
+ queryStatement: null,
134
+ queryString,
135
+ ...(featureId ? { featureId } : {}),
136
+ deterministicIds: [],
137
+ },
138
+ ],
139
+ parameterId,
140
+ parameterName: "",
141
+ libraryRelationType: "NONE",
142
+ };
143
+ }
144
+ function sketchRegion(parameterId, sketchFeatureId) {
145
+ return {
146
+ btType: "BTMParameterQueryList-148",
147
+ queries: [
148
+ {
149
+ btType: "BTMIndividualSketchRegionQuery-140",
150
+ queryStatement: null,
151
+ filterInnerLoops: true,
152
+ queryString: `query = qSketchRegion(id + "${sketchFeatureId}", true);`,
153
+ featureId: sketchFeatureId,
154
+ deterministicIds: [],
155
+ },
156
+ ],
157
+ parameterId,
158
+ parameterName: "",
159
+ libraryRelationType: "NONE",
160
+ };
161
+ }
162
+ function buildCircleSketch(input) {
163
+ return sketch(input.name, planeId(input.plane), [circleEntity("circle.1", input.center, input.radius)]);
164
+ }
165
+ function buildCircleAxisSketch(input) {
166
+ return sketch(input.name, planeId(input.plane), [
167
+ circleEntity("profile.circle", input.center, input.radius),
168
+ lineEntity("axis.1", input.axisStart, input.axisEnd, true),
169
+ ]);
170
+ }
171
+ function buildExtrude(input) {
172
+ return {
173
+ btType: "BTFeatureDefinitionCall-1406",
174
+ feature: {
175
+ btType: "BTMFeature-134",
176
+ featureType: "extrude",
177
+ name: input.name,
178
+ suppressed: false,
179
+ namespace: "",
180
+ parameters: [
181
+ sketchRegion("entities", input.sketchFeatureId),
182
+ pEnum("operationType", "NewBodyOperationType", input.operationType),
183
+ pQuantity("depth", input.depth, "in"),
184
+ pBool("oppositeDirection", false),
185
+ pBool("defaultScope", true),
186
+ ],
187
+ },
188
+ };
189
+ }
190
+ function buildRevolve(input) {
191
+ return {
192
+ btType: "BTFeatureDefinitionCall-1406",
193
+ feature: {
194
+ btType: "BTMFeature-134",
195
+ featureType: "revolve",
196
+ name: input.name,
197
+ suppressed: false,
198
+ namespace: "",
199
+ parameters: [
200
+ pEnum("bodyType", "ExtendedToolBodyType", "SOLID"),
201
+ pEnum("operationType", "NewBodyOperationType", input.operationType),
202
+ sketchRegion("entities", input.sketchFeatureId),
203
+ pQuery("axis", `query = qConstructionFilter(qCreatedBy(makeId("${input.sketchFeatureId}"), EntityType.EDGE), ConstructionObject.YES);`, input.sketchFeatureId),
204
+ pBool("fullRevolve", false),
205
+ pEnum("endBound", "RevolveBoundingType", "BLIND"),
206
+ pQuantity("angle", input.angle, "deg"),
207
+ pBool("defaultScope", true),
208
+ ],
209
+ },
210
+ };
211
+ }
212
+ function buildBooleanUnion() {
213
+ return {
214
+ btType: "BTFeatureDefinitionCall-1406",
215
+ feature: {
216
+ btType: "BTMFeature-134",
217
+ featureType: "booleanBodies",
218
+ name: "Union bodies",
219
+ suppressed: false,
220
+ namespace: "",
221
+ parameters: [
222
+ pEnum("operationType", "BooleanOperationType", "UNION"),
223
+ pBool("defaultScope", false),
224
+ pQuery("tools", "query = qAllModifiableSolidBodies();"),
225
+ pBool("toolsExplicit", true),
226
+ ],
227
+ },
228
+ };
229
+ }
package/dist/cli.js CHANGED
@@ -41,6 +41,7 @@ const client_1 = require("./api/client");
41
41
  const documents_1 = require("./api/documents");
42
42
  const edges_1 = require("./api/edges");
43
43
  const partstudio_1 = require("./api/partstudio");
44
+ const modeling_1 = require("./builders/modeling");
44
45
  const output_1 = require("./output");
45
46
  async function main(argv) {
46
47
  try {
@@ -104,6 +105,12 @@ async function run(argv) {
104
105
  case "add-feature":
105
106
  case "update-feature":
106
107
  case "rollback":
108
+ case "sketch-circle":
109
+ case "sketch-circle-axis":
110
+ case "extrude":
111
+ case "revolve":
112
+ case "boolean-union":
113
+ case "validate-partstudio":
107
114
  case "get-edges":
108
115
  case "find-circular-edges":
109
116
  case "find-edges-by-feature":
@@ -127,7 +134,7 @@ function parseArgs(argv) {
127
134
  if (eq !== -1) {
128
135
  options[key] = item.slice(eq + 1);
129
136
  }
130
- else if (argv[index + 1] && !argv[index + 1].startsWith("-")) {
137
+ else if (argv[index + 1] && (!argv[index + 1].startsWith("-") || isNegativeValue(argv[index + 1]))) {
131
138
  options[key] = argv[index + 1];
132
139
  index += 1;
133
140
  }
@@ -147,6 +154,9 @@ function parseArgs(argv) {
147
154
  }
148
155
  return { command, positionals, options };
149
156
  }
157
+ function isNegativeValue(value) {
158
+ return /^-\d/.test(value) || /^-\.\d/.test(value);
159
+ }
150
160
  async function handleConfig(parsed) {
151
161
  const action = parsed.positionals[0];
152
162
  const store = new credentials_1.CredentialStore();
@@ -324,7 +334,7 @@ async function handleReadCommand(parsed) {
324
334
  case "add-feature": {
325
335
  const { doc, ws, elem } = dwe(parsed.options);
326
336
  const feature = requiredJson(parsed.options);
327
- (0, output_1.emit)(await withFeatureId(partstudios.addFeature(doc, ws, elem, feature)));
337
+ (0, output_1.emit)(await addFeatureResult(partstudios, doc, ws, elem, feature, !parsed.options.noValidate));
328
338
  return;
329
339
  }
330
340
  case "update-feature": {
@@ -338,6 +348,67 @@ async function handleReadCommand(parsed) {
338
348
  (0, output_1.emit)(await partstudios.rollback(doc, ws, elem, requiredNumberOption(parsed.options, "index")));
339
349
  return;
340
350
  }
351
+ case "sketch-circle": {
352
+ const { doc, ws, elem } = dwe(parsed.options);
353
+ (0, output_1.emit)(await addFeatureResult(partstudios, doc, ws, elem, (0, modeling_1.buildCircleSketch)({
354
+ name: stringOption(parsed.options, "name") ?? "Sketch circle",
355
+ plane: stringOption(parsed.options, "plane") ?? "Front",
356
+ center: parsePointOption(parsed.options, "center"),
357
+ radius: requiredNumberOption(parsed.options, "radius"),
358
+ }), !parsed.options.noValidate));
359
+ return;
360
+ }
361
+ case "sketch-circle-axis": {
362
+ const { doc, ws, elem } = dwe(parsed.options);
363
+ (0, output_1.emit)(await addFeatureResult(partstudios, doc, ws, elem, (0, modeling_1.buildCircleAxisSketch)({
364
+ name: stringOption(parsed.options, "name") ?? "Sketch circle and axis",
365
+ plane: stringOption(parsed.options, "plane") ?? "Front",
366
+ center: parsePointOption(parsed.options, "center"),
367
+ radius: requiredNumberOption(parsed.options, "radius"),
368
+ axisStart: parsePointOption(parsed.options, "axisStart"),
369
+ axisEnd: parsePointOption(parsed.options, "axisEnd"),
370
+ }), !parsed.options.noValidate));
371
+ return;
372
+ }
373
+ case "extrude": {
374
+ const { doc, ws, elem } = dwe(parsed.options);
375
+ (0, output_1.emit)(await addFeatureResult(partstudios, doc, ws, elem, (0, modeling_1.buildExtrude)({
376
+ name: stringOption(parsed.options, "name") ?? "Extrude",
377
+ sketchFeatureId: requiredOption(parsed.options, "sketch"),
378
+ depth: requiredNumberOption(parsed.options, "depth"),
379
+ operationType: stringOption(parsed.options, "op") ?? "NEW",
380
+ }), !parsed.options.noValidate));
381
+ return;
382
+ }
383
+ case "revolve": {
384
+ const { doc, ws, elem } = dwe(parsed.options);
385
+ (0, output_1.emit)(await addFeatureResult(partstudios, doc, ws, elem, (0, modeling_1.buildRevolve)({
386
+ name: stringOption(parsed.options, "name") ?? "Revolve",
387
+ sketchFeatureId: requiredOption(parsed.options, "sketch"),
388
+ angle: numberOption(parsed.options, "angle", 360),
389
+ operationType: stringOption(parsed.options, "op") ?? "NEW",
390
+ }), !parsed.options.noValidate));
391
+ return;
392
+ }
393
+ case "boolean-union": {
394
+ const { doc, ws, elem } = dwe(parsed.options);
395
+ (0, output_1.emit)(await addFeatureResult(partstudios, doc, ws, elem, (0, modeling_1.buildBooleanUnion)(), !parsed.options.noValidate));
396
+ return;
397
+ }
398
+ case "validate-partstudio": {
399
+ const { doc, ws, elem } = dwe(parsed.options);
400
+ try {
401
+ (0, output_1.emit)(await partstudios.validatePartStudio(doc, ws, elem, {
402
+ parts: optionalNumberOption(parsed.options, "expectParts"),
403
+ bodies: optionalNumberOption(parsed.options, "expectBodies"),
404
+ }));
405
+ }
406
+ catch (error) {
407
+ const message = error instanceof Error ? error.message : String(error);
408
+ throw new output_1.CliError(message, null, 1);
409
+ }
410
+ return;
411
+ }
341
412
  case "get-edges": {
342
413
  const { doc, ws, elem } = dwe(parsed.options);
343
414
  (0, output_1.emit)(await edges.getEdges(doc, ws, elem));
@@ -362,10 +433,20 @@ async function handleReadCommand(parsed) {
362
433
  }
363
434
  }
364
435
  }
365
- async function withFeatureId(responsePromise) {
366
- const response = await responsePromise;
436
+ async function addFeatureResult(partstudios, doc, ws, elem, feature, validate) {
437
+ const response = await partstudios.addFeature(doc, ws, elem, feature);
367
438
  const featureId = isRecord(response) && isRecord(response.feature) ? response.feature.featureId ?? null : null;
368
- return { featureId, response };
439
+ const result = { featureId, response };
440
+ if (validate && typeof featureId === "string") {
441
+ try {
442
+ result.validation = await partstudios.validateFeature(doc, ws, elem, featureId);
443
+ }
444
+ catch (error) {
445
+ const message = error instanceof Error ? error.message : String(error);
446
+ throw new output_1.CliError(message, { featureId, response }, 1);
447
+ }
448
+ }
449
+ return result;
369
450
  }
370
451
  function isRecord(value) {
371
452
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
@@ -419,6 +500,15 @@ function requiredNumberOption(options, key) {
419
500
  function requiredOption(options, key) {
420
501
  return stringOption(options, key) ?? missing(key);
421
502
  }
503
+ function parsePointOption(options, key) {
504
+ try {
505
+ return (0, modeling_1.parsePoint2)(requiredOption(options, key));
506
+ }
507
+ catch (error) {
508
+ const message = error instanceof Error ? error.message : String(error);
509
+ throw new output_1.CliError(message, null, 2);
510
+ }
511
+ }
422
512
  function requiredJson(options) {
423
513
  try {
424
514
  return (0, partstudio_1.loadJson)(stringOption(options, "json"), stringOption(options, "jsonFile"));
@@ -470,6 +560,12 @@ Commands:
470
560
  add-feature
471
561
  update-feature
472
562
  rollback
563
+ sketch-circle
564
+ sketch-circle-axis
565
+ extrude
566
+ revolve
567
+ boolean-union
568
+ validate-partstudio
473
569
  get-edges
474
570
  find-circular-edges
475
571
  find-edges-by-feature
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "onshape",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Node.js CLI for Onshape CAD automation with the same JSON contract as onshape-cli.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",