@pro-laico/payload-icons 0.0.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.
Files changed (119) hide show
  1. package/LICENSE.md +22 -0
  2. package/README.md +9 -0
  3. package/dist/cache/index.d.ts +9 -0
  4. package/dist/cache/index.d.ts.map +1 -0
  5. package/dist/cache/index.js +96 -0
  6. package/dist/cache/index.js.map +1 -0
  7. package/dist/collections/Icon.d.ts +14 -0
  8. package/dist/collections/Icon.d.ts.map +1 -0
  9. package/dist/collections/Icon.js +96 -0
  10. package/dist/collections/Icon.js.map +1 -0
  11. package/dist/collections/IconRequest.d.ts +30 -0
  12. package/dist/collections/IconRequest.d.ts.map +1 -0
  13. package/dist/collections/IconRequest.js +82 -0
  14. package/dist/collections/IconRequest.js.map +1 -0
  15. package/dist/collections/IconSet.d.ts +43 -0
  16. package/dist/collections/IconSet.d.ts.map +1 -0
  17. package/dist/collections/IconSet.js +168 -0
  18. package/dist/collections/IconSet.js.map +1 -0
  19. package/dist/components/Icon.d.ts +35 -0
  20. package/dist/components/Icon.d.ts.map +1 -0
  21. package/dist/components/Icon.js +56 -0
  22. package/dist/components/Icon.js.map +1 -0
  23. package/dist/components/admin/clearIconRequests.d.ts +14 -0
  24. package/dist/components/admin/clearIconRequests.d.ts.map +1 -0
  25. package/dist/components/admin/clearIconRequests.js +52 -0
  26. package/dist/components/admin/clearIconRequests.js.map +1 -0
  27. package/dist/components/admin/iconPreview.d.ts +7 -0
  28. package/dist/components/admin/iconPreview.d.ts.map +1 -0
  29. package/dist/components/admin/iconPreview.js +49 -0
  30. package/dist/components/admin/iconPreview.js.map +1 -0
  31. package/dist/components/admin/iconRowLabel.d.ts +8 -0
  32. package/dist/components/admin/iconRowLabel.d.ts.map +1 -0
  33. package/dist/components/admin/iconRowLabel.js +28 -0
  34. package/dist/components/admin/iconRowLabel.js.map +1 -0
  35. package/dist/components/admin/iconUsagePanel.client.d.ts +26 -0
  36. package/dist/components/admin/iconUsagePanel.client.d.ts.map +1 -0
  37. package/dist/components/admin/iconUsagePanel.client.js +290 -0
  38. package/dist/components/admin/iconUsagePanel.client.js.map +1 -0
  39. package/dist/components/admin/iconUsagePanel.d.ts +12 -0
  40. package/dist/components/admin/iconUsagePanel.d.ts.map +1 -0
  41. package/dist/components/admin/iconUsagePanel.js +68 -0
  42. package/dist/components/admin/iconUsagePanel.js.map +1 -0
  43. package/dist/hooks/formatSVG.d.ts +22 -0
  44. package/dist/hooks/formatSVG.d.ts.map +1 -0
  45. package/dist/hooks/formatSVG.js +270 -0
  46. package/dist/hooks/formatSVG.js.map +1 -0
  47. package/dist/index.d.ts +7 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +10 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/lib/activeField.d.ts +24 -0
  52. package/dist/lib/activeField.d.ts.map +1 -0
  53. package/dist/lib/activeField.js +71 -0
  54. package/dist/lib/activeField.js.map +1 -0
  55. package/dist/lib/authenticated.d.ts +5 -0
  56. package/dist/lib/authenticated.d.ts.map +1 -0
  57. package/dist/lib/authenticated.js +4 -0
  58. package/dist/lib/authenticated.js.map +1 -0
  59. package/dist/lib/defaultAccess.d.ts +8 -0
  60. package/dist/lib/defaultAccess.d.ts.map +1 -0
  61. package/dist/lib/defaultAccess.js +6 -0
  62. package/dist/lib/defaultAccess.js.map +1 -0
  63. package/dist/lib/extractSVG.d.ts +3 -0
  64. package/dist/lib/extractSVG.d.ts.map +1 -0
  65. package/dist/lib/extractSVG.js +27 -0
  66. package/dist/lib/extractSVG.js.map +1 -0
  67. package/dist/lib/getPayloadClient.d.ts +22 -0
  68. package/dist/lib/getPayloadClient.d.ts.map +1 -0
  69. package/dist/lib/getPayloadClient.js +39 -0
  70. package/dist/lib/getPayloadClient.js.map +1 -0
  71. package/dist/lib/mergeHooks.d.ts +8 -0
  72. package/dist/lib/mergeHooks.d.ts.map +1 -0
  73. package/dist/lib/mergeHooks.js +19 -0
  74. package/dist/lib/mergeHooks.js.map +1 -0
  75. package/dist/lib/titleCase.d.ts +7 -0
  76. package/dist/lib/titleCase.d.ts.map +1 -0
  77. package/dist/lib/titleCase.js +17 -0
  78. package/dist/lib/titleCase.js.map +1 -0
  79. package/dist/plugin.d.ts +91 -0
  80. package/dist/plugin.d.ts.map +1 -0
  81. package/dist/plugin.js +72 -0
  82. package/dist/plugin.js.map +1 -0
  83. package/dist/scan/cli.d.ts +14 -0
  84. package/dist/scan/cli.d.ts.map +1 -0
  85. package/dist/scan/cli.js +114 -0
  86. package/dist/scan/cli.js.map +1 -0
  87. package/dist/scan/extract.d.ts +58 -0
  88. package/dist/scan/extract.d.ts.map +1 -0
  89. package/dist/scan/extract.js +286 -0
  90. package/dist/scan/extract.js.map +1 -0
  91. package/dist/scan/index.d.ts +81 -0
  92. package/dist/scan/index.d.ts.map +1 -0
  93. package/dist/scan/index.js +133 -0
  94. package/dist/scan/index.js.map +1 -0
  95. package/dist/scan/live.d.ts +20 -0
  96. package/dist/scan/live.d.ts.map +1 -0
  97. package/dist/scan/live.js +109 -0
  98. package/dist/scan/live.js.map +1 -0
  99. package/dist/scan/load.d.ts +28 -0
  100. package/dist/scan/load.d.ts.map +1 -0
  101. package/dist/scan/load.js +37 -0
  102. package/dist/scan/load.js.map +1 -0
  103. package/dist/scan/types.d.ts +29 -0
  104. package/dist/scan/types.d.ts.map +1 -0
  105. package/dist/scan/types.js +8 -0
  106. package/dist/scan/types.js.map +1 -0
  107. package/dist/types.d.ts +38 -0
  108. package/dist/types.d.ts.map +1 -0
  109. package/dist/types.js +3 -0
  110. package/dist/types.js.map +1 -0
  111. package/dist/usage/recordMiss.d.ts +17 -0
  112. package/dist/usage/recordMiss.d.ts.map +1 -0
  113. package/dist/usage/recordMiss.js +60 -0
  114. package/dist/usage/recordMiss.js.map +1 -0
  115. package/dist/usage/trackIconMiss.d.ts +16 -0
  116. package/dist/usage/trackIconMiss.d.ts.map +1 -0
  117. package/dist/usage/trackIconMiss.js +43 -0
  118. package/dist/usage/trackIconMiss.js.map +1 -0
  119. package/package.json +106 -0
