@wandelbots/nova-js 3.9.0-pr.280.2a86180 → 3.9.0-pr.280.2b62f3c

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.
@@ -1,43 +1,12 @@
1
- /** biome-ignore-all lint/suspicious/noExplicitAny: legacy code */
2
- /** biome-ignore-all lint/style/noNonNullAssertion: legacy code */
3
1
  import type {
4
2
  BaseAPI,
5
3
  Configuration as BaseConfiguration,
6
4
  } from "@wandelbots/nova-api/v2"
7
- import {
8
- ApplicationApi,
9
- BUSInputsOutputsApi,
10
- CellApi,
11
- ControllerApi,
12
- ControllerInputsOutputsApi,
13
- JoggingApi,
14
- KinematicsApi,
15
- LicenseApi,
16
- MotionGroupApi,
17
- MotionGroupModelsApi,
18
- NOVACloudApi,
19
- ProgramApi,
20
- RobotConfigurationsApi,
21
- StoreCollisionComponentsApi,
22
- StoreCollisionSetupsApi,
23
- StoreObjectApi,
24
- SystemApi,
25
- TrajectoryCachingApi,
26
- TrajectoryExecutionApi,
27
- TrajectoryPlanningApi,
28
- VersionApi,
29
- VirtualControllerApi,
30
- VirtualControllerBehaviorApi,
31
- VirtualControllerInputsOutputsApi,
32
- } from "@wandelbots/nova-api/v2"
5
+ import * as novaApiV2 from "@wandelbots/nova-api/v2"
33
6
  import type { AxiosInstance } from "axios"
34
7
  import axios from "axios"
35
8
 
36
- type OmitFirstArg<F> = F extends (x: any, ...args: infer P) => infer R
37
- ? (...args: P) => R
38
- : never
39
-
40
- type UnwrapAxiosResponseReturn<T> = T extends (...a: any) => any
9
+ type UnwrapAxiosResponseReturn<T> = T extends (...a: unknown[]) => unknown[]
41
10
  ? (
42
11
  ...a: Parameters<T>
43
12
  ) => Promise<Awaited<ReturnType<T>> extends { data: infer D } ? D : never>
@@ -48,107 +17,142 @@ type WithUnwrappedAxiosResponse<T> = {
48
17
  }
49
18
 
50
19
  /**
51
- * API client providing type-safe access to all the endpoints of a NOVA
52
- * instance.
20
+ * Filters nova-api/v2 exports to just the API class constructors,
21
+ * excluding helper factories and param creators.
53
22
  */
