@supersuit/artifacts 0.57.2 → 0.58.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/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,32 @@
|
|
|
3
3
|
`@supersuit/artifacts`. One entry per version, newest first. Each entry says what changed, how a
|
|
4
4
|
host can tell whether it is affected (DETECTOR), what a host does about it (REMEDY), and the tests.
|
|
5
5
|
|
|
6
|
+
## 0.58.1 (2026-10-27)
|
|
7
|
+
|
|
8
|
+
**The no-link door on a per-reader page offers sign-in.** A signed-in owner or hub admin already got
|
|
9
|
+
through a `links: per-reader` page without a link, but a signed-out one met "This page opens from the
|
|
10
|
+
link it was sent with." and nothing else, so a co-founder opening a company page's bare address had
|
|
11
|
+
no way in (2026-10-27).
|
|
12
|
+
|
|
13
|
+
- DETECTOR: open a per-reader page's bare address signed out; the door has no sign-in link.
|
|
14
|
+
- REMEDY: none for the host beyond taking this version. The door now carries "Sign in" (only with
|
|
15
|
+
`signInOrigin` set, only to a visitor not signed in); a signed-in reader who is not owner or admin
|
|
16
|
+
still sees the plain door, and nothing of the page is shown before the gate.
|
|
17
|
+
- Tests: `src/routes/per-reader.test.tsx` (sign-in offered when signed out, not offered when signed in).
|
|
18
|
+
|
|
19
|
+
## 0.58.0 (2026-10-27)
|
|
20
|
+
|
|
21
|
+
**A link's provenance can name its reader by an opaque person id.** Superlinks step 6: a funnel
|
|
22
|
+
suggestion ("Ada read the proposal to the end: move her to proposed?") has to know whose opens these
|
|
23
|
+
are. A label is a name and cannot be trusted as an identity; the sender's opaque person id can.
|
|
24
|
+
|
|
25
|
+
- `provenance.subject`: a person id, `p_` and 8 characters from the sender's records. Anything else
|
|
26
|
+
(a name, a different shape) is refused 400 and nothing is added. Kept on the row and answered back
|
|
27
|
+
like the rest of the provenance.
|
|
28
|
+
- DETECTOR: a sender that posts `provenance.subject` and reads it back missing is on 0.57.x or older.
|
|
29
|
+
- REMEDY: update the host.
|
|
30
|
+
- Tests: recipients-api.test.tsx (subject kept; a name and malformed ids refused).
|
|
31
|
+
|
|
6
32
|
## 0.57.2 (2026-10-26)
|
|
7
33
|
|
|
8
34
|
**0.57.1, published.** 0.57.1 was tagged but never reached npm: its publish run refused it, because
|
package/README.md
CHANGED
|
@@ -1085,7 +1085,7 @@ mark is counted as before-send; senders mark as soon as delivery is confirmed.
|
|
|
1085
1085
|
### Who minted a link (0.53.0)
|
|
1086
1086
|
|
|
1087
1087
|
Every link can say who made it, on whose authority, and when: `add: [{ label, provenance: { actor,
|
|
1088
|
-
authority, at?, link?, funnel?, stage? } }]
|
|
1088
|
+
authority, at?, link?, funnel?, stage?, subject? } }]`; `subject` (0.58.0) is the sender's opaque person id (`p_` and 8 characters), never a name. `actor` is a person acting by hand or "<person>'s
|
|
1089
1089
|
Pluma"; `authority` names what they acted under ("approved by <person> on the send card", "on
|
|
1090
1090
|
<person>'s explicit request", "under the recurrent <name>"), with `link` to the session or message
|
|
1091
1091
|
where one exists (https only); `at` defaults to now. `funnel` (a slug) and `stage` (an id, only with
|
package/lib/routes/artifacts.js
CHANGED
|
@@ -353,7 +353,9 @@ export function createArtifactRoutes(config) {
|
|
|
353
353
|
const secret = usableSecret(config.recipientSecret?.());
|
|
354
354
|
const g = await perReaderGate({ id, param, cookie: await readCookie(ridCookieName(id)).catch(() => undefined), secret, recipients: config.recipients });
|
|
355
355
|
if (g.state === 'shut') {
|
|
356
|
-
return (
|
|
356
|
+
return (_jsxs(BrandGround, { pack: pack, mode: a.theme, children: [_jsx("p", { "data-per-reader-shut": true, className: "mx-auto max-w-xl px-6 pt-32 text-center text-lg opacity-80", children: g.why === 'no-link' ? NO_LINK : UNAVAILABLE }), g.why === 'no-link' && !me && signInOrigin
|
|
357
|
+
? _jsxs("p", { "data-per-reader-sign-in": true, className: "mx-auto max-w-xl px-6 pt-4 text-center text-base opacity-70", children: ["Published this page or help run this site? ", _jsx("a", { className: "underline", href: signInUrl(signInOrigin, pageUrl(id)), children: "Sign in" })] })
|
|
358
|
+
: null] }));
|
|
357
359
|
}
|
|
358
360
|
// A fetcher is not the reader: it sets no cookie, carries no render token and sees no note.
|
|
359
361
|
const remember = g.fromLink && !bot
|
|
@@ -18,7 +18,7 @@ const SLUG = /^[a-z0-9][a-z0-9_-]{0,63}$/;
|
|
|
18
18
|
* now), and the funnel step it serves. -> the clean record, or the reason it is refused. */
|
|
19
19
|
export function readProvenance(v, now = new Date()) {
|
|
20
20
|
if (!v || typeof v !== 'object' || Array.isArray(v))
|
|
21
|
-
return { ok: false, why: 'provenance is { actor, authority, at?, link?, funnel?, stage? }' };
|
|
21
|
+
return { ok: false, why: 'provenance is { actor, authority, at?, link?, funnel?, stage?, subject? }' };
|
|
22
22
|
const p = v;
|
|
23
23
|
const text = (x, max) => (typeof x === 'string' && x.trim() ? x.trim().slice(0, max) : '');
|
|
24
24
|
const actor = text(p.actor, 120), authority = text(p.authority, 300);
|
|
@@ -42,8 +42,12 @@ export function readProvenance(v, now = new Date()) {
|
|
|
42
42
|
return { ok: false, why: 'provenance.funnel must be a funnel slug' };
|
|
43
43
|
if (p.stage !== undefined && (typeof p.stage !== 'string' || !SLUG.test(p.stage) || p.funnel === undefined))
|
|
44
44
|
return { ok: false, why: 'provenance.stage must be a stage id, with its funnel' };
|
|
45
|
+
// The reader by the sender's opaque person id (superlinks step 6), so a funnel can tell whose opens
|
|
46
|
+
// these are without a name ever reaching the host.
|
|
47
|
+
if (p.subject !== undefined && (typeof p.subject !== 'string' || !/^p_[0-9abcdefghjkmnpqrstvwxyz]{8}$/.test(p.subject)))
|
|
48
|
+
return { ok: false, why: 'provenance.subject must be an opaque person id (p_ and 8 characters), never a name' };
|
|
45
49
|
const at = typeof p.at === 'string' ? new Date(p.at).toISOString() : now.toISOString();
|
|
46
|
-
return { ok: true, value: { actor, authority, at, ...(link ? { link } : {}), ...(p.funnel ? { funnel: p.funnel } : {}), ...(p.stage ? { stage: p.stage } : {}) } };
|
|
50
|
+
return { ok: true, value: { actor, authority, at, ...(link ? { link } : {}), ...(p.funnel ? { funnel: p.funnel } : {}), ...(p.stage ? { stage: p.stage } : {}), ...(p.subject ? { subject: p.subject } : {}) } };
|
|
47
51
|
}
|
|
48
52
|
const none = () => new NextResponse(null, { status: 204, headers: { 'cache-control': 'no-store' } });
|
|
49
53
|
const json = (b, status = 200) => NextResponse.json(b, { status, headers: { 'cache-control': 'no-store' } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supersuit/artifacts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.58.1",
|
|
4
4
|
"description": "Publish markdown as branded, read-aloud pages from a Next.js app: the artifacts store, the route factory, the read-along reader, brand packs, password and confidential pages, and share cards.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|