@monkeyplus/flow 6.0.17 → 6.0.19

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 (161) hide show
  1. package/README.md +2 -0
  2. package/cms/dist/assets/Button-BqeUw3Nb.js +1 -0
  3. package/cms/dist/assets/Cms--iKNGffI.js +1 -0
  4. package/cms/dist/assets/Cms-DPkKSuvX.js +1 -0
  5. package/cms/dist/assets/Cms.vue_vue_type_script_setup_true_lang-BLjYlaya.js +1 -0
  6. package/cms/dist/assets/Collection-CC4xVuVM.js +1 -0
  7. package/cms/dist/assets/Collection-oMH_nwxS.js +1 -0
  8. package/cms/dist/assets/Collection-sfaJ9fwa.js +1 -0
  9. package/cms/dist/assets/Collection.vue_vue_type_script_setup_true_lang-DBh9sOKn.js +4 -0
  10. package/cms/dist/assets/DropdownMenu-pXjjYDJf.js +1 -0
  11. package/cms/dist/assets/EditorDragHandle-DjhDrza4.js +210 -0
  12. package/cms/dist/assets/Entry-D1FEE6Xe.js +1 -0
  13. package/cms/dist/assets/Entry-DIjoQMCn.js +1 -0
  14. package/cms/dist/assets/Entry-Dx-Gentu.js +1 -0
  15. package/cms/dist/assets/Entry-futdQUpk.js +1 -0
  16. package/cms/dist/assets/Entry.vue_vue_type_script_setup_true_lang-CGsCqUcC.js +1 -0
  17. package/cms/dist/assets/Entry.vue_vue_type_script_setup_true_lang-yZb441-Z.js +1 -0
  18. package/cms/dist/assets/Fields-1zzijKwy.css +1 -0
  19. package/cms/dist/assets/Fields-DG6uyVyw.js +1 -0
  20. package/cms/dist/assets/Main-Xea2Go7n.js +1 -0
  21. package/cms/dist/assets/Media-D9qcmiia.js +64 -0
  22. package/cms/dist/assets/Modal-DzMlh1K9.js +1 -0
  23. package/cms/dist/assets/Page-Bg6ZJkCi.js +1 -0
  24. package/cms/dist/assets/PageSideForm-CJvudHte.js +1 -0
  25. package/cms/dist/assets/PageSideForm-v14TdYOR.js +1 -0
  26. package/cms/dist/assets/Pages-CgtLUrd1.js +1 -0
  27. package/cms/dist/assets/Pages-Colx-y_M.js +1 -0
  28. package/cms/dist/assets/Presence-DXmvDu-B.js +3 -0
  29. package/cms/dist/assets/Preview-DyW6gcwI.js +1 -0
  30. package/cms/dist/assets/RovingFocusGroup-Zc2iLojI.js +1 -0
  31. package/cms/dist/assets/RovingFocusItem-XP2WbDfI.js +1 -0
  32. package/cms/dist/assets/Settings-6BY8hMNJ.js +1 -0
  33. package/cms/dist/assets/Sidebar-7tc1rH-m.js +1 -0
  34. package/cms/dist/assets/TooltipProvider-T49u14GZ.js +1 -0
  35. package/cms/dist/assets/_plugin-vue_export-helper-BDNMzG2s.js +1 -0
  36. package/cms/dist/assets/app-DBVODLiC.js +1 -0
  37. package/cms/dist/assets/app-DBac-q-m.css +2 -0
  38. package/cms/dist/assets/app-DzK4b1Um.js +1 -0
  39. package/cms/dist/assets/app.vue_vue_type_script_setup_true_lang-BXAw_NlS.js +13 -0
  40. package/cms/dist/assets/cms-D2eW8dB-.js +1 -0
  41. package/cms/dist/assets/dist-DASomEJF.js +1 -0
  42. package/cms/dist/assets/dist-TO46HCAG.js +1 -0
  43. package/cms/dist/assets/en.svg +11 -0
  44. package/cms/dist/assets/es-ec.svg +13 -0
  45. package/cms/dist/assets/es.svg +42 -0
  46. package/cms/dist/assets/index-1Es5a0_Z.js +18 -0
  47. package/cms/dist/assets/index-yfLwsAFs.css +1 -0
  48. package/cms/dist/assets/index.vue_vue_type_style_index_0_scoped_2e5c9142_lang-CfB8rLPy.js +1 -0
  49. package/cms/dist/assets/logger-DSeL-og4.js +10 -0
  50. package/cms/dist/assets/not-found-B8Yomlc0.js +1 -0
  51. package/cms/dist/assets/not-found-z4zyiO6n.js +1 -0
  52. package/cms/dist/assets/pages-B6dE90Nv.js +1 -0
  53. package/cms/dist/assets/pages-kaHLKSrN.js +1 -0
  54. package/cms/dist/assets/preview-D1CIVM6p.js +1 -0
  55. package/cms/dist/assets/useCmsBreadcrumb-BGzdrscS.js +1 -0
  56. package/cms/dist/assets/useOverlay-BON7Ngo_.js +1 -0
  57. package/cms/dist/assets/usePortal-C04XvxfO.js +1 -0
  58. package/cms/dist/assets/useToast-T1ue7roH.js +1 -0
  59. package/cms/dist/assets/utils-Cx7OEZFn.js +1 -0
  60. package/cms/dist/index.html +12 -0
  61. package/cms/server/database/schema.d.ts +648 -0
  62. package/cms/server/database/schema.mjs +43 -0
  63. package/cms/server/routes/api/auth/[...auth].d.ts +2 -0
  64. package/cms/server/routes/api/auth/[...auth].mjs +5 -0
  65. package/cms/server/routes/api/draft.d.ts +3 -0
  66. package/cms/server/routes/api/draft.mjs +26 -0
  67. package/cms/server/routes/api/repos/remote/[repo]/blobs/[file]/index.d.ts +2 -0
  68. package/cms/server/routes/api/repos/remote/[repo]/blobs/[file]/index.mjs +20 -0
  69. package/cms/server/routes/api/repos/remote/[repo]/blobs/index.post.d.ts +2 -0
  70. package/cms/server/routes/api/repos/remote/[repo]/blobs/index.post.mjs +8 -0
  71. package/cms/server/routes/api/repos/remote/[repo]/branches/[branch]/index.d.ts +2 -0
  72. package/cms/server/routes/api/repos/remote/[repo]/branches/[branch]/index.mjs +8 -0
  73. package/cms/server/routes/api/repos/remote/[repo]/commits/index.post.d.ts +2 -0
  74. package/cms/server/routes/api/repos/remote/[repo]/commits/index.post.mjs +8 -0
  75. package/cms/server/routes/api/repos/remote/[repo]/files/[branch]/index.d.ts +2 -0
  76. package/cms/server/routes/api/repos/remote/[repo]/files/[branch]/index.mjs +38 -0
  77. package/cms/server/routes/api/repos/remote/[repo]/files/index.post.d.ts +2 -0
  78. package/cms/server/routes/api/repos/remote/[repo]/files/index.post.mjs +8 -0
  79. package/cms/server/routes/api/repos/remote/[repo]/refs/heads/[branch]/index.patch.d.ts +2 -0
  80. package/cms/server/routes/api/repos/remote/[repo]/refs/heads/[branch]/index.patch.mjs +9 -0
  81. package/cms/server/utils/auth.d.ts +3 -0
  82. package/cms/server/utils/auth.mjs +16 -0
  83. package/cms/server/utils/db.d.ts +3 -0
  84. package/cms/server/utils/db.mjs +5 -0
  85. package/cms/server/utils/github.d.ts +15 -0
  86. package/cms/server/utils/github.mjs +160 -0
  87. package/cms/server/utils/github_token.d.ts +71 -0
  88. package/cms/server/utils/github_token.mjs +377 -0
  89. package/modules/cms/module.d.ts +11 -0
  90. package/modules/cms/module.mjs +163 -0
  91. package/modules/cms/server/api/admin.d.ts +2 -0
  92. package/modules/cms/server/api/admin.mjs +18 -0
  93. package/modules/cms/server/api/config.d.ts +2 -0
  94. package/modules/cms/server/api/config.mjs +88 -0
  95. package/modules/cms/server/api/localFs.d.ts +2 -0
  96. package/modules/cms/server/api/localFs.mjs +88 -0
  97. package/modules/cms/server/api/meta.d.ts +2 -0
  98. package/modules/cms/server/api/meta.mjs +12 -0
  99. package/modules/cms/server/lib/composables.d.ts +116 -0
  100. package/modules/cms/server/lib/composables.mjs +82 -0
  101. package/modules/cms/server/lib/fs.d.ts +1 -0
  102. package/modules/cms/server/lib/fs.mjs +18 -0
  103. package/modules/cms/server/lib/helpers.d.ts +14 -0
  104. package/modules/cms/server/lib/helpers.mjs +78 -0
  105. package/modules/cms/server/lib/types.d.ts +120 -0
  106. package/modules/cms/server/lib/types.mjs +0 -0
  107. package/modules/cms/server/lib/widgets.d.ts +82 -0
  108. package/modules/cms/server/lib/widgets.mjs +200 -0
  109. package/modules/cms/server/trial.d.ts +2 -0
  110. package/modules/cms/server/trial.mjs +8 -0
  111. package/modules/content/query.mjs +31 -3
  112. package/modules/images/ipx.mjs +4 -2
  113. package/modules/images/module.d.ts +0 -1
  114. package/modules/images/module.mjs +5 -3
  115. package/modules/images/runtime/build.mjs +12 -0
  116. package/modules/images/runtime/image.mjs +4 -3
  117. package/modules/images/runtime/renames.mjs +59 -8
  118. package/modules/images/runtime/types.d.ts +27 -4
  119. package/modules/images/watermark.d.ts +1 -0
  120. package/modules/images/watermark.mjs +113 -0
  121. package/modules/netlify-cms/handler.mjs +2 -1
  122. package/modules/netlify-cms/module.mjs +1 -1
  123. package/modules/netlify-cms/server/api/config.mjs +25 -1
  124. package/modules/netlify-cms/server/api/local-fs.d.ts +51 -0
  125. package/modules/netlify-cms/server/api/local-fs.mjs +81 -77
  126. package/modules/netlify-cms/server/lib/cms/handler.d.ts +1 -1
  127. package/modules/netlify-cms/server/lib/cms/handlerV1.d.ts +1 -1
  128. package/modules/netlify-cms/server/lib/composables.d.ts +8 -0
  129. package/modules/netlify-cms/server/lib/composables.mjs +2 -1
  130. package/monkeyplus-flow-6.0.18.tgz +0 -0
  131. package/package.json +10 -2
  132. package/server/lib/context.d.ts +3 -0
  133. package/server/lib/context.mjs +5 -0
  134. package/server/lib/handler.mjs +58 -23
  135. package/server/lib/pages.d.ts +2 -2
  136. package/server/lib/pages.mjs +8 -6
  137. package/server/lib/render.mjs +20 -4
  138. package/server/plugins/00.lifecycle.mjs +2 -1
  139. package/src/public/index.d.ts +1 -0
  140. package/src/public/index.mjs +1 -0
  141. package/src/public/nitro.mjs +2 -0
  142. package/src/public/query-content.mjs +9 -2
  143. package/src/public/shared.d.ts +1 -0
  144. package/src/public/shared.mjs +3 -0
  145. package/src/public/vite.mjs +63 -8
  146. package/src/runtime/components/FlowIsland.mjs +31 -4
  147. package/src/runtime/components/MkImage.d.ts +100 -22
  148. package/src/runtime/components/MkImage.mjs +20 -12
  149. package/src/runtime/components/MkLink.d.ts +8 -5
  150. package/src/runtime/components/MkLink.mjs +9 -3
  151. package/src/runtime/components/MkPicture.d.ts +92 -7
  152. package/src/runtime/components/MkPicture.mjs +8 -2
  153. package/src/runtime/components/image-shared.d.ts +0 -1
  154. package/src/runtime/components/image-shared.mjs +9 -18
  155. package/src/runtime/config.d.ts +6 -15
  156. package/src/runtime/head.d.ts +2 -1
  157. package/src/runtime/head.mjs +5 -2
  158. package/src/runtime/islands.mjs +20 -2
  159. package/src/runtime/page-discovery.mjs +9 -1
  160. package/src/runtime/pages.d.ts +14 -13
  161. package/src/runtime/virtual-pages.mjs +2 -2
