@tscircuit/props 0.0.698 → 0.0.699

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.
@@ -0,0 +1,79 @@
1
+ import { distance, type Distance } from "lib/common/distance"
2
+ import { expectTypesMatch } from "lib/typecheck"
3
+ import { z } from "zod"
4
+ import { assemblyFastenerProps, type AssemblyFastenerProps } from "./fastener"
5
+
6
+ export type AssemblyBoltHeadType = "socket" | "hex" | "pan" | "countersunk"
7
+
8
+ /** A bolt specified by dimensions, or by one existing assembly CAD source. */
9
+ export interface AssemblyBoltProps extends AssemblyFastenerProps {
10
+ /** Nominal thread diameter in mm or a unit string. Required with length
11
+ * when no model, modelUrl, or cadModel is supplied.
12
+ */
13
+ diameter?: Distance
14
+ /** Shaft length in mm or a unit string, excluding the head (including the
15
+ * head for countersunk bolts). Required with diameter for dimensional bolts.
16
+ */
17
+ length?: Distance
18
+ /** Optional thread pitch in mm or a unit string. No pitch is inferred. */
19
+ threadPitch?: Distance
20
+ /** Head shape for dimensional bolts; defaults to socket. */
21
+ headType?: AssemblyBoltHeadType
22
+ }
23
+
24
+ const positiveDistance = distance.pipe(z.number().positive().finite())
25
+
26
+ export const assemblyBoltProps = assemblyFastenerProps
27
+ .and(
28
+ z.object({
29
+ diameter: positiveDistance.optional(),
30
+ length: positiveDistance.optional(),
31
+ threadPitch: positiveDistance.optional(),
32
+ headType: z.enum(["socket", "hex", "pan", "countersunk"]).optional(),
33
+ }),
34
+ )
35
+ .superRefine((bolt, ctx) => {
36
+ const hasModel = [bolt.model, bolt.modelUrl, bolt.cadModel].some(
37
+ (value) => value !== undefined,
38
+ )
39
+ if (hasModel) {
40
+ for (const prop of [
41
+ "diameter",
42
+ "length",
43
+ "threadPitch",
44
+ "headType",
45
+ ] as const) {
46
+ if (bolt[prop] !== undefined) {
47
+ ctx.addIssue({
48
+ code: z.ZodIssueCode.custom,
49
+ path: [prop],
50
+ message:
51
+ "Bolt dimensions and headType are exclusive with CAD sources",
52
+ })
53
+ }
54
+ }
55
+ } else {
56
+ for (const prop of ["diameter", "length"] as const) {
57
+ if (bolt[prop] === undefined) {
58
+ ctx.addIssue({
59
+ code: z.ZodIssueCode.custom,
60
+ path: [prop],
61
+ message: "Provide diameter and length, or an assembly CAD source",
62
+ })
63
+ }
64
+ }
65
+ }
66
+ })
67
+ .transform((bolt) =>
68
+ bolt.diameter === undefined
69
+ ? bolt
70
+ : { ...bolt, headType: bolt.headType ?? ("socket" as const) },
71
+ )
72
+
73
+ export type AssemblyBoltPropsInput = {
74
+ [K in keyof z.input<typeof assemblyBoltProps>]: z.input<
75
+ typeof assemblyBoltProps
76
+ >[K]
77
+ }
78
+
79
+ expectTypesMatch<AssemblyBoltProps, AssemblyBoltPropsInput>(true)
@@ -0,0 +1,78 @@
1
+ import { distance, type Distance } from "lib/common/distance"
2
+ import { expectTypesMatch } from "lib/typecheck"
3
+ import { z } from "zod"
4
+ import { assemblyPartProps, type AssemblyPartProps } from "./part"
5
+
6
+ /** An assembly part that fastens to its mounting target, even without geometry. */
7
+ export interface AssemblyFastenerProps extends AssemblyPartProps {
8
+ /** Qualified reference surface, e.g. "FRAME.boltHole". Implies fastening.
9
+ * Without mountFace, use the fastener's authored local mounting origin.
10
+ */
11
+ mountedTo?: string
12
+ /** Common fastener vocabulary for mountedTo; parsed output uses mountedTo.
13
+ * Both may be supplied only when their trimmed values agree.
14
+ */
15
+ fastenedTo?: string
16
+ /** Optional named mating face on this fastener; requires a mounting target.
17
+ * When supplied, outward normals oppose and in-plane X directions align.
18
+ */
19
+ mountFace?: string
20
+ /** Nonnegative surface clearance in mm or a unit string; defaults to zero
21
+ * in the consumer. Requires a mounting target; omitted values stay omitted.
22
+ */
23
+ mountGap?: Distance
24
+ }
25
+
26
+ const mountingTarget = z
27
+ .string()
28
+ .trim()
29
+ .regex(
30
+ /^[A-Za-z_][A-Za-z0-9_-]*(?:\.[A-Za-z_][A-Za-z0-9_-]*)+$/,
31
+ "Expected a qualified reference surface, e.g. part.face",
32
+ )
33
+
34
+ export const assemblyFastenerProps = assemblyPartProps
35
+ .and(
36
+ z.object({
37
+ mountedTo: mountingTarget.optional(),
38
+ fastenedTo: mountingTarget.optional(),
39
+ mountFace: z.string().trim().min(1).optional(),
40
+ mountGap: distance.pipe(z.number().nonnegative().finite()).optional(),
41
+ }),
42
+ )
43
+ .superRefine((fastener, ctx) => {
44
+ if (
45
+ fastener.mountedTo !== undefined &&
46
+ fastener.fastenedTo !== undefined &&
47
+ fastener.mountedTo !== fastener.fastenedTo
48
+ ) {
49
+ ctx.addIssue({
50
+ code: z.ZodIssueCode.custom,
51
+ path: ["fastenedTo"],
52
+ message: "mountedTo and fastenedTo must refer to the same surface",
53
+ })
54
+ }
55
+ if (fastener.mountedTo === undefined && fastener.fastenedTo === undefined) {
56
+ for (const prop of ["mountFace", "mountGap"] as const) {
57
+ if (fastener[prop] !== undefined) {
58
+ ctx.addIssue({
59
+ code: z.ZodIssueCode.custom,
60
+ path: [prop],
61
+ message: `${prop} requires mountedTo or fastenedTo`,
62
+ })
63
+ }
64
+ }
65
+ }
66
+ })
67
+ .transform(({ fastenedTo, ...fastener }) => {
68
+ const mountedTo = fastener.mountedTo ?? fastenedTo
69
+ return mountedTo === undefined ? fastener : { ...fastener, mountedTo }
70
+ })
71
+
72
+ export type AssemblyFastenerPropsInput = {
73
+ [K in keyof z.input<typeof assemblyFastenerProps>]: z.input<
74
+ typeof assemblyFastenerProps
75
+ >[K]
76
+ }
77
+
78
+ expectTypesMatch<AssemblyFastenerProps, AssemblyFastenerPropsInput>(true)
@@ -1,5 +1,7 @@
1
1
  import { assemblyCadAssemblyProps } from "./cadassembly"
