| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.planeId = planeId; | ||
| exports.parsePoint2 = parsePoint2; | ||
| exports.buildCircleSketch = buildCircleSketch; | ||
| exports.buildCircleAxisSketch = buildCircleAxisSketch; | ||
| exports.buildExtrude = buildExtrude; | ||
| exports.buildRevolve = buildRevolve; | ||
| exports.buildBooleanUnion = buildBooleanUnion; | ||
| const INCH_TO_METER = 0.0254; | ||
| const PLANE_IDS = { | ||
| front: "JCC", | ||
| top: "JDC", | ||
| right: "JEC", | ||
| }; | ||
| function planeId(name) { | ||
| const value = PLANE_IDS[name.toLowerCase()]; | ||
| if (!value) | ||
| throw new Error(`Unknown plane '${name}'. Use Front, Top, or Right.`); | ||
| return value; | ||
| } | ||
| function parsePoint2(value) { | ||
| const parts = value.split(",").map((part) => Number(part.trim())); | ||
| if (parts.length !== 2 || parts.some((part) => !Number.isFinite(part))) { | ||
| throw new Error(`Expected a 2D point as x,y; got '${value}'.`); | ||
| } | ||
| return [parts[0], parts[1]]; | ||
| } | ||
| function toMeters(value) { | ||
| return value * INCH_TO_METER; | ||
| } | ||
| function circleEntity(id, center, radius) { | ||
| return { | ||
| btType: "BTMSketchCurve-4", | ||
| entityId: id, | ||
| centerId: `${id}.center`, | ||
| geometry: { | ||
| btType: "BTCurveGeometryCircle-115", | ||
| radius: toMeters(radius), | ||
| xCenter: toMeters(center[0]), | ||
| yCenter: toMeters(center[1]), | ||
| xDir: 1, | ||
| yDir: 0, | ||
| clockwise: false, | ||
| }, | ||
| isConstruction: false, | ||
| }; | ||
| } | ||
| function lineEntity(id, start, end, isConstruction = false) { | ||
| const x1 = toMeters(start[0]); | ||
| const y1 = toMeters(start[1]); | ||
| const x2 = toMeters(end[0]); | ||
| const y2 = toMeters(end[1]); | ||
| const dx = x2 - x1; | ||
| const dy = y2 - y1; | ||
| const length = Math.hypot(dx, dy); | ||
| if (length === 0) | ||
| throw new Error("Line start and end must be different."); | ||
| return { | ||
| btType: "BTMSketchCurveSegment-155", | ||
| entityId: id, | ||
| startPointId: `${id}.start`, | ||
| endPointId: `${id}.end`, | ||
| startParam: 0, | ||
| endParam: length, | ||
| geometry: { | ||
| btType: "BTCurveGeometryLine-117", | ||
| pntX: x1, | ||
| pntY: y1, | ||
| dirX: dx / length, | ||
| dirY: dy / length, | ||
| }, | ||
| isConstruction, | ||
| }; | ||
| } | ||
| function sketch(name, sketchPlaneId, entities) { | ||
| return { | ||
| feature: { | ||
| btType: "BTMSketch-151", | ||
| featureType: "newSketch", | ||
| name, | ||
| suppressed: false, | ||
| parameters: [ | ||
| { | ||
| btType: "BTMParameterQueryList-148", | ||
| queries: [{ btType: "BTMIndividualQuery-138", deterministicIds: [sketchPlaneId] }], | ||
| parameterId: "sketchPlane", | ||
| }, | ||
| ], | ||
| entities, | ||
| constraints: [], | ||
| }, | ||
| }; | ||
| } | ||
| function pEnum(parameterId, enumName, value) { | ||
| return { | ||
| btType: "BTMParameterEnum-145", | ||
| namespace: "", | ||
| enumName, | ||
| value, | ||
| parameterId, | ||
| parameterName: "", | ||
| libraryRelationType: "NONE", | ||
| }; | ||
| } | ||
| function pBool(parameterId, value) { | ||
| return { | ||
| btType: "BTMParameterBoolean-144", | ||
| value, | ||
| parameterId, | ||
| parameterName: "", | ||
| libraryRelationType: "NONE", | ||
| }; | ||
| } | ||
| function pQuantity(parameterId, value, units) { | ||
| return { | ||
| btType: "BTMParameterQuantity-147", | ||
| isInteger: false, | ||
| value, | ||
| units: "", | ||
| expression: `${value} ${units}`, | ||
| parameterId, | ||
| parameterName: "", | ||
| libraryRelationType: "NONE", | ||
| }; | ||
| } | ||
| function pQuery(parameterId, queryString, featureId) { | ||
| return { | ||
| btType: "BTMParameterQueryList-148", | ||
| queries: [ | ||
| { | ||
| btType: "BTMIndividualQuery-138", | ||
| queryStatement: null, | ||
| queryString, | ||
| ...(featureId ? { featureId } : {}), | ||
| deterministicIds: [], | ||
| }, | ||
| ], | ||
| parameterId, | ||
| parameterName: "", | ||
| libraryRelationType: "NONE", | ||
| }; | ||
| } | ||
| function sketchRegion(parameterId, sketchFeatureId) { | ||
| return { | ||
| btType: "BTMParameterQueryList-148", | ||
| queries: [ | ||
| { | ||
| btType: "BTMIndividualSketchRegionQuery-140", | ||
| queryStatement: null, | ||
| filterInnerLoops: true, | ||
| queryString: `query = qSketchRegion(id + "${sketchFeatureId}", true);`, | ||
| featureId: sketchFeatureId, | ||
| deterministicIds: [], | ||
| }, | ||
| ], | ||
| parameterId, | ||
| parameterName: "", | ||
| libraryRelationType: "NONE", | ||
| }; | ||
| } | ||
| function buildCircleSketch(input) { | ||
| return sketch(input.name, planeId(input.plane), [circleEntity("circle.1", input.center, input.radius)]); | ||
| } | ||
| function buildCircleAxisSketch(input) { | ||
| return sketch(input.name, planeId(input.plane), [ | ||
| circleEntity("profile.circle", input.center, input.radius), | ||
| lineEntity("axis.1", input.axisStart, input.axisEnd, true), | ||
| ]); | ||
| } | ||
| function buildExtrude(input) { | ||
| return { | ||
| btType: "BTFeatureDefinitionCall-1406", | ||
| feature: { | ||
| btType: "BTMFeature-134", | ||
| featureType: "extrude", | ||
| name: input.name, | ||
| suppressed: false, | ||
| namespace: "", | ||
| parameters: [ | ||
| sketchRegion("entities", input.sketchFeatureId), | ||
| pEnum("operationType", "NewBodyOperationType", input.operationType), | ||
| pQuantity("depth", input.depth, "in"), | ||
| pBool("oppositeDirection", false), | ||
| pBool("defaultScope", true), | ||
| ], | ||
| }, | ||
| }; | ||
| } | ||
| function buildRevolve(input) { | ||
| return { | ||
| btType: "BTFeatureDefinitionCall-1406", | ||
| feature: { | ||
| btType: "BTMFeature-134", | ||
| featureType: "revolve", | ||
| name: input.name, | ||
| suppressed: false, | ||
| namespace: "", | ||
| parameters: [ | ||
| pEnum("bodyType", "ExtendedToolBodyType", "SOLID"), | ||
| pEnum("operationType", "NewBodyOperationType", input.operationType), | ||
| sketchRegion("entities", input.sketchFeatureId), | ||
| pQuery("axis", `query = qConstructionFilter(qCreatedBy(makeId("${input.sketchFeatureId}"), EntityType.EDGE), ConstructionObject.YES);`, input.sketchFeatureId), | ||
| pBool("fullRevolve", false), | ||
| pEnum("endBound", "RevolveBoundingType", "BLIND"), | ||
| pQuantity("angle", input.angle, "deg"), | ||
| pBool("defaultScope", true), | ||
| ], | ||
| }, | ||
| }; | ||
| } | ||
| function buildBooleanUnion() { | ||
| return { | ||
| btType: "BTFeatureDefinitionCall-1406", | ||
| feature: { | ||
| btType: "BTMFeature-134", | ||
| featureType: "booleanBodies", | ||
| name: "Union bodies", | ||
| suppressed: false, | ||
| namespace: "", | ||
| parameters: [ | ||
| pEnum("operationType", "BooleanOperationType", "UNION"), | ||
| pBool("defaultScope", false), | ||
| pQuery("tools", "query = qAllModifiableSolidBodies();"), | ||
| pBool("toolsExplicit", true), | ||
| ], | ||
| }, | ||
| }; | ||
| } |
@@ -50,2 +50,30 @@ "use strict"; | ||
| } | ||
| async validateFeature(documentId, workspaceId, elementId, featureId) { | ||
| const features = await this.getFeatures(documentId, workspaceId, elementId); | ||
| const states = isRecord(features) && isRecord(features.featureStates) ? features.featureStates : {}; | ||
| const state = isRecord(states[featureId]) ? states[featureId] : {}; | ||
| const status = typeof state.featureStatus === "string" ? state.featureStatus : null; | ||
| if (status === "ERROR") { | ||
| throw new Error(`Feature ${featureId} regenerated with status ERROR.`); | ||
| } | ||
| return { featureId, featureStatus: status }; | ||
| } | ||
| async validatePartStudio(documentId, workspaceId, elementId, expectations = {}) { | ||
| const partsRaw = await this.getParts(documentId, workspaceId, elementId); | ||
| const parts = Array.isArray(partsRaw) ? partsRaw : []; | ||
| const massRaw = await this.client.get(`/api/v6/partstudios/d/${documentId}/w/${workspaceId}/e/${elementId}/massproperties`); | ||
| const bodiesRecord = isRecord(massRaw) && isRecord(massRaw.bodies) ? massRaw.bodies : {}; | ||
| const bodyCount = Object.keys(bodiesRecord).length; | ||
| if (expectations.parts !== undefined && parts.length !== expectations.parts) { | ||
| throw new Error(`Expected ${expectations.parts} part(s), found ${parts.length}.`); | ||
| } | ||
| if (expectations.bodies !== undefined && bodyCount !== expectations.bodies) { | ||
| throw new Error(`Expected ${expectations.bodies} bod(y/ies), found ${bodyCount}.`); | ||
| } | ||
| return { | ||
| parts: parts.length, | ||
| bodies: bodyCount, | ||
| partIds: parts.map((part) => (isRecord(part) ? part.partId : undefined)).filter(Boolean), | ||
| }; | ||
| } | ||
| } | ||
@@ -59,1 +87,4 @@ exports.PartStudioManager = PartStudioManager; | ||
| } | ||
| function isRecord(value) { | ||
| return Boolean(value) && typeof value === "object" && !Array.isArray(value); | ||
| } |
+101
-5
@@ -44,2 +44,3 @@ "use strict"; | ||
| const partstudio_1 = require("./api/partstudio"); | ||
| const modeling_1 = require("./builders/modeling"); | ||
| const output_1 = require("./output"); | ||
@@ -108,2 +109,8 @@ async function main(argv) { | ||
| case "rollback": | ||
| case "sketch-circle": | ||
| case "sketch-circle-axis": | ||
| case "extrude": | ||
| case "revolve": | ||
| case "boolean-union": | ||
| case "validate-partstudio": | ||
| case "get-edges": | ||
@@ -132,3 +139,3 @@ case "find-circular-edges": | ||
| } | ||
| else if (argv[index + 1] && !argv[index + 1].startsWith("-")) { | ||
| else if (argv[index + 1] && (!argv[index + 1].startsWith("-") || isNegativeValue(argv[index + 1]))) { | ||
| options[key] = argv[index + 1]; | ||
@@ -153,2 +160,5 @@ index += 1; | ||
| } | ||
| function isNegativeValue(value) { | ||
| return /^-\d/.test(value) || /^-\.\d/.test(value); | ||
| } | ||
| async function handleConfig(parsed) { | ||
@@ -331,3 +341,3 @@ const action = parsed.positionals[0]; | ||
| const feature = requiredJson(parsed.options); | ||
| (0, output_1.emit)(await withFeatureId(partstudios.addFeature(doc, ws, elem, feature))); | ||
| (0, output_1.emit)(await addFeatureResult(partstudios, doc, ws, elem, feature, !parsed.options.noValidate)); | ||
| return; | ||
@@ -346,2 +356,63 @@ } | ||
| } | ||
| case "sketch-circle": { | ||
| const { doc, ws, elem } = dwe(parsed.options); | ||
| (0, output_1.emit)(await addFeatureResult(partstudios, doc, ws, elem, (0, modeling_1.buildCircleSketch)({ | ||
| name: stringOption(parsed.options, "name") ?? "Sketch circle", | ||
| plane: stringOption(parsed.options, "plane") ?? "Front", | ||
| center: parsePointOption(parsed.options, "center"), | ||
| radius: requiredNumberOption(parsed.options, "radius"), | ||
| }), !parsed.options.noValidate)); | ||
| return; | ||
| } | ||
| case "sketch-circle-axis": { | ||
| const { doc, ws, elem } = dwe(parsed.options); | ||
| (0, output_1.emit)(await addFeatureResult(partstudios, doc, ws, elem, (0, modeling_1.buildCircleAxisSketch)({ | ||
| name: stringOption(parsed.options, "name") ?? "Sketch circle and axis", | ||
| plane: stringOption(parsed.options, "plane") ?? "Front", | ||
| center: parsePointOption(parsed.options, "center"), | ||
| radius: requiredNumberOption(parsed.options, "radius"), | ||
| axisStart: parsePointOption(parsed.options, "axisStart"), | ||
| axisEnd: parsePointOption(parsed.options, "axisEnd"), | ||
| }), !parsed.options.noValidate)); | ||
| return; | ||
| } | ||
| case "extrude": { | ||
| const { doc, ws, elem } = dwe(parsed.options); | ||
| (0, output_1.emit)(await addFeatureResult(partstudios, doc, ws, elem, (0, modeling_1.buildExtrude)({ | ||
| name: stringOption(parsed.options, "name") ?? "Extrude", | ||
| sketchFeatureId: requiredOption(parsed.options, "sketch"), | ||
| depth: requiredNumberOption(parsed.options, "depth"), | ||
| operationType: stringOption(parsed.options, "op") ?? "NEW", | ||
| }), !parsed.options.noValidate)); | ||
| return; | ||
| } | ||
| case "revolve": { | ||
| const { doc, ws, elem } = dwe(parsed.options); | ||
| (0, output_1.emit)(await addFeatureResult(partstudios, doc, ws, elem, (0, modeling_1.buildRevolve)({ | ||
| name: stringOption(parsed.options, "name") ?? "Revolve", | ||
| sketchFeatureId: requiredOption(parsed.options, "sketch"), | ||
| angle: numberOption(parsed.options, "angle", 360), | ||
| operationType: stringOption(parsed.options, "op") ?? "NEW", | ||
| }), !parsed.options.noValidate)); | ||
| return; | ||
| } | ||
| case "boolean-union": { | ||
| const { doc, ws, elem } = dwe(parsed.options); | ||
| (0, output_1.emit)(await addFeatureResult(partstudios, doc, ws, elem, (0, modeling_1.buildBooleanUnion)(), !parsed.options.noValidate)); | ||
| return; | ||
| } | ||
| case "validate-partstudio": { | ||
| const { doc, ws, elem } = dwe(parsed.options); | ||
| try { | ||
| (0, output_1.emit)(await partstudios.validatePartStudio(doc, ws, elem, { | ||
| parts: optionalNumberOption(parsed.options, "expectParts"), | ||
| bodies: optionalNumberOption(parsed.options, "expectBodies"), | ||
| })); | ||
| } | ||
| catch (error) { | ||
| const message = error instanceof Error ? error.message : String(error); | ||
| throw new output_1.CliError(message, null, 1); | ||
| } | ||
| return; | ||
| } | ||
| case "get-edges": { | ||
@@ -371,6 +442,16 @@ const { doc, ws, elem } = dwe(parsed.options); | ||
| } | ||
| async function withFeatureId(responsePromise) { | ||
| const response = await responsePromise; | ||
| async function addFeatureResult(partstudios, doc, ws, elem, feature, validate) { | ||
| const response = await partstudios.addFeature(doc, ws, elem, feature); | ||
| const featureId = isRecord(response) && isRecord(response.feature) ? response.feature.featureId ?? null : null; | ||
| return { featureId, response }; | ||
| const result = { featureId, response }; | ||
| if (validate && typeof featureId === "string") { | ||
| try { | ||
| result.validation = await partstudios.validateFeature(doc, ws, elem, featureId); | ||
| } | ||
| catch (error) { | ||
| const message = error instanceof Error ? error.message : String(error); | ||
| throw new output_1.CliError(message, { featureId, response }, 1); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
@@ -429,2 +510,11 @@ function isRecord(value) { | ||
| } | ||
| function parsePointOption(options, key) { | ||
| try { | ||
| return (0, modeling_1.parsePoint2)(requiredOption(options, key)); | ||
| } | ||
| catch (error) { | ||
| const message = error instanceof Error ? error.message : String(error); | ||
| throw new output_1.CliError(message, null, 2); | ||
| } | ||
| } | ||
| function requiredJson(options) { | ||
@@ -481,2 +571,8 @@ try { | ||
| rollback | ||
| sketch-circle | ||
| sketch-circle-axis | ||
| extrude | ||
| revolve | ||
| boolean-union | ||
| validate-partstudio | ||
| get-edges | ||
@@ -483,0 +579,0 @@ find-circular-edges |
+1
-1
| { | ||
| "name": "onshape", | ||
| "version": "0.1.0", | ||
| "version": "0.1.1", | ||
| "description": "Node.js CLI for Onshape CAD automation with the same JSON contract as onshape-cli.", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
60040
29.25%11
10%1527
30.4%