@@ -0,0 +1,200 @@
1
+ import { join } from "node:path";
2
+ import { mapObjIndexed } from "./helpers.mjs";
3
+ function returnWidget(widget, options = {}) {
4
+ return (name, _ctx) => ({
5
+ name,
6
+ ...widget,
7
+ label: name,
8
+ ...options
9
+ });
10
+ }
11
+ export function boolean(options) {
12
+ return returnWidget(
13
+ { widget: "boolean" },
14
+ options
15
+ );
16
+ }
17
+ export function editor(options) {
18
+ return returnWidget(
19
+ { widget: "editor" },
20
+ options
21
+ );
22
+ }
23
+ export function color(options) {
24
+ return returnWidget(
25
+ { widget: "color" },
26
+ options
27
+ );
28
+ }
29
+ export function dateTime(options) {
30
+ return returnWidget(
31
+ { widget: "date" },
32
+ options
33
+ );
34
+ }
35
+ export function list(options) {
36
+ return (id, ctx) => {
37
+ return object(
38
+ { ...options, widget: "list", fields: options.fields }
39
+ )(id, ctx);
40
+ };
41
+ }
42
+ export function number(options) {
43
+ return returnWidget(
44
+ { widget: "number" },
45
+ options
46
+ );
47
+ }
48
+ export function object(options) {
49
+ return (id, ctx) => {
50
+ const _fields = mapObjIndexed((buildField, id2) => {
51
+ return buildField(id2, ctx);
52
+ }, options.fields);
53
+ return {
54
+ name: id,
55
+ widget: "object",
56
+ ...options,
57
+ fields: Object.values(_fields)
58
+ };
59
+ };
60
+ }
61
+ export function select(options) {
62
+ return returnWidget(
63
+ { widget: "select" },
64
+ options
65
+ );
66
+ }
67
+ export function text(options) {
68
+ return returnWidget({ widget: "text" }, options);
69
+ }
70
+ export function string(options) {
71
+ return returnWidget(
72
+ { widget: "string" },
73
+ options
74
+ );
75
+ }
76
+ export function file(options) {
77
+ return returnWidget({ widget: "file" }, options);
78
+ }
79
+ export function image(options) {
80
+ return (name, ctx) => {
81
+ if (options?.mediaFolder)
82
+ options.mediaFolder = join("", options.mediaFolder);
83
+ return returnWidget(
84
+ { widget: "image" },
85
+ options
86
+ )(name, ctx);
87
+ };
88
+ }
89
+ export function imageObject(options) {
90
+ return (name, ctx) => {
91
+ if (options?.mediaFolder)
92
+ options.mediaFolder = join("", options.mediaFolder);
93
+ return {
94
+ name,
95
+ widget: "object",
96
+ label: options?.label,
97
+ collapsed: false,
98
+ fields: [
99
+ returnWidget(
100
+ { widget: "image" },
101
+ options
102
+ )("src", ctx),
103
+ returnWidget(
104
+ { widget: "string" },
105
+ {
106
+ required: false
107
+ }
108
+ )("alt", {
109
+ ...ctx
110
+ }),
111
+ returnWidget(
112
+ { widget: "string" },
113
+ {
114
+ required: false
115
+ }
116
+ )("title", ctx),
117
+ returnWidget(
118
+ { widget: "string" },
119
+ {
120
+ required: false
121
+ }
122
+ )("rename", ctx)
123
+ ]
124
+ };
125
+ };
126
+ }
127
+ export function link(options) {
128
+ return (name, ctx) => {
129
+ return {
130
+ name,
131
+ widget: "object",
132
+ label: options?.label,
133
+ ...options,
134
+ fields: [
135
+ // Label
136
+ returnWidget(
137
+ { widget: "string" },
138
+ { label: "Texto" }
139
+ )("label", ctx),
140
+ // Link
141
+ returnWidget(
142
+ { widget: "select" },
143
+ {
144
+ required: false,
145
+ items: ["Interna", "Externa"]
146
+ }
147
+ )("type", {
148
+ ...ctx
149
+ }),
150
+ // Link
151
+ returnWidget(
152
+ { widget: "string" },
153
+ {
154
+ required: false,
155
+ label: "Link",
156
+ options: {
157
+ show: {
158
+ type: "Externa"
159
+ }
160
+ }
161
+ }
162
+ )("link", {
163
+ ...ctx
164
+ }),
165
+ // Link 2
166
+ returnWidget(
167
+ { widget: "select" },
168
+ {
169
+ label: "P\xE1gina",
170
+ required: false,
171
+ items: ["uno", "dos"],
172
+ options: {
173
+ alias: "link",
174
+ list: "pages",
175
+ show: {
176
+ type: "Interna"
177
+ }
178
+ }
179
+ }
180
+ )("link2", ctx)
181
+ ]
182
+ };
183
+ };
184
+ }
185
+ export const widgets = {
186
+ string,
187
+ color,
188
+ number,
189
+ file,
190
+ dateTime,
191
+ image,
192
+ editor,
193
+ object,
194
+ boolean,
195
+ list,
196
+ text,
197
+ select,
198
+ imageObject,
199
+ link
200
+ };
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,8 @@
1
+ import { defineEventHandler, getRouterParams } from "nitro/h3";
2
+ export default defineEventHandler(async (event) => {
3
+ const params = getRouterParams(event);
4
+ return {
5
+ message: "hello",
6
+ params
7
+ };
8
+ });
@@ -1,4 +1,5 @@
1
1
  import { extname } from "node:path";