@@ -0,0 +1,270 @@
1
+ // Strip executable content from untrusted SVG uploads before they're stored and later inlined
2
+ // via dangerouslySetInnerHTML: <script> elements (svgo 4's `removeScripts`, renamed from 3.x's
3
+ // `removeScriptElement`) + on* event handlers. Also drop legacy editor attributes that survive
4
+ // preset-default and break React rendering when the root attrs are spread onto a JSX <svg>
5
+ // (`xml:space` → "Invalid DOM property") or mean nothing inline (`version`, `enable-background`).
6
+ const sanitizePlugins = [
7
+ 'removeScripts',
8
+ // elemSeparator '|': removeAttrs treats ':' as its element:attr:value separator, which would
9
+ // split the literal attribute name `xml:space` — swap the separator so it matches verbatim.
10
+ {
11
+ name: 'removeAttrs',
12
+ params: {
13
+ attrs: [
14
+ 'on.*',
15
+ 'xml:space',
16
+ 'enable-background',
17
+ 'version'
18
+ ],
19
+ elemSeparator: '|'
20
+ }
21
+ }
22
+ ];
23
+ // svgo has no builtin for javascript: URLs, so scrub (xlink:)href values from the serialized
24
+ // output as a final pass.
25
+ const stripDangerousUrls = (svg)=>svg.replace(/\s(?:xlink:)?href\s*=\s*(["'])\s*javascript:[^"']*\1/gi, '');
26
+ // The optimizer targets FILL glyphs: presentation attrs are stripped and fill="currentColor" is
27
+ // re-added, so a stroke-outline source (Lucide-style) renders its enclosed shapes as solid blobs.
28
+ const STROKE_WARNING = 'Warning: stroke-based icon detected — enclosed shapes will render filled. Use a fill-based glyph.';
29
+ const isStrokeBased = (svg)=>/fill\s*=\s*(["'])\s*none\s*\1/i.test(svg) && /\bstroke(?:-width)?\s*=/i.test(svg);
30
+ /**
31
+ * Optimize and sanitize an uploaded SVG: run svgo (sanitize → preset-default → strip presentation
32
+ * attrs → re-add `currentColor` fill/stroke so the icon themes with CSS color), tighten the
33
+ * `viewBox` to the real path bounds, and store the result as `svgString` for inline rendering.
34
+ *
35
+ * svgo + svg-path-bbox are imported dynamically so they never land in a frontend/edge bundle —
36
+ * this only runs server-side when an SVG is actually uploaded. On failure the raw upload is still
37
+ * stored (never optimized/inlined) and the `optimized` field carries the error so the doc shows it.
38
+ */ export const formatSvg = async (icon, svgData, logger)=>{
39
+ try {
40
+ const [{ optimize }, { svgPathBbox }] = await Promise.all([
41
+ import("svgo"),
42
+ import("svg-path-bbox")
43
+ ]);
44
+ const svg = svgData.toString('utf-8');
45
+ const originalSize = svgData.length;
46
+ const strokeBased = isStrokeBased(svg);
47
+ if (strokeBased) logger.warn(`[payload-icons] ${STROKE_WARNING}`);
48
+ // Prefix the report field so the editor sees the warning on the doc, not just in the server log.
49
+ const report = (msg)=>strokeBased ? `${STROKE_WARNING} ${msg}` : msg;
50
+ const hasTransforms = svg.includes('transform=');
51
+ const hasClipPaths = svg.includes('clip-path=') || svg.includes('<clipPath');
52
+ if (hasTransforms || hasClipPaths) {
53
+ logger.warn({
54
+ msg: '[payload-icons] Unsupported SVG features; skipping optimization (scripts still stripped)',
55
+ hasClipPaths,
56
+ hasTransforms
57
+ });
58
+ // Even when we skip optimization we MUST strip scripts/event handlers, because svgString is
59
+ // later inlined via dangerouslySetInnerHTML for every visitor.
60
+ const sanitized = stripDangerousUrls(optimize(svg, {
61
+ multipass: false,
62
+ plugins: sanitizePlugins
63
+ }).data);
64
+ return {
65
+ ...icon,
66
+ optimized: report('Skipped optimization (transform/clip-path present); scripts stripped'),
67
+ svgString: sanitized
68
+ };
69
+ }
70
+ const optimized = optimize(svg, {
71
+ path: 'input.svg',
72
+ multipass: true,
73
+ plugins: [
74
+ // Strip <script> elements + on* handlers first so nothing downstream re-introduces them.
75
+ ...sanitizePlugins,
76
+ 'preset-default',
77
+ 'convertStyleToAttrs',
78
+ 'removeDimensions',
79
+ {
80
+ name: 'removeAttrs',
81
+ params: {
82
+ attrs: [
83
+ 'fill',
84
+ 'stroke',
85
+ 'stroke-width',
86
+ 'stroke-linecap',
87
+ 'stroke-linejoin',
88
+ 'stroke-miterlimit',
89
+ 'stroke-dasharray',
90
+ 'stroke-dashoffset'
91
+ ]
92
+ }
93
+ },
94
+ {
95
+ name: 'cleanupIds',
96
+ params: {
97
+ minify: true,
98
+ remove: false
99
+ }
100
+ },
101
+ {
102
+ name: 'mergePaths',
103
+ params: {
104
+ force: false,
105
+ noSpaceAfterFlags: true
106
+ }
107
+ },
108
+ {
109
+ name: 'cleanupNumericValues',
110
+ params: {
111
+ floatPrecision: 1,
112
+ leadingZero: true
113
+ }
114
+ },
115
+ {
116
+ name: 'removeUnknownsAndDefaults',
117
+ params: {
118
+ keepAriaAttrs: true,
119
+ keepDataAttrs: true,
120
+ keepRoleAttr: true
121
+ }
122
+ },
123
+ // Re-add currentColor so the icon inherits CSS `color` on the frontend (after the
124
+ // presentation attrs above were stripped).
125
+ {
126
+ name: 'addAttributesToSVGElement',
127
+ params: {
128
+ attributes: [
129
+ {
130
+ fill: 'currentColor'
131
+ },
132
+ {
133
+ stroke: 'currentColor'
134
+ }
135
+ ]
136
+ }
137
+ },
138
+ {
139
+ name: 'convertTransform',
140
+ params: {
141
+ convertToShorts: true,
142
+ degPrecision: 1,
143
+ floatPrecision: 1,
144
+ transformPrecision: 1
145
+ }
146
+ },
147
+ {
148
+ name: 'convertPathData',
149
+ params: {
150
+ floatPrecision: 1,
151
+ leadingZero: true,
152
+ noSpaceAfterFlags: true,
153
+ removeUseless: true,
154
+ straightCurves: true,
155
+ transformPrecision: 1
156
+ }
157
+ },
158
+ {
159
+ name: 'sortAttrs',
160
+ params: {
161
+ order: [
162
+ 'id',
163
+ 'class',
164
+ 'style',
165
+ 'x',
166
+ 'y',
167
+ 'width',
168
+ 'height',
169
+ 'viewBox',
170
+ 'fill',
171
+ 'stroke',
172
+ 'stroke-width',
173
+ 'stroke-linecap',
174
+ 'stroke-linejoin',
175
+ 'stroke-miterlimit',
176
+ 'stroke-dasharray',
177
+ 'stroke-dashoffset',
178
+ 'd',
179
+ 'transform'
180
+ ]
181
+ }
182
+ }
183
+ ],
184
+ js2svg: {
185
+ pretty: true,
186
+ indent: 2,
187
+ eol: 'lf'
188
+ }
189
+ });
190
+ let svgStr = optimized.data;
191
+ svgStr = svgStr.replace(/viewBox="([^"]+)"/g, (_match, viewBox)=>{
192
+ const coords = viewBox.split(' ').map((coord)=>Number.isNaN(parseFloat(coord)) ? coord : parseFloat(coord).toFixed(1)).join(' ');
193
+ return `viewBox="${coords}"`;
194
+ });
195
+ const paths = Array.from(svgStr.matchAll(/<path[^>]*d="([^"]+)"/g));
196
+ if (paths.length) {
197
+ let minX = Infinity;
198
+ let minY = Infinity;
199
+ let maxX = -Infinity;
200
+ let maxY = -Infinity;
201
+ for (const [, d] of paths){
202
+ if (!d) continue;
203
+ try {
204
+ const [x1, y1, x2, y2] = svgPathBbox(d);
205
+ minX = Math.min(minX, x1);
206
+ minY = Math.min(minY, y1);
207
+ maxX = Math.max(maxX, x2);
208
+ maxY = Math.max(maxY, y2);
209
+ } catch (e) {
210
+ logger.warn({
211
+ msg: '[payload-icons] Failed to calculate path bounds',
212
+ err: e
213
+ });
214
+ }
215
+ }
216
+ if (minX !== Infinity && minY !== Infinity && maxX !== -Infinity && maxY !== -Infinity) {
217
+ const width = maxX - minX;
218
+ const height = maxY - minY;
219
+ // Square the viewBox around the glyph's center so icons share a consistent box.
220
+ const side = Math.max(width, height);
221
+ const centerX = minX + width / 2;
222
+ const centerY = minY + height / 2;
223
+ const newViewBox = `${(centerX - side / 2).toFixed(1)} ${(centerY - side / 2).toFixed(1)} ${side.toFixed(1)} ${side.toFixed(1)}`;
224
+ svgStr = svgStr.includes('viewBox=') ? svgStr.replace(/viewBox="[^"]+"/, `viewBox="${newViewBox}"`) : svgStr.replace('<svg', `<svg viewBox="${newViewBox}"`);
225
+ }
226
+ }
227
+ svgStr = stripDangerousUrls(svgStr);
228
+ const finalSize = Buffer.from(svgStr).length;
229
+ const reduction = originalSize - finalSize;
230
+ const reductionPercentage = (reduction / originalSize * 100).toFixed(1);
231
+ const optimizedString = `SVG optimized: ${originalSize} to ${finalSize} bytes (${reductionPercentage}% reduction)`;
232
+ logger.info(`[payload-icons] ${optimizedString}`);
233
+ return {
234
+ ...icon,
235
+ filesize: finalSize,
236
+ optimized: report(optimizedString),
237
+ svgString: svgStr
238
+ };
239
+ } catch (error) {
240
+ logger.error({
241
+ msg: '[payload-icons] Error processing SVG',
242
+ err: error
243
+ });
244
+ // Surface the failure on the doc itself — both output fields are condition-hidden when empty,
245
+ // so a silent return would look like a normal save while the icon never renders.
246
+ return {
247
+ ...icon,
248
+ optimized: `Optimization failed: ${error instanceof Error ? error.message : String(error)} — icon will not render.`
249
+ };
250
+ }
251
+ };
252
+ /** `beforeChange` hook: when an SVG file is uploaded (create or update), optimize + sanitize it
253
+ * and fold `svgString`/`optimized` into the doc. A no-op for changes that carry no new file. */ export const formatSVGHook = async ({ data, operation, req })=>{
254
+ if (operation === 'create' || operation === 'update') {
255
+ if (data?.filename && req.file) {
256
+ try {
257
+ return await formatSvg(data, req.file.data, req.payload.logger);
258
+ } catch (error) {
259
+ req.payload.logger.warn({
260
+ msg: '[payload-icons] Error in formatSVGHook',
261
+ err: error
262
+ });
263
+ return data;
264
+ }
265
+ }
266
+ }
267
+ return data;
268
+ };
269
+
270
+ //# sourceMappingURL=formatSVG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/hooks/formatSVG.ts"],"sourcesContent":["import type { CollectionBeforeChangeHook, Payload } from 'payload'\nimport type { PluginConfig } from 'svgo'\n\n/** The subset of an icon doc this hook writes. */\ntype IconData = Record<string, unknown> & { filesize?: number; optimized?: string; svgString?: string }\n\n// Strip executable content from untrusted SVG uploads before they're stored and later inlined\n// via dangerouslySetInnerHTML: <script> elements (svgo 4's `removeScripts`, renamed from 3.x's\n// `removeScriptElement`) + on* event handlers. Also drop legacy editor attributes that survive\n// preset-default and break React rendering when the root attrs are spread onto a JSX <svg>\n// (`xml:space` → \"Invalid DOM property\") or mean nothing inline (`version`, `enable-background`).\nconst sanitizePlugins: PluginConfig[] = [\n 'removeScripts',\n // elemSeparator '|': removeAttrs treats ':' as its element:attr:value separator, which would\n // split the literal attribute name `xml:space` — swap the separator so it matches verbatim.\n { name: 'removeAttrs', params: { attrs: ['on.*', 'xml:space', 'enable-background', 'version'], elemSeparator: '|' } },\n]\n\n// svgo has no builtin for javascript: URLs, so scrub (xlink:)href values from the serialized\n// output as a final pass.\nconst stripDangerousUrls = (svg: string): string => svg.replace(/\\s(?:xlink:)?href\\s*=\\s*([\"'])\\s*javascript:[^\"']*\\1/gi, '')\n\n// The optimizer targets FILL glyphs: presentation attrs are stripped and fill=\"currentColor\" is\n// re-added, so a stroke-outline source (Lucide-style) renders its enclosed shapes as solid blobs.\nconst STROKE_WARNING = 'Warning: stroke-based icon detected — enclosed shapes will render filled. Use a fill-based glyph.'\nconst isStrokeBased = (svg: string): boolean => /fill\\s*=\\s*([\"'])\\s*none\\s*\\1/i.test(svg) && /\\bstroke(?:-width)?\\s*=/i.test(svg)\n\n/**\n * Optimize and sanitize an uploaded SVG: run svgo (sanitize → preset-default → strip presentation\n * attrs → re-add `currentColor` fill/stroke so the icon themes with CSS color), tighten the\n * `viewBox` to the real path bounds, and store the result as `svgString` for inline rendering.\n *\n * svgo + svg-path-bbox are imported dynamically so they never land in a frontend/edge bundle —\n * this only runs server-side when an SVG is actually uploaded. On failure the raw upload is still\n * stored (never optimized/inlined) and the `optimized` field carries the error so the doc shows it.\n */\nexport const formatSvg = async (icon: IconData, svgData: Buffer, logger: Payload['logger']): Promise<IconData> => {\n try {\n const [{ optimize }, { svgPathBbox }] = await Promise.all([import('svgo'), import('svg-path-bbox')])\n\n const svg = svgData.toString('utf-8')\n const originalSize = svgData.length\n\n const strokeBased = isStrokeBased(svg)\n if (strokeBased) logger.warn(`[payload-icons] ${STROKE_WARNING}`)\n // Prefix the report field so the editor sees the warning on the doc, not just in the server log.\n const report = (msg: string): string => (strokeBased ? `${STROKE_WARNING} ${msg}` : msg)\n\n const hasTransforms = svg.includes('transform=')\n const hasClipPaths = svg.includes('clip-path=') || svg.includes('<clipPath')\n if (hasTransforms || hasClipPaths) {\n logger.warn({\n msg: '[payload-icons] Unsupported SVG features; skipping optimization (scripts still stripped)',\n hasClipPaths,\n hasTransforms,\n })\n // Even when we skip optimization we MUST strip scripts/event handlers, because svgString is\n // later inlined via dangerouslySetInnerHTML for every visitor.\n const sanitized = stripDangerousUrls(optimize(svg, { multipass: false, plugins: sanitizePlugins }).data)\n return { ...icon, optimized: report('Skipped optimization (transform/clip-path present); scripts stripped'), svgString: sanitized }\n }\n\n const optimized = optimize(svg, {\n path: 'input.svg',\n multipass: true,\n plugins: [\n // Strip <script> elements + on* handlers first so nothing downstream re-introduces them.\n ...sanitizePlugins,\n 'preset-default',\n 'convertStyleToAttrs',\n 'removeDimensions',\n {\n name: 'removeAttrs',\n params: {\n attrs: [\n 'fill',\n 'stroke',\n 'stroke-width',\n 'stroke-linecap',\n 'stroke-linejoin',\n 'stroke-miterlimit',\n 'stroke-dasharray',\n 'stroke-dashoffset',\n ],\n },\n },\n { name: 'cleanupIds', params: { minify: true, remove: false } },\n { name: 'mergePaths', params: { force: false, noSpaceAfterFlags: true } },\n { name: 'cleanupNumericValues', params: { floatPrecision: 1, leadingZero: true } },\n { name: 'removeUnknownsAndDefaults', params: { keepAriaAttrs: true, keepDataAttrs: true, keepRoleAttr: true } },\n // Re-add currentColor so the icon inherits CSS `color` on the frontend (after the\n // presentation attrs above were stripped).\n { name: 'addAttributesToSVGElement', params: { attributes: [{ fill: 'currentColor' }, { stroke: 'currentColor' }] } },\n { name: 'convertTransform', params: { convertToShorts: true, degPrecision: 1, floatPrecision: 1, transformPrecision: 1 } },\n {\n name: 'convertPathData',\n params: {\n floatPrecision: 1,\n leadingZero: true,\n noSpaceAfterFlags: true,\n removeUseless: true,\n straightCurves: true,\n transformPrecision: 1,\n },\n },\n {\n name: 'sortAttrs',\n params: {\n order: [\n 'id',\n 'class',\n 'style',\n 'x',\n 'y',\n 'width',\n 'height',\n 'viewBox',\n 'fill',\n 'stroke',\n 'stroke-width',\n 'stroke-linecap',\n 'stroke-linejoin',\n 'stroke-miterlimit',\n 'stroke-dasharray',\n 'stroke-dashoffset',\n 'd',\n 'transform',\n ],\n },\n },\n ],\n js2svg: { pretty: true, indent: 2, eol: 'lf' },\n })\n\n let svgStr = optimized.data\n\n svgStr = svgStr.replace(/viewBox=\"([^\"]+)\"/g, (_match, viewBox) => {\n const coords = viewBox\n .split(' ')\n .map((coord: string) => (Number.isNaN(parseFloat(coord)) ? coord : parseFloat(coord).toFixed(1)))\n .join(' ')\n return `viewBox=\"${coords}\"`\n })\n\n const paths = Array.from(svgStr.matchAll(/<path[^>]*d=\"([^\"]+)\"/g))\n\n if (paths.length) {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n\n for (const [, d] of paths) {\n if (!d) continue\n try {\n const [x1, y1, x2, y2] = svgPathBbox(d)\n minX = Math.min(minX, x1)\n minY = Math.min(minY, y1)\n maxX = Math.max(maxX, x2)\n maxY = Math.max(maxY, y2)\n } catch (e) {\n logger.warn({ msg: '[payload-icons] Failed to calculate path bounds', err: e })\n }\n }\n\n if (minX !== Infinity && minY !== Infinity && maxX !== -Infinity && maxY !== -Infinity) {\n const width = maxX - minX\n const height = maxY - minY\n\n // Square the viewBox around the glyph's center so icons share a consistent box.\n const side = Math.max(width, height)\n const centerX = minX + width / 2\n const centerY = minY + height / 2\n const newViewBox = `${(centerX - side / 2).toFixed(1)} ${(centerY - side / 2).toFixed(1)} ${side.toFixed(1)} ${side.toFixed(1)}`\n\n svgStr = svgStr.includes('viewBox=')\n ? svgStr.replace(/viewBox=\"[^\"]+\"/, `viewBox=\"${newViewBox}\"`)\n : svgStr.replace('<svg', `<svg viewBox=\"${newViewBox}\"`)\n }\n }\n\n svgStr = stripDangerousUrls(svgStr)\n\n const finalSize = Buffer.from(svgStr).length\n const reduction = originalSize - finalSize\n const reductionPercentage = ((reduction / originalSize) * 100).toFixed(1)\n const optimizedString = `SVG optimized: ${originalSize} to ${finalSize} bytes (${reductionPercentage}% reduction)`\n logger.info(`[payload-icons] ${optimizedString}`)\n\n return { ...icon, filesize: finalSize, optimized: report(optimizedString), svgString: svgStr }\n } catch (error) {\n logger.error({ msg: '[payload-icons] Error processing SVG', err: error })\n // Surface the failure on the doc itself — both output fields are condition-hidden when empty,\n // so a silent return would look like a normal save while the icon never renders.\n return { ...icon, optimized: `Optimization failed: ${error instanceof Error ? error.message : String(error)} — icon will not render.` }\n }\n}\n\n/** `beforeChange` hook: when an SVG file is uploaded (create or update), optimize + sanitize it\n * and fold `svgString`/`optimized` into the doc. A no-op for changes that carry no new file. */\nexport const formatSVGHook: CollectionBeforeChangeHook = async ({ data, operation, req }) => {\n if (operation === 'create' || operation === 'update') {\n if (data?.filename && req.file) {\n try {\n return await formatSvg(data, req.file.data, req.payload.logger)\n } catch (error) {\n req.payload.logger.warn({ msg: '[payload-icons] Error in formatSVGHook', err: error })\n return data\n }\n }\n }\n return data\n}\n"],"names":["sanitizePlugins","name","params","attrs","elemSeparator","stripDangerousUrls","svg","replace","STROKE_WARNING","isStrokeBased","test","formatSvg","icon","svgData","logger","optimize","svgPathBbox","Promise","all","toString","originalSize","length","strokeBased","warn","report","msg","hasTransforms","includes","hasClipPaths","sanitized","multipass","plugins","data","optimized","svgString","path","minify","remove","force","noSpaceAfterFlags","floatPrecision","leadingZero","keepAriaAttrs","keepDataAttrs","keepRoleAttr","attributes","fill","stroke","convertToShorts","degPrecision","transformPrecision","removeUseless","straightCurves","order","js2svg","pretty","indent","eol","svgStr","_match","viewBox","coords","split","map","coord","Number","isNaN","parseFloat","toFixed","join","paths","Array","from","matchAll","minX","Infinity","minY","maxX","maxY","d","x1","y1","x2","y2","Math","min","max","e","err","width","height","side","centerX","centerY","newViewBox","finalSize","Buffer","reduction","reductionPercentage","optimizedString","info","filesize","error","Error","message","String","formatSVGHook","operation","req","filename","file","payload"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export { iconsPlugin, default } from './plugin';
2
+ export type { IconsPluginOptions } from './plugin';
3
+ export type { IconCollectionOverrides, IconDoc } from './types';
4
+ export type { IconSetCollectionOverrides } from './collections/IconSet';
5
+ export type { IconRequestCollectionOverrides } from './collections/IconRequest';
6
+ export { extractSvgContent, extractSvgProps } from './lib/extractSVG';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAG/C,YAAY,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAClD,YAAY,EAAE,uBAAuB,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAC/D,YAAY,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAA;AACvE,YAAY,EAAE,8BAA8B,EAAE,MAAM,2BAA2B,CAAA;AAK/E,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ // The plugin — the single entry point.
2
+ export { iconsPlugin, default } from "./plugin.js";
3
+ // SVG string helpers — inline an icon's `svgString` as a real `<svg>` when rendering it yourself
4
+ // (e.g. with `getIconSvg` from the `./cache` subpath):
5
+ // <svg {...extractSvgProps(svg)} className="size-6" dangerouslySetInnerHTML={{ __html: extractSvgContent(svg) }} />
6
+ export { extractSvgContent, extractSvgProps } from "./lib/extractSVG.js"; // NOTE: server-only / Next-coupled / React entry points are exposed via subpaths to keep them out of
7
+ // this server/config module: the drop-in `<Icon name="…" />` server component is `./components/Icon`,
8
+ // the active-set resolver `getIconSvg` is `./cache`, and the admin field components are `./admin/*`.
9
+
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// The plugin — the single entry point.\nexport { iconsPlugin, default } from './plugin'\n\n// Public types, re-exported so consumers can type options/helpers without deep imports.\nexport type { IconsPluginOptions } from './plugin'\nexport type { IconCollectionOverrides, IconDoc } from './types'\nexport type { IconSetCollectionOverrides } from './collections/IconSet'\nexport type { IconRequestCollectionOverrides } from './collections/IconRequest'\n\n// SVG string helpers — inline an icon's `svgString` as a real `<svg>` when rendering it yourself\n// (e.g. with `getIconSvg` from the `./cache` subpath):\n// <svg {...extractSvgProps(svg)} className=\"size-6\" dangerouslySetInnerHTML={{ __html: extractSvgContent(svg) }} />\nexport { extractSvgContent, extractSvgProps } from './lib/extractSVG'\n\n// NOTE: server-only / Next-coupled / React entry points are exposed via subpaths to keep them out of\n// this server/config module: the drop-in `<Icon name=\"…\" />` server component is `./components/Icon`,\n// the active-set resolver `getIconSvg` is `./cache`, and the admin field components are `./admin/*`.\n"],"names":["iconsPlugin","default","extractSvgContent","extractSvgProps"],"mappings":"AAAA,uCAAuC;AACvC,SAASA,WAAW,EAAEC,OAAO,QAAQ,cAAU;AAQ/C,iGAAiG;AACjG,uDAAuD;AACvD,sHAAsH;AACtH,SAASC,iBAAiB,EAAEC,eAAe,QAAQ,sBAAkB,CAErE,qGAAqG;CACrG,sGAAsG;CACtG,qGAAqG"}
@@ -0,0 +1,24 @@
1
+ import type { CheckboxField, CollectionBeforeChangeHook } from 'payload';
2
+ /**
3
+ * The single-active toggle for the `iconSet` collection. The frontend renders
4
+ * whichever set has `active: true` (in its status lane), so flipping this on one
5
+ * set re-skins every `<Icon>` across the site. The invariant is enforced by
6
+ * {@link enforceSingleActive}.
7
+ */
8
+ export declare const activeField: CheckboxField;
9
+ /**
10
+ * `beforeChange` hook enforcing the single-active invariant, adapted (standalone,
11
+ * no `@pro-laico/core`) from Atomic's `unsetActive`. When a set is saved
12
+ * `active`, every other set in the **same status lane** is flipped `active:
13
+ * false` in the same request/transaction:
14
+ *
15
+ * - The `_status: draft | published` filter scopes the deactivation to the lane
16
+ * being written, so staging a new active set as a *draft* doesn't disturb the
17
+ * live (published) active set — the swap only goes live on publish. (Skipped
18
+ * when the collection has no drafts.)
19
+ * - It runs in `beforeChange` and **rethrows** on failure, so a failed
20
+ * deactivation rolls the whole save back atomically rather than leaving two
21
+ * active sets.
22
+ */
23
+ export declare const enforceSingleActive: CollectionBeforeChangeHook;
24
+ //# sourceMappingURL=activeField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activeField.d.ts","sourceRoot":"","sources":["../../src/lib/activeField.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,0BAA0B,EAAkB,MAAM,SAAS,CAAA;AAExF;;;;;GAKG;AACH,eAAO,MAAM,WAAW,EAAE,aASzB,CAAA;AAKD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mBAAmB,EAAE,0BAwBjC,CAAA"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * The single-active toggle for the `iconSet` collection. The frontend renders
3
+ * whichever set has `active: true` (in its status lane), so flipping this on one
4
+ * set re-skins every `<Icon>` across the site. The invariant is enforced by
5
+ * {@link enforceSingleActive}.
6
+ */ export const activeField = {
7
+ name: 'active',
8
+ type: 'checkbox',
9
+ defaultValue: false,
10
+ index: true,
11
+ admin: {
12
+ description: 'Render this set across the frontend. Activating a set deactivates the others.',
13
+ style: {
14
+ maxWidth: '160px',
15
+ alignSelf: 'center'
16
+ }
17
+ }
18
+ };
19
+ /** Hook-`context` flag so the cascade of deactivations we trigger doesn't recurse. */ const CASCADE = 'iconSetEnforceSingleActive';
20
+ /**
21
+ * `beforeChange` hook enforcing the single-active invariant, adapted (standalone,
22
+ * no `@pro-laico/core`) from Atomic's `unsetActive`. When a set is saved
23
+ * `active`, every other set in the **same status lane** is flipped `active:
24
+ * false` in the same request/transaction:
25
+ *
26
+ * - The `_status: draft | published` filter scopes the deactivation to the lane
27
+ * being written, so staging a new active set as a *draft* doesn't disturb the
28
+ * live (published) active set — the swap only goes live on publish. (Skipped
29
+ * when the collection has no drafts.)
30
+ * - It runs in `beforeChange` and **rethrows** on failure, so a failed
31
+ * deactivation rolls the whole save back atomically rather than leaving two
32
+ * active sets.
33
+ */ export const enforceSingleActive = async ({ data, originalDoc, collection, req, context })=>{
34
+ if (!data?.active || context[CASCADE]) return data;
35
+ const hasDrafts = Boolean(collection.versions?.drafts);
36
+ const draft = hasDrafts && data._status === 'draft';
37
+ const id = originalDoc?.id // undefined on create — then there are no other rows to exclude
38
+ ;
39
+ await req.payload.update({
40
+ req,
41
+ draft,
42
+ collection: collection.slug,
43
+ // `active` isn't a field on every collection slug, so the bulk-update `data`
44
+ // union has no matching branch under a partial schema — cast past it.
45
+ data: {
46
+ active: false
47
+ },
48
+ where: {
49
+ active: {
50
+ equals: true
51
+ },
52
+ ...id != null ? {
53
+ id: {
54
+ not_equals: id
55
+ }
56
+ } : {},
57
+ ...hasDrafts ? {
58
+ _status: {
59
+ equals: draft ? 'draft' : 'published'
60
+ }
61
+ } : {}
62
+ },
63
+ context: {
64
+ [CASCADE]: true
65
+ },
66
+ overrideAccess: true
67
+ });
68
+ return data;
69
+ };
70
+
71
+ //# sourceMappingURL=activeField.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/activeField.ts"],"sourcesContent":["import type { CheckboxField, CollectionBeforeChangeHook, CollectionSlug } from 'payload'\n\n/**\n * The single-active toggle for the `iconSet` collection. The frontend renders\n * whichever set has `active: true` (in its status lane), so flipping this on one\n * set re-skins every `<Icon>` across the site. The invariant is enforced by\n * {@link enforceSingleActive}.\n */\nexport const activeField: CheckboxField = {\n name: 'active',\n type: 'checkbox',\n defaultValue: false,\n index: true,\n admin: {\n description: 'Render this set across the frontend. Activating a set deactivates the others.',\n style: { maxWidth: '160px', alignSelf: 'center' },\n },\n}\n\n/** Hook-`context` flag so the cascade of deactivations we trigger doesn't recurse. */\nconst CASCADE = 'iconSetEnforceSingleActive'\n\n/**\n * `beforeChange` hook enforcing the single-active invariant, adapted (standalone,\n * no `@pro-laico/core`) from Atomic's `unsetActive`. When a set is saved\n * `active`, every other set in the **same status lane** is flipped `active:\n * false` in the same request/transaction:\n *\n * - The `_status: draft | published` filter scopes the deactivation to the lane\n * being written, so staging a new active set as a *draft* doesn't disturb the\n * live (published) active set — the swap only goes live on publish. (Skipped\n * when the collection has no drafts.)\n * - It runs in `beforeChange` and **rethrows** on failure, so a failed\n * deactivation rolls the whole save back atomically rather than leaving two\n * active sets.\n */\nexport const enforceSingleActive: CollectionBeforeChangeHook = async ({ data, originalDoc, collection, req, context }) => {\n if (!data?.active || context[CASCADE]) return data\n\n const hasDrafts = Boolean((collection.versions as { drafts?: unknown } | undefined)?.drafts)\n const draft = hasDrafts && data._status === 'draft'\n const id = originalDoc?.id // undefined on create — then there are no other rows to exclude\n\n await req.payload.update({\n req,\n draft,\n collection: collection.slug as CollectionSlug,\n // `active` isn't a field on every collection slug, so the bulk-update `data`\n // union has no matching branch under a partial schema — cast past it.\n data: { active: false } as unknown as never,\n where: {\n active: { equals: true },\n ...(id != null ? { id: { not_equals: id } } : {}),\n ...(hasDrafts ? { _status: { equals: draft ? 'draft' : 'published' } } : {}),\n },\n context: { [CASCADE]: true },\n overrideAccess: true,\n })\n\n return data\n}\n"],"names":["activeField","name","type","defaultValue","index","admin","description","style","maxWidth","alignSelf","CASCADE","enforceSingleActive","data","originalDoc","collection","req","context","active","hasDrafts","Boolean","versions","drafts","draft","_status","id","payload","update","slug","where","equals","not_equals","overrideAccess"],"mappings":"AAEA;;;;;CAKC,GACD,OAAO,MAAMA,cAA6B;IACxCC,MAAM;IACNC,MAAM;IACNC,cAAc;IACdC,OAAO;IACPC,OAAO;QACLC,aAAa;QACbC,OAAO;YAAEC,UAAU;YAASC,WAAW;QAAS;IAClD;AACF,EAAC;AAED,oFAAoF,GACpF,MAAMC,UAAU;AAEhB;;;;;;;;;;;;;CAaC,GACD,OAAO,MAAMC,sBAAkD,OAAO,EAAEC,IAAI,EAAEC,WAAW,EAAEC,UAAU,EAAEC,GAAG,EAAEC,OAAO,EAAE;IACnH,IAAI,CAACJ,MAAMK,UAAUD,OAAO,CAACN,QAAQ,EAAE,OAAOE;IAE9C,MAAMM,YAAYC,QAASL,WAAWM,QAAQ,EAAuCC;IACrF,MAAMC,QAAQJ,aAAaN,KAAKW,OAAO,KAAK;IAC5C,MAAMC,KAAKX,aAAaW,GAAG,gEAAgE;;IAE3F,MAAMT,IAAIU,OAAO,CAACC,MAAM,CAAC;QACvBX;QACAO;QACAR,YAAYA,WAAWa,IAAI;QAC3B,6EAA6E;QAC7E,sEAAsE;QACtEf,MAAM;YAAEK,QAAQ;QAAM;QACtBW,OAAO;YACLX,QAAQ;gBAAEY,QAAQ;YAAK;YACvB,GAAIL,MAAM,OAAO;gBAAEA,IAAI;oBAAEM,YAAYN;gBAAG;YAAE,IAAI,CAAC,CAAC;YAChD,GAAIN,YAAY;gBAAEK,SAAS;oBAAEM,QAAQP,QAAQ,UAAU;gBAAY;YAAE,IAAI,CAAC,CAAC;QAC7E;QACAN,SAAS;YAAE,CAACN,QAAQ,EAAE;QAAK;QAC3BqB,gBAAgB;IAClB;IAEA,OAAOnB;AACT,EAAC"}
@@ -0,0 +1,5 @@
1
+ import type { Access } from 'payload';
2
+ /** Access gate: any logged-in user. Used for the `iconSet` / `iconRequest`
3
+ * collections, which are author/diagnostic data rather than public assets. */
4
+ export declare const authd: Access;
5
+ //# sourceMappingURL=authenticated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authenticated.d.ts","sourceRoot":"","sources":["../../src/lib/authenticated.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAErC;+EAC+E;AAC/E,eAAO,MAAM,KAAK,EAAE,MAAuC,CAAA"}
@@ -0,0 +1,4 @@
1
+ /** Access gate: any logged-in user. Used for the `iconSet` / `iconRequest`
2
+ * collections, which are author/diagnostic data rather than public assets. */ export const authd = ({ req })=>Boolean(req.user);
3
+
4
+ //# sourceMappingURL=authenticated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/authenticated.ts"],"sourcesContent":["import type { Access } from 'payload'\n\n/** Access gate: any logged-in user. Used for the `iconSet` / `iconRequest`\n * collections, which are author/diagnostic data rather than public assets. */\nexport const authd: Access = ({ req }) => Boolean(req.user)\n"],"names":["authd","req","Boolean","user"],"mappings":"AAEA;6EAC6E,GAC7E,OAAO,MAAMA,QAAgB,CAAC,EAAEC,GAAG,EAAE,GAAKC,QAAQD,IAAIE,IAAI,EAAC"}
@@ -0,0 +1,8 @@
1
+ import type { Access } from 'payload';
2
+ /** Default write gate: allow only a logged-in user from the configured admin collection.
3
+ * Override per-operation via the plugin's `access` option. */
4
+ export declare const defaultAdminAccess: Access;
5
+ /** Default read gate: public. Icons are frontend assets, so anyone can read them; tighten this
6
+ * via `access.read` if your icons are private. */
7
+ export declare const defaultReadAccess: Access;
8
+ //# sourceMappingURL=defaultAccess.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaultAccess.d.ts","sourceRoot":"","sources":["../../src/lib/defaultAccess.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAErC;+DAC+D;AAC/D,eAAO,MAAM,kBAAkB,EAAE,MAAgG,CAAA;AAEjI;mDACmD;AACnD,eAAO,MAAM,iBAAiB,EAAE,MAAmB,CAAA"}
@@ -0,0 +1,6 @@
1
+ /** Default write gate: allow only a logged-in user from the configured admin collection.
2
+ * Override per-operation via the plugin's `access` option. */ export const defaultAdminAccess = ({ req })=>Boolean(req.user && req.user.collection === req.payload.config.admin.user);
3
+ /** Default read gate: public. Icons are frontend assets, so anyone can read them; tighten this
4
+ * via `access.read` if your icons are private. */ export const defaultReadAccess = ()=>true;
5
+
6
+ //# sourceMappingURL=defaultAccess.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/defaultAccess.ts"],"sourcesContent":["import type { Access } from 'payload'\n\n/** Default write gate: allow only a logged-in user from the configured admin collection.\n * Override per-operation via the plugin's `access` option. */\nexport const defaultAdminAccess: Access = ({ req }) => Boolean(req.user && req.user.collection === req.payload.config.admin.user)\n\n/** Default read gate: public. Icons are frontend assets, so anyone can read them; tighten this\n * via `access.read` if your icons are private. */\nexport const defaultReadAccess: Access = () => true\n"],"names":["defaultAdminAccess","req","Boolean","user","collection","payload","config","admin","defaultReadAccess"],"mappings":"AAEA;6DAC6D,GAC7D,OAAO,MAAMA,qBAA6B,CAAC,EAAEC,GAAG,EAAE,GAAKC,QAAQD,IAAIE,IAAI,IAAIF,IAAIE,IAAI,CAACC,UAAU,KAAKH,IAAII,OAAO,CAACC,MAAM,CAACC,KAAK,CAACJ,IAAI,EAAC;AAEjI;iDACiD,GACjD,OAAO,MAAMK,oBAA4B,IAAM,KAAI"}
@@ -0,0 +1,3 @@
1
+ export declare const extractSvgContent: (svgString: string) => string;
2
+ export declare const extractSvgProps: (svgString: string) => Record<string, string>;
3
+ //# sourceMappingURL=extractSVG.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractSVG.d.ts","sourceRoot":"","sources":["../../src/lib/extractSVG.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,iBAAiB,GAAI,WAAW,MAAM,KAAG,MAA2E,CAAA;AAgBjI,eAAO,MAAM,eAAe,GAAI,WAAW,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAUxE,CAAA"}
@@ -0,0 +1,27 @@
1
+ // Pull the inner markup out of an `<svg>` wrapper so the `<Icon>` component can re-emit it
2
+ // inside its own `<svg>` (props merged from the JSX node). Lazy `*?` so a (valid) nested
3
+ // `<svg>` doesn't make the match run to the outer document's last `</svg>` and swallow the
4
+ // inner closing tag.
5
+ export const extractSvgContent = (svgString)=>svgString.match(/<svg[^>]*>([\s\S]*?)<\/svg>/i)?.[1] || svgString;
6
+ /** Root attributes that are useless on an inlined `<svg>` AND unsafe to spread onto a React
7
+ * element: React rejects namespaced names, and `xmlns`/`version`/`enable-background` mean
8
+ * nothing inline. */ const DROP_PROPS = /^(?:xmlns(?::.+)?|xml:.+|version|enable-background)$/i;
9
+ /** React's SVG props are camelCase (`fill-rule` → `fillRule`, `xlink:href` → `xlinkHref`) —
10
+ * spreading the raw hyphenated/namespaced names triggers "Invalid DOM property" errors.
11
+ * `data-*` / `aria-*` are the exception: React expects those verbatim. */ const toReactProp = (key)=>/^(?:data|aria)-/i.test(key) ? key : key.replace(/[-:]([a-z])/gi, (_, ch)=>ch.toUpperCase());
12
+ // Parse the attributes off the opening `<svg …>` tag into a React-safe props object (viewBox,
13
+ // fill, fillRule, …), so the rendered element keeps the source's intrinsic attributes unless a
14
+ // JSX prop overrides them.
15
+ export const extractSvgProps = (svgString)=>{
16
+ const match = svgString.match(/<svg([^>]*)>/i);
17
+ if (!match?.[1]) return {};
18
+ const props = {};
19
+ // Allow namespaced/hyphenated attribute names (`xmlns:xlink`, `fill-rule`, `xml:space`,
20
+ // `stroke-width`) and both single- and double-quoted values.
21
+ for (const [, key, dq, sq] of match[1].matchAll(/([\w:.-]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g)){
22
+ if (key && !DROP_PROPS.test(key)) props[toReactProp(key)] = dq ?? sq ?? '';
23
+ }
24
+ return props;
25
+ };
26
+
27
+ //# sourceMappingURL=extractSVG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/extractSVG.ts"],"sourcesContent":["// Pull the inner markup out of an `<svg>` wrapper so the `<Icon>` component can re-emit it\n// inside its own `<svg>` (props merged from the JSX node). Lazy `*?` so a (valid) nested\n// `<svg>` doesn't make the match run to the outer document's last `</svg>` and swallow the\n// inner closing tag.\nexport const extractSvgContent = (svgString: string): string => svgString.match(/<svg[^>]*>([\\s\\S]*?)<\\/svg>/i)?.[1] || svgString\n\n/** Root attributes that are useless on an inlined `<svg>` AND unsafe to spread onto a React\n * element: React rejects namespaced names, and `xmlns`/`version`/`enable-background` mean\n * nothing inline. */\nconst DROP_PROPS = /^(?:xmlns(?::.+)?|xml:.+|version|enable-background)$/i\n\n/** React's SVG props are camelCase (`fill-rule` → `fillRule`, `xlink:href` → `xlinkHref`) —\n * spreading the raw hyphenated/namespaced names triggers \"Invalid DOM property\" errors.\n * `data-*` / `aria-*` are the exception: React expects those verbatim. */\nconst toReactProp = (key: string): string =>\n /^(?:data|aria)-/i.test(key) ? key : key.replace(/[-:]([a-z])/gi, (_, ch: string) => ch.toUpperCase())\n\n// Parse the attributes off the opening `<svg …>` tag into a React-safe props object (viewBox,\n// fill, fillRule, …), so the rendered element keeps the source's intrinsic attributes unless a\n// JSX prop overrides them.\nexport const extractSvgProps = (svgString: string): Record<string, string> => {\n const match = svgString.match(/<svg([^>]*)>/i)\n if (!match?.[1]) return {}\n const props: Record<string, string> = {}\n // Allow namespaced/hyphenated attribute names (`xmlns:xlink`, `fill-rule`, `xml:space`,\n // `stroke-width`) and both single- and double-quoted values.\n for (const [, key, dq, sq] of match[1].matchAll(/([\\w:.-]+)\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/g)) {\n if (key && !DROP_PROPS.test(key)) props[toReactProp(key)] = dq ?? sq ?? ''\n }\n return props\n}\n"],"names":["extractSvgContent","svgString","match","DROP_PROPS","toReactProp","key","test","replace","_","ch","toUpperCase","extractSvgProps","props","dq","sq","matchAll"],"mappings":"AAAA,2FAA2F;AAC3F,yFAAyF;AACzF,2FAA2F;AAC3F,qBAAqB;AACrB,OAAO,MAAMA,oBAAoB,CAACC,YAA8BA,UAAUC,KAAK,CAAC,iCAAiC,CAAC,EAAE,IAAID,UAAS;AAEjI;;oBAEoB,GACpB,MAAME,aAAa;AAEnB;;yEAEyE,GACzE,MAAMC,cAAc,CAACC,MACnB,mBAAmBC,IAAI,CAACD,OAAOA,MAAMA,IAAIE,OAAO,CAAC,iBAAiB,CAACC,GAAGC,KAAeA,GAAGC,WAAW;AAErG,8FAA8F;AAC9F,+FAA+F;AAC/F,2BAA2B;AAC3B,OAAO,MAAMC,kBAAkB,CAACV;IAC9B,MAAMC,QAAQD,UAAUC,KAAK,CAAC;IAC9B,IAAI,CAACA,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC;IACzB,MAAMU,QAAgC,CAAC;IACvC,wFAAwF;IACxF,6DAA6D;IAC7D,KAAK,MAAM,GAAGP,KAAKQ,IAAIC,GAAG,IAAIZ,KAAK,CAAC,EAAE,CAACa,QAAQ,CAAC,6CAA8C;QAC5F,IAAIV,OAAO,CAACF,WAAWG,IAAI,CAACD,MAAMO,KAAK,CAACR,YAAYC,KAAK,GAAGQ,MAAMC,MAAM;IAC1E;IACA,OAAOF;AACT,EAAC"}
@@ -0,0 +1,22 @@
1
+ import { type Payload, type SanitizedConfig } from 'payload';
2
+ /** Called from the plugin's `onInit`: remember the running app's config for the server components. */
3
+ export declare const stashConfig: (config: SanitizedConfig) => void;
4
+ /** Called from the plugin factory: remember the resolved `iconSet` slug for the cache resolver. */
5
+ export declare const stashIconSetSlug: (slug: string) => void;
6
+ /** The app's icon-set collection slug (honors `iconSetOverrides.slug`); the default when unset. */
7
+ export declare const getIconSetSlug: () => string;
8
+ /**
9
+ * Resolve the host app's Payload config, in order:
10
+ * 1. the globalThis stash the plugin's `onInit` fills the moment Payload boots — covers every
11
+ * render after init with no bundler involvement, or
12
+ * 2. the `@payload-config` alias `withPayload` sets up — which only resolves from inside a
13
+ * published package when the consumer transpiles it
14
+ * (`transpilePackages: ['@pro-laico/payload-icons']` in next.config). The `as string` keeps
15
+ * TS from resolving the alias at this package's own build time.
16
+ */
17
+ export declare const getConfig: () => Promise<SanitizedConfig>;
18
+ /** A booted Payload instance, using the host app's config. The shared server-side entry point
19
+ * for the cache resolvers, the usage panel, and the runtime miss recorder, so the
20
+ * config-resolution trick lives in one place. */
21
+ export declare const getPayloadClient: () => Promise<Payload>;
22
+ //# sourceMappingURL=getPayloadClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getPayloadClient.d.ts","sourceRoot":"","sources":["../../src/lib/getPayloadClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,SAAS,CAAA;AASxE,sGAAsG;AACtG,eAAO,MAAM,WAAW,GAAI,QAAQ,eAAe,KAAG,IAErD,CAAA;AAMD,mGAAmG;AACnG,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG,IAE/C,CAAA;AAED,mGAAmG;AACnG,eAAO,MAAM,cAAc,QAAO,MAA0G,CAAA;AAE5I;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS,QAAa,OAAO,CAAC,eAAe,CAYzD,CAAA;AAED;;kDAEkD;AAClD,eAAO,MAAM,gBAAgB,QAAa,OAAO,CAAC,OAAO,CAA8C,CAAA"}
@@ -0,0 +1,39 @@
1
+ import { getPayload } from "payload";
2
+ /** The globalThis slot the plugin's `onInit` fills with the app's sanitized config.
3
+ * `Symbol.for` → one shared registry entry, so every pro-laico package reads the same
4
+ * stash without importing each other. */ const CONFIG_SLOT = Symbol.for('pro-laico.payload-config');
5
+ /** Called from the plugin's `onInit`: remember the running app's config for the server components. */ export const stashConfig = (config)=>{
6
+ ;
7
+ globalThis[CONFIG_SLOT] = config;
8
+ };
9
+ /** globalThis slot for the resolved `iconSet` slug, filled when the plugin is APPLIED (config build
10
+ * time), so it's set in any process that loaded the config — which `getConfig` always does. */ const ICON_SET_SLUG_SLOT = Symbol.for('pro-laico.payload-icons.iconSetSlug');
11
+ /** Called from the plugin factory: remember the resolved `iconSet` slug for the cache resolver. */ export const stashIconSetSlug = (slug)=>{
12
+ ;
13
+ globalThis[ICON_SET_SLUG_SLOT] = slug;
14
+ };
15
+ /** The app's icon-set collection slug (honors `iconSetOverrides.slug`); the default when unset. */ export const getIconSetSlug = ()=>globalThis[ICON_SET_SLUG_SLOT] ?? 'iconSet';
16
+ /**
17
+ * Resolve the host app's Payload config, in order:
18
+ * 1. the globalThis stash the plugin's `onInit` fills the moment Payload boots — covers every
19
+ * render after init with no bundler involvement, or
20
+ * 2. the `@payload-config` alias `withPayload` sets up — which only resolves from inside a
21
+ * published package when the consumer transpiles it
22
+ * (`transpilePackages: ['@pro-laico/payload-icons']` in next.config). The `as string` keeps
23
+ * TS from resolving the alias at this package's own build time.
24
+ */ export const getConfig = async ()=>{
25
+ const stashed = globalThis[CONFIG_SLOT];
26
+ if (stashed) return stashed;
27
+ try {
28
+ return await (await import("@payload-config")).default;
29
+ } catch {
30
+ throw new Error("[payload-icons] could not resolve the app's Payload config: Payload hasn't initialized in this process yet, and the '@payload-config' alias didn't resolve from inside this package. Either add '@pro-laico/payload-icons' to `transpilePackages` in next.config (so the host alias applies), or ensure something calls getPayload() before this component renders.");
31
+ }
32
+ };
33
+ /** A booted Payload instance, using the host app's config. The shared server-side entry point
34
+ * for the cache resolvers, the usage panel, and the runtime miss recorder, so the
35
+ * config-resolution trick lives in one place. */ export const getPayloadClient = async ()=>getPayload({
36
+ config: await getConfig()
37
+ });
38
+
39
+ //# sourceMappingURL=getPayloadClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/getPayloadClient.ts"],"sourcesContent":["import { getPayload, type Payload, type SanitizedConfig } from 'payload'\n\ntype Awaitable<T> = T | Promise<T>\n\n/** The globalThis slot the plugin's `onInit` fills with the app's sanitized config.\n * `Symbol.for` → one shared registry entry, so every pro-laico package reads the same\n * stash without importing each other. */\nconst CONFIG_SLOT = Symbol.for('pro-laico.payload-config')\n\n/** Called from the plugin's `onInit`: remember the running app's config for the server components. */\nexport const stashConfig = (config: SanitizedConfig): void => {\n ;(globalThis as Record<symbol, unknown>)[CONFIG_SLOT] = config\n}\n\n/** globalThis slot for the resolved `iconSet` slug, filled when the plugin is APPLIED (config build\n * time), so it's set in any process that loaded the config — which `getConfig` always does. */\nconst ICON_SET_SLUG_SLOT = Symbol.for('pro-laico.payload-icons.iconSetSlug')\n\n/** Called from the plugin factory: remember the resolved `iconSet` slug for the cache resolver. */\nexport const stashIconSetSlug = (slug: string): void => {\n ;(globalThis as Record<symbol, unknown>)[ICON_SET_SLUG_SLOT] = slug\n}\n\n/** The app's icon-set collection slug (honors `iconSetOverrides.slug`); the default when unset. */\nexport const getIconSetSlug = (): string => ((globalThis as Record<symbol, unknown>)[ICON_SET_SLUG_SLOT] as string | undefined) ?? 'iconSet'\n\n/**\n * Resolve the host app's Payload config, in order:\n * 1. the globalThis stash the plugin's `onInit` fills the moment Payload boots — covers every\n * render after init with no bundler involvement, or\n * 2. the `@payload-config` alias `withPayload` sets up — which only resolves from inside a\n * published package when the consumer transpiles it\n * (`transpilePackages: ['@pro-laico/payload-icons']` in next.config). The `as string` keeps\n * TS from resolving the alias at this package's own build time.\n */\nexport const getConfig = async (): Promise<SanitizedConfig> => {\n const stashed = (globalThis as Record<symbol, unknown>)[CONFIG_SLOT] as SanitizedConfig | undefined\n if (stashed) return stashed\n try {\n return (await ((\n await import('@payload-config' as string)\n ).default as Awaitable<SanitizedConfig>)) as SanitizedConfig\n } catch {\n throw new Error(\n \"[payload-icons] could not resolve the app's Payload config: Payload hasn't initialized in this process yet, and the '@payload-config' alias didn't resolve from inside this package. Either add '@pro-laico/payload-icons' to `transpilePackages` in next.config (so the host alias applies), or ensure something calls getPayload() before this component renders.\",\n )\n }\n}\n\n/** A booted Payload instance, using the host app's config. The shared server-side entry point\n * for the cache resolvers, the usage panel, and the runtime miss recorder, so the\n * config-resolution trick lives in one place. */\nexport const getPayloadClient = async (): Promise<Payload> => getPayload({ config: await getConfig() })\n"],"names":["getPayload","CONFIG_SLOT","Symbol","for","stashConfig","config","globalThis","ICON_SET_SLUG_SLOT","stashIconSetSlug","slug","getIconSetSlug","getConfig","stashed","default","Error","getPayloadClient"],"mappings":"AAAA,SAASA,UAAU,QAA4C,UAAS;AAIxE;;wCAEwC,GACxC,MAAMC,cAAcC,OAAOC,GAAG,CAAC;AAE/B,oGAAoG,GACpG,OAAO,MAAMC,cAAc,CAACC;;IACxBC,UAAsC,CAACL,YAAY,GAAGI;AAC1D,EAAC;AAED;8FAC8F,GAC9F,MAAME,qBAAqBL,OAAOC,GAAG,CAAC;AAEtC,iGAAiG,GACjG,OAAO,MAAMK,mBAAmB,CAACC;;IAC7BH,UAAsC,CAACC,mBAAmB,GAAGE;AACjE,EAAC;AAED,iGAAiG,GACjG,OAAO,MAAMC,iBAAiB,IAAc,AAAC,AAACJ,UAAsC,CAACC,mBAAmB,IAA2B,UAAS;AAE5I;;;;;;;;CAQC,GACD,OAAO,MAAMI,YAAY;IACvB,MAAMC,UAAU,AAACN,UAAsC,CAACL,YAAY;IACpE,IAAIW,SAAS,OAAOA;IACpB,IAAI;QACF,OAAQ,MAAO,AACb,CAAA,MAAM,MAAM,CAAC,kBAA2B,EACxCC,OAAO;IACX,EAAE,OAAM;QACN,MAAM,IAAIC,MACR;IAEJ;AACF,EAAC;AAED;;gDAEgD,GAChD,OAAO,MAAMC,mBAAmB,UAA8Bf,WAAW;QAAEK,QAAQ,MAAMM;IAAY,GAAE"}