@nubitio/hydra 0.1.0
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/LICENSE +21 -0
- package/README.md +53 -0
- package/dist/index.cjs +862 -0
- package/dist/index.d.cts +336 -0
- package/dist/index.d.mts +336 -0
- package/dist/index.mjs +826 -0
- package/package.json +56 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { DataRecord, GridData } from "@nubitio/core";
|
|
2
|
+
import { DataRecord as DataRecord$1, Field, ResourceFilterDescriptor, ResourceFilterRule, ResourceLoadOptions, ResourceSchemaResolution, ResourceSortDescriptor, ResourceStore, ResourceStoreFactory, ResourceStoreOptions } from "@nubitio/crud";
|
|
3
|
+
import React, { ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
//#region packages/hydra/HydraRemoteDataSource.d.ts
|
|
6
|
+
type RemoteFilterDescriptor = ResourceFilterDescriptor;
|
|
7
|
+
type RemoteSortDescriptor = ResourceSortDescriptor;
|
|
8
|
+
type RemoteLoadOptions = ResourceLoadOptions;
|
|
9
|
+
type RemoteDataSourceOptions = ResourceStoreOptions;
|
|
10
|
+
declare class HydraRemoteDataSource implements ResourceStore {
|
|
11
|
+
private readonly config;
|
|
12
|
+
private readonly httpClient;
|
|
13
|
+
constructor(config: ResourceStoreOptions);
|
|
14
|
+
makeFilterRules(filterRules: ResourceFilterRule[]): string;
|
|
15
|
+
prepareLoadOptions(loadOptions: ResourceLoadOptions): ResourceLoadOptions;
|
|
16
|
+
load(loadOptions: ResourceLoadOptions): Promise<GridData<DataRecord>>;
|
|
17
|
+
byKey(key: unknown): Promise<DataRecord | null>;
|
|
18
|
+
private fetchAll;
|
|
19
|
+
private fetchById;
|
|
20
|
+
}
|
|
21
|
+
declare const createHydraResourceStore: ResourceStoreFactory;
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region packages/hydra/HydraResourceStoreProvider.d.ts
|
|
24
|
+
interface HydraResourceStoreProviderProps {
|
|
25
|
+
children: React.ReactNode;
|
|
26
|
+
}
|
|
27
|
+
declare function HydraResourceStoreProvider({
|
|
28
|
+
children
|
|
29
|
+
}: HydraResourceStoreProviderProps): React.JSX.Element;
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region packages/hydra/HydraResourceSchemaProvider.d.ts
|
|
32
|
+
interface HydraResourceSchemaProviderProps {
|
|
33
|
+
children: React.ReactNode;
|
|
34
|
+
}
|
|
35
|
+
declare function HydraResourceSchemaProvider({
|
|
36
|
+
children
|
|
37
|
+
}: HydraResourceSchemaProviderProps): React.JSX.Element;
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region packages/hydra/types.d.ts
|
|
40
|
+
/**
|
|
41
|
+
* TypeScript types that model a Hydra JSON-LD API documentation response.
|
|
42
|
+
*
|
|
43
|
+
* These are pure data types — no React, no hooks, no side effects.
|
|
44
|
+
*/
|
|
45
|
+
interface HydraProperty {
|
|
46
|
+
'@id': string;
|
|
47
|
+
'@type': string;
|
|
48
|
+
label: string;
|
|
49
|
+
supportedOperation?: HydraSupportedOperation[];
|
|
50
|
+
/**
|
|
51
|
+
* In real API Platform responses this can be:
|
|
52
|
+
* - a plain string: "xmls:string", "#Company"
|
|
53
|
+
* - an IRI object: { "@id": "http://www.w3.org/2001/XMLSchema#string" }
|
|
54
|
+
* - null or undefined
|
|
55
|
+
* Use normalizeRange() in openApiParser.ts to safely extract a string.
|
|
56
|
+
*/
|
|
57
|
+
range?: unknown;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* UI display hints injected by the backend via `#[ApiProperty(openapiContext: ['x-crud' => [...]])]`.
|
|
61
|
+
* All properties are optional — absent means "use inferred value".
|
|
62
|
+
*/
|
|
63
|
+
interface CrudHints {
|
|
64
|
+
/** Override whether the field is shown in the filter row. */
|
|
65
|
+
filterable?: boolean;
|
|
66
|
+
/** Override whether the field can be sorted in the grid. */
|
|
67
|
+
sortable?: boolean;
|
|
68
|
+
/** When true, the column is hidden from the grid by default. */
|
|
69
|
+
hidden?: boolean;
|
|
70
|
+
/** Column display order. */
|
|
71
|
+
order?: number;
|
|
72
|
+
/** Column width in pixels or as a CSS string. */
|
|
73
|
+
width?: number;
|
|
74
|
+
}
|
|
75
|
+
interface HydraSupportedProperty {
|
|
76
|
+
'@type': 'SupportedProperty';
|
|
77
|
+
property: HydraProperty;
|
|
78
|
+
/**
|
|
79
|
+
* Human-readable translated label provided by the backend (e.g. via API
|
|
80
|
+
* Platform `description` → `title` on the outer SupportedProperty wrapper).
|
|
81
|
+
* This is the field we want for display — e.g. "Nombre" for a Spanish locale.
|
|
82
|
+
* Preferred over the auto-derived capitalised property name when present.
|
|
83
|
+
*
|
|
84
|
+
* Note: the inner `property.label` holds the raw technical property name
|
|
85
|
+
* (e.g. "name") and is NOT translated. Only the outer `title` / `hydra:title`
|
|
86
|
+
* on this wrapper carries the translated human-readable label.
|
|
87
|
+
*/
|
|
88
|
+
title: string;
|
|
89
|
+
/**
|
|
90
|
+
* Alternative JSON-LD key for the same translated title, present in some
|
|
91
|
+
* API Platform response variants.
|
|
92
|
+
*/
|
|
93
|
+
'hydra:title'?: string;
|
|
94
|
+
required?: boolean;
|
|
95
|
+
readable?: boolean;
|
|
96
|
+
writeable?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* UI CRUD hints injected by `TranslatedDocumentationNormalizer` from
|
|
99
|
+
* `#[ApiProperty(openapiContext: ['x-crud' => [...]])]` on the PHP entity.
|
|
100
|
+
*/
|
|
101
|
+
'x-crud'?: CrudHints;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* A single entry from `supportedOperation` on a Hydra class.
|
|
105
|
+
* API Platform serialises the HTTP method as either `hydra:method` or `method`
|
|
106
|
+
* depending on the AP version; we accept both so parsing never silently drops
|
|
107
|
+
* an operation.
|
|
108
|
+
*
|
|
109
|
+
* Note: the outer array key is the compact `supportedOperation` (no `hydra:` prefix)
|
|
110
|
+
* as confirmed from the /api/docs.jsonld wire format.
|
|
111
|
+
*/
|
|
112
|
+
interface HydraSupportedOperation {
|
|
113
|
+
'hydra:method'?: string;
|
|
114
|
+
method?: string;
|
|
115
|
+
}
|
|
116
|
+
interface HydraClass {
|
|
117
|
+
'@id': string;
|
|
118
|
+
'@type': string;
|
|
119
|
+
title: string;
|
|
120
|
+
supportedProperty: HydraSupportedProperty[];
|
|
121
|
+
/**
|
|
122
|
+
* Hydra search template describing server-side filterable query params.
|
|
123
|
+
* Present on collection operations in API Platform JSON-LD responses.
|
|
124
|
+
*/
|
|
125
|
+
'hydra:search'?: HydraSearchTemplate;
|
|
126
|
+
/**
|
|
127
|
+
* Supported HTTP operations for this class, e.g. GET, POST, PUT, PATCH, DELETE.
|
|
128
|
+
* Used to infer canAdd / canEdit / canDelete permissions without manual config.
|
|
129
|
+
*
|
|
130
|
+
* Wire format (confirmed from /api/docs.jsonld): compact key `supportedOperation`
|
|
131
|
+
* (no `hydra:` prefix). API Platform compact context strips the namespace prefix.
|
|
132
|
+
*/
|
|
133
|
+
supportedOperation?: HydraSupportedOperation[];
|
|
134
|
+
}
|
|
135
|
+
/** Raw `hydra:IriTemplate` block found inside `hydra:search`. */
|
|
136
|
+
interface HydraSearchTemplate {
|
|
137
|
+
'hydra:mapping'?: HydraIriTemplateMapping[];
|
|
138
|
+
}
|
|
139
|
+
/** A single entry in `hydra:mapping`. */
|
|
140
|
+
interface HydraIriTemplateMapping {
|
|
141
|
+
/** The resource property this mapping targets, e.g. "name". */
|
|
142
|
+
'hydra:property'?: string;
|
|
143
|
+
/** The URL template variable, e.g. "name" or "order[createdAt]". */
|
|
144
|
+
'hydra:variable'?: string;
|
|
145
|
+
/** Whether this parameter is required. */
|
|
146
|
+
'hydra:required'?: boolean;
|
|
147
|
+
}
|
|
148
|
+
interface HydraApiDoc {
|
|
149
|
+
'@context'?: string;
|
|
150
|
+
'@id': string;
|
|
151
|
+
'@type': 'ApiDocumentation' | 'hydra:ApiDocumentation';
|
|
152
|
+
supportedClass: HydraClass[];
|
|
153
|
+
}
|
|
154
|
+
/** Normalized field descriptor derived from a HydraSupportedProperty */
|
|
155
|
+
interface HydraFieldSchema {
|
|
156
|
+
name: string;
|
|
157
|
+
range?: string;
|
|
158
|
+
propertyType: string;
|
|
159
|
+
required: boolean;
|
|
160
|
+
readable: boolean;
|
|
161
|
+
writeable: boolean;
|
|
162
|
+
/**
|
|
163
|
+
* Human-readable label from `hydra:title` on the property descriptor.
|
|
164
|
+
* When present and non-empty, the mapper uses this instead of the
|
|
165
|
+
* auto-capitalised property name.
|
|
166
|
+
*/
|
|
167
|
+
'hydra:title'?: string;
|
|
168
|
+
/**
|
|
169
|
+
* UI CRUD hints forwarded from `x-crud` on the raw `HydraSupportedProperty`.
|
|
170
|
+
* When present, these values override inferred field properties in the mapper.
|
|
171
|
+
*/
|
|
172
|
+
crudHints?: CrudHints;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* A single entry from a Hydra `hydra:search` / `hydra:mapping` block.
|
|
176
|
+
* Describes a query parameter accepted by the server for filtering.
|
|
177
|
+
*/
|
|
178
|
+
interface HydraSearchMapping {
|
|
179
|
+
/** The resource property name this filter applies to (e.g. "name", "status"). */
|
|
180
|
+
property: string;
|
|
181
|
+
/** The URL query parameter variable (e.g. "name", "order[createdAt]"). */
|
|
182
|
+
variable: string;
|
|
183
|
+
/** Whether the parameter is required by the server. */
|
|
184
|
+
required: boolean;
|
|
185
|
+
}
|
|
186
|
+
/** Normalized resource descriptor derived from a HydraClass */
|
|
187
|
+
interface HydraResourceSchema {
|
|
188
|
+
className: string;
|
|
189
|
+
apiUrl: string;
|
|
190
|
+
fields: HydraFieldSchema[];
|
|
191
|
+
/**
|
|
192
|
+
* Filters discoverable from `hydra:search.hydra:mapping` on the collection
|
|
193
|
+
* operation. Absent/empty means no server-side filter info was found.
|
|
194
|
+
*/
|
|
195
|
+
searchMappings?: HydraSearchMapping[];
|
|
196
|
+
/**
|
|
197
|
+
* HTTP methods supported by this resource class, uppercased.
|
|
198
|
+
* e.g. ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].
|
|
199
|
+
* Derived from `supportedOperation` (compact wire key) in the Hydra API doc.
|
|
200
|
+
* Empty array means no operation info was found (fall back to platform defaults).
|
|
201
|
+
*/
|
|
202
|
+
supportedOperations?: string[];
|
|
203
|
+
}
|
|
204
|
+
interface OpenApiProperty {
|
|
205
|
+
type?: string;
|
|
206
|
+
format?: string;
|
|
207
|
+
readOnly?: boolean;
|
|
208
|
+
writeOnly?: boolean;
|
|
209
|
+
enum?: string[];
|
|
210
|
+
}
|
|
211
|
+
interface OpenApiSchema {
|
|
212
|
+
properties?: Record<string, OpenApiProperty>;
|
|
213
|
+
required?: string[];
|
|
214
|
+
}
|
|
215
|
+
interface OpenApiDoc {
|
|
216
|
+
openapi: string;
|
|
217
|
+
components: {
|
|
218
|
+
schemas: Record<string, OpenApiSchema>;
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
type ApiDoc = {
|
|
222
|
+
format: 'hydra';
|
|
223
|
+
doc: HydraApiDoc;
|
|
224
|
+
} | {
|
|
225
|
+
format: 'openapi';
|
|
226
|
+
doc: OpenApiDoc;
|
|
227
|
+
};
|
|
228
|
+
//#endregion
|
|
229
|
+
//#region packages/hydra/openApiParser.d.ts
|
|
230
|
+
/**
|
|
231
|
+
* Parse a raw `/api/docs.jsonld` Hydra JSON-LD response into a typed
|
|
232
|
+
* Record<className, HydraResourceSchema>.
|
|
233
|
+
*
|
|
234
|
+
* @throws Error if doc is not a valid Hydra ApiDocumentation.
|
|
235
|
+
*/
|
|
236
|
+
declare function parseHydraDoc(doc: HydraApiDoc): Record<string, HydraResourceSchema>;
|
|
237
|
+
/**
|
|
238
|
+
* Parse a raw `/api/docs.json` OpenAPI 3.1 response into the same
|
|
239
|
+
* Record<className, HydraResourceSchema> output shape so that
|
|
240
|
+
* `useResourceSchema` can consume it transparently.
|
|
241
|
+
*/
|
|
242
|
+
declare function parseOpenApiDoc(doc: OpenApiDoc): Record<string, HydraResourceSchema>;
|
|
243
|
+
//#endregion
|
|
244
|
+
//#region packages/hydra/HydraToFieldMapper.d.ts
|
|
245
|
+
/**
|
|
246
|
+
* Determine the FieldType bucket for a given `range` string.
|
|
247
|
+
*
|
|
248
|
+
* Handles both the short-form `xmls:` prefix used by API Platform
|
|
249
|
+
* AND the full XSD IRI form `http://www.w3.org/2001/XMLSchema#...`
|
|
250
|
+
* that can appear when `property.range` is an IRI object `{ "@id": "..." }`
|
|
251
|
+
* and has been normalised to a string by `normalizeRange`.
|
|
252
|
+
*
|
|
253
|
+
* Returns a string tag that the caller can switch on; not the FieldType enum
|
|
254
|
+
* directly (to keep this function pure / dependency-free of FieldBuilders).
|
|
255
|
+
*/
|
|
256
|
+
type RangeTag = 'boolean' | 'dateTime' | 'integer' | 'decimal' | 'entity' | 'text';
|
|
257
|
+
declare function resolveRangeTag(range: string | undefined, propertyType: string): RangeTag;
|
|
258
|
+
/**
|
|
259
|
+
* Map a single HydraResourceSchema to an array of Field objects using the
|
|
260
|
+
* existing field builder utilities.
|
|
261
|
+
*
|
|
262
|
+
* Mapping rules (in priority order):
|
|
263
|
+
* 1. name === 'id' or name === '@id' → always emit as a hidden identity field
|
|
264
|
+
* (visible: false, readonly: true, isIdentity: true)
|
|
265
|
+
* Required so native grids and forms always have a stable row key.
|
|
266
|
+
* 2. readable: false → skip (already filtered upstream, but defensive)
|
|
267
|
+
* 3. writeable: false (display-only) → noneField
|
|
268
|
+
* 4. range resolves to 'boolean' → switchField
|
|
269
|
+
* 5. range resolves to 'dateTime' → datetimeField
|
|
270
|
+
* 6. range resolves to 'integer' → numberField with precision(0)
|
|
271
|
+
* 7. range resolves to 'decimal' → numberField
|
|
272
|
+
* 8. range resolves to 'entity' OR propertyType === 'Link' → entityField
|
|
273
|
+
* 9. range resolves to 'text' OR fallback → textField
|
|
274
|
+
*
|
|
275
|
+
* For each field, `filterable` is `true` when the field's property name appears in
|
|
276
|
+
* `schema.searchMappings` (from `hydra:search`). When `searchMappings` is empty
|
|
277
|
+
* (e.g. the resource uses a catch-all data-grid filter that doesn't
|
|
278
|
+
* enumerate field-level mappings), ALL fields default to `filterable: true`.
|
|
279
|
+
*
|
|
280
|
+
* After processing all schema fields, if no `id` field was found in the schema,
|
|
281
|
+
* a synthetic hidden identity field is injected so the grid always has a key.
|
|
282
|
+
*
|
|
283
|
+
* @param schema - The Hydra resource schema to map.
|
|
284
|
+
* @param urlLookup - Optional lookup function to resolve the API URL for a related
|
|
285
|
+
* entity class. When provided, Rule 8 uses this before falling back to the
|
|
286
|
+
* automatic pluralization heuristic. Example: `(cls) => resourceMap[cls]?.apiUrl`
|
|
287
|
+
*
|
|
288
|
+
* @pure — no React, no hooks, no side effects.
|
|
289
|
+
*/
|
|
290
|
+
declare function mapHydraSchemaToFields(schema: HydraResourceSchema, urlLookup?: (className: string) => string | undefined, schemaLookup?: (className: string) => HydraResourceSchema | undefined): Field[];
|
|
291
|
+
//#endregion
|
|
292
|
+
//#region packages/hydra/useHydraMetadata.d.ts
|
|
293
|
+
/**
|
|
294
|
+
* Stable query key used by useHydraMetadata.
|
|
295
|
+
* Export this so that consumers (e.g. SmartCrudPage retry button) can
|
|
296
|
+
* invalidate exactly the right query without coupling to an internal string.
|
|
297
|
+
*/
|
|
298
|
+
declare const API_DOC_QUERY_KEY: readonly ["api-doc-discovery"];
|
|
299
|
+
interface UseHydraMetadataResult {
|
|
300
|
+
data: ApiDoc | undefined;
|
|
301
|
+
isLoading: boolean;
|
|
302
|
+
error: Error | undefined;
|
|
303
|
+
}
|
|
304
|
+
declare function useHydraMetadata(): UseHydraMetadataResult;
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region packages/hydra/useResourceSchema.d.ts
|
|
307
|
+
type UseResourceSchemaResult = ResourceSchemaResolution;
|
|
308
|
+
declare function useResourceSchema<T extends DataRecord$1 = DataRecord$1>(apiUrl: string): UseResourceSchemaResult;
|
|
309
|
+
//#endregion
|
|
310
|
+
//#region packages/hydra/SchemaContext.d.ts
|
|
311
|
+
interface SchemaProviderProps {
|
|
312
|
+
children: ReactNode;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Wrap your app (or a subtree) with `SchemaProvider` to share a single
|
|
316
|
+
* `/api/docs.jsonld` fetch across all `SmartCrudPage` instances.
|
|
317
|
+
*/
|
|
318
|
+
declare function SchemaProvider({
|
|
319
|
+
children
|
|
320
|
+
}: SchemaProviderProps): React.JSX.Element;
|
|
321
|
+
interface UseSchemaContextResult {
|
|
322
|
+
data: UseHydraMetadataResult['data'];
|
|
323
|
+
isLoading: boolean;
|
|
324
|
+
isError: boolean;
|
|
325
|
+
error: Error | undefined;
|
|
326
|
+
refetch?: () => void;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Returns the shared schema context if inside a `<SchemaProvider>`,
|
|
330
|
+
* otherwise falls back to calling `useHydraMetadata()` directly.
|
|
331
|
+
*
|
|
332
|
+
* This makes `SmartCrudPage` work standalone without requiring a provider.
|
|
333
|
+
*/
|
|
334
|
+
declare function useSchemaContext(): UseSchemaContextResult;
|
|
335
|
+
//#endregion
|
|
336
|
+
export { API_DOC_QUERY_KEY, type ApiDoc, type HydraApiDoc, HydraRemoteDataSource, HydraResourceSchemaProvider, type HydraResourceSchemaProviderProps, HydraResourceStoreProvider, type HydraResourceStoreProviderProps, type OpenApiDoc, type RemoteDataSourceOptions, type RemoteFilterDescriptor, type RemoteLoadOptions, type RemoteSortDescriptor, SchemaProvider, type UseSchemaContextResult, createHydraResourceStore, mapHydraSchemaToFields, parseHydraDoc, parseOpenApiDoc, resolveRangeTag, useHydraMetadata, useResourceSchema, useSchemaContext };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { DataRecord, GridData } from "@nubitio/core";
|
|
2
|
+
import React, { ReactNode } from "react";
|
|
3
|
+
import { DataRecord as DataRecord$1, Field, ResourceFilterDescriptor, ResourceFilterRule, ResourceLoadOptions, ResourceSchemaResolution, ResourceSortDescriptor, ResourceStore, ResourceStoreFactory, ResourceStoreOptions } from "@nubitio/crud";
|
|
4
|
+
|
|
5
|
+
//#region packages/hydra/HydraRemoteDataSource.d.ts
|
|
6
|
+
type RemoteFilterDescriptor = ResourceFilterDescriptor;
|
|
7
|
+
type RemoteSortDescriptor = ResourceSortDescriptor;
|
|
8
|
+
type RemoteLoadOptions = ResourceLoadOptions;
|
|
9
|
+
type RemoteDataSourceOptions = ResourceStoreOptions;
|
|
10
|
+
declare class HydraRemoteDataSource implements ResourceStore {
|
|
11
|
+
private readonly config;
|
|
12
|
+
private readonly httpClient;
|
|
13
|
+
constructor(config: ResourceStoreOptions);
|
|
14
|
+
makeFilterRules(filterRules: ResourceFilterRule[]): string;
|
|
15
|
+
prepareLoadOptions(loadOptions: ResourceLoadOptions): ResourceLoadOptions;
|
|
16
|
+
load(loadOptions: ResourceLoadOptions): Promise<GridData<DataRecord>>;
|
|
17
|
+
byKey(key: unknown): Promise<DataRecord | null>;
|
|
18
|
+
private fetchAll;
|
|
19
|
+
private fetchById;
|
|
20
|
+
}
|
|
21
|
+
declare const createHydraResourceStore: ResourceStoreFactory;
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region packages/hydra/HydraResourceStoreProvider.d.ts
|
|
24
|
+
interface HydraResourceStoreProviderProps {
|
|
25
|
+
children: React.ReactNode;
|
|
26
|
+
}
|
|
27
|
+
declare function HydraResourceStoreProvider({
|
|
28
|
+
children
|
|
29
|
+
}: HydraResourceStoreProviderProps): React.JSX.Element;
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region packages/hydra/HydraResourceSchemaProvider.d.ts
|
|
32
|
+
interface HydraResourceSchemaProviderProps {
|
|
33
|
+
children: React.ReactNode;
|
|
34
|
+
}
|
|
35
|
+
declare function HydraResourceSchemaProvider({
|
|
36
|
+
children
|
|
37
|
+
}: HydraResourceSchemaProviderProps): React.JSX.Element;
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region packages/hydra/types.d.ts
|
|
40
|
+
/**
|
|
41
|
+
* TypeScript types that model a Hydra JSON-LD API documentation response.
|
|
42
|
+
*
|
|
43
|
+
* These are pure data types — no React, no hooks, no side effects.
|
|
44
|
+
*/
|
|
45
|
+
interface HydraProperty {
|
|
46
|
+
'@id': string;
|
|
47
|
+
'@type': string;
|
|
48
|
+
label: string;
|
|
49
|
+
supportedOperation?: HydraSupportedOperation[];
|
|
50
|
+
/**
|
|
51
|
+
* In real API Platform responses this can be:
|
|
52
|
+
* - a plain string: "xmls:string", "#Company"
|
|
53
|
+
* - an IRI object: { "@id": "http://www.w3.org/2001/XMLSchema#string" }
|
|
54
|
+
* - null or undefined
|
|
55
|
+
* Use normalizeRange() in openApiParser.ts to safely extract a string.
|
|
56
|
+
*/
|
|
57
|
+
range?: unknown;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* UI display hints injected by the backend via `#[ApiProperty(openapiContext: ['x-crud' => [...]])]`.
|
|
61
|
+
* All properties are optional — absent means "use inferred value".
|
|
62
|
+
*/
|
|
63
|
+
interface CrudHints {
|
|
64
|
+
/** Override whether the field is shown in the filter row. */
|
|
65
|
+
filterable?: boolean;
|
|
66
|
+
/** Override whether the field can be sorted in the grid. */
|
|
67
|
+
sortable?: boolean;
|
|
68
|
+
/** When true, the column is hidden from the grid by default. */
|
|
69
|
+
hidden?: boolean;
|
|
70
|
+
/** Column display order. */
|
|
71
|
+
order?: number;
|
|
72
|
+
/** Column width in pixels or as a CSS string. */
|
|
73
|
+
width?: number;
|
|
74
|
+
}
|
|
75
|
+
interface HydraSupportedProperty {
|
|
76
|
+
'@type': 'SupportedProperty';
|
|
77
|
+
property: HydraProperty;
|
|
78
|
+
/**
|
|
79
|
+
* Human-readable translated label provided by the backend (e.g. via API
|
|
80
|
+
* Platform `description` → `title` on the outer SupportedProperty wrapper).
|
|
81
|
+
* This is the field we want for display — e.g. "Nombre" for a Spanish locale.
|
|
82
|
+
* Preferred over the auto-derived capitalised property name when present.
|
|
83
|
+
*
|
|
84
|
+
* Note: the inner `property.label` holds the raw technical property name
|
|
85
|
+
* (e.g. "name") and is NOT translated. Only the outer `title` / `hydra:title`
|
|
86
|
+
* on this wrapper carries the translated human-readable label.
|
|
87
|
+
*/
|
|
88
|
+
title: string;
|
|
89
|
+
/**
|
|
90
|
+
* Alternative JSON-LD key for the same translated title, present in some
|
|
91
|
+
* API Platform response variants.
|
|
92
|
+
*/
|
|
93
|
+
'hydra:title'?: string;
|
|
94
|
+
required?: boolean;
|
|
95
|
+
readable?: boolean;
|
|
96
|
+
writeable?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* UI CRUD hints injected by `TranslatedDocumentationNormalizer` from
|
|
99
|
+
* `#[ApiProperty(openapiContext: ['x-crud' => [...]])]` on the PHP entity.
|
|
100
|
+
*/
|
|
101
|
+
'x-crud'?: CrudHints;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* A single entry from `supportedOperation` on a Hydra class.
|
|
105
|
+
* API Platform serialises the HTTP method as either `hydra:method` or `method`
|
|
106
|
+
* depending on the AP version; we accept both so parsing never silently drops
|
|
107
|
+
* an operation.
|
|
108
|
+
*
|
|
109
|
+
* Note: the outer array key is the compact `supportedOperation` (no `hydra:` prefix)
|
|
110
|
+
* as confirmed from the /api/docs.jsonld wire format.
|
|
111
|
+
*/
|
|
112
|
+
interface HydraSupportedOperation {
|
|
113
|
+
'hydra:method'?: string;
|
|
114
|
+
method?: string;
|
|
115
|
+
}
|
|
116
|
+
interface HydraClass {
|
|
117
|
+
'@id': string;
|
|
118
|
+
'@type': string;
|
|
119
|
+
title: string;
|
|
120
|
+
supportedProperty: HydraSupportedProperty[];
|
|
121
|
+
/**
|
|
122
|
+
* Hydra search template describing server-side filterable query params.
|
|
123
|
+
* Present on collection operations in API Platform JSON-LD responses.
|
|
124
|
+
*/
|
|
125
|
+
'hydra:search'?: HydraSearchTemplate;
|
|
126
|
+
/**
|
|
127
|
+
* Supported HTTP operations for this class, e.g. GET, POST, PUT, PATCH, DELETE.
|
|
128
|
+
* Used to infer canAdd / canEdit / canDelete permissions without manual config.
|
|
129
|
+
*
|
|
130
|
+
* Wire format (confirmed from /api/docs.jsonld): compact key `supportedOperation`
|
|
131
|
+
* (no `hydra:` prefix). API Platform compact context strips the namespace prefix.
|
|
132
|
+
*/
|
|
133
|
+
supportedOperation?: HydraSupportedOperation[];
|
|
134
|
+
}
|
|
135
|
+
/** Raw `hydra:IriTemplate` block found inside `hydra:search`. */
|
|
136
|
+
interface HydraSearchTemplate {
|
|
137
|
+
'hydra:mapping'?: HydraIriTemplateMapping[];
|
|
138
|
+
}
|
|
139
|
+
/** A single entry in `hydra:mapping`. */
|
|
140
|
+
interface HydraIriTemplateMapping {
|
|
141
|
+
/** The resource property this mapping targets, e.g. "name". */
|
|
142
|
+
'hydra:property'?: string;
|
|
143
|
+
/** The URL template variable, e.g. "name" or "order[createdAt]". */
|
|
144
|
+
'hydra:variable'?: string;
|
|
145
|
+
/** Whether this parameter is required. */
|
|
146
|
+
'hydra:required'?: boolean;
|
|
147
|
+
}
|
|
148
|
+
interface HydraApiDoc {
|
|
149
|
+
'@context'?: string;
|
|
150
|
+
'@id': string;
|
|
151
|
+
'@type': 'ApiDocumentation' | 'hydra:ApiDocumentation';
|
|
152
|
+
supportedClass: HydraClass[];
|
|
153
|
+
}
|
|
154
|
+
/** Normalized field descriptor derived from a HydraSupportedProperty */
|
|
155
|
+
interface HydraFieldSchema {
|
|
156
|
+
name: string;
|
|
157
|
+
range?: string;
|
|
158
|
+
propertyType: string;
|
|
159
|
+
required: boolean;
|
|
160
|
+
readable: boolean;
|
|
161
|
+
writeable: boolean;
|
|
162
|
+
/**
|
|
163
|
+
* Human-readable label from `hydra:title` on the property descriptor.
|
|
164
|
+
* When present and non-empty, the mapper uses this instead of the
|
|
165
|
+
* auto-capitalised property name.
|
|
166
|
+
*/
|
|
167
|
+
'hydra:title'?: string;
|
|
168
|
+
/**
|
|
169
|
+
* UI CRUD hints forwarded from `x-crud` on the raw `HydraSupportedProperty`.
|
|
170
|
+
* When present, these values override inferred field properties in the mapper.
|
|
171
|
+
*/
|
|
172
|
+
crudHints?: CrudHints;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* A single entry from a Hydra `hydra:search` / `hydra:mapping` block.
|
|
176
|
+
* Describes a query parameter accepted by the server for filtering.
|
|
177
|
+
*/
|
|
178
|
+
interface HydraSearchMapping {
|
|
179
|
+
/** The resource property name this filter applies to (e.g. "name", "status"). */
|
|
180
|
+
property: string;
|
|
181
|
+
/** The URL query parameter variable (e.g. "name", "order[createdAt]"). */
|
|
182
|
+
variable: string;
|
|
183
|
+
/** Whether the parameter is required by the server. */
|
|
184
|
+
required: boolean;
|
|
185
|
+
}
|
|
186
|
+
/** Normalized resource descriptor derived from a HydraClass */
|
|
187
|
+
interface HydraResourceSchema {
|
|
188
|
+
className: string;
|
|
189
|
+
apiUrl: string;
|
|
190
|
+
fields: HydraFieldSchema[];
|
|
191
|
+
/**
|
|
192
|
+
* Filters discoverable from `hydra:search.hydra:mapping` on the collection
|
|
193
|
+
* operation. Absent/empty means no server-side filter info was found.
|
|
194
|
+
*/
|
|
195
|
+
searchMappings?: HydraSearchMapping[];
|
|
196
|
+
/**
|
|
197
|
+
* HTTP methods supported by this resource class, uppercased.
|
|
198
|
+
* e.g. ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].
|
|
199
|
+
* Derived from `supportedOperation` (compact wire key) in the Hydra API doc.
|
|
200
|
+
* Empty array means no operation info was found (fall back to platform defaults).
|
|
201
|
+
*/
|
|
202
|
+
supportedOperations?: string[];
|
|
203
|
+
}
|
|
204
|
+
interface OpenApiProperty {
|
|
205
|
+
type?: string;
|
|
206
|
+
format?: string;
|
|
207
|
+
readOnly?: boolean;
|
|
208
|
+
writeOnly?: boolean;
|
|
209
|
+
enum?: string[];
|
|
210
|
+
}
|
|
211
|
+
interface OpenApiSchema {
|
|
212
|
+
properties?: Record<string, OpenApiProperty>;
|
|
213
|
+
required?: string[];
|
|
214
|
+
}
|
|
215
|
+
interface OpenApiDoc {
|
|
216
|
+
openapi: string;
|
|
217
|
+
components: {
|
|
218
|
+
schemas: Record<string, OpenApiSchema>;
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
type ApiDoc = {
|
|
222
|
+
format: 'hydra';
|
|
223
|
+
doc: HydraApiDoc;
|
|
224
|
+
} | {
|
|
225
|
+
format: 'openapi';
|
|
226
|
+
doc: OpenApiDoc;
|
|
227
|
+
};
|
|
228
|
+
//#endregion
|
|
229
|
+
//#region packages/hydra/openApiParser.d.ts
|
|
230
|
+
/**
|
|
231
|
+
* Parse a raw `/api/docs.jsonld` Hydra JSON-LD response into a typed
|
|
232
|
+
* Record<className, HydraResourceSchema>.
|
|
233
|
+
*
|
|
234
|
+
* @throws Error if doc is not a valid Hydra ApiDocumentation.
|
|
235
|
+
*/
|
|
236
|
+
declare function parseHydraDoc(doc: HydraApiDoc): Record<string, HydraResourceSchema>;
|
|
237
|
+
/**
|
|
238
|
+
* Parse a raw `/api/docs.json` OpenAPI 3.1 response into the same
|
|
239
|
+
* Record<className, HydraResourceSchema> output shape so that
|
|
240
|
+
* `useResourceSchema` can consume it transparently.
|
|
241
|
+
*/
|
|
242
|
+
declare function parseOpenApiDoc(doc: OpenApiDoc): Record<string, HydraResourceSchema>;
|
|
243
|
+
//#endregion
|
|
244
|
+
//#region packages/hydra/HydraToFieldMapper.d.ts
|
|
245
|
+
/**
|
|
246
|
+
* Determine the FieldType bucket for a given `range` string.
|
|
247
|
+
*
|
|
248
|
+
* Handles both the short-form `xmls:` prefix used by API Platform
|
|
249
|
+
* AND the full XSD IRI form `http://www.w3.org/2001/XMLSchema#...`
|
|
250
|
+
* that can appear when `property.range` is an IRI object `{ "@id": "..." }`
|
|
251
|
+
* and has been normalised to a string by `normalizeRange`.
|
|
252
|
+
*
|
|
253
|
+
* Returns a string tag that the caller can switch on; not the FieldType enum
|
|
254
|
+
* directly (to keep this function pure / dependency-free of FieldBuilders).
|
|
255
|
+
*/
|
|
256
|
+
type RangeTag = 'boolean' | 'dateTime' | 'integer' | 'decimal' | 'entity' | 'text';
|
|
257
|
+
declare function resolveRangeTag(range: string | undefined, propertyType: string): RangeTag;
|
|
258
|
+
/**
|
|
259
|
+
* Map a single HydraResourceSchema to an array of Field objects using the
|
|
260
|
+
* existing field builder utilities.
|
|
261
|
+
*
|
|
262
|
+
* Mapping rules (in priority order):
|
|
263
|
+
* 1. name === 'id' or name === '@id' → always emit as a hidden identity field
|
|
264
|
+
* (visible: false, readonly: true, isIdentity: true)
|
|
265
|
+
* Required so native grids and forms always have a stable row key.
|
|
266
|
+
* 2. readable: false → skip (already filtered upstream, but defensive)
|
|
267
|
+
* 3. writeable: false (display-only) → noneField
|
|
268
|
+
* 4. range resolves to 'boolean' → switchField
|
|
269
|
+
* 5. range resolves to 'dateTime' → datetimeField
|
|
270
|
+
* 6. range resolves to 'integer' → numberField with precision(0)
|
|
271
|
+
* 7. range resolves to 'decimal' → numberField
|
|
272
|
+
* 8. range resolves to 'entity' OR propertyType === 'Link' → entityField
|
|
273
|
+
* 9. range resolves to 'text' OR fallback → textField
|
|
274
|
+
*
|
|
275
|
+
* For each field, `filterable` is `true` when the field's property name appears in
|
|
276
|
+
* `schema.searchMappings` (from `hydra:search`). When `searchMappings` is empty
|
|
277
|
+
* (e.g. the resource uses a catch-all data-grid filter that doesn't
|
|
278
|
+
* enumerate field-level mappings), ALL fields default to `filterable: true`.
|
|
279
|
+
*
|
|
280
|
+
* After processing all schema fields, if no `id` field was found in the schema,
|
|
281
|
+
* a synthetic hidden identity field is injected so the grid always has a key.
|
|
282
|
+
*
|
|
283
|
+
* @param schema - The Hydra resource schema to map.
|
|
284
|
+
* @param urlLookup - Optional lookup function to resolve the API URL for a related
|
|
285
|
+
* entity class. When provided, Rule 8 uses this before falling back to the
|
|
286
|
+
* automatic pluralization heuristic. Example: `(cls) => resourceMap[cls]?.apiUrl`
|
|
287
|
+
*
|
|
288
|
+
* @pure — no React, no hooks, no side effects.
|
|
289
|
+
*/
|
|
290
|
+
declare function mapHydraSchemaToFields(schema: HydraResourceSchema, urlLookup?: (className: string) => string | undefined, schemaLookup?: (className: string) => HydraResourceSchema | undefined): Field[];
|
|
291
|
+
//#endregion
|
|
292
|
+
//#region packages/hydra/useHydraMetadata.d.ts
|
|
293
|
+
/**
|
|
294
|
+
* Stable query key used by useHydraMetadata.
|
|
295
|
+
* Export this so that consumers (e.g. SmartCrudPage retry button) can
|
|
296
|
+
* invalidate exactly the right query without coupling to an internal string.
|
|
297
|
+
*/
|
|
298
|
+
declare const API_DOC_QUERY_KEY: readonly ["api-doc-discovery"];
|
|
299
|
+
interface UseHydraMetadataResult {
|
|
300
|
+
data: ApiDoc | undefined;
|
|
301
|
+
isLoading: boolean;
|
|
302
|
+
error: Error | undefined;
|
|
303
|
+
}
|
|
304
|
+
declare function useHydraMetadata(): UseHydraMetadataResult;
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region packages/hydra/useResourceSchema.d.ts
|
|
307
|
+
type UseResourceSchemaResult = ResourceSchemaResolution;
|
|
308
|
+
declare function useResourceSchema<T extends DataRecord$1 = DataRecord$1>(apiUrl: string): UseResourceSchemaResult;
|
|
309
|
+
//#endregion
|
|
310
|
+
//#region packages/hydra/SchemaContext.d.ts
|
|
311
|
+
interface SchemaProviderProps {
|
|
312
|
+
children: ReactNode;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Wrap your app (or a subtree) with `SchemaProvider` to share a single
|
|
316
|
+
* `/api/docs.jsonld` fetch across all `SmartCrudPage` instances.
|
|
317
|
+
*/
|
|
318
|
+
declare function SchemaProvider({
|
|
319
|
+
children
|
|
320
|
+
}: SchemaProviderProps): React.JSX.Element;
|
|
321
|
+
interface UseSchemaContextResult {
|
|
322
|
+
data: UseHydraMetadataResult['data'];
|
|
323
|
+
isLoading: boolean;
|
|
324
|
+
isError: boolean;
|
|
325
|
+
error: Error | undefined;
|
|
326
|
+
refetch?: () => void;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Returns the shared schema context if inside a `<SchemaProvider>`,
|
|
330
|
+
* otherwise falls back to calling `useHydraMetadata()` directly.
|
|
331
|
+
*
|
|
332
|
+
* This makes `SmartCrudPage` work standalone without requiring a provider.
|
|
333
|
+
*/
|
|
334
|
+
declare function useSchemaContext(): UseSchemaContextResult;
|
|
335
|
+
//#endregion
|
|
336
|
+
export { API_DOC_QUERY_KEY, type ApiDoc, type HydraApiDoc, HydraRemoteDataSource, HydraResourceSchemaProvider, type HydraResourceSchemaProviderProps, HydraResourceStoreProvider, type HydraResourceStoreProviderProps, type OpenApiDoc, type RemoteDataSourceOptions, type RemoteFilterDescriptor, type RemoteLoadOptions, type RemoteSortDescriptor, SchemaProvider, type UseSchemaContextResult, createHydraResourceStore, mapHydraSchemaToFields, parseHydraDoc, parseOpenApiDoc, resolveRangeTag, useHydraMetadata, useResourceSchema, useSchemaContext };
|