2
+ import { consola } from "consola";
2
3
  import { defineEventHandler, getQuery, getRequestURL } from "nitro/h3";
3
4
  import { useStorage } from "nitro/storage";
4
5
  function normalizeQueryPath(path) {
@@ -85,6 +86,31 @@ function parseContentFile(keyPath, raw) {
85
86
  };
86
87
  }
87
88
  export async function readContentEntries() {
89
+ const applyCmsPreview = (entries2) => {
90
+ try {
91
+ const previewData = globalThis.__cmsPreviewData;
92
+ if (previewData) {
93
+ for (const [rawKey, payload] of Object.entries(previewData)) {
94
+ if (!payload || typeof payload !== "object")
95
+ continue;
96
+ let targetPath = rawKey;
97
+ if (targetPath.startsWith("/content/")) {
98
+ targetPath = targetPath.slice(8);
99
+ }
100
+ targetPath = targetPath.replace(/\.(json|md|yaml|yml|txt)$/i, "");
101
+ const entry = entries2.find((e) => e.path === targetPath);
102
+ if (entry) {
103
+ entry.data = { ...entry.data, ...payload };
104
+ if ("title" in payload) {
105
+ entry.title = payload.title;
106
+ }
107
+ }
108
+ }
109
+ }
110
+ } catch (e) {
111
+ }
112
+ return entries2;
113
+ };
88
114
  const storage = useStorage("content");
89
115
  const keys = await storage.getKeys();
90
116
  const entries = [];
@@ -116,9 +142,10 @@ export async function readContentEntries() {
116
142
  await walkDir(contentDir, contentDir);
117
143
  } catch (e) {
118
144
  }
119
- return entries.sort((left, right) => left.path.localeCompare(right.path));
145
+ const sorted = entries.sort((left, right) => left.path.localeCompare(right.path));
146
+ return applyCmsPreview(sorted);
120
147
  } catch (e) {
121
- console.error("[Flow Content] fs fallback failed:", e);
148
+ consola.error("[Flow Content] fs fallback failed:", e);
122
149
  }
