@cancia/astro 0.2.1 → 0.3.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/chunk-52URFK5Y.js +580 -0
- package/dist/{chunk-7MPOERVU.js → chunk-AIPRCBJM.js} +16 -3
- package/dist/{chunk-ST44VULL.js → chunk-L2VKQJPY.js} +9 -4
- package/dist/chunk-PIDFNJME.js +19 -0
- package/dist/{chunk-337LJIKX.js → chunk-UR5WC3RA.js} +1 -1
- package/dist/endpoints/publish.js +23 -17
- package/dist/git-backed-DFAB0tzf.d.ts +98 -0
- package/dist/index.d.ts +33 -1
- package/dist/index.js +53 -23
- package/dist/loader/index.js +2 -2
- package/dist/runtime.d.ts +17 -0
- package/dist/runtime.js +3 -3
- package/dist/storage/index.d.ts +13 -84
- package/dist/storage/index.js +13 -5
- package/package.json +1 -1
- package/dist/chunk-5ELSN6LI.js +0 -244
package/dist/chunk-5ELSN6LI.js
DELETED
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
// src/storage/github-client.ts
|
|
2
|
-
function toBase64(text) {
|
|
3
|
-
if (typeof Buffer !== "undefined") {
|
|
4
|
-
return Buffer.from(text, "utf-8").toString("base64");
|
|
5
|
-
}
|
|
6
|
-
const bytes = new TextEncoder().encode(text);
|
|
7
|
-
let binary = "";
|
|
8
|
-
for (const b of bytes) binary += String.fromCharCode(b);
|
|
9
|
-
return btoa(binary);
|
|
10
|
-
}
|
|
11
|
-
function createGitHubClient(opts) {
|
|
12
|
-
const { repo, branch, token, committer } = opts;
|
|
13
|
-
const doFetch = opts.fetch ?? globalThis.fetch;
|
|
14
|
-
const apiBase = (opts.apiBase ?? "https://api.github.com").replace(/\/$/, "");
|
|
15
|
-
if (!doFetch) {
|
|
16
|
-
throw new Error("createGitHubClient: no fetch available (pass opts.fetch)");
|
|
17
|
-
}
|
|
18
|
-
const headers = () => ({
|
|
19
|
-
Authorization: `Bearer ${token}`,
|
|
20
|
-
Accept: "application/vnd.github+json",
|
|
21
|
-
"X-GitHub-Api-Version": "2022-11-28"
|
|
22
|
-
});
|
|
23
|
-
const contentsUrl = (path) => {
|
|
24
|
-
const encoded = path.split("/").map((seg) => encodeURIComponent(seg)).join("/");
|
|
25
|
-
return `${apiBase}/repos/${repo}/contents/${encoded}`;
|
|
26
|
-
};
|
|
27
|
-
async function getFileSha(path) {
|
|
28
|
-
const url = `${contentsUrl(path)}?ref=${encodeURIComponent(branch)}`;
|
|
29
|
-
const res = await doFetch(url, { method: "GET", headers: headers() });
|
|
30
|
-
if (res.status === 404) return null;
|
|
31
|
-
if (!res.ok) {
|
|
32
|
-
const detail = await res.text().catch(() => "");
|
|
33
|
-
throw new Error(`GitHub getFileSha ${path} failed: ${res.status} ${detail}`);
|
|
34
|
-
}
|
|
35
|
-
const body = await res.json();
|
|
36
|
-
return body.sha ?? null;
|
|
37
|
-
}
|
|
38
|
-
async function putFile(file, message) {
|
|
39
|
-
const sha = await getFileSha(file.path);
|
|
40
|
-
const payload = {
|
|
41
|
-
message,
|
|
42
|
-
content: toBase64(file.content),
|
|
43
|
-
branch
|
|
44
|
-
};
|
|
45
|
-
if (sha) payload.sha = sha;
|
|
46
|
-
if (committer) payload.committer = committer;
|
|
47
|
-
const res = await doFetch(contentsUrl(file.path), {
|
|
48
|
-
method: "PUT",
|
|
49
|
-
headers: headers(),
|
|
50
|
-
body: JSON.stringify(payload)
|
|
51
|
-
});
|
|
52
|
-
if (!res.ok) {
|
|
53
|
-
const detail = await res.text().catch(() => "");
|
|
54
|
-
throw new Error(`GitHub commit ${file.path} failed: ${res.status} ${detail}`);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
async function commitFiles(files, message) {
|
|
58
|
-
for (const file of files) {
|
|
59
|
-
await putFile(file, message);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return { getFileSha, commitFiles };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// src/storage/git-backed.ts
|
|
66
|
-
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
|
67
|
-
import { join, relative } from "path";
|
|
68
|
-
function createGitBackedAdapter(opts) {
|
|
69
|
-
const projectRoot = opts.projectRoot ?? process.cwd();
|
|
70
|
-
const branch = opts.branch ?? "main";
|
|
71
|
-
const debounceMs = opts.debounceMs ?? 3e3;
|
|
72
|
-
const commitMessage = opts.commitMessage ?? "Cancia: content update";
|
|
73
|
-
const warn = opts.warn ?? ((m) => console.warn(m));
|
|
74
|
-
const onError = opts.onError ?? ((m, e) => console.error(m, e));
|
|
75
|
-
const kvPath = opts.contentPaths?.kvPath ?? join(projectRoot, "cancia-content.json");
|
|
76
|
-
const pagesPath = opts.contentPaths?.pagesPath ?? join(projectRoot, ".cancia", "pages.json");
|
|
77
|
-
const listsDir = opts.contentPaths?.listsDir ?? join(projectRoot, ".cancia", "lists");
|
|
78
|
-
const token = opts.token ?? process.env.CANCIA_GITHUB_TOKEN ?? "";
|
|
79
|
-
let client = null;
|
|
80
|
-
if (opts.client) {
|
|
81
|
-
client = opts.client;
|
|
82
|
-
} else if (token) {
|
|
83
|
-
client = createGitHubClient({
|
|
84
|
-
repo: opts.repo,
|
|
85
|
-
branch,
|
|
86
|
-
token,
|
|
87
|
-
committer: opts.committer,
|
|
88
|
-
fetch: opts.fetch,
|
|
89
|
-
apiBase: opts.apiBase
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
const gitEnabled = client !== null;
|
|
93
|
-
if (!gitEnabled) {
|
|
94
|
-
warn(
|
|
95
|
-
"[cancia] Git-backed storage: no GitHub token (CANCIA_GITHUB_TOKEN) \u2014 running local-only. Edits save to disk but are NOT committed/pushed."
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
const dirty = /* @__PURE__ */ new Set();
|
|
99
|
-
let timer = null;
|
|
100
|
-
let flushing = null;
|
|
101
|
-
let rerunRequested = false;
|
|
102
|
-
function toRepoPath(absPath) {
|
|
103
|
-
return relative(projectRoot, absPath).split("\\").join("/");
|
|
104
|
-
}
|
|
105
|
-
function markDirty(absPath) {
|
|
106
|
-
dirty.add(absPath);
|
|
107
|
-
}
|
|
108
|
-
function markListDirty(listName, site) {
|
|
109
|
-
const siteDir = join(listsDir, listName, site);
|
|
110
|
-
if (!existsSync(siteDir)) return;
|
|
111
|
-
const walk = (dir) => {
|
|
112
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
113
|
-
const full = join(dir, entry.name);
|
|
114
|
-
if (entry.isDirectory()) walk(full);
|
|
115
|
-
else if (entry.isFile()) markDirty(full);
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
walk(siteDir);
|
|
119
|
-
}
|
|
120
|
-
function scheduleFlush() {
|
|
121
|
-
if (!gitEnabled) return;
|
|
122
|
-
if (timer) clearTimeout(timer);
|
|
123
|
-
timer = setTimeout(() => {
|
|
124
|
-
timer = null;
|
|
125
|
-
void runFlush();
|
|
126
|
-
}, debounceMs);
|
|
127
|
-
}
|
|
128
|
-
async function runFlush() {
|
|
129
|
-
if (flushing) {
|
|
130
|
-
rerunRequested = true;
|
|
131
|
-
return flushing;
|
|
132
|
-
}
|
|
133
|
-
flushing = doFlush().finally(() => {
|
|
134
|
-
flushing = null;
|
|
135
|
-
if (rerunRequested) {
|
|
136
|
-
rerunRequested = false;
|
|
137
|
-
void runFlush();
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
return flushing;
|
|
141
|
-
}
|
|
142
|
-
async function doFlush() {
|
|
143
|
-
if (!client || dirty.size === 0) return;
|
|
144
|
-
const batch = [...dirty];
|
|
145
|
-
const files = [];
|
|
146
|
-
for (const abs of batch) {
|
|
147
|
-
if (!existsSync(abs) || !statSync(abs).isFile()) continue;
|
|
148
|
-
files.push({ path: toRepoPath(abs), content: readFileSync(abs, "utf-8") });
|
|
149
|
-
}
|
|
150
|
-
if (files.length === 0) {
|
|
151
|
-
for (const abs of batch) dirty.delete(abs);
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
try {
|
|
155
|
-
await client.commitFiles(files, commitMessage);
|
|
156
|
-
for (const abs of batch) dirty.delete(abs);
|
|
157
|
-
} catch (err) {
|
|
158
|
-
onError(
|
|
159
|
-
"[cancia] Git-backed storage: commit failed \u2014 data saved locally, will retry on next flush.",
|
|
160
|
-
err
|
|
161
|
-
);
|
|
162
|
-
throw err;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
async function flush() {
|
|
166
|
-
if (!gitEnabled) return;
|
|
167
|
-
if (timer) {
|
|
168
|
-
clearTimeout(timer);
|
|
169
|
-
timer = null;
|
|
170
|
-
}
|
|
171
|
-
await runFlush();
|
|
172
|
-
}
|
|
173
|
-
const kv = {
|
|
174
|
-
get: (site, key, lang) => opts.local.kv.get(site, key, lang),
|
|
175
|
-
getAll: (site) => opts.local.kv.getAll(site),
|
|
176
|
-
async set(site, key, lang, value) {
|
|
177
|
-
await opts.local.kv.set(site, key, lang, value);
|
|
178
|
-
markDirty(kvPath);
|
|
179
|
-
scheduleFlush();
|
|
180
|
-
},
|
|
181
|
-
async delete(site, key, lang) {
|
|
182
|
-
await opts.local.kv.delete(site, key, lang);
|
|
183
|
-
markDirty(kvPath);
|
|
184
|
-
scheduleFlush();
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
const pages = {
|
|
188
|
-
get: (site, route) => opts.local.pages.get(site, route),
|
|
189
|
-
list: (site) => opts.local.pages.list(site),
|
|
190
|
-
async set(site, route, meta, rev) {
|
|
191
|
-
const result = await opts.local.pages.set(site, route, meta, rev);
|
|
192
|
-
markDirty(pagesPath);
|
|
193
|
-
scheduleFlush();
|
|
194
|
-
return result;
|
|
195
|
-
},
|
|
196
|
-
async delete(site, route) {
|
|
197
|
-
await opts.local.pages.delete(site, route);
|
|
198
|
-
markDirty(pagesPath);
|
|
199
|
-
scheduleFlush();
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
const lists = {
|
|
203
|
-
list: (site, listName, locale) => opts.local.lists.list(site, listName, locale),
|
|
204
|
-
get: (site, listName, id, locale) => opts.local.lists.get(site, listName, id, locale),
|
|
205
|
-
translations: (site, listName) => opts.local.lists.translations(site, listName),
|
|
206
|
-
async create(site, listName, data, locale, id) {
|
|
207
|
-
const entry = await opts.local.lists.create(site, listName, data, locale, id);
|
|
208
|
-
markListDirty(listName, site);
|
|
209
|
-
scheduleFlush();
|
|
210
|
-
return entry;
|
|
211
|
-
},
|
|
212
|
-
async update(site, listName, id, locale, data, rev) {
|
|
213
|
-
const entry = await opts.local.lists.update(site, listName, id, locale, data, rev);
|
|
214
|
-
markListDirty(listName, site);
|
|
215
|
-
scheduleFlush();
|
|
216
|
-
return entry;
|
|
217
|
-
},
|
|
218
|
-
async delete(site, listName, id, locale) {
|
|
219
|
-
await opts.local.lists.delete(site, listName, id, locale);
|
|
220
|
-
markListDirty(listName, site);
|
|
221
|
-
scheduleFlush();
|
|
222
|
-
},
|
|
223
|
-
async reorder(site, listName, ids) {
|
|
224
|
-
await opts.local.lists.reorder(site, listName, ids);
|
|
225
|
-
markListDirty(listName, site);
|
|
226
|
-
scheduleFlush();
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
const git = {
|
|
230
|
-
flush,
|
|
231
|
-
get gitEnabled() {
|
|
232
|
-
return gitEnabled;
|
|
233
|
-
},
|
|
234
|
-
pendingPaths() {
|
|
235
|
-
return [...dirty].map(toRepoPath);
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
return { kv, pages, lists, git };
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
export {
|
|
242
|
-
createGitHubClient,
|
|
243
|
-
createGitBackedAdapter
|
|
244
|
-
};
|