okno 1.0.0-beta.10 → 1.0.0-beta.12

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 (43) hide show
  1. package/dist/dispatch-bnmIdH8E.js +759 -0
  2. package/dist/editor/index.js +14888 -14157
  3. package/dist/{core/content-encode.d.ts → engine/encode.d.ts} +7 -6
  4. package/dist/engine/encode.d.ts.map +1 -0
  5. package/dist/engine/fields.d.ts +29 -0
  6. package/dist/engine/fields.d.ts.map +1 -0
  7. package/dist/engine/index.d.ts +17 -0
  8. package/dist/engine/index.d.ts.map +1 -0
  9. package/dist/{core → engine}/order.d.ts +2 -0
  10. package/dist/engine/order.d.ts.map +1 -0
  11. package/dist/{core → engine}/parse.d.ts +4 -3
  12. package/dist/engine/parse.d.ts.map +1 -0
  13. package/dist/engine/schema.d.ts +69 -0
  14. package/dist/engine/schema.d.ts.map +1 -0
  15. package/dist/index.js +1 -1
  16. package/dist/local/disk.d.ts +57 -0
  17. package/dist/local/disk.d.ts.map +1 -0
  18. package/dist/local/dispatch.d.ts +27 -0
  19. package/dist/local/dispatch.d.ts.map +1 -0
  20. package/dist/local/self-written.d.ts +17 -0
  21. package/dist/local/self-written.d.ts.map +1 -0
  22. package/dist/mcp.js +1 -1
  23. package/dist/next/config.d.ts +9 -0
  24. package/dist/next/config.d.ts.map +1 -0
  25. package/dist/next/handler.d.ts +15 -0
  26. package/dist/next/handler.d.ts.map +1 -0
  27. package/dist/next/index.d.ts +14 -0
  28. package/dist/next/index.d.ts.map +1 -0
  29. package/dist/next/index.js +57 -0
  30. package/dist/plugin-DkXOrSVC.js +721 -0
  31. package/dist/runtime/wrap.d.ts.map +1 -1
  32. package/dist/runtime/wrap.js +146 -145
  33. package/dist/vite/index.js +1 -1
  34. package/dist/vite/local-engine.d.ts +11 -27
  35. package/dist/vite/local-engine.d.ts.map +1 -1
  36. package/dist/vite/plugin.d.ts.map +1 -1
  37. package/package.json +5 -1
  38. package/src/runtime/wrap.ts +36 -28
  39. package/dist/core/content-encode.d.ts.map +0 -1
  40. package/dist/core/order.d.ts.map +0 -1
  41. package/dist/core/parse.d.ts.map +0 -1
  42. package/dist/plugin-CHrWSW4z.js +0 -1405
  43. /package/dist/{content-encode-DoojdsVU.js → encode-DoojdsVU.js} +0 -0