2
2
  import { assemblyCableProps } from "./cable"
3
+ import { assemblyBoltProps } from "./bolt"
4
+ import { assemblyFastenerProps } from "./fastener"
3
5
  import { assemblyDeviceProps } from "./device"
4
6
  import { assemblyPartProps } from "./part"
5
7
  import { assemblyReferenceSurfaceProps } from "./referencesurface"
@@ -9,6 +11,8 @@ import { assemblyScreenProps } from "./screen"
9
11
  import { assemblySubassemblyProps } from "./subassembly"
10
12
 
11
13
  export * from "./board-mounting"
14
+ export * from "./bolt"
15
+ export * from "./fastener"
12
16
  export * from "./cable"
13
17
  export * from "./device"
14
18
  export * from "./motor"
@@ -20,6 +24,8 @@ export * from "./subassembly"
20
24
  export * from "./cadassembly"
21
25
 
22
26
  export const assemblyProps = {
27
+ bolt: assemblyBoltProps,
28
+ fastener: assemblyFastenerProps,
23
29
  cable: assemblyCableProps,
24
30
  device: assemblyDeviceProps,
25
31
  motor: assemblyMotorProps,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/props",
3
- "version": "0.0.698",
3
+ "version": "0.0.699",
4
4
  "description": "Props for tscircuit builtin component types",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",