@cancia/astro 0.10.0 → 0.12.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-4PYVIIOO.js +25 -0
- package/dist/{chunk-DIV2FFYX.js → chunk-5R3I5RLT.js} +15 -8
- package/dist/chunk-6GUOGDWO.js +172 -0
- package/dist/chunk-DEI3FBNY.js +65 -0
- package/dist/{chunk-ONKCUTPU.js → chunk-DOFLZ7KG.js} +6 -7
- package/dist/{chunk-GJIX7MWC.js → chunk-ECPYHIAW.js} +9 -1
- package/dist/{chunk-YGJ3JHXF.js → chunk-QQEUCOXQ.js} +11 -0
- package/dist/{chunk-NVXNJ4YC.js → chunk-R7VA3Z5N.js} +53 -11
- package/dist/{chunk-5IPHDIC6.js → chunk-RHFTQW4W.js} +10 -1
- package/dist/{chunk-2NVZWZPE.js → chunk-S5YLB6P3.js} +310 -7
- package/dist/{chunk-LJSSPOSW.js → chunk-VF3EXXEM.js} +139 -16
- package/dist/chunk-XINNLVZP.js +183 -0
- package/dist/chunk-XZCXWILA.js +145 -0
- package/dist/content.js +3 -3
- package/dist/{upload-DwCGjXbz.d.ts → email-BRK4WAO2.d.ts} +9 -1
- package/dist/endpoints/auth-callback.d.ts +1 -0
- package/dist/endpoints/auth-callback.js +9 -0
- package/dist/endpoints/auth-logout.d.ts +1 -0
- package/dist/endpoints/auth-logout.js +9 -0
- package/dist/endpoints/auth-request.d.ts +18 -0
- package/dist/endpoints/auth-request.js +9 -0
- package/dist/endpoints/auth-session.d.ts +1 -0
- package/dist/endpoints/auth-session.js +9 -0
- package/dist/endpoints/auth.js +10 -2
- package/dist/endpoints/content.js +12 -12
- package/dist/endpoints/lists.js +4 -1
- package/dist/endpoints/publish.js +8 -6
- package/dist/endpoints/schemas.js +6 -3
- package/dist/endpoints/upload.js +11 -10
- package/dist/{git-backed-DtiH52EI.d.ts → git-backed-CwTFfUS4.d.ts} +2 -2
- package/dist/{github-client-Db0pIrqE.d.ts → github-client-Db5EPcSL.d.ts} +1 -1
- package/dist/index.d.ts +32 -6
- package/dist/index.js +125 -35
- package/dist/loader/index.d.ts +34 -4
- package/dist/loader/index.js +3 -3
- package/dist/runtime.d.ts +69 -3
- package/dist/runtime.js +6 -4
- package/dist/schema/index.d.ts +14 -1
- package/dist/schema/index.js +1 -1
- package/dist/storage/index.d.ts +4 -4
- package/dist/storage/index.js +5 -5
- package/dist/{types-BMlLS-OS.d.ts → types-CKzfz5iu.d.ts} +1 -1
- package/package.json +1 -1
- package/dist/chunk-MXVOJRAM.js +0 -504
package/dist/index.js
CHANGED
|
@@ -1,40 +1,54 @@
|
|
|
1
1
|
import {
|
|
2
2
|
makeSchemasRoute
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-DOFLZ7KG.js";
|
|
4
|
+
import {
|
|
5
|
+
handleLoginCallback,
|
|
6
|
+
handleLoginRequest,
|
|
7
|
+
handleLogout,
|
|
8
|
+
handleSessionCheck,
|
|
9
|
+
resolveLoginEmailSender
|
|
10
|
+
} from "./chunk-XZCXWILA.js";
|
|
4
11
|
import {
|
|
5
12
|
runPublish
|
|
6
13
|
} from "./chunk-5RCLBWRH.js";
|
|
7
14
|
import {
|
|
8
15
|
createRateLimiter,
|
|
9
16
|
runAuth
|
|
10
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-ECPYHIAW.js";
|
|
11
18
|
import {
|
|
12
19
|
makeListsRoutes
|
|
13
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-5R3I5RLT.js";
|
|
14
21
|
import {
|
|
22
|
+
createSqliteAuthStore,
|
|
15
23
|
makeLocalUploadHandler,
|
|
16
24
|
makeR2UploadHandler,
|
|
17
25
|
makeUploadRoute,
|
|
18
26
|
setCanciaRuntime
|
|
19
|
-
} from "./chunk-
|
|
20
|
-
import "./chunk-
|
|
27
|
+
} from "./chunk-VF3EXXEM.js";
|
|
28
|
+
import "./chunk-RHFTQW4W.js";
|
|
29
|
+
import {
|
|
30
|
+
guardRequest
|
|
31
|
+
} from "./chunk-4PYVIIOO.js";
|
|
32
|
+
import {
|
|
33
|
+
authorizeRequest
|
|
34
|
+
} from "./chunk-6GUOGDWO.js";
|
|
21
35
|
import {
|
|
22
36
|
createSQLiteAdapter
|
|
23
37
|
} from "./chunk-CJDIVWO3.js";
|
|
24
38
|
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
createSqliteAdapterV2
|
|
28
|
-
} from "./chunk-MXVOJRAM.js";
|
|
39
|
+
createGitBackedAdapter
|
|
40
|
+
} from "./chunk-XINNLVZP.js";
|
|
29
41
|
import "./chunk-U7V53JX7.js";
|
|
30
42
|
import "./chunk-VL6FO446.js";
|
|
31
43
|
import {
|
|
32
44
|
canciaLoader
|
|
33
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-R7VA3Z5N.js";
|
|
34
46
|
import {
|
|
47
|
+
closeSqliteAdapterV2,
|
|
35
48
|
createJsonFileAdapter,
|
|
36
|
-
createJsonFileAdapterV2
|
|
37
|
-
|
|
49
|
+
createJsonFileAdapterV2,
|
|
50
|
+
createSqliteAdapterV2
|
|
51
|
+
} from "./chunk-S5YLB6P3.js";
|
|
38
52
|
import {
|
|
39
53
|
RevConflictError
|
|
40
54
|
} from "./chunk-7IA5B5CF.js";
|
|
@@ -43,7 +57,7 @@ import {
|
|
|
43
57
|
defineList,
|
|
44
58
|
describeList,
|
|
45
59
|
z
|
|
46
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-QQEUCOXQ.js";
|
|
47
61
|
import "./chunk-KHCFVVYV.js";
|
|
48
62
|
|
|
49
63
|
// src/integration.ts
|
|
@@ -53,16 +67,13 @@ import { readFile } from "fs/promises";
|
|
|
53
67
|
import { isAbsolute, join as join2 } from "path";
|
|
54
68
|
|
|
55
69
|
// src/routes/content.ts
|
|
56
|
-
function makeContentRoute(storage, secret) {
|
|
57
|
-
function checkAuth(req) {
|
|
58
|
-
|
|
59
|
-
const token = req.headers.get("Authorization")?.replace("Bearer ", "").trim();
|
|
60
|
-
if (token !== secret) return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401 });
|
|
61
|
-
return null;
|
|
70
|
+
function makeContentRoute(storage, secret, auth) {
|
|
71
|
+
async function checkAuth(req) {
|
|
72
|
+
return (await guardRequest(req, { secret, auth })).deny;
|
|
62
73
|
}
|
|
63
74
|
return {
|
|
64
75
|
GET: async ({ request }) => {
|
|
65
|
-
const deny = checkAuth(request);
|
|
76
|
+
const deny = await checkAuth(request);
|
|
66
77
|
if (deny) return deny;
|
|
67
78
|
const site = new URL(request.url).searchParams.get("site");
|
|
68
79
|
if (!site) return new Response(JSON.stringify({ error: "Missing ?site=" }), { status: 400 });
|
|
@@ -72,7 +83,7 @@ function makeContentRoute(storage, secret) {
|
|
|
72
83
|
});
|
|
73
84
|
},
|
|
74
85
|
POST: async ({ request }) => {
|
|
75
|
-
const deny = checkAuth(request);
|
|
86
|
+
const deny = await checkAuth(request);
|
|
76
87
|
if (deny) return deny;
|
|
77
88
|
const body = await request.json().catch(() => null);
|
|
78
89
|
if (!body?.site || !body?.key || !body?.lang || body?.value === void 0) {
|
|
@@ -84,7 +95,7 @@ function makeContentRoute(storage, secret) {
|
|
|
84
95
|
});
|
|
85
96
|
},
|
|
86
97
|
DELETE: async ({ request }) => {
|
|
87
|
-
const deny = checkAuth(request);
|
|
98
|
+
const deny = await checkAuth(request);
|
|
88
99
|
if (deny) return deny;
|
|
89
100
|
const body = await request.json().catch(() => null);
|
|
90
101
|
if (!body?.site || !body?.key || !body?.lang) {
|
|
@@ -99,14 +110,10 @@ function makeContentRoute(storage, secret) {
|
|
|
99
110
|
}
|
|
100
111
|
|
|
101
112
|
// src/routes/publish.ts
|
|
102
|
-
function makePublishRoute(deployHook, secret, hookOptions, dispatch, mode) {
|
|
113
|
+
function makePublishRoute(deployHook, secret, hookOptions, dispatch, mode, auth) {
|
|
103
114
|
return async ({ request }) => {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (token !== secret) {
|
|
107
|
-
return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401 });
|
|
108
|
-
}
|
|
109
|
-
}
|
|
115
|
+
const deny = (await guardRequest(request, { secret, auth })).deny;
|
|
116
|
+
if (deny) return deny;
|
|
110
117
|
const hook = deployHook ?? process.env.CANCIA_DEPLOY_HOOK;
|
|
111
118
|
return runPublish({
|
|
112
119
|
mode,
|
|
@@ -357,6 +364,34 @@ ${code}`;
|
|
|
357
364
|
${code.slice(at)}`;
|
|
358
365
|
}
|
|
359
366
|
|
|
367
|
+
// src/routes/auth-magic.ts
|
|
368
|
+
function makeMagicAuthRoutes(ctx) {
|
|
369
|
+
const limiter = createRateLimiter();
|
|
370
|
+
const shared = { store: ctx.store, site: ctx.site, sendEmail: ctx.sendEmail, limiter };
|
|
371
|
+
return {
|
|
372
|
+
async request(req) {
|
|
373
|
+
return handleLoginRequest(req, shared, new URL(req.url).origin);
|
|
374
|
+
},
|
|
375
|
+
async callback(req) {
|
|
376
|
+
return handleLoginCallback(req, shared);
|
|
377
|
+
},
|
|
378
|
+
async logout(req) {
|
|
379
|
+
return handleLogout(req, shared);
|
|
380
|
+
},
|
|
381
|
+
async session(req) {
|
|
382
|
+
return handleSessionCheck(
|
|
383
|
+
req,
|
|
384
|
+
shared,
|
|
385
|
+
() => authorizeRequest({
|
|
386
|
+
request: req,
|
|
387
|
+
secret: ctx.secret,
|
|
388
|
+
auth: { store: ctx.store, site: ctx.site }
|
|
389
|
+
})
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
360
395
|
// src/integration.ts
|
|
361
396
|
function resolvePublish(opts, hasDeployHookEnv) {
|
|
362
397
|
const publish = opts.publish;
|
|
@@ -386,6 +421,15 @@ function buildDefaultV2Store(opts, projectRoot) {
|
|
|
386
421
|
}
|
|
387
422
|
return createJsonFileAdapterV2({ projectRoot });
|
|
388
423
|
}
|
|
424
|
+
function buildAuthContext(opts, projectRoot) {
|
|
425
|
+
const editors = opts.editors?.map((e) => e.trim().toLowerCase()).filter(Boolean) ?? [];
|
|
426
|
+
if (editors.length === 0) return void 0;
|
|
427
|
+
const dbPath = opts.db?.kind === "sqlite" && opts.db.path ? isAbsolute(opts.db.path) ? opts.db.path : join2(projectRoot, opts.db.path) : join2(projectRoot, "cancia.db");
|
|
428
|
+
const store = createSqliteAuthStore({ dbPath });
|
|
429
|
+
const site = opts.site ?? "default";
|
|
430
|
+
void store.seedEditors(site, editors);
|
|
431
|
+
return { store, site };
|
|
432
|
+
}
|
|
389
433
|
function canciaIntegration(opts = {}) {
|
|
390
434
|
let storage;
|
|
391
435
|
let resolvedToken = "";
|
|
@@ -459,8 +503,12 @@ function canciaIntegration(opts = {}) {
|
|
|
459
503
|
prefix: r2Opts.prefix
|
|
460
504
|
};
|
|
461
505
|
}
|
|
506
|
+
const bakedEditors = opts.editors?.map((e) => e.trim().toLowerCase()).filter(Boolean) ?? [];
|
|
462
507
|
bakedConfig = {
|
|
463
508
|
projectRoot: resolvedRootPath,
|
|
509
|
+
site,
|
|
510
|
+
editors: bakedEditors,
|
|
511
|
+
authDbPath: opts.db?.kind === "sqlite" && opts.db.path ? opts.db.path : "cancia.db",
|
|
464
512
|
schemasPath: opts.schemasPath,
|
|
465
513
|
defaultLocale: resolvedLocales[0],
|
|
466
514
|
locales: resolvedLocales,
|
|
@@ -485,7 +533,12 @@ function canciaIntegration(opts = {}) {
|
|
|
485
533
|
languages,
|
|
486
534
|
page: "unknown",
|
|
487
535
|
public: opts.public ?? false,
|
|
488
|
-
canPublish
|
|
536
|
+
canPublish,
|
|
537
|
+
// Whether to offer the email login box. The ADDRESSES are never
|
|
538
|
+
// sent to the browser — publishing a client's staff list on every
|
|
539
|
+
// page would be a disclosure, and the server checks the allow-list
|
|
540
|
+
// anyway. The toolbar only needs to know the flow exists.
|
|
541
|
+
magicLink: bakedEditors.length > 0
|
|
489
542
|
})};`
|
|
490
543
|
);
|
|
491
544
|
injectScript("page", `import "@cancia/toolbar";`);
|
|
@@ -497,6 +550,10 @@ function canciaIntegration(opts = {}) {
|
|
|
497
550
|
injectRoute({ pattern: "/api/cancia/publish", entrypoint: endpointPath("publish"), prerender: false });
|
|
498
551
|
injectRoute({ pattern: "/api/cancia/auth", entrypoint: endpointPath("auth"), prerender: false });
|
|
499
552
|
injectRoute({ pattern: "/api/cancia/health", entrypoint: endpointPath("health"), prerender: false });
|
|
553
|
+
injectRoute({ pattern: "/api/cancia/auth/request", entrypoint: endpointPath("auth-request"), prerender: false });
|
|
554
|
+
injectRoute({ pattern: "/api/cancia/auth/callback", entrypoint: endpointPath("auth-callback"), prerender: false });
|
|
555
|
+
injectRoute({ pattern: "/api/cancia/auth/logout", entrypoint: endpointPath("auth-logout"), prerender: false });
|
|
556
|
+
injectRoute({ pattern: "/api/cancia/auth/session", entrypoint: endpointPath("auth-session"), prerender: false });
|
|
500
557
|
injectRoute({ pattern: "/api/cancia/schemas", entrypoint: endpointPath("schemas"), prerender: false });
|
|
501
558
|
injectRoute({ pattern: "/api/cancia/lists/[listName]", entrypoint: endpointPath("lists"), prerender: false });
|
|
502
559
|
injectRoute({ pattern: "/api/cancia/lists/[listName]/[id]", entrypoint: endpointPath("lists"), prerender: false });
|
|
@@ -616,6 +673,9 @@ function canciaIntegration(opts = {}) {
|
|
|
616
673
|
defaultLocale: resolvedLocales[0],
|
|
617
674
|
locales: resolvedLocales,
|
|
618
675
|
secret: routeSecret,
|
|
676
|
+
site: opts.site ?? "default",
|
|
677
|
+
auth: buildAuthContext(opts, resolvedRootPath),
|
|
678
|
+
sendLoginEmail: resolveLoginEmailSender(opts.sendLoginEmail),
|
|
619
679
|
uploadHandler,
|
|
620
680
|
deployHook,
|
|
621
681
|
deployHookToken,
|
|
@@ -627,8 +687,9 @@ function canciaIntegration(opts = {}) {
|
|
|
627
687
|
redeployHook: resolvedRedeployHook,
|
|
628
688
|
maxUploadMB: opts.maxUploadMB ?? 10
|
|
629
689
|
});
|
|
630
|
-
const
|
|
631
|
-
const
|
|
690
|
+
const devAuthCtx = buildAuthContext(opts, resolvedRootPath);
|
|
691
|
+
const contentRoutes = makeContentRoute(storage, routeSecret, devAuthCtx);
|
|
692
|
+
const uploadRoute = makeUploadRoute(uploadHandler, routeSecret, opts.maxUploadMB, devAuthCtx);
|
|
632
693
|
const publishRoute = makePublishRoute(
|
|
633
694
|
deployHook,
|
|
634
695
|
routeSecret,
|
|
@@ -638,20 +699,33 @@ function canciaIntegration(opts = {}) {
|
|
|
638
699
|
headers: opts.deployHookHeaders
|
|
639
700
|
},
|
|
640
701
|
{ repo: resolvedPublishRepo, token: publishGithubToken },
|
|
641
|
-
resolvedPublishMode
|
|
702
|
+
resolvedPublishMode,
|
|
703
|
+
devAuthCtx
|
|
642
704
|
);
|
|
643
705
|
const authRoute = makeAuthRoute(secret);
|
|
706
|
+
const magicRoutes = devAuthCtx ? makeMagicAuthRoutes({
|
|
707
|
+
store: devAuthCtx.store,
|
|
708
|
+
site: devAuthCtx.site,
|
|
709
|
+
sendEmail: resolveLoginEmailSender(opts.sendLoginEmail),
|
|
710
|
+
secret: routeSecret
|
|
711
|
+
}) : void 0;
|
|
712
|
+
const magicNotConfigured = () => new Response(
|
|
713
|
+
JSON.stringify({ error: "Magic-link login is not configured for this site." }),
|
|
714
|
+
{ status: 404, headers: { "Content-Type": "application/json" } }
|
|
715
|
+
);
|
|
644
716
|
const listsRoutes = makeListsRoutes({
|
|
645
717
|
storageV2,
|
|
646
718
|
projectRoot: resolvedRootPath,
|
|
647
719
|
schemasPath: opts.schemasPath,
|
|
648
720
|
secret: routeSecret,
|
|
721
|
+
auth: devAuthCtx,
|
|
649
722
|
defaultLocale: resolvedLocales[0]
|
|
650
723
|
});
|
|
651
724
|
const schemasRoute = makeSchemasRoute({
|
|
652
725
|
projectRoot: resolvedRootPath,
|
|
653
726
|
schemasPath: opts.schemasPath,
|
|
654
|
-
secret: routeSecret
|
|
727
|
+
secret: routeSecret,
|
|
728
|
+
auth: devAuthCtx
|
|
655
729
|
});
|
|
656
730
|
server.middlewares.use(async (req, res, next) => {
|
|
657
731
|
const url = req.url ?? "";
|
|
@@ -716,8 +790,21 @@ function canciaIntegration(opts = {}) {
|
|
|
716
790
|
response = await publishRoute(ctx);
|
|
717
791
|
} else if (url === "/api/cancia/health") {
|
|
718
792
|
response = new Response(JSON.stringify({ ok: true }), { status: 200 });
|
|
793
|
+
} else if (url === "/api/cancia/auth/request" && method === "POST") {
|
|
794
|
+
response = magicRoutes ? await magicRoutes.request(webReq) : magicNotConfigured();
|
|
795
|
+
} else if (url === "/api/cancia/auth/callback" && method === "POST") {
|
|
796
|
+
response = magicRoutes ? await magicRoutes.callback(webReq) : magicNotConfigured();
|
|
797
|
+
} else if (url === "/api/cancia/auth/logout" && method === "POST") {
|
|
798
|
+
response = magicRoutes ? await magicRoutes.logout(webReq) : magicNotConfigured();
|
|
799
|
+
} else if (url === "/api/cancia/auth/session" && method === "GET") {
|
|
800
|
+
response = magicRoutes ? await magicRoutes.session(webReq) : magicNotConfigured();
|
|
719
801
|
} else if (url === "/api/cancia/auth" && method === "POST") {
|
|
720
|
-
response =
|
|
802
|
+
response = magicRoutes ? new Response(
|
|
803
|
+
JSON.stringify({
|
|
804
|
+
error: "This site uses email login. Open the site and request a link instead."
|
|
805
|
+
}),
|
|
806
|
+
{ status: 403, headers: { "Content-Type": "application/json" } }
|
|
807
|
+
) : await authRoute(webReq);
|
|
721
808
|
}
|
|
722
809
|
if (!response) return next();
|
|
723
810
|
res.statusCode = response.status;
|
|
@@ -782,6 +869,9 @@ function canciaIntegration(opts = {}) {
|
|
|
782
869
|
defaultLocale: resolvedLocales[0],
|
|
783
870
|
locales: resolvedLocales,
|
|
784
871
|
secret: routeSecret,
|
|
872
|
+
site: opts.site ?? "default",
|
|
873
|
+
auth: buildAuthContext(opts, resolvedRootPath),
|
|
874
|
+
sendLoginEmail: resolveLoginEmailSender(opts.sendLoginEmail),
|
|
785
875
|
uploadHandler,
|
|
786
876
|
deployHook,
|
|
787
877
|
deployHookToken,
|
package/dist/loader/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Loader } from 'astro/loaders';
|
|
2
|
-
import {
|
|
2
|
+
import { C as CanciaStorageV2 } from '../types-CKzfz5iu.js';
|
|
3
3
|
import { ListSchema } from '../schema/index.js';
|
|
4
4
|
import 'zod';
|
|
5
5
|
import '../portable-text-D74aIuHn.js';
|
|
@@ -13,9 +13,39 @@ interface CanciaLoaderOptions {
|
|
|
13
13
|
*/
|
|
14
14
|
site: string;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* Where this site's content lives.
|
|
17
|
+
*
|
|
18
|
+
* USUALLY UNNECESSARY. A `cancia.db` at the project root — where the
|
|
19
|
+
* integration's `db: { kind: "sqlite" }` puts it — is detected automatically,
|
|
20
|
+
* so a fresh project needs nothing here.
|
|
21
|
+
*
|
|
22
|
+
* Set it when the site uses a CUSTOM `db.path` (autodetection deliberately
|
|
23
|
+
* checks only the default location and never searches), or to force the
|
|
24
|
+
* JSON-file backend on a project that also has a stray .db.
|
|
25
|
+
*
|
|
26
|
+
* When set, MIRROR THE INTEGRATION'S `db` OPTION.
|
|
27
|
+
*
|
|
28
|
+
* cancia({ db: { kind: "sqlite" } }) // astro.config.mjs
|
|
29
|
+
* canciaLoader({ …, db: { kind: "sqlite" } }) // content.config.ts
|
|
30
|
+
*
|
|
31
|
+
* It has to be repeated because `content.config.ts` is evaluated by Astro
|
|
32
|
+
* BEFORE the integration's hooks run, so the loader cannot read the baked
|
|
33
|
+
* config the way the SSR runtime does.
|
|
34
|
+
*
|
|
35
|
+
* Omitting it on a sqlite-backed site is the bug this option exists to make
|
|
36
|
+
* visible: the loader used to hardcode the JSON-file adapter, so it read an
|
|
37
|
+
* empty directory and `getCollection()` returned nothing — silently, because
|
|
38
|
+
* "no entries" is a legitimate state. A page using the common
|
|
39
|
+
* `entries.length > 0 ? entries : seeded` fallback then rendered its
|
|
40
|
+
* hardcoded seed array and looked entirely correct.
|
|
41
|
+
*/
|
|
42
|
+
db?: {
|
|
43
|
+
kind: "sqlite" | "json-file";
|
|
44
|
+
path?: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Custom storage adapter. Wins over `db` — for tests, monorepos, or a
|
|
48
|
+
* backend Cancia does not know about.
|
|
19
49
|
*/
|
|
20
50
|
storage?: CanciaStorageV2;
|
|
21
51
|
/**
|
package/dist/loader/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
canciaLoader
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-R7VA3Z5N.js";
|
|
4
|
+
import "../chunk-S5YLB6P3.js";
|
|
5
5
|
import "../chunk-7IA5B5CF.js";
|
|
6
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-QQEUCOXQ.js";
|
|
7
7
|
import "../chunk-KHCFVVYV.js";
|
|
8
8
|
export {
|
|
9
9
|
canciaLoader
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,6 +1,41 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { U as UploadHandler } from './
|
|
3
|
-
import {
|
|
1
|
+
import { d as CanciaStorage, C as CanciaStorageV2 } from './types-CKzfz5iu.js';
|
|
2
|
+
import { S as SendLoginEmail, U as UploadHandler } from './email-BRK4WAO2.js';
|
|
3
|
+
import { b as GitHubCommitter } from './github-client-Db5EPcSL.js';
|
|
4
|
+
|
|
5
|
+
/** A live browser session. `id` is the SHA-256 hash of the cookie value. */
|
|
6
|
+
interface SessionRecord {
|
|
7
|
+
/** Hash of the session secret — never the secret itself. */
|
|
8
|
+
id: string;
|
|
9
|
+
email: string;
|
|
10
|
+
createdAt: string;
|
|
11
|
+
expiresAt: string;
|
|
12
|
+
}
|
|
13
|
+
/** A pending, single-use login link. `hash` is the SHA-256 of the emailed token. */
|
|
14
|
+
interface LoginTokenRecord {
|
|
15
|
+
hash: string;
|
|
16
|
+
email: string;
|
|
17
|
+
expiresAt: string;
|
|
18
|
+
}
|
|
19
|
+
interface CanciaAuthStore {
|
|
20
|
+
/** Replace the allow-list with exactly these addresses. Called on boot. */
|
|
21
|
+
seedEditors(site: string, emails: string[]): Promise<void>;
|
|
22
|
+
/** Is this address allowed to sign in? */
|
|
23
|
+
isEditor(site: string, email: string): Promise<boolean>;
|
|
24
|
+
createLoginToken(site: string, rec: LoginTokenRecord): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Atomically redeem a login token: return it and delete it in one step, so
|
|
27
|
+
* two concurrent redemptions of the same link cannot both succeed.
|
|
28
|
+
* Returns null when the hash is unknown (already used, or never existed).
|
|
29
|
+
*/
|
|
30
|
+
consumeLoginToken(site: string, hash: string): Promise<LoginTokenRecord | null>;
|
|
31
|
+
createSession(site: string, rec: SessionRecord): Promise<void>;
|
|
32
|
+
getSession(site: string, id: string): Promise<SessionRecord | null>;
|
|
33
|
+
/** Push a session's expiry out — the sliding half of the window. */
|
|
34
|
+
touchSession(site: string, id: string, expiresAt: string): Promise<void>;
|
|
35
|
+
deleteSession(site: string, id: string): Promise<void>;
|
|
36
|
+
/** Drop expired tokens and sessions. Best-effort housekeeping. */
|
|
37
|
+
purgeExpired(site: string, now: string): Promise<void>;
|
|
38
|
+
}
|
|
4
39
|
|
|
5
40
|
/**
|
|
6
41
|
* The resolved publish mode (034). One of:
|
|
@@ -35,6 +70,23 @@ interface CanciaRuntime {
|
|
|
35
70
|
/** All configured locales, in order with defaultLocale first. */
|
|
36
71
|
locales: string[];
|
|
37
72
|
secret: string | undefined;
|
|
73
|
+
/** Site identifier — scopes auth rows, as it scopes content rows. */
|
|
74
|
+
site: string;
|
|
75
|
+
/**
|
|
76
|
+
* Magic-link auth (042). Undefined when the site has no `editors`, which is
|
|
77
|
+
* the pre-042 world where the bearer token is the only credential. Every
|
|
78
|
+
* caller reads `undefined` as "bearer only", so no separate flag is needed.
|
|
79
|
+
*/
|
|
80
|
+
auth: {
|
|
81
|
+
store: CanciaAuthStore;
|
|
82
|
+
site: string;
|
|
83
|
+
} | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* Custom login-email sender. Not baked (it is a function): supplied by the
|
|
86
|
+
* dev hook, and undefined in a fresh SSR process, where the sender is
|
|
87
|
+
* resolved from env instead.
|
|
88
|
+
*/
|
|
89
|
+
sendLoginEmail?: SendLoginEmail;
|
|
38
90
|
uploadHandler: UploadHandler;
|
|
39
91
|
deployHook: string | undefined;
|
|
40
92
|
/**
|
|
@@ -97,6 +149,19 @@ interface BakedR2Config {
|
|
|
97
149
|
}
|
|
98
150
|
interface BakedConfig {
|
|
99
151
|
projectRoot: string;
|
|
152
|
+
/** Site identifier. Non-secret. */
|
|
153
|
+
site?: string;
|
|
154
|
+
/**
|
|
155
|
+
* Addresses allowed to sign in (042). Non-secret — an allow-list is not a
|
|
156
|
+
* credential, and the client needs to know whether to show the login box.
|
|
157
|
+
*/
|
|
158
|
+
editors?: string[];
|
|
159
|
+
/**
|
|
160
|
+
* Where the auth tables live. Same file as the content DB; kept as its own
|
|
161
|
+
* field because a json-file content site still needs somewhere to put
|
|
162
|
+
* sessions, and that somewhere is a sqlite file.
|
|
163
|
+
*/
|
|
164
|
+
authDbPath?: string;
|
|
100
165
|
schemasPath: string | undefined;
|
|
101
166
|
defaultLocale: string;
|
|
102
167
|
locales: string[];
|
|
@@ -154,6 +219,7 @@ declare function setCanciaRuntime(runtime: CanciaRuntime): void;
|
|
|
154
219
|
* vitest's vi.resetModules() (its singletons would otherwise leak across cases).
|
|
155
220
|
*/
|
|
156
221
|
declare function __resetRuntimeForTests(): void;
|
|
222
|
+
|
|
157
223
|
declare function getCanciaRuntime(): CanciaRuntime;
|
|
158
224
|
|
|
159
225
|
export { type BakedConfig, type BakedR2Config, type BakedStorageDescriptor, type CanciaRuntime, type PublishMode, __resetRuntimeForTests, getCanciaRuntime, setBakedConfig, setCanciaRuntime };
|
package/dist/runtime.js
CHANGED
|
@@ -3,11 +3,13 @@ import {
|
|
|
3
3
|
getCanciaRuntime,
|
|
4
4
|
setBakedConfig,
|
|
5
5
|
setCanciaRuntime
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-VF3EXXEM.js";
|
|
7
|
+
import "./chunk-RHFTQW4W.js";
|
|
8
|
+
import "./chunk-4PYVIIOO.js";
|
|
9
|
+
import "./chunk-6GUOGDWO.js";
|
|
10
|
+
import "./chunk-XINNLVZP.js";
|
|
9
11
|
import "./chunk-U7V53JX7.js";
|
|
10
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-S5YLB6P3.js";
|
|
11
13
|
import "./chunk-7IA5B5CF.js";
|
|
12
14
|
export {
|
|
13
15
|
__resetRuntimeForTests,
|
package/dist/schema/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export { P as PT_DECORATORS, a as PT_LIST_ITEMS, b as PT_STYLES, c as PortableTe
|
|
|
16
16
|
* z.boolean() → "checkbox"
|
|
17
17
|
* z.enum([...]) → "select"
|
|
18
18
|
*/
|
|
19
|
-
type FieldWidget = "text" | "textarea" | "url" | "email" | "datetime" | "number" | "checkbox" | "select" | "image" | "slug" | "array" | "object" | "reference" | "richtext" | "link";
|
|
19
|
+
type FieldWidget = "text" | "textarea" | "url" | "email" | "datetime" | "number" | "checkbox" | "select" | "image" | "file" | "slug" | "array" | "object" | "reference" | "richtext" | "link";
|
|
20
20
|
/**
|
|
21
21
|
* Fields common to every widget's metadata. All optional.
|
|
22
22
|
*/
|
|
@@ -49,6 +49,8 @@ type FieldMeta = (FieldMetaBase & {
|
|
|
49
49
|
}) | (FieldMetaBase & {
|
|
50
50
|
widget: "image";
|
|
51
51
|
alt?: boolean;
|
|
52
|
+
}) | (FieldMetaBase & {
|
|
53
|
+
widget: "file";
|
|
52
54
|
}) | (FieldMetaBase & {
|
|
53
55
|
widget: "slug";
|
|
54
56
|
source?: string;
|
|
@@ -204,6 +206,17 @@ declare const defineField: {
|
|
|
204
206
|
image: (o?: FieldMetaBase & {
|
|
205
207
|
alt?: boolean;
|
|
206
208
|
}) => z.ZodString;
|
|
209
|
+
/**
|
|
210
|
+
* An uploaded document — a PDF today.
|
|
211
|
+
*
|
|
212
|
+
* Stores a URL exactly as `image` does, because the upload path is the same:
|
|
213
|
+
* the file goes to the configured store (R2, or public/uploads) and what
|
|
214
|
+
* comes back is a plain URL. A separate widget rather than reusing `image`
|
|
215
|
+
* so the editor renders a filename and a download affordance instead of a
|
|
216
|
+
* thumbnail, and so an `image` field cannot silently accept a PDF and
|
|
217
|
+
* render a broken <img>.
|
|
218
|
+
*/
|
|
219
|
+
file: (o?: FieldMetaBase) => z.ZodString;
|
|
207
220
|
select: (o: FieldMetaBase & {
|
|
208
221
|
options: string[];
|
|
209
222
|
}) => z.ZodEnum<{
|
package/dist/schema/index.js
CHANGED
package/dist/storage/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { R as Rev } from '../types-
|
|
2
|
-
export {
|
|
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,
|
|
1
|
+
import { R as Rev } from '../types-CKzfz5iu.js';
|
|
2
|
+
export { a as CanciaKVStore, b as CanciaListStore, c as CanciaPageStore, d as CanciaStorage, C as CanciaStorageV2, L as ListEntry, P as PageMeta, e as PageRecord, f as PageSEO, g as RevConflictError } from '../types-CKzfz5iu.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-CwTFfUS4.js';
|
|
4
|
+
export { C as CommitFile, G as GitHubClient, a as GitHubClientOptions, b as GitHubCommitter, c as createGitHubClient } from '../github-client-Db5EPcSL.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Canonical JSON: keys sorted alphabetically at every level. Two values that
|
package/dist/storage/index.js
CHANGED
|
@@ -3,19 +3,19 @@ import {
|
|
|
3
3
|
createSQLiteAdapter
|
|
4
4
|
} from "../chunk-CJDIVWO3.js";
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
createSqliteAdapterV2
|
|
9
|
-
} from "../chunk-MXVOJRAM.js";
|
|
6
|
+
createGitBackedAdapter
|
|
7
|
+
} from "../chunk-XINNLVZP.js";
|
|
10
8
|
import {
|
|
11
9
|
createGitHubClient
|
|
12
10
|
} from "../chunk-U7V53JX7.js";
|
|
13
11
|
import {
|
|
14
12
|
canonicalize,
|
|
13
|
+
closeSqliteAdapterV2,
|
|
15
14
|
createJsonFileAdapter,
|
|
16
15
|
createJsonFileAdapterV2,
|
|
16
|
+
createSqliteAdapterV2,
|
|
17
17
|
hashRev
|
|
18
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-S5YLB6P3.js";
|
|
19
19
|
import {
|
|
20
20
|
RevConflictError
|
|
21
21
|
} from "../chunk-7IA5B5CF.js";
|
|
@@ -158,4 +158,4 @@ interface CanciaStorageV2 {
|
|
|
158
158
|
lists: CanciaListStore;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
export { type
|
|
161
|
+
export { type CanciaStorageV2 as C, type ListEntry as L, type PageMeta as P, type Rev as R, type CanciaKVStore as a, type CanciaListStore as b, type CanciaPageStore as c, type CanciaStorage as d, type PageRecord as e, type PageSEO as f, RevConflictError as g };
|