@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.
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +122 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/auth/session.d.ts +14 -0
- package/dist/runtime/auth/session.d.ts.map +1 -0
- package/dist/runtime/auth/session.js +77 -0
- package/dist/runtime/auth/session.js.map +1 -0
- package/dist/runtime/bind.d.ts +14 -0
- package/dist/runtime/bind.d.ts.map +1 -0
- package/dist/runtime/bind.js +11 -0
- package/dist/runtime/bind.js.map +1 -0
- package/dist/runtime/config.d.ts +6 -0
- package/dist/runtime/config.d.ts.map +1 -0
- package/dist/runtime/config.js +18 -0
- package/dist/runtime/config.js.map +1 -0
- package/dist/runtime/content.d.ts +30 -0
- package/dist/runtime/content.d.ts.map +1 -0
- package/dist/runtime/content.js +48 -0
- package/dist/runtime/content.js.map +1 -0
- package/dist/runtime/index.d.ts +10 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +12 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/markers.d.ts +17 -0
- package/dist/runtime/markers.d.ts.map +1 -0
- package/dist/runtime/markers.js +17 -0
- package/dist/runtime/markers.js.map +1 -0
- package/dist/runtime/middleware.d.ts +12 -0
- package/dist/runtime/middleware.d.ts.map +1 -0
- package/dist/runtime/middleware.js +37 -0
- package/dist/runtime/middleware.js.map +1 -0
- package/dist/runtime/mutations/contracts.d.ts +57 -0
- package/dist/runtime/mutations/contracts.d.ts.map +1 -0
- package/dist/runtime/mutations/contracts.js +242 -0
- package/dist/runtime/mutations/contracts.js.map +1 -0
- package/dist/runtime/mutations/engine.d.ts +23 -0
- package/dist/runtime/mutations/engine.d.ts.map +1 -0
- package/dist/runtime/mutations/engine.js +161 -0
- package/dist/runtime/mutations/engine.js.map +1 -0
- package/dist/runtime/routes/_helpers.d.ts +6 -0
- package/dist/runtime/routes/_helpers.d.ts.map +1 -0
- package/dist/runtime/routes/_helpers.js +23 -0
- package/dist/runtime/routes/_helpers.js.map +1 -0
- package/dist/runtime/routes/admin.d.ts +3 -0
- package/dist/runtime/routes/admin.d.ts.map +1 -0
- package/dist/runtime/routes/admin.js +110 -0
- package/dist/runtime/routes/admin.js.map +1 -0
- package/dist/runtime/routes/auth-login.d.ts +4 -0
- package/dist/runtime/routes/auth-login.d.ts.map +1 -0
- package/dist/runtime/routes/auth-login.js +66 -0
- package/dist/runtime/routes/auth-login.js.map +1 -0
- package/dist/runtime/routes/auth-logout.d.ts +4 -0
- package/dist/runtime/routes/auth-logout.d.ts.map +1 -0
- package/dist/runtime/routes/auth-logout.js +51 -0
- package/dist/runtime/routes/auth-logout.js.map +1 -0
- package/dist/runtime/routes/editor-assets.d.ts +3 -0
- package/dist/runtime/routes/editor-assets.d.ts.map +1 -0
- package/dist/runtime/routes/editor-assets.js +47 -0
- package/dist/runtime/routes/editor-assets.js.map +1 -0
- package/dist/runtime/routes/entries.d.ts +3 -0
- package/dist/runtime/routes/entries.d.ts.map +1 -0
- package/dist/runtime/routes/entries.js +89 -0
- package/dist/runtime/routes/entries.js.map +1 -0
- package/dist/runtime/routes/history.d.ts +4 -0
- package/dist/runtime/routes/history.d.ts.map +1 -0
- package/dist/runtime/routes/history.js +56 -0
- package/dist/runtime/routes/history.js.map +1 -0
- package/dist/runtime/routes/mutate.d.ts +4 -0
- package/dist/runtime/routes/mutate.d.ts.map +1 -0
- package/dist/runtime/routes/mutate.js +35 -0
- package/dist/runtime/routes/mutate.js.map +1 -0
- package/dist/runtime/routes/schema.d.ts +3 -0
- package/dist/runtime/routes/schema.d.ts.map +1 -0
- package/dist/runtime/routes/schema.js +27 -0
- package/dist/runtime/routes/schema.js.map +1 -0
- package/dist/runtime/routes/studio-home.d.ts +3 -0
- package/dist/runtime/routes/studio-home.d.ts.map +1 -0
- package/dist/runtime/routes/studio-home.js +174 -0
- package/dist/runtime/routes/studio-home.js.map +1 -0
- package/dist/runtime/routes/theme-bootstrap.d.ts +4 -0
- package/dist/runtime/routes/theme-bootstrap.d.ts.map +1 -0
- package/dist/runtime/routes/theme-bootstrap.js +65 -0
- package/dist/runtime/routes/theme-bootstrap.js.map +1 -0
- package/dist/runtime/routes/theme-factory.d.ts +3 -0
- package/dist/runtime/routes/theme-factory.d.ts.map +1 -0
- package/dist/runtime/routes/theme-factory.js +142 -0
- package/dist/runtime/routes/theme-factory.js.map +1 -0
- package/dist/runtime/routes/upload.d.ts +3 -0
- package/dist/runtime/routes/upload.d.ts.map +1 -0
- package/dist/runtime/routes/upload.js +29 -0
- package/dist/runtime/routes/upload.js.map +1 -0
- package/dist/runtime/schema/infer-json-schema.d.ts +12 -0
- package/dist/runtime/schema/infer-json-schema.d.ts.map +1 -0
- package/dist/runtime/schema/infer-json-schema.js +75 -0
- package/dist/runtime/schema/infer-json-schema.js.map +1 -0
- package/dist/runtime/storage/filesystem-adapter.d.ts +29 -0
- package/dist/runtime/storage/filesystem-adapter.d.ts.map +1 -0
- package/dist/runtime/storage/filesystem-adapter.js +182 -0
- package/dist/runtime/storage/filesystem-adapter.js.map +1 -0
- package/dist/runtime/storage/filesystem-upload-handler.d.ts +11 -0
- package/dist/runtime/storage/filesystem-upload-handler.d.ts.map +1 -0
- package/dist/runtime/storage/filesystem-upload-handler.js +37 -0
- package/dist/runtime/storage/filesystem-upload-handler.js.map +1 -0
- package/dist/runtime/storage/in-memory-adapter.d.ts +24 -0
- package/dist/runtime/storage/in-memory-adapter.d.ts.map +1 -0
- package/dist/runtime/storage/in-memory-adapter.js +78 -0
- package/dist/runtime/storage/in-memory-adapter.js.map +1 -0
- package/dist/runtime/themes/preset-catalog.d.ts +9 -0
- package/dist/runtime/themes/preset-catalog.d.ts.map +1 -0
- package/dist/runtime/themes/preset-catalog.js +47 -0
- package/dist/runtime/themes/preset-catalog.js.map +1 -0
- package/dist/runtime/utils.d.ts +6 -0
- package/dist/runtime/utils.d.ts.map +1 -0
- package/dist/runtime/utils.js +29 -0
- package/dist/runtime/utils.js.map +1 -0
- package/dist/types.d.ts +28 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/vite/virtual-config-plugin.d.ts +7 -0
- package/dist/vite/virtual-config-plugin.d.ts.map +1 -0
- package/dist/vite/virtual-config-plugin.js +25 -0
- package/dist/vite/virtual-config-plugin.js.map +1 -0
- package/package.json +35 -0
- package/static/cms/editor/config.js +6 -0
- package/static/cms/editor/guards.js +68 -0
- package/static/cms/editor/helpers.js +16 -0
- package/static/cms/editor/image-edit.js +148 -0
- package/static/cms/editor/image-utils.js +84 -0
- package/static/cms/editor/image.css +133 -0
- package/static/cms/editor/link-ui.css +143 -0
- package/static/cms/editor/linkify.js +55 -0
- package/static/cms/editor/panel.css +91 -0
- package/static/cms/editor/panel.js +64 -0
- package/static/cms/editor/save-queue.js +167 -0
- package/static/cms/editor/section-controls/activation.js +10 -0
- package/static/cms/editor/section-controls/api.js +88 -0
- package/static/cms/editor/section-controls/constants.js +24 -0
- package/static/cms/editor/section-controls/index.js +622 -0
- package/static/cms/editor/section-controls/model.js +76 -0
- package/static/cms/editor/section-controls/mutations.js +112 -0
- package/static/cms/editor/section-controls/page-context.js +34 -0
- package/static/cms/editor/section-controls/pickers.js +196 -0
- package/static/cms/editor/section-controls/reorder.js +92 -0
- package/static/cms/editor/section-controls/selection.js +54 -0
- package/static/cms/editor/section-controls/spacing-drag.js +83 -0
- package/static/cms/editor/section-controls/ui-elements.js +54 -0
- package/static/cms/editor/section-controls/utils.js +35 -0
- package/static/cms/editor/section-controls.css +349 -0
- package/static/cms/editor/section-controls.js +1 -0
- package/static/cms/editor/security.js +23 -0
- package/static/cms/editor/sync.js +64 -0
- package/static/cms/editor/text-edit.js +129 -0
- package/static/cms/editor/text-link-interactions.js +191 -0
- package/static/cms/editor/toast.css +50 -0
- package/static/cms/editor/toast.js +29 -0
- package/static/cms/editor/tokens-and-text.css +94 -0
- package/static/cms/editor/toolbar.css +261 -0
- package/static/cms/editor/toolbar.js +110 -0
- package/static/cms/editor.css +10 -0
- package/static/cms/editor.js +101 -0
- 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 @@
|
|
|
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":"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"}
|
|
@@ -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 @@
|
|
|
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"}
|