123
150
  }
124
151
  for (const key of keys) {
@@ -133,7 +160,8 @@ export async function readContentEntries() {
133
160
  entries.push(entry);
134
161
  }
135
162
  }
136
- return entries.sort((left, right) => left.path.localeCompare(right.path));
163
+ const sortedEntries = entries.sort((left, right) => left.path.localeCompare(right.path));
164
+ return applyCmsPreview(sortedEntries);
137
165
  }
138
166
  function sortTree(nodes) {
139
167
  nodes.sort((left, right) => {
@@ -1,8 +1,10 @@
1
1
  import process from "node:process";
2
+ import { consola } from "consola";
2
3
  import { createIPX, createIPXH3Handler, ipxFSStorage, ipxHttpStorage } from "ipx";
3
4
  import { defineHandler } from "nitro";
4
5
  import { getRequestURL } from "nitro/h3";
5
6
  import { useRuntimeConfig } from "nitro/runtime-config";
7
+ import { withWatermark } from "./watermark.mjs";
6
8
  let cachedDir = "";
7
9
  let cachedDomainsKey = "";
8
10
  let cachedHandler;
@@ -36,13 +38,13 @@ function resolveHandler() {
36
38
  httpStorage: ipxHttpStorage({ domains })
37
39
  } : {}
38
40
  });
39
- cachedHandler = createIPXH3Handler(ipx);
41
+ cachedHandler = createIPXH3Handler(withWatermark(ipx));
40
42
  }
