@monkeyplus/flow 6.0.16 → 6.0.18

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 (102) hide show
  1. package/README.md +2 -0
  2. package/cms/server/database/schema.d.ts +648 -0
  3. package/cms/server/database/schema.mjs +43 -0
  4. package/cms/server/routes/api/auth/[...auth].d.ts +2 -0
  5. package/cms/server/routes/api/auth/[...auth].mjs +5 -0
  6. package/cms/server/routes/api/draft.d.ts +3 -0
  7. package/cms/server/routes/api/draft.mjs +26 -0
  8. package/cms/server/routes/api/repos/remote/[repo]/blobs/[file]/index.d.ts +2 -0
  9. package/cms/server/routes/api/repos/remote/[repo]/blobs/[file]/index.mjs +20 -0
  10. package/cms/server/routes/api/repos/remote/[repo]/blobs/index.post.d.ts +2 -0
  11. package/cms/server/routes/api/repos/remote/[repo]/blobs/index.post.mjs +8 -0
  12. package/cms/server/routes/api/repos/remote/[repo]/branches/[branch]/index.d.ts +2 -0
  13. package/cms/server/routes/api/repos/remote/[repo]/branches/[branch]/index.mjs +8 -0
  14. package/cms/server/routes/api/repos/remote/[repo]/commits/index.post.d.ts +2 -0
  15. package/cms/server/routes/api/repos/remote/[repo]/commits/index.post.mjs +8 -0
  16. package/cms/server/routes/api/repos/remote/[repo]/files/[branch]/index.d.ts +2 -0
  17. package/cms/server/routes/api/repos/remote/[repo]/files/[branch]/index.mjs +38 -0
  18. package/cms/server/routes/api/repos/remote/[repo]/files/index.post.d.ts +2 -0
  19. package/cms/server/routes/api/repos/remote/[repo]/files/index.post.mjs +8 -0
  20. package/cms/server/routes/api/repos/remote/[repo]/refs/heads/[branch]/index.patch.d.ts +2 -0
  21. package/cms/server/routes/api/repos/remote/[repo]/refs/heads/[branch]/index.patch.mjs +9 -0
  22. package/cms/server/utils/auth.d.ts +3 -0
  23. package/cms/server/utils/auth.mjs +16 -0
  24. package/cms/server/utils/db.d.ts +3 -0
  25. package/cms/server/utils/db.mjs +5 -0
  26. package/cms/server/utils/github.d.ts +15 -0
  27. package/cms/server/utils/github.mjs +160 -0
  28. package/cms/server/utils/github_token.d.ts +71 -0
  29. package/cms/server/utils/github_token.mjs +377 -0
  30. package/modules/cms/module.d.ts +11 -0
  31. package/modules/cms/module.mjs +163 -0
  32. package/modules/cms/server/api/admin.d.ts +2 -0
  33. package/modules/cms/server/api/admin.mjs +18 -0
  34. package/modules/cms/server/api/config.d.ts +2 -0
  35. package/modules/cms/server/api/config.mjs +88 -0
  36. package/modules/cms/server/api/localFs.d.ts +2 -0
  37. package/modules/cms/server/api/localFs.mjs +88 -0
  38. package/modules/cms/server/api/meta.d.ts +2 -0
  39. package/modules/cms/server/api/meta.mjs +12 -0
  40. package/modules/cms/server/lib/composables.d.ts +116 -0
  41. package/modules/cms/server/lib/composables.mjs +82 -0
  42. package/modules/cms/server/lib/fs.d.ts +1 -0
  43. package/modules/cms/server/lib/fs.mjs +18 -0
  44. package/modules/cms/server/lib/helpers.d.ts +14 -0
  45. package/modules/cms/server/lib/helpers.mjs +78 -0
  46. package/modules/cms/server/lib/types.d.ts +120 -0
  47. package/modules/cms/server/lib/types.mjs +0 -0
  48. package/modules/cms/server/lib/widgets.d.ts +82 -0
  49. package/modules/cms/server/lib/widgets.mjs +200 -0
  50. package/modules/cms/server/trial.d.ts +2 -0
  51. package/modules/cms/server/trial.mjs +8 -0
  52. package/modules/content/query.mjs +31 -3
  53. package/modules/images/ipx.mjs +4 -2
  54. package/modules/images/module.d.ts +0 -1
  55. package/modules/images/module.mjs +5 -3
  56. package/modules/images/runtime/build.mjs +12 -0
  57. package/modules/images/runtime/image.mjs +4 -3
  58. package/modules/images/runtime/renames.mjs +59 -8
  59. package/modules/images/runtime/types.d.ts +27 -4
  60. package/modules/images/watermark.d.ts +1 -0
  61. package/modules/images/watermark.mjs +113 -0
  62. package/modules/netlify-cms/handler.mjs +2 -1
  63. package/modules/netlify-cms/module.mjs +1 -1
  64. package/modules/netlify-cms/server/api/config.mjs +25 -1
  65. package/modules/netlify-cms/server/api/local-fs.d.ts +51 -0
  66. package/modules/netlify-cms/server/api/local-fs.mjs +81 -77
  67. package/modules/netlify-cms/server/lib/cms/handler.d.ts +1 -1
  68. package/modules/netlify-cms/server/lib/cms/handlerV1.d.ts +1 -1
  69. package/modules/netlify-cms/server/lib/composables.d.ts +8 -0
  70. package/modules/netlify-cms/server/lib/composables.mjs +2 -1
  71. package/package.json +2 -2
  72. package/server/lib/context.d.ts +3 -0
  73. package/server/lib/context.mjs +5 -0
  74. package/server/lib/handler.mjs +58 -23
  75. package/server/lib/pages.d.ts +2 -2
  76. package/server/lib/pages.mjs +8 -6
  77. package/server/lib/render.mjs +20 -4
  78. package/server/plugins/00.lifecycle.mjs +2 -1
  79. package/src/public/index.d.ts +1 -0
  80. package/src/public/index.mjs +1 -0
  81. package/src/public/nitro.mjs +2 -0
  82. package/src/public/query-content.mjs +9 -2
  83. package/src/public/shared.d.ts +1 -0
  84. package/src/public/shared.mjs +3 -0
  85. package/src/public/vite.mjs +63 -8
  86. package/src/runtime/components/FlowIsland.mjs +32 -5
  87. package/src/runtime/components/MkImage.d.ts +100 -22
  88. package/src/runtime/components/MkImage.mjs +20 -12
  89. package/src/runtime/components/MkLink.d.ts +8 -5
  90. package/src/runtime/components/MkLink.mjs +9 -3
  91. package/src/runtime/components/MkPicture.d.ts +92 -7
  92. package/src/runtime/components/MkPicture.mjs +8 -2
  93. package/src/runtime/components/image-shared.d.ts +0 -1
  94. package/src/runtime/components/image-shared.mjs +9 -18
  95. package/src/runtime/config.d.ts +6 -15
  96. package/src/runtime/head.d.ts +2 -1
  97. package/src/runtime/head.mjs +5 -2
  98. package/src/runtime/islands.mjs +20 -2
  99. package/src/runtime/page-discovery.mjs +9 -1
  100. package/src/runtime/pages.d.ts +14 -13
  101. package/src/runtime/virtual-pages.mjs +2 -2
  102. package/src/runtime/vue.mjs +10 -0
