strapi-relation-names 1.0.0 → 1.2.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/README.md +8 -8
- package/dist/admin/{SettingsPage-C1nFFJjH.js → SettingsPage-BGsuRnwx.js} +73 -8
- package/dist/admin/{SettingsPage-BQo5mpAY.mjs → SettingsPage-Blk-4H_X.mjs} +73 -8
- package/dist/admin/{index-SsQXjQq0.js → index-DFr0KRky.js} +1 -1
- package/dist/admin/{index-CJO9wtUk.mjs → index-DdvdGn6T.mjs} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/types.d.ts +2 -0
- package/dist/admin/src/utils/template.d.ts +13 -4
- package/dist/server/index.js +104 -27
- package/dist/server/index.mjs +104 -27
- package/dist/server/src/index.d.ts +1 -1
- package/dist/server/src/services/index.d.ts +1 -1
- package/dist/server/src/services/relation-labels.d.ts +6 -1
- package/dist/server/src/services/utils/relation-labels.d.ts +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,6 +9,10 @@ Display Strapi relations using readable labels built from your content fields in
|
|
|
9
9
|

|
|
10
10
|

|
|
11
11
|
|
|
12
|
+
Ever wanted to display a relation as custom text or with nested field values? This plugin solves exactly that:
|
|
13
|
+
|
|
14
|
+