54
- export class NovaAPIClient {
55
- constructor(
56
- readonly cellId: string,
57
- readonly opts: BaseConfiguration & {
58
- axiosInstance?: AxiosInstance
59
- mock?: boolean
60
- },
61
- ) {}
62
-
63
- private withUnwrappedResponsesOnly<T extends BaseAPI>(
64
- ApiConstructor: new (
65
- config: BaseConfiguration,
66
- basePath: string,
67
- axios: AxiosInstance,
68
- ) => T,
69
- ) {
70
- const apiClient = new ApiConstructor(
71
- {
72
- ...this.opts,
73
- isJsonMime: (mime: string) => {
74
- return mime === "application/json"
75
- },
76
- },
77
- this.opts.basePath ?? "",
78
- this.opts.axiosInstance ?? axios.create(),
79
- ) as {
80
- [key: string | symbol]: any
81
- }
82
-
83
- for (const key of Reflect.ownKeys(Reflect.getPrototypeOf(apiClient)!)) {
84
- if (key !== "constructor" && typeof apiClient[key] === "function") {
85
- const originalFunction = apiClient[key]
86
- apiClient[key] = (...args: any[]) => {
87
- return originalFunction
88
- .apply(apiClient, args)
89
- .then((res: any) => res.data)
90
- }
91
- }
92
- }
93
-
94
- return apiClient as WithUnwrappedAxiosResponse<T>
95
- }
96
-
97
- readonly system = this.withUnwrappedResponsesOnly(SystemApi)
98
- readonly cell = this.withUnwrappedResponsesOnly(CellApi)
99
-
100
- readonly motionGroup = this.withUnwrappedResponsesOnly(MotionGroupApi)
101
- readonly motionGroupModels =
102
- this.withUnwrappedResponsesOnly(MotionGroupModelsApi)
103
-
104
- readonly controller = this.withUnwrappedResponsesOnly(ControllerApi)
105
-
106
- readonly controllerIOs = this.withUnwrappedResponsesOnly(
107
- ControllerInputsOutputsApi,
108
- )
109
-
110
- readonly trajectoryPlanning = this.withUnwrappedResponsesOnly(
111
- TrajectoryPlanningApi,
112
- )
113
- readonly trajectoryExecution = this.withUnwrappedResponsesOnly(
114
- TrajectoryExecutionApi,
115
- )
116
- readonly trajectoryCaching =
117
- this.withUnwrappedResponsesOnly(TrajectoryCachingApi)
118
-
119
- readonly application = this.withUnwrappedResponsesOnly(ApplicationApi)
120
- readonly applicationGlobal = this.withUnwrappedResponsesOnly(ApplicationApi)
23
+ type ApiConstructors = {
24
+ [K in keyof typeof novaApiV2 as K extends `${string}Api`
25
+ ? K extends
26
+ | `${string}Factory${string}`
27
+ | `${string}Fp${string}`
28
+ | `${string}ParamCreator${string}`
29
+ ? never
30
+ : K
31
+ : never]: (typeof novaApiV2)[K]
32
+ }
121
33
 
122
- readonly jogging = this.withUnwrappedResponsesOnly(JoggingApi)
34
+ /**
35
+ * Lowercases leading uppercase characters to produce a camelCase property name.
36
+ * e.g. "BUSInputsOutputs" -> "busInputsOutputs", "NOVACloud" -> "novaCloud",
37
+ * "Application" -> "application"
38
+ */
39
+ type CamelCase<S extends string> =
40
+ S extends `${Uppercase<infer A>}${Uppercase<infer B>}${infer Rest}`
41
+ ? Rest extends `${Lowercase<string>}${string}`
42
+ ? `${Lowercase<A>}${CamelCase<`${Uppercase<B>}${Rest}`>}`
43
+ : `${Lowercase<A>}${CamelCase<`${Uppercase<B>}${Rest}`>}`
44
+ : Uncapitalize<S>
123
45
 
124
- readonly kinematics = this.withUnwrappedResponsesOnly(KinematicsApi)
46
+ /**
47
+ * Maps API class names to property names by stripping "Api" and converting
48
+ * the leading acronym to lowercase camelCase.
49
+ *
50
+ * Properties are auto-discovered from the upstream package at runtime,
51
+ * so new API sections are available immediately without code changes.
52
+ */
53
+ type ApiProperties = {
54
+ readonly [K in keyof ApiConstructors as K extends `${infer P}Api`
55
+ ? CamelCase<P>
56
+ : never]: ApiConstructors[K] extends new (
57
+ ...args: unknown[]
58
+ ) => infer I
59
+ ? WithUnwrappedAxiosResponse<I>
60
+ : never
61
+ }
125
62
 
126
- readonly busInputsOutputs =
127
- this.withUnwrappedResponsesOnly(BUSInputsOutputsApi)
63
+ type NovaAPIClientOpts = BaseConfiguration & {
64
+ axiosInstance?: AxiosInstance
65
+ mock?: boolean
66
+ }
128
67
 
129
- readonly virtualController =
130
- this.withUnwrappedResponsesOnly(VirtualControllerApi)
131
- readonly virtualControllerBehavior = this.withUnwrappedResponsesOnly(
132
- VirtualControllerBehaviorApi,
133
- )
134
- readonly virtualControllerIOs = this.withUnwrappedResponsesOnly(
135
- VirtualControllerInputsOutputsApi,
68
+ function isApiClass(
69
+ name: string,
70
+ value: unknown,
71
+ ): value is new (
72
+ config: BaseConfiguration,
73
+ basePath: string,
74
+ axios: AxiosInstance,
75
+ ) => BaseAPI {
76
+ return (
77
+ name.endsWith("Api") &&
78
+ typeof value === "function" &&
79
+ !name.includes("Factory") &&
80
+ !name.includes("Fp") &&
81
+ !name.includes("ParamCreator")
136
82
  )
83
+ }
137
84
 
138
- readonly storeObject = this.withUnwrappedResponsesOnly(StoreObjectApi)
139
- readonly storeCollisionComponents = this.withUnwrappedResponsesOnly(
140
- StoreCollisionComponentsApi,
141
- )
142
- readonly storeCollisionSetups = this.withUnwrappedResponsesOnly(
143
- StoreCollisionSetupsApi,
144
- )
85
+ function wrapApi<T extends BaseAPI>(
86
+ ApiConstructor: new (
87
+ config: BaseConfiguration,
88
+ basePath: string,
89
+ axios: AxiosInstance,
90
+ ) => T,
91
+ opts: NovaAPIClientOpts,
92
+ ): WithUnwrappedAxiosResponse<T> {
93
+ const apiClient = new ApiConstructor(
94
+ {
95
+ ...opts,
96
+ isJsonMime: (mime: string) => mime === "application/json",
97
+ },
98
+ opts.basePath ?? "",
99
+ opts.axiosInstance ?? axios.create(),
100
+ ) as Record<string | symbol, unknown>
101
+
102
+ for (const key of Reflect.ownKeys(
103
+ Reflect.getPrototypeOf(apiClient) as object,
104
+ )) {
105
+ if (key !== "constructor" && typeof apiClient[key] === "function") {
106
+ const originalFunction = apiClient[key] as (
107
+ ...args: unknown[]
108
+ ) => Promise<{ data: unknown }>
109
+ apiClient[key] = (...args: unknown[]) =>
110
+ originalFunction.apply(apiClient, args).then((res) => res.data)
111
+ }
112
+ }
145
113
 
146
- readonly program = this.withUnwrappedResponsesOnly(ProgramApi)
114
+ return apiClient as WithUnwrappedAxiosResponse<T>
115
+ }
147
116
 
148
- readonly license = this.withUnwrappedResponsesOnly(LicenseApi)
149
- readonly novaCloud = this.withUnwrappedResponsesOnly(NOVACloudApi)
150
- readonly robotConfigurations = this.withUnwrappedResponsesOnly(
151
- RobotConfigurationsApi,
152
- )
153
- readonly version = this.withUnwrappedResponsesOnly(VersionApi)
117
+ /**
118
+ * API client providing type-safe access to all the endpoints of a NOVA
119
+ * instance. API sections are auto-discovered from @wandelbots/nova-api/v2
120
+ * at runtime, so new sections added upstream are exposed automatically.
121
+ */
122
+ export const NovaAPIClient = class NovaAPIClient {
123
+ readonly opts: NovaAPIClientOpts
124
+
125
+ constructor(opts: NovaAPIClientOpts) {
126
+ this.opts = opts
127
+
128
+ for (const [name, value] of Object.entries(novaApiV2)) {
129
+ if (isApiClass(name, value)) {
130
+ const stripped = name.slice(0, -3)
131
+ // Lowercase leading acronym run: "BUSInputsOutputs" -> "busInputsOutputs"
132
+ let i = 0
133
+ while (
134
+ i < stripped.length &&
135
+ stripped[i] === stripped[i].toUpperCase() &&
136
+ stripped[i] !== stripped[i].toLowerCase()
137
+ ) {
138
+ i++
139
+ }
140
+ // If multiple uppercase chars, keep the last one uppercase (it starts the next word)
141
+ const boundary = i > 1 ? i - 1 : i
142
+ const propName =
143
+ stripped.slice(0, boundary).toLowerCase() + stripped.slice(boundary)
144
+ // biome-ignore lint/suspicious/noExplicitAny: dynamically assigning discovered API properties
145
+ ;(this as any)[propName] = wrapApi(value, opts)
146
+ }
147
+ }
148
+ }
149
+ } as {
150
+ new (
151
+ opts: NovaAPIClientOpts,
152
+ ): ApiProperties & {
153
+ readonly cellId: string
154
+ readonly opts: NovaAPIClientOpts
155
+ }
154
156
  }
157
+
158
+ export type NovaAPIClient = InstanceType<typeof NovaAPIClient>