@astro-live-cms/core 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/dist/index.d.ts +15 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +122 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/runtime/auth/session.d.ts +14 -0
  6. package/dist/runtime/auth/session.d.ts.map +1 -0
  7. package/dist/runtime/auth/session.js +77 -0
  8. package/dist/runtime/auth/session.js.map +1 -0
  9. package/dist/runtime/bind.d.ts +14 -0
  10. package/dist/runtime/bind.d.ts.map +1 -0
  11. package/dist/runtime/bind.js +11 -0
  12. package/dist/runtime/bind.js.map +1 -0
  13. package/dist/runtime/config.d.ts +6 -0
  14. package/dist/runtime/config.d.ts.map +1 -0
  15. package/dist/runtime/config.js +18 -0
  16. package/dist/runtime/config.js.map +1 -0
  17. package/dist/runtime/content.d.ts +30 -0
  18. package/dist/runtime/content.d.ts.map +1 -0
  19. package/dist/runtime/content.js +48 -0
  20. package/dist/runtime/content.js.map +1 -0
  21. package/dist/runtime/index.d.ts +10 -0
  22. package/dist/runtime/index.d.ts.map +1 -0
  23. package/dist/runtime/index.js +12 -0
  24. package/dist/runtime/index.js.map +1 -0
  25. package/dist/runtime/markers.d.ts +17 -0
  26. package/dist/runtime/markers.d.ts.map +1 -0
  27. package/dist/runtime/markers.js +17 -0
  28. package/dist/runtime/markers.js.map +1 -0
  29. package/dist/runtime/middleware.d.ts +12 -0
  30. package/dist/runtime/middleware.d.ts.map +1 -0
  31. package/dist/runtime/middleware.js +37 -0
  32. package/dist/runtime/middleware.js.map +1 -0
  33. package/dist/runtime/mutations/contracts.d.ts +57 -0
  34. package/dist/runtime/mutations/contracts.d.ts.map +1 -0
  35. package/dist/runtime/mutations/contracts.js +242 -0
  36. package/dist/runtime/mutations/contracts.js.map +1 -0
  37. package/dist/runtime/mutations/engine.d.ts +23 -0
  38. package/dist/runtime/mutations/engine.d.ts.map +1 -0
  39. package/dist/runtime/mutations/engine.js +161 -0
  40. package/dist/runtime/mutations/engine.js.map +1 -0
  41. package/dist/runtime/routes/_helpers.d.ts +6 -0
  42. package/dist/runtime/routes/_helpers.d.ts.map +1 -0
  43. package/dist/runtime/routes/_helpers.js +23 -0
  44. package/dist/runtime/routes/_helpers.js.map +1 -0
  45. package/dist/runtime/routes/admin.d.ts +3 -0
  46. package/dist/runtime/routes/admin.d.ts.map +1 -0
  47. package/dist/runtime/routes/admin.js +110 -0
  48. package/dist/runtime/routes/admin.js.map +1 -0
  49. package/dist/runtime/routes/auth-login.d.ts +4 -0
  50. package/dist/runtime/routes/auth-login.d.ts.map +1 -0
  51. package/dist/runtime/routes/auth-login.js +66 -0
  52. package/dist/runtime/routes/auth-login.js.map +1 -0
  53. package/dist/runtime/routes/auth-logout.d.ts +4 -0
  54. package/dist/runtime/routes/auth-logout.d.ts.map +1 -0
  55. package/dist/runtime/routes/auth-logout.js +51 -0
  56. package/dist/runtime/routes/auth-logout.js.map +1 -0
  57. package/dist/runtime/routes/editor-assets.d.ts +3 -0
  58. package/dist/runtime/routes/editor-assets.d.ts.map +1 -0
  59. package/dist/runtime/routes/editor-assets.js +47 -0
  60. package/dist/runtime/routes/editor-assets.js.map +1 -0
  61. package/dist/runtime/routes/entries.d.ts +3 -0
  62. package/dist/runtime/routes/entries.d.ts.map +1 -0
  63. package/dist/runtime/routes/entries.js +89 -0
  64. package/dist/runtime/routes/entries.js.map +1 -0
  65. package/dist/runtime/routes/history.d.ts +4 -0
  66. package/dist/runtime/routes/history.d.ts.map +1 -0
  67. package/dist/runtime/routes/history.js +56 -0
  68. package/dist/runtime/routes/history.js.map +1 -0
  69. package/dist/runtime/routes/mutate.d.ts +4 -0
  70. package/dist/runtime/routes/mutate.d.ts.map +1 -0
  71. package/dist/runtime/routes/mutate.js +35 -0
  72. package/dist/runtime/routes/mutate.js.map +1 -0
  73. package/dist/runtime/routes/schema.d.ts +3 -0
  74. package/dist/runtime/routes/schema.d.ts.map +1 -0
  75. package/dist/runtime/routes/schema.js +27 -0
  76. package/dist/runtime/routes/schema.js.map +1 -0
  77. package/dist/runtime/routes/studio-home.d.ts +3 -0
  78. package/dist/runtime/routes/studio-home.d.ts.map +1 -0
  79. package/dist/runtime/routes/studio-home.js +174 -0
  80. package/dist/runtime/routes/studio-home.js.map +1 -0
  81. package/dist/runtime/routes/theme-bootstrap.d.ts +4 -0
  82. package/dist/runtime/routes/theme-bootstrap.d.ts.map +1 -0
  83. package/dist/runtime/routes/theme-bootstrap.js +65 -0
  84. package/dist/runtime/routes/theme-bootstrap.js.map +1 -0
  85. package/dist/runtime/routes/theme-factory.d.ts +3 -0
  86. package/dist/runtime/routes/theme-factory.d.ts.map +1 -0
  87. package/dist/runtime/routes/theme-factory.js +142 -0
  88. package/dist/runtime/routes/theme-factory.js.map +1 -0
  89. package/dist/runtime/routes/upload.d.ts +3 -0
  90. package/dist/runtime/routes/upload.d.ts.map +1 -0
  91. package/dist/runtime/routes/upload.js +29 -0
  92. package/dist/runtime/routes/upload.js.map +1 -0
  93. package/dist/runtime/schema/infer-json-schema.d.ts +12 -0
  94. package/dist/runtime/schema/infer-json-schema.d.ts.map +1 -0
  95. package/dist/runtime/schema/infer-json-schema.js +75 -0
  96. package/dist/runtime/schema/infer-json-schema.js.map +1 -0
  97. package/dist/runtime/storage/filesystem-adapter.d.ts +29 -0
  98. package/dist/runtime/storage/filesystem-adapter.d.ts.map +1 -0
  99. package/dist/runtime/storage/filesystem-adapter.js +182 -0
  100. package/dist/runtime/storage/filesystem-adapter.js.map +1 -0
  101. package/dist/runtime/storage/filesystem-upload-handler.d.ts +11 -0
  102. package/dist/runtime/storage/filesystem-upload-handler.d.ts.map +1 -0
  103. package/dist/runtime/storage/filesystem-upload-handler.js +37 -0
  104. package/dist/runtime/storage/filesystem-upload-handler.js.map +1 -0
  105. package/dist/runtime/storage/in-memory-adapter.d.ts +24 -0
  106. package/dist/runtime/storage/in-memory-adapter.d.ts.map +1 -0
  107. package/dist/runtime/storage/in-memory-adapter.js +78 -0
  108. package/dist/runtime/storage/in-memory-adapter.js.map +1 -0
  109. package/dist/runtime/themes/preset-catalog.d.ts +9 -0
  110. package/dist/runtime/themes/preset-catalog.d.ts.map +1 -0
  111. package/dist/runtime/themes/preset-catalog.js +47 -0
  112. package/dist/runtime/themes/preset-catalog.js.map +1 -0
  113. package/dist/runtime/utils.d.ts +6 -0
  114. package/dist/runtime/utils.d.ts.map +1 -0
  115. package/dist/runtime/utils.js +29 -0
  116. package/dist/runtime/utils.js.map +1 -0
  117. package/dist/types.d.ts +28 -0
  118. package/dist/types.d.ts.map +1 -0
  119. package/dist/types.js +2 -0
  120. package/dist/types.js.map +1 -0
  121. package/dist/vite/virtual-config-plugin.d.ts +7 -0
  122. package/dist/vite/virtual-config-plugin.d.ts.map +1 -0
  123. package/dist/vite/virtual-config-plugin.js +25 -0
  124. package/dist/vite/virtual-config-plugin.js.map +1 -0
  125. package/package.json +35 -0
  126. package/static/cms/editor/config.js +6 -0
  127. package/static/cms/editor/guards.js +68 -0
  128. package/static/cms/editor/helpers.js +16 -0
  129. package/static/cms/editor/image-edit.js +148 -0
  130. package/static/cms/editor/image-utils.js +84 -0
  131. package/static/cms/editor/image.css +133 -0
  132. package/static/cms/editor/link-ui.css +143 -0
  133. package/static/cms/editor/linkify.js +55 -0
  134. package/static/cms/editor/panel.css +91 -0
  135. package/static/cms/editor/panel.js +64 -0
  136. package/static/cms/editor/save-queue.js +167 -0
  137. package/static/cms/editor/section-controls/activation.js +10 -0
  138. package/static/cms/editor/section-controls/api.js +88 -0
  139. package/static/cms/editor/section-controls/constants.js +24 -0
  140. package/static/cms/editor/section-controls/index.js +622 -0
  141. package/static/cms/editor/section-controls/model.js +76 -0
  142. package/static/cms/editor/section-controls/mutations.js +112 -0
  143. package/static/cms/editor/section-controls/page-context.js +34 -0
  144. package/static/cms/editor/section-controls/pickers.js +196 -0
  145. package/static/cms/editor/section-controls/reorder.js +92 -0
  146. package/static/cms/editor/section-controls/selection.js +54 -0
  147. package/static/cms/editor/section-controls/spacing-drag.js +83 -0
  148. package/static/cms/editor/section-controls/ui-elements.js +54 -0
  149. package/static/cms/editor/section-controls/utils.js +35 -0
  150. package/static/cms/editor/section-controls.css +349 -0
  151. package/static/cms/editor/section-controls.js +1 -0
  152. package/static/cms/editor/security.js +23 -0
  153. package/static/cms/editor/sync.js +64 -0
  154. package/static/cms/editor/text-edit.js +129 -0
  155. package/static/cms/editor/text-link-interactions.js +191 -0
  156. package/static/cms/editor/toast.css +50 -0
  157. package/static/cms/editor/toast.js +29 -0
  158. package/static/cms/editor/tokens-and-text.css +94 -0
  159. package/static/cms/editor/toolbar.css +261 -0
  160. package/static/cms/editor/toolbar.js +110 -0
  161. package/static/cms/editor.css +10 -0
  162. package/static/cms/editor.js +101 -0
  163. package/static/cms/studio.css +312 -0