|
|
15
|
+
|
|
12
16
|
---
|
|
13
17
|
|
|
14
18
|
## ✨ Features
|
|
@@ -138,13 +142,7 @@ Customer: {firstName} {lastName}
|
|
|
138
142
|
|
|
139
143
|
## 🗂️ How It Works
|
|
140
144
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
The first placeholder in a template is used internally by Strapi's Admin relation renderer.
|
|
144
|
-
|
|
145
|
-
Strapi's existing relation search and sorting continue to use the configured default main field.
|
|
146
|
-
|
|
147
|
-
Missing field values are replaced with an empty string.
|
|
145
|
+
At runtime, the server-side plugin decorates Strapi Content Manager relation responses and field metadata. It reads the configured template, validates each placeholder against the related content type schema, and renders the label from the returned relation data. If a value is not present, it performs a permission-aware hydration query; nested component paths are loaded by populating their component root. The rendered label is then exposed through Strapi's normal relation display mechanism. Missing field values are replaced with an empty string.
|
|
148
146
|
|
|
149
147
|
For example:
|
|
150
148
|
|
|
@@ -172,9 +170,11 @@ The default label is also used when:
|
|
|
172
170
|
|
|
173
171
|
- [ ] Live label previews while editing templates
|
|
174
172
|
- [ ] Define default relation templates in plugin configuration
|
|
175
|
-
- [
|
|
173
|
+
- [x] Support nested field paths in templates
|
|
174
|
+
- [x] Support nested relation fields in templates
|
|
176
175
|
- [ ] Import and export relation-label settings
|
|
177
176
|
- [ ] Conditional labels and configurable fallback rules
|
|
177
|
+
- [ ] Support label transformation functions (e.g., `toUpperCase`, `toLowerCase`, `capitalize`, etc.)
|
|
178
178
|
|
|
179
179
|
If you have any feature requests or suggestions, please open a dedicated issue.
|
|
180
180
|
|
|
@@ -5,7 +5,7 @@ const designSystem = require("@strapi/design-system");
|
|
|
5
5
|
const react = require("react");
|
|
6
6
|
const reactIntl = require("react-intl");
|
|
7
7
|
const admin = require("@strapi/strapi/admin");
|
|
8
|
-
const index = require("./index-
|
|
8
|
+
const index = require("./index-DFr0KRky.js");
|
|
9
9
|
const FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
10
10
|
"string",
|
|
11
11
|
"text",
|
|
@@ -21,6 +21,25 @@ const FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
|
21
21
|
"time",
|
|
22
22
|
"boolean"
|
|
23
23
|
]);
|
|
24
|
+
const SYSTEM_SCALAR_FIELDS = [
|
|
25
|
+
"id",
|
|
26
|
+
"documentId",
|
|
27
|
+
"createdAt",
|
|
28
|
+
"updatedAt",
|
|
29
|
+
"publishedAt",
|
|
30
|
+
"locale",
|
|
31
|
+
"status"
|
|
32
|
+
];
|
|
33
|
+
const PLACEHOLDER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
34
|
+
const getNestedSchemaUid = (attribute) => {
|
|
35
|
+
if (attribute.type === "component") {
|
|
36
|
+
return attribute.component;
|
|
37
|
+
}
|
|
38
|
+
if (attribute.type === "relation") {
|
|
39
|
+
return attribute.targetModel ?? attribute.target;
|
|
40
|
+
}
|
|
41
|
+
return void 0;
|
|
42
|
+
};
|
|
24
43
|
const getPlaceholders = (template) => {
|
|
25
44
|
if (!template.trim()) {
|
|
26
45
|
return null;
|
|
@@ -33,7 +52,7 @@ const getPlaceholders = (template) => {
|
|
|
33
52
|
return null;
|
|
34
53
|
}
|
|
35
54
|
const name = match[1];
|
|
36
|
-
if (
|
|
55
|
+
if (!PLACEHOLDER_PATTERN.test(name)) {
|
|
37
56
|
return null;
|
|
38
57
|
}
|
|
39
58
|
placeholders.push(name);
|
|
@@ -44,16 +63,62 @@ const getPlaceholders = (template) => {
|
|
|
44
63
|
}
|
|
45
64
|
return placeholders.length > 0 ? placeholders : null;
|
|
46
65
|
};
|
|
47
|
-
const validateTemplate = (template, attributes) => {
|
|
66
|
+
const validateTemplate = (template, attributes, schemas) => {
|
|
48
67
|
const placeholders = getPlaceholders(template);
|
|
49
68
|
if (!placeholders) {
|
|
50
69
|
return "Use at least one valid {fieldName} placeholder.";
|
|
51
70
|
}
|
|
52
|
-
const invalidField = placeholders.find(
|
|
53
|
-
|
|
54
|
-
|
|
71
|
+
const invalidField = placeholders.find((path) => {
|
|
72
|
+
const segments = path.split(".");
|
|
73
|
+
let currentAttributes = attributes;
|
|
74
|
+
for (const [index2, segment] of segments.entries()) {
|
|
75
|
+
if (index2 === 0 && segments.length === 1 && SYSTEM_SCALAR_FIELDS.includes(segment)) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
const attribute = currentAttributes?.[segment];
|
|
79
|
+
const isLastSegment = index2 === segments.length - 1;
|
|
80
|
+
if (!attribute || attribute.private === true) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
if (isLastSegment) {
|
|
84
|
+
return !FIELD_TYPES.has(attribute.type ?? "");
|
|
85
|
+
}
|
|
86
|
+
const nestedSchemaUid = getNestedSchemaUid(attribute);
|
|
87
|
+
if (!nestedSchemaUid) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
currentAttributes = schemas?.get(nestedSchemaUid)?.attributes;
|
|
91
|
+
}
|
|
92
|
+
return true;
|
|
93
|
+
});
|
|
55
94
|
return invalidField ? `“${invalidField}” is not an available scalar field.` : null;
|
|
56
95
|
};
|
|
96
|
+
const getAvailableFields = (schema, schemas, prefix = "", ancestors = /* @__PURE__ */ new Set()) => {
|
|
97
|
+
if (!schema) {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
const fields = Object.entries(schema.attributes ?? {}).flatMap(([name, attribute]) => {
|
|
101
|
+
if (attribute.private === true) {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
const path = `${prefix}${name}`;
|
|
105
|
+
if (FIELD_TYPES.has(attribute.type ?? "")) {
|
|
106
|
+
return [path];
|
|
107
|
+
}
|
|
108
|
+
const nestedSchemaUid = getNestedSchemaUid(attribute);
|
|
109
|
+
if (!nestedSchemaUid || ancestors.has(nestedSchemaUid)) {
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
return getAvailableFields(
|
|
113
|
+
schemas.get(nestedSchemaUid),
|
|
114
|
+
schemas,
|
|
115
|
+
`${path}.`,
|
|
116
|
+
/* @__PURE__ */ new Set([...ancestors, nestedSchemaUid])
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
const systemFields = prefix === "" ? SYSTEM_SCALAR_FIELDS : [];
|
|
120
|
+
return [...systemFields, ...fields.filter((field) => !systemFields.includes(field))];
|
|
121
|
+
};
|
|
57
122
|
const PLUGIN_SETTINGS_PATH = "/strapi-relation-names/settings";
|
|
58
123
|
const getRelationTarget = (attribute) => attribute.targetModel ?? attribute.target;
|
|
59
124
|
const SettingsPage = () => {
|
|
@@ -143,7 +208,7 @@ const SettingsPage = () => {
|
|
|
143
208
|
!isLoading ? rows.map(({ source, fieldName, target }) => {
|
|
144
209
|
const targetAttributes = target?.attributes ?? {};
|
|
145
210
|
const template = settings.relations[source.uid]?.[fieldName] ?? "";
|
|
146
|
-
const validationError = template ? validateTemplate(template, targetAttributes) : null;
|
|
211
|
+
const validationError = template ? validateTemplate(template, targetAttributes, schemaMap) : null;
|
|
147
212
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
148
213
|
designSystem.Box,
|
|
149
214
|
{
|
|
@@ -186,7 +251,7 @@ const SettingsPage = () => {
|
|
|
186
251
|
formatMessage({ id: index.getTranslation("settings.availableFields") }),
|
|
187
252
|
":",
|
|
188
253
|
" ",
|
|
189
|
-
|
|
254
|
+
getAvailableFields(target, schemaMap).join(", ")
|
|
190
255
|
] }) })
|
|
191
256
|
]
|
|
192
257
|
},
|
|
@@ -3,7 +3,7 @@ import { Main, Box, Typography, Field, TextInput, Flex, Button } from "@strapi/d
|
|
|
3
3
|
import { useState, useCallback, useEffect, useMemo } from "react";
|
|
4
4
|
import { useIntl } from "react-intl";
|
|
5
5
|
import { useFetchClient } from "@strapi/strapi/admin";
|
|
6
|
-
import { g as getTranslation } from "./index-
|
|
6
|
+
import { g as getTranslation } from "./index-DdvdGn6T.mjs";
|
|
7
7
|
const FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
8
8
|
"string",
|
|
9
9
|
"text",
|
|
@@ -19,6 +19,25 @@ const FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
|
19
19
|
"time",
|
|
20
20
|
"boolean"
|
|
21
21
|
]);
|
|
22
|
+
const SYSTEM_SCALAR_FIELDS = [
|
|
23
|
+
"id",
|
|
24
|
+
"documentId",
|
|
25
|
+
"createdAt",
|
|
26
|
+
"updatedAt",
|
|
27
|
+
"publishedAt",
|
|
28
|
+
"locale",
|
|
29
|
+
"status"
|
|
30
|
+
];
|
|
31
|
+
const PLACEHOLDER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
32
|
+
const getNestedSchemaUid = (attribute) => {
|
|
33
|
+
if (attribute.type === "component") {
|
|
34
|
+
return attribute.component;
|
|
35
|
+
}
|
|
36
|
+
if (attribute.type === "relation") {
|
|
37
|
+
return attribute.targetModel ?? attribute.target;
|
|
38
|
+
}
|
|
39
|
+
return void 0;
|
|
40
|
+
};
|
|
22
41
|
const getPlaceholders = (template) => {
|
|
23
42
|
if (!template.trim()) {
|
|
24
43
|
return null;
|
|
@@ -31,7 +50,7 @@ const getPlaceholders = (template) => {
|
|
|
31
50
|
return null;
|
|
32
51
|
}
|
|
33
52
|
const name = match[1];
|
|
34
|
-
if (
|
|
53
|
+
if (!PLACEHOLDER_PATTERN.test(name)) {
|
|
35
54
|
return null;
|
|
36
55
|
}
|
|
37
56
|
placeholders.push(name);
|
|
@@ -42,16 +61,62 @@ const getPlaceholders = (template) => {
|
|
|
42
61
|
}
|
|
43
62
|
return placeholders.length > 0 ? placeholders : null;
|
|
44
63
|
};
|
|
45
|
-
const validateTemplate = (template, attributes) => {
|
|
64
|
+
const validateTemplate = (template, attributes, schemas) => {
|
|
46
65
|
const placeholders = getPlaceholders(template);
|
|
47
66
|
if (!placeholders) {
|
|
48
67
|
return "Use at least one valid {fieldName} placeholder.";
|
|
49
68
|
}
|
|
50
|
-
const invalidField = placeholders.find(
|
|
51
|
-
|
|
52
|
-
|
|
69
|
+
const invalidField = placeholders.find((path) => {
|
|
70
|
+
const segments = path.split(".");
|
|
71
|
+
let currentAttributes = attributes;
|
|
72
|
+
for (const [index, segment] of segments.entries()) {
|
|
73
|
+
if (index === 0 && segments.length === 1 && SYSTEM_SCALAR_FIELDS.includes(segment)) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
const attribute = currentAttributes?.[segment];
|
|
77
|
+
const isLastSegment = index === segments.length - 1;
|
|
78
|
+
if (!attribute || attribute.private === true) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
if (isLastSegment) {
|
|
82
|
+
return !FIELD_TYPES.has(attribute.type ?? "");
|
|
83
|
+
}
|
|
84
|
+
const nestedSchemaUid = getNestedSchemaUid(attribute);
|
|
85
|
+
if (!nestedSchemaUid) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
currentAttributes = schemas?.get(nestedSchemaUid)?.attributes;
|
|
89
|
+
}
|
|
90
|
+
return true;
|
|
91
|
+
});
|
|
53
92
|
return invalidField ? `“${invalidField}” is not an available scalar field.` : null;
|
|
54
93
|
};
|
|
94
|
+
const getAvailableFields = (schema, schemas, prefix = "", ancestors = /* @__PURE__ */ new Set()) => {
|
|
95
|
+
if (!schema) {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
const fields = Object.entries(schema.attributes ?? {}).flatMap(([name, attribute]) => {
|
|
99
|
+
if (attribute.private === true) {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
const path = `${prefix}${name}`;
|
|
103
|
+
if (FIELD_TYPES.has(attribute.type ?? "")) {
|
|
104
|
+
return [path];
|
|
105
|
+
}
|
|
106
|
+
const nestedSchemaUid = getNestedSchemaUid(attribute);
|
|
107
|
+
if (!nestedSchemaUid || ancestors.has(nestedSchemaUid)) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
return getAvailableFields(
|
|
111
|
+
schemas.get(nestedSchemaUid),
|
|
112
|
+
schemas,
|
|
113
|
+
`${path}.`,
|
|
114
|
+
/* @__PURE__ */ new Set([...ancestors, nestedSchemaUid])
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
const systemFields = prefix === "" ? SYSTEM_SCALAR_FIELDS : [];
|
|
118
|
+
return [...systemFields, ...fields.filter((field) => !systemFields.includes(field))];
|
|
119
|
+
};
|
|
55
120
|
const PLUGIN_SETTINGS_PATH = "/strapi-relation-names/settings";
|
|
56
121
|
const getRelationTarget = (attribute) => attribute.targetModel ?? attribute.target;
|
|
57
122
|
const SettingsPage = () => {
|
|
@@ -141,7 +206,7 @@ const SettingsPage = () => {
|
|
|
141
206
|
!isLoading ? rows.map(({ source, fieldName, target }) => {
|
|
142
207
|
const targetAttributes = target?.attributes ?? {};
|
|
143
208
|
const template = settings.relations[source.uid]?.[fieldName] ?? "";
|
|
144
|
-
const validationError = template ? validateTemplate(template, targetAttributes) : null;
|
|
209
|
+
const validationError = template ? validateTemplate(template, targetAttributes, schemaMap) : null;
|
|
145
210
|
return /* @__PURE__ */ jsxs(
|
|
146
211
|
Box,
|
|
147
212
|
{
|
|
@@ -184,7 +249,7 @@ const SettingsPage = () => {
|
|
|
184
249
|
formatMessage({ id: getTranslation("settings.availableFields") }),
|
|
185
250
|
":",
|
|
186
251
|
" ",
|
|
187
|
-
|
|
252
|
+
getAvailableFields(target, schemaMap).join(", ")
|
|
188
253
|
] }) })
|
|
189
254
|
]
|
|
190
255
|
},
|
|
@@ -89,7 +89,7 @@ const plugin = {
|
|
|
89
89
|
id: `${PLUGIN_ID}.plugin.name`,
|
|
90
90
|
defaultMessage: "Relation Names"
|
|
91
91
|
},
|
|
92
|
-
Component: () => Promise.resolve().then(() => require("./SettingsPage-
|
|
92
|
+
Component: () => Promise.resolve().then(() => require("./SettingsPage-BGsuRnwx.js")).then((module2) => ({ default: module2.SettingsPage })),
|
|
93
93
|
permissions: []
|
|
94
94
|
});
|
|
95
95
|
app.registerPlugin({
|
|
@@ -88,7 +88,7 @@ const plugin = {
|
|
|
88
88
|
id: `${PLUGIN_ID}.plugin.name`,
|
|
89
89
|
defaultMessage: "Relation Names"
|
|
90
90
|
},
|
|
91
|
-
Component: () => import("./SettingsPage-
|
|
91
|
+
Component: () => import("./SettingsPage-Blk-4H_X.mjs").then((module) => ({ default: module.SettingsPage })),
|
|
92
92
|
permissions: []
|
|
93
93
|
});
|
|
94
94
|
app.registerPlugin({
|
package/dist/admin/index.js
CHANGED
package/dist/admin/index.mjs
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
declare const validateTemplate: (template: string, attributes: Record<string, {
|
|
1
|
+
type TemplateAttribute = {
|
|
3
2
|
type?: string;
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
component?: string;
|
|
4
|
+
target?: string;
|
|
5
|
+
targetModel?: string;
|
|
6
|
+
private?: boolean;
|
|
7
|
+
};
|
|
8
|
+
type TemplateSchema = {
|
|
9
|
+
attributes?: Record<string, TemplateAttribute>;
|
|
10
|
+
};
|
|
11
|
+
declare const getPlaceholders: (template: string) => string[] | null;
|
|
12
|
+
declare const validateTemplate: (template: string, attributes: Record<string, TemplateAttribute> | undefined, schemas?: ReadonlyMap<string, TemplateSchema>) => string | null;
|
|
13
|
+
declare const getAvailableFields: (schema: TemplateSchema | undefined, schemas: ReadonlyMap<string, TemplateSchema>, prefix?: string, ancestors?: Set<string>) => string[];
|
|
14
|
+
export { getAvailableFields, getPlaceholders, validateTemplate };
|
package/dist/server/index.js
CHANGED
|
@@ -151,7 +151,25 @@ const SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
|
151
151
|
"time",
|
|
152
152
|
"boolean"
|
|
153
153
|
]);
|
|
154
|
+
const SYSTEM_SCALAR_FIELDS = /* @__PURE__ */ new Set([
|
|
155
|
+
"id",
|
|
156
|
+
"documentId",
|
|
157
|
+
"createdAt",
|
|
158
|
+
"updatedAt",
|
|
159
|
+
"publishedAt",
|
|
160
|
+
"locale",
|
|
161
|
+
"status"
|
|
162
|
+
]);
|
|
163
|
+
const PLACEHOLDER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
154
164
|
const isRecord$1 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
165
|
+
const getPathValue = (value, path) => {
|
|
166
|
+
return path.split(".").reduce((current, segment) => {
|
|
167
|
+
if (Array.isArray(current)) {
|
|
168
|
+
return current.map((item) => isRecord$1(item) ? item[segment] : void 0).filter((item) => item !== void 0);
|
|
169
|
+
}
|
|
170
|
+
return isRecord$1(current) ? current[segment] : void 0;
|
|
171
|
+
}, value);
|
|
172
|
+
};
|
|
155
173
|
const parseTemplate = (template) => {
|
|
156
174
|
if (!template.trim()) {
|
|
157
175
|
return null;
|
|
@@ -172,7 +190,7 @@ const parseTemplate = (template) => {
|
|
|
172
190
|
return null;
|
|
173
191
|
}
|
|
174
192
|
const name = template.slice(openingBrace + 1, end);
|
|
175
|
-
if (
|
|
193
|
+
if (!PLACEHOLDER_PATTERN.test(name)) {
|
|
176
194
|
return null;
|
|
177
195
|
}
|
|
178
196
|
placeholders.push(name);
|
|
@@ -180,17 +198,36 @@ const parseTemplate = (template) => {
|
|
|
180
198
|
}
|
|
181
199
|
return placeholders.length > 0 ? placeholders : null;
|
|
182
200
|
};
|
|
183
|
-
const compileTemplate = (template, targetSchema) => {
|
|
201
|
+
const compileTemplate = (template, targetSchema, resolveSchema) => {
|
|
184
202
|
const placeholders = parseTemplate(template);
|
|
185
|
-
const attributes = targetSchema?.attributes ?? {};
|
|
186
203
|
if (!placeholders || !targetSchema) {
|
|
187
204
|
return null;
|
|
188
205
|
}
|
|
189
|
-
const valid = placeholders.every((
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
206
|
+
const valid = placeholders.every((path) => {
|
|
207
|
+
const segments = path.split(".");
|
|
208
|
+
let schema = targetSchema;
|
|
209
|
+
for (const [index2, segment] of segments.entries()) {
|
|
210
|
+
const attribute = schema.attributes?.[segment];
|
|
211
|
+
const isLastSegment = index2 === segments.length - 1;
|
|
212
|
+
if (index2 === 0 && isLastSegment && SYSTEM_SCALAR_FIELDS.has(segment)) {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
if (!attribute || attribute.private === true) {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
if (isLastSegment) {
|
|
219
|
+
return SCALAR_TYPES.has(attribute.type ?? "");
|
|
220
|
+
}
|
|
221
|
+
const nestedSchemaUid = attribute.type === "component" ? attribute.component : attribute.type === "relation" ? attribute.target : void 0;
|
|
222
|
+
if (!nestedSchemaUid || !resolveSchema) {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
schema = resolveSchema(nestedSchemaUid);
|
|
226
|
+
if (!schema) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return false;
|
|
194
231
|
});
|
|
195
232
|
if (!valid) {
|
|
196
233
|
return null;
|
|
@@ -198,14 +235,14 @@ const compileTemplate = (template, targetSchema) => {
|
|
|
198
235
|
return {
|
|
199
236
|
template,
|
|
200
237
|
placeholders,
|
|
201
|
-
displayField: placeholders[0]
|
|
238
|
+
displayField: placeholders[0].split(".").slice(-1)[0] ?? placeholders[0]
|
|
202
239
|
};
|
|
203
240
|
};
|
|
204
241
|
const renderTemplate = (compiled, values) => {
|
|
205
242
|
const rendered = compiled.template.replace(
|
|
206
|
-
/\{([A-Za-z_][A-Za-z0-9_]*)\}/g,
|
|
243
|
+
/\{([A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*)\}/g,
|
|
207
244
|
(_match, name) => {
|
|
208
|
-
const value = values
|
|
245
|
+
const value = getPathValue(values, name);
|
|
209
246
|
return value === null || value === void 0 ? "" : String(value);
|
|
210
247
|
}
|
|
211
248
|
);
|
|
@@ -272,28 +309,62 @@ const getRuntime = async (strapi, settings2, sourceUid, targetField, userAbility
|
|
|
272
309
|
}
|
|
273
310
|
const template = settings2.relations[sourceUid]?.[targetField];
|
|
274
311
|
const targetSchema = strapi.getModel(attribute.target);
|
|
275
|
-
const compiled = typeof template === "string" ? compileTemplate(
|
|
312
|
+
const compiled = typeof template === "string" ? compileTemplate(
|
|
313
|
+
template,
|
|
314
|
+
targetSchema,
|
|
315
|
+
(uid) => strapi.getModel(uid)
|
|
316
|
+
) : null;
|
|
276
317
|
if (!compiled || !targetSchema) {
|
|
277
318
|
return null;
|
|
278
319
|
}
|
|
320
|
+
const originalMainField = await getOriginalMainField(
|
|
321
|
+
strapi,
|
|
322
|
+
sourceSchema,
|
|
323
|
+
targetSchema,
|
|
324
|
+
targetField,
|
|
325
|
+
userAbility
|
|
326
|
+
);
|
|
279
327
|
return {
|
|
280
328
|
...compiled,
|
|
329
|
+
displayField: compiled.placeholders.some((placeholder) => placeholder.includes(".")) ? originalMainField : compiled.displayField,
|
|
281
330
|
sourceUid,
|
|
282
331
|
targetUid: targetSchema.uid,
|
|
283
|
-
originalMainField
|
|
284
|
-
strapi,
|
|
285
|
-
sourceSchema,
|
|
286
|
-
targetSchema,
|
|
287
|
-
targetField,
|
|
288
|
-
userAbility
|
|
289
|
-
)
|
|
332
|
+
originalMainField
|
|
290
333
|
};
|
|
291
334
|
};
|
|
335
|
+
const buildPopulate = (placeholders) => {
|
|
336
|
+
const populate = {};
|
|
337
|
+
for (const placeholder of placeholders) {
|
|
338
|
+
const segments = placeholder.split(".");
|
|
339
|
+
if (segments.length < 2) {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
let current = populate;
|
|
343
|
+
for (const [index2, segment] of segments.slice(0, -1).entries()) {
|
|
344
|
+
const existing = current[segment];
|
|
345
|
+
if (existing === true) {
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
if (index2 === segments.length - 2) {
|
|
349
|
+
current[segment] = existing ?? true;
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
if (!existing) {
|
|
353
|
+
current[segment] = { populate: {} };
|
|
354
|
+
}
|
|
355
|
+
const next = current[segment];
|
|
356
|
+
if (next !== true) {
|
|
357
|
+
current = next.populate;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return Object.keys(populate).length > 0 ? populate : void 0;
|
|
362
|
+
};
|
|
292
363
|
const hydrateValues = async (strapi, ctx, runtime, values) => {
|
|
293
364
|
const targetSchema = strapi.getModel(runtime.targetUid);
|
|
294
365
|
const targetModelType = targetSchema.modelType;
|
|
295
366
|
const missingFields = runtime.placeholders.filter(
|
|
296
|
-
(field) => values.some((value) =>
|
|
367
|
+
(field) => values.some((value) => getPathValue(value, field) === void 0)
|
|
297
368
|
);
|
|
298
369
|
if (missingFields.length === 0) {
|
|
299
370
|
return new Map(values.map((value) => [value, value]));
|
|
@@ -306,7 +377,7 @@ const hydrateValues = async (strapi, ctx, runtime, values) => {
|
|
|
306
377
|
const permissionChecker = strapi.plugin("content-manager").service("permission-checker").create({ userAbility: ctx.state.userAbility, model: runtime.targetUid });
|
|
307
378
|
const fields = Array.from(
|
|
308
379
|
/* @__PURE__ */ new Set([
|
|
309
|
-
...runtime.placeholders,
|
|
380
|
+
...runtime.placeholders.filter((field) => !field.includes(".")),
|
|
310
381
|
runtime.originalMainField,
|
|
311
382
|
"id",
|
|
312
383
|
"documentId",
|
|
@@ -314,10 +385,12 @@ const hydrateValues = async (strapi, ctx, runtime, values) => {
|
|
|
314
385
|
"publishedAt"
|
|
315
386
|
])
|
|
316
387
|
);
|
|
388
|
+
const populate = buildPopulate(runtime.placeholders);
|
|
317
389
|
const identityField = targetModelType === "component" ? "id" : "documentId";
|
|
318
390
|
const permissionQuery = await permissionChecker.sanitizedQuery.read({
|
|
319
391
|
fields,
|
|
320
|
-
filters: { [identityField]: { $in: identities } }
|
|
392
|
+
filters: { [identityField]: { $in: identities } },
|
|
393
|
+
...populate ? { populate } : {}
|
|
321
394
|
});
|
|
322
395
|
const query = strapi.get("query-params").transform(runtime.targetUid, permissionQuery);
|
|
323
396
|
const hydrated = await strapi.db.query(runtime.targetUid).findMany(query);
|
|
@@ -370,23 +443,27 @@ const relationLabels = ({ strapi }) => {
|
|
|
370
443
|
const targetSchema = strapi.getModel(attribute.target);
|
|
371
444
|
const compiled = compileTemplate(
|
|
372
445
|
settings2.relations[sourceUid]?.[fieldName] ?? "",
|
|
373
|
-
targetSchema
|
|
446
|
+
targetSchema,
|
|
447
|
+
(uid) => strapi.getModel(uid)
|
|
374
448
|
);
|
|
375
449
|
if (!compiled || !isRecord$1(metadata)) {
|
|
376
450
|
continue;
|
|
377
451
|
}
|
|
452
|
+
const editMetadata = isRecord$1(metadata.edit) ? metadata.edit : {};
|
|
453
|
+
const configuredMainField = typeof editMetadata.mainField === "string" ? editMetadata.mainField : "id";
|
|
454
|
+
const displayField = compiled.placeholders.some((placeholder) => placeholder.includes(".")) ? configuredMainField : compiled.displayField;
|
|
378
455
|
metadatas[fieldName] = {
|
|
379
456
|
...metadata,
|
|
380
457
|
edit: {
|
|
381
|
-
...
|
|
382
|
-
mainField:
|
|
458
|
+
...editMetadata,
|
|
459
|
+
mainField: displayField
|
|
383
460
|
},
|
|
384
461
|
list: {
|
|
385
462
|
...isRecord$1(metadata.list) ? metadata.list : {},
|
|
386
|
-
mainField:
|
|
463
|
+
mainField: displayField
|
|
387
464
|
}
|
|
388
465
|
};
|
|
389
|
-
relationNames[fieldName] = { mainField:
|
|
466
|
+
relationNames[fieldName] = { mainField: displayField };
|
|
390
467
|
}
|
|
391
468
|
return {
|
|
392
469
|
...configuration,
|
package/dist/server/index.mjs
CHANGED
|
@@ -149,7 +149,25 @@ const SCALAR_TYPES = /* @__PURE__ */ new Set([
|
|
|
149
149
|
"time",
|
|
150
150
|
"boolean"
|
|
151
151
|
]);
|
|
152
|
+
const SYSTEM_SCALAR_FIELDS = /* @__PURE__ */ new Set([
|
|
153
|
+
"id",
|
|
154
|
+
"documentId",
|
|
155
|
+
"createdAt",
|
|
156
|
+
"updatedAt",
|
|
157
|
+
"publishedAt",
|
|
158
|
+
"locale",
|
|
159
|
+
"status"
|
|
160
|
+
]);
|
|
161
|
+
const PLACEHOLDER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
152
162
|
const isRecord$1 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
163
|
+
const getPathValue = (value, path) => {
|
|
164
|
+
return path.split(".").reduce((current, segment) => {
|
|
165
|
+
if (Array.isArray(current)) {
|
|
166
|
+
return current.map((item) => isRecord$1(item) ? item[segment] : void 0).filter((item) => item !== void 0);
|
|
167
|
+
}
|
|
168
|
+
return isRecord$1(current) ? current[segment] : void 0;
|
|
169
|
+
}, value);
|
|
170
|
+
};
|
|
153
171
|
const parseTemplate = (template) => {
|
|
154
172
|
if (!template.trim()) {
|
|
155
173
|
return null;
|
|
@@ -170,7 +188,7 @@ const parseTemplate = (template) => {
|
|
|
170
188
|
return null;
|
|
171
189
|
}
|
|
172
190
|
const name = template.slice(openingBrace + 1, end);
|
|
173
|
-
if (
|
|
191
|
+
if (!PLACEHOLDER_PATTERN.test(name)) {
|
|
174
192
|
return null;
|
|
175
193
|
}
|
|
176
194
|
placeholders.push(name);
|
|
@@ -178,17 +196,36 @@ const parseTemplate = (template) => {
|
|
|
178
196
|
}
|
|
179
197
|
return placeholders.length > 0 ? placeholders : null;
|
|
180
198
|
};
|
|
181
|
-
const compileTemplate = (template, targetSchema) => {
|
|
199
|
+
const compileTemplate = (template, targetSchema, resolveSchema) => {
|
|
182
200
|
const placeholders = parseTemplate(template);
|
|
183
|
-
const attributes = targetSchema?.attributes ?? {};
|
|
184
201
|
if (!placeholders || !targetSchema) {
|
|
185
202
|
return null;
|
|
186
203
|
}
|
|
187
|
-
const valid = placeholders.every((
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
204
|
+
const valid = placeholders.every((path) => {
|
|
205
|
+
const segments = path.split(".");
|
|
206
|
+
let schema = targetSchema;
|
|
207
|
+
for (const [index2, segment] of segments.entries()) {
|
|
208
|
+
const attribute = schema.attributes?.[segment];
|
|
209
|
+
const isLastSegment = index2 === segments.length - 1;
|
|
210
|
+
if (index2 === 0 && isLastSegment && SYSTEM_SCALAR_FIELDS.has(segment)) {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
if (!attribute || attribute.private === true) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
if (isLastSegment) {
|
|
217
|
+
return SCALAR_TYPES.has(attribute.type ?? "");
|
|
218
|
+
}
|
|
219
|
+
const nestedSchemaUid = attribute.type === "component" ? attribute.component : attribute.type === "relation" ? attribute.target : void 0;
|
|
220
|
+
if (!nestedSchemaUid || !resolveSchema) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
schema = resolveSchema(nestedSchemaUid);
|
|
224
|
+
if (!schema) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return false;
|
|
192
229
|
});
|
|
193
230
|
if (!valid) {
|
|
194
231
|
return null;
|
|
@@ -196,14 +233,14 @@ const compileTemplate = (template, targetSchema) => {
|
|
|
196
233
|
return {
|
|
197
234
|
template,
|
|
198
235
|
placeholders,
|
|
199
|
-
displayField: placeholders[0]
|
|
236
|
+
displayField: placeholders[0].split(".").slice(-1)[0] ?? placeholders[0]
|
|
200
237
|
};
|
|
201
238
|
};
|
|
202
239
|
const renderTemplate = (compiled, values) => {
|
|
203
240
|
const rendered = compiled.template.replace(
|
|
204
|
-
/\{([A-Za-z_][A-Za-z0-9_]*)\}/g,
|
|
241
|
+
/\{([A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*)\}/g,
|
|
205
242
|
(_match, name) => {
|
|
206
|
-
const value = values
|
|
243
|
+
const value = getPathValue(values, name);
|
|
207
244
|
return value === null || value === void 0 ? "" : String(value);
|
|
208
245
|
}
|
|
209
246
|
);
|
|
@@ -270,28 +307,62 @@ const getRuntime = async (strapi, settings2, sourceUid, targetField, userAbility
|
|
|
270
307
|
}
|
|
271
308
|
const template = settings2.relations[sourceUid]?.[targetField];
|
|
272
309
|
const targetSchema = strapi.getModel(attribute.target);
|
|
273
|
-
const compiled = typeof template === "string" ? compileTemplate(
|
|
310
|
+
const compiled = typeof template === "string" ? compileTemplate(
|
|
311
|
+
template,
|
|
312
|
+
targetSchema,
|
|
313
|
+
(uid) => strapi.getModel(uid)
|
|
314
|
+
) : null;
|
|
274
315
|
if (!compiled || !targetSchema) {
|
|
275
316
|
return null;
|
|
276
317
|
}
|
|
318
|
+
const originalMainField = await getOriginalMainField(
|
|
319
|
+
strapi,
|
|
320
|
+
sourceSchema,
|
|
321
|
+
targetSchema,
|
|
322
|
+
targetField,
|
|
323
|
+
userAbility
|
|
324
|
+
);
|
|
277
325
|
return {
|
|
278
326
|
...compiled,
|
|
327
|
+
displayField: compiled.placeholders.some((placeholder) => placeholder.includes(".")) ? originalMainField : compiled.displayField,
|
|
279
328
|
sourceUid,
|
|
280
329
|
targetUid: targetSchema.uid,
|
|
281
|
-
originalMainField
|
|
282
|
-
strapi,
|
|
283
|
-
sourceSchema,
|
|
284
|
-
targetSchema,
|
|
285
|
-
targetField,
|
|
286
|
-
userAbility
|
|
287
|
-
)
|
|
330
|
+
originalMainField
|
|
288
331
|
};
|
|
289
332
|
};
|
|
333
|
+
const buildPopulate = (placeholders) => {
|
|
334
|
+
const populate = {};
|
|
335
|
+
for (const placeholder of placeholders) {
|
|
336
|
+
const segments = placeholder.split(".");
|
|
337
|
+
if (segments.length < 2) {
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
let current = populate;
|
|
341
|
+
for (const [index2, segment] of segments.slice(0, -1).entries()) {
|
|
342
|
+
const existing = current[segment];
|
|
343
|
+
if (existing === true) {
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
if (index2 === segments.length - 2) {
|
|
347
|
+
current[segment] = existing ?? true;
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
if (!existing) {
|
|
351
|
+
current[segment] = { populate: {} };
|
|
352
|
+
}
|
|
353
|
+
const next = current[segment];
|
|
354
|
+
if (next !== true) {
|
|
355
|
+
current = next.populate;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return Object.keys(populate).length > 0 ? populate : void 0;
|
|
360
|
+
};
|
|
290
361
|
const hydrateValues = async (strapi, ctx, runtime, values) => {
|
|
291
362
|
const targetSchema = strapi.getModel(runtime.targetUid);
|
|
292
363
|
const targetModelType = targetSchema.modelType;
|
|
293
364
|
const missingFields = runtime.placeholders.filter(
|
|
294
|
-
(field) => values.some((value) =>
|
|
365
|
+
(field) => values.some((value) => getPathValue(value, field) === void 0)
|
|
295
366
|
);
|
|
296
367
|
if (missingFields.length === 0) {
|
|
297
368
|
return new Map(values.map((value) => [value, value]));
|
|
@@ -304,7 +375,7 @@ const hydrateValues = async (strapi, ctx, runtime, values) => {
|
|
|
304
375
|
const permissionChecker = strapi.plugin("content-manager").service("permission-checker").create({ userAbility: ctx.state.userAbility, model: runtime.targetUid });
|
|
305
376
|
const fields = Array.from(
|
|
306
377
|
/* @__PURE__ */ new Set([
|
|
307
|
-
...runtime.placeholders,
|
|
378
|
+
...runtime.placeholders.filter((field) => !field.includes(".")),
|
|
308
379
|
runtime.originalMainField,
|
|
309
380
|
"id",
|
|
310
381
|
"documentId",
|
|
@@ -312,10 +383,12 @@ const hydrateValues = async (strapi, ctx, runtime, values) => {
|
|
|
312
383
|
"publishedAt"
|
|
313
384
|
])
|
|
314
385
|
);
|
|
386
|
+
const populate = buildPopulate(runtime.placeholders);
|
|
315
387
|
const identityField = targetModelType === "component" ? "id" : "documentId";
|
|
316
388
|
const permissionQuery = await permissionChecker.sanitizedQuery.read({
|
|
317
389
|
fields,
|
|
318
|
-
filters: { [identityField]: { $in: identities } }
|
|
390
|
+
filters: { [identityField]: { $in: identities } },
|
|
391
|
+
...populate ? { populate } : {}
|
|
319
392
|
});
|
|
320
393
|
const query = strapi.get("query-params").transform(runtime.targetUid, permissionQuery);
|
|
321
394
|
const hydrated = await strapi.db.query(runtime.targetUid).findMany(query);
|
|
@@ -368,23 +441,27 @@ const relationLabels = ({ strapi }) => {
|
|
|
368
441
|
const targetSchema = strapi.getModel(attribute.target);
|
|
369
442
|
const compiled = compileTemplate(
|
|
370
443
|
settings2.relations[sourceUid]?.[fieldName] ?? "",
|
|
371
|
-
targetSchema
|
|
444
|
+
targetSchema,
|
|
445
|
+
(uid) => strapi.getModel(uid)
|
|
372
446
|
);
|
|
373
447
|
if (!compiled || !isRecord$1(metadata)) {
|
|
374
448
|
continue;
|
|
375
449
|
}
|
|
450
|
+
const editMetadata = isRecord$1(metadata.edit) ? metadata.edit : {};
|
|
451
|
+
const configuredMainField = typeof editMetadata.mainField === "string" ? editMetadata.mainField : "id";
|
|
452
|
+
const displayField = compiled.placeholders.some((placeholder) => placeholder.includes(".")) ? configuredMainField : compiled.displayField;
|
|
376
453
|
metadatas[fieldName] = {
|
|
377
454
|
...metadata,
|
|
378
455
|
edit: {
|
|
379
|
-
...
|
|
380
|
-
mainField:
|
|
456
|
+
...editMetadata,
|
|
457
|
+
mainField: displayField
|
|
381
458
|
},
|
|
382
459
|
list: {
|
|
383
460
|
...isRecord$1(metadata.list) ? metadata.list : {},
|
|
384
|
-
mainField:
|
|
461
|
+
mainField: displayField
|
|
385
462
|
}
|
|
386
463
|
};
|
|
387
|
-
relationNames[fieldName] = { mainField:
|
|
464
|
+
relationNames[fieldName] = { mainField: displayField };
|
|
388
465
|
}
|
|
389
466
|
return {
|
|
390
467
|
...configuration,
|
|
@@ -48,7 +48,7 @@ declare const _default: {
|
|
|
48
48
|
decorateCollectionResults: (ctx: any, sourceUid: string, results: unknown[]) => Promise<unknown[]>;
|
|
49
49
|
decorateConfiguration: (data: any, sourceUid: string) => Promise<any>;
|
|
50
50
|
decorateRelationResults: (ctx: any, sourceUid: string, targetField: string, results: unknown[]) => Promise<unknown[]>;
|
|
51
|
-
compileTemplate: (template: string, targetSchema: import('./services/utils/relation-labels').SchemaLike | undefined) => import('./services/utils/relation-labels').CompiledTemplate | null;
|
|
51
|
+
compileTemplate: (template: string, targetSchema: import('./services/utils/relation-labels').SchemaLike | undefined, resolveSchema?: import('./services/utils/relation-labels').SchemaResolver) => import('./services/utils/relation-labels').CompiledTemplate | null;
|
|
52
52
|
parseTemplate: (template: string) => string[] | null;
|
|
53
53
|
renderTemplate: (compiled: import('./services/utils/relation-labels').CompiledTemplate, values: Record<string, unknown>) => string;
|
|
54
54
|
};
|
|
@@ -5,7 +5,7 @@ declare const _default: {
|
|
|
5
5
|
decorateCollectionResults: (ctx: any, sourceUid: string, results: unknown[]) => Promise<unknown[]>;
|
|
6
6
|
decorateConfiguration: (data: any, sourceUid: string) => Promise<any>;
|
|
7
7
|
decorateRelationResults: (ctx: any, sourceUid: string, targetField: string, results: unknown[]) => Promise<unknown[]>;
|
|
8
|
-
compileTemplate: (template: string, targetSchema: import('./utils/relation-labels').SchemaLike | undefined) => import('./utils/relation-labels').CompiledTemplate | null;
|
|
8
|
+
compileTemplate: (template: string, targetSchema: import('./utils/relation-labels').SchemaLike | undefined, resolveSchema?: import('./utils/relation-labels').SchemaResolver) => import('./utils/relation-labels').CompiledTemplate | null;
|
|
9
9
|
parseTemplate: (template: string) => string[] | null;
|
|
10
10
|
renderTemplate: (compiled: import('./utils/relation-labels').CompiledTemplate, values: Record<string, unknown>) => string;
|
|
11
11
|
};
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { Core } from '@strapi/strapi';
|
|
2
2
|
import { SchemaLike } from './utils/relation-labels';
|
|
3
|
+
type Populate = Record<string, true | {
|
|
4
|
+
populate: Populate;
|
|
5
|
+
}>;
|
|
6
|
+
declare const buildPopulate: (placeholders: string[]) => Populate | undefined;
|
|
3
7
|
declare const relationLabels: ({ strapi }: {
|
|
4
8
|
strapi: Core.Strapi;
|
|
5
9
|
}) => {
|
|
6
10
|
decorateCollectionResults: (ctx: any, sourceUid: string, results: unknown[]) => Promise<unknown[]>;
|
|
7
11
|
decorateConfiguration: (data: any, sourceUid: string) => Promise<any>;
|
|
8
12
|
decorateRelationResults: (ctx: any, sourceUid: string, targetField: string, results: unknown[]) => Promise<unknown[]>;
|
|
9
|
-
compileTemplate: (template: string, targetSchema: SchemaLike | undefined) => import('./utils/relation-labels').CompiledTemplate | null;
|
|
13
|
+
compileTemplate: (template: string, targetSchema: SchemaLike | undefined, resolveSchema?: import('./utils/relation-labels').SchemaResolver) => import('./utils/relation-labels').CompiledTemplate | null;
|
|
10
14
|
parseTemplate: (template: string) => string[] | null;
|
|
11
15
|
renderTemplate: (compiled: import('./utils/relation-labels').CompiledTemplate, values: Record<string, unknown>) => string;
|
|
12
16
|
};
|
|
17
|
+
export { buildPopulate };
|
|
13
18
|
export { compileTemplate, parseTemplate, renderTemplate } from './utils/relation-labels';
|
|
14
19
|
export default relationLabels;
|
|
@@ -11,6 +11,7 @@ export type SchemaLike = {
|
|
|
11
11
|
modelType?: string;
|
|
12
12
|
attributes?: Record<string, SchemaAttribute>;
|
|
13
13
|
};
|
|
14
|
+
export type SchemaResolver = (uid: string) => SchemaLike | undefined;
|
|
14
15
|
export type CompiledTemplate = {
|
|
15
16
|
template: string;
|
|
16
17
|
placeholders: string[];
|
|
@@ -22,6 +23,7 @@ export type RelationRuntime = CompiledTemplate & {
|
|
|
22
23
|
originalMainField: string;
|
|
23
24
|
};
|
|
24
25
|
export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
|
|
26
|
+
export declare const getPathValue: (value: unknown, path: string) => unknown;
|
|
25
27
|
export declare const parseTemplate: (template: string) => string[] | null;
|
|
26
|
-
export declare const compileTemplate: (template: string, targetSchema: SchemaLike | undefined) => CompiledTemplate | null;
|
|
28
|
+
export declare const compileTemplate: (template: string, targetSchema: SchemaLike | undefined, resolveSchema?: SchemaResolver) => CompiledTemplate | null;
|
|
27
29
|
export declare const renderTemplate: (compiled: CompiledTemplate, values: Record<string, unknown>) => string;
|