41
43
  return cachedHandler;
42
44
  }
43
45
  export default defineHandler(async (event) => {
44
46
  if (!getRequestURL(event)?.pathname.startsWith("/_ipx")) {
45
- console.log("route", event.req.url.toString());
47
+ consola.log("route", event.req.url.toString());
46
48
  return;
47
49
  }
48
50
  const handler = resolveHandler();
@@ -1,4 +1,3 @@
1
1
  import type { FlowImagesModuleOptions } from './runtime/types.ts';
2
- export type { FlowImagesModuleOptions as ImagesModuleOptions } from './runtime/types.ts';
3
2
  declare const _default: import("../../src/runtime/config.ts").FlowModuleDefinition<FlowImagesModuleOptions>;
4
3
  export default _default;
@@ -1,12 +1,14 @@
1
1
  import { rmSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import process from "node:process";
4
+ import { consola } from "consola";
4
5
  import { createIPX, createIPXNodeServer, ipxFSStorage, ipxHttpStorage } from "ipx";
5
6
  import { resolvePackageFile, resolvePackagePath } from "../../src/public/shared.mjs";
6
7
  import { defineFlowModule } from "../../src/runtime/config.mjs";
7
8
  import { materializeGeneratedImages } from "./runtime/build.mjs";
8
9
  import { screens } from "./runtime/helpers.mjs";
9
10
  import { resetFlowImageRuntimeState } from "./runtime/server.mjs";
11
+ import { withWatermark } from "./watermark.mjs";
10
12
  function withoutTrailingSlash(value) {
11
13
  return value.replace(/\/+$/, "");
12
14
  }
@@ -51,7 +53,7 @@ function createIpxDevServerPlugin(imagesRuntimeConfig) {
51
53
  storage: ipxFSStorage({ dir: imagesRuntimeConfig.publicDir }),
52
54
  ...domains.length ? { httpStorage: ipxHttpStorage({ domains }) } : {}
53
55
  });
54
- middleware = createIPXNodeServer(ipx);
56
+ middleware = createIPXNodeServer(withWatermark(ipx));
55
57
  }
56
58
  server.middlewares.use("/_ipx", (req, res, next) => {
57
59
  try {
@@ -78,7 +80,7 @@ export default defineFlowModule({
78
80
  lazy: true,
79
81
  screens,
80
82
  baseURL: "/_ipx",
81
- dirImages: "/images",
83
+ dirImages: "/assets/images",
82
84
  domains: {},
83
85
  presets: {}
84
86
  },
@@ -129,7 +131,7 @@ export default defineFlowModule({
129
131
  }
130
132
  const result = await materializeGeneratedImages(imagesRuntimeConfig);
131
133
  if (result.total > 0) {
132
- console.log(
134
+ consola.log(
133
135
  `[flow:images] materialized ${result.total} images (${result.generated} generated, ${result.cacheHits} cache hits${imagesRuntimeConfig.netlifyCache && process.env.NETLIFY ? ", netlify cache enabled" : ""})`
134
136
  );
135
137
  }
@@ -129,6 +129,12 @@ export async function materializeGeneratedImages(config) {
129
129
  let cacheHits = 0;
130
130
  let generated = 0;
131
131
  const batchSize = resolveConfiguredBatchSize(config.buildBatchSize);
132
+ const originalSourcesToDelete = /* @__PURE__ */ new Set();
133
+ for (const image of images) {
134
+ if (image.isRenamed) {
135
+ originalSourcesToDelete.add(image.src);
136
+ }
137
+ }
132
138
  for (const batch of createBatches(images, batchSize)) {
133
139
  const results = await Promise.all(batch.map(async (image) => {
134
140
  const signature = await createSignature(image, config.publicDir);
@@ -166,6 +172,12 @@ export async function materializeGeneratedImages(config) {
166
172
  await writeFile(config.generatedCacheManifestPath, `${JSON.stringify(nextManifest, null, 2)}
167
173
  `, "utf8");
168
174
  }
175
+ for (const src of originalSourcesToDelete) {
176
+ const originalPath = outputFilePath(config.outputDir, src);
177
+ if (existsSync(originalPath)) {
178
+ await rm(originalPath, { force: true });
179
+ }
180
+ }
169
181
  return {
170
182
  cacheHits,
171
183
  generated,
@@ -158,7 +158,7 @@ export function createImageResolver(imagesOptions, stateImages, runtime = {}) {
158
158
  image.ext = image.format && `.${image.format}` || guessExt(logicalInput);
159
159
  let baseDir = isStrapi ? normalizeDirImages(imagesOptions.dirImages) : getBaseDir(sourceInput, imagesOptions.dirImages);
160
160
  const originalExt = guessExt(logicalInput);
161
- let renamedImage = stateImages.all[logicalInput]?.rename;
161
+ let renamedImage = stateImages.all[sourceInput]?.rename || stateImages.all[logicalInput]?.rename;
162
162
  if (!renamedImage && imagesOptions.domains) {
163
163
  renamedImage = resolveRemoteRename(sourceInput, imagesOptions.domains);
164
164
  }
@@ -181,12 +181,13 @@ export function createImageResolver(imagesOptions, stateImages, runtime = {}) {
181
181
  ...image,
182
182
  src: image.src,
183
183
  generate: image.generate,
184
- modifiers: image.modifiers
184
+ modifiers: image.modifiers,
185
+ isRenamed: !!resolvedOptions.rename || !!renamedImage
185
186
  });
186
187
  }
187
188
  const src = runtime.generateOutput && image.generate ? image.generate : image.url;
188
189
  if (resolvedOptions._meta) {
189
- const meta = { ...stateImages.all[logicalInput] || { name: logicalInput, alt: void 0, title: void 0 } };
190
+ const meta = { ...stateImages.all[sourceInput] || stateImages.all[logicalInput] || { name: logicalInput, alt: void 0, title: void 0 } };
190
191
  if (!meta.alt) {
191
192
  meta.alt = getNormalName(meta.name);
192
193
  }
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync, readdirSync } from "node:fs";
2
2
  import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
3
3
  import { joinURL } from "ufo";
4
+ import { consola } from "consola";
4
5
  const RENAME_FILE = "_rename";
5
6
  function normalizePublicPath(path) {
6
7
  return `/${path}`.replace(/\\/g, "/").replace(/\/+/g, "/");
@@ -28,27 +29,57 @@ function toPublicImagePath(rootDir, manifestDir, fileName) {
28
29
  const relativeDir = relative(rootDir, manifestDir).split(sep).join("/");
29
30
  return normalizePublicPath(joinURL(relativeDir, fileName));
30
31
  }
32
+ function formatKebabCase(str) {
33
+ return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "").trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9\-]/g, "").replace(/-+/g, "-");
34
+ }
35
+ function findOriginalExtension(manifestDir, baseName) {
36
+ try {
37
+ for (const entry of readdirSync(manifestDir, { withFileTypes: true })) {
38
+ if (entry.isFile() && entry.name !== RENAME_FILE) {
39
+ const ext = extname(entry.name);
40
+ const nameWithoutExt = basename(entry.name, ext);
41
+ if (nameWithoutExt === baseName) {
42
+ return ext;
43
+ }
44
+ }
45
+ }
46
+ } catch {
47
+ }
48
+ return "";
49
+ }
31
50
  function parseRenameLine(rootDir, filePath, line) {
32
51
  const trimmed = line.trim();
33
52
  if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("//")) {
34
53
  return void 0;
35
54
  }
36
- const [original, renamed, title, ...altParts] = trimmed.split(/\s+/);
37
- if (!original || !renamed || isHeaderLine(original, renamed)) {
55
+ const parts = trimmed.split("|").map((s) => s.trim());
56
+ let original = parts[0];
57
+ const rawRenamed = parts[1];
58
+ const title = parts[2];
59
+ const alt = parts[3];
60
+ if (!original || !rawRenamed || isHeaderLine(original, rawRenamed)) {
38
61
  return void 0;
39
62
  }
40
- const sourceExt = extname(original);
41
- const renamedWithExt = extname(renamed) ? renamed : `${renamed}${sourceExt}`;
42
63
  const manifestDir = dirname(filePath);
64
+ let sourceExt = extname(original);
65
+ if (!sourceExt) {
66
+ sourceExt = findOriginalExtension(manifestDir, original);
67
+ if (sourceExt) {
68
+ original = `${original}${sourceExt}`;
69
+ }
70
+ }
71
+ const rawRenamedExt = extname(rawRenamed);
72
+ const renamedBase = formatKebabCase(rawRenamedExt ? basename(rawRenamed, rawRenamedExt) : rawRenamed);
73
+ const renamedWithExt = rawRenamedExt ? `${renamedBase}${rawRenamedExt}` : `${renamedBase}${sourceExt}`;
43
74
  const sourcePath = toPublicImagePath(rootDir, manifestDir, original);
44
75
  const renamedPath = toPublicImagePath(rootDir, manifestDir, renamedWithExt);
45
76
  return {
46
77
  key: sourcePath,
47
78
  value: {
48
79
  rename: renamedPath,
49
- name: basename(renamedWithExt, extname(renamedWithExt)),
50
- title,
51
- alt: altParts.length ? altParts.join(" ") : void 0
80
+ name: renamedBase,
81
+ title: title || void 0,
82
+ alt: alt || void 0
52
83
  }
53
84
  };
54
85
  }
@@ -67,7 +98,7 @@ function readRenameDirectory(rootDir) {
67
98
  return entries;
68
99
  }
69
100
  export function loadImageRenames(sources) {
70
- return sources.reduce((collection, source) => {
101
+ const allEntries = sources.reduce((collection, source) => {
71
102
  if (!source || !existsSync(source)) {
72
103
  return collection;
73
104
  }
@@ -76,4 +107,24 @@ export function loadImageRenames(sources) {
76
107
  ...readRenameDirectory(source)
77
108
  };
78
109
  }, {});
110
+ const renamedToOriginal = {};
111
+ for (const [originalPath, meta] of Object.entries(allEntries)) {
112
+ const renamedPath = meta.rename;
113
+ if (!renamedToOriginal[renamedPath]) {
114
+ renamedToOriginal[renamedPath] = [];
115
+ }
116
+ renamedToOriginal[renamedPath].push(originalPath);
117
+ }
118
+ for (const [renamedPath, originalPaths] of Object.entries(renamedToOriginal)) {
119
+ if (originalPaths.length > 1) {
120
+ consola.warn(
121
+ `
122
+ [Flow Images] \u26A0\uFE0F ALERTA: M\xFAltiples im\xE1genes intentan renombrarse al mismo destino "${renamedPath}".
123
+ Im\xE1genes originales:
124
+ ${originalPaths.map((p) => ` - ${p}`).join("\n")}
125
+ `
126
+ );
127
+ }
128
+ }
129
+ return allEntries;
79
130
  }
@@ -16,11 +16,33 @@ export interface FlowImageMeta {
16
16
  alt?: string;
17
17
  title?: string;
18
18
  }
19
+ export interface WatermarkOptions {
20
+ text?: string;
21
+ image?: string;
22
+ opacity?: number;
23
+ position?: string;
24
+ x?: number;
25
+ y?: number;
26
+ color?: string;
27
+ background?: string;
28
+ fontSize?: number;
29
+ fontWeight?: string;
30
+ ratio?: number;
31
+ }
19
32
  export interface ImageModifiers {
20
- width?: number;
21
- height?: number;
22
- fit?: string;
23
- format?: string;
33
+ width?: number | string;
34
+ height?: number | string;
35
+ fit?: 'contain' | 'cover' | 'fill' | 'inside' | 'outside' | string;
36
+ format?: 'auto' | 'webp' | 'jpeg' | 'png' | 'avif' | 'gif' | string;
37
+ quality?: number | string;
38
+ background?: string;
39
+ blur?: number | string;
40
+ trim?: number | string;
41
+ position?: 'top' | 'right top' | 'right' | 'right bottom' | 'bottom' | 'left bottom' | 'left' | 'left top' | 'center' | string;
42
+ grayscale?: boolean | string;
43
+ rotate?: number | string;
44
+ size?: string;
45
+ watermark?: boolean | string | WatermarkOptions;
24
46
  [key: string]: any;
25
47
  }
26
48
  export interface ImageOptions {
@@ -51,6 +73,7 @@ export interface GeneratedImageEntry extends ResolvedImage {
51
73
  src: string;
52
74
  generate: string;
53
75
  modifiers: Record<string, string>;
76
+ isRenamed?: boolean;
54
77
  }
55
78
  export type GetImageFunction = ((input: string, modifiers?: Record<string, any>, options?: ImageOptions) => string | Record<string, unknown>) & {
56
79
  getSizes: (input: string, options: ImageSizesOptions) => ImageSizes;
@@ -0,0 +1 @@
1
+ export declare function withWatermark(ipxInstance: any): (id: string, modifiers?: Record<string, string>, opts?: any) => any;
@@ -0,0 +1,113 @@
1
+ import { Buffer } from "node:buffer";
2
+ import { resolve } from "node:path";
3
+ import process from "node:process";
4
+ import { consola } from "consola";
5
+ const positionsMap = {
6
+ "top": "north",
7
+ "bottom": "south",
8
+ "center": "center",
9
+ "centre": "centre",
10
+ "left": "west",
11
+ "right": "east",
12
+ "top-left": "northwest",
13
+ "top-right": "northeast",
14
+ "bottom-left": "southwest",
15
+ "bottom-right": "southeast",
16
+ "southeast": "southeast",
17
+ "southwest": "southwest",
18
+ "northeast": "northeast",
19
+ "northwest": "northwest",
20
+ "north": "north",
21
+ "south": "south",
22
+ "east": "east",
23
+ "west": "west"
24
+ };
25
+ function getAlpha(opacity) {
26
+ if (opacity === void 0)
27
+ return "e6";
28
+ return Math.round(opacity * 255).toString(16).padStart(2, "0");
29
+ }
30
+ export function withWatermark(ipxInstance) {
31
+ return (id, modifiers = {}, opts = {}) => {
32
+ const hasWatermark = modifiers.watermark !== void 0;
33
+ const watermarkRaw = typeof modifiers.watermark === "string" && modifiers.watermark !== "true" && modifiers.watermark !== "" ? modifiers.watermark : "WATERMARK";
34
+ if (hasWatermark) {
35
+ delete modifiers.watermark;
36
+ }
37
+ const instance = ipxInstance(id, modifiers, opts);
38
+ const origProcess = instance.process;
39
+ instance.process = async () => {
40
+ const res = await origProcess();
41
+ if (hasWatermark && res.data && typeof res.data !== "string" && res.format !== "gif" && res.format !== "svg") {
42
+ try {
43
+ const sharp = (await import("sharp")).default;
44
+ let watermarkOptions = {
45
+ text: "WATERMARK",
46
+ opacity: 0.9,
47
+ position: "southeast",
48
+ color: "#ffffff",
49
+ background: "#00000080",
50
+ fontWeight: "bold"
51
+ };
52
+ if (modifiers.watermark !== "true") {
53
+ const decodedText = decodeURIComponent(watermarkRaw).replace(/_/g, " ");
54
+ if (decodedText.startsWith("b64")) {
55
+ try {
56
+ const jsonStr = Buffer.from(decodedText.slice(3), "base64").toString("utf-8");
57
+ const parsed = JSON.parse(jsonStr);
58
+ watermarkOptions = { ...watermarkOptions, ...parsed };
59
+ } catch (e) {
60
+ consola.error("[flow:images] Error parsing watermark b64:", e);
61
+ watermarkOptions.text = decodedText;
62
+ }
63
+ } else {
64
+ watermarkOptions.text = decodedText;
65
+ }
66
+ }
67
+ consola.log("[flow:images] Aplicando marca de agua din\xE1mica:", watermarkOptions);
68
+ const gravity = positionsMap[watermarkOptions.position || "southeast"] || "southeast";
69
+ if (watermarkOptions.image) {
70
+ const watermarkPath = resolve(process.cwd(), watermarkOptions.image);
71
+ let watermarkBuffer = await sharp(watermarkPath).toBuffer();
72
+ if (watermarkOptions.ratio) {
73
+ const mainMeta = await sharp(res.data).metadata();
74
+ const waterMarkWidth = Math.round((mainMeta.width || 1e3) * watermarkOptions.ratio);
75
+ watermarkBuffer = await sharp(watermarkBuffer).resize(waterMarkWidth).toBuffer();
76
+ }
77
+ res.data = await sharp(res.data).composite([{
78
+ input: watermarkBuffer,
79
+ gravity,
80
+ top: watermarkOptions.y,
81
+ left: watermarkOptions.x,
82
+ blend: "over"
83
+ }]).toBuffer();
84
+ } else {
85
+ const text = watermarkOptions.text || "WATERMARK";
86
+ const color = watermarkOptions.color || "#ffffff";
87
+ const weight = watermarkOptions.fontWeight || "bold";
88
+ const pangoMarkup = `<span foreground="${color}" font_weight="${weight}"> ${text} </span>`;
89
+ res.data = await sharp(res.data).composite([{
90
+ input: {
91
+ text: {
92
+ text: pangoMarkup,
93
+ rgba: true,
94
+ dpi: watermarkOptions.fontSize ? watermarkOptions.fontSize * 5 : 150,
95
+ // rough dpi estimation based on size
96
+ align: "center"
97
+ }
98
+ },
99
+ gravity,
100
+ top: watermarkOptions.y,
101
+ left: watermarkOptions.x,
102
+ blend: "over"
103
+ }]).toBuffer();
104
+ }
105
+ } catch (err) {
106
+ consola.error("[flow:images] Error applying watermark:", err);
107
+ }
108
+ }
109
+ return res;
110
+ };
111
+ return instance;
112
+ };
113
+ }
@@ -1,5 +1,6 @@
1
+ import { consola } from "consola";
1
2
  import { defineEventHandler } from "nitro/h3";
2
- console.log("Loading handler.ts");
3
+ consola.log("Loading handler.ts");
3
4
  export default defineEventHandler(async (event) => {
4
5
  return "hola";
5
6
  });