@verbb/plugin-kit-core 2.0.3 → 2.0.5
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/CHANGELOG.md +19 -0
- package/dist/host/hostBridge.js +35 -0
- package/dist/host/hostBridge.js.map +1 -0
- package/dist/host/portal.js +52 -0
- package/dist/host/portal.js.map +1 -0
- package/dist/index.js +10 -0
- package/dist/utils/collections.js +155 -0
- package/dist/utils/collections.js.map +1 -0
- package/dist/utils/forms.js +46 -0
- package/dist/utils/forms.js.map +1 -0
- package/dist/utils/handle.js +69 -0
- package/dist/utils/handle.js.map +1 -0
- package/dist/utils/markdown.d.ts +19 -7
- package/dist/utils/markdown.d.ts.map +1 -1
- package/dist/utils/markdown.js +61 -0
- package/dist/utils/markdown.js.map +1 -0
- package/dist/utils/promises.js +36 -0
- package/dist/utils/promises.js.map +1 -0
- package/dist/utils/query.js +13 -0
- package/dist/utils/query.js.map +1 -0
- package/dist/utils/string.js +42 -0
- package/dist/utils/string.js.map +1 -0
- package/package.json +25 -4
- package/dist/plugin-kit-core.es.js +0 -464
- package/dist/plugin-kit-core.es.js.map +0 -1
- package/dist/plugin-kit-core.umd.js +0 -528
- package/dist/plugin-kit-core.umd.js.map +0 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//#region src/utils/string.ts
|
|
2
|
+
/**
|
|
3
|
+
* Convert a string to a handle format
|
|
4
|
+
* @param {string} sourceValue - The source string to convert
|
|
5
|
+
* @param {string} handleCasing - The casing format ('camelCase', 'pascal', 'snake', 'kebab')
|
|
6
|
+
* @param {boolean} allowNonAlphaStart - Whether to allow non-alphabetic characters at the start
|
|
7
|
+
* @returns {string} The generated handle
|
|
8
|
+
*/
|
|
9
|
+
var generateHandle = function(sourceValue, handleCasing = "camelCase", allowNonAlphaStart = false) {
|
|
10
|
+
let handle = sourceValue.replace("/<(.*?)>/g", "");
|
|
11
|
+
handle = handle.replace(/['"'""\[\]\(\)\{\}:]/g, "");
|
|
12
|
+
handle = handle.toLowerCase();
|
|
13
|
+
handle = window.Craft.asciiString(handle);
|
|
14
|
+
if (!allowNonAlphaStart) handle = handle.replace(/^[^a-z]+/, "");
|
|
15
|
+
const words = window.Craft.filterArray(handle.split(/[^a-z0-9]+/));
|
|
16
|
+
handle = "";
|
|
17
|
+
if (handleCasing === "snake") return words.join("_");
|
|
18
|
+
if (handleCasing === "kebab") return words.join("-");
|
|
19
|
+
for (let i = 0; i < words.length; i++) if (handleCasing !== "pascal" && i === 0) handle += words[i];
|
|
20
|
+
else handle += words[i].charAt(0).toUpperCase() + words[i].substr(1);
|
|
21
|
+
return handle;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Find a unique handle by checking against reserved handles and adding suffixes
|
|
25
|
+
* @param {string} baseHandle - The base handle to check
|
|
26
|
+
* @param {Array} allReservedHandles - Array of all reserved handles to check against (static + dynamic)
|
|
27
|
+
* @returns {string} A unique handle
|
|
28
|
+
*/
|
|
29
|
+
var findUniqueHandle = (baseHandle, allReservedHandles = []) => {
|
|
30
|
+
if (!baseHandle) return "";
|
|
31
|
+
let handle = baseHandle;
|
|
32
|
+
let counter = 1;
|
|
33
|
+
while (allReservedHandles.includes(handle)) {
|
|
34
|
+
handle = `${baseHandle}${counter}`;
|
|
35
|
+
counter++;
|
|
36
|
+
}
|
|
37
|
+
return handle;
|
|
38
|
+
};
|
|
39
|
+
//#endregion
|
|
40
|
+
export { findUniqueHandle, generateHandle };
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=string.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"string.js","names":[],"sources":["../../src/utils/string.ts"],"sourcesContent":["/**\n * Convert a string to a handle format\n * @param {string} sourceValue - The source string to convert\n * @param {string} handleCasing - The casing format ('camelCase', 'pascal', 'snake', 'kebab')\n * @param {boolean} allowNonAlphaStart - Whether to allow non-alphabetic characters at the start\n * @returns {string} The generated handle\n */\nexport const generateHandle = function(sourceValue: string, handleCasing: string = 'camelCase', allowNonAlphaStart: boolean = false): string {\n // Remove HTML tags\n let handle = sourceValue.replace('/<(.*?)>/g', '');\n\n // Remove inner-word punctuation\n\n handle = handle.replace(/['\"'\"\"\\[\\]\\(\\)\\{\\}:]/g, '');\n\n // Make it lowercase\n handle = handle.toLowerCase();\n\n // Convert extended ASCII characters to basic ASCII\n handle = (window as any).Craft.asciiString(handle);\n\n if (!allowNonAlphaStart) {\n // Handle must start with a letter\n handle = handle.replace(/^[^a-z]+/, '');\n }\n\n // Get the \"words\"\n const words = (window as any).Craft.filterArray(handle.split(/[^a-z0-9]+/));\n handle = '';\n\n if (handleCasing === 'snake') {\n return words.join('_');\n }\n\n if (handleCasing === 'kebab') {\n return words.join('-');\n }\n\n // Make it camelCase\n for (let i = 0; i < words.length; i++) {\n if (handleCasing !== 'pascal' && i === 0) {\n handle += words[i];\n } else {\n handle += words[i].charAt(0).toUpperCase() + words[i].substr(1);\n }\n }\n\n return handle;\n};\n\n/**\n * Find a unique handle by checking against reserved handles and adding suffixes\n * @param {string} baseHandle - The base handle to check\n * @param {Array} allReservedHandles - Array of all reserved handles to check against (static + dynamic)\n * @returns {string} A unique handle\n */\nexport const findUniqueHandle = (baseHandle: string, allReservedHandles: string[] = []): string => {\n if (!baseHandle) { return ''; }\n\n let handle = baseHandle;\n let counter = 1;\n\n // Keep adding numbers until we find a unique handle\n while (allReservedHandles.includes(handle)) {\n handle = `${baseHandle}${counter}`;\n counter++;\n }\n\n return handle;\n};\n"],"mappings":";;;;;;;;AAOA,IAAa,iBAAiB,SAAS,aAAqB,eAAuB,aAAa,qBAA8B,OAAe;CAEzI,IAAI,SAAS,YAAY,QAAQ,cAAc,GAAG;AAIlD,UAAS,OAAO,QAAQ,yBAAyB,GAAG;AAGpD,UAAS,OAAO,aAAa;AAG7B,UAAU,OAAe,MAAM,YAAY,OAAO;AAElD,KAAI,CAAC,mBAED,UAAS,OAAO,QAAQ,YAAY,GAAG;CAI3C,MAAM,QAAS,OAAe,MAAM,YAAY,OAAO,MAAM,aAAa,CAAC;AAC3E,UAAS;AAET,KAAI,iBAAiB,QACjB,QAAO,MAAM,KAAK,IAAI;AAG1B,KAAI,iBAAiB,QACjB,QAAO,MAAM,KAAK,IAAI;AAI1B,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,IAC9B,KAAI,iBAAiB,YAAY,MAAM,EACnC,WAAU,MAAM;KAEhB,WAAU,MAAM,GAAG,OAAO,EAAE,CAAC,aAAa,GAAG,MAAM,GAAG,OAAO,EAAE;AAIvE,QAAO;;;;;;;;AASX,IAAa,oBAAoB,YAAoB,qBAA+B,EAAE,KAAa;AAC/F,KAAI,CAAC,WAAc,QAAO;CAE1B,IAAI,SAAS;CACb,IAAI,UAAU;AAGd,QAAO,mBAAmB,SAAS,OAAO,EAAE;AACxC,WAAS,GAAG,aAAa;AACzB;;AAGJ,QAAO"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@verbb/plugin-kit-core",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"
|
|
6
|
-
"
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.js",
|
|
7
8
|
"types": "dist/index.d.ts",
|
|
8
9
|
"files": [
|
|
9
10
|
"dist",
|
|
@@ -28,7 +29,27 @@
|
|
|
28
29
|
"exports": {
|
|
29
30
|
".": {
|
|
30
31
|
"types": "./dist/index.d.ts",
|
|
31
|
-
"import": "./dist/
|
|
32
|
+
"import": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./utils": {
|
|
35
|
+
"types": "./dist/utils/index.d.ts",
|
|
36
|
+
"import": "./dist/utils/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./utils/string": {
|
|
39
|
+
"types": "./dist/utils/string.d.ts",
|
|
40
|
+
"import": "./dist/utils/string.js"
|
|
41
|
+
},
|
|
42
|
+
"./utils/handle": {
|
|
43
|
+
"types": "./dist/utils/handle.d.ts",
|
|
44
|
+
"import": "./dist/utils/handle.js"
|
|
45
|
+
},
|
|
46
|
+
"./utils/markdown": {
|
|
47
|
+
"types": "./dist/utils/markdown.d.ts",
|
|
48
|
+
"import": "./dist/utils/markdown.js"
|
|
49
|
+
},
|
|
50
|
+
"./host": {
|
|
51
|
+
"types": "./dist/host/index.d.ts",
|
|
52
|
+
"import": "./dist/host/index.js"
|
|
32
53
|
},
|
|
33
54
|
"./eslint/*": "./dist/eslint/*",
|
|
34
55
|
"./prettier/*": "./dist/prettier/*",
|
|
@@ -1,464 +0,0 @@
|
|
|
1
|
-
import MarkdownIt from "markdown-it";
|
|
2
|
-
//#region src/host/portal.ts
|
|
3
|
-
var defaultPortalClassName;
|
|
4
|
-
var defaultPortalContainer;
|
|
5
|
-
var defaultShadowRootSelectors = ["[data-pk-shadow-root]"];
|
|
6
|
-
var setPortalClassName = (className) => {
|
|
7
|
-
defaultPortalClassName = className.trim() || void 0;
|
|
8
|
-
};
|
|
9
|
-
var getPortalClassName = (className) => {
|
|
10
|
-
return (className ?? defaultPortalClassName)?.trim() || void 0;
|
|
11
|
-
};
|
|
12
|
-
var resolvePortalContainer = (container) => {
|
|
13
|
-
if (!container) return;
|
|
14
|
-
if (typeof container === "object" && "current" in container) return container.current ?? void 0;
|
|
15
|
-
return container ?? void 0;
|
|
16
|
-
};
|
|
17
|
-
var setPortalContainer = (container) => {
|
|
18
|
-
defaultPortalContainer = resolvePortalContainer(container);
|
|
19
|
-
};
|
|
20
|
-
var getPortalContainer = (container) => {
|
|
21
|
-
return resolvePortalContainer(container) ?? defaultPortalContainer;
|
|
22
|
-
};
|
|
23
|
-
var setShadowRootSelectors = (selectors) => {
|
|
24
|
-
const normalizedSelectors = (selectors || []).map((selector) => selector.trim()).filter(Boolean);
|
|
25
|
-
defaultShadowRootSelectors = normalizedSelectors.length > 0 ? normalizedSelectors : ["[data-pk-shadow-root]"];
|
|
26
|
-
};
|
|
27
|
-
var getShadowRootSelectors = () => {
|
|
28
|
-
return defaultShadowRootSelectors;
|
|
29
|
-
};
|
|
30
|
-
/**
|
|
31
|
-
* Resolve where floating content should be portaled.
|
|
32
|
-
* Falls back to document.body when no container is configured.
|
|
33
|
-
*/
|
|
34
|
-
var getPortalMountNode = (container) => {
|
|
35
|
-
const resolved = getPortalContainer(container);
|
|
36
|
-
if (resolved instanceof HTMLElement) return resolved;
|
|
37
|
-
if (resolved instanceof ShadowRoot) {
|
|
38
|
-
const target = getShadowRootSelectors().map((selector) => resolved.querySelector(selector)).find(Boolean) ?? (resolved.host instanceof HTMLElement ? resolved.host : void 0);
|
|
39
|
-
if (target) return target;
|
|
40
|
-
}
|
|
41
|
-
return document.body;
|
|
42
|
-
};
|
|
43
|
-
var getPortalTargetForAppend = () => {
|
|
44
|
-
const container = getPortalContainer();
|
|
45
|
-
if (!container) return;
|
|
46
|
-
if (container instanceof HTMLElement) return container;
|
|
47
|
-
const shadowRoot = container;
|
|
48
|
-
return getShadowRootSelectors().map((selector) => shadowRoot.querySelector(selector)).find(Boolean) ?? (shadowRoot.host instanceof HTMLElement ? shadowRoot.host : void 0);
|
|
49
|
-
};
|
|
50
|
-
//#endregion
|
|
51
|
-
//#region src/host/hostBridge.ts
|
|
52
|
-
var hostBridge = {};
|
|
53
|
-
var setHostBridge = (bridge = {}) => {
|
|
54
|
-
hostBridge = {
|
|
55
|
-
...hostBridge,
|
|
56
|
-
...bridge
|
|
57
|
-
};
|
|
58
|
-
};
|
|
59
|
-
var getHostBridge = () => {
|
|
60
|
-
return hostBridge;
|
|
61
|
-
};
|
|
62
|
-
var requireHostBridgeMethod = (methodName) => {
|
|
63
|
-
const method = hostBridge[methodName];
|
|
64
|
-
if (!method) throw new Error(`Plugin Kit host bridge method "${String(methodName)}" is required but was not configured.`);
|
|
65
|
-
return method;
|
|
66
|
-
};
|
|
67
|
-
var hostRequest = async (method, action, config) => {
|
|
68
|
-
return requireHostBridgeMethod("request")(method, action, config);
|
|
69
|
-
};
|
|
70
|
-
var hostOpenElementSelector = (elementType, options) => {
|
|
71
|
-
requireHostBridgeMethod("openElementSelector")(elementType, options);
|
|
72
|
-
};
|
|
73
|
-
var hostFormatDate = (date) => {
|
|
74
|
-
return requireHostBridgeMethod("formatDate")(date);
|
|
75
|
-
};
|
|
76
|
-
var hostGetTimepickerOptions = () => {
|
|
77
|
-
return requireHostBridgeMethod("getTimepickerOptions")();
|
|
78
|
-
};
|
|
79
|
-
var hostGetLocale = () => {
|
|
80
|
-
return requireHostBridgeMethod("getLocale")();
|
|
81
|
-
};
|
|
82
|
-
//#endregion
|
|
83
|
-
//#region src/utils/collections.ts
|
|
84
|
-
/**
|
|
85
|
-
* Collections utility for managing client-side collections of models
|
|
86
|
-
*/
|
|
87
|
-
/**
|
|
88
|
-
* Generates a unique client-side ID string
|
|
89
|
-
* @param {string} [prefix='client'] - Optional prefix for the generated ID
|
|
90
|
-
* @returns {string} A unique ID string in the format: prefix_timestamp_randomString
|
|
91
|
-
*/
|
|
92
|
-
var generateId = (prefix = "client") => {
|
|
93
|
-
return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
94
|
-
};
|
|
95
|
-
/**
|
|
96
|
-
* Normalize a collection by adding _id to each item that doesn't have one
|
|
97
|
-
* @param {Array} collection - Array of items
|
|
98
|
-
* @returns {Array} - Normalized collection with _id on each item
|
|
99
|
-
*/
|
|
100
|
-
var normalizeCollection = (collection = []) => {
|
|
101
|
-
return collection.map((item) => {
|
|
102
|
-
if (item._id) return item;
|
|
103
|
-
return {
|
|
104
|
-
...item,
|
|
105
|
-
_id: generateId()
|
|
106
|
-
};
|
|
107
|
-
});
|
|
108
|
-
};
|
|
109
|
-
/**
|
|
110
|
-
* Create a new collection item
|
|
111
|
-
* @param {Object} itemData - Initial data for the new item
|
|
112
|
-
* @returns {Object} - The new item
|
|
113
|
-
*/
|
|
114
|
-
var createItem = (itemData = {}) => {
|
|
115
|
-
return {
|
|
116
|
-
...itemData,
|
|
117
|
-
_id: generateId()
|
|
118
|
-
};
|
|
119
|
-
};
|
|
120
|
-
/**
|
|
121
|
-
* Duplicate an existing item in a collection
|
|
122
|
-
* @param {Array} collection - Current collection
|
|
123
|
-
* @param {Object} item - Item to duplicate
|
|
124
|
-
* @param {Function} transformCallback - Optional callback to transform the duplicated item
|
|
125
|
-
* @returns {Object} - Updated collection with duplicated item
|
|
126
|
-
*/
|
|
127
|
-
var duplicateItem = (collection = [], item, transformCallback = null) => {
|
|
128
|
-
const duplicatedItem = {
|
|
129
|
-
...item,
|
|
130
|
-
_id: generateId()
|
|
131
|
-
};
|
|
132
|
-
const finalItem = transformCallback ? transformCallback(duplicatedItem) : duplicatedItem;
|
|
133
|
-
return [...collection, finalItem];
|
|
134
|
-
};
|
|
135
|
-
/**
|
|
136
|
-
* Delete a item from a collection
|
|
137
|
-
* @param {Array} collection - Current collection
|
|
138
|
-
* @param {Object} item - Item to delete
|
|
139
|
-
* @returns {Object} - Updated collection without the deleted item
|
|
140
|
-
*/
|
|
141
|
-
var deleteItem = (collection = [], itemToDelete) => {
|
|
142
|
-
return collection.filter((item) => {
|
|
143
|
-
return item._id !== itemToDelete._id;
|
|
144
|
-
});
|
|
145
|
-
};
|
|
146
|
-
/**
|
|
147
|
-
* Update an item in a collection
|
|
148
|
-
* @param {Array} collection - Current collection
|
|
149
|
-
* @param {Object} item - Item to update
|
|
150
|
-
* @param {Object} updates - Updates to apply to the item
|
|
151
|
-
* @returns {Object} - Updated collection with updated item
|
|
152
|
-
*/
|
|
153
|
-
var updateItem = (collection = [], itemToUpdate, updates) => {
|
|
154
|
-
return collection.map((item) => {
|
|
155
|
-
if (item._id === itemToUpdate._id) return {
|
|
156
|
-
...item,
|
|
157
|
-
...updates,
|
|
158
|
-
_id: item._id
|
|
159
|
-
};
|
|
160
|
-
return item;
|
|
161
|
-
});
|
|
162
|
-
};
|
|
163
|
-
/**
|
|
164
|
-
* Move an item to a new position in a collection
|
|
165
|
-
* @param {Array} collection - Current collection
|
|
166
|
-
* @param {Object} fromItem - Item to move
|
|
167
|
-
* @param {Object} toItem - Item to move to (insert before this item)
|
|
168
|
-
* @returns {Array} - Updated collection with moved item
|
|
169
|
-
*/
|
|
170
|
-
var moveItem = (collection = [], fromItem, toItem) => {
|
|
171
|
-
if (fromItem._id === toItem._id) return collection;
|
|
172
|
-
const fromIndex = collection.findIndex((item) => {
|
|
173
|
-
return item._id === fromItem._id;
|
|
174
|
-
});
|
|
175
|
-
const toIndex = collection.findIndex((item) => {
|
|
176
|
-
return item._id === toItem._id;
|
|
177
|
-
});
|
|
178
|
-
if (fromIndex === -1 || toIndex === -1) return collection;
|
|
179
|
-
const newCollection = [...collection];
|
|
180
|
-
const [movedItem] = newCollection.splice(fromIndex, 1);
|
|
181
|
-
newCollection.splice(toIndex, 0, movedItem);
|
|
182
|
-
return newCollection;
|
|
183
|
-
};
|
|
184
|
-
/**
|
|
185
|
-
* Find an item in a collection by _id
|
|
186
|
-
* @param {Array} collection - Collection to search
|
|
187
|
-
* @param {string} _id - Client-side ID to find
|
|
188
|
-
* @returns {Object|null} - Found model or null
|
|
189
|
-
*/
|
|
190
|
-
var findItemById = (collection = [], _id) => {
|
|
191
|
-
return collection.find((item) => {
|
|
192
|
-
return item._id === _id;
|
|
193
|
-
}) || null;
|
|
194
|
-
};
|
|
195
|
-
/**
|
|
196
|
-
* Get all items that have server-side IDs (existing items)
|
|
197
|
-
* @param {Array} collection - Collection to filter
|
|
198
|
-
* @returns {Array} - Items with server-side IDs
|
|
199
|
-
*/
|
|
200
|
-
var getExistingItems = (collection = []) => {
|
|
201
|
-
return collection.filter((item) => {
|
|
202
|
-
return item.id;
|
|
203
|
-
});
|
|
204
|
-
};
|
|
205
|
-
/**
|
|
206
|
-
* Get all items that don't have server-side IDs (new items)
|
|
207
|
-
* @param {Array} collection - Collection to filter
|
|
208
|
-
* @returns {Array} - Items without server-side IDs
|
|
209
|
-
*/
|
|
210
|
-
var getNewItems = (collection = []) => {
|
|
211
|
-
return collection.filter((item) => {
|
|
212
|
-
return !item.id;
|
|
213
|
-
});
|
|
214
|
-
};
|
|
215
|
-
var findRecursive = (items, predicate, optionsKey = "options") => {
|
|
216
|
-
for (const item of items) {
|
|
217
|
-
if (item[optionsKey] && Array.isArray(item[optionsKey])) {
|
|
218
|
-
const result = findRecursive(item[optionsKey], predicate, optionsKey);
|
|
219
|
-
if (result) return result;
|
|
220
|
-
}
|
|
221
|
-
if (predicate(item)) return item;
|
|
222
|
-
}
|
|
223
|
-
return null;
|
|
224
|
-
};
|
|
225
|
-
/**
|
|
226
|
-
* Creates a deep clone of a value using JSON serialization
|
|
227
|
-
* @param {any} value - The value to clone
|
|
228
|
-
* @returns {any} A deep clone of the input value, or undefined if input is undefined
|
|
229
|
-
*/
|
|
230
|
-
var clone = function(value) {
|
|
231
|
-
if (value === void 0) return;
|
|
232
|
-
return JSON.parse(JSON.stringify(value));
|
|
233
|
-
};
|
|
234
|
-
//#endregion
|
|
235
|
-
//#region src/utils/forms.ts
|
|
236
|
-
var nl2br = (str) => {
|
|
237
|
-
return str.replace(/\n/g, "<br>");
|
|
238
|
-
};
|
|
239
|
-
var getErrorHeading = (error) => {
|
|
240
|
-
if (error.response?.statusText) return error.response.statusText;
|
|
241
|
-
if (error.message?.includes("Network Error")) return "Network Error";
|
|
242
|
-
if (error.message?.includes("timeout")) return "Request Timeout";
|
|
243
|
-
return "An error has occurred";
|
|
244
|
-
};
|
|
245
|
-
var getErrorText = (error) => {
|
|
246
|
-
if (error.response?.data?.message) return error.response.data.message;
|
|
247
|
-
if (error.response?.data?.error) return error.response.data.error;
|
|
248
|
-
if (error.message) return error.message;
|
|
249
|
-
return String(error);
|
|
250
|
-
};
|
|
251
|
-
var getErrorTrace = (error, maxTraceLines = 5) => {
|
|
252
|
-
const traces = [];
|
|
253
|
-
const file1 = error.response?.data?.file;
|
|
254
|
-
const line1 = error.response?.data?.line;
|
|
255
|
-
if (file1 && line1) traces.push(`${file1}:${line1}`);
|
|
256
|
-
const traceArray = error.response?.data?.trace || [];
|
|
257
|
-
for (let i = 0; i < Math.min(maxTraceLines, traceArray.length); i++) {
|
|
258
|
-
const traceItem = traceArray[i];
|
|
259
|
-
if (traceItem?.file && traceItem?.line) traces.push(`${traceItem.file}:${traceItem.line}`);
|
|
260
|
-
}
|
|
261
|
-
if (error.stack && traces.length === 0) traces.push(error.stack);
|
|
262
|
-
return {
|
|
263
|
-
traces,
|
|
264
|
-
traceAsString: traces.map(nl2br).join("<br>")
|
|
265
|
-
};
|
|
266
|
-
};
|
|
267
|
-
var getErrorMessage = function(error, maxTraceLines = 5) {
|
|
268
|
-
const { traces, traceAsString } = getErrorTrace(error, maxTraceLines);
|
|
269
|
-
return {
|
|
270
|
-
heading: getErrorHeading(error),
|
|
271
|
-
text: getErrorText(error),
|
|
272
|
-
trace: traceAsString,
|
|
273
|
-
traceAsString,
|
|
274
|
-
traceAsArray: traces
|
|
275
|
-
};
|
|
276
|
-
};
|
|
277
|
-
//#endregion
|
|
278
|
-
//#region src/utils/string.ts
|
|
279
|
-
/**
|
|
280
|
-
* Convert a string to a handle format
|
|
281
|
-
* @param {string} sourceValue - The source string to convert
|
|
282
|
-
* @param {string} handleCasing - The casing format ('camelCase', 'pascal', 'snake', 'kebab')
|
|
283
|
-
* @param {boolean} allowNonAlphaStart - Whether to allow non-alphabetic characters at the start
|
|
284
|
-
* @returns {string} The generated handle
|
|
285
|
-
*/
|
|
286
|
-
var generateHandle = function(sourceValue, handleCasing = "camelCase", allowNonAlphaStart = false) {
|
|
287
|
-
let handle = sourceValue.replace("/<(.*?)>/g", "");
|
|
288
|
-
handle = handle.replace(/['"'""\[\]\(\)\{\}:]/g, "");
|
|
289
|
-
handle = handle.toLowerCase();
|
|
290
|
-
handle = window.Craft.asciiString(handle);
|
|
291
|
-
if (!allowNonAlphaStart) handle = handle.replace(/^[^a-z]+/, "");
|
|
292
|
-
const words = window.Craft.filterArray(handle.split(/[^a-z0-9]+/));
|
|
293
|
-
handle = "";
|
|
294
|
-
if (handleCasing === "snake") return words.join("_");
|
|
295
|
-
if (handleCasing === "kebab") return words.join("-");
|
|
296
|
-
for (let i = 0; i < words.length; i++) if (handleCasing !== "pascal" && i === 0) handle += words[i];
|
|
297
|
-
else handle += words[i].charAt(0).toUpperCase() + words[i].substr(1);
|
|
298
|
-
return handle;
|
|
299
|
-
};
|
|
300
|
-
/**
|
|
301
|
-
* Find a unique handle by checking against reserved handles and adding suffixes
|
|
302
|
-
* @param {string} baseHandle - The base handle to check
|
|
303
|
-
* @param {Array} allReservedHandles - Array of all reserved handles to check against (static + dynamic)
|
|
304
|
-
* @returns {string} A unique handle
|
|
305
|
-
*/
|
|
306
|
-
var findUniqueHandle = (baseHandle, allReservedHandles = []) => {
|
|
307
|
-
if (!baseHandle) return "";
|
|
308
|
-
let handle = baseHandle;
|
|
309
|
-
let counter = 1;
|
|
310
|
-
while (allReservedHandles.includes(handle)) {
|
|
311
|
-
handle = `${baseHandle}${counter}`;
|
|
312
|
-
counter++;
|
|
313
|
-
}
|
|
314
|
-
return handle;
|
|
315
|
-
};
|
|
316
|
-
//#endregion
|
|
317
|
-
//#region src/utils/handle.ts
|
|
318
|
-
/** Minimal dot/bracket path getter so this stays dependency-free (no lodash). */
|
|
319
|
-
var getByPath = (source, path) => {
|
|
320
|
-
if (source == null || typeof source !== "object" || !path) return;
|
|
321
|
-
const segments = path.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
|
|
322
|
-
let current = source;
|
|
323
|
-
for (const segment of segments) {
|
|
324
|
-
if (current == null || typeof current !== "object") return;
|
|
325
|
-
current = current[segment];
|
|
326
|
-
}
|
|
327
|
-
return current;
|
|
328
|
-
};
|
|
329
|
-
var normalizeHandleSource = (value) => {
|
|
330
|
-
if (value == null) return "";
|
|
331
|
-
return String(value).replace(/\{[^}]*\}/g, " ").replace(/\s+/g, " ").trim();
|
|
332
|
-
};
|
|
333
|
-
/**
|
|
334
|
-
* Resolve dynamic reserved handles from other field values (e.g. a sibling field whose
|
|
335
|
-
* value would collide once slugified).
|
|
336
|
-
*/
|
|
337
|
-
var getDynamicReservedHandles = (values, reservedFieldValues = []) => {
|
|
338
|
-
const dynamicHandles = [];
|
|
339
|
-
reservedFieldValues.forEach((fieldPath) => {
|
|
340
|
-
const value = getByPath(values, fieldPath);
|
|
341
|
-
if (value && typeof value === "string") {
|
|
342
|
-
const handleValue = generateHandle(normalizeHandleSource(value));
|
|
343
|
-
if (handleValue) dynamicHandles.push(handleValue);
|
|
344
|
-
}
|
|
345
|
-
});
|
|
346
|
-
return dynamicHandles;
|
|
347
|
-
};
|
|
348
|
-
var truncateHandle = (handle, maxLength) => {
|
|
349
|
-
if (!Number.isFinite(maxLength)) return handle;
|
|
350
|
-
const length = Math.max(Number(maxLength), 0);
|
|
351
|
-
return handle.slice(0, length);
|
|
352
|
-
};
|
|
353
|
-
var findUniqueHandleWithinMaxLength = (baseHandle, reservedHandles = [], maxLength) => {
|
|
354
|
-
if (!baseHandle) return "";
|
|
355
|
-
if (!Number.isFinite(maxLength)) return findUniqueHandle(baseHandle, reservedHandles);
|
|
356
|
-
const normalizedReserved = new Set((reservedHandles || []).map((handle) => {
|
|
357
|
-
return String(handle || "").toLowerCase();
|
|
358
|
-
}));
|
|
359
|
-
const truncatedBase = truncateHandle(baseHandle, maxLength);
|
|
360
|
-
if (!truncatedBase) return "";
|
|
361
|
-
if (!normalizedReserved.has(truncatedBase.toLowerCase())) return truncatedBase;
|
|
362
|
-
let suffix = 1;
|
|
363
|
-
while (suffix < 1e4) {
|
|
364
|
-
const suffixText = String(suffix);
|
|
365
|
-
const baseMaxLength = Math.max(Number(maxLength) - suffixText.length, 0);
|
|
366
|
-
const truncatedWithSuffix = `${truncatedBase.slice(0, baseMaxLength)}${suffixText}`;
|
|
367
|
-
if (!normalizedReserved.has(truncatedWithSuffix.toLowerCase())) return truncatedWithSuffix;
|
|
368
|
-
suffix += 1;
|
|
369
|
-
}
|
|
370
|
-
return truncatedBase;
|
|
371
|
-
};
|
|
372
|
-
/**
|
|
373
|
-
* Generate a unique handle from a human-readable source value, honouring reserved handles
|
|
374
|
-
* (static + dynamically derived from other field values) and an optional max length.
|
|
375
|
-
*/
|
|
376
|
-
var buildUniqueHandleFromSource = ({ sourceValue, values = {}, reservedHandles = [], reservedFieldValues = [], maxLength }) => {
|
|
377
|
-
const baseHandle = generateHandle(normalizeHandleSource(sourceValue));
|
|
378
|
-
const dynamicHandles = getDynamicReservedHandles(values, reservedFieldValues);
|
|
379
|
-
return findUniqueHandleWithinMaxLength(baseHandle, [...reservedHandles, ...dynamicHandles], maxLength);
|
|
380
|
-
};
|
|
381
|
-
//#endregion
|
|
382
|
-
//#region src/utils/markdown.ts
|
|
383
|
-
/**
|
|
384
|
-
* Initialize markdown-it instance with secure defaults and common options
|
|
385
|
-
* - HTML is disabled for security
|
|
386
|
-
* - Links are auto-detected
|
|
387
|
-
* - Typography features like smart quotes are enabled
|
|
388
|
-
* - Line breaks are converted to <br> tags
|
|
389
|
-
*/
|
|
390
|
-
var md = new MarkdownIt({
|
|
391
|
-
html: false,
|
|
392
|
-
linkify: true,
|
|
393
|
-
typographer: true,
|
|
394
|
-
breaks: true
|
|
395
|
-
});
|
|
396
|
-
/**
|
|
397
|
-
* Renders markdown content as block-level HTML
|
|
398
|
-
* Includes block elements like headers, paragraphs, lists etc.
|
|
399
|
-
*
|
|
400
|
-
* @param content - The markdown string to render
|
|
401
|
-
* @returns Rendered HTML string, or empty string if no content provided
|
|
402
|
-
*/
|
|
403
|
-
var renderMarkdown = (content) => {
|
|
404
|
-
if (!content) return "";
|
|
405
|
-
return md.render(content);
|
|
406
|
-
};
|
|
407
|
-
/**
|
|
408
|
-
* Renders markdown content as inline HTML only
|
|
409
|
-
* Excludes block-level elements, only processes inline markdown syntax
|
|
410
|
-
*
|
|
411
|
-
* @param content - The markdown string to render
|
|
412
|
-
* @returns Rendered HTML string, or empty string if no content provided
|
|
413
|
-
*/
|
|
414
|
-
var renderInlineMarkdown = (content) => {
|
|
415
|
-
if (!content) return "";
|
|
416
|
-
return md.renderInline(content);
|
|
417
|
-
};
|
|
418
|
-
//#endregion
|
|
419
|
-
//#region src/utils/promises.ts
|
|
420
|
-
/**
|
|
421
|
-
* Creates a function that ensures a promise takes at least a minimum amount of time to resolve
|
|
422
|
-
*
|
|
423
|
-
* @param ms - The minimum time in milliseconds that the promise should take
|
|
424
|
-
* @returns A function that wraps a promise or promise-returning function
|
|
425
|
-
* @example
|
|
426
|
-
* ```ts
|
|
427
|
-
* const slowFetch = takeAtLeast(1000)(fetch('https://api.example.com'));
|
|
428
|
-
* // Will take at least 1 second even if the fetch is faster
|
|
429
|
-
* ```
|
|
430
|
-
*/
|
|
431
|
-
var takeAtLeast = function(ms) {
|
|
432
|
-
/**
|
|
433
|
-
* Wraps a promise or promise-returning function to ensure minimum execution time
|
|
434
|
-
*
|
|
435
|
-
* @param promiseOrFn - A promise or a function that returns a promise
|
|
436
|
-
* @returns A promise that resolves with the original promise's value after at least `ms` milliseconds
|
|
437
|
-
* @throws Will throw if the original promise rejects
|
|
438
|
-
*/
|
|
439
|
-
return function(promiseOrFn) {
|
|
440
|
-
const promise = typeof promiseOrFn === "function" ? promiseOrFn() : promiseOrFn;
|
|
441
|
-
const delay = new Promise((resolve) => {
|
|
442
|
-
return setTimeout(resolve, ms);
|
|
443
|
-
});
|
|
444
|
-
return Promise.allSettled([promise, delay]).then((results) => {
|
|
445
|
-
const [promiseResult] = results;
|
|
446
|
-
if (promiseResult.status === "rejected") throw promiseResult.reason;
|
|
447
|
-
return promiseResult.value;
|
|
448
|
-
});
|
|
449
|
-
};
|
|
450
|
-
};
|
|
451
|
-
//#endregion
|
|
452
|
-
//#region src/utils/query.ts
|
|
453
|
-
var getQueryParam = (key) => {
|
|
454
|
-
return new URLSearchParams(window.location.search).get(key);
|
|
455
|
-
};
|
|
456
|
-
var setQueryParam = (key, value) => {
|
|
457
|
-
const url = new URL(window.location.href);
|
|
458
|
-
url.searchParams.set(key, value);
|
|
459
|
-
window.history.replaceState({}, "", url.toString());
|
|
460
|
-
};
|
|
461
|
-
//#endregion
|
|
462
|
-
export { buildUniqueHandleFromSource, clone, createItem, deleteItem, duplicateItem, findItemById, findRecursive, findUniqueHandle, generateHandle, generateId, getDynamicReservedHandles, getErrorMessage, getExistingItems, getHostBridge, getNewItems, getPortalClassName, getPortalContainer, getPortalMountNode, getPortalTargetForAppend, getQueryParam, getShadowRootSelectors, hostFormatDate, hostGetLocale, hostGetTimepickerOptions, hostOpenElementSelector, hostRequest, md, moveItem, normalizeCollection, renderInlineMarkdown, renderMarkdown, setHostBridge, setPortalClassName, setPortalContainer, setQueryParam, setShadowRootSelectors, takeAtLeast, updateItem };
|
|
463
|
-
|
|
464
|
-
//# sourceMappingURL=plugin-kit-core.es.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-kit-core.es.js","names":[],"sources":["../src/host/portal.ts","../src/host/hostBridge.ts","../src/utils/collections.ts","../src/utils/forms.ts","../src/utils/string.ts","../src/utils/handle.ts","../src/utils/markdown.ts","../src/utils/promises.ts","../src/utils/query.ts"],"sourcesContent":["export type PortalContainer = HTMLElement | ShadowRoot | null;\n\nexport type PortalContainerRef = {\n current: PortalContainer;\n};\n\nexport type PortalContainerInput = PortalContainer | PortalContainerRef;\n\nlet defaultPortalClassName: string | undefined;\nlet defaultPortalContainer: PortalContainer | undefined;\nlet defaultShadowRootSelectors: string[] = ['[data-pk-shadow-root]'];\n\nexport const setPortalClassName = (className: string): void => {\n defaultPortalClassName = className.trim() || undefined;\n};\n\nexport const getPortalClassName = (className?: string): string | undefined => {\n return (className ?? defaultPortalClassName)?.trim() || undefined;\n};\n\nconst resolvePortalContainer = (container?: PortalContainerInput): PortalContainer | undefined => {\n if (!container) {\n return undefined;\n }\n\n if (typeof container === 'object' && 'current' in container) {\n return container.current ?? undefined;\n }\n\n return container ?? undefined;\n};\n\nexport const setPortalContainer = (container: PortalContainerInput): void => {\n defaultPortalContainer = resolvePortalContainer(container);\n};\n\nexport const getPortalContainer = (container?: PortalContainerInput): PortalContainer | undefined => {\n return resolvePortalContainer(container) ?? defaultPortalContainer;\n};\n\nexport const setShadowRootSelectors = (selectors: string[]): void => {\n const normalizedSelectors = (selectors || [])\n .map((selector) => selector.trim())\n .filter(Boolean);\n\n defaultShadowRootSelectors = normalizedSelectors.length > 0\n ? normalizedSelectors\n : ['[data-pk-shadow-root]'];\n};\n\nexport const getShadowRootSelectors = (): string[] => {\n return defaultShadowRootSelectors;\n};\n\n/**\n * Resolve where floating content should be portaled.\n * Falls back to document.body when no container is configured.\n */\nexport const getPortalMountNode = (container?: PortalContainerInput): HTMLElement => {\n const resolved = getPortalContainer(container);\n\n if (resolved instanceof HTMLElement) {\n return resolved;\n }\n\n if (resolved instanceof ShadowRoot) {\n const target = getShadowRootSelectors()\n .map((selector) => resolved.querySelector<HTMLElement>(selector))\n .find(Boolean)\n ?? (resolved.host instanceof HTMLElement ? resolved.host : undefined);\n\n if (target) {\n return target;\n }\n }\n\n return document.body;\n};\n\nexport const getPortalTargetForAppend = (): HTMLElement | undefined => {\n const container = getPortalContainer();\n if (!container) {\n return undefined;\n }\n\n if (container instanceof HTMLElement) {\n return container;\n }\n\n const shadowRoot = container;\n return getShadowRootSelectors()\n .map((selector) => shadowRoot.querySelector<HTMLElement>(selector))\n .find(Boolean)\n ?? (shadowRoot.host instanceof HTMLElement ? shadowRoot.host : undefined);\n};\n","export type HostRequestMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n\nexport type HostRequestConfig = {\n data?: Record<string, unknown>;\n [key: string]: unknown;\n};\n\nexport type HostSelectedElement = {\n id?: number;\n siteId?: number;\n label?: string;\n url?: string;\n [key: string]: unknown;\n};\n\nexport type HostElementSelectorOptions = {\n storageKey: string;\n sources?: string[];\n criteria?: Record<string, unknown>;\n multiSelect?: boolean;\n limit?: number | null;\n defaultSiteId?: number;\n autoFocusSearchBox?: boolean;\n showSiteMenu?: boolean;\n onShow?: () => void;\n onSelect: (elements: HostSelectedElement[]) => void;\n closeOtherModals?: boolean;\n};\n\nexport type PluginKitHostBridge = {\n request: <T = unknown>(method: HostRequestMethod, action: string, config?: HostRequestConfig) => Promise<T>;\n openElementSelector: (elementType: string, options: HostElementSelectorOptions) => void;\n formatDate: (date: Date) => string;\n getTimepickerOptions: () => Record<string, unknown>;\n getLocale: () => string;\n};\n\nlet hostBridge: Partial<PluginKitHostBridge> = {};\n\nexport const setHostBridge = (bridge: Partial<PluginKitHostBridge> = {}): void => {\n hostBridge = {\n ...hostBridge,\n ...bridge,\n };\n};\n\nexport const getHostBridge = (): Partial<PluginKitHostBridge> => {\n return hostBridge;\n};\n\nconst requireHostBridgeMethod = <K extends keyof PluginKitHostBridge>(\n methodName: K,\n): NonNullable<PluginKitHostBridge[K]> => {\n const method = hostBridge[methodName];\n\n if (!method) {\n throw new Error(`Plugin Kit host bridge method \"${String(methodName)}\" is required but was not configured.`);\n }\n\n return method as NonNullable<PluginKitHostBridge[K]>;\n};\n\nexport const hostRequest = async <T = unknown>(\n method: HostRequestMethod,\n action: string,\n config?: HostRequestConfig,\n): Promise<T> => {\n const request = requireHostBridgeMethod('request');\n return request(method, action, config);\n};\n\nexport const hostOpenElementSelector = (elementType: string, options: HostElementSelectorOptions): void => {\n const openElementSelector = requireHostBridgeMethod('openElementSelector');\n openElementSelector(elementType, options);\n};\n\nexport const hostFormatDate = (date: Date): string => {\n const formatDate = requireHostBridgeMethod('formatDate');\n return formatDate(date);\n};\n\nexport const hostGetTimepickerOptions = (): Record<string, unknown> => {\n const getTimepickerOptions = requireHostBridgeMethod('getTimepickerOptions');\n return getTimepickerOptions();\n};\n\nexport const hostGetLocale = (): string => {\n const getLocale = requireHostBridgeMethod('getLocale');\n return getLocale();\n};\n","/**\n * Collections utility for managing client-side collections of models\n */\n\n/**\n * Generates a unique client-side ID string\n * @param {string} [prefix='client'] - Optional prefix for the generated ID\n * @returns {string} A unique ID string in the format: prefix_timestamp_randomString\n */\nexport const generateId = (prefix = 'client'): string => {\n return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n};\n\n/**\n * Normalize a collection by adding _id to each item that doesn't have one\n * @param {Array} collection - Array of items\n * @returns {Array} - Normalized collection with _id on each item\n */\nexport const normalizeCollection = (collection: any[] = []): any[] => {\n return collection.map((item) => {\n // If item already has _id, keep it (for client-side created items)\n if (item._id) {\n return item;\n }\n\n // Generate a client-side internal ID for server-side items\n return {\n ...item,\n _id: generateId(),\n };\n });\n};\n\n/**\n * Create a new collection item\n * @param {Object} itemData - Initial data for the new item\n * @returns {Object} - The new item\n */\nexport const createItem = (itemData: any = {}): any => {\n return {\n ...itemData,\n _id: generateId(),\n };\n};\n\n/**\n * Duplicate an existing item in a collection\n * @param {Array} collection - Current collection\n * @param {Object} item - Item to duplicate\n * @param {Function} transformCallback - Optional callback to transform the duplicated item\n * @returns {Object} - Updated collection with duplicated item\n */\nexport const duplicateItem = (collection: any[] = [], item: any, transformCallback: ((item: any) => any) | null = null): any[] => {\n // Create base duplicated item\n const duplicatedItem = {\n ...item,\n // Generate new client-side ID\n _id: generateId(),\n };\n\n // Apply custom transformation if provided\n const finalItem = transformCallback ? transformCallback(duplicatedItem) : duplicatedItem;\n\n return [...collection, finalItem];\n};\n\n/**\n * Delete a item from a collection\n * @param {Array} collection - Current collection\n * @param {Object} item - Item to delete\n * @returns {Object} - Updated collection without the deleted item\n */\nexport const deleteItem = (collection: any[] = [], itemToDelete: any): any[] => {\n return collection.filter((item) => { return item._id !== itemToDelete._id; });\n};\n\n/**\n * Update an item in a collection\n * @param {Array} collection - Current collection\n * @param {Object} item - Item to update\n * @param {Object} updates - Updates to apply to the item\n * @returns {Object} - Updated collection with updated item\n */\nexport const updateItem = (collection: any[] = [], itemToUpdate: any, updates: any): any[] => {\n return collection.map((item) => {\n if (item._id === itemToUpdate._id) {\n return {\n ...item,\n ...updates,\n // Preserve the _id\n _id: item._id,\n };\n }\n return item;\n });\n};\n\n/**\n * Move an item to a new position in a collection\n * @param {Array} collection - Current collection\n * @param {Object} fromItem - Item to move\n * @param {Object} toItem - Item to move to (insert before this item)\n * @returns {Array} - Updated collection with moved item\n */\nexport const moveItem = (collection: any[] = [], fromItem: any, toItem: any): any[] => {\n if (fromItem._id === toItem._id) {\n return collection;\n }\n\n const fromIndex = collection.findIndex((item) => { return item._id === fromItem._id; });\n const toIndex = collection.findIndex((item) => { return item._id === toItem._id; });\n\n if (fromIndex === -1 || toIndex === -1) {\n return collection;\n }\n\n const newCollection = [...collection];\n const [movedItem] = newCollection.splice(fromIndex, 1);\n newCollection.splice(toIndex, 0, movedItem);\n\n return newCollection;\n};\n\n/**\n * Find an item in a collection by _id\n * @param {Array} collection - Collection to search\n * @param {string} _id - Client-side ID to find\n * @returns {Object|null} - Found model or null\n */\nexport const findItemById = (collection: any[] = [], _id: string): any | null => {\n return collection.find((item) => { return item._id === _id; }) || null;\n};\n\n/**\n * Get all items that have server-side IDs (existing items)\n * @param {Array} collection - Collection to filter\n * @returns {Array} - Items with server-side IDs\n */\nexport const getExistingItems = (collection: any[] = []): any[] => {\n return collection.filter((item) => { return item.id; });\n};\n\n/**\n * Get all items that don't have server-side IDs (new items)\n * @param {Array} collection - Collection to filter\n * @returns {Array} - Items without server-side IDs\n */\nexport const getNewItems = (collection: any[] = []): any[] => {\n return collection.filter((item) => { return !item.id; });\n};\n\nexport const findRecursive = <T = any>(items: T[], predicate: (item: T) => boolean, optionsKey = 'options'): T | null => {\n for (const item of items) {\n // If this item has nested options, search recursively\n if ((item as any)[optionsKey] && Array.isArray((item as any)[optionsKey])) {\n const result = findRecursive((item as any)[optionsKey], predicate, optionsKey);\n\n if (result) {\n return result;\n }\n }\n\n // Check if this item matches the predicate\n if (predicate(item)) {\n return item;\n }\n }\n\n return null;\n};\n\n/**\n * Creates a deep clone of a value using JSON serialization\n * @param {any} value - The value to clone\n * @returns {any} A deep clone of the input value, or undefined if input is undefined\n */\nexport const clone = function <T>(value: T): T | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n return JSON.parse(JSON.stringify(value));\n};\n","interface ErrorContent {\n heading: string;\n text: string;\n trace: string;\n traceAsString: string;\n traceAsArray: string[];\n}\n\ninterface ServerError {\n response?: {\n statusText?: string;\n data?: {\n message?: string;\n error?: string;\n file?: string;\n line?: string;\n trace?: Array<{\n file?: string;\n line?: string;\n }>;\n };\n };\n message?: string;\n stack?: string;\n}\n\nconst nl2br = (str: string): string => {\n return str.replace(/\\n/g, '<br>');\n};\n\nconst getErrorHeading = (error: ServerError): string => {\n // Server error status text (e.g., \"Internal Server Error\", \"Bad Request\")\n if (error.response?.statusText) {\n return error.response.statusText;\n }\n\n // Network errors or other client-side errors\n if (error.message?.includes('Network Error')) {\n return 'Network Error';\n }\n\n if (error.message?.includes('timeout')) {\n return 'Request Timeout';\n }\n\n // Default fallback\n return 'An error has occurred';\n};\n\nconst getErrorText = (error: ServerError): string => {\n // Server-side error messages\n if (error.response?.data?.message) {\n return error.response.data.message;\n }\n\n if (error.response?.data?.error) {\n return error.response.data.error;\n }\n\n // Client-side error messages\n if (error.message) {\n return error.message;\n }\n\n // Fallback to string representation\n return String(error);\n};\n\nconst getErrorTrace = (error: ServerError, maxTraceLines: number = 5): { traces: string[], traceAsString: string } => {\n const traces: string[] = [];\n\n // Server-side file and line information\n const file1 = error.response?.data?.file;\n const line1 = error.response?.data?.line;\n\n if (file1 && line1) {\n traces.push(`${file1}:${line1}`);\n }\n\n // Server-side stack trace (up to maxTraceLines)\n const traceArray = error.response?.data?.trace || [];\n\n for (let i = 0; i < Math.min(maxTraceLines, traceArray.length); i++) {\n const traceItem = traceArray[i];\n\n if (traceItem?.file && traceItem?.line) {\n traces.push(`${traceItem.file}:${traceItem.line}`);\n }\n }\n\n // Client-side stack trace - only if there's no server-side trace\n if (error.stack && traces.length === 0) {\n traces.push(error.stack);\n }\n\n return {\n traces,\n traceAsString: traces.map(nl2br).join('<br>'),\n };\n};\n\nexport const getErrorMessage = function(error: ServerError, maxTraceLines: number = 5): ErrorContent {\n const { traces, traceAsString } = getErrorTrace(error, maxTraceLines);\n\n return {\n heading: getErrorHeading(error),\n text: getErrorText(error),\n trace: traceAsString, // Keep for backward compatibility\n traceAsString,\n traceAsArray: traces,\n };\n};\n","/**\n * Convert a string to a handle format\n * @param {string} sourceValue - The source string to convert\n * @param {string} handleCasing - The casing format ('camelCase', 'pascal', 'snake', 'kebab')\n * @param {boolean} allowNonAlphaStart - Whether to allow non-alphabetic characters at the start\n * @returns {string} The generated handle\n */\nexport const generateHandle = function(sourceValue: string, handleCasing: string = 'camelCase', allowNonAlphaStart: boolean = false): string {\n // Remove HTML tags\n let handle = sourceValue.replace('/<(.*?)>/g', '');\n\n // Remove inner-word punctuation\n\n handle = handle.replace(/['\"'\"\"\\[\\]\\(\\)\\{\\}:]/g, '');\n\n // Make it lowercase\n handle = handle.toLowerCase();\n\n // Convert extended ASCII characters to basic ASCII\n handle = (window as any).Craft.asciiString(handle);\n\n if (!allowNonAlphaStart) {\n // Handle must start with a letter\n handle = handle.replace(/^[^a-z]+/, '');\n }\n\n // Get the \"words\"\n const words = (window as any).Craft.filterArray(handle.split(/[^a-z0-9]+/));\n handle = '';\n\n if (handleCasing === 'snake') {\n return words.join('_');\n }\n\n if (handleCasing === 'kebab') {\n return words.join('-');\n }\n\n // Make it camelCase\n for (let i = 0; i < words.length; i++) {\n if (handleCasing !== 'pascal' && i === 0) {\n handle += words[i];\n } else {\n handle += words[i].charAt(0).toUpperCase() + words[i].substr(1);\n }\n }\n\n return handle;\n};\n\n/**\n * Find a unique handle by checking against reserved handles and adding suffixes\n * @param {string} baseHandle - The base handle to check\n * @param {Array} allReservedHandles - Array of all reserved handles to check against (static + dynamic)\n * @returns {string} A unique handle\n */\nexport const findUniqueHandle = (baseHandle: string, allReservedHandles: string[] = []): string => {\n if (!baseHandle) { return ''; }\n\n let handle = baseHandle;\n let counter = 1;\n\n // Keep adding numbers until we find a unique handle\n while (allReservedHandles.includes(handle)) {\n handle = `${baseHandle}${counter}`;\n counter++;\n }\n\n return handle;\n};\n","import { findUniqueHandle, generateHandle } from './string';\n\n/** Minimal dot/bracket path getter so this stays dependency-free (no lodash). */\nconst getByPath = (source: unknown, path: string): unknown => {\n if (source == null || typeof source !== 'object' || !path) {\n return undefined;\n }\n\n const segments = path.replace(/\\[(\\d+)\\]/g, '.$1').split('.').filter(Boolean);\n let current: unknown = source;\n\n for (const segment of segments) {\n if (current == null || typeof current !== 'object') {\n return undefined;\n }\n\n current = (current as Record<string, unknown>)[segment];\n }\n\n return current;\n};\n\nconst normalizeHandleSource = (value: unknown): string => {\n if (value == null) {\n return '';\n }\n\n return String(value)\n // Strip variable/template tokens so handle generation uses human text only.\n .replace(/\\{[^}]*\\}/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n};\n\n/**\n * Resolve dynamic reserved handles from other field values (e.g. a sibling field whose\n * value would collide once slugified).\n */\nexport const getDynamicReservedHandles = (\n values: Record<string, unknown>,\n reservedFieldValues: string[] = [],\n): string[] => {\n const dynamicHandles: string[] = [];\n\n reservedFieldValues.forEach((fieldPath) => {\n const value = getByPath(values, fieldPath);\n\n if (value && typeof value === 'string') {\n const handleValue = generateHandle(normalizeHandleSource(value));\n\n if (handleValue) {\n dynamicHandles.push(handleValue);\n }\n }\n });\n\n return dynamicHandles;\n};\n\nconst truncateHandle = (handle: string, maxLength?: number): string => {\n if (!Number.isFinite(maxLength)) {\n return handle;\n }\n\n const length = Math.max(Number(maxLength), 0);\n return handle.slice(0, length);\n};\n\nconst findUniqueHandleWithinMaxLength = (\n baseHandle: string,\n reservedHandles: string[] = [],\n maxLength?: number,\n): string => {\n if (!baseHandle) {\n return '';\n }\n\n if (!Number.isFinite(maxLength)) {\n return findUniqueHandle(baseHandle, reservedHandles);\n }\n\n const normalizedReserved = new Set((reservedHandles || []).map((handle) => {\n return String(handle || '').toLowerCase();\n }));\n\n const truncatedBase = truncateHandle(baseHandle, maxLength);\n if (!truncatedBase) {\n return '';\n }\n\n if (!normalizedReserved.has(truncatedBase.toLowerCase())) {\n return truncatedBase;\n }\n\n let suffix = 1;\n\n while (suffix < 10000) {\n const suffixText = String(suffix);\n const baseMaxLength = Math.max(Number(maxLength) - suffixText.length, 0);\n const truncatedWithSuffix = `${truncatedBase.slice(0, baseMaxLength)}${suffixText}`;\n\n if (!normalizedReserved.has(truncatedWithSuffix.toLowerCase())) {\n return truncatedWithSuffix;\n }\n\n suffix += 1;\n }\n\n return truncatedBase;\n};\n\n/**\n * Generate a unique handle from a human-readable source value, honouring reserved handles\n * (static + dynamically derived from other field values) and an optional max length.\n */\nexport const buildUniqueHandleFromSource = ({\n sourceValue,\n values = {},\n reservedHandles = [],\n reservedFieldValues = [],\n maxLength,\n}: {\n sourceValue: unknown;\n values?: Record<string, unknown>;\n reservedHandles?: string[];\n reservedFieldValues?: string[];\n maxLength?: number;\n}): string => {\n const baseHandle = generateHandle(normalizeHandleSource(sourceValue));\n const dynamicHandles = getDynamicReservedHandles(values, reservedFieldValues);\n const allReservedHandles = [...reservedHandles, ...dynamicHandles];\n\n return findUniqueHandleWithinMaxLength(baseHandle, allReservedHandles, maxLength);\n};\n","import MarkdownIt from 'markdown-it';\n\n/**\n * Initialize markdown-it instance with secure defaults and common options\n * - HTML is disabled for security\n * - Links are auto-detected\n * - Typography features like smart quotes are enabled\n * - Line breaks are converted to <br> tags\n */\nconst md = new MarkdownIt({\n html: false, // Disable HTML for security\n linkify: true, // Auto-detect links\n typographer: true, // Enable typographic replacements\n breaks: true, // Convert line breaks to <br>\n});\n\n/**\n * Renders markdown content as block-level HTML\n * Includes block elements like headers, paragraphs, lists etc.\n *\n * @param content - The markdown string to render\n * @returns Rendered HTML string, or empty string if no content provided\n */\nexport const renderMarkdown = (content: string): string => {\n if (!content) { return ''; }\n\n return md.render(content);\n};\n\n/**\n * Renders markdown content as inline HTML only\n * Excludes block-level elements, only processes inline markdown syntax\n *\n * @param content - The markdown string to render\n * @returns Rendered HTML string, or empty string if no content provided\n */\nexport const renderInlineMarkdown = (content: string): string => {\n if (!content) { return ''; }\n\n return md.renderInline(content);\n};\n\n/**\n * Export the configured markdown-it instance for direct usage\n * Allows access to the full markdown-it API if needed\n */\nexport { md };\n","/**\n * Creates a function that ensures a promise takes at least a minimum amount of time to resolve\n *\n * @param ms - The minimum time in milliseconds that the promise should take\n * @returns A function that wraps a promise or promise-returning function\n * @example\n * ```ts\n * const slowFetch = takeAtLeast(1000)(fetch('https://api.example.com'));\n * // Will take at least 1 second even if the fetch is faster\n * ```\n */\nexport const takeAtLeast = function(ms: number) {\n /**\n * Wraps a promise or promise-returning function to ensure minimum execution time\n *\n * @param promiseOrFn - A promise or a function that returns a promise\n * @returns A promise that resolves with the original promise's value after at least `ms` milliseconds\n * @throws Will throw if the original promise rejects\n */\n return function <T>(promiseOrFn: Promise<T> | (() => Promise<T>)): Promise<T> {\n const promise = typeof promiseOrFn === 'function' ? promiseOrFn() : promiseOrFn;\n const delay = new Promise<void>((resolve) => { return setTimeout(resolve, ms); });\n\n return Promise.allSettled([promise, delay]).then((results) => {\n const [promiseResult] = results;\n\n if (promiseResult.status === 'rejected') {\n throw promiseResult.reason;\n }\n\n return promiseResult.value;\n });\n };\n};\n","export const getQueryParam = (key: string): string | null => {\n const urlParams = new URLSearchParams(window.location.search);\n return urlParams.get(key);\n};\n\nexport const setQueryParam = (key: string, value: string): void => {\n const url = new URL(window.location.href);\n url.searchParams.set(key, value);\n window.history.replaceState({}, '', url.toString());\n};\n"],"mappings":"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"}
|