@frontera-sdk/blueprint 1.50.82 → 1.50.83
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.
- package/package.json +2 -2
- package/src/action-types.ts +21 -3
- package/src/hooks.ts +30 -7
- package/src/types.ts +94 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontera-sdk/blueprint",
|
|
3
|
-
"version": "1.50.
|
|
3
|
+
"version": "1.50.83",
|
|
4
4
|
"description": "React hooks for reading Blueprint data and invoking governed Actions from inside a Frontera app.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"frontera",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"smoke": "bun run scripts/smoke.ts"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@frontera-sdk/core": "1.50.
|
|
59
|
+
"@frontera-sdk/core": "1.50.83"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"@tanstack/react-query": "^5.90.21",
|
package/src/action-types.ts
CHANGED
|
@@ -158,6 +158,16 @@ export interface ActionRequestRequester {
|
|
|
158
158
|
userId: string | null
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
/** How to read one submitted parameter value: its label, declared kind and whether it is withheld. */
|
|
162
|
+
export interface ActionRequestInputField {
|
|
163
|
+
/** The parameter's own name, for a label. */
|
|
164
|
+
displayName: string
|
|
165
|
+
/** The declared kind (`string`, `number`, `geopoint`, …), or `null` when the parameter is no longer published. */
|
|
166
|
+
kind: string | null
|
|
167
|
+
/** The value is withheld from this reader; `input` holds `"[redacted]"` in its place. */
|
|
168
|
+
hidden: boolean
|
|
169
|
+
}
|
|
170
|
+
|
|
161
171
|
/**
|
|
162
172
|
* What a request is asking for, ready to render.
|
|
163
173
|
*
|
|
@@ -172,15 +182,23 @@ export interface ActionRequestRequester {
|
|
|
172
182
|
* it agree on what to call it.
|
|
173
183
|
*
|
|
174
184
|
* A parameter the Action declares as confidential or restricted arrives as the
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
185
|
+
* placeholder string `'[redacted]'` with `inputFields[key].hidden` true. Decide
|
|
186
|
+
* by `hidden`, never by matching the text: a submitted string can read
|
|
187
|
+
* `'[redacted]'` too. Show it as withheld rather than as an empty value: the
|
|
188
|
+
* parameter WAS supplied, and the approver is simply not the audience for it.
|
|
178
189
|
*/
|
|
179
190
|
export interface ActionRequestIntent {
|
|
180
191
|
actionApiName: string
|
|
181
192
|
actionDisplayName: string
|
|
182
193
|
/** Submitted values, keyed by parameter API name. */
|
|
183
194
|
input: Record<string, unknown>
|
|
195
|
+
/**
|
|
196
|
+
* How to read each `input` entry, under the same key. Format a value by its
|
|
197
|
+
* declared `kind`, never by its shape (a `json` value can look like a
|
|
198
|
+
* location); `hidden` means the value is withheld from this reader. Absent
|
|
199
|
+
* on replies from a platform release that predates it.
|
|
200
|
+
*/
|
|
201
|
+
inputFields?: Record<string, ActionRequestInputField>
|
|
184
202
|
reason: string | null
|
|
185
203
|
correlationId: string | null
|
|
186
204
|
requester: ActionRequestRequester
|
package/src/hooks.ts
CHANGED
|
@@ -2,15 +2,17 @@ import { createContext, useContext } from 'react'
|
|
|
2
2
|
import { useQuery, type UseQueryOptions, type UseQueryResult } from '@tanstack/react-query'
|
|
3
3
|
|
|
4
4
|
import type { BlueprintClient } from './blueprint-client'
|
|
5
|
-
import { objectsOf } from './types'
|
|
5
|
+
import { DISTANCE_ROW_KEY, objectsOf } from './types'
|
|
6
6
|
import type {
|
|
7
7
|
AggregateRequest,
|
|
8
8
|
AggregateResponse,
|
|
9
9
|
BlueprintFilterableProperty,
|
|
10
|
+
BlueprintLocationProperty,
|
|
10
11
|
BlueprintObjectName,
|
|
11
12
|
BlueprintRow,
|
|
12
13
|
BlueprintSortableProperty,
|
|
13
14
|
MetricQueryRequest,
|
|
15
|
+
NearestRequest,
|
|
14
16
|
ObjectInstance,
|
|
15
17
|
QueryRequest,
|
|
16
18
|
QueryResponse,
|
|
@@ -97,6 +99,12 @@ type EffectiveSortable<TLegacy, TObject extends BlueprintObjectName> =
|
|
|
97
99
|
? BlueprintSortableProperty<TObject>
|
|
98
100
|
: Extract<keyof TLegacy, string>
|
|
99
101
|
|
|
102
|
+
/** A location property of the row, or any name for an untyped object type. */
|
|
103
|
+
type EffectiveLocation<TLegacy, TObject extends BlueprintObjectName> =
|
|
104
|
+
string extends EffectiveFilterable<TLegacy, TObject>
|
|
105
|
+
? string
|
|
106
|
+
: BlueprintLocationProperty<EffectiveRow<TLegacy, TObject>>
|
|
107
|
+
|
|
100
108
|
type SelectedRow<TRow, TSelect> =
|
|
101
109
|
undefined extends TSelect
|
|
102
110
|
? TRow
|
|
@@ -104,15 +112,28 @@ type SelectedRow<TRow, TSelect> =
|
|
|
104
112
|
? Pick<TRow, TSelect[number]>
|
|
105
113
|
: TRow
|
|
106
114
|
|
|
115
|
+
/** `nearest` adds the distance to every row, whatever `select` narrowed. */
|
|
116
|
+
type WithDistance<TRow, TNearest> =
|
|
117
|
+
TNearest extends NearestRequest ? TRow & { [DISTANCE_ROW_KEY]: number } : TRow
|
|
118
|
+
|
|
119
|
+
type ObjectsRow<
|
|
120
|
+
TLegacy,
|
|
121
|
+
TObject extends BlueprintObjectName,
|
|
122
|
+
TSelect,
|
|
123
|
+
TNearest,
|
|
124
|
+
> = WithDistance<SelectedRow<EffectiveRow<TLegacy, TObject>, TSelect>, TNearest>
|
|
125
|
+
|
|
107
126
|
type ObjectsOptions<
|
|
108
127
|
TLegacy,
|
|
109
128
|
TObject extends BlueprintObjectName,
|
|
110
129
|
TSelect extends readonly Extract<keyof EffectiveRow<TLegacy, TObject>, string>[] | undefined,
|
|
111
|
-
|
|
112
|
-
|
|
130
|
+
TNearest extends NearestRequest<EffectiveLocation<TLegacy, TObject>> | undefined,
|
|
131
|
+
> = ReadOptions<QueryResponse<ObjectsRow<TLegacy, TObject, TSelect, TNearest>>> &
|
|
132
|
+
Omit<QueryRequest, 'objectSet' | 'select' | 'orderBy' | 'nearest'> & {
|
|
113
133
|
select?: TSelect
|
|
114
134
|
orderBy?: Array<{ property: EffectiveSortable<TLegacy, TObject>; dir: 'asc' | 'desc' }>
|
|
115
135
|
where?: TypedWhereNode<EffectiveRow<TLegacy, TObject>, EffectiveFilterable<TLegacy, TObject>>
|
|
136
|
+
nearest?: TNearest
|
|
116
137
|
}
|
|
117
138
|
|
|
118
139
|
export function useObjects<
|
|
@@ -120,18 +141,20 @@ export function useObjects<
|
|
|
120
141
|
const TObject extends BlueprintObjectName = BlueprintObjectName,
|
|
121
142
|
const TSelect extends readonly Extract<keyof EffectiveRow<TLegacy, TObject>, string>[] | undefined =
|
|
122
143
|
readonly Extract<keyof EffectiveRow<TLegacy, TObject>, string>[] | undefined,
|
|
144
|
+
const TNearest extends NearestRequest<EffectiveLocation<TLegacy, TObject>> | undefined = undefined,
|
|
123
145
|
>(
|
|
124
146
|
objectType: TObject,
|
|
125
|
-
options: ObjectsOptions<NoInfer<TLegacy>, TObject, TSelect> = {},
|
|
126
|
-
): UseQueryResult<QueryResponse<
|
|
127
|
-
const { select, orderBy, pageSize, pageToken, where, ...queryOptions } = options
|
|
128
|
-
return useObjectQuery<
|
|
147
|
+
options: ObjectsOptions<NoInfer<TLegacy>, TObject, TSelect, TNearest> = {},
|
|
148
|
+
): UseQueryResult<QueryResponse<ObjectsRow<TLegacy, TObject, TSelect, TNearest>>, Error> {
|
|
149
|
+
const { select, orderBy, pageSize, pageToken, where, nearest, ...queryOptions } = options
|
|
150
|
+
return useObjectQuery<ObjectsRow<TLegacy, TObject, TSelect, TNearest>>(
|
|
129
151
|
{
|
|
130
152
|
objectSet: objectsOf(objectType, where as WhereNode | undefined),
|
|
131
153
|
select: select ? [...select] : undefined,
|
|
132
154
|
orderBy,
|
|
133
155
|
pageSize,
|
|
134
156
|
pageToken,
|
|
157
|
+
...(nearest ? { nearest } : {}),
|
|
135
158
|
},
|
|
136
159
|
queryOptions,
|
|
137
160
|
)
|
package/src/types.ts
CHANGED
|
@@ -20,6 +20,60 @@ export type ConditionOp =
|
|
|
20
20
|
| 'isNull' | 'isNotNull'
|
|
21
21
|
| 'gt' | 'gte' | 'lt' | 'lte' | 'between'
|
|
22
22
|
| 'dateRange'
|
|
23
|
+
/** Some element of an array property satisfies every `elementWhere` condition. */
|
|
24
|
+
| 'arrayAny'
|
|
25
|
+
/** A location within `radiusMeters` of the point in `value`. */
|
|
26
|
+
| 'nearby'
|
|
27
|
+
/** A location inside the box in `value`. */
|
|
28
|
+
| 'withinBbox'
|
|
29
|
+
|
|
30
|
+
/** The operators a location property takes, and the only ones it takes. */
|
|
31
|
+
export type LocationConditionOp = 'nearby' | 'withinBbox' | 'isNull' | 'isNotNull'
|
|
32
|
+
|
|
33
|
+
/** Every operator except the location-only ones. */
|
|
34
|
+
export type ScalarConditionOp = Exclude<ConditionOp, 'nearby' | 'withinBbox'>
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A WGS 84 location in decimal degrees: `lat` in -90..90, `lon` in -180..180.
|
|
38
|
+
* Always named, never a `[lat, lon]` pair. This is the value a location
|
|
39
|
+
* property reads as, and the centre of a `nearby` condition.
|
|
40
|
+
*/
|
|
41
|
+
export interface BlueprintGeoPoint {
|
|
42
|
+
lat: number
|
|
43
|
+
lon: number
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A latitude/longitude box with inclusive edges. `west > east` means the box
|
|
48
|
+
* crosses the 180° meridian (for example `west: 170, east: -170`). A box that
|
|
49
|
+
* should contain a pole must span every longitude (`west: -180, east: 180`).
|
|
50
|
+
*/
|
|
51
|
+
export interface BlueprintGeoBbox {
|
|
52
|
+
west: number
|
|
53
|
+
south: number
|
|
54
|
+
east: number
|
|
55
|
+
north: number
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** One condition on an element of an array property (`arrayAny`). */
|
|
59
|
+
export interface ArrayElementCondition {
|
|
60
|
+
/** Field of a struct element; omit when the elements are scalars. */
|
|
61
|
+
field?: string
|
|
62
|
+
op: 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte'
|
|
63
|
+
value: unknown
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The row key `nearest` adds: the distance in metres from `nearest.from`.
|
|
68
|
+
* Property names start with a lowercase letter, so it never collides with one.
|
|
69
|
+
*/
|
|
70
|
+
export const DISTANCE_ROW_KEY = '_distanceMeters'
|
|
71
|
+
|
|
72
|
+
/** Order rows by distance from `from`, closest first. */
|
|
73
|
+
export interface NearestRequest<TProperty extends string = string> {
|
|
74
|
+
property: TProperty
|
|
75
|
+
from: BlueprintGeoPoint
|
|
76
|
+
}
|
|
23
77
|
|
|
24
78
|
export type DatePreset =
|
|
25
79
|
| 'TODAY' | 'YESTERDAY' | 'LAST_7_DAYS' | 'LAST_30_DAYS' | 'LAST_90_DAYS'
|
|
@@ -71,20 +125,45 @@ export type BlueprintSortableProperty<TObject extends BlueprintObjectName> =
|
|
|
71
125
|
type PropertyValue<TRow, TProperty extends string> =
|
|
72
126
|
TProperty extends keyof TRow ? TRow[TProperty] : unknown
|
|
73
127
|
|
|
128
|
+
/**
|
|
129
|
+
* The properties of a row that read as a location. Derived from the row type
|
|
130
|
+
* (a generated row types a location as `BlueprintGeoPoint | null`), so it needs
|
|
131
|
+
* nothing from the registry.
|
|
132
|
+
*/
|
|
133
|
+
export type BlueprintLocationProperty<TRow> = {
|
|
134
|
+
[K in Extract<keyof TRow, string>]-?: [NonNullable<TRow[K]>] extends [never]
|
|
135
|
+
? never
|
|
136
|
+
: NonNullable<TRow[K]> extends BlueprintGeoPoint ? K : never
|
|
137
|
+
}[Extract<keyof TRow, string>]
|
|
138
|
+
|
|
74
139
|
export type TypedPropertyCondition<TRow, TProperty extends string> =
|
|
75
140
|
TProperty extends unknown
|
|
76
141
|
? {
|
|
77
142
|
property: TProperty
|
|
78
|
-
|
|
143
|
+
// An untyped (string-keyed) object type takes every operator; a
|
|
144
|
+
// registered one keeps the location operators for its locations.
|
|
145
|
+
op: string extends TProperty ? ConditionOp : ScalarConditionOp
|
|
79
146
|
value?: PropertyValue<TRow, TProperty>
|
|
80
147
|
values?: Array<PropertyValue<TRow, TProperty>>
|
|
81
148
|
preset?: DatePreset
|
|
82
149
|
timezone?: string
|
|
150
|
+
elementWhere?: ArrayElementCondition[]
|
|
151
|
+
radiusMeters?: number
|
|
83
152
|
}
|
|
84
153
|
: never
|
|
85
154
|
|
|
155
|
+
/** A condition on a location property: geo operators and presence only. */
|
|
156
|
+
export type TypedLocationCondition<TProperty extends string> =
|
|
157
|
+
TProperty extends unknown
|
|
158
|
+
?
|
|
159
|
+
| { property: TProperty; op: 'nearby'; value: BlueprintGeoPoint; radiusMeters: number }
|
|
160
|
+
| { property: TProperty; op: 'withinBbox'; value: BlueprintGeoBbox }
|
|
161
|
+
| { property: TProperty; op: 'isNull' | 'isNotNull' }
|
|
162
|
+
: never
|
|
163
|
+
|
|
86
164
|
export type TypedWhereNode<TRow, TProperty extends string> =
|
|
87
165
|
| TypedPropertyCondition<TRow, TProperty>
|
|
166
|
+
| TypedLocationCondition<BlueprintLocationProperty<TRow>>
|
|
88
167
|
| { and: Array<TypedWhereNode<TRow, TProperty>> }
|
|
89
168
|
| { or: Array<TypedWhereNode<TRow, TProperty>> }
|
|
90
169
|
| { not: TypedWhereNode<TRow, TProperty> }
|
|
@@ -102,6 +181,13 @@ export interface PropertyCondition {
|
|
|
102
181
|
/** `dateRange` only — resolved to [start, end) server-side. */
|
|
103
182
|
preset?: DatePreset
|
|
104
183
|
timezone?: string
|
|
184
|
+
/** `arrayAny` only — conditions one element must satisfy together. */
|
|
185
|
+
elementWhere?: ArrayElementCondition[]
|
|
186
|
+
/**
|
|
187
|
+
* `nearby` only — the radius in metres around the point in `value`, measured
|
|
188
|
+
* over the Earth's surface; objects exactly on the circle match.
|
|
189
|
+
*/
|
|
190
|
+
radiusMeters?: number
|
|
105
191
|
}
|
|
106
192
|
|
|
107
193
|
export type WhereNode =
|
|
@@ -139,6 +225,13 @@ export interface QueryRequest {
|
|
|
139
225
|
* the types sent a parameter guaranteed to fail.
|
|
140
226
|
*/
|
|
141
227
|
pageToken?: string
|
|
228
|
+
/**
|
|
229
|
+
* Order by distance from `nearest.from` to the location `nearest.property`,
|
|
230
|
+
* closest first, and add `_distanceMeters` to every row. The object set must
|
|
231
|
+
* also filter that location with `nearby` or `withinBbox` (outside any `or`
|
|
232
|
+
* or `not`), and `orderBy` must be omitted.
|
|
233
|
+
*/
|
|
234
|
+
nearest?: NearestRequest
|
|
142
235
|
}
|
|
143
236
|
|
|
144
237
|
/**
|