@drawnagency/primitives 0.1.63 → 0.1.65
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/auth/errors.d.ts +12 -0
- package/dist/auth/errors.d.ts.map +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +3 -1
- package/dist/{chunk-UNWNT52N.js → chunk-EM5DFHDZ.js} +8 -1
- package/dist/components/shell/view-branch.d.ts +26 -0
- package/dist/components/shell/view-branch.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/package.json +1 -1
- package/src/auth/errors.ts +16 -0
- package/src/auth/index.ts +1 -1
- package/src/components/shell/EditorShell.tsx +17 -14
- package/src/components/shell/view-branch.ts +33 -0
package/dist/auth/errors.d.ts
CHANGED
|
@@ -5,4 +5,16 @@
|
|
|
5
5
|
export declare class LastOwnerError extends Error {
|
|
6
6
|
constructor(message?: string);
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Thrown when an authenticated operation cannot proceed because the underlying
|
|
10
|
+
* Supabase session is missing, expired, or its refresh token is stale/used.
|
|
11
|
+
* This is distinct from a never-authenticated request: the caller passed the
|
|
12
|
+
* middleware gate (e.g. via a still-valid app session cookie) but the Supabase
|
|
13
|
+
* session needed for the operation is dead. API routes should translate this to
|
|
14
|
+
* HTTP 401 so the UI prompts the user to request a fresh link, rather than
|
|
15
|
+
* surfacing an opaque 500.
|
|
16
|
+
*/
|
|
17
|
+
export declare class SessionExpiredError extends Error {
|
|
18
|
+
constructor(message?: string);
|
|
19
|
+
}
|
|
8
20
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/auth/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,SAAiC;CAIrD"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/auth/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,SAAiC;CAIrD;AAED;;;;;;;;GAQG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,SAAyD;CAI7E"}
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -3,5 +3,5 @@ export type { UiCapabilities } from "./capabilities";
|
|
|
3
3
|
export { deriveUiCapabilities } from "./capabilities";
|
|
4
4
|
export { requireSessionSecret, isSameOriginRequest, safeNextPath } from "./security";
|
|
5
5
|
export { signSessionToken, verifySessionToken, signAudienceToken, verifyAudienceToken, setSessionCookie, SESSION_COOKIE, AUDIENCE_COOKIE, SESSION_MAX_AGE_SECONDS, AUDIENCE_MAX_AGE_SECONDS } from "./cookies";
|
|
6
|
-
export { LastOwnerError } from "./errors";
|
|
6
|
+
export { LastOwnerError, SessionExpiredError } from "./errors";
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/auth/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,UAAU,EACV,WAAW,GACZ,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAC/M,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,UAAU,EACV,WAAW,GACZ,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAC/M,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/auth/index.js
CHANGED
|
@@ -4,13 +4,14 @@ import {
|
|
|
4
4
|
LastOwnerError,
|
|
5
5
|
SESSION_COOKIE,
|
|
6
6
|
SESSION_MAX_AGE_SECONDS,
|
|
7
|
+
SessionExpiredError,
|
|
7
8
|
deriveUiCapabilities,
|
|
8
9
|
setSessionCookie,
|
|
9
10
|
signAudienceToken,
|
|
10
11
|
signSessionToken,
|
|
11
12
|
verifyAudienceToken,
|
|
12
13
|
verifySessionToken
|
|
13
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-EM5DFHDZ.js";
|
|
14
15
|
import {
|
|
15
16
|
isSameOriginRequest,
|
|
16
17
|
requireSessionSecret,
|
|
@@ -24,6 +25,7 @@ export {
|
|
|
24
25
|
LastOwnerError,
|
|
25
26
|
SESSION_COOKIE,
|
|
26
27
|
SESSION_MAX_AGE_SECONDS,
|
|
28
|
+
SessionExpiredError,
|
|
27
29
|
deriveUiCapabilities,
|
|
28
30
|
isSameOriginRequest,
|
|
29
31
|
requireSessionSecret,
|
|
@@ -74,6 +74,12 @@ var LastOwnerError = class extends Error {
|
|
|
74
74
|
this.name = "LastOwnerError";
|
|
75
75
|
}
|
|
76
76
|
};
|
|
77
|
+
var SessionExpiredError = class extends Error {
|
|
78
|
+
constructor(message = "Your session has expired. Please request a new link.") {
|
|
79
|
+
super(message);
|
|
80
|
+
this.name = "SessionExpiredError";
|
|
81
|
+
}
|
|
82
|
+
};
|
|
77
83
|
|
|
78
84
|
export {
|
|
79
85
|
deriveUiCapabilities,
|
|
@@ -86,5 +92,6 @@ export {
|
|
|
86
92
|
signAudienceToken,
|
|
87
93
|
verifyAudienceToken,
|
|
88
94
|
setSessionCookie,
|
|
89
|
-
LastOwnerError
|
|
95
|
+
LastOwnerError,
|
|
96
|
+
SessionExpiredError
|
|
90
97
|
};
|
|
@@ -9,4 +9,30 @@ export type ViewBranch = "saved" | "live";
|
|
|
9
9
|
* `archived` — so edits to live content show alongside drafts.
|
|
10
10
|
*/
|
|
11
11
|
export declare function isSectionVisibleInViewBranch(status: string, viewBranch: ViewBranch): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Which sections to render in the editor's view mode.
|
|
14
|
+
*
|
|
15
|
+
* - Edit mode: the in-memory sections as-is (edit chrome handles them).
|
|
16
|
+
* - "Unpublished" (saved): the editor's CURRENT in-memory content — all edits
|
|
17
|
+
* minus archived. It must NOT depend on a re-fetched branch: after a publish
|
|
18
|
+
* the `saved` branch is deleted and `/api/content?branch=saved` falls back to
|
|
19
|
+
* `main`, which would clobber unsaved edits (they'd flash then vanish).
|
|
20
|
+
* - "Published" (live): the deployed site — the fetched `main` branch filtered to
|
|
21
|
+
* `status === "live"`, in page order. Renders nothing until the fetch resolves
|
|
22
|
+
* so in-memory edits never flash as if they were published.
|
|
23
|
+
*/
|
|
24
|
+
export declare function computeDisplaySections<T extends {
|
|
25
|
+
section: {
|
|
26
|
+
id: string;
|
|
27
|
+
};
|
|
28
|
+
meta: {
|
|
29
|
+
status: string;
|
|
30
|
+
};
|
|
31
|
+
}>(args: {
|
|
32
|
+
isEditMode: boolean;
|
|
33
|
+
viewBranch: ViewBranch;
|
|
34
|
+
sections: T[];
|
|
35
|
+
viewSections: T[] | null;
|
|
36
|
+
activePageSectionIds: string[];
|
|
37
|
+
}): T[];
|
|
12
38
|
//# sourceMappingURL=view-branch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view-branch.d.ts","sourceRoot":"","sources":["../../../src/components/shell/view-branch.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;AAE1C;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAE5F"}
|
|
1
|
+
{"version":3,"file":"view-branch.d.ts","sourceRoot":"","sources":["../../../src/components/shell/view-branch.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;AAE1C;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,OAAO,CAE5F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS;IAAE,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EAAE,IAAI,EAAE;IAC5G,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,CAAC,EAAE,CAAC;IACd,YAAY,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC;IACzB,oBAAoB,EAAE,MAAM,EAAE,CAAC;CAChC,GAAG,CAAC,EAAE,CAaN"}
|
package/dist/index.js
CHANGED
|
@@ -20,13 +20,14 @@ import {
|
|
|
20
20
|
LastOwnerError,
|
|
21
21
|
SESSION_COOKIE,
|
|
22
22
|
SESSION_MAX_AGE_SECONDS,
|
|
23
|
+
SessionExpiredError,
|
|
23
24
|
deriveUiCapabilities,
|
|
24
25
|
setSessionCookie,
|
|
25
26
|
signAudienceToken,
|
|
26
27
|
signSessionToken,
|
|
27
28
|
verifyAudienceToken,
|
|
28
29
|
verifySessionToken
|
|
29
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-EM5DFHDZ.js";
|
|
30
31
|
import {
|
|
31
32
|
AudienceColorSchema,
|
|
32
33
|
AudienceNameSchema,
|
|
@@ -157,6 +158,7 @@ export {
|
|
|
157
158
|
SESSION_COOKIE,
|
|
158
159
|
SESSION_MAX_AGE_SECONDS,
|
|
159
160
|
SectionMetaSchema,
|
|
161
|
+
SessionExpiredError,
|
|
160
162
|
SessionSchema,
|
|
161
163
|
SingleMediaReferenceSchema,
|
|
162
164
|
SiteConfigSchema,
|
package/package.json
CHANGED
package/src/auth/errors.ts
CHANGED
|
@@ -8,3 +8,19 @@ export class LastOwnerError extends Error {
|
|
|
8
8
|
this.name = "LastOwnerError";
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Thrown when an authenticated operation cannot proceed because the underlying
|
|
14
|
+
* Supabase session is missing, expired, or its refresh token is stale/used.
|
|
15
|
+
* This is distinct from a never-authenticated request: the caller passed the
|
|
16
|
+
* middleware gate (e.g. via a still-valid app session cookie) but the Supabase
|
|
17
|
+
* session needed for the operation is dead. API routes should translate this to
|
|
18
|
+
* HTTP 401 so the UI prompts the user to request a fresh link, rather than
|
|
19
|
+
* surfacing an opaque 500.
|
|
20
|
+
*/
|
|
21
|
+
export class SessionExpiredError extends Error {
|
|
22
|
+
constructor(message = "Your session has expired. Please request a new link.") {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = "SessionExpiredError";
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/auth/index.ts
CHANGED
|
@@ -17,4 +17,4 @@ export { deriveUiCapabilities } from "./capabilities";
|
|
|
17
17
|
|
|
18
18
|
export { requireSessionSecret, isSameOriginRequest, safeNextPath } from "./security";
|
|
19
19
|
export { signSessionToken, verifySessionToken, signAudienceToken, verifyAudienceToken, setSessionCookie, SESSION_COOKIE, AUDIENCE_COOKIE, SESSION_MAX_AGE_SECONDS, AUDIENCE_MAX_AGE_SECONDS } from "./cookies";
|
|
20
|
-
export { LastOwnerError } from "./errors";
|
|
20
|
+
export { LastOwnerError, SessionExpiredError } from "./errors";
|
|
@@ -69,7 +69,7 @@ import { Button } from "../shared/Button";
|
|
|
69
69
|
import { SplitButton } from "../shared/SplitButton";
|
|
70
70
|
import { IconButton } from "../shared/IconButton";
|
|
71
71
|
import { SegmentedControl } from "../shared/SegmentedControl";
|
|
72
|
-
import {
|
|
72
|
+
import { computeDisplaySections } from "./view-branch";
|
|
73
73
|
import { SettingsIcon } from "../shared/icons";
|
|
74
74
|
import { ImageIcon, ListOrderedIcon, FilesIcon, X } from "lucide-react";
|
|
75
75
|
import { ErrorBoundary } from "../shared/ErrorBoundary";
|
|
@@ -1053,14 +1053,16 @@ function ViewBranchWatcher({
|
|
|
1053
1053
|
const { isEditMode, viewBranch } = useEditorContext();
|
|
1054
1054
|
|
|
1055
1055
|
useEffect(() => {
|
|
1056
|
-
|
|
1056
|
+
// Only "Published" (live) needs a fetch — it previews the deployed main
|
|
1057
|
+
// branch. "Unpublished" (saved) renders the editor's in-memory edits, so no
|
|
1058
|
+
// fetch (fetching `saved` would 404 after a publish and fall back to main,
|
|
1059
|
+
// clobbering unsaved edits). Null viewSections routes to the in-memory path.
|
|
1060
|
+
if (isEditMode || viewBranch !== "live") {
|
|
1057
1061
|
setViewSections(null);
|
|
1058
1062
|
return;
|
|
1059
1063
|
}
|
|
1060
1064
|
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
fetch(`/api/content?branch=${branch}`)
|
|
1065
|
+
fetch(`/api/content?branch=main`)
|
|
1064
1066
|
.then((res) => res.json())
|
|
1065
1067
|
.then((data) => {
|
|
1066
1068
|
setViewSections(data.sections);
|
|
@@ -1216,15 +1218,16 @@ function EditorContent({
|
|
|
1216
1218
|
|
|
1217
1219
|
const editingEnabled = isEditMode && !isPublishing;
|
|
1218
1220
|
|
|
1219
|
-
//
|
|
1220
|
-
//
|
|
1221
|
-
//
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1221
|
+
// "Published" (live) previews the deployed main branch (status === "live");
|
|
1222
|
+
// "Unpublished" (saved) previews the editor's current in-memory edits (all
|
|
1223
|
+
// statuses except archived) — see computeDisplaySections in view-branch.ts.
|
|
1224
|
+
const displaySections = computeDisplaySections({
|
|
1225
|
+
isEditMode,
|
|
1226
|
+
viewBranch,
|
|
1227
|
+
sections,
|
|
1228
|
+
viewSections,
|
|
1229
|
+
activePageSectionIds,
|
|
1230
|
+
});
|
|
1228
1231
|
|
|
1229
1232
|
useEffect(() => {
|
|
1230
1233
|
return combine(
|
|
@@ -12,3 +12,36 @@ export type ViewBranch = "saved" | "live";
|
|
|
12
12
|
export function isSectionVisibleInViewBranch(status: string, viewBranch: ViewBranch): boolean {
|
|
13
13
|
return viewBranch === "live" ? status === "live" : status !== "archived";
|
|
14
14
|
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Which sections to render in the editor's view mode.
|
|
18
|
+
*
|
|
19
|
+
* - Edit mode: the in-memory sections as-is (edit chrome handles them).
|
|
20
|
+
* - "Unpublished" (saved): the editor's CURRENT in-memory content — all edits
|
|
21
|
+
* minus archived. It must NOT depend on a re-fetched branch: after a publish
|
|
22
|
+
* the `saved` branch is deleted and `/api/content?branch=saved` falls back to
|
|
23
|
+
* `main`, which would clobber unsaved edits (they'd flash then vanish).
|
|
24
|
+
* - "Published" (live): the deployed site — the fetched `main` branch filtered to
|
|
25
|
+
* `status === "live"`, in page order. Renders nothing until the fetch resolves
|
|
26
|
+
* so in-memory edits never flash as if they were published.
|
|
27
|
+
*/
|
|
28
|
+
export function computeDisplaySections<T extends { section: { id: string }; meta: { status: string } }>(args: {
|
|
29
|
+
isEditMode: boolean;
|
|
30
|
+
viewBranch: ViewBranch;
|
|
31
|
+
sections: T[];
|
|
32
|
+
viewSections: T[] | null;
|
|
33
|
+
activePageSectionIds: string[];
|
|
34
|
+
}): T[] {
|
|
35
|
+
const { isEditMode, viewBranch, sections, viewSections, activePageSectionIds } = args;
|
|
36
|
+
|
|
37
|
+
if (isEditMode) return sections;
|
|
38
|
+
|
|
39
|
+
if (viewBranch === "saved") {
|
|
40
|
+
return sections.filter((s) => isSectionVisibleInViewBranch(s.meta.status, "saved"));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (viewSections === null) return [];
|
|
44
|
+
return activePageSectionIds
|
|
45
|
+
.map((id) => viewSections.find((s) => s.section.id === id))
|
|
46
|
+
.filter((s): s is T => !!s && isSectionVisibleInViewBranch(s.meta.status, "live"));
|
|
47
|
+
}
|