@cancia/astro 0.3.0 → 0.4.1
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 → chunk-5AUIPW2I.js} +3 -65
- package/dist/chunk-BMCI2F2Y.js +45 -0
- package/dist/{chunk-AIPRCBJM.js → chunk-U46HCJN3.js} +5 -1
- package/dist/chunk-U7V53JX7.js +83 -0
- package/dist/endpoints/publish.js +27 -3
- package/dist/{git-backed-DFAB0tzf.d.ts → git-backed-DtiH52EI.d.ts} +1 -1
- package/dist/{github-client-BAZ1pW24.d.ts → github-client-Db0pIrqE.d.ts} +9 -0
- package/dist/index.d.ts +22 -2
- package/dist/index.js +43 -12
- package/dist/runtime.d.ts +25 -3
- package/dist/runtime.js +3 -2
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.js +4 -2
- package/package.json +1 -1
- package/dist/chunk-PIDFNJME.js +0 -19
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createGitHubClient
|
|
3
|
+
} from "./chunk-U7V53JX7.js";
|
|
1
4
|
import {
|
|
2
5
|
hashRev
|
|
3
6
|
} from "./chunk-L2VKQJPY.js";
|
|
@@ -332,70 +335,6 @@ function closeSqliteAdapterV2(dbPath) {
|
|
|
332
335
|
}
|
|
333
336
|
}
|
|
334
337
|
|
|
335
|
-
// src/storage/github-client.ts
|
|
336
|
-
function toBase64(text) {
|
|
337
|
-
if (typeof Buffer !== "undefined") {
|
|
338
|
-
return Buffer.from(text, "utf-8").toString("base64");
|
|
339
|
-
}
|
|
340
|
-
const bytes = new TextEncoder().encode(text);
|
|
341
|
-
let binary = "";
|
|
342
|
-
for (const b of bytes) binary += String.fromCharCode(b);
|
|
343
|
-
return btoa(binary);
|
|
344
|
-
}
|
|
345
|
-
function createGitHubClient(opts) {
|
|
346
|
-
const { repo, branch, token, committer } = opts;
|
|
347
|
-
const doFetch = opts.fetch ?? globalThis.fetch;
|
|
348
|
-
const apiBase = (opts.apiBase ?? "https://api.github.com").replace(/\/$/, "");
|
|
349
|
-
if (!doFetch) {
|
|
350
|
-
throw new Error("createGitHubClient: no fetch available (pass opts.fetch)");
|
|
351
|
-
}
|
|
352
|
-
const headers = () => ({
|
|
353
|
-
Authorization: `Bearer ${token}`,
|
|
354
|
-
Accept: "application/vnd.github+json",
|
|
355
|
-
"X-GitHub-Api-Version": "2022-11-28"
|
|
356
|
-
});
|
|
357
|
-
const contentsUrl = (path) => {
|
|
358
|
-
const encoded = path.split("/").map((seg) => encodeURIComponent(seg)).join("/");
|
|
359
|
-
return `${apiBase}/repos/${repo}/contents/${encoded}`;
|
|
360
|
-
};
|
|
361
|
-
async function getFileSha(path) {
|
|
362
|
-
const url = `${contentsUrl(path)}?ref=${encodeURIComponent(branch)}`;
|
|
363
|
-
const res = await doFetch(url, { method: "GET", headers: headers() });
|
|
364
|
-
if (res.status === 404) return null;
|
|
365
|
-
if (!res.ok) {
|
|
366
|
-
const detail = await res.text().catch(() => "");
|
|
367
|
-
throw new Error(`GitHub getFileSha ${path} failed: ${res.status} ${detail}`);
|
|
368
|
-
}
|
|
369
|
-
const body = await res.json();
|
|
370
|
-
return body.sha ?? null;
|
|
371
|
-
}
|
|
372
|
-
async function putFile(file, message) {
|
|
373
|
-
const sha = await getFileSha(file.path);
|
|
374
|
-
const payload = {
|
|
375
|
-
message,
|
|
376
|
-
content: toBase64(file.content),
|
|
377
|
-
branch
|
|
378
|
-
};
|
|
379
|
-
if (sha) payload.sha = sha;
|
|
380
|
-
if (committer) payload.committer = committer;
|
|
381
|
-
const res = await doFetch(contentsUrl(file.path), {
|
|
382
|
-
method: "PUT",
|
|
383
|
-
headers: headers(),
|
|
384
|
-
body: JSON.stringify(payload)
|
|
385
|
-
});
|
|
386
|
-
if (!res.ok) {
|
|
387
|
-
const detail = await res.text().catch(() => "");
|
|
388
|
-
throw new Error(`GitHub commit ${file.path} failed: ${res.status} ${detail}`);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
async function commitFiles(files, message) {
|
|
392
|
-
for (const file of files) {
|
|
393
|
-
await putFile(file, message);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
return { getFileSha, commitFiles };
|
|
397
|
-
}
|
|
398
|
-
|
|
399
338
|
// src/storage/git-backed.ts
|
|
400
339
|
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
|
401
340
|
import { join, relative } from "path";
|
|
@@ -575,6 +514,5 @@ function createGitBackedAdapter(opts) {
|
|
|
575
514
|
export {
|
|
576
515
|
createSqliteAdapterV2,
|
|
577
516
|
closeSqliteAdapterV2,
|
|
578
|
-
createGitHubClient,
|
|
579
517
|
createGitBackedAdapter
|
|
580
518
|
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createGitHubClient
|
|
3
|
+
} from "./chunk-U7V53JX7.js";
|
|
4
|
+
|
|
5
|
+
// src/publish-hook.ts
|
|
6
|
+
async function firePublish(hook, opts = {}) {
|
|
7
|
+
if (!hook) return { ok: false, kind: "no-hook" };
|
|
8
|
+
const headers = { ...opts.headers ?? {} };
|
|
9
|
+
if (opts.token) headers.Authorization = `Bearer ${opts.token}`;
|
|
10
|
+
const init = { method: opts.method ?? "POST" };
|
|
11
|
+
if (Object.keys(headers).length > 0) init.headers = headers;
|
|
12
|
+
try {
|
|
13
|
+
const res = await fetch(hook, init);
|
|
14
|
+
if (!res.ok) return { ok: false, kind: "bad-response", status: res.status };
|
|
15
|
+
return { ok: true };
|
|
16
|
+
} catch {
|
|
17
|
+
return { ok: false, kind: "unreachable" };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/publish-dispatch.ts
|
|
22
|
+
var CANCIA_PUBLISH_EVENT = "cancia-publish";
|
|
23
|
+
async function fireDispatch(opts) {
|
|
24
|
+
if (!opts.repo || !opts.token) return { ok: false, kind: "not-configured" };
|
|
25
|
+
const client = createGitHubClient({
|
|
26
|
+
repo: opts.repo,
|
|
27
|
+
// branch is irrelevant for dispatch (repo-level event) but required by the
|
|
28
|
+
// client's options — a harmless placeholder.
|
|
29
|
+
branch: "main",
|
|
30
|
+
token: opts.token,
|
|
31
|
+
fetch: opts.fetch,
|
|
32
|
+
apiBase: opts.apiBase
|
|
33
|
+
});
|
|
34
|
+
try {
|
|
35
|
+
await client.dispatch(opts.eventType ?? CANCIA_PUBLISH_EVENT, opts.clientPayload);
|
|
36
|
+
return { ok: true };
|
|
37
|
+
} catch {
|
|
38
|
+
return { ok: false, kind: "failed" };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export {
|
|
43
|
+
firePublish,
|
|
44
|
+
fireDispatch
|
|
45
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createGitBackedAdapter,
|
|
3
3
|
createSqliteAdapterV2
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-5AUIPW2I.js";
|
|
5
5
|
import {
|
|
6
6
|
createJsonFileAdapter,
|
|
7
7
|
createJsonFileAdapterV2
|
|
@@ -172,6 +172,8 @@ function buildRuntimeFromBaked(baked) {
|
|
|
172
172
|
const secret = baked.public ? void 0 : token || void 0;
|
|
173
173
|
const deployHook = process.env.CANCIA_DEPLOY_HOOK || void 0;
|
|
174
174
|
const deployHookToken = process.env.CANCIA_DEPLOY_HOOK_TOKEN || void 0;
|
|
175
|
+
const publishRepo = baked.publishRepo || void 0;
|
|
176
|
+
const publishGithubToken = process.env.CANCIA_GITHUB_TOKEN?.trim() || void 0;
|
|
175
177
|
const storageV2 = buildStorageV2(baked, projectRoot);
|
|
176
178
|
const storage = storageV2 ? storageV2.kv : lazyJsonFileAdapter(projectRoot);
|
|
177
179
|
const uploadHandler = buildUploadHandler(baked, projectRoot);
|
|
@@ -188,6 +190,8 @@ function buildRuntimeFromBaked(baked) {
|
|
|
188
190
|
deployHookToken,
|
|
189
191
|
deployHookMethod: baked.deployHookMethod,
|
|
190
192
|
deployHookHeaders: baked.deployHookHeaders,
|
|
193
|
+
publishRepo,
|
|
194
|
+
publishGithubToken,
|
|
191
195
|
maxUploadMB: baked.maxUploadMB
|
|
192
196
|
};
|
|
193
197
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
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
|
+
async function dispatch(eventType, clientPayload) {
|
|
63
|
+
const url = `${apiBase}/repos/${repo}/dispatches`;
|
|
64
|
+
const payload = { event_type: eventType };
|
|
65
|
+
if (clientPayload !== void 0) payload.client_payload = clientPayload;
|
|
66
|
+
const res = await doFetch(url, {
|
|
67
|
+
method: "POST",
|
|
68
|
+
headers: headers(),
|
|
69
|
+
body: JSON.stringify(payload)
|
|
70
|
+
});
|
|
71
|
+
if (!res.ok) {
|
|
72
|
+
const detail = await res.text().catch(() => "");
|
|
73
|
+
throw new Error(
|
|
74
|
+
`GitHub repository_dispatch ${eventType} failed: ${res.status} ${detail}`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return { getFileSha, commitFiles, dispatch };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export {
|
|
82
|
+
createGitHubClient
|
|
83
|
+
};
|
|
@@ -1,16 +1,38 @@
|
|
|
1
1
|
import {
|
|
2
|
+
fireDispatch,
|
|
2
3
|
firePublish
|
|
3
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-BMCI2F2Y.js";
|
|
5
|
+
import "../chunk-U7V53JX7.js";
|
|
4
6
|
|
|
5
7
|
// src/endpoints/publish.ts
|
|
6
8
|
import { getCanciaRuntime } from "virtual:cancia/runtime";
|
|
7
9
|
async function POST({ request }) {
|
|
8
|
-
const {
|
|
10
|
+
const {
|
|
11
|
+
deployHook,
|
|
12
|
+
deployHookToken,
|
|
13
|
+
deployHookMethod,
|
|
14
|
+
deployHookHeaders,
|
|
15
|
+
publishRepo,
|
|
16
|
+
publishGithubToken,
|
|
17
|
+
secret
|
|
18
|
+
} = getCanciaRuntime();
|
|
9
19
|
if (secret) {
|
|
10
20
|
const token = request.headers.get("Authorization")?.replace("Bearer ", "").trim();
|
|
11
21
|
if (token !== secret)
|
|
12
22
|
return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401 });
|
|
13
23
|
}
|
|
24
|
+
const ghToken = publishGithubToken ?? process.env.CANCIA_GITHUB_TOKEN?.trim();
|
|
25
|
+
if (publishRepo && ghToken) {
|
|
26
|
+
const dispatched = await fireDispatch({ repo: publishRepo, token: ghToken });
|
|
27
|
+
if (dispatched.ok)
|
|
28
|
+
return new Response(JSON.stringify({ ok: true }), {
|
|
29
|
+
headers: { "Content-Type": "application/json" }
|
|
30
|
+
});
|
|
31
|
+
return new Response(
|
|
32
|
+
JSON.stringify({ error: "GitHub repository_dispatch failed" }),
|
|
33
|
+
{ status: 502 }
|
|
34
|
+
);
|
|
35
|
+
}
|
|
14
36
|
const hook = deployHook ?? process.env.CANCIA_DEPLOY_HOOK;
|
|
15
37
|
const result = await firePublish(hook, {
|
|
16
38
|
token: deployHookToken,
|
|
@@ -23,7 +45,9 @@ async function POST({ request }) {
|
|
|
23
45
|
});
|
|
24
46
|
if (result.kind === "no-hook")
|
|
25
47
|
return new Response(
|
|
26
|
-
JSON.stringify({
|
|
48
|
+
JSON.stringify({
|
|
49
|
+
error: "No publish mechanism configured. Set a publish repo + CANCIA_GITHUB_TOKEN, or CANCIA_DEPLOY_HOOK."
|
|
50
|
+
}),
|
|
27
51
|
{ status: 503 }
|
|
28
52
|
);
|
|
29
53
|
if (result.kind === "bad-response")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { C as CanciaStorage, a as CanciaStorageV2 } from './types-BMlLS-OS.js';
|
|
2
|
-
import { G as GitHubCommitter, a as GitHubClient, F as FetchLike } from './github-client-
|
|
2
|
+
import { G as GitHubCommitter, a as GitHubClient, F as FetchLike } from './github-client-Db0pIrqE.js';
|
|
3
3
|
|
|
4
4
|
declare function createJsonFileAdapter(filePath?: string): CanciaStorage;
|
|
5
5
|
|
|
@@ -42,6 +42,15 @@ interface GitHubClient {
|
|
|
42
42
|
* any PUT fails so the caller can keep the batch dirty and retry.
|
|
43
43
|
*/
|
|
44
44
|
commitFiles(files: CommitFile[], message: string): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Fire a `repository_dispatch` event on the repo. POSTs
|
|
47
|
+
* `/repos/{owner}/{repo}/dispatches` with `{ event_type, client_payload }`
|
|
48
|
+
* and the Bearer token. GitHub returns 204 on success — throws on anything
|
|
49
|
+
* else. This is the host-independent publish trigger (031): a workflow in the
|
|
50
|
+
* client repo listens for the event and makes an empty commit, so the host's
|
|
51
|
+
* normal git integration rebuilds. Same repo-write PAT the Contents API uses.
|
|
52
|
+
*/
|
|
53
|
+
dispatch(eventType: string, clientPayload?: object): Promise<void>;
|
|
45
54
|
}
|
|
46
55
|
declare function createGitHubClient(opts: GitHubClientOptions): GitHubClient;
|
|
47
56
|
|
package/dist/index.d.ts
CHANGED
|
@@ -3,10 +3,10 @@ import { C as CanciaStorage, a as CanciaStorageV2 } from './types-BMlLS-OS.js';
|
|
|
3
3
|
export { b as CanciaKVStore, c as CanciaListStore, d as CanciaPageStore, L as ListEntry, P as PageMeta, e as PageRecord, f as PageSEO, R as Rev, g as RevConflictError } from './types-BMlLS-OS.js';
|
|
4
4
|
import { U as UploadHandler } from './upload-DwCGjXbz.js';
|
|
5
5
|
export { m as makeLocalUploadHandler } from './upload-DwCGjXbz.js';
|
|
6
|
-
import { G as GitHubCommitter } from './github-client-
|
|
6
|
+
import { G as GitHubCommitter } from './github-client-Db0pIrqE.js';
|
|
7
7
|
export { CanciaLoaderOptions, canciaLoader } from './loader/index.js';
|
|
8
8
|
export { FieldDescription, FieldMeta, FieldMetaBase, FieldWidget, ListDescription, ListSchema, SchemasModule, defineField, defineList, describeList } from './schema/index.js';
|
|
9
|
-
export { G as GitBackedContentPaths, a as GitBackedControls, b as GitBackedOptions, c as GitBackedStorage, S as SqliteV2Options, d as closeSqliteAdapterV2, e as createGitBackedAdapter, f as createJsonFileAdapter, g as createJsonFileAdapterV2, h as createSQLiteAdapter, i as createSqliteAdapterV2 } from './git-backed-
|
|
9
|
+
export { G as GitBackedContentPaths, a as GitBackedControls, b as GitBackedOptions, c as GitBackedStorage, S as SqliteV2Options, d as closeSqliteAdapterV2, e as createGitBackedAdapter, f as createJsonFileAdapter, g as createJsonFileAdapterV2, h as createSQLiteAdapter, i as createSqliteAdapterV2 } from './git-backed-DtiH52EI.js';
|
|
10
10
|
export { z } from 'zod';
|
|
11
11
|
import 'astro/loaders';
|
|
12
12
|
import './portable-text-BikSqS9T.js';
|
|
@@ -59,6 +59,26 @@ interface CanciaIntegrationOptions {
|
|
|
59
59
|
deployHookMethod?: string;
|
|
60
60
|
/** Optional extra headers merged into the deploy-hook request. */
|
|
61
61
|
deployHookHeaders?: Record<string, string>;
|
|
62
|
+
/**
|
|
63
|
+
* The host-independent standard publish path (031). When set, the Publish
|
|
64
|
+
* button fires a GitHub `repository_dispatch` (event `cancia-publish`) on
|
|
65
|
+
* this repo instead of the raw deploy hook — a workflow in the repo
|
|
66
|
+
* (templates/cancia-publish.yml) makes an empty commit → the host's normal
|
|
67
|
+
* git integration rebuilds. Identical for every host; no host API / IP
|
|
68
|
+
* allowlist. The token is NEVER baked — it is read from
|
|
69
|
+
* `process.env.CANCIA_GITHUB_TOKEN` at runtime (a fine-grained PAT with
|
|
70
|
+
* `contents:write`, which covers repository_dispatch). Precedence:
|
|
71
|
+
* publish-dispatch (if repo + token) → deploy hook (030) → 503.
|
|
72
|
+
*
|
|
73
|
+
* If a git-backed `git.repo` is already declared, that repo is reused as the
|
|
74
|
+
* publish target unless overridden here.
|
|
75
|
+
*/
|
|
76
|
+
publish?: {
|
|
77
|
+
github?: {
|
|
78
|
+
/** "owner/name" of the repo whose `cancia-publish` workflow rebuilds. */
|
|
79
|
+
repo: string;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
62
82
|
/**
|
|
63
83
|
* Custom storage adapter.
|
|
64
84
|
* Default: SQLite (cancia.db in project root) — works anywhere with Node.
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
+
fireDispatch,
|
|
2
3
|
firePublish
|
|
3
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-BMCI2F2Y.js";
|
|
4
5
|
import {
|
|
5
6
|
makeListsRoutes
|
|
6
7
|
} from "./chunk-22DJVJBR.js";
|
|
@@ -13,7 +14,7 @@ import {
|
|
|
13
14
|
makeR2UploadHandler,
|
|
14
15
|
makeUploadRoute,
|
|
15
16
|
setCanciaRuntime
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-U46HCJN3.js";
|
|
17
18
|
import {
|
|
18
19
|
defineField,
|
|
19
20
|
defineList,
|
|
@@ -30,7 +31,8 @@ import {
|
|
|
30
31
|
closeSqliteAdapterV2,
|
|
31
32
|
createGitBackedAdapter,
|
|
32
33
|
createSqliteAdapterV2
|
|
33
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-5AUIPW2I.js";
|
|
35
|
+
import "./chunk-U7V53JX7.js";
|
|
34
36
|
import {
|
|
35
37
|
createJsonFileAdapter,
|
|
36
38
|
createJsonFileAdapterV2
|
|
@@ -93,7 +95,7 @@ function makeContentRoute(storage, secret) {
|
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
// src/routes/publish.ts
|
|
96
|
-
function makePublishRoute(deployHook, secret, hookOptions) {
|
|
98
|
+
function makePublishRoute(deployHook, secret, hookOptions, dispatch) {
|
|
97
99
|
return async ({ request }) => {
|
|
98
100
|
if (secret) {
|
|
99
101
|
const token = request.headers.get("Authorization")?.replace("Bearer ", "").trim();
|
|
@@ -101,6 +103,18 @@ function makePublishRoute(deployHook, secret, hookOptions) {
|
|
|
101
103
|
return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401 });
|
|
102
104
|
}
|
|
103
105
|
}
|
|
106
|
+
if (dispatch?.repo && dispatch?.token) {
|
|
107
|
+
const dispatched = await fireDispatch({ repo: dispatch.repo, token: dispatch.token });
|
|
108
|
+
if (dispatched.ok) {
|
|
109
|
+
return new Response(JSON.stringify({ ok: true }), {
|
|
110
|
+
headers: { "Content-Type": "application/json" }
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return new Response(
|
|
114
|
+
JSON.stringify({ error: "GitHub repository_dispatch failed" }),
|
|
115
|
+
{ status: 502 }
|
|
116
|
+
);
|
|
117
|
+
}
|
|
104
118
|
const hook = deployHook ?? process.env.CANCIA_DEPLOY_HOOK;
|
|
105
119
|
const result = await firePublish(hook, hookOptions);
|
|
106
120
|
if (result.ok) {
|
|
@@ -110,7 +124,9 @@ function makePublishRoute(deployHook, secret, hookOptions) {
|
|
|
110
124
|
}
|
|
111
125
|
if (result.kind === "no-hook") {
|
|
112
126
|
return new Response(
|
|
113
|
-
JSON.stringify({
|
|
127
|
+
JSON.stringify({
|
|
128
|
+
error: "No publish mechanism configured. Set a publish repo + CANCIA_GITHUB_TOKEN, or CANCIA_DEPLOY_HOOK."
|
|
129
|
+
}),
|
|
114
130
|
{ status: 503 }
|
|
115
131
|
);
|
|
116
132
|
}
|
|
@@ -224,6 +240,7 @@ function canciaIntegration(opts = {}) {
|
|
|
224
240
|
let resolvedRootPath = "";
|
|
225
241
|
let resolvedUploadHandler;
|
|
226
242
|
let resolvedDeployHook;
|
|
243
|
+
let resolvedPublishRepo;
|
|
227
244
|
let resolvedLocales = ["en"];
|
|
228
245
|
let bakedConfig = null;
|
|
229
246
|
return {
|
|
@@ -255,6 +272,8 @@ function canciaIntegration(opts = {}) {
|
|
|
255
272
|
\x1B[32mcancia\x1B[0m No CANCIA_TOKEN found \u2014 generated one for you.`);
|
|
256
273
|
}
|
|
257
274
|
const hasDeployHook = !!(opts.deployHook ?? env.CANCIA_DEPLOY_HOOK ?? process.env.CANCIA_DEPLOY_HOOK);
|
|
275
|
+
resolvedPublishRepo = opts.publish?.github?.repo ?? opts.git?.repo;
|
|
276
|
+
const canPublish = hasDeployHook || !!resolvedPublishRepo;
|
|
258
277
|
let storageDescriptor;
|
|
259
278
|
if (opts.git) {
|
|
260
279
|
storageDescriptor = {
|
|
@@ -289,9 +308,10 @@ function canciaIntegration(opts = {}) {
|
|
|
289
308
|
locales: resolvedLocales,
|
|
290
309
|
maxUploadMB: opts.maxUploadMB ?? 10,
|
|
291
310
|
public: opts.public ?? false,
|
|
292
|
-
|
|
311
|
+
canPublish,
|
|
293
312
|
deployHookMethod: opts.deployHookMethod,
|
|
294
313
|
deployHookHeaders: opts.deployHookHeaders,
|
|
314
|
+
publishRepo: resolvedPublishRepo,
|
|
295
315
|
storage: storageDescriptor,
|
|
296
316
|
r2: r2Baked
|
|
297
317
|
};
|
|
@@ -305,7 +325,7 @@ function canciaIntegration(opts = {}) {
|
|
|
305
325
|
languages,
|
|
306
326
|
page: "unknown",
|
|
307
327
|
public: opts.public ?? false,
|
|
308
|
-
|
|
328
|
+
canPublish
|
|
309
329
|
})};`
|
|
310
330
|
);
|
|
311
331
|
injectScript("page", `import "@cancia/toolbar";`);
|
|
@@ -352,6 +372,7 @@ function canciaIntegration(opts = {}) {
|
|
|
352
372
|
const secret = resolvedToken;
|
|
353
373
|
const deployHook = opts.deployHook ?? process.env.CANCIA_DEPLOY_HOOK;
|
|
354
374
|
const deployHookToken = process.env.CANCIA_DEPLOY_HOOK_TOKEN || opts.deployHookToken;
|
|
375
|
+
const publishGithubToken = process.env.CANCIA_GITHUB_TOKEN?.trim() || void 0;
|
|
355
376
|
let uploadHandler;
|
|
356
377
|
if (opts.r2) {
|
|
357
378
|
const serverEnv = loadEnv(process.env.NODE_ENV ?? "development", resolvedRootPath, "");
|
|
@@ -385,15 +406,22 @@ function canciaIntegration(opts = {}) {
|
|
|
385
406
|
deployHookToken,
|
|
386
407
|
deployHookMethod: opts.deployHookMethod,
|
|
387
408
|
deployHookHeaders: opts.deployHookHeaders,
|
|
409
|
+
publishRepo: resolvedPublishRepo,
|
|
410
|
+
publishGithubToken,
|
|
388
411
|
maxUploadMB: opts.maxUploadMB ?? 10
|
|
389
412
|
});
|
|
390
413
|
const contentRoutes = makeContentRoute(storage, routeSecret);
|
|
391
414
|
const uploadRoute = makeUploadRoute(uploadHandler, routeSecret, opts.maxUploadMB);
|
|
392
|
-
const publishRoute = makePublishRoute(
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
415
|
+
const publishRoute = makePublishRoute(
|
|
416
|
+
deployHook,
|
|
417
|
+
routeSecret,
|
|
418
|
+
{
|
|
419
|
+
token: deployHookToken,
|
|
420
|
+
method: opts.deployHookMethod,
|
|
421
|
+
headers: opts.deployHookHeaders
|
|
422
|
+
},
|
|
423
|
+
{ repo: resolvedPublishRepo, token: publishGithubToken }
|
|
424
|
+
);
|
|
397
425
|
const authRoute = makeAuthRoute(secret);
|
|
398
426
|
const listsRoutes = makeListsRoutes({
|
|
399
427
|
storageV2,
|
|
@@ -526,6 +554,7 @@ function canciaIntegration(opts = {}) {
|
|
|
526
554
|
}
|
|
527
555
|
const deployHook = opts.deployHook ?? process.env.CANCIA_DEPLOY_HOOK;
|
|
528
556
|
const deployHookToken = process.env.CANCIA_DEPLOY_HOOK_TOKEN || opts.deployHookToken;
|
|
557
|
+
const publishGithubToken = process.env.CANCIA_GITHUB_TOKEN?.trim() || void 0;
|
|
529
558
|
const storageV2 = opts.storageV2 === null ? void 0 : opts.storageV2 ?? buildDefaultV2Store(opts, resolvedRootPath);
|
|
530
559
|
setCanciaRuntime({
|
|
531
560
|
storage,
|
|
@@ -540,6 +569,8 @@ function canciaIntegration(opts = {}) {
|
|
|
540
569
|
deployHookToken,
|
|
541
570
|
deployHookMethod: opts.deployHookMethod,
|
|
542
571
|
deployHookHeaders: opts.deployHookHeaders,
|
|
572
|
+
publishRepo: resolvedPublishRepo,
|
|
573
|
+
publishGithubToken,
|
|
543
574
|
maxUploadMB: opts.maxUploadMB ?? 10
|
|
544
575
|
});
|
|
545
576
|
}
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { C as CanciaStorage, a as CanciaStorageV2 } from './types-BMlLS-OS.js';
|
|
2
2
|
import { U as UploadHandler } from './upload-DwCGjXbz.js';
|
|
3
|
-
import { G as GitHubCommitter } from './github-client-
|
|
3
|
+
import { G as GitHubCommitter } from './github-client-Db0pIrqE.js';
|
|
4
4
|
|
|
5
5
|
interface CanciaRuntime {
|
|
6
6
|
/** v1 KV-only storage. Kept for the existing /content endpoint. */
|
|
@@ -37,6 +37,18 @@ interface CanciaRuntime {
|
|
|
37
37
|
deployHookMethod: string | undefined;
|
|
38
38
|
/** Optional extra headers merged into the hook request. Non-secret, baked. */
|
|
39
39
|
deployHookHeaders: Record<string, string> | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* "owner/name" of the GitHub repo whose `cancia-publish` workflow rebuilds
|
|
42
|
+
* the site (031). Non-secret, baked. When set (with CANCIA_GITHUB_TOKEN in
|
|
43
|
+
* env) the publish route fires a repository_dispatch instead of the raw
|
|
44
|
+
* deploy hook — the host-independent standard publish path.
|
|
45
|
+
*/
|
|
46
|
+
publishRepo?: string | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* GitHub PAT (contents:write) used to fire the repository_dispatch. SECRET —
|
|
49
|
+
* read from CANCIA_GITHUB_TOKEN at runtime, never baked into dist/.
|
|
50
|
+
*/
|
|
51
|
+
publishGithubToken?: string | undefined;
|
|
40
52
|
maxUploadMB: number;
|
|
41
53
|
}
|
|
42
54
|
/** Describes which storage adapter to build lazily in the server process. */
|
|
@@ -69,12 +81,22 @@ interface BakedConfig {
|
|
|
69
81
|
maxUploadMB: number;
|
|
70
82
|
/** When true, endpoints skip auth (public demos). Baked, never a secret. */
|
|
71
83
|
public: boolean;
|
|
72
|
-
/**
|
|
73
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Whether the site can publish — a deploy hook (030) OR a repository_dispatch
|
|
86
|
+
* repo (031) is configured. Client-inject flag for the toolbar's Publish
|
|
87
|
+
* button; the actual publish credentials are read from env at request time.
|
|
88
|
+
*/
|
|
89
|
+
canPublish: boolean;
|
|
74
90
|
/** Optional deploy-hook HTTP method (default POST). Non-secret. */
|
|
75
91
|
deployHookMethod: string | undefined;
|
|
76
92
|
/** Optional deploy-hook extra headers. Non-secret (the token stays in env). */
|
|
77
93
|
deployHookHeaders: Record<string, string> | undefined;
|
|
94
|
+
/**
|
|
95
|
+
* "owner/name" of the GitHub repo for repository_dispatch publish (031).
|
|
96
|
+
* Non-secret, baked. The token (CANCIA_GITHUB_TOKEN) is read from env at
|
|
97
|
+
* runtime. Absent = no dispatch path (falls through to the deploy hook).
|
|
98
|
+
*/
|
|
99
|
+
publishRepo?: string | undefined;
|
|
78
100
|
/** How to build the v2 storage adapter at runtime. Absent = no v2 storage. */
|
|
79
101
|
storage: BakedStorageDescriptor | undefined;
|
|
80
102
|
/** When set, build an R2 upload handler; secrets come from env at runtime. */
|
package/dist/runtime.js
CHANGED
|
@@ -2,8 +2,9 @@ import {
|
|
|
2
2
|
getCanciaRuntime,
|
|
3
3
|
setBakedConfig,
|
|
4
4
|
setCanciaRuntime
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-U46HCJN3.js";
|
|
6
|
+
import "./chunk-5AUIPW2I.js";
|
|
7
|
+
import "./chunk-U7V53JX7.js";
|
|
7
8
|
import "./chunk-L2VKQJPY.js";
|
|
8
9
|
import "./chunk-7IA5B5CF.js";
|
|
9
10
|
import "./chunk-5IPHDIC6.js";
|
package/dist/storage/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { R as Rev } from '../types-BMlLS-OS.js';
|
|
2
2
|
export { b as CanciaKVStore, c as CanciaListStore, d as CanciaPageStore, C as CanciaStorage, a as CanciaStorageV2, L as ListEntry, P as PageMeta, e as PageRecord, f as PageSEO, g as RevConflictError } from '../types-BMlLS-OS.js';
|
|
3
|
-
export { G as GitBackedContentPaths, a as GitBackedControls, b as GitBackedOptions, c as GitBackedStorage, S as SqliteV2Options, d as closeSqliteAdapterV2, e as createGitBackedAdapter, f as createJsonFileAdapter, g as createJsonFileAdapterV2, h as createSQLiteAdapter, i as createSqliteAdapterV2 } from '../git-backed-
|
|
4
|
-
export { C as CommitFile, a as GitHubClient, b as GitHubClientOptions, G as GitHubCommitter, c as createGitHubClient } from '../github-client-
|
|
3
|
+
export { G as GitBackedContentPaths, a as GitBackedControls, b as GitBackedOptions, c as GitBackedStorage, S as SqliteV2Options, d as closeSqliteAdapterV2, e as createGitBackedAdapter, f as createJsonFileAdapter, g as createJsonFileAdapterV2, h as createSQLiteAdapter, i as createSqliteAdapterV2 } from '../git-backed-DtiH52EI.js';
|
|
4
|
+
export { C as CommitFile, a as GitHubClient, b as GitHubClientOptions, G as GitHubCommitter, c as createGitHubClient } from '../github-client-Db0pIrqE.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Canonical JSON: keys sorted alphabetically at every level. Two values that
|
package/dist/storage/index.js
CHANGED
|
@@ -4,9 +4,11 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
closeSqliteAdapterV2,
|
|
6
6
|
createGitBackedAdapter,
|
|
7
|
-
createGitHubClient,
|
|
8
7
|
createSqliteAdapterV2
|
|
9
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-5AUIPW2I.js";
|
|
9
|
+
import {
|
|
10
|
+
createGitHubClient
|
|
11
|
+
} from "../chunk-U7V53JX7.js";
|
|
10
12
|
import {
|
|
11
13
|
canonicalize,
|
|
12
14
|
createJsonFileAdapter,
|
package/package.json
CHANGED
package/dist/chunk-PIDFNJME.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// src/publish-hook.ts
|
|
2
|
-
async function firePublish(hook, opts = {}) {
|
|
3
|
-
if (!hook) return { ok: false, kind: "no-hook" };
|
|
4
|
-
const headers = { ...opts.headers ?? {} };
|
|
5
|
-
if (opts.token) headers.Authorization = `Bearer ${opts.token}`;
|
|
6
|
-
const init = { method: opts.method ?? "POST" };
|
|
7
|
-
if (Object.keys(headers).length > 0) init.headers = headers;
|
|
8
|
-
try {
|
|
9
|
-
const res = await fetch(hook, init);
|
|
10
|
-
if (!res.ok) return { ok: false, kind: "bad-response", status: res.status };
|
|
11
|
-
return { ok: true };
|
|
12
|
-
} catch {
|
|
13
|
-
return { ok: false, kind: "unreachable" };
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export {
|
|
18
|
-
firePublish
|
|
19
|
-
};
|