@snagset/server 0.0.3 → 0.1.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/README.md +4 -3
- package/dist/app.js +167 -30
- package/dist/app.js.map +1 -1
- package/dist/bin.js +46 -9
- package/dist/bin.js.map +1 -1
- package/dist/boot.js +105 -54
- package/dist/boot.js.map +1 -1
- package/dist/config.generated.js +5 -2
- package/dist/config.generated.js.map +1 -1
- package/dist/db/connect.js +7 -0
- package/dist/db/connect.js.map +1 -1
- package/dist/db/migrations/0005_studio_reviewers.sql +45 -0
- package/dist/db/migrations/0006_instance_settings.sql +69 -0
- package/dist/db/migrations/0007_jira_provider.no-txn.sql +13 -0
- package/dist/domain.js +129 -0
- package/dist/domain.js.map +1 -0
- package/dist/instance/settings.js +54 -0
- package/dist/instance/settings.js.map +1 -0
- package/dist/integrations/github.js +112 -0
- package/dist/integrations/github.js.map +1 -0
- package/dist/integrations/inbound.js +88 -0
- package/dist/integrations/inbound.js.map +1 -0
- package/dist/integrations/jira.js +129 -0
- package/dist/integrations/jira.js.map +1 -0
- package/dist/integrations/linear.js +111 -0
- package/dist/integrations/linear.js.map +1 -0
- package/dist/integrations/outbox.js +305 -0
- package/dist/integrations/outbox.js.map +1 -0
- package/dist/integrations/slack.js +135 -0
- package/dist/integrations/slack.js.map +1 -0
- package/dist/integrations/store.js +101 -0
- package/dist/integrations/store.js.map +1 -0
- package/dist/mail/index.js +21 -11
- package/dist/mail/index.js.map +1 -1
- package/dist/mail/resolve.js +80 -0
- package/dist/mail/resolve.js.map +1 -0
- package/dist/projectCache.js +3 -2
- package/dist/projectCache.js.map +1 -1
- package/dist/projects.js +8 -2
- package/dist/projects.js.map +1 -1
- package/dist/routes/account.js +59 -0
- package/dist/routes/account.js.map +1 -0
- package/dist/routes/activity.js +111 -0
- package/dist/routes/activity.js.map +1 -0
- package/dist/routes/anchorReports.js +196 -0
- package/dist/routes/anchorReports.js.map +1 -0
- package/dist/routes/assignees.js +139 -0
- package/dist/routes/assignees.js.map +1 -0
- package/dist/routes/attachments.js +2 -2
- package/dist/routes/attachments.js.map +1 -1
- package/dist/routes/bodyLimit.js +60 -0
- package/dist/routes/bodyLimit.js.map +1 -0
- package/dist/routes/comments.js +27 -17
- package/dist/routes/comments.js.map +1 -1
- package/dist/routes/cors.js +10 -1
- package/dist/routes/cors.js.map +1 -1
- package/dist/routes/instance.js +164 -0
- package/dist/routes/instance.js.map +1 -0
- package/dist/routes/integrations.js +610 -0
- package/dist/routes/integrations.js.map +1 -0
- package/dist/routes/jobs.js +15 -3
- package/dist/routes/jobs.js.map +1 -1
- package/dist/routes/mcpTokens.js +119 -0
- package/dist/routes/mcpTokens.js.map +1 -0
- package/dist/routes/members.js +177 -0
- package/dist/routes/members.js.map +1 -0
- package/dist/routes/owner.js +64 -7
- package/dist/routes/owner.js.map +1 -1
- package/dist/routes/sites.js +181 -8
- package/dist/routes/sites.js.map +1 -1
- package/dist/routes/threads.js +276 -9
- package/dist/routes/threads.js.map +1 -1
- package/dist/serverless.js +39 -6
- package/dist/serverless.js.map +1 -1
- package/dist/studioReviewers.js +67 -0
- package/dist/studioReviewers.js.map +1 -0
- package/dist/vendor/adapters/outbound/sanitise.js +18 -3
- package/dist/vendor/types/brief.js +121 -0
- package/package.json +1 -1
- package/static/dashboard/assets/CompositeItem-CoPuxIvr.js +1 -0
- package/static/dashboard/assets/ConfirmDialog-DkCcJwll.js +1 -0
- package/static/dashboard/assets/CopyButton-BNa2pLLv.js +1 -0
- package/static/dashboard/assets/FieldItemContext-DQXjpFtW.js +1 -0
- package/static/dashboard/assets/InstallTag-SOlWl7Hx.js +1 -0
- package/static/dashboard/assets/SaveState-CmxB4XyX.js +6 -0
- package/static/dashboard/assets/_app-DNkv3HBi.js +1 -0
- package/static/dashboard/assets/_app-xVQe811c.js +56 -0
- package/static/dashboard/assets/activity-DsMa5Ja6.js +1 -0
- package/static/dashboard/assets/arrow-left-vQV4xhAD.js +6 -0
- package/static/dashboard/assets/avatar-DuPL6QD_.js +1 -0
- package/static/dashboard/assets/badge-BWjiIc8T.js +1 -0
- package/static/dashboard/assets/card-BIBfCno8.js +1 -0
- package/static/dashboard/assets/check-Dpx6GTdT.js +6 -0
- package/static/dashboard/assets/chevron-right-DKIlBUGN.js +6 -0
- package/static/dashboard/assets/copy-DCbhEe2L.js +6 -0
- package/static/dashboard/assets/createBaseUIEventDetails-C_ZVkrgp.js +1 -0
- package/static/dashboard/assets/createLucideIcon-C_IGE4WD.js +36 -0
- package/static/dashboard/assets/dialog-BT2uWf4e.js +6 -0
- package/static/dashboard/assets/dropdown-menu-DSXzPAwB.js +11 -0
- package/static/dashboard/assets/image-off-DGWejLDG.js +6 -0
- package/static/dashboard/assets/index-BRTBtV91.css +1 -0
- package/static/dashboard/assets/index-D6FWZoJT.js +129 -0
- package/static/dashboard/assets/label-9QsSpLMA.js +1 -0
- package/static/dashboard/assets/labels--UUyt9uB.js +1 -0
- package/static/dashboard/assets/layout-grid-UPGEmEw-.js +6 -0
- package/static/dashboard/assets/me-0HQsBXLU.js +1 -0
- package/static/dashboard/assets/parts-C29uZ5zo.js +1 -0
- package/static/dashboard/assets/plus-BFNS5LcY.js +6 -0
- package/static/dashboard/assets/s._site-D6Ep2tdc.js +1 -0
- package/static/dashboard/assets/s._site.comments-BRVI5gpJ.js +39 -0
- package/static/dashboard/assets/s._site.reviewers-CiEkZnnp.js +1 -0
- package/static/dashboard/assets/s._site.settings-BqLGyfEd.js +1 -0
- package/static/dashboard/assets/setup-dUMCPWdf.js +1 -0
- package/static/dashboard/assets/sign-in-CdRa-bio.js +1 -0
- package/static/dashboard/assets/sites-CmbtFKoo.js +1 -0
- package/static/dashboard/assets/studio-BHuVXWZh.js +1 -0
- package/static/dashboard/assets/studio-BjeTOKda.js +1 -0
- package/static/dashboard/assets/studio.index--kuzOs7w.js +1 -0
- package/static/dashboard/assets/studio.integrations-Bq6HP80a.js +1 -0
- package/static/dashboard/assets/studio.members-8AB1KYsw.js +1 -0
- package/static/dashboard/assets/sun-BCiTcgnh.js +16 -0
- package/static/dashboard/assets/table-BXmeI_yA.js +1 -0
- package/static/dashboard/assets/update-duXzIzFS.js +1 -0
- package/static/dashboard/assets/useBaseQuery-Cp9wivVR.js +1 -0
- package/static/dashboard/assets/useQuery-BZXZlOqO.js +1 -0
- package/static/dashboard/assets/useRender-CO8aPW06.js +1 -0
- package/static/dashboard/assets/users-Dvw2FCKM.js +6 -0
- package/static/dashboard/assets/welcome-BHKjw20b.js +11 -0
- package/static/dashboard/assets/you-RBpAfUMU.js +1 -0
- package/static/dashboard/assets/zod-BpyjQIvb.js +1 -0
- package/static/dashboard/index.html +17 -2
- package/static/widget/snag.capture.js +1 -1
- package/static/widget/snag.dock.js +1 -1
- package/static/widget/snag.panels.js +1 -1
- package/static/widget/snag.runtime.js +2 -2
- package/static/widget/snag.text.js +1 -1
- package/vercel.json +4 -0
- package/static/dashboard/assets/index-DsUJ96yc.css +0 -1
- package/static/dashboard/assets/index-DultqpS7.js +0 -1
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { ID_PREFIX } from "../vendor/types/names.js";
|
|
4
|
+
function fail(c, status, code, message, details) {
|
|
5
|
+
return c.json({ error: { code, message, ...(details ? { details } : {}), requestId: c.get("requestId") } }, status);
|
|
6
|
+
}
|
|
7
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
8
|
+
function rowId(wire, prefix) {
|
|
9
|
+
if (typeof wire !== "string")
|
|
10
|
+
return null;
|
|
11
|
+
const bare = wire.startsWith(prefix) ? wire.slice(prefix.length) : wire;
|
|
12
|
+
return UUID.test(bare) ? bare.toLowerCase() : null;
|
|
13
|
+
}
|
|
14
|
+
/** 03 §5's enum, not free text. `manual` is reported by v2 repair, never by `resolve()`. */
|
|
15
|
+
const STRATEGY = new Set([
|
|
16
|
+
"testid",
|
|
17
|
+
"domid",
|
|
18
|
+
"css",
|
|
19
|
+
"aria",
|
|
20
|
+
"text-exact",
|
|
21
|
+
"text-fuzzy",
|
|
22
|
+
"path-rect",
|
|
23
|
+
"none",
|
|
24
|
+
"manual",
|
|
25
|
+
]);
|
|
26
|
+
/**
|
|
27
|
+
* foundations §14.2's ordering. `pending` is deliberately absent — it is not a
|
|
28
|
+
* settled state, and §6.7 rejects it: a client reporting "I have not finished
|
|
29
|
+
* yet" is not reporting an outcome.
|
|
30
|
+
*/
|
|
31
|
+
const RANK = { orphaned: 0, approximate: 1, anchored: 2 };
|
|
32
|
+
const MAX_REPORTS = 50;
|
|
33
|
+
/** §6.7: idempotent within 60 s per (thread, build, strategy). */
|
|
34
|
+
const DEDUPE_MS = 60_000;
|
|
35
|
+
const RATE_WINDOW_MS = 60_000;
|
|
36
|
+
export function anchorReportRoutes(sql, projects, perMinute) {
|
|
37
|
+
const app = new Hono();
|
|
38
|
+
app.post("/api/v1/projects/:uid/anchor-reports", async (c) => {
|
|
39
|
+
const p = await projects.get(c.req.param("uid")).catch(() => null);
|
|
40
|
+
if (!p)
|
|
41
|
+
return fail(c, 404, "project_not_found", "No project with that uid.");
|
|
42
|
+
const session = c.get("session");
|
|
43
|
+
if (!session) {
|
|
44
|
+
return fail(c, 401, "unauthorized", "Open your review link, or reload the page, and try again.");
|
|
45
|
+
}
|
|
46
|
+
const body = (await c.req.json().catch(() => null));
|
|
47
|
+
const reports = Array.isArray(body?.reports) ? body.reports : null;
|
|
48
|
+
if (!reports || reports.length === 0 || reports.length > MAX_REPORTS) {
|
|
49
|
+
return fail(c, 400, "validation_failed", `reports must be an array of 1 to ${MAX_REPORTS} items.`, { field: "reports" });
|
|
50
|
+
}
|
|
51
|
+
// Validate EVERY report before writing ANY of them. A batch that is half
|
|
52
|
+
// valid should not leave half a batch in the telemetry — the client has no
|
|
53
|
+
// way to learn which half landed, since the success case is a bare 204.
|
|
54
|
+
const parsed = [];
|
|
55
|
+
for (const r of reports) {
|
|
56
|
+
const threadRow = rowId(r["threadId"], ID_PREFIX.thread);
|
|
57
|
+
if (!threadRow) {
|
|
58
|
+
return fail(c, 400, "validation_failed", "threadId must be a thread id.", { field: "threadId" });
|
|
59
|
+
}
|
|
60
|
+
const state = String(r["state"]);
|
|
61
|
+
if (!(state in RANK)) {
|
|
62
|
+
return fail(c, 400, "validation_failed", "state must be one of orphaned, approximate, anchored — 'pending' is not an outcome.", { field: "state" });
|
|
63
|
+
}
|
|
64
|
+
const strategy = String(r["strategy"]);
|
|
65
|
+
if (!STRATEGY.has(strategy)) {
|
|
66
|
+
// 422, not 400, and §6.7 says why: a telemetry table that accepts
|
|
67
|
+
// anything measures nothing.
|
|
68
|
+
return fail(c, 422, "validation_failed", `Unknown anchor strategy '${strategy}'.`, {
|
|
69
|
+
field: "strategy",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const confidence = Number(r["confidence"]);
|
|
73
|
+
if (!Number.isFinite(confidence) || confidence < 0 || confidence > 1) {
|
|
74
|
+
return fail(c, 400, "validation_failed", "confidence must be between 0 and 1.", {
|
|
75
|
+
field: "confidence",
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const ms = Number(r["ms"]);
|
|
79
|
+
parsed.push({
|
|
80
|
+
threadRow,
|
|
81
|
+
state,
|
|
82
|
+
confidence,
|
|
83
|
+
strategy,
|
|
84
|
+
build: typeof r["build"] === "string" && r["build"] ? r["build"] : null,
|
|
85
|
+
ms: Number.isFinite(ms) ? Math.round(ms) : null,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Rate limit AFTER validation, so a malformed batch still gets its 4xx
|
|
90
|
+
* rather than being silently swallowed by a limit it also tripped — the
|
|
91
|
+
* developer integrating this needs the error more than we need the saving.
|
|
92
|
+
*/
|
|
93
|
+
const now = new Date();
|
|
94
|
+
const windowStart = new Date(now.getTime() - RATE_WINDOW_MS);
|
|
95
|
+
const [counter] = await sql `
|
|
96
|
+
INSERT INTO rate_counters (org_id, bucket_key, window_start, count, expires_at)
|
|
97
|
+
VALUES (${p.org_id}, ${`anch:${session.sid}`}, ${now}, ${parsed.length},
|
|
98
|
+
${new Date(now.getTime() + RATE_WINDOW_MS)})
|
|
99
|
+
ON CONFLICT (org_id, bucket_key) DO UPDATE
|
|
100
|
+
SET count = CASE WHEN rate_counters.window_start < ${windowStart} THEN ${parsed.length}
|
|
101
|
+
ELSE rate_counters.count + ${parsed.length} END,
|
|
102
|
+
window_start = CASE WHEN rate_counters.window_start < ${windowStart} THEN ${now}
|
|
103
|
+
ELSE rate_counters.window_start END,
|
|
104
|
+
expires_at = ${new Date(now.getTime() + RATE_WINDOW_MS)}
|
|
105
|
+
RETURNING count::text
|
|
106
|
+
`;
|
|
107
|
+
// Dropped, not queued, and not an error. §12.1 rule 8.
|
|
108
|
+
if (Number(counter?.count ?? 0) > perMinute())
|
|
109
|
+
return c.body(null, 204);
|
|
110
|
+
await sql.begin(async (tx) => {
|
|
111
|
+
await tx `SELECT set_config('app.org_id', ${p.org_id}, true)`;
|
|
112
|
+
for (const r of parsed) {
|
|
113
|
+
const [thread] = await tx `
|
|
114
|
+
SELECT id, anchor_state, anchor_state_build, last_anchor_check, version
|
|
115
|
+
FROM threads WHERE id = ${r.threadRow} AND project_id = ${p.id} AND deleted_at IS NULL
|
|
116
|
+
`;
|
|
117
|
+
// Not this project's thread. Skipped rather than failing the batch:
|
|
118
|
+
// the 404 case §6.7 names is a caller sending someone else's id, and
|
|
119
|
+
// by here we have already taken the rate-limit hit for it. A deleted
|
|
120
|
+
// thread is the same shape and is even less worth an error.
|
|
121
|
+
if (!thread)
|
|
122
|
+
continue;
|
|
123
|
+
/**
|
|
124
|
+
* §6.7's 60-second idempotency, by natural key rather than a header. A
|
|
125
|
+
* re-rendering SPA that resolves the same pin four times in a second
|
|
126
|
+
* is not four observations; it is one, seen four times.
|
|
127
|
+
*/
|
|
128
|
+
const [recent] = await tx `
|
|
129
|
+
SELECT count(*)::int AS n FROM anchor_events
|
|
130
|
+
WHERE thread_id = ${thread.id}
|
|
131
|
+
AND strategy = ${r.strategy}
|
|
132
|
+
AND build IS NOT DISTINCT FROM ${r.build}
|
|
133
|
+
AND at > ${new Date(now.getTime() - DEDUPE_MS)}
|
|
134
|
+
`;
|
|
135
|
+
if ((recent?.n ?? 0) > 0)
|
|
136
|
+
continue;
|
|
137
|
+
/**
|
|
138
|
+
* THE MONOTONIC MERGE (§6.7, foundations §14.2 normative).
|
|
139
|
+
*
|
|
140
|
+
* A new build is exactly when a pin is allowed to get worse — that is
|
|
141
|
+
* what keeps this monotonic rather than a one-way ratchet that could
|
|
142
|
+
* never report a real regression. Otherwise the maximum wins within
|
|
143
|
+
* the scope, and when neither side knows a build the scope is a
|
|
144
|
+
* rolling 24 hours.
|
|
145
|
+
*/
|
|
146
|
+
const newBuild = !!r.build && r.build !== thread.anchor_state_build;
|
|
147
|
+
const stale = !r.build &&
|
|
148
|
+
!thread.anchor_state_build &&
|
|
149
|
+
!!thread.last_anchor_check &&
|
|
150
|
+
now.getTime() - new Date(thread.last_anchor_check).getTime() > 24 * 60 * 60 * 1000;
|
|
151
|
+
const resets = newBuild || stale;
|
|
152
|
+
const next = resets
|
|
153
|
+
? r.state
|
|
154
|
+
: (RANK[r.state] ?? 0) > (RANK[thread.anchor_state] ?? 0)
|
|
155
|
+
? r.state
|
|
156
|
+
: thread.anchor_state;
|
|
157
|
+
const applied = next !== thread.anchor_state || resets;
|
|
158
|
+
await tx `
|
|
159
|
+
INSERT INTO anchor_events (id, org_id, project_id, thread_id, state, confidence,
|
|
160
|
+
strategy, actor_kind, actor_id, build, ms, applied)
|
|
161
|
+
VALUES (${randomUUID()}, ${p.org_id}, ${p.id}, ${thread.id}, ${r.state}::snag_anchor_state,
|
|
162
|
+
${r.confidence}, ${r.strategy}, 'runtime', NULL, ${r.build}, ${r.ms}, ${applied})
|
|
163
|
+
`;
|
|
164
|
+
/**
|
|
165
|
+
* `updated_at` and `version` move ONLY when the state actually
|
|
166
|
+
* changed. Telemetry must not wake a polling client, and it must not
|
|
167
|
+
* enqueue an outbox row for a non-event — a reviewer being emailed
|
|
168
|
+
* because somebody's browser re-measured a pin is the kind of noise
|
|
169
|
+
* that teaches people to filter the sender.
|
|
170
|
+
*/
|
|
171
|
+
if (next !== thread.anchor_state) {
|
|
172
|
+
await tx `
|
|
173
|
+
UPDATE threads
|
|
174
|
+
SET anchor_state = ${next}::snag_anchor_state,
|
|
175
|
+
anchor_state_build = ${r.build ?? thread.anchor_state_build},
|
|
176
|
+
last_anchor_check = ${now},
|
|
177
|
+
updated_at = ${now},
|
|
178
|
+
version = version + 1
|
|
179
|
+
WHERE id = ${thread.id}
|
|
180
|
+
`;
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
await tx `
|
|
184
|
+
UPDATE threads
|
|
185
|
+
SET last_anchor_check = ${now},
|
|
186
|
+
anchor_state_build = ${r.build ?? thread.anchor_state_build}
|
|
187
|
+
WHERE id = ${thread.id}
|
|
188
|
+
`;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
return c.body(null, 204);
|
|
193
|
+
});
|
|
194
|
+
return app;
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=anchorReports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchorReports.js","sourceRoot":"","sources":["../../src/routes/anchorReports.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { ID_PREFIX } from "../vendor/types/names.js";
|
|
4
|
+
/**
|
|
5
|
+
* WHO IS IN THE POOL — §6.4b's predicate, in one place, over an alias `r`.
|
|
6
|
+
* Blocked and erased reviewers leave the pool but keep their existing
|
|
7
|
+
* assignments (un-assigning on block would rewrite history on threads
|
|
8
|
+
* somebody is mid-conversation on); a guest who exchanged a session but never
|
|
9
|
+
* wrote is not in it (the matrix's "be assigned: ✓ once they have written").
|
|
10
|
+
* The thread PATCH validates a candidate against the same predicate, which is
|
|
11
|
+
* why it is exported rather than inlined in the query below.
|
|
12
|
+
*/
|
|
13
|
+
export function inPool(t) {
|
|
14
|
+
return t `r.blocked_at IS NULL AND r.erased_at IS NULL
|
|
15
|
+
AND (r.user_id IS NOT NULL OR r.invited_by IS NOT NULL
|
|
16
|
+
OR r.thread_count > 0 OR r.comment_count > 0)`;
|
|
17
|
+
}
|
|
18
|
+
function fail(c, status, code, message, details) {
|
|
19
|
+
return c.json({ error: { code, message, ...(details ? { details } : {}), requestId: c.get("requestId") } }, status);
|
|
20
|
+
}
|
|
21
|
+
/** Foundations §5's `Reviewer`, plus the two derived flags the dropdown groups by. */
|
|
22
|
+
export function shapeReviewer(r, withEmail) {
|
|
23
|
+
return {
|
|
24
|
+
id: ID_PREFIX.reviewer + String(r["id"]),
|
|
25
|
+
name: String(r["name"] ?? ""),
|
|
26
|
+
kind: String(r["kind"] ?? "guest"),
|
|
27
|
+
...(withEmail && r["email"] ? { email: String(r["email"]) } : {}),
|
|
28
|
+
...(r["avatar_url"] ? { avatarUrl: String(r["avatar_url"]) } : {}),
|
|
29
|
+
// Derived on the wire, not stored twice — so the dropdown can put "the
|
|
30
|
+
// studio" above "people who have commented". NOT a permission.
|
|
31
|
+
isTeam: !!r["is_team"],
|
|
32
|
+
isMe: !!r["is_me"],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function assigneeRoutes(deps) {
|
|
36
|
+
const { sql, projects } = deps;
|
|
37
|
+
const app = new Hono();
|
|
38
|
+
const inOrg = (orgId, fn) => sql.begin(async (tx) => {
|
|
39
|
+
await tx `SELECT set_config('app.org_id', ${orgId}, true)`;
|
|
40
|
+
return fn(tx);
|
|
41
|
+
});
|
|
42
|
+
app.get("/api/v1/projects/:uid/assignees", async (c) => {
|
|
43
|
+
const p = await projects.get(c.req.param("uid"));
|
|
44
|
+
if (!p)
|
|
45
|
+
return fail(c, 404, "project_not_found", "No project with that uid.");
|
|
46
|
+
const owner = c.get("owner");
|
|
47
|
+
const session = c.get("session");
|
|
48
|
+
const asOwner = !!owner && owner.orgId === p.org_id;
|
|
49
|
+
if (!asOwner && !session) {
|
|
50
|
+
return fail(c, 401, "unauthorized", "Open your review link, or reload the page, and try again.");
|
|
51
|
+
}
|
|
52
|
+
// Emails are for the people who can route work, not for a client reading
|
|
53
|
+
// names: a link session sees who is in the pool, never the studio's
|
|
54
|
+
// addresses.
|
|
55
|
+
const mayAssign = asOwner || !!session?.perm.includes("thread:assign");
|
|
56
|
+
// A prefix match on name and email — the pool is single- to double-digit
|
|
57
|
+
// for this product's audience; a convenience, not a search feature.
|
|
58
|
+
const q = (c.req.query("q") ?? "").trim().slice(0, 80);
|
|
59
|
+
const limit = Math.min(Math.max(Number(c.req.query("limit")) || 100, 1), 200);
|
|
60
|
+
const rows = await inOrg(p.org_id, (tx) => tx `
|
|
61
|
+
SELECT r.id, r.name, r.email, r.kind, r.avatar_url,
|
|
62
|
+
(r.user_id IS NOT NULL) AS is_team,
|
|
63
|
+
${asOwner ? tx `(r.user_id = ${owner.userId}::uuid)` : tx `(r.id = ${session.sub}::uuid)`} AS is_me,
|
|
64
|
+
count(*) OVER () AS total
|
|
65
|
+
FROM reviewers r
|
|
66
|
+
WHERE r.project_id = ${p.id}::uuid
|
|
67
|
+
AND ${inPool(tx)}
|
|
68
|
+
${q ? tx `AND (r.name ILIKE ${q + "%"} OR r.email ILIKE ${q + "%"})` : tx ``}
|
|
69
|
+
-- The studio first (you assign to a colleague more often than to
|
|
70
|
+
-- anyone else), then most recently active, which puts the person who
|
|
71
|
+
-- just commented at the top of the list you opened because they did.
|
|
72
|
+
ORDER BY is_team DESC, r.last_seen_at DESC
|
|
73
|
+
LIMIT ${limit}
|
|
74
|
+
`);
|
|
75
|
+
c.header("Cache-Control", "private, max-age=30");
|
|
76
|
+
return c.json({
|
|
77
|
+
data: rows.map((r) => shapeReviewer(r, mayAssign)),
|
|
78
|
+
total: Number(rows[0]?.["total"] ?? 0),
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
/**
|
|
82
|
+
* Naming a person who has not written yet — DESIGN.md Q4's *+ Add person*,
|
|
83
|
+
* and the case D3 needs on day one: a question assigned to a client BEFORE
|
|
84
|
+
* that client has said anything.
|
|
85
|
+
*
|
|
86
|
+
* A PLACEHOLDER: `kind='guest'`, no secret, `invited_by` set. A name to
|
|
87
|
+
* assign to and not a credential — no exchange can ever authenticate as it.
|
|
88
|
+
*
|
|
89
|
+
* Dashboard only in v1. §6.4b also admits a session holding `thread:assign`,
|
|
90
|
+
* but `invited_by` is a `users.id` and a team-link session carries no user;
|
|
91
|
+
* refusing with a sentence beats inventing an inviter.
|
|
92
|
+
*/
|
|
93
|
+
app.post("/api/v1/projects/:uid/assignees", async (c) => {
|
|
94
|
+
const p = await projects.get(c.req.param("uid"));
|
|
95
|
+
if (!p)
|
|
96
|
+
return fail(c, 404, "project_not_found", "No project with that uid.");
|
|
97
|
+
const owner = c.get("owner");
|
|
98
|
+
const asOwner = !!owner && owner.orgId === p.org_id;
|
|
99
|
+
if (!asOwner) {
|
|
100
|
+
return fail(c, 403, "forbidden", "Adding a person to assign to is done from the dashboard.");
|
|
101
|
+
}
|
|
102
|
+
const body = (await c.req.json().catch(() => null));
|
|
103
|
+
const name = typeof body?.name === "string" ? body.name.trim().slice(0, 80) : "";
|
|
104
|
+
if (!name)
|
|
105
|
+
return fail(c, 400, "validation_failed", "Give the person a name.", { field: "name" });
|
|
106
|
+
const emailRaw = typeof body?.email === "string" ? body.email.trim().toLowerCase() : "";
|
|
107
|
+
if (emailRaw && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailRaw)) {
|
|
108
|
+
return fail(c, 400, "validation_failed", "That does not look like an email address.", { field: "email" });
|
|
109
|
+
}
|
|
110
|
+
const email = emailRaw || null;
|
|
111
|
+
const [row, created] = await inOrg(p.org_id, async (tx) => {
|
|
112
|
+
if (email) {
|
|
113
|
+
// The same person named twice is one person: an unerased row with
|
|
114
|
+
// that address — a placeholder, a link reviewer, a studio member —
|
|
115
|
+
// is returned rather than duplicated.
|
|
116
|
+
const [same] = await tx `
|
|
117
|
+
SELECT r.id, r.name, r.email, r.kind, r.avatar_url, (r.user_id IS NOT NULL) AS is_team,
|
|
118
|
+
(r.user_id = ${owner.userId}::uuid) AS is_me
|
|
119
|
+
FROM reviewers r
|
|
120
|
+
WHERE r.project_id = ${p.id}::uuid AND lower(r.email) = ${email} AND r.erased_at IS NULL
|
|
121
|
+
ORDER BY r.last_seen_at DESC
|
|
122
|
+
LIMIT 1
|
|
123
|
+
`;
|
|
124
|
+
if (same)
|
|
125
|
+
return [same, false];
|
|
126
|
+
}
|
|
127
|
+
const id = randomUUID();
|
|
128
|
+
const [made] = await tx `
|
|
129
|
+
INSERT INTO reviewers (id, org_id, project_id, kind, name, email, invited_by)
|
|
130
|
+
VALUES (${id}::uuid, ${p.org_id}::uuid, ${p.id}::uuid, 'guest', ${name}, ${email}, ${owner.userId}::uuid)
|
|
131
|
+
RETURNING id, name, email, kind, avatar_url, false AS is_team, false AS is_me
|
|
132
|
+
`;
|
|
133
|
+
return [made, true];
|
|
134
|
+
});
|
|
135
|
+
return c.json(shapeReviewer(row, true), created ? 201 : 200);
|
|
136
|
+
});
|
|
137
|
+
return app;
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=assignees.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assignees.js","sourceRoot":"","sources":["../../src/routes/assignees.ts"],"names":[],"mappings":"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"}
|
|
@@ -152,8 +152,8 @@ export function attachmentRoutes(deps) {
|
|
|
152
152
|
return app;
|
|
153
153
|
}
|
|
154
154
|
/** A9b: derived server-side from bucket presence, never probed by the widget. */
|
|
155
|
-
export async function features(sql, keys) {
|
|
156
|
-
const st = await storageStatus(sql, keys);
|
|
155
|
+
export async function features(sql, keys, env = process.env) {
|
|
156
|
+
const st = await storageStatus(sql, keys, env);
|
|
157
157
|
return { screenshots: st.connected, uploads: st.connected };
|
|
158
158
|
}
|
|
159
159
|
//# sourceMappingURL=attachments.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../src/routes/attachments.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../src/routes/attachments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 05 §9.3 — the JSON body cap, which was registered and enforced by nothing.
|
|
3
|
+
*
|
|
4
|
+
* `SNAGSET_MAX_BODY_BYTES` has been in the config registry since 08 §4 with a
|
|
5
|
+
* 1 MB default, cited by name in two specs, printed by `snagset env` — and read
|
|
6
|
+
* by no code at all. So every JSON route on this server would read whatever it
|
|
7
|
+
* was sent, and the only thing standing between an instance and a body large
|
|
8
|
+
* enough to exhaust its memory was the hosting platform's own proxy, which a
|
|
9
|
+
* self-hoster on their own box does not have.
|
|
10
|
+
*
|
|
11
|
+
* ── THE TWO NUMBERS, AND WHY ONLY ONE IS TUNABLE ──
|
|
12
|
+
*
|
|
13
|
+
* 05 §9.3 caps JSON at **256 KB** and says plainly it is *"not operator-
|
|
14
|
+
* tunable"*. 08 §4 caps binary routes at `SNAGSET_MAX_BODY_BYTES` (1 MB) and
|
|
15
|
+
* that one IS tunable. They are different limits for different reasons: the
|
|
16
|
+
* JSON cap protects the parser, and every JSON body this API accepts is small
|
|
17
|
+
* by construction — a comment, an anchor, a capture context whose breadcrumbs
|
|
18
|
+
* are already truncated server-side. An operator raising it is not configuring
|
|
19
|
+
* the product, they are disabling a bound the wire format does not need.
|
|
20
|
+
*
|
|
21
|
+
* Binary is different: images are ≤ 256 KB by construction, but the ceiling is
|
|
22
|
+
* the operator's because their proxy and their bucket are.
|
|
23
|
+
*
|
|
24
|
+
* ── WHY `Content-Length` AND NOT A COUNTING STREAM ──
|
|
25
|
+
*
|
|
26
|
+
* Rejecting on the declared length is the whole point: it costs nothing and it
|
|
27
|
+
* happens BEFORE the body is read. A stream that counts as it goes has already
|
|
28
|
+
* accepted the bytes it is measuring. A liar who declares 1 KB and sends 10 MB
|
|
29
|
+
* defeats the header check — and is then bounded by the platform's own limit
|
|
30
|
+
* and by the fact that `c.req.json()` throws on a truncated body, which is the
|
|
31
|
+
* same place an unbounded read would have failed anyway. The header check is
|
|
32
|
+
* the cheap 99%, not the security boundary; it exists so an honest client with
|
|
33
|
+
* a runaway payload gets a clean `413` instead of an OOM.
|
|
34
|
+
*/
|
|
35
|
+
/** 05 §9.3, and deliberately not `SNAGSET_MAX_BODY_BYTES`. */
|
|
36
|
+
export const JSON_BODY_CAP = 256 * 1024;
|
|
37
|
+
export function bodyLimit(binaryBytes) {
|
|
38
|
+
return async (c, next) => {
|
|
39
|
+
const method = c.req.method;
|
|
40
|
+
if (method === "GET" || method === "HEAD" || method === "OPTIONS")
|
|
41
|
+
return next();
|
|
42
|
+
const declared = Number(c.req.header("content-length") ?? "");
|
|
43
|
+
if (!Number.isFinite(declared) || declared <= 0)
|
|
44
|
+
return next();
|
|
45
|
+
const type = (c.req.header("content-type") ?? "").toLowerCase();
|
|
46
|
+
const isJson = type.includes("json") || type === "";
|
|
47
|
+
const cap = isJson ? JSON_BODY_CAP : binaryBytes();
|
|
48
|
+
if (declared > cap) {
|
|
49
|
+
return c.json({
|
|
50
|
+
error: {
|
|
51
|
+
code: "body_too_large",
|
|
52
|
+
message: `That request body is ${Math.round(declared / 1024)} KB; the limit here is ${Math.round(cap / 1024)} KB.`,
|
|
53
|
+
requestId: c.get("requestId"),
|
|
54
|
+
},
|
|
55
|
+
}, 413);
|
|
56
|
+
}
|
|
57
|
+
return next();
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=bodyLimit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bodyLimit.js","sourceRoot":"","sources":["../../src/routes/bodyLimit.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,8DAA8D;AAC9D,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,GAAG,IAAI,CAAC;AAExC,MAAM,UAAU,SAAS,CAAC,WAAyB;IACjD,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACvB,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC;QAC5B,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,IAAI,EAAE,CAAC;QAEjF,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,CAAC;YAAE,OAAO,IAAI,EAAE,CAAC;QAE/D,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAChE,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,KAAK,EAAE,CAAC;QACpD,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAEnD,IAAI,QAAQ,GAAG,GAAG,EAAE,CAAC;YACnB,OAAO,CAAC,CAAC,IAAI,CACX;gBACE,KAAK,EAAE;oBACL,IAAI,EAAE,gBAAgB;oBACtB,OAAO,EAAE,wBAAwB,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,0BAA0B,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM;oBAClH,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC;iBAC9B;aACF,EACD,GAAG,CACJ,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/routes/comments.js
CHANGED
|
@@ -3,6 +3,7 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
import { ID_PREFIX } from "../vendor/types/names.js";
|
|
4
4
|
import { csrfOk } from "./owner.js";
|
|
5
5
|
import { mcpAllows, scopeDenied, checkWriteBudget, budgetExhausted, recordAgentWrite, agentReviewer, } from "./mcpAuth.js";
|
|
6
|
+
import { studioReviewerId } from "../studioReviewers.js";
|
|
6
7
|
import { queueCommentNotice } from "../mail/notify.js";
|
|
7
8
|
function fail(c, status, code, message, details) {
|
|
8
9
|
return c.json({ error: { code, message, ...(details ? { details } : {}), requestId: c.get("requestId") } }, status);
|
|
@@ -22,7 +23,7 @@ export function commentRoutes(deps) {
|
|
|
22
23
|
if (!threadRow)
|
|
23
24
|
return fail(c, 404, "thread_not_found", "No such thread.");
|
|
24
25
|
const [thread] = await sql `
|
|
25
|
-
SELECT t.id, t.project_id, t.org_id, t.private, p.uid AS project_uid,
|
|
26
|
+
SELECT t.id, t.project_id, t.org_id, t.private, p.uid AS project_uid, p.status AS project_status,
|
|
26
27
|
t.key,
|
|
27
28
|
-- The page URL lives INSIDE the capture context, not in a column
|
|
28
29
|
-- of its own (05 §3.5). Two arrows, not one, so it arrives as
|
|
@@ -93,6 +94,13 @@ export function commentRoutes(deps) {
|
|
|
93
94
|
if (asAgent && !asOwner && thread.private) {
|
|
94
95
|
return fail(c, 403, "forbidden", "This thread is team-only. An agent cannot reply on it.");
|
|
95
96
|
}
|
|
97
|
+
// D91 — the widget switch. A paused site refuses the WIDGET's writes (05
|
|
98
|
+
// §4.9 layer 6, `project_paused`); the studio's own replies from the
|
|
99
|
+
// dashboard go on, because the switch is about the client's page, not
|
|
100
|
+
// about the team's triage of what already arrived.
|
|
101
|
+
if (asSession && !asOwner && thread.project_status !== "active") {
|
|
102
|
+
return fail(c, 403, "project_paused", "This site's widget is switched off. Its owner can switch it back on.");
|
|
103
|
+
}
|
|
96
104
|
// 07 §8.4's write budget, checked before the write. §8.6 mitigation 5.
|
|
97
105
|
if (asAgent && !asOwner && !asSession) {
|
|
98
106
|
const spent = await checkWriteBudget(sql, mcp.tokenId);
|
|
@@ -206,22 +214,17 @@ export function commentRoutes(deps) {
|
|
|
206
214
|
});
|
|
207
215
|
}
|
|
208
216
|
else {
|
|
217
|
+
// The owner's own reviewer row in this project — materialised on
|
|
218
|
+
// project and membership create since D90, found-or-created here for
|
|
219
|
+
// anything older (studioReviewers.ts).
|
|
209
220
|
const o = owner;
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
else {
|
|
218
|
-
reviewerId = randomUUID();
|
|
219
|
-
await tx `
|
|
220
|
-
INSERT INTO reviewers (id, org_id, project_id, kind, name, email, user_id)
|
|
221
|
-
VALUES (${reviewerId}::uuid, ${thread.org_id}::uuid, ${thread.project_id}::uuid, 'verified',
|
|
222
|
-
${o.name || o.email}, ${o.email}, ${o.userId}::uuid)
|
|
223
|
-
`;
|
|
224
|
-
}
|
|
221
|
+
reviewerId = await studioReviewerId(tx, {
|
|
222
|
+
orgId: thread.org_id,
|
|
223
|
+
projectId: thread.project_id,
|
|
224
|
+
userId: o.userId,
|
|
225
|
+
name: o.name,
|
|
226
|
+
email: o.email,
|
|
227
|
+
});
|
|
225
228
|
}
|
|
226
229
|
// Locked here, not read-then-trusted: `seq` and both counters are
|
|
227
230
|
// derived from THIS row under the lock, so two replies landing at once
|
|
@@ -244,6 +247,13 @@ export function commentRoutes(deps) {
|
|
|
244
247
|
version = version + 1,
|
|
245
248
|
updated_at = now()
|
|
246
249
|
WHERE id = ${thread.id}::uuid
|
|
250
|
+
`;
|
|
251
|
+
// The author's own counter (05 §3.4) — what puts a person who has only
|
|
252
|
+
// ever replied into the assignee pool (§6.4b), and floats whoever just
|
|
253
|
+
// wrote to the top of it.
|
|
254
|
+
await tx `
|
|
255
|
+
UPDATE reviewers SET comment_count = comment_count + 1, last_seen_at = now()
|
|
256
|
+
WHERE id = ${reviewerId}::uuid
|
|
247
257
|
`;
|
|
248
258
|
const [author] = await tx `
|
|
249
259
|
SELECT id, name, kind FROM reviewers WHERE id = ${reviewerId}::uuid
|
|
@@ -276,7 +286,7 @@ export function commentRoutes(deps) {
|
|
|
276
286
|
* whatever forwards, backs up or indexes it — for a comment the product
|
|
277
287
|
* promises is private. Not worth the surprise for a notification.
|
|
278
288
|
*/
|
|
279
|
-
if (!wantPrivate && deps.notifications) {
|
|
289
|
+
if (!wantPrivate && (await deps.notifications())) {
|
|
280
290
|
await queueCommentNotice(tx, {
|
|
281
291
|
orgId: thread.org_id,
|
|
282
292
|
projectId: thread.project_id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comments.js","sourceRoot":"","sources":["../../src/routes/comments.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAgB,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGjD,OAAO,EAAE,MAAM,EAAoB,MAAM,YAAY,CAAC;AAEtD,OAAO,EACL,SAAS,EACT,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAmCvD,SAAS,IAAI,CAAC,CAAM,EAAE,MAAc,EAAE,IAAY,EAAE,OAAe,EAAE,OAAiB;IACpF,OAAO,CAAC,CAAC,IAAI,CACX,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,EAAE,EAC5F,MAAa,CACd,CAAC;AACJ,CAAC;AAED,MAAM,IAAI,GAAG,iEAAiE,CAAC;AAC/E,SAAS,KAAK,CAAC,IAAa,EAAE,MAAc;IAC1C,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACxE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACrD,CAAC;
|
|
1
|
+
{"version":3,"file":"comments.js","sourceRoot":"","sources":["../../src/routes/comments.ts"],"names":[],"mappings":"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"}
|
package/dist/routes/cors.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HTTP_HEADER } from "../vendor/types/names.js";
|
|
2
|
+
import { isLocalOrigin, originMatchesDomain } from "../domain.js";
|
|
2
3
|
/**
|
|
3
4
|
* The request headers a widget may send — 05 §2.3's request rows, plus the two
|
|
4
5
|
* standard ones. Built from the registry rather than typed out, so adding a
|
|
@@ -136,7 +137,15 @@ export function corsForProjects(sql, lookup) {
|
|
|
136
137
|
const project = await lookup(uid).catch(() => null);
|
|
137
138
|
if (project) {
|
|
138
139
|
const patterns = await patternsFor(project.id);
|
|
139
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Three ways in (D92, 05 §4.3 as amended): an exact or wildcard
|
|
142
|
+
* origin from the allow-list; the site's DOMAIN — its host or any
|
|
143
|
+
* subdomain, any scheme, any port; and the developer's own machine
|
|
144
|
+
* (rule 4), which the spec allows by default and which had never been
|
|
145
|
+
* implemented. The domain rides the project cache, so a change lands
|
|
146
|
+
* within the same ten seconds as everything else about a project.
|
|
147
|
+
*/
|
|
148
|
+
const ok = isAllowed(patterns, origin) || originMatchesDomain(project.domain ?? "", origin) || isLocalOrigin(origin);
|
|
140
149
|
/**
|
|
141
150
|
* The verdict, stashed for the config route (05 §7.2's origin-aware
|
|
142
151
|
* body, D69). Computed HERE and not re-derived in the route because
|