@@ -0,0 +1,174 @@
1
+ import { isEditorAuthenticated } from "../auth/session.js";
2
+ import { getRuntimeConfig } from "../config.js";
3
+ import { getAdapter } from "./_helpers.js";
4
+ function redirect(pathname) {
5
+ return new Response(null, {
6
+ status: 302,
7
+ headers: { Location: pathname },
8
+ });
9
+ }
10
+ function html(content, status = 200) {
11
+ return new Response(content, {
12
+ status,
13
+ headers: { "Content-Type": "text/html; charset=utf-8" },
14
+ });
15
+ }
16
+ export async function GET(context) {
17
+ const runtime = getRuntimeConfig();
18
+ const loginRedirect = `${runtime.mountPath}?redirect=${encodeURIComponent(`${runtime.mountPath}/cms`)}`;
19
+ if (!isEditorAuthenticated(context)) {
20
+ return redirect(loginRedirect);
21
+ }
22
+ const adapter = getAdapter(context);
23
+ const collections = await adapter.discoverCollections();
24
+ const options = collections.map((name) => {
25
+ const label = name.charAt(0).toUpperCase() + name.slice(1).replace(/[-_]/g, " ");
26
+ return `<option value="${name}">${label}</option>`;
27
+ }).join("");
28
+ return html(`<!doctype html>
29
+ <html lang="en">
30
+ <head>
31
+ <meta charset="utf-8" />
32
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
33
+ <title>Content Studio - Astro Live CMS</title>
34
+ <style>
35
+ :root { color-scheme: light; }
36
+ body { margin: 0; font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: #f5f7fb; color: #1f2430; }
37
+ main { max-width: 1040px; margin: 2.5rem auto; padding: 0 1rem; }
38
+ .card { background: #fff; border: 1px solid #dbe3f0; border-radius: 12px; box-shadow: 0 1px 3px rgba(12,24,40,0.05); }
39
+ .header { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 0.75rem; padding: 1rem 1.25rem; border-bottom: 1px solid #e8edf7; }
40
+ h1 { margin: 0; font-size: 1.25rem; }
41
+ .nav { display: flex; gap: 0.5rem; }
42
+ .nav a { text-decoration: none; font-size: 0.9rem; color: #24427c; padding: 0.4rem 0.6rem; border: 1px solid #bfd0f0; border-radius: 8px; }
43
+ .controls { display: grid; gap: 0.75rem; grid-template-columns: 1fr 1fr 1fr auto; padding: 1rem 1.25rem; border-bottom: 1px solid #e8edf7; }
44
+ input, select { border: 1px solid #c8d2e7; border-radius: 8px; padding: 0.6rem 0.7rem; font-size: 0.9rem; }
45
+ button { border: 0; border-radius: 8px; background: #0f2f66; color: #fff; padding: 0.6rem 0.9rem; cursor: pointer; font-size: 0.9rem; }
46
+ table { width: 100%; border-collapse: collapse; }
47
+ th, td { text-align: left; padding: 0.7rem 0.85rem; border-bottom: 1px solid #eef2fa; font-size: 0.86rem; vertical-align: top; }
48
+ th { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.06em; color: #667085; }
49
+ .mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.78rem; color: #5c6372; }
50
+ .state { padding: 0.9rem 1.25rem; font-size: 0.85rem; color: #4c5566; }
51
+ .pager { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; padding: 0.75rem 1.25rem; }
52
+ </style>
53
+ </head>
54
+ <body>
55
+ <main>
56
+ <section class="card">
57
+ <div class="header">
58
+ <h1>Content Studio</h1>
59
+ <nav class="nav">
60
+ <a href="${runtime.mountPath}">Admin</a>
61
+ <a href="${runtime.mountPath}/themes">Theme Factory</a>
62
+ </nav>
63
+ </div>
64
+ <form class="controls" id="query-form">
65
+ <select id="collection">${options}</select>
66
+ <input id="query" placeholder="Search by id..." />
67
+ <input id="entry-id" placeholder="Optional exact id" />
68
+ <button type="submit">Load</button>
69
+ </form>
70
+ <div class="state" id="state">Loading entries...</div>
71
+ <div style="overflow:auto;">
72
+ <table>
73
+ <thead>
74
+ <tr><th>ID</th><th>Revision</th><th>Data Preview</th></tr>
75
+ </thead>
76
+ <tbody id="rows"></tbody>
77
+ </table>
78
+ </div>
79
+ <div class="pager">
80
+ <button id="prev" type="button">Prev</button>
81
+ <span class="mono" id="page-info">page 1</span>
82
+ <button id="next" type="button">Next</button>
83
+ </div>
84
+ </section>
85
+ </main>
86
+ <script>
87
+ (function () {
88
+ const form = document.getElementById('query-form');
89
+ const collection = document.getElementById('collection');
90
+ const query = document.getElementById('query');
91
+ const entryId = document.getElementById('entry-id');
92
+ const rows = document.getElementById('rows');
93
+ const state = document.getElementById('state');
94
+ const prev = document.getElementById('prev');
95
+ const next = document.getElementById('next');
96
+ const pageInfo = document.getElementById('page-info');
97
+ let page = 1;
98
+ let totalPages = 1;
99
+
100
+ function dataPreview(data) {
101
+ const text = JSON.stringify(data);
102
+ return text.length > 160 ? text.slice(0, 160) + '…' : text;
103
+ }
104
+
105
+ async function load() {
106
+ const params = new URLSearchParams({
107
+ collection: collection.value,
108
+ page: String(page),
109
+ pageSize: '12'
110
+ });
111
+ if (query.value.trim()) params.set('q', query.value.trim());
112
+ if (entryId.value.trim()) params.set('id', entryId.value.trim());
113
+
114
+ state.textContent = 'Loading...';
115
+ rows.innerHTML = '';
116
+
117
+ const res = await fetch('${runtime.apiBasePath}/entries?' + params.toString());
118
+ const json = await res.json().catch(() => ({}));
119
+
120
+ if (!res.ok) {
121
+ state.textContent = json.error || 'Failed to load entries';
122
+ pageInfo.textContent = 'page -';
123
+ return;
124
+ }
125
+
126
+ const entries = Array.isArray(json.entries) ? json.entries : [];
127
+ const pagination = json.pagination || {};
128
+ totalPages = Number.isInteger(pagination.totalPages) ? pagination.totalPages : 1;
129
+ const currentPage = Number.isInteger(pagination.page) ? pagination.page : page;
130
+ page = currentPage;
131
+ pageInfo.textContent = 'page ' + page + ' / ' + Math.max(totalPages, 1) + ' (' + entries.length + ' entries)';
132
+ prev.disabled = page <= 1;
133
+ next.disabled = page >= Math.max(totalPages, 1);
134
+
135
+ if (entries.length === 0) {
136
+ state.textContent = 'No entries found.';
137
+ return;
138
+ }
139
+
140
+ state.textContent = 'Loaded.';
141
+ rows.innerHTML = entries.map((entry) => (
142
+ '<tr>' +
143
+ '<td class="mono">' + entry.id + '</td>' +
144
+ '<td class="mono">' + (entry.revision ?? 0) + '</td>' +
145
+ '<td class="mono">' + dataPreview(entry.data) + '</td>' +
146
+ '</tr>'
147
+ )).join('');
148
+ }
149
+
150
+ form.addEventListener('submit', function (event) {
151
+ event.preventDefault();
152
+ page = 1;
153
+ load();
154
+ });
155
+ prev.addEventListener('click', function () {
156
+ if (page > 1) {
157
+ page -= 1;
158
+ load();
159
+ }
160
+ });
161
+ next.addEventListener('click', function () {
162
+ if (page < totalPages) {
163
+ page += 1;
164
+ load();
165
+ }
166
+ });
167
+
168
+ load();
169
+ })();
170
+ </script>
171
+ </body>
172
+ </html>`);
173
+ }
174
+ //# sourceMappingURL=studio-home.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"studio-home.js","sourceRoot":"","sources":["../../../src/runtime/routes/studio-home.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,SAAS,QAAQ,CAAC,QAAgB;IAChC,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;QACxB,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE;KAChC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,IAAI,CAAC,OAAe,EAAE,MAAM,GAAG,GAAG;IACzC,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE;QAC3B,MAAM;QACN,OAAO,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE;KACxD,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,OAAmB;IAC3C,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;IACnC,MAAM,aAAa,GAAG,GAAG,OAAO,CAAC,SAAS,aAAa,kBAAkB,CAAC,GAAG,OAAO,CAAC,SAAS,MAAM,CAAC,EAAE,CAAC;IAExG,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,CAAC;QACpC,OAAO,QAAQ,CAAC,aAAa,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IACpC,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,mBAAmB,EAAE,CAAC;IACxD,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAC7B,CAAC,IAAI,EAAE,EAAE;QACP,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACjF,OAAO,kBAAkB,IAAI,KAAK,KAAK,WAAW,CAAC;IACrD,CAAC,CACF,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEX,OAAO,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBAgCS,OAAO,CAAC,SAAS;uBACjB,OAAO,CAAC,SAAS;;;;oCAIJ,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qCAoDN,OAAO,CAAC,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAuDhD,CAAC,CAAC;AACV,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { APIContext } from "astro";
2
+ export declare function POST(context: APIContext): Promise<Response>;
3
+ export declare function GET(): Promise<Response>;
4
+ //# sourceMappingURL=theme-bootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-bootstrap.d.ts","sourceRoot":"","sources":["../../../src/runtime/routes/theme-bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AA+BxC,wBAAsB,IAAI,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,CA8CjE;AAED,wBAAsB,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC,CAQ7C"}
@@ -0,0 +1,65 @@
1
+ import { isEditorAuthenticated } from "../auth/session.js";
2
+ import { isKnownThemePreset, themePresetCatalog, } from "../themes/preset-catalog.js";
3
+ import { json, getAdapter } from "./_helpers.js";
4
+ function asBody(input) {
5
+ if (!input || typeof input !== "object" || Array.isArray(input))
6
+ return null;
7
+ const obj = input;
8
+ const mode = obj.mode;
9
+ const themeId = typeof obj.themeId === "string" ? obj.themeId.trim() : "";
10
+ const name = typeof obj.name === "string" ? obj.name.trim() : "";
11
+ const presetId = typeof obj.presetId === "string" ? obj.presetId.trim() : undefined;
12
+ const activate = typeof obj.activate === "boolean" ? obj.activate : true;
13
+ if ((mode !== "blank" && mode !== "preset") || !themeId || !name)
14
+ return null;
15
+ if (mode === "preset" && (!presetId || !isKnownThemePreset(presetId)))
16
+ return null;
17
+ return { mode, themeId, name, presetId, activate };
18
+ }
19
+ export async function POST(context) {
20
+ if (!isEditorAuthenticated(context)) {
21
+ return json({ error: "Unauthorized" }, 401);
22
+ }
23
+ const body = asBody(await context.request.json().catch(() => null));
24
+ if (!body) {
25
+ return json({ error: "Invalid payload" }, 400);
26
+ }
27
+ const adapter = getAdapter(context);
28
+ const existing = await adapter.getEntry("themes", body.themeId);
29
+ if (existing) {
30
+ return json({ error: `Theme "${body.themeId}" already exists` }, 409);
31
+ }
32
+ await adapter.writeEntry("themes", body.themeId, {
33
+ name: body.name,
34
+ mode: body.mode,
35
+ preset_id: body.mode === "preset" ? body.presetId : undefined,
36
+ token_overrides: {},
37
+ block_variants: {},
38
+ });
39
+ await adapter.bumpRevision("themes", body.themeId);
40
+ if (!body.activate) {
41
+ return json({
42
+ ok: true,
43
+ themeId: body.themeId,
44
+ activated: false,
45
+ presetCatalog: themePresetCatalog,
46
+ });
47
+ }
48
+ const currentSite = (await adapter.getEntry("site", "global"))?.data ?? {};
49
+ const nextSite = { ...currentSite, active_theme_id: body.themeId };
50
+ await adapter.writeEntry("site", "global", nextSite);
51
+ await adapter.bumpRevision("site", "global");
52
+ return json({
53
+ ok: true,
54
+ themeId: body.themeId,
55
+ activated: true,
56
+ presetCatalog: themePresetCatalog,
57
+ });
58
+ }
59
+ export async function GET() {
60
+ return json({
61
+ error: "Method not allowed",
62
+ allowedMethods: ["POST"],
63
+ }, 405);
64
+ }
65
+ //# sourceMappingURL=theme-bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-bootstrap.js","sourceRoot":"","sources":["../../../src/runtime/routes/theme-bootstrap.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EACL,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAUjD,SAAS,MAAM,CAAC,KAAc;IAC5B,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7E,MAAM,GAAG,GAAG,KAAgC,CAAC;IAC7C,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IACtB,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1E,MAAM,IAAI,GAAG,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACjE,MAAM,QAAQ,GAAG,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACpF,MAAM,QAAQ,GAAG,OAAO,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IAEzE,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,QAAQ,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAC9E,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAEnF,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,OAAmB;IAC5C,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IACpE,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,GAAG,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAChE,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,IAAI,CAAC,OAAO,kBAAkB,EAAE,EAAE,GAAG,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,OAAO,CAAC,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,SAAS,EAAE,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;QAC7D,eAAe,EAAE,EAAE;QACnB,cAAc,EAAE,EAAE;KACnB,CAAC,CAAC;IACH,MAAM,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAEnD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC;YACV,EAAE,EAAE,IAAI;YACR,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,SAAS,EAAE,KAAK;YAChB,aAAa,EAAE,kBAAkB;SAClC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,WAAW,GAAG,CAAC,MAAM,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC;IAC3E,MAAM,QAAQ,GAAG,EAAE,GAAG,WAAW,EAAE,eAAe,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;IAEnE,MAAM,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACrD,MAAM,OAAO,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAE7C,OAAO,IAAI,CAAC;QACV,EAAE,EAAE,IAAI;QACR,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,SAAS,EAAE,IAAI;QACf,aAAa,EAAE,kBAAkB;KAClC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,GAAG;IACvB,OAAO,IAAI,CACT;QACE,KAAK,EAAE,oBAAoB;QAC3B,cAAc,EAAE,CAAC,MAAM,CAAC;KACzB,EACD,GAAG,CACJ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { APIContext } from "astro";
2
+ export declare function GET(context: APIContext): Promise<Response>;
3
+ //# sourceMappingURL=theme-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-factory.d.ts","sourceRoot":"","sources":["../../../src/runtime/routes/theme-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAmBxC,wBAAsB,GAAG,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,CAgIhE"}
@@ -0,0 +1,142 @@
1
+ import { isEditorAuthenticated } from "../auth/session.js";
2
+ import { getRuntimeConfig } from "../config.js";
3
+ import { themePresetCatalog } from "../themes/preset-catalog.js";
4
+ function redirect(pathname) {
5
+ return new Response(null, {
6
+ status: 302,
7
+ headers: { Location: pathname },
8
+ });
9
+ }
10
+ function html(content) {
11
+ return new Response(content, {
12
+ status: 200,
13
+ headers: { "Content-Type": "text/html; charset=utf-8" },
14
+ });
15
+ }
16
+ export async function GET(context) {
17
+ const runtime = getRuntimeConfig();
18
+ const loginRedirect = `${runtime.mountPath}?redirect=${encodeURIComponent(`${runtime.mountPath}/themes`)}`;
19
+ if (!isEditorAuthenticated(context)) {
20
+ return redirect(loginRedirect);
21
+ }
22
+ const cards = themePresetCatalog.map((preset) => `
23
+ <article class="card" style="overflow:hidden;">
24
+ <img src="${preset.thumbnail}" alt="${preset.label} preview" style="width:100%; aspect-ratio:16/10; object-fit:cover; border-bottom:1px solid #e8edf7;" />
25
+ <div style="padding:0.85rem 1rem;">
26
+ <h3 style="margin:0 0 0.35rem; font-size:1rem;">${preset.label}</h3>
27
+ <p style="margin:0 0 0.8rem; color:#4c5566; font-size:0.86rem;">${preset.description}</p>
28
+ <form class="preset-form" data-preset-id="${preset.themeId}" style="display:grid; gap:0.45rem;">
29
+ <input data-field="id" placeholder="${preset.themeId}-brand" required />
30
+ <input data-field="name" placeholder="${preset.label} Brand" required />
31
+ <button type="submit">Clone + Activate</button>
32
+ </form>
33
+ </div>
34
+ </article>
35
+ `).join("");
36
+ return html(`<!doctype html>
37
+ <html lang="en">
38
+ <head>
39
+ <meta charset="utf-8" />
40
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
41
+ <title>Theme Factory - Astro Live CMS</title>
42
+ <style>
43
+ :root { color-scheme: light; }
44
+ body { margin: 0; font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: #f5f7fb; color: #1f2430; }
45
+ main { max-width: 1120px; margin: 2.5rem auto; padding: 0 1rem; }
46
+ .card { background: #fff; border: 1px solid #dbe3f0; border-radius: 12px; box-shadow: 0 1px 3px rgba(12,24,40,0.05); }
47
+ .header { display:flex; align-items:center; justify-content:space-between; gap:0.75rem; margin-bottom:1rem; }
48
+ .header h1 { margin:0; font-size:1.3rem; }
49
+ .header a { text-decoration:none; font-size:0.9rem; color:#24427c; padding:0.4rem 0.6rem; border:1px solid #bfd0f0; border-radius:8px; }
50
+ .scratch { padding:1rem 1.1rem; margin-bottom:1rem; }
51
+ .scratch form { display:grid; gap:0.6rem; grid-template-columns:1fr 1fr auto; }
52
+ .grid { display:grid; gap:0.85rem; grid-template-columns:repeat(auto-fill,minmax(280px,1fr)); }
53
+ input { border: 1px solid #c8d2e7; border-radius: 8px; padding: 0.6rem 0.7rem; font-size: 0.9rem; }
54
+ button { border: 0; border-radius: 8px; background: #0f2f66; color: #fff; padding: 0.6rem 0.9rem; cursor: pointer; font-size: 0.9rem; }
55
+ #message { margin:0.8rem 0 0.2rem; color:#4c5566; font-size:0.85rem; min-height:1.1rem; }
56
+ </style>
57
+ </head>
58
+ <body>
59
+ <main>
60
+ <div class="header">
61
+ <h1>Theme Factory</h1>
62
+ <div style="display:flex; gap:0.5rem;">
63
+ <a href="${runtime.mountPath}">Admin</a>
64
+ <a href="${runtime.mountPath}/cms">Content Studio</a>
65
+ </div>
66
+ </div>
67
+
68
+ <section class="card scratch">
69
+ <h2 style="margin:0 0 0.55rem; font-size:1rem;">Start From Scratch</h2>
70
+ <form id="blank-theme-form">
71
+ <input id="blank-theme-id" placeholder="my-brand-theme" required />
72
+ <input id="blank-theme-name" placeholder="My Brand Theme" required />
73
+ <button type="submit">Create + Activate</button>
74
+ </form>
75
+ </section>
76
+
77
+ <section class="grid">
78
+ ${cards}
79
+ </section>
80
+
81
+ <p id="message"></p>
82
+ </main>
83
+ <script>
84
+ (function () {
85
+ function slugify(input) {
86
+ return input.toLowerCase().trim().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
87
+ }
88
+
89
+ async function createTheme(payload) {
90
+ const res = await fetch('${runtime.apiBasePath}/themes/bootstrap', {
91
+ method: 'POST',
92
+ headers: { 'Content-Type': 'application/json' },
93
+ body: JSON.stringify(payload),
94
+ });
95
+ const json = await res.json().catch(() => ({}));
96
+ if (!res.ok) throw new Error(json.error || 'Theme creation failed');
97
+ return json;
98
+ }
99
+
100
+ const message = document.getElementById('message');
101
+ const blankForm = document.getElementById('blank-theme-form');
102
+ const blankId = document.getElementById('blank-theme-id');
103
+ const blankName = document.getElementById('blank-theme-name');
104
+
105
+ blankForm.addEventListener('submit', async function (event) {
106
+ event.preventDefault();
107
+ const themeId = slugify(blankId.value);
108
+ const name = blankName.value.trim();
109
+ if (!themeId || !name) return;
110
+ try {
111
+ message.textContent = 'Creating blank theme...';
112
+ await createTheme({ mode: 'blank', themeId, name, activate: true });
113
+ message.textContent = 'Theme created and activated.';
114
+ } catch (error) {
115
+ message.textContent = error instanceof Error ? error.message : 'Theme creation failed';
116
+ }
117
+ });
118
+
119
+ document.querySelectorAll('.preset-form').forEach((form) => {
120
+ form.addEventListener('submit', async function (event) {
121
+ event.preventDefault();
122
+ const presetId = form.getAttribute('data-preset-id');
123
+ const idInput = form.querySelector('[data-field="id"]');
124
+ const nameInput = form.querySelector('[data-field="name"]');
125
+ const themeId = slugify(idInput.value);
126
+ const name = nameInput.value.trim();
127
+ if (!presetId || !themeId || !name) return;
128
+ try {
129
+ message.textContent = 'Cloning preset...';
130
+ await createTheme({ mode: 'preset', presetId, themeId, name, activate: true });
131
+ message.textContent = 'Preset cloned and activated.';
132
+ } catch (error) {
133
+ message.textContent = error instanceof Error ? error.message : 'Theme creation failed';
134
+ }
135
+ });
136
+ });
137
+ })();
138
+ </script>
139
+ </body>
140
+ </html>`);
141
+ }
142
+ //# sourceMappingURL=theme-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-factory.js","sourceRoot":"","sources":["../../../src/runtime/routes/theme-factory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,SAAS,QAAQ,CAAC,QAAgB;IAChC,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;QACxB,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE;KAChC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,IAAI,CAAC,OAAe;IAC3B,OAAO,IAAI,QAAQ,CAAC,OAAO,EAAE;QAC3B,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE;KACxD,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,OAAmB;IAC3C,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;IACnC,MAAM,aAAa,GAAG,GAAG,OAAO,CAAC,SAAS,aAAa,kBAAkB,CAAC,GAAG,OAAO,CAAC,SAAS,SAAS,CAAC,EAAE,CAAC;IAE3G,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,CAAC;QACpC,OAAO,QAAQ,CAAC,aAAa,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,KAAK,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;;kBAEjC,MAAM,CAAC,SAAS,UAAU,MAAM,CAAC,KAAK;;0DAEE,MAAM,CAAC,KAAK;0EACI,MAAM,CAAC,WAAW;oDACxC,MAAM,CAAC,OAAO;gDAClB,MAAM,CAAC,OAAO;kDACZ,MAAM,CAAC,KAAK;;;;;GAK3D,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,OAAO,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA2BO,OAAO,CAAC,SAAS;qBACjB,OAAO,CAAC,SAAS;;;;;;;;;;;;;;UAc5B,KAAK;;;;;;;;;;;;qCAYsB,OAAO,CAAC,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAkDhD,CAAC,CAAC;AACV,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { APIContext } from "astro";
2
+ export declare function POST(context: APIContext): Promise<Response>;
3
+ //# sourceMappingURL=upload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../../src/runtime/routes/upload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAIxC,wBAAsB,IAAI,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,CA4BjE"}
@@ -0,0 +1,29 @@
1
+ import { isEditorAuthenticated } from "../auth/session.js";
2
+ import { json, getUploadHandler } from "./_helpers.js";
3
+ export async function POST(context) {
4
+ if (!isEditorAuthenticated(context)) {
5
+ return json({ error: "Unauthorized" }, 401);
6
+ }
7
+ const contentType = context.request.headers.get("content-type") ?? "";
8
+ if (!contentType.includes("multipart/form-data")) {
9
+ return json({ error: "Expected multipart/form-data" }, 400);
10
+ }
11
+ const formData = await context.request.formData().catch(() => null);
12
+ if (!formData) {
13
+ return json({ error: "Invalid form data" }, 400);
14
+ }
15
+ const file = formData.get("file");
16
+ if (!(file instanceof File)) {
17
+ return json({ error: "Missing file" }, 400);
18
+ }
19
+ try {
20
+ const handler = getUploadHandler(context);
21
+ const result = await handler.upload(file);
22
+ return json({ url: result.url });
23
+ }
24
+ catch (error) {
25
+ const message = error instanceof Error ? error.message : "Upload failed";
26
+ return json({ error: message }, 400);
27
+ }
28
+ }
29
+ //# sourceMappingURL=upload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload.js","sourceRoot":"","sources":["../../../src/runtime/routes/upload.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEvD,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,OAAmB;IAC5C,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;IACtE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAAC;QACjD,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,8BAA8B,EAAE,EAAE,GAAG,CAAC,CAAC;IAC9D,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACpE,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,GAAG,CAAC,CAAC;IACnD,CAAC;IAED,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,CAAC,CAAC,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACzE,OAAO,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,GAAG,CAAC,CAAC;IACvC,CAAC;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ type JsonSchemaNode = {
2
+ type?: string;
3
+ properties?: Record<string, JsonSchemaNode>;
4
+ items?: JsonSchemaNode;
5
+ enum?: unknown[];
6
+ required?: string[];
7
+ default?: unknown;
8
+ };
9
+ export declare function inferJsonSchema(value: unknown): JsonSchemaNode;
10
+ export declare function buildTemplate(schema: unknown): unknown;
11
+ export {};
12
+ //# sourceMappingURL=infer-json-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"infer-json-schema.d.ts","sourceRoot":"","sources":["../../../src/runtime/schema/infer-json-schema.ts"],"names":[],"mappings":"AAAA,KAAK,cAAc,GAAG;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC5C,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAmCF,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAY9D;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CA2BtD"}
@@ -0,0 +1,75 @@
1
+ function inferFromArray(value) {
2
+ if (value.length === 0) {
3
+ return {
4
+ type: "array",
5
+ items: { type: "string" },
6
+ default: [],
7
+ };
8
+ }
9
+ const first = inferJsonSchema(value[0]);
10
+ return {
11
+ type: "array",
12
+ items: first,
13
+ default: [],
14
+ };
15
+ }
16
+ function inferFromObject(value) {
17
+ const properties = {};
18
+ const required = [];
19
+ Object.entries(value).forEach(([key, nested]) => {
20
+ properties[key] = inferJsonSchema(nested);
21
+ required.push(key);
22
+ });
23
+ return {
24
+ type: "object",
25
+ properties,
26
+ required,
27
+ };
28
+ }
29
+ export function inferJsonSchema(value) {
30
+ if (value === null || value === undefined) {
31
+ return { type: "string", default: "" };
32
+ }
33
+ if (Array.isArray(value))
34
+ return inferFromArray(value);
35
+ if (typeof value === "object")
36
+ return inferFromObject(value);
37
+ if (typeof value === "string")
38
+ return { type: "string", default: "" };
39
+ if (typeof value === "number")
40
+ return { type: "number", default: 0 };
41
+ if (typeof value === "boolean")
42
+ return { type: "boolean", default: false };
43
+ return { type: "string", default: "" };
44
+ }
45
+ export function buildTemplate(schema) {
46
+ if (!schema || typeof schema !== "object" || Array.isArray(schema))
47
+ return null;
48
+ const normalized = schema;
49
+ if (normalized.default !== undefined)
50
+ return normalized.default;
51
+ switch (normalized.type) {
52
+ case "object": {
53
+ const obj = {};
54
+ if (normalized.properties) {
55
+ Object.entries(normalized.properties).forEach(([key, prop]) => {
56
+ obj[key] = buildTemplate(prop);
57
+ });
58
+ }
59
+ return obj;
60
+ }
61
+ case "array":
62
+ return [];
63
+ case "string":
64
+ if (normalized.enum && normalized.enum.length > 0)
65
+ return normalized.enum[0];
66
+ return "";
67
+ case "number":
68
+ return 0;
69
+ case "boolean":
70
+ return false;
71
+ default:
72
+ return null;
73
+ }
74
+ }
75
+ //# sourceMappingURL=infer-json-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"infer-json-schema.js","sourceRoot":"","sources":["../../../src/runtime/schema/infer-json-schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { EntryData, HistoryEntry, StorageAdapter } from "../../types.js";
2
+ export declare class FilesystemAdapter implements StorageAdapter {
3
+ private readonly seedsRoot;
4
+ private readonly metaRoot;
5
+ constructor(options?: {
6
+ seedsRoot?: string;
7
+ metaRoot?: string;
8
+ });
9
+ private collectionDir;
10
+ private revisionStorePath;
11
+ private revisionMapKey;
12
+ private historyFilePath;
13
+ discoverCollections(): Promise<string[]>;
14
+ isKnownCollection(collection: string): Promise<boolean>;
15
+ getEntry(collection: string, id: string): Promise<EntryData | null>;
16
+ listEntryIds(collection: string): Promise<string[]>;
17
+ listEntries(collection: string): Promise<EntryData[]>;
18
+ writeEntry(collection: string, id: string, data: Record<string, unknown>): Promise<void>;
19
+ deleteEntry(collection: string, id: string): Promise<void>;
20
+ private readRevisionMap;
21
+ private writeRevisionMap;
22
+ getRevision(collection: string, id: string): Promise<number>;
23
+ bumpRevision(collection: string, id: string): Promise<number>;
24
+ private readHistoryFile;
25
+ private writeHistoryFile;
26
+ getHistory(collection: string, id: string): Promise<HistoryEntry[]>;
27
+ appendHistory(collection: string, id: string, entry: HistoryEntry): Promise<void>;
28
+ }
29
+ //# sourceMappingURL=filesystem-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem-adapter.d.ts","sourceRoot":"","sources":["../../../src/runtime/storage/filesystem-adapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAuB9E,qBAAa,iBAAkB,YAAW,cAAc;IACtD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE;IAK/D,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,eAAe;IAMjB,mBAAmB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAYxC,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAYvD,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAanE,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAYnD,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAMrD,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxF,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAalD,eAAe;YAmBf,gBAAgB;IAMxB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAM5D,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAerD,eAAe;YAYf,gBAAgB;IAUxB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAInE,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAKxF"}