@nubitio/hydra 0.5.19 → 0.5.22
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/dist/index.cjs +46 -5
- package/dist/index.d.cts +16 -1
- package/dist/index.d.mts +16 -1
- package/dist/index.mjs +46 -5
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -93,12 +93,24 @@ function getFlatIriFilter(filter) {
|
|
|
93
93
|
if (!Array.isArray(filter)) return null;
|
|
94
94
|
return filter.length === 1 && Array.isArray(filter[0]) ? filter[0] : filter;
|
|
95
95
|
}
|
|
96
|
+
function parseGridSummaryHeader(headers) {
|
|
97
|
+
const raw = headers.get("x-grid-summary");
|
|
98
|
+
if (!raw) return null;
|
|
99
|
+
try {
|
|
100
|
+
const parsed = JSON.parse(raw);
|
|
101
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
102
|
+
} catch {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
96
106
|
function parseCollectionResponse(responseData, responseHeaders) {
|
|
107
|
+
const gridSummary = parseGridSummaryHeader(responseHeaders);
|
|
97
108
|
if (Array.isArray(responseData)) {
|
|
98
109
|
const headerCount = Number(responseHeaders.get("x-total-count"));
|
|
99
110
|
return {
|
|
100
111
|
data: responseData,
|
|
101
|
-
totalCount: Number.isFinite(headerCount) ? headerCount : responseData.length
|
|
112
|
+
totalCount: Number.isFinite(headerCount) ? headerCount : responseData.length,
|
|
113
|
+
gridSummary
|
|
102
114
|
};
|
|
103
115
|
}
|
|
104
116
|
if (responseData && typeof responseData === "object") {
|
|
@@ -109,13 +121,15 @@ function parseCollectionResponse(responseData, responseHeaders) {
|
|
|
109
121
|
const totalCount = Number(rawTotal);
|
|
110
122
|
return {
|
|
111
123
|
data: member,
|
|
112
|
-
totalCount: Number.isFinite(totalCount) ? totalCount : member.length
|
|
124
|
+
totalCount: Number.isFinite(totalCount) ? totalCount : member.length,
|
|
125
|
+
gridSummary
|
|
113
126
|
};
|
|
114
127
|
}
|
|
115
128
|
}
|
|
116
129
|
return {
|
|
117
130
|
data: [],
|
|
118
|
-
totalCount: 0
|
|
131
|
+
totalCount: 0,
|
|
132
|
+
gridSummary
|
|
119
133
|
};
|
|
120
134
|
}
|
|
121
135
|
var HydraRemoteDataSource = class {
|
|
@@ -174,7 +188,8 @@ var HydraRemoteDataSource = class {
|
|
|
174
188
|
return {
|
|
175
189
|
data,
|
|
176
190
|
totalCount,
|
|
177
|
-
summary: null
|
|
191
|
+
summary: null,
|
|
192
|
+
gridSummary: parsed.gridSummary
|
|
178
193
|
};
|
|
179
194
|
}
|
|
180
195
|
async fetchById(url, id, loadOptions) {
|
|
@@ -194,6 +209,16 @@ function HydraResourceStoreProvider({ children }) {
|
|
|
194
209
|
});
|
|
195
210
|
}
|
|
196
211
|
//#endregion
|
|
212
|
+
//#region packages/hydra/buildSummaryFields.ts
|
|
213
|
+
function buildSummaryFieldsFromSchema(schema) {
|
|
214
|
+
return schema.fields.filter((field) => field.crudHints?.summable).map((field) => ({
|
|
215
|
+
column: field.name,
|
|
216
|
+
summaryType: field.crudHints?.summaryType ?? "sum",
|
|
217
|
+
valueFormat: field.crudHints?.format === "currency" ? "currency" : void 0,
|
|
218
|
+
label: field["hydra:title"] && field["hydra:title"] !== field.name ? field["hydra:title"] : void 0
|
|
219
|
+
}));
|
|
220
|
+
}
|
|
221
|
+
//#endregion
|
|
197
222
|
//#region packages/hydra/openApiParser.ts
|
|
198
223
|
/**
|
|
199
224
|
* Converts a camelCase or PascalCase name to dash-case.
|
|
@@ -410,6 +435,7 @@ function parseHydraDoc(doc, entrypointHrefs) {
|
|
|
410
435
|
fields,
|
|
411
436
|
formLayout: cls["x-crud-layout"],
|
|
412
437
|
workflow: cls["x-workflow"],
|
|
438
|
+
sequence: cls["x-sequence"],
|
|
413
439
|
searchMappings: extractSearchMappings(cls),
|
|
414
440
|
supportedOperations: Array.from(new Set([...collectionOperationsMap[className] ?? [], ...extractSupportedOperations(cls)]))
|
|
415
441
|
};
|
|
@@ -520,6 +546,18 @@ function applyCrudHints(field, hints) {
|
|
|
520
546
|
if (hints.order !== void 0) field.order = hints.order;
|
|
521
547
|
if (hints.width !== void 0) field.width = hints.width;
|
|
522
548
|
}
|
|
549
|
+
/**
|
|
550
|
+
* Apply `x-sequence` hints: the server-allocated field is read-only and
|
|
551
|
+
* excluded from create/edit forms (the listener assigns it on POST).
|
|
552
|
+
*/
|
|
553
|
+
function applySequenceHints(fields, schema) {
|
|
554
|
+
const sequenceField = schema.sequence?.field;
|
|
555
|
+
if (!sequenceField) return;
|
|
556
|
+
for (const field of fields) if (field.name === sequenceField) {
|
|
557
|
+
field.visibleOnForm = false;
|
|
558
|
+
field.readonly = true;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
523
561
|
function resolveEntityValueField(relatedSchema) {
|
|
524
562
|
if (!relatedSchema) return "_iri";
|
|
525
563
|
const fieldNames = new Set(relatedSchema.fields.map((field) => field.name));
|
|
@@ -716,6 +754,7 @@ function mapHydraSchemaToFields(schema, urlLookup, schemaLookup) {
|
|
|
716
754
|
};
|
|
717
755
|
fields.unshift(syntheticId);
|
|
718
756
|
}
|
|
757
|
+
applySequenceHints(fields, schema);
|
|
719
758
|
return fields;
|
|
720
759
|
}
|
|
721
760
|
//#endregion
|
|
@@ -908,7 +947,9 @@ function useResourceSchema(apiUrl) {
|
|
|
908
947
|
error: void 0,
|
|
909
948
|
supportedOperations: resourceSchema.supportedOperations ?? [],
|
|
910
949
|
formLayout: resourceSchema.formLayout,
|
|
911
|
-
workflow: resourceSchema.workflow
|
|
950
|
+
workflow: resourceSchema.workflow,
|
|
951
|
+
sequence: resourceSchema.sequence,
|
|
952
|
+
summaryFields: buildSummaryFieldsFromSchema(resourceSchema)
|
|
912
953
|
};
|
|
913
954
|
}, [
|
|
914
955
|
data,
|
package/dist/index.d.cts
CHANGED
|
@@ -74,6 +74,10 @@ interface CrudHints {
|
|
|
74
74
|
* upload controls that submit the media IRI.
|
|
75
75
|
*/
|
|
76
76
|
format?: 'currency' | 'image' | 'file';
|
|
77
|
+
/** Include this column in the server-side grid summary footer. */
|
|
78
|
+
summable?: boolean;
|
|
79
|
+
/** Aggregate function for summable columns. @default 'sum' */
|
|
80
|
+
summaryType?: 'sum' | 'count' | 'avg' | 'min' | 'max';
|
|
77
81
|
}
|
|
78
82
|
interface HydraSupportedProperty {
|
|
79
83
|
'@type': 'SupportedProperty';
|
|
@@ -133,6 +137,13 @@ interface WorkflowSchema {
|
|
|
133
137
|
field: string;
|
|
134
138
|
transitions: WorkflowTransitionSchema[];
|
|
135
139
|
}
|
|
140
|
+
interface SequenceSchema {
|
|
141
|
+
field: string;
|
|
142
|
+
name?: string;
|
|
143
|
+
prefix?: string;
|
|
144
|
+
padding?: number;
|
|
145
|
+
scope?: string[];
|
|
146
|
+
}
|
|
136
147
|
interface HydraClass {
|
|
137
148
|
'@id': string;
|
|
138
149
|
'@type': string;
|
|
@@ -140,6 +151,8 @@ interface HydraClass {
|
|
|
140
151
|
supportedProperty: HydraSupportedProperty[];
|
|
141
152
|
/** State machine metadata from nubitio/workflow-bundle. */
|
|
142
153
|
'x-workflow'?: WorkflowSchema;
|
|
154
|
+
/** Auto-numbering metadata from nubitio/sequence-bundle. */
|
|
155
|
+
'x-sequence'?: SequenceSchema;
|
|
143
156
|
/**
|
|
144
157
|
* Class-level UI hints injected by TranslatedDocumentationNormalizer from
|
|
145
158
|
* the ApiResource's extraProperties['x-crud']['formLayout']. Mirrors
|
|
@@ -234,6 +247,8 @@ interface HydraResourceSchema {
|
|
|
234
247
|
supportedOperations?: string[];
|
|
235
248
|
/** Workflow transitions for auto row actions (nubitio/workflow-bundle). */
|
|
236
249
|
workflow?: WorkflowSchema;
|
|
250
|
+
/** Server-allocated sequence field (nubitio/sequence-bundle). */
|
|
251
|
+
sequence?: SequenceSchema;
|
|
237
252
|
}
|
|
238
253
|
interface OpenApiProperty {
|
|
239
254
|
type?: string;
|
|
@@ -384,4 +399,4 @@ interface UseSchemaContextResult {
|
|
|
384
399
|
*/
|
|
385
400
|
declare function useSchemaContext(): UseSchemaContextResult;
|
|
386
401
|
//#endregion
|
|
387
|
-
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, type WorkflowSchema, type WorkflowTransitionSchema, createHydraResourceStore, mapHydraSchemaToFields, parseHydraDoc, parseOpenApiDoc, resolveRangeTag, useHydraMetadata, useResourceSchema, useSchemaContext };
|
|
402
|
+
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 SequenceSchema, type UseSchemaContextResult, type WorkflowSchema, type WorkflowTransitionSchema, createHydraResourceStore, mapHydraSchemaToFields, parseHydraDoc, parseOpenApiDoc, resolveRangeTag, useHydraMetadata, useResourceSchema, useSchemaContext };
|
package/dist/index.d.mts
CHANGED
|
@@ -74,6 +74,10 @@ interface CrudHints {
|
|
|
74
74
|
* upload controls that submit the media IRI.
|
|
75
75
|
*/
|
|
76
76
|
format?: 'currency' | 'image' | 'file';
|
|
77
|
+
/** Include this column in the server-side grid summary footer. */
|
|
78
|
+
summable?: boolean;
|
|
79
|
+
/** Aggregate function for summable columns. @default 'sum' */
|
|
80
|
+
summaryType?: 'sum' | 'count' | 'avg' | 'min' | 'max';
|
|
77
81
|
}
|
|
78
82
|
interface HydraSupportedProperty {
|
|
79
83
|
'@type': 'SupportedProperty';
|
|
@@ -133,6 +137,13 @@ interface WorkflowSchema {
|
|
|
133
137
|
field: string;
|
|
134
138
|
transitions: WorkflowTransitionSchema[];
|
|
135
139
|
}
|
|
140
|
+
interface SequenceSchema {
|
|
141
|
+
field: string;
|
|
142
|
+
name?: string;
|
|
143
|
+
prefix?: string;
|
|
144
|
+
padding?: number;
|
|
145
|
+
scope?: string[];
|
|
146
|
+
}
|
|
136
147
|
interface HydraClass {
|
|
137
148
|
'@id': string;
|
|
138
149
|
'@type': string;
|
|
@@ -140,6 +151,8 @@ interface HydraClass {
|
|
|
140
151
|
supportedProperty: HydraSupportedProperty[];
|
|
141
152
|
/** State machine metadata from nubitio/workflow-bundle. */
|
|
142
153
|
'x-workflow'?: WorkflowSchema;
|
|
154
|
+
/** Auto-numbering metadata from nubitio/sequence-bundle. */
|
|
155
|
+
'x-sequence'?: SequenceSchema;
|
|
143
156
|
/**
|
|
144
157
|
* Class-level UI hints injected by TranslatedDocumentationNormalizer from
|
|
145
158
|
* the ApiResource's extraProperties['x-crud']['formLayout']. Mirrors
|
|
@@ -234,6 +247,8 @@ interface HydraResourceSchema {
|
|
|
234
247
|
supportedOperations?: string[];
|
|
235
248
|
/** Workflow transitions for auto row actions (nubitio/workflow-bundle). */
|
|
236
249
|
workflow?: WorkflowSchema;
|
|
250
|
+
/** Server-allocated sequence field (nubitio/sequence-bundle). */
|
|
251
|
+
sequence?: SequenceSchema;
|
|
237
252
|
}
|
|
238
253
|
interface OpenApiProperty {
|
|
239
254
|
type?: string;
|
|
@@ -384,4 +399,4 @@ interface UseSchemaContextResult {
|
|
|
384
399
|
*/
|
|
385
400
|
declare function useSchemaContext(): UseSchemaContextResult;
|
|
386
401
|
//#endregion
|
|
387
|
-
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, type WorkflowSchema, type WorkflowTransitionSchema, createHydraResourceStore, mapHydraSchemaToFields, parseHydraDoc, parseOpenApiDoc, resolveRangeTag, useHydraMetadata, useResourceSchema, useSchemaContext };
|
|
402
|
+
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 SequenceSchema, type UseSchemaContextResult, type WorkflowSchema, type WorkflowTransitionSchema, createHydraResourceStore, mapHydraSchemaToFields, parseHydraDoc, parseOpenApiDoc, resolveRangeTag, useHydraMetadata, useResourceSchema, useSchemaContext };
|
package/dist/index.mjs
CHANGED
|
@@ -69,12 +69,24 @@ function getFlatIriFilter(filter) {
|
|
|
69
69
|
if (!Array.isArray(filter)) return null;
|
|
70
70
|
return filter.length === 1 && Array.isArray(filter[0]) ? filter[0] : filter;
|
|
71
71
|
}
|
|
72
|
+
function parseGridSummaryHeader(headers) {
|
|
73
|
+
const raw = headers.get("x-grid-summary");
|
|
74
|
+
if (!raw) return null;
|
|
75
|
+
try {
|
|
76
|
+
const parsed = JSON.parse(raw);
|
|
77
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
78
|
+
} catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
72
82
|
function parseCollectionResponse(responseData, responseHeaders) {
|
|
83
|
+
const gridSummary = parseGridSummaryHeader(responseHeaders);
|
|
73
84
|
if (Array.isArray(responseData)) {
|
|
74
85
|
const headerCount = Number(responseHeaders.get("x-total-count"));
|
|
75
86
|
return {
|
|
76
87
|
data: responseData,
|
|
77
|
-
totalCount: Number.isFinite(headerCount) ? headerCount : responseData.length
|
|
88
|
+
totalCount: Number.isFinite(headerCount) ? headerCount : responseData.length,
|
|
89
|
+
gridSummary
|
|
78
90
|
};
|
|
79
91
|
}
|
|
80
92
|
if (responseData && typeof responseData === "object") {
|
|
@@ -85,13 +97,15 @@ function parseCollectionResponse(responseData, responseHeaders) {
|
|
|
85
97
|
const totalCount = Number(rawTotal);
|
|
86
98
|
return {
|
|
87
99
|
data: member,
|
|
88
|
-
totalCount: Number.isFinite(totalCount) ? totalCount : member.length
|
|
100
|
+
totalCount: Number.isFinite(totalCount) ? totalCount : member.length,
|
|
101
|
+
gridSummary
|
|
89
102
|
};
|
|
90
103
|
}
|
|
91
104
|
}
|
|
92
105
|
return {
|
|
93
106
|
data: [],
|
|
94
|
-
totalCount: 0
|
|
107
|
+
totalCount: 0,
|
|
108
|
+
gridSummary
|
|
95
109
|
};
|
|
96
110
|
}
|
|
97
111
|
var HydraRemoteDataSource = class {
|
|
@@ -150,7 +164,8 @@ var HydraRemoteDataSource = class {
|
|
|
150
164
|
return {
|
|
151
165
|
data,
|
|
152
166
|
totalCount,
|
|
153
|
-
summary: null
|
|
167
|
+
summary: null,
|
|
168
|
+
gridSummary: parsed.gridSummary
|
|
154
169
|
};
|
|
155
170
|
}
|
|
156
171
|
async fetchById(url, id, loadOptions) {
|
|
@@ -170,6 +185,16 @@ function HydraResourceStoreProvider({ children }) {
|
|
|
170
185
|
});
|
|
171
186
|
}
|
|
172
187
|
//#endregion
|
|
188
|
+
//#region packages/hydra/buildSummaryFields.ts
|
|
189
|
+
function buildSummaryFieldsFromSchema(schema) {
|
|
190
|
+
return schema.fields.filter((field) => field.crudHints?.summable).map((field) => ({
|
|
191
|
+
column: field.name,
|
|
192
|
+
summaryType: field.crudHints?.summaryType ?? "sum",
|
|
193
|
+
valueFormat: field.crudHints?.format === "currency" ? "currency" : void 0,
|
|
194
|
+
label: field["hydra:title"] && field["hydra:title"] !== field.name ? field["hydra:title"] : void 0
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
//#endregion
|
|
173
198
|
//#region packages/hydra/openApiParser.ts
|
|
174
199
|
/**
|
|
175
200
|
* Converts a camelCase or PascalCase name to dash-case.
|
|
@@ -386,6 +411,7 @@ function parseHydraDoc(doc, entrypointHrefs) {
|
|
|
386
411
|
fields,
|
|
387
412
|
formLayout: cls["x-crud-layout"],
|
|
388
413
|
workflow: cls["x-workflow"],
|
|
414
|
+
sequence: cls["x-sequence"],
|
|
389
415
|
searchMappings: extractSearchMappings(cls),
|
|
390
416
|
supportedOperations: Array.from(new Set([...collectionOperationsMap[className] ?? [], ...extractSupportedOperations(cls)]))
|
|
391
417
|
};
|
|
@@ -496,6 +522,18 @@ function applyCrudHints(field, hints) {
|
|
|
496
522
|
if (hints.order !== void 0) field.order = hints.order;
|
|
497
523
|
if (hints.width !== void 0) field.width = hints.width;
|
|
498
524
|
}
|
|
525
|
+
/**
|
|
526
|
+
* Apply `x-sequence` hints: the server-allocated field is read-only and
|
|
527
|
+
* excluded from create/edit forms (the listener assigns it on POST).
|
|
528
|
+
*/
|
|
529
|
+
function applySequenceHints(fields, schema) {
|
|
530
|
+
const sequenceField = schema.sequence?.field;
|
|
531
|
+
if (!sequenceField) return;
|
|
532
|
+
for (const field of fields) if (field.name === sequenceField) {
|
|
533
|
+
field.visibleOnForm = false;
|
|
534
|
+
field.readonly = true;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
499
537
|
function resolveEntityValueField(relatedSchema) {
|
|
500
538
|
if (!relatedSchema) return "_iri";
|
|
501
539
|
const fieldNames = new Set(relatedSchema.fields.map((field) => field.name));
|
|
@@ -692,6 +730,7 @@ function mapHydraSchemaToFields(schema, urlLookup, schemaLookup) {
|
|
|
692
730
|
};
|
|
693
731
|
fields.unshift(syntheticId);
|
|
694
732
|
}
|
|
733
|
+
applySequenceHints(fields, schema);
|
|
695
734
|
return fields;
|
|
696
735
|
}
|
|
697
736
|
//#endregion
|
|
@@ -884,7 +923,9 @@ function useResourceSchema(apiUrl) {
|
|
|
884
923
|
error: void 0,
|
|
885
924
|
supportedOperations: resourceSchema.supportedOperations ?? [],
|
|
886
925
|
formLayout: resourceSchema.formLayout,
|
|
887
|
-
workflow: resourceSchema.workflow
|
|
926
|
+
workflow: resourceSchema.workflow,
|
|
927
|
+
sequence: resourceSchema.sequence,
|
|
928
|
+
summaryFields: buildSummaryFieldsFromSchema(resourceSchema)
|
|
888
929
|
};
|
|
889
930
|
}, [
|
|
890
931
|
data,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nubitio/hydra",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Hydra / OpenAPI adapter for @nubitio/crud — automatic schema discovery and data source from API Platform docs.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"react": "^19.0.0",
|
|
51
51
|
"react-dom": "^19.0.0",
|
|
52
52
|
"react-i18next": "^14.0.0",
|
|
53
|
-
"@nubitio/core": "^0.5.
|
|
54
|
-
"@nubitio/crud": "^0.5.
|
|
53
|
+
"@nubitio/core": "^0.5.22",
|
|
54
|
+
"@nubitio/crud": "^0.5.22"
|
|
55
55
|
}
|
|
56
56
|
}
|