@@ -0,0 +1,759 @@
1
+ import { join as g, relative as N, sep as R, dirname as x } from "node:path";
2
+ import { readdir as K, readFile as re, writeFile as oe } from "node:fs/promises";
3
+ import { existsSync as y, readFileSync as $, readdirSync as Z, statSync as ie, realpathSync as ae, writeFileSync as j, mkdirSync as P, unlinkSync as z, renameSync as ce } from "node:fs";
4
+ import { simpleGit as B } from "simple-git";
5
+ import "./acorn-CZBJD705.js";
6
+ import { p as D, b as L, a as le, e as ue } from "./encode-DoojdsVU.js";
7
+ import { execSync as ee } from "node:child_process";
8
+ const fe = /* @__PURE__ */ new Set([".astro", ".svelte", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".vue"]), de = /* @__PURE__ */ new Set([
9
+ "node_modules",
10
+ ".astro",
11
+ ".svelte-kit",
12
+ ".next",
13
+ ".nuxt",
14
+ ".vite",
15
+ "dist",
16
+ "build",
17
+ ".git",
18
+ ".turbo"
19
+ ]);
20
+ async function C(e, t) {
21
+ const n = new RegExp(`import\\s*\\{([^}]+)\\}\\s*from\\s*["']okno:${t}["']`, "g"), r = /* @__PURE__ */ new Set();
22
+ async function o(a) {
23
+ let u;
24
+ try {
25
+ u = await K(a, { withFileTypes: !0 });
26
+ } catch {
27
+ return;
28
+ }
29
+ for (const f of u) {
30
+ if (f.name.startsWith(".") && f.name !== "." || de.has(f.name)) continue;
31
+ const l = g(a, f.name);
32
+ if (f.isDirectory()) {
33
+ await o(l);
34
+ continue;
35
+ }
36
+ const s = f.name.lastIndexOf(".");
37
+ if (s === -1) continue;
38
+ const i = f.name.slice(s);
39
+ if (!fe.has(i)) continue;
40
+ let c;
41
+ try {
42
+ c = await re(l, "utf-8");
43
+ } catch {
44
+ continue;
45
+ }
46
+ if (c.includes(`okno:${t}`))
47
+ for (const d of c.matchAll(n)) {
48
+ const p = d[1] ?? "";
49
+ for (const h of p.split(",")) {
50
+ const m = h.trim().split(/\s+as\s+/)[0]?.trim();
51
+ m && /^[A-Za-z_$][\w$]*$/.test(m) && r.add(m);
52
+ }
53
+ }
54
+ }
55
+ }
56
+ return await o(e), r;
57
+ }
58
+ async function et(e, t) {
59
+ const n = await Oe(e), r = new Map(n.map((i) => [i.name, i.first])), o = be(e), a = [
60
+ "// Auto-generated by okno. Do not edit.",
61
+ "",
62
+ "/** A wrapped content value that can be spread onto elements and rendered as text. */",
63
+ "type OknoValue<T = unknown> = {",
64
+ ' readonly "data-okno": string',
65
+ " toString(): string",
66
+ " [Symbol.toPrimitive](hint: string): string",
67
+ " onedit(callback: (value: T) => void): () => void",
68
+ " /** Re-root reads at a locale; no-op when unset or on the default locale. Returns the SAME wrapped shape (not `this`, which would drop array element types). */",
69
+ " locale(code?: string | null): OknoWrapped<T>",
70
+ " /** `true` when this field/reference actually holds a value — every read is a truthy wrapper, so `entry.ref ? …` can't tell set from unset. */",
71
+ " readonly exists: boolean",
72
+ "}",
73
+ "",
74
+ "/** Array-membership boundary — only on array items (never on a scalar `{...field}` spread, so it can't collide with HTML attributes). Spread onto the item wrapper: `<article {...item.slot}>` → data-okno-item=\"<list>.<i>\". `slot` is a reserved field key so content can't shadow it. */",
75
+ 'type OknoSlot = { readonly slot: { readonly "data-okno-item": string } }',
76
+ "",
77
+ "/** A media value's `.image` handle — a spreadable bundle for a framework image component (Astro `<Image {...cover.image}>`): build-optimized `src` (from `okno/files`), plus alt/dimensions and the edit hook. Use `<img {...cover}>` for the raw/universal path. `src` is loose (framework's shape, e.g. Astro `ImageMetadata`) so okno stays framework-agnostic. */",
78
+ 'type OknoImage = { src: any; alt: string; width?: number; height?: number; readonly "data-okno": string }',
79
+ "",
80
+ "/** Recursively wraps content types so values are spreadable/renderable. Media (`{src}`) and link (`{url,text}`) objects keep RAW attribute values so `<img {...media}>` / `<a {...link}>` type-check; media also exposes `.image`; other objects expose wrapped fields + the data-okno hook. Array items also carry `.slot`. */",
81
+ "type OknoWrapped<T> =",
82
+ " T extends string | number | boolean",
83
+ " ? OknoValue<T>",
84
+ " : T extends ReadonlyArray<infer U>",
85
+ " ? (OknoWrapped<U> & OknoSlot)[]",
86
+ " : T extends { src: string }",
87
+ ' ? Omit<T, "focal"> & OknoValue<T> & { readonly image: OknoImage } & (T extends { focal: infer F } ? { readonly focal: OknoWrapped<F> } : {})',
88
+ " : T extends { url: string; text: string }",
89
+ " ? { href: string; target?: string; rel?: string } & OknoValue<T>",
90
+ " : T extends Record<string, unknown>",
91
+ " ? { [K in keyof T]: OknoWrapped<T[K]> } & OknoValue<T>",
92
+ " : OknoValue<T>",
93
+ "",
94
+ "/** Content referenced in code but not yet scaffolded — shape unknown, so every field is a flat leaf. Nested access (`home.hero.title`) is a type error on purpose: okno fields are flat (dividers organize the editor, they don't nest data). Use a Block for nested shapes. */",
95
+ "type OknoUnscaffolded = OknoWrapped<Record<string, unknown>>",
96
+ "",
97
+ "/** Authorship + timestamp metadata Okno writes on every entry (page, collection item, global). Read-only — never edit or spread; it's the data your own `.sort()` uses. Each is wrapped, so render `{a.updated.date}` and compare `String(a.updated.date)`. */",
98
+ "type OknoStamp = { readonly date: string; readonly author: string }",
99
+ "type OknoMeta = {",
100
+ " readonly created: OknoWrapped<OknoStamp>",
101
+ " readonly updated: OknoWrapped<OknoStamp>",
102
+ "}",
103
+ ""
104
+ ], u = (i, c, d) => {
105
+ const p = `typeof import("${d}").default`, h = o?.[i]?.[c]?.fields, m = h ? Object.entries(h) : [], w = m.filter(
106
+ ([, v]) => v?.type === "reference" && typeof v.collection == "string" && r.has(v.collection)
107
+ ), b = m.filter(([, v]) => v?.type === "enum"), O = '"created" | "updated"';
108
+ if (w.length === 0 && b.length === 0) return `OknoWrapped<Omit<${p}, ${O}>> & OknoMeta`;
109
+ const F = `${[...w, ...b].map(([v]) => `"${v}"`).join(" | ")} | ${O}`, se = [
110
+ ...w.map(([v, T]) => {
111
+ const W = `typeof import("./collections/${T.collection}/${r.get(T.collection)}").default`;
112
+ return `${v}: ${T.multiple ? `${W}[]` : W}`;
113
+ }),
114
+ ...b.map(([v, T]) => {
115
+ const W = (Array.isArray(T.options) ? T.options : []).map((A) => typeof A == "string" ? A : A?.value).filter((A) => typeof A == "string"), G = `{ name: string; value: ${W.length ? W.map((A) => JSON.stringify(A)).join(" | ") : "string"} }`;
116
+ return `${v}: ${T.multiple ? `${G}[]` : G}`;
117
+ })
118
+ ].join("; ");
119
+ return `OknoWrapped<Omit<${p}, ${F}> & { ${se} }> & OknoMeta`;
120
+ }, [f, l, s] = await Promise.all([
121
+ C(t, "pages"),
122
+ C(t, "globals"),
123
+ C(t, "collections")
124
+ ]);
125
+ a.push(
126
+ ...H("okno:pages", "pages", await V(g(e, "pages")), f, u)
127
+ ), a.push(...we(n, s, u)), a.push(
128
+ ...H("okno:globals", "globals", await V(g(e, "globals")), l, u)
129
+ ), a.push(...me(Se(e), ye(o))), a.push(...pe()), a.push(...he()), await oe(g(e, "modules.d.ts"), a.join(`
130
+ `));
131
+ }
132
+ function H(e, t, n, r, o) {
133
+ const a = /* @__PURE__ */ new Set(), u = [];
134
+ for (const f of n)
135
+ u.push(` export const ${f}: ${o(t, f, `./${t}/${f}`)}`), a.add(f);
136
+ for (const f of r)
137
+ a.has(f) || u.push(` export const ${f}: OknoUnscaffolded`);
138
+ return [`declare module "${e}" {`, ...u, "}", ""];
139
+ }
140
+ function pe() {
141
+ return [
142
+ 'declare module "okno:locales" {',
143
+ ' /** A configured language. `id` is the BCP-47 tag (e.g. "zh-Hant"), `name` the display label, `slug` the URL segment you map into routing. `link(url)` returns this page\'s hreflang <link> descriptor — spread onto a <link> (pass Astro.url for an absolute href). Okno owns no routing; you map these. */',
144
+ " export interface OknoLocale {",
145
+ " id: string",
146
+ " name: string",
147
+ " slug: string",
148
+ ' link(url: URL | string): { rel: "alternate"; hreflang: string; href: string }',
149
+ " }",
150
+ " /** The configured languages; `locales.default` is the base-locale entry. Empty (`.default` undefined) when no languages are configured. */",
151
+ " export const locales: OknoLocale[] & { default: OknoLocale }",
152
+ "}",
153
+ ""
154
+ ];
155
+ }
156
+ function he() {
157
+ return [
158
+ 'declare module "okno:content" {',
159
+ ' /** Unified content namespace — `import * as okno from "okno:content"` then `okno.pages.home`, `okno.globals.nav`, `okno.collections.articles`, `okno.strings.nav.home`. */',
160
+ ' export * as pages from "okno:pages"',
161
+ ' export * as globals from "okno:globals"',
162
+ ' export * as collections from "okno:collections"',
163
+ ' export { strings } from "okno:strings"',
164
+ "}",
165
+ ""
166
+ ];
167
+ }
168
+ function me(e, t) {
169
+ const n = e.length ? `${ge(e, t)}` : "any", r = e.length ? `
170
+ locale(code?: string | null): ${n}` : "";
171
+ return [
172
+ 'declare module "okno:strings" {',
173
+ " /** The UI-strings store. A plain leaf is string-like (spread `{...s.nav.home}` for the edit hook, render `{s.nav.home}` for the text); a SLOTTED leaf is a `(params) => …` function — call it, then spread/render the result (`{...s.cart.items({count})}`). `.locale(code)` re-roots the whole tree at a language. */",
174
+ ` export const strings: ${e.length ? `${n} & {${r}
175
+ }` : "any"}`,
176
+ "}",
177
+ ""
178
+ ];
179
+ }
180
+ function ge(e, t) {
181
+ const n = { children: /* @__PURE__ */ new Map() };
182
+ for (const u of e) {
183
+ let f = n;
184
+ for (const l of u.split(".")) {
185
+ let s = f.children.get(l);
186
+ s || (s = { children: /* @__PURE__ */ new Map() }, f.children.set(l, s)), f = s;
187
+ }
188
+ }
189
+ const r = (u) => u === "text" ? "string" : "number", o = (u) => {
190
+ const f = t[u];
191
+ return !f || Object.keys(f).length === 0 ? "OknoValue<string>" : `(p: { ${Object.entries(f).map(([s, i]) => `${JSON.stringify(s)}: ${r(i)}`).join("; ")} }) => OknoValue<string>`;
192
+ }, a = (u, f) => u.children.size === 0 ? o(f) : `{ ${[...u.children.entries()].map(
193
+ ([s, i]) => `${JSON.stringify(s)}: ${a(i, f ? `${f}.${s}` : s)}`
194
+ ).join("; ")} }`;
195
+ return a(n, "");
196
+ }
197
+ function ye(e) {
198
+ const t = {}, n = e?.strings;
199
+ if (!n || typeof n != "object" || Array.isArray(n)) return t;
200
+ for (const [r, o] of Object.entries(n)) {
201
+ const a = o?.slots;
202
+ if (!a || typeof a != "object" || Array.isArray(a)) continue;
203
+ const u = {};
204
+ for (const [f, l] of Object.entries(a))
205
+ (l === "text" || l === "number" || l === "count") && (u[f] = l);
206
+ Object.keys(u).length && (t[r] = u);
207
+ }
208
+ return t;
209
+ }
210
+ function we(e, t, n) {
211
+ const r = /* @__PURE__ */ new Set(), o = [];
212
+ for (const a of e) {
213
+ const u = n("collections", a.name, `./collections/${a.name}/${a.first}`);
214
+ o.push(` export const ${a.name}: (${u})[]`), r.add(a.name);
215
+ }
216
+ for (const a of t)
217
+ r.has(a) || o.push(` export const ${a}: OknoUnscaffolded[]`);
218
+ return ['declare module "okno:collections" {', ...o, "}", ""];
219
+ }
220
+ function be(e) {
221
+ const t = g(e, "schema.ts");
222
+ if (!y(t)) return null;
223
+ try {
224
+ let n = $(t, "utf8");
225
+ const r = n.indexOf("export default");
226
+ return r === -1 ? null : (n = n.slice(r + 14).replace(/\bas const\b/g, "").replace(/\bsatisfies\s+[A-Za-z0-9_.]+/g, "").trim(), n.endsWith(";") && (n = n.slice(0, -1).trim()), JSON.parse(n));
227
+ } catch {
228
+ return null;
229
+ }
230
+ }
231
+ function Se(e) {
232
+ const t = g(e, "strings.ts");
233
+ if (!y(t)) return [];
234
+ try {
235
+ let n = $(t, "utf8");
236
+ const r = n.indexOf("export default");
237
+ if (r === -1) return [];
238
+ n = n.slice(r + 14).replace(/\bas const\b/g, "").replace(/\bsatisfies\s+[A-Za-z0-9_.]+/g, "").trim(), n.endsWith(";") && (n = n.slice(0, -1).trim());
239
+ const o = JSON.parse(n);
240
+ return o && typeof o == "object" && !Array.isArray(o) ? Object.keys(o) : [];
241
+ } catch {
242
+ return [];
243
+ }
244
+ }
245
+ async function Oe(e) {
246
+ const t = g(e, "collections");
247
+ if (!y(t)) return [];
248
+ const n = (await K(t, { withFileTypes: !0 })).filter((o) => o.isDirectory()), r = [];
249
+ for (const o of n) {
250
+ const a = await V(g(t, o.name));
251
+ a.length > 0 && r.push({ name: o.name, first: a[0] });
252
+ }
253
+ return r;
254
+ }
255
+ async function V(e) {
256
+ return y(e) ? (await K(e)).filter((n) => n.endsWith(".ts") && n !== "index.ts" && n !== "modules.d.ts").map((n) => n.replace(".ts", "")) : [];
257
+ }
258
+ function ke(e) {
259
+ switch (e) {
260
+ case "number":
261
+ return 0;
262
+ case "boolean":
263
+ return !1;
264
+ case "media":
265
+ return null;
266
+ default:
267
+ return "";
268
+ }
269
+ }
270
+ function $e(e) {
271
+ const t = {};
272
+ for (const n of e) {
273
+ const r = n.path.split(".");
274
+ let o = t;
275
+ for (let u = 0; u < r.length - 1; u++) {
276
+ const f = r[u];
277
+ (!o[f] || o[f].type !== "group") && (o[f] = { type: "group", fields: {} }), o = o[f].fields;
278
+ }
279
+ const a = r[r.length - 1];
280
+ if (!o[a]) {
281
+ const u = {};
282
+ for (const [f, l] of Object.entries(n)) f !== "path" && f !== "value" && (u[f] = l);
283
+ o[a] = { ...u, type: n.type };
284
+ }
285
+ }
286
+ return t;
287
+ }
288
+ function ve(e) {
289
+ const t = {};
290
+ for (const n of e) {
291
+ const r = n.path.split(".");
292
+ let o = t;
293
+ for (let u = 0; u < r.length - 1; u++) {
294
+ const f = r[u];
295
+ (!o[f] || typeof o[f] != "object") && (o[f] = {}), o = o[f];
296
+ }
297
+ const a = r[r.length - 1];
298
+ o[a] = n.value !== void 0 && n.value !== null ? n.value : ke(n.type);
299
+ }
300
+ return t;
301
+ }
302
+ function X(e, t, n) {
303
+ const r = {};
304
+ let o = !1;
305
+ for (const [a, u] of Object.entries(e)) {
306
+ if (n.has(a)) {
307
+ o = !0;
308
+ continue;
309
+ }
310
+ const f = a in t && !(t[a] in e) ? t[a] : a;
311
+ f !== a && (o = !0), r[f] = u;
312
+ }
313
+ return { out: r, changed: o };
314
+ }
315
+ class S extends Error {
316
+ status;
317
+ constructor(t, n) {
318
+ super(n), this.name = "EngineError", this.status = t;
319
+ }
320
+ }
321
+ const je = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/, Te = /^[a-z]{2,3}(-[A-Za-z]{4})?(-([A-Za-z]{2}|[0-9]{3}))?$/, Ae = (e) => e.charAt(0).toUpperCase() + e.slice(1);
322
+ function xe(e, t, n, r) {
323
+ const o = e[t] ??= {};
324
+ if (!o[n]) throw new S(404, `${t}/${n} does not exist in schema`);
325
+ o[n].fields = r;
326
+ }
327
+ function Pe(e, t, n, r) {
328
+ const o = e[t] ??= {}, a = o[n];
329
+ if (!a) throw new S(404, `${t}/${n} does not exist in schema`);
330
+ const u = { ...a, name: r };
331
+ delete u.label, o[n] = u;
332
+ }
333
+ function Me(e, t, n, r) {
334
+ const o = e[t] ??= {}, a = o[n];
335
+ if (!a) throw new S(404, `${t}/${n} does not exist in schema`);
336
+ const u = { ...a };
337
+ typeof r == "string" && r.trim() ? u.url = r.trim() : delete u.url, o[n] = u;
338
+ }
339
+ function Ee(e, t, n, r) {
340
+ if (!je.test(r)) throw new S(400, "Invalid target slug");
341
+ if (n === r) return { unchanged: !0 };
342
+ const o = e[t] ??= {};
343
+ if (!o[n]) throw new S(404, `${t}/${n} does not exist in schema`);
344
+ if (o[r]) throw new S(409, `${t}/${r} already exists`);
345
+ const a = {};
346
+ for (const f of Object.keys(o)) a[f === n ? r : f] = o[f];
347
+ e[t] = a;
348
+ const u = e.layout?.[t];
349
+ return Array.isArray(u) && (e.layout[t] = u.map(
350
+ (f) => typeof f == "string" ? f === n ? r : f : {
351
+ ...f,
352
+ items: (f.items ?? []).map((l) => l === n ? r : l)
353
+ }
354
+ )), { unchanged: !1 };
355
+ }
356
+ function Ie(e, t, n) {
357
+ const r = e[t];
358
+ if (!r) throw new S(404, `${t} not in schema`);
359
+ const o = {};
360
+ for (const a of n) r[a] && (o[a] = r[a]);
361
+ for (const a of Object.keys(r)) a in o || (o[a] = r[a]);
362
+ e[t] = o;
363
+ }
364
+ function Re(e, t, n) {
365
+ const r = e.layout ??= {};
366
+ n.length ? r[t] = n : delete r[t], Object.keys(r).length === 0 && delete e.layout;
367
+ }
368
+ function We(e, t, n) {
369
+ const o = (e.collections ??= {})[t];
370
+ if (!o) throw new S(404, `collections/${t} not in schema`);
371
+ o.order = n.filter((a) => typeof a == "string");
372
+ }
373
+ function ze(e, t, n) {
374
+ const o = (e.collections ??= {})[t];
375
+ if (!o) throw new S(404, `collections/${t} not in schema`);
376
+ n ? o.drafts = !0 : delete o.drafts;
377
+ }
378
+ function Ue(e, t, n) {
379
+ const r = e[t];
380
+ if (!r || !r[n]) throw new S(404, `${t}/${n} does not exist in schema`);
381
+ delete r[n];
382
+ }
383
+ function Ne(e, t, n, r, o) {
384
+ e[n] || (e[n] = {}), e[n][t] = { name: o?.trim() || Ae(t), fields: $e(r) };
385
+ }
386
+ function Fe(e, t) {
387
+ t && Object.keys(t).length ? e.strings = t : delete e.strings;
388
+ }
389
+ function Ce(e, t) {
390
+ let n = null;
391
+ if (t && Array.isArray(t.supported) && t.supported.length > 0) {
392
+ const r = t.supported.map((u) => typeof u == "string" ? { id: u } : u);
393
+ if (!r.every((u) => u && typeof u.id == "string" && Te.test(u.id)))
394
+ throw new S(400, "Invalid locale code");
395
+ const o = r.map((u) => u.id);
396
+ if (new Set(o).size !== o.length) throw new S(400, "Duplicate locale code");
397
+ const a = t.default;
398
+ if (typeof a != "string" || !o.includes(a))
399
+ throw new S(400, "Default must be one of the supported locales");
400
+ n = {
401
+ default: a,
402
+ supported: r.map((u) => ({
403
+ id: u.id,
404
+ name: typeof u.name == "string" && u.name ? u.name : u.id,
405
+ // Preserve an intentionally empty slug (= no URL prefix for the default).
406
+ slug: typeof u.slug == "string" ? u.slug : u.id.toLowerCase()
407
+ }))
408
+ };
409
+ }
410
+ n ? e.locales = n : delete e.locales;
411
+ }
412
+ const te = /* @__PURE__ */ new Map();
413
+ function E(e) {
414
+ te.set(e, Date.now());
415
+ }
416
+ function tt(e) {
417
+ const t = te.get(e);
418
+ return t !== void 0 && Date.now() - t < 3e3;
419
+ }
420
+ const _e = ["pages", "collections", "globals"];
421
+ function M(e, t) {
422
+ if (!t || t.includes("..") || t.includes("\0") || t.startsWith("/") || !t.endsWith(".ts")) return null;
423
+ const n = g(e, t);
424
+ return n !== e && !n.startsWith(e + R) ? null : n;
425
+ }
426
+ function I(e, t) {
427
+ if (!t || t.includes("..") || t.includes("\0") || t.startsWith("/")) return null;
428
+ const n = g(e, t);
429
+ return n !== e && !n.startsWith(e + R) ? null : n;
430
+ }
431
+ function Le(e) {
432
+ const t = {}, n = (o) => {
433
+ if (y(o))
434
+ for (const a of Z(o, { withFileTypes: !0 })) {
435
+ const u = g(o, a.name);
436
+ if (a.isDirectory()) n(u);
437
+ else if (a.name.endsWith(".ts") && a.name !== "index.ts") {
438
+ const f = N(e, u).split(R).join("/");
439
+ t[f] = $(u, "utf-8");
440
+ }
441
+ }
442
+ };
443
+ for (const o of _e) n(g(e, o));
444
+ const r = g(e, "strings.ts");
445
+ return y(r) && (t["strings.ts"] = $(r, "utf-8")), n(g(e, "strings")), t;
446
+ }
447
+ function ne(e) {
448
+ const t = g(e, "files"), n = [], r = (o) => {
449
+ if (y(o))
450
+ for (const a of Z(o, { withFileTypes: !0 })) {
451
+ const u = g(o, a.name);
452
+ if (a.isDirectory()) r(u);
453
+ else if (a.name !== ".okno-tree.json") {
454
+ const f = N(t, u).split(R).join("/");
455
+ n.push({ path: `files/${f}`, filename: a.name, size: ie(u).size });
456
+ }
457
+ }
458
+ };
459
+ return r(t), n;
460
+ }
461
+ function Ve(e) {
462
+ const t = g(e, "schema.ts"), n = g(e, "users.json"), r = g(e, "settings.json"), o = g(e, "origins.json");
463
+ return {
464
+ schema: y(t) ? $(t, "utf-8") : null,
465
+ users: y(n) ? $(n, "utf-8") : null,
466
+ settings: y(r) ? $(r, "utf-8") : null,
467
+ origins: y(o) ? $(o, "utf-8") : null,
468
+ content: Le(e),
469
+ files: ne(e)
470
+ };
471
+ }
472
+ const q = (e) => e.replace(/[^a-zA-Z0-9._-]/g, "-"), Q = (e) => e.split("/").map((t) => t.replace(/[^a-zA-Z0-9._-]/g, "-")).filter((t) => t && t !== "-" && t !== "..").join("/");
473
+ function k(e) {
474
+ const t = g(e, "schema.ts");
475
+ let n = `import type { Schema } from "okno"
476
+
477
+ `, r = {};
478
+ if (y(t)) {
479
+ const o = $(t, "utf-8"), a = o.indexOf("export default");
480
+ a > 0 && (n = o.slice(0, a));
481
+ try {
482
+ r = D(o) ?? {};
483
+ } catch {
484
+ r = {};
485
+ }
486
+ }
487
+ return { schema: r, header: n };
488
+ }
489
+ function Je(e, t, n) {
490
+ j(g(e, "schema.ts"), le(t, n));
491
+ }
492
+ function Ke(e, t, n, r, o) {
493
+ const a = g(e, t), u = new Set(o), f = [];
494
+ if (!y(a) || Object.keys(r).length === 0 && u.size === 0) return f;
495
+ const l = [], s = (i) => {
496
+ for (const c of Z(i, { withFileTypes: !0 })) {
497
+ const d = g(i, c.name);
498
+ c.isDirectory() ? s(d) : c.name === `${n}.ts` && l.push(d);
499
+ }
500
+ };
501
+ s(a);
502
+ for (const i of l) {
503
+ let c;
504
+ try {
505
+ c = D($(i, "utf-8"));
506
+ } catch {
507
+ continue;
508
+ }
509
+ let d, p = !1;
510
+ if (Array.isArray(c))
511
+ d = c.map((m) => {
512
+ if (m && typeof m == "object" && !Array.isArray(m)) {
513
+ const w = X(m, r, u);
514
+ return w.changed && (p = !0), w.out;
515
+ }
516
+ return m;
517
+ });
518
+ else if (c && typeof c == "object") {
519
+ const m = X(c, r, u);
520
+ d = m.out, p = m.changed;
521
+ } else
522
+ continue;
523
+ if (!p) continue;
524
+ E(i), j(i, L(d));
525
+ const h = N(e, i).split(R).join("/");
526
+ f.push({ rel: h, abs: i }), console.log(`[okno] field-change migrated content: ${h}`, { renames: r, deletes: o });
527
+ }
528
+ return f;
529
+ }
530
+ let _;
531
+ function J(e) {
532
+ return _ || (_ = ee("git rev-parse --show-toplevel", { cwd: e, encoding: "utf-8" }).trim()), _;
533
+ }
534
+ function Y(e) {
535
+ return N(J(e), ae(e)).split(R).join("/");
536
+ }
537
+ let U;
538
+ function Ze(e) {
539
+ if (U !== void 0) return U;
540
+ try {
541
+ U = ee("git config user.name", { cwd: e, encoding: "utf-8" }).trim() || "local";
542
+ } catch {
543
+ U = "local";
544
+ }
545
+ return U;
546
+ }
547
+ async function nt(e, t) {
548
+ const { method: n, route: r } = t, o = { files: [], modules: [] }, a = (s, i) => {
549
+ const c = s.split("/")[0];
550
+ c !== "pages" && c !== "collections" && c !== "globals" || (o.modules.push(c), o.files.push(i));
551
+ }, u = () => o.modules.push("pages", "collections", "globals"), f = (s, i) => {
552
+ Je(e, s, i), u();
553
+ }, l = (s, i) => ({ status: s, body: i, invalidation: o });
554
+ try {
555
+ if (n === "GET" && r === "content") return l(200, Ve(e));
556
+ if (n === "POST" && r === "content/save") {
557
+ const { path: s, content: i, created: c, author: d } = await t.json();
558
+ if (!s || !i) return l(400, { error: "Missing path or content" });
559
+ const p = M(e, s);
560
+ if (!p) return l(400, { error: "Invalid path" });
561
+ const h = ue(i, { now: (/* @__PURE__ */ new Date()).toISOString(), author: d || Ze(e), created: c });
562
+ return P(x(p), { recursive: !0 }), E(p), j(p, h), a(s, p), l(200, { ok: !0, commit: { fileSha: "" } });
563
+ }
564
+ if (n === "POST" && r === "content/delete") {
565
+ const s = (await t.json())?.path;
566
+ if (!s) return l(400, { error: "Missing path" });
567
+ const i = M(e, s);
568
+ return i ? (y(i) && (z(i), a(s, i)), l(200, { ok: !0 })) : l(400, { error: "Invalid path" });
569
+ }
570
+ if (n === "POST" && r === "strings/save") {
571
+ const { path: s, content: i } = await t.json();
572
+ if (!s || !i || typeof i != "object") return l(400, { error: "Missing path or content" });
573
+ if (s !== "strings.ts" && !/^strings\/[^/]+\.ts$/.test(s)) return l(400, { error: "Invalid strings path" });
574
+ const c = M(e, s);
575
+ return c ? (P(x(c), { recursive: !0 }), E(c), j(c, L(i)), o.modules.push("strings", "content"), o.files.push(c), l(200, { ok: !0, commit: { fileSha: "" } })) : l(400, { error: "Invalid path" });
576
+ }
577
+ if (n === "POST" && r === "strings/set-schema") {
578
+ const { strings: s } = await t.json();
579
+ if (s != null && (typeof s != "object" || Array.isArray(s))) return l(400, { error: "Invalid strings" });
580
+ const { schema: i, header: c } = k(e);
581
+ return Fe(i, s), f(i, c), o.modules.push("strings", "content"), l(200, { ok: !0, commit: { fileSha: "" }, schema: i });
582
+ }
583
+ if (n === "GET" && r === "publish-state")
584
+ try {
585
+ const i = await B(J(e)).status(), c = Y(e), d = i.files.some((p) => p.path.startsWith(c + "/"));
586
+ return l(200, { pendingPublish: d || i.ahead > 0 });
587
+ } catch {
588
+ return l(200, { pendingPublish: !1 });
589
+ }
590
+ if (n === "POST" && r === "publish") {
591
+ const s = B(J(e)), i = Y(e);
592
+ await s.raw(["add", "-A", "--", i]);
593
+ const c = await s.status(), d = c.staged.length > 0;
594
+ let p = !1;
595
+ d && (await s.commit("okno: publish content"), p = !0);
596
+ let h = !1, m = null;
597
+ try {
598
+ (p || c.ahead > 0) && (await s.push(), h = !0);
599
+ } catch (w) {
600
+ m = w instanceof Error ? w.message.split(`
601
+ `)[0] : String(w);
602
+ }
603
+ return l(200, { ok: !0, alreadyUpToDate: !p && c.ahead === 0, committed: p, pushed: h, pushError: m });
604
+ }
605
+ if (n === "POST" && r === "schema/save-fields") {
606
+ const { kind: s, name: i, fields: c, renames: d, deletes: p } = await t.json();
607
+ if (!s || !i || !c) return l(400, { error: "Missing kind, name or fields" });
608
+ const { schema: h, header: m } = k(e);
609
+ xe(h, s, i, c), f(h, m);
610
+ const w = d && typeof d == "object" ? d : {}, b = Array.isArray(p) ? p : [];
611
+ if (Object.keys(w).length > 0 || b.length > 0)
612
+ for (const { rel: O, abs: F } of Ke(e, s, i, w, b)) a(O, F);
613
+ return l(200, { ok: !0, commit: { fileSha: "" }, schema: h });
614
+ }
615
+ if (n === "POST" && r === "schema/set-locales") {
616
+ const s = (await t.json())?.locales, { schema: i, header: c } = k(e);
617
+ return Ce(i, s), f(i, c), l(200, { ok: !0, commit: { fileSha: "" } });
618
+ }
619
+ if (n === "POST" && r === "schema/set-name") {
620
+ const { kind: s, slug: i, name: c } = await t.json();
621
+ if (!s || !i || !c) return l(400, { error: "Missing kind, slug or name" });
622
+ const { schema: d, header: p } = k(e);
623
+ return Pe(d, s, i, c), f(d, p), l(200, { ok: !0, commit: { fileSha: "" } });
624
+ }
625
+ if (n === "POST" && r === "schema/set-url") {
626
+ const { kind: s, slug: i, url: c } = await t.json();
627
+ if (!s || !i) return l(400, { error: "Missing kind or slug" });
628
+ const { schema: d, header: p } = k(e);
629
+ return Me(d, s, i, c), f(d, p), l(200, { ok: !0, commit: { fileSha: "" } });
630
+ }
631
+ if (n === "POST" && r === "schema/rename-slug") {
632
+ const { kind: s, from: i, to: c } = await t.json();
633
+ if (!s || !i || !c) return l(400, { error: "Missing kind, from or to" });
634
+ const { schema: d, header: p } = k(e), { unchanged: h } = Ee(d, s, i, c);
635
+ if (h) return l(200, { ok: !0, unchanged: !0 });
636
+ f(d, p);
637
+ const m = `${s}/${i}.ts`, w = `${s}/${c}.ts`, b = M(e, m), O = M(e, w);
638
+ return b && O && y(b) && (P(x(O), { recursive: !0 }), E(b), E(O), j(O, $(b)), z(b), a(m, b), a(w, O)), l(200, { ok: !0, commit: { fileSha: "" }, schema: d, from: i, to: c, kind: s });
639
+ }
640
+ if (n === "POST" && r === "schema/reorder") {
641
+ const { kind: s, order: i } = await t.json();
642
+ if (!s || !Array.isArray(i)) return l(400, { error: "Missing kind or order" });
643
+ const { schema: c, header: d } = k(e);
644
+ return Ie(c, s, i), f(c, d), l(200, { ok: !0, commit: { fileSha: "" }, schema: c });
645
+ }
646
+ if (n === "POST" && r === "schema/set-layout") {
647
+ const { kind: s, layout: i } = await t.json();
648
+ if (!s || !Array.isArray(i)) return l(400, { error: "Missing kind or layout" });
649
+ const { schema: c, header: d } = k(e);
650
+ return Re(c, s, i), f(c, d), l(200, { ok: !0, commit: { fileSha: "" }, schema: c });
651
+ }
652
+ if (n === "POST" && r === "collections/set-order") {
653
+ const { name: s, order: i } = await t.json();
654
+ if (!s || !Array.isArray(i)) return l(400, { error: "Missing name or order" });
655
+ const { schema: c, header: d } = k(e);
656
+ return We(c, s, i), f(c, d), l(200, { ok: !0, commit: { fileSha: "" }, schema: c });
657
+ }
658
+ if (n === "POST" && r === "collections/set-drafts") {
659
+ const { name: s, drafts: i } = await t.json();
660
+ if (!s || typeof i != "boolean") return l(400, { error: "Missing name or drafts" });
661
+ const { schema: c, header: d } = k(e);
662
+ return ze(c, s, i), f(c, d), l(200, { ok: !0, commit: { fileSha: "" }, schema: c });
663
+ }
664
+ if (n === "POST" && r === "schema/delete-entry") {
665
+ const { kind: s, name: i } = await t.json();
666
+ if (!s || !i) return l(400, { error: "Missing kind or name" });
667
+ const { schema: c, header: d } = k(e);
668
+ Ue(c, s, i), f(c, d);
669
+ const p = `${s}/${i}.ts`, h = M(e, p);
670
+ return h && y(h) && (E(h), z(h), a(p, h)), l(200, { ok: !0, commit: { fileSha: "" }, schema: c });
671
+ }
672
+ if (n === "POST" && r === "setup/scaffold-page") {
673
+ const { name: s, kind: i, fields: c, label: d } = await t.json();
674
+ if (!s || !i) return l(400, { error: "Missing name or kind" });
675
+ const p = c ?? [], { schema: h, header: m } = k(e);
676
+ if (Ne(h, s, i, p, d), f(h, m), i !== "blocks") {
677
+ const w = `${i}/${s}.ts`, b = M(e, w);
678
+ if (b) {
679
+ const O = i === "collections" ? `export default []
680
+ ` : L(ve(p));
681
+ P(x(b), { recursive: !0 }), E(b), j(b, O), a(w, b);
682
+ }
683
+ }
684
+ return l(200, { ok: !0 });
685
+ }
686
+ if (n === "GET" && r === "files") {
687
+ const s = g(e, "files", ".okno-tree.json");
688
+ return l(200, {
689
+ files: ne(e),
690
+ manifest: y(s) ? $(s, "utf-8") : null
691
+ });
692
+ }
693
+ if (n === "POST" && r === "files/blob") {
694
+ const s = q(decodeURIComponent(t.header("x-okno-filename") || "")), i = t.header("x-okno-dir") ? decodeURIComponent(t.header("x-okno-dir")) : "";
695
+ if (!s || s === "-") return l(400, { error: "Invalid filename" });
696
+ const c = Q(i), d = c ? `${c}/${s}` : s, p = I(e, `files/${d}`);
697
+ if (!p) return l(400, { error: "Invalid path" });
698
+ const h = await t.binary();
699
+ return h.length === 0 ? l(400, { error: "Empty body" }) : (P(x(p), { recursive: !0 }), j(p, h), l(200, { ok: !0, blobSha: "", path: `files/${d}`, filename: s }));
700
+ }
701
+ if (n === "POST" && r === "files/commit") return l(200, { ok: !0 });
702
+ if (n === "POST" && r === "files/upload") {
703
+ const { filename: s, content: i, dir: c } = await t.json();
704
+ if (!s || i == null) return l(400, { error: "Missing filename or content" });
705
+ const d = q(s);
706
+ if (!d || d === "-") return l(400, { error: "Invalid filename" });
707
+ const p = Q(c ?? ""), h = p ? `${p}/${d}` : d, m = I(e, `files/${h}`);
708
+ return m ? (P(x(m), { recursive: !0 }), j(m, Buffer.from(i, "base64")), l(200, { ok: !0, path: `files/${h}`, filename: d, commit: { fileSha: "" } })) : l(400, { error: "Invalid path" });
709
+ }
710
+ if (n === "POST" && r === "files/delete") {
711
+ const s = (await t.json())?.path;
712
+ if (!s) return l(400, { error: "Missing path" });
713
+ const i = I(e, s);
714
+ return i ? (y(i) && z(i), l(200, { ok: !0 })) : l(400, { error: "Invalid path" });
715
+ }
716
+ if (n === "POST" && r === "files/delete-many") {
717
+ const s = (await t.json())?.paths;
718
+ if (!Array.isArray(s)) return l(400, { error: "Missing paths" });
719
+ for (const i of s) {
720
+ const c = I(e, String(i));
721
+ c && y(c) && z(c);
722
+ }
723
+ return l(200, { ok: !0 });
724
+ }
725
+ if (n === "POST" && r === "files/rename") {
726
+ const { from: s, to: i } = await t.json();
727
+ if (!s || !i) return l(400, { error: "Missing from or to" });
728
+ const c = I(e, s), d = I(e, i);
729
+ return !c || !d ? l(400, { error: "Invalid path" }) : y(c) ? (P(x(d), { recursive: !0 }), ce(c, d), l(200, { ok: !0, commit: { fileSha: "" } })) : l(404, { error: "Source not found" });
730
+ }
731
+ if (n === "POST" && r === "users/save") {
732
+ const s = (await t.json())?.users;
733
+ return Array.isArray(s) ? (j(g(e, "users.json"), JSON.stringify(s, null, " ") + `
734
+ `), l(200, { ok: !0, commit: { fileSha: "" } })) : l(400, { error: "Missing users" });
735
+ }
736
+ if (n === "POST" && r === "settings/save") {
737
+ const s = (await t.json())?.settings;
738
+ return !s || typeof s != "object" ? l(400, { error: "Missing settings" }) : (j(g(e, "settings.json"), JSON.stringify(s, null, " ") + `
739
+ `), l(200, { ok: !0, commit: { fileSha: "" } }));
740
+ }
741
+ if (n === "POST" && r === "origins/save") {
742
+ const s = (await t.json())?.origins;
743
+ if (!Array.isArray(s) || s.some((c) => typeof c != "string")) return l(400, { error: "Missing origins" });
744
+ const i = [...new Set(s.map((c) => c.trim()).filter(Boolean))];
745
+ return j(g(e, "origins.json"), JSON.stringify(i, null, " ") + `
746
+ `), l(200, { ok: !0, origins: i, commit: { fileSha: "" } });
747
+ }
748
+ return null;
749
+ } catch (s) {
750
+ return s instanceof S ? { status: s.status, body: { error: s.message } } : { status: 500, body: { error: s instanceof Error ? s.message : String(s) } };
751
+ }
752
+ }
753
+ export {
754
+ Ve as a,
755
+ tt as c,
756
+ nt as d,
757
+ et as g,
758
+ C as s
759
+ };