@@ -0,0 +1,377 @@
1
+ const REVALIDATE_INTERVAL = 6e4;
2
+ export class GHToken {
3
+ token;
4
+ valid;
5
+ remaining;
6
+ limit;
7
+ reset;
8
+ _lastValidated;
9
+ _app;
10
+ _appInstallationId;
11
+ constructor(token, opts) {
12
+ this.token = token;
13
+ this._app = opts?.app;
14
+ this._appInstallationId = opts?.appInstallationId;
15
+ }
16
+ get maskedToken() {
17
+ return this.token.length > 8 ? `${this.token.slice(0, 4)}***${this.token.slice(-4)}` : "***";
18
+ }
19
+ [Symbol.for("nodejs.util.inspect.custom")]() {
20
+ return this.toString();
21
+ }
22
+ toJSON() {
23
+ return {
24
+ token: this.maskedToken,
25
+ valid: this.valid,
26
+ remaining: this.remaining,
27
+ limit: this.limit,
28
+ reset: this.reset
29
+ };
30
+ }
31
+ toString() {
32
+ const type = this._app ? "app" : "pat";
33
+ return `[GitHub ${type} token ${this.maskedToken}]`;
34
+ }
35
+ updateStatus(res) {
36
+ this.valid = res.status !== 401;
37
+ const remaining = res.headers.get("x-ratelimit-remaining");
38
+ const limit = res.headers.get("x-ratelimit-limit");
39
+ const resetEpoch = res.headers.get("x-ratelimit-reset");
40
+ if (remaining)
41
+ this.remaining = Number.parseInt(remaining);
42
+ if (limit)
43
+ this.limit = Number.parseInt(limit);
44
+ if (resetEpoch)
45
+ this.reset = Number.parseInt(resetEpoch) * 1e3;
46
+ }
47
+ /** NOTE: Each call consumes one API request to fetch rate limit info. */
48
+ async validate() {
49
+ try {
50
+ const res = await fetch("https://api.github.com/meta", {
51
+ headers: {
52
+ "User-Agent": "fetch",
53
+ "Authorization": `token ${this.token}`
54
+ }
55
+ });
56
+ this.updateStatus(res);
57
+ this._lastValidated = Date.now();
58
+ const resetInfo = this.reset ? ` resets in ${formatDuration(this.reset - Date.now())}` : "";
59
+ const resource = res.headers.get("x-ratelimit-resource") || "";
60
+ console.log(
61
+ `GitHub token ${this.valid ? "validated" : "invalid"} (${res.status}): ${this.remaining}/${this.limit}${resource ? ` [${resource}]` : ""}${resetInfo}`
62
+ );
63
+ } catch (error) {
64
+ console.error("Error validating GitHub token:", error);
65
+ }
66
+ }
67
+ /** Returns true if this token needs revalidation */
68
+ isStale(now = Date.now()) {
69
+ if (!this._lastValidated)
70
+ return true;
71
+ if (this.reset && this.reset > now)
72
+ return false;
73
+ return now - this._lastValidated > REVALIDATE_INTERVAL;
74
+ }
75
+ /** Clears expired rate limit state */
76
+ clearExpiredLimits(now = Date.now()) {
77
+ if (this.valid !== false && this.remaining === 0 && this.reset && this.reset < now) {
78
+ this.remaining = void 0;
79
+ this.limit = void 0;
80
+ this.reset = void 0;
81
+ }
82
+ }
83
+ /** Whether this token is usable for requests */
84
+ get available() {
85
+ return this.valid && (this.remaining ?? 1) > 0;
86
+ }
87
+ }
88
+ export const ghTokens = (process.env.GH_TOKEN || "").split(",").map((t) => t.trim()).filter(Boolean).map((t) => new GHToken(t));
89
+ export async function ensureAllTokensValidated() {
90
+ await _ensureAllAppTokensOnce();
91
+ await Promise.all(ghTokens.map((t) => t.validate()));
92
+ }
93
+ export const ensureTokensValidated = idempotent(async () => {
94
+ await _validateUntilOneAvailable();
95
+ if (!getGHToken()) {
96
+ await revalidateGHTokens();
97
+ }
98
+ });
99
+ async function _validateUntilOneAvailable() {
100
+ const tasks = [
101
+ ...ghTokens.map((t) => t.validate()),
102
+ ...process.env.GH_APP_ID && process.env.GH_APP_PRIVATE_KEY ? [ensureAppToken()] : []
103
+ ];
104
+ if (tasks.length > 0) {
105
+ await Promise.allSettled(tasks);
106
+ }
107
+ }
108
+ export const revalidateGHTokens = idempotent(_doRevalidateGHTokens, { once: false });
109
+ async function _doRevalidateGHTokens() {
110
+ const now = Date.now();
111
+ const staleTokens = ghTokens.filter((t) => t.isStale(now));
112
+ if (staleTokens.length === 0 && getGHToken()) {
113
+ return false;
114
+ }
115
+ await Promise.all([...staleTokens.map((t) => t.validate()), ensureAppToken()]);
116
+ return true;
117
+ }
118
+ export function getGHToken() {
119
+ const now = Date.now();
120
+ for (const token of ghTokens) {
121
+ token.clearExpiredLimits(now);
122
+ }
123
+ let best;
124
+ for (const t of ghTokens) {
125
+ if (t.available && (t.remaining ?? 1) > (best?.remaining ?? 0)) {
126
+ best = t;
127
+ }
128
+ }
129
+ return best;
130
+ }
131
+ export async function acquireGHToken() {
132
+ await ensureTokensValidated();
133
+ const token = getGHToken();
134
+ if (token)
135
+ return token;
136
+ await revalidateGHTokens();
137
+ return getGHToken();
138
+ }
139
+ export function getAggregateRateLimit() {
140
+ const validTokens = ghTokens.filter((t) => t.valid);
141
+ const totalRemaining = validTokens.reduce((sum, t) => sum + (t.remaining ?? 0), 0);
142
+ const totalLimit = validTokens.reduce((sum, t) => sum + (t.limit ?? 0), 0);
143
+ const soonestReset = ghTokens.filter((t) => t.valid && t.reset).sort((a, b) => (a.reset || 0) - (b.reset || 0))[0];
144
+ return { remaining: totalRemaining, limit: totalLimit, reset: soonestReset?.reset };
145
+ }
146
+ const RETRY_JITTER_MAX = 30 * 60;
147
+ export function retryAfterSeconds(resetMs, fallback = 60) {
148
+ const base = resetMs ? Math.max(1, Math.ceil((resetMs - Date.now()) / 1e3)) : fallback;
149
+ const jitter = Math.floor(Math.random() * RETRY_JITTER_MAX);
150
+ return base + jitter;
151
+ }
152
+ export function formatDuration(ms) {
153
+ const minutes = Math.round(ms / 6e4);
154
+ if (minutes < 1)
155
+ return "<1m";
156
+ if (minutes < 60)
157
+ return `${minutes}m`;
158
+ const hours = Math.floor(minutes / 60);
159
+ const rem = minutes % 60;
160
+ return rem > 0 ? `${hours}h${rem}m` : `${hours}h`;
161
+ }
162
+ export const ensureAppToken = idempotent(() => _refreshAppToken(false), { once: false });
163
+ const _ensureAllAppTokensOnce = idempotent(() => _refreshAppToken(true));
164
+ async function _refreshAppToken(all) {
165
+ try {
166
+ const appId = process.env.GH_APP_ID;
167
+ const privateKey = process.env.GH_APP_PRIVATE_KEY;
168
+ if (!appId || !privateKey) {
169
+ return;
170
+ }
171
+ const jwt = await _createAppJWT(appId, privateKey);
172
+ const installationsRes = await fetch("https://api.github.com/app/installations", {
173
+ headers: {
174
+ "Authorization": `Bearer ${jwt}`,
175
+ "Accept": "application/vnd.github+json",
176
+ "User-Agent": "fetch"
177
+ }
178
+ });
179
+ if (!installationsRes.ok) {
180
+ throw new Error(`Failed to fetch installations: ${installationsRes.status}`);
181
+ }
182
+ const installations = await installationsRes.json();
183
+ const safeInstallations = installations.filter((i) => {
184
+ const perms = Object.values(i.permissions || {});
185
+ return perms.every((p) => p === "read");
186
+ });
187
+ if (safeInstallations.length < installations.length) {
188
+ console.log(
189
+ `Filtered ${installations.length - safeInstallations.length} installation(s) with write permissions`
190
+ );
191
+ }
192
+ let installationIds = safeInstallations.map((i) => String(i.id));
193
+ if (installationIds.length === 0) {
194
+ console.log("No GitHub App installations found");
195
+ return;
196
+ }
197
+ if (!all && installationIds.length > 5) {
198
+ for (let i = installationIds.length - 1; i > 0; i--) {
199
+ const j = Math.floor(Math.random() * (i + 1));
200
+ const tmp = installationIds[i];
201
+ installationIds[i] = installationIds[j];
202
+ installationIds[j] = tmp;
203
+ }
204
+ installationIds = installationIds.slice(0, 5);
205
+ }
206
+ let earliestRefresh = Number.POSITIVE_INFINITY;
207
+ await Promise.all(
208
+ installationIds.map(async (installationId) => {
209
+ try {
210
+ const tokenRes = await fetch(
211
+ `https://api.github.com/app/installations/${installationId}/access_tokens`,
212
+ {
213
+ method: "POST",
214
+ headers: {
215
+ "Authorization": `Bearer ${jwt}`,
216
+ "Accept": "application/vnd.github+json",
217
+ "User-Agent": "fetch"
218
+ }
219
+ }
220
+ );
221
+ if (!tokenRes.ok) {
222
+ throw new Error(`Failed to create installation token: ${tokenRes.status}`);
223
+ }
224
+ const res = await tokenRes.json();
225
+ const existing = ghTokens.find((t) => t._appInstallationId === installationId);
226
+ if (existing) {
227
+ existing.token = res.token;
228
+ existing.valid = true;
229
+ existing.remaining = void 0;
230
+ existing.limit = void 0;
231
+ existing.reset = void 0;
232
+ await existing.validate();
233
+ } else {
234
+ const newToken = new GHToken(res.token, {
235
+ app: true,
236
+ appInstallationId: installationId
237
+ });
238
+ newToken.valid = true;
239
+ ghTokens.push(newToken);
240
+ await newToken.validate();
241
+ }
242
+ const expiresAt = new Date(res.expires_at).getTime();
243
+ const refreshIn = Math.max(expiresAt - Date.now() - 5 * 6e4, 6e4);
244
+ earliestRefresh = Math.min(earliestRefresh, refreshIn);
245
+ console.log(
246
+ `GitHub App token acquired for installation ${installationId} (expires ${res.expires_at})`
247
+ );
248
+ } catch (error) {
249
+ console.error(
250
+ `Failed to get GitHub App token for installation ${installationId}:`,
251
+ error
252
+ );
253
+ }
254
+ })
255
+ );
256
+ if (earliestRefresh < Number.POSITIVE_INFINITY) {
257
+ setTimeout(ensureAppToken, earliestRefresh);
258
+ }
259
+ } catch (error) {
260
+ console.error("Failed to initialize GitHub App tokens:", error);
261
+ }
262
+ }
263
+ export async function _createAppJWT(appId, privateKey) {
264
+ const now = Math.floor(Date.now() / 1e3);
265
+ const header = _base64url(JSON.stringify({ alg: "RS256", typ: "JWT" }));
266
+ const payload = _base64url(JSON.stringify({ iat: now - 60, exp: now + 600, iss: appId }));
267
+ const key = await crypto.subtle.importKey(
268
+ "pkcs8",
269
+ _pemToKeyData(privateKey),
270
+ { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
271
+ false,
272
+ ["sign"]
273
+ );
274
+ const sig = await crypto.subtle.sign(
275
+ "RSASSA-PKCS1-v1_5",
276
+ key,
277
+ new TextEncoder().encode(`${header}.${payload}`)
278
+ );
279
+ const signature = _bufferToBase64url(sig);
280
+ return `${header}.${payload}.${signature}`;
281
+ }
282
+ export function _base64url(str) {
283
+ return _bufferToBase64url(new TextEncoder().encode(str));
284
+ }
285
+ function _bufferToBase64url(buf) {
286
+ const bytes = new Uint8Array(buf);
287
+ let binary = "";
288
+ for (const byte of bytes) {
289
+ binary += String.fromCharCode(byte);
290
+ }
291
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
292
+ }
293
+ function _pemToKeyData(pem) {
294
+ const normalized = pem.replace(/\\n/g, "\n");
295
+ const isPkcs1 = normalized.includes("BEGIN RSA PRIVATE KEY");
296
+ const base64 = normalized.replace(/-----BEGIN [\w ]+-----/, "").replace(/-----END [\w ]+-----/, "").replace(/\s/g, "");
297
+ const der = _base64ToBinary(base64);
298
+ return isPkcs1 ? _pkcs1ToPkcs8(der) : der;
299
+ }
300
+ function _base64ToBinary(base64) {
301
+ const binary = atob(base64);
302
+ const bytes = new Uint8Array(binary.length);
303
+ for (let i = 0; i < binary.length; i++) {
304
+ bytes[i] = binary.charCodeAt(i);
305
+ }
306
+ return bytes.buffer;
307
+ }
308
+ function _pkcs1ToPkcs8(pkcs1) {
309
+ const pkcs1Bytes = new Uint8Array(pkcs1);
310
+ const algorithmId = new Uint8Array([
311
+ 48,
312
+ 13,
313
+ 6,
314
+ 9,
315
+ 42,
316
+ 134,
317
+ 72,
318
+ 134,
319
+ 247,
320
+ 13,
321
+ 1,
322
+ 1,
323
+ 1,
324
+ 5,
325
+ 0
326
+ ]);
327
+ const version = new Uint8Array([2, 1, 0]);
328
+ const octetString = _asn1Wrap(4, pkcs1Bytes);
329
+ const totalLen = version.length + algorithmId.length + octetString.length;
330
+ const seq = _asn1Wrap(48, _concat(version, algorithmId, octetString), totalLen);
331
+ return seq.buffer;
332
+ }
333
+ function _asn1Wrap(tag, content, knownLen) {
334
+ const len = knownLen ?? content.length;
335
+ const lenBytes = _asn1Length(len);
336
+ const result = new Uint8Array(1 + lenBytes.length + content.length);
337
+ result[0] = tag;
338
+ result.set(lenBytes, 1);
339
+ result.set(content, 1 + lenBytes.length);
340
+ return result;
341
+ }
342
+ function _asn1Length(len) {
343
+ if (len < 128)
344
+ return new Uint8Array([len]);
345
+ if (len < 256)
346
+ return new Uint8Array([129, len]);
347
+ if (len < 65536)
348
+ return new Uint8Array([130, len >> 8 & 255, len & 255]);
349
+ return new Uint8Array([131, len >> 16 & 255, len >> 8 & 255, len & 255]);
350
+ }
351
+ function _concat(...arrays) {
352
+ const total = arrays.reduce((sum, a) => sum + a.length, 0);
353
+ const result = new Uint8Array(total);
354
+ let offset = 0;
355
+ for (const a of arrays) {
356
+ result.set(a, offset);
357
+ offset += a.length;
358
+ }
359
+ return result;
360
+ }
361
+ function idempotent(fn, opts) {
362
+ const once = opts?.once !== false;
363
+ let pending;
364
+ const wrapped = () => {
365
+ if (!pending) {
366
+ pending = fn().finally(() => {
367
+ if (!once)
368
+ pending = void 0;
369
+ });
370
+ }
371
+ return pending;
372
+ };
373
+ wrapped.reset = () => {
374
+ pending = void 0;
375
+ };
376
+ return wrapped;
377
+ }
@@ -0,0 +1,11 @@
1
+ export interface NetlifyCmsModuleOptions {
2
+ base?: string;
3
+ dirContent?: string;
4
+ dirSchema?: string;
5
+ branch?: string;
6
+ mediaFolder?: string;
7
+ publicFolder?: string;
8
+ playground?: false;
9
+ }
10
+ declare const _default: import("../../src/runtime/config.ts").FlowModuleDefinition<NetlifyCmsModuleOptions>;
11
+ export default _default;
@@ -0,0 +1,163 @@
1
+ import fs from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { consola } from "consola";
4
+ import { resolvePackageFile, resolvePackagePath, resolvePath } from "../../src/public/shared.mjs";
5
+ import { defineFlowModule } from "../../src/runtime/config.mjs";
6
+ export default defineFlowModule({
7
+ meta: {
8
+ name: "cms",
9
+ configKey: "cms"
10
+ },
11
+ defaults: {
12
+ base: "",
13
+ dirContent: "content",
14
+ dirSchema: "cms",
15
+ branch: "master",
16
+ mediaFolder: "media",
17
+ publicFolder: "public",
18
+ playground: false
19
+ },
20
+ setup(options, context) {
21
+ consola.log("[cms] options", options);
22
+ if (!context.nitro.runtimeConfig?.flow) {
23
+ context.nitro.runtimeConfig.flow = {};
24
+ }
25
+ context.nitro.runtimeConfig.flow.cms = options;
26
+ context.nitro.alias = context.nitro.alias || {};
27
+ const cmsUtilsPath = context.projectRoot === resolvePackagePath() ? resolve(context.projectRoot, "cms/server/utils") : resolvePackagePath("cms/server/utils");
28
+ context.nitro.alias["#cms-utils"] = cmsUtilsPath;
29
+ context.vite.resolve.alias["#cms-utils"] = cmsUtilsPath;
30
+ const base = "cms/server/routes/api/repos/remote/[repo]";
31
+ const paths = {
32
+ draft: resolvePath(context.projectRoot, "cms/server/routes/api/draft"),
33
+ //
34
+ blobsFileIndex: resolvePath(context.projectRoot, `${base}/blobs/[file]/index`),
35
+ blobsIndexPost: resolvePath(context.projectRoot, `${base}/blobs/index.post`),
36
+ branchesBranchIndex: resolvePath(context.projectRoot, `${base}/branches/[branch]/index`),
37
+ commitsIndexPost: resolvePath(context.projectRoot, `${base}/commits/index.post`),
38
+ filesBranchIndex: resolvePath(context.projectRoot, `${base}/files/[branch]/index`),
39
+ filesIndexPost: resolvePath(context.projectRoot, `${base}/files/index.post`),
40
+ refsHeadsBranchIndexPatch: resolvePath(context.projectRoot, `${base}/refs/heads/[branch]/index.patch`),
41
+ //
42
+ config: resolvePath(context.projectRoot, `modules/cms/server/api/config`),
43
+ meta: resolvePath(context.projectRoot, `modules/cms/server/api/meta`),
44
+ localFs: resolvePath(context.projectRoot, `modules/cms/server/api/localFs`)
45
+ };
46
+ const locales = context.flowConfig.locale?.locales || ["es"];
47
+ if (!context.nitro.handlers) {
48
+ context.nitro.handlers = [];
49
+ }
50
+ const baseRemote = "/api/repos/remote/";
51
+ context.nitro.handlers.push({
52
+ route: `/api/repos/local/:repo/**`,
53
+ handler: paths.localFs
54
+ });
55
+ context.nitro.handlers.push({
56
+ route: `${baseRemote}:repo/blobs/:file`,
57
+ handler: paths.blobsFileIndex
58
+ });
59
+ context.nitro.handlers.push({
60
+ route: `${baseRemote}:repo/blobs`,
61
+ handler: paths.blobsIndexPost,
62
+ method: "POST"
63
+ });
64
+ context.nitro.handlers.push({
65
+ route: `${baseRemote}:repo/branches/:branch`,
66
+ handler: paths.branchesBranchIndex
67
+ });
68
+ context.nitro.handlers.push({
69
+ route: `${baseRemote}:repo/commits`,
70
+ handler: paths.commitsIndexPost,
71
+ method: "POST"
72
+ });
73
+ context.nitro.handlers.push({
74
+ route: `${baseRemote}:repo/files/:branch`,
75
+ handler: paths.filesBranchIndex
76
+ });
77
+ context.nitro.handlers.push({
78
+ route: `${baseRemote}:repo/files`,
79
+ handler: paths.filesIndexPost
80
+ });
81
+ context.nitro.handlers.push({
82
+ route: `${baseRemote}:repo/refs/heads/:branch`,
83
+ handler: paths.refsHeadsBranchIndexPatch,
84
+ method: "PATCH"
85
+ });
86
+ context.nitro.handlers.push({
87
+ route: "/api/draft",
88
+ handler: paths.draft
89
+ });
90
+ context.nitro.handlers.push({
91
+ route: "/cms/_/meta.json",
92
+ handler: paths.meta
93
+ });
94
+ context.prerenderRoutes?.add("/cms/_/meta.json");
95
+ if (!options.playground) {
96
+ context.nitro.handlers.push({
97
+ route: "/cms/v3",
98
+ handler: resolvePath(context.projectRoot, "modules/cms/server/api/admin")
99
+ });
100
+ context.nitro.handlers.push({
101
+ route: "/cms/v3/**",
102
+ handler: resolvePath(context.projectRoot, "modules/cms/server/api/admin")
103
+ });
104
+ context.nitro.publicAssets = context.nitro.publicAssets || [];
105
+ context.nitro.publicAssets.push({
106
+ dir: resolvePackagePath("cms/dist/assets"),
107
+ baseURL: "/cms/v3/assets",
108
+ maxAge: 60 * 60 * 24 * 365
109
+ });
110
+ context.vite.plugins = context.vite.plugins || [];
111
+ context.vite.plugins.push({
112
+ name: "cms-serve-assets",
113
+ configureServer(server) {
114
+ server.middlewares.use("/cms/v3/assets", (req, res, next) => {
115
+ const url = req.url.split("?")[0];
116
+ const file = resolvePackagePath("cms/dist/assets", url.slice(1));
117
+ if (fs.existsSync(file)) {
118
+ const content = fs.readFileSync(file);
119
+ if (file.endsWith(".js"))
120
+ res.setHeader("Content-Type", "application/javascript");
121
+ if (file.endsWith(".css"))
122
+ res.setHeader("Content-Type", "text/css");
123
+ res.end(content);
124
+ return;
125
+ }
126
+ next();
127
+ });
128
+ }
129
+ });
130
+ }
131
+ for (const locale of locales) {
132
+ consola.log("[cms] locale", locale);
133
+ const configRoute = `/cms/_/configs/${locale}.json`;
134
+ context.nitro.handlers.push({
135
+ method: "GET",
136
+ route: configRoute,
137
+ handler: paths.config
138
+ });
139
+ context.prerenderRoutes?.add(configRoute);
140
+ }
141
+ context.nitro.imports = context.nitro.imports || { imports: [] };
142
+ context.nitro.imports.imports?.push({
143
+ name: "defineCmsItems",
144
+ from: resolvePackageFile("modules/cms/server/lib/composables.ts", "modules/cms/server/lib/composables.mjs", "modules/cms/server/lib/composables.js")
145
+ });
146
+ context.nitro.imports.imports?.push({
147
+ name: "defineCmsPage",
148
+ from: resolvePackageFile("modules/cms/server/lib/composables.ts", "modules/cms/server/lib/composables.mjs", "modules/cms/server/lib/composables.js")
149
+ });
150
+ context.nitro.imports.imports?.push({
151
+ name: "defineCmsPages",
152
+ from: resolvePackageFile("modules/cms/server/lib/composables.ts", "modules/cms/server/lib/composables.mjs", "modules/cms/server/lib/composables.js")
153
+ });
154
+ context.nitro.imports.imports?.push({
155
+ name: "defineCmsPosts",
156
+ from: resolvePackageFile("modules/cms/server/lib/composables.ts", "modules/cms/server/lib/composables.mjs", "modules/cms/server/lib/composables.js")
157
+ });
158
+ context.nitro.imports.imports?.push({
159
+ name: "defineCmsSettings",
160
+ from: resolvePackageFile("modules/cms/server/lib/composables.ts", "modules/cms/server/lib/composables.mjs", "modules/cms/server/lib/composables.js")
161
+ });
162
+ }
163
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,18 @@
1
+ import { defineEventHandler, setResponseHeader } from "nitro/h3";
2
+ const adminHtml = `<!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>CMS</title>
8
+ <script type="module" crossorigin src="/cms/v3/assets/index-1Es5a0_Z.js"><\/script>
9
+ </head>
10
+ <body>
11
+ <div id="app"></div>
12
+ </body>
13
+ </html>
14
+ `;
15
+ export default defineEventHandler(async (event) => {
16
+ setResponseHeader(event, "Content-Type", "text/html;charset=utf-8");
17
+ return adminHtml;
18
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,88 @@
1
+ import fs from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import process from "node:process";
4
+ import { consola } from "consola";
5
+ import defu from "defu";
6
+ import { createJiti } from "jiti";
7
+ import { defineEventHandler } from "nitro/h3";
8
+ import { getUrl } from "../../../../server/lib/pages.mjs";
9
+ import { defineCmsItems, defineCmsPage, defineCmsPages, defineCmsPosts, defineCmsSettings } from "../lib/composables.mjs";
10
+ import { collections, defineCms } from "../lib/helpers.mjs";
11
+ import { widgets } from "../lib/widgets.mjs";
12
+ export default defineEventHandler(async (event) => {
13
+ if (!globalThis.defineCmsItems) {
14
+ globalThis.defineCmsItems = defineCmsItems;
15
+ }
16
+ if (!globalThis.defineCmsPage) {
17
+ globalThis.defineCmsPage = defineCmsPage;
18
+ }
19
+ if (!globalThis.defineCmsPages) {
20
+ globalThis.defineCmsPages = defineCmsPages;
21
+ }
22
+ if (!globalThis.defineCmsPosts) {
23
+ globalThis.defineCmsPosts = defineCmsPosts;
24
+ }
25
+ if (!globalThis.defineCmsSettings) {
26
+ globalThis.defineCmsSettings = defineCmsSettings;
27
+ }
28
+ const urlParts = event.path?.split("?")[0].split("/") || [];
29
+ const locale = (urlParts.pop() || "es").replace(".json", "");
30
+ const options = {};
31
+ const defaultOptions = {
32
+ config: {
33
+ // public_folder: '/media',
34
+ locale
35
+ },
36
+ base: "/"
37
+ };
38
+ const { config, base } = defu(options, defaultOptions);
39
+ const projectRoot = process.cwd();
40
+ const cmsDir = resolve(projectRoot, "cms");
41
+ const loadedCollections = [];
42
+ try {
43
+ if (fs.existsSync(cmsDir)) {
44
+ const jiti = createJiti(resolve(projectRoot, "cms", "dummy.ts"), { interopDefault: true, fsCache: false, requireCache: false });
45
+ const walkSync = (dir, filelist = []) => {
46
+ const files = fs.readdirSync(dir);
47
+ for (const file of files) {
48
+ const filepath = resolve(dir, file);
49
+ const stat = fs.statSync(filepath);
50
+ if (stat.isDirectory()) {
51
+ filelist = walkSync(filepath, filelist);
52
+ } else if (file.endsWith(".ts")) {
53
+ filelist.push(filepath);
54
+ }
55
+ }
56
+ return filelist;
57
+ };
58
+ const cmsFiles = walkSync(cmsDir);
59
+ for (const filePath of cmsFiles) {
60
+ const mod = jiti(filePath);
61
+ const colFn = mod.default || mod;
62
+ if (typeof colFn === "function") {
63
+ loadedCollections.push(colFn);
64
+ }
65
+ }
66
+ }
67
+ } catch (e) {
68
+ consola.error("[CMS CONFIG] Error loading collections dynamically:", e);
69
+ }
70
+ const resolvedCollections = loadedCollections.map((c) => c({ widgets, collections }));
71
+ const manifest = defineCms(config, resolvedCollections)(`${base}content/${locale}/`, { rootDir: base });
72
+ if (manifest.collections) {
73
+ for (const col of manifest.collections) {
74
+ console.log("pageeee.............", col);
75
+ if (col.preview?.pageName) {
76
+ const pageName = col.preview?.pageName;
77
+ try {
78
+ const url = await getUrl(pageName, locale);
79
+ col.prefix.url = url;
80
+ } catch (e) {
81
+ consola.error("[CMS CONFIG] Error resolving url for injectContext:", e);
82
+ }
83
+ }
84
+ delete col.injectContext;
85
+ }
86
+ }
87
+ return manifest;
88
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;