@bongos/core 1.19.512 → 1.19.514
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/.bongos-core.json +77 -42
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +2 -0
- package/clients/bongos-client/index.cjs +2 -0
- package/clients/bongos-client/index.d.ts +3 -0
- package/clients/bongos-client/index.mjs +2 -0
- package/docs/adr/0182-project-visibility-two-axes.md +2 -0
- package/docs/adr/0239-a-recommendation-is-a-table-that-carries-its-own-reason.md +177 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +41 -4
- package/docs/api-reference.md +4 -3
- package/docs/copy-inventory.md +36 -35
- package/docs/copy-registry.json +93 -74
- package/docs/module-api-changelog.md +4 -0
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +2 -0
- package/modules/hall-ui/public/hall-kit.js +309 -0
- package/modules/hall-ui/public/hall-render.js +10 -38
- package/modules/hall-ui/public/profile-charts.js +58 -65
- package/modules/hall-ui/public/profile.css +9 -18
- package/modules/hall-ui/public/style.css +101 -15
- package/modules/hall-ui/public/watch.css +6 -14
- package/modules/hall-ui/public/watch.html +1 -1
- package/modules/hall-ui/public/watch.js +139 -54
- package/modules/platform-identity/platform-identity.js +24 -20
- package/modules/platform-identity/projects-feed.js +142 -0
- package/modules/platform-identity/routes/projects.js +27 -8
- package/modules/platform-identity/tests/platform-identity.mjs +9 -5
- package/modules/provisioning/recommendations.js +260 -0
- package/modules/provisioning/routes/provisioning.js +29 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +1 -1
- package/tests/hall_audit.mjs +84 -0
- package/tests/hall_kit_charts.mjs +328 -0
- package/tests/hall_profile_world.mjs +27 -6
- package/tests/helpers.mjs +161 -0
- package/tests/joinability_invite_only.mjs +10 -2
- package/tests/projects_hub.mjs +5 -1
- package/tests/provisioning_recommendations.mjs +448 -0
- package/tests/task_detail_ui.mjs +1 -153
- package/tests/visible_projects_feed.mjs +320 -0
- package/tests/watch_applications_queue.mjs +381 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
// tests/visible_projects_feed.mjs — the VISIBLE-PROJECTS FEED (task 1002325, BV1.R06;
|
|
2
|
+
// ADR 0182 D2a / D3).
|
|
3
|
+
//
|
|
4
|
+
// The feed is the in-repo lever the orbs page reads, and this file pins the three
|
|
5
|
+
// things R06 changed about it:
|
|
6
|
+
//
|
|
7
|
+
// 1. WHO IS ON THE MAP. The predicate stops being `featured = true` and becomes
|
|
8
|
+
// `status = 'active' AND publishable` — the publish gate R16 (task 1002335)
|
|
9
|
+
// already published as SQL. Dark matter (no name, or no description) is excluded
|
|
10
|
+
// SERVER-SIDE, so it never reaches a client that could choose to draw it.
|
|
11
|
+
// `featured` keeps a job — it RANKS among rows already on the map (D3) — and
|
|
12
|
+
// loses the one it should never have had, gating presence at all.
|
|
13
|
+
// 2. WHAT EACH ORB IS. Every row carries `state` (public|private|stealth) and `orb`
|
|
14
|
+
// (planet|star|black-hole), derived from ONE mapping so the feed, the Explore
|
|
15
|
+
// view (R07) and the static orbs site (R09) cannot each invent their own.
|
|
16
|
+
// 3. WHAT A BLACK HOLE MAY SAY. Stealth exposes EXISTENCE, never IDENTITY (D2a), and
|
|
17
|
+
// the redaction is at the boundary because the orbs page is a static site outside
|
|
18
|
+
// this repo and could not be trusted to redact even in principle.
|
|
19
|
+
//
|
|
20
|
+
// The stealth assertions are the security spec: they assert ABSENT FIELDS, not absent
|
|
21
|
+
// pixels, which is what R10 (task 1002329) then demonstrates live.
|
|
22
|
+
//
|
|
23
|
+
// DB-free: a recording fake pool for the SQL, and the REAL router driven with a fake
|
|
24
|
+
// req/res for the route composition.
|
|
25
|
+
//
|
|
26
|
+
// Run: node --test --test-reporter=tap tests/visible_projects_feed.mjs
|
|
27
|
+
|
|
28
|
+
import assert from 'node:assert/strict';
|
|
29
|
+
import { test } from 'node:test';
|
|
30
|
+
import { createRequire } from 'node:module';
|
|
31
|
+
|
|
32
|
+
const require = createRequire(import.meta.url);
|
|
33
|
+
const pi = require('../modules/platform-identity/platform-identity.js');
|
|
34
|
+
const feed = require('../modules/platform-identity/projects-feed.js');
|
|
35
|
+
const projectStats = require('../modules/platform-identity/project-stats.js');
|
|
36
|
+
const projectsRoutes = require('../modules/platform-identity/routes/projects.js');
|
|
37
|
+
|
|
38
|
+
// A recording fake pool: returns queued responses in order, records every call.
|
|
39
|
+
function mockPool(responses = []) {
|
|
40
|
+
const calls = [];
|
|
41
|
+
let i = 0;
|
|
42
|
+
return {
|
|
43
|
+
calls,
|
|
44
|
+
query: async (text, params) => {
|
|
45
|
+
calls.push({ text: String(text), params: params || [] });
|
|
46
|
+
return responses[i++] || { rows: [], rowCount: 0 };
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ── the mapping: one state, one orb (ADR 0182 D1) ────────────────────────────
|
|
52
|
+
|
|
53
|
+
test('orbForState maps each stored state to the orb ADR 0182 D1 names for it', () => {
|
|
54
|
+
// Arrange / Act / Assert — the mapping IS the assertion, and the criterion
|
|
55
|
+
// (`proj-visibility-states`) locks these three words.
|
|
56
|
+
assert.equal(feed.orbForState('public'), 'planet');
|
|
57
|
+
assert.equal(feed.orbForState('private'), 'star');
|
|
58
|
+
assert.equal(feed.orbForState('stealth'), 'black-hole');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('the orb mapping is reached through the module surface, so no caller keeps a copy', () => {
|
|
62
|
+
// R07 (Explore) and R09 (the static orbs site) both render these; a second copy of
|
|
63
|
+
// the mapping is how two surfaces end up drawing one project two ways.
|
|
64
|
+
assert.equal(pi.orbForState, feed.orbForState);
|
|
65
|
+
assert.deepEqual({ ...pi.ORB_BY_STATE }, { public: 'planet', private: 'star', stealth: 'black-hole' });
|
|
66
|
+
assert.ok(Object.isFrozen(pi.ORB_BY_STATE), 'a caller must not be able to re-map an orb at runtime');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('an unrecognised state falls back to the ANONYMOUS orb, never the identified one', () => {
|
|
70
|
+
// Fail-closed, because this mapping is a privacy boundary. A fourth enum value added
|
|
71
|
+
// later and not mapped here would otherwise ship its project's name as a planet; the
|
|
72
|
+
// safe degradation is to draw it as a black hole and say nothing about it.
|
|
73
|
+
assert.equal(feed.orbForState('members-only'), 'black-hole');
|
|
74
|
+
assert.equal(feed.orbForState(undefined), 'black-hole');
|
|
75
|
+
assert.equal(feed.orbForState(null), 'black-hole');
|
|
76
|
+
assert.equal(feed.orbForState(''), 'black-hole');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// ── the wire shape: identified for two states, anonymous for the third ───────
|
|
80
|
+
|
|
81
|
+
const PUBLIC_ROW = Object.freeze({
|
|
82
|
+
origin: 'https://mercury.example', name: 'Mercury', tagline: 'A weather sim.',
|
|
83
|
+
art_url: 'https://mercury.example/art.png', type: 'game', template: 'blob-tint',
|
|
84
|
+
created_at: '2026-08-01T00:00:00Z', visibility: 'public',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('feedShapeFor keeps a public project fully identified and marks it a planet', () => {
|
|
88
|
+
// Arrange
|
|
89
|
+
const row = { ...PUBLIC_ROW };
|
|
90
|
+
|
|
91
|
+
// Act
|
|
92
|
+
const wire = feed.feedShapeFor(row);
|
|
93
|
+
|
|
94
|
+
// Assert
|
|
95
|
+
assert.equal(wire.state, 'public');
|
|
96
|
+
assert.equal(wire.orb, 'planet');
|
|
97
|
+
assert.equal(wire.name, 'Mercury');
|
|
98
|
+
assert.equal(wire.origin, 'https://mercury.example');
|
|
99
|
+
assert.equal(wire.tagline, 'A weather sim.');
|
|
100
|
+
assert.equal(wire.art_url, 'https://mercury.example/art.png');
|
|
101
|
+
assert.equal(wire.created_at, '2026-08-01T00:00:00Z');
|
|
102
|
+
assert.ok(!('visibility' in wire), 'the stored column is spent deriving `state`; it never doubles it on the wire');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('a private project stays IDENTIFIED — a star is visible, only its door is closed', () => {
|
|
106
|
+
// ADR 0182 D2, inherited unchanged from ADR 0112's private goals: private means
|
|
107
|
+
// visible-but-closed. Redacting a star would be the bug, not the safe choice.
|
|
108
|
+
const wire = feed.feedShapeFor({ ...PUBLIC_ROW, visibility: 'private' });
|
|
109
|
+
assert.equal(wire.state, 'private');
|
|
110
|
+
assert.equal(wire.orb, 'star');
|
|
111
|
+
assert.equal(wire.name, 'Mercury');
|
|
112
|
+
assert.equal(wire.origin, 'https://mercury.example');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('a stealth project ships NO identifying field — every one of them is null', () => {
|
|
116
|
+
// Arrange — a fully populated row, so nothing is absent by accident.
|
|
117
|
+
const row = { ...PUBLIC_ROW, visibility: 'stealth' };
|
|
118
|
+
|
|
119
|
+
// Act
|
|
120
|
+
const wire = feed.feedShapeFor(row);
|
|
121
|
+
|
|
122
|
+
// Assert — ADR 0182 D2a names these four explicitly as fields that must not leave
|
|
123
|
+
// the server. Asserted BY NAME so the ADR's own list is executable.
|
|
124
|
+
for (const field of ['origin', 'name', 'tagline', 'art_url']) {
|
|
125
|
+
assert.equal(wire[field], null, `D2a: a stealth project's ${field} must not leave the server`);
|
|
126
|
+
}
|
|
127
|
+
// …and the general rule the four are only examples of: existence, never identity.
|
|
128
|
+
for (const [key, value] of Object.entries(wire)) {
|
|
129
|
+
if (key === 'state' || key === 'orb') continue;
|
|
130
|
+
assert.equal(value, null, `a black hole says nothing but that it is there — ${key} leaked`);
|
|
131
|
+
}
|
|
132
|
+
assert.equal(wire.state, 'stealth');
|
|
133
|
+
assert.equal(wire.orb, 'black-hole');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('the wire never DROPS a key for a stealth row — it nulls it', () => {
|
|
137
|
+
// The feed's standing contract (the NULL_STATS shape): a value that could not be
|
|
138
|
+
// answered is null, never absent, because JSON.stringify drops the absent one and a
|
|
139
|
+
// client reading `p.builders` would then see `undefined` on some rows and null on
|
|
140
|
+
// others. Redaction must not become the one exception to that.
|
|
141
|
+
const wire = feed.feedShapeFor({ ...PUBLIC_ROW, visibility: 'stealth' });
|
|
142
|
+
for (const key of Object.keys(PUBLIC_ROW)) {
|
|
143
|
+
if (key === 'visibility') continue;
|
|
144
|
+
assert.ok(key in wire, `${key} must still be a KEY on the wire, carrying null`);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('a field the redactor has never seen is anonymous by DEFAULT, not by being listed', () => {
|
|
149
|
+
// The property that keeps this correct as the feed grows: the stealth shape is an
|
|
150
|
+
// ALLOW-list of what may be said (state, orb), not a deny-list of what must be
|
|
151
|
+
// hidden. A column added to the feed next year is redacted without anyone
|
|
152
|
+
// remembering to add it here — which is the only version of this that stays true.
|
|
153
|
+
const wire = feed.feedShapeFor({
|
|
154
|
+
...PUBLIC_ROW, visibility: 'stealth', owner_avatar: 'https://x/a.png', faces: ['https://x/b.png'],
|
|
155
|
+
some_field_invented_after_this_test: 'leak',
|
|
156
|
+
});
|
|
157
|
+
assert.equal(wire.owner_avatar, null);
|
|
158
|
+
assert.deepEqual(wire.faces, null);
|
|
159
|
+
assert.equal(wire.some_field_invented_after_this_test, null);
|
|
160
|
+
assert.deepEqual(Object.keys(wire).filter((k) => wire[k] !== null).sort(), ['orb', 'state']);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('a black hole carries no stable key that yesterday\'s feed could be diffed against', () => {
|
|
164
|
+
// Deliberate: any per-project identifier that survives a state change (a row id, a
|
|
165
|
+
// slug, a hash of the origin) lets an observer who kept yesterday's public feed name
|
|
166
|
+
// the project that went stealth today. Black holes are interchangeable on purpose.
|
|
167
|
+
const a = feed.feedShapeFor({ ...PUBLIC_ROW, id: '7', visibility: 'stealth' });
|
|
168
|
+
const b = feed.feedShapeFor({ ...PUBLIC_ROW, id: '9', origin: 'https://venus.example', visibility: 'stealth' });
|
|
169
|
+
assert.deepEqual(a, b, 'two stealth projects must be indistinguishable on the wire');
|
|
170
|
+
assert.equal(a.id, null);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// ── who is on the map: the predicate (ADR 0182 D3) ───────────────────────────
|
|
174
|
+
|
|
175
|
+
test('listPublicProjects excludes dark matter using the SHARED publish gate, not a copy', async () => {
|
|
176
|
+
// Arrange
|
|
177
|
+
const pool = mockPool([{ rows: [] }]);
|
|
178
|
+
|
|
179
|
+
// Act
|
|
180
|
+
await pi.listPublicProjects({ pool });
|
|
181
|
+
|
|
182
|
+
// Assert
|
|
183
|
+
const sql = pool.calls[0].text;
|
|
184
|
+
assert.ok(sql.includes(pi.publishableSql()),
|
|
185
|
+
'the gate rides in the WHERE, from the one SQL definition R16 published for this feed');
|
|
186
|
+
assert.match(sql, /status = 'active'/, 'a deprovisioned project is off the map too (D3)');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('featuring no longer GATES presence — it only ranks among projects already on the map', async () => {
|
|
190
|
+
// ADR 0182 D3, and the behaviour change the whole goal turns on: an owner who sets
|
|
191
|
+
// their project public used to see nothing happen until an Archon featured them.
|
|
192
|
+
const pool = mockPool([{ rows: [] }]);
|
|
193
|
+
await pi.listPublicProjects({ pool });
|
|
194
|
+
const sql = pool.calls[0].text;
|
|
195
|
+
const where = (sql.match(/WHERE([\s\S]*?)ORDER BY/i) || [, ''])[1];
|
|
196
|
+
assert.doesNotMatch(where, /featured/, 'featured must not appear in the predicate at all');
|
|
197
|
+
assert.match(sql, /ORDER BY featured DESC/, 'it keeps the job D3 leaves it: ranking');
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test('the public feed is CAPPED — the swap to self-service removed the human bound on its size', async () => {
|
|
201
|
+
// Until R06 the listing was bounded by an Archon choosing to feature a project. Making
|
|
202
|
+
// it `publishable` replaced that bound with self-service, on a route that is public,
|
|
203
|
+
// CORS-open, unauthenticated AND fans out one outbound stats fetch per row. The cap is
|
|
204
|
+
// the cheap half of that trade; pagination is its own task.
|
|
205
|
+
const pool = mockPool([{ rows: [] }]);
|
|
206
|
+
await pi.listPublicProjects({ pool });
|
|
207
|
+
assert.match(pool.calls[0].text, new RegExp(`LIMIT ${feed.PUBLIC_FEED_MAX_PROJECTS}\\b`));
|
|
208
|
+
assert.equal(Number.isInteger(feed.PUBLIC_FEED_MAX_PROJECTS), true,
|
|
209
|
+
'the cap is interpolated into SQL, so it must be a literal integer and never a caller value');
|
|
210
|
+
assert.ok(feed.PUBLIC_FEED_MAX_PROJECTS > 0);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('listPublicProjects projects `visibility`, the column the orb is derived from', async () => {
|
|
214
|
+
// Without it every row would fall to the anonymous default and the whole map would
|
|
215
|
+
// go dark — silently, and in the safe direction, which is exactly why it needs a test.
|
|
216
|
+
const pool = mockPool([{ rows: [] }]);
|
|
217
|
+
await pi.listPublicProjects({ pool });
|
|
218
|
+
const sel = (pool.calls[0].text.match(/SELECT([\s\S]*?)FROM/i) || [, ''])[1];
|
|
219
|
+
assert.match(sel, /\bvisibility\b/);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test('listPublicProjects stays a PUBLIC projection — no owner, no status, no bookkeeping', async () => {
|
|
223
|
+
// Inherited from the feed this replaced, and re-asserted rather than assumed: the
|
|
224
|
+
// route is rank:public and CORS-open, so SELECT * must never sneak in behind it.
|
|
225
|
+
const pool = mockPool([{ rows: [] }]);
|
|
226
|
+
await pi.listPublicProjects({ pool });
|
|
227
|
+
const sql = pool.calls[0].text;
|
|
228
|
+
const sel = (sql.match(/SELECT([\s\S]*?)FROM/i) || [, ''])[1];
|
|
229
|
+
assert.ok(sel.trim(), 'the projection is an explicit SELECT list');
|
|
230
|
+
assert.doesNotMatch(sel, /\*/, 'no star projection on a public route');
|
|
231
|
+
assert.doesNotMatch(sel, /owner_github_id/, 'public projection must not leak owner id');
|
|
232
|
+
assert.doesNotMatch(sel, /updated_at/, 'curation noise — it may sort, it may never project');
|
|
233
|
+
assert.doesNotMatch(sel, /\bdescription\b/,
|
|
234
|
+
'description is the publish BAR, not a card field — it is spent in the predicate');
|
|
235
|
+
assert.match(sel, /created_at/, 'the LISTED date stays public');
|
|
236
|
+
assert.match(sel, /\btype\b/, 'the declared type stays public (task 1002772)');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// ── the route: the composition, executed ─────────────────────────────────────
|
|
240
|
+
|
|
241
|
+
// The real router, driven with a fake req/res. `pi.listPublicProjects` and the stats
|
|
242
|
+
// enrichment are stubbed so the test needs neither Postgres nor the network — what is
|
|
243
|
+
// under test is the ROUTE's composition, not either dependency.
|
|
244
|
+
async function callFeaturedFeed(rows) {
|
|
245
|
+
const router = projectsRoutes();
|
|
246
|
+
const layer = router.stack.find((l) => l.route && l.route.path === '/projects/featured'
|
|
247
|
+
&& l.route.methods.get);
|
|
248
|
+
assert.ok(layer, 'GET /projects/featured must still be a route on this router');
|
|
249
|
+
|
|
250
|
+
const askedFor = [];
|
|
251
|
+
const savedList = pi.listPublicProjects;
|
|
252
|
+
const savedStats = projectStats.getProjectStats;
|
|
253
|
+
pi.listPublicProjects = async () => rows;
|
|
254
|
+
projectStats.getProjectStats = async (origins) => { askedFor.push(...origins); return {}; };
|
|
255
|
+
try {
|
|
256
|
+
let body = null;
|
|
257
|
+
const res = {
|
|
258
|
+
json: (b) => { body = b; },
|
|
259
|
+
fail: (code, detail) => { throw new Error(`route failed: ${code} ${JSON.stringify(detail)}`); },
|
|
260
|
+
};
|
|
261
|
+
await layer.route.stack[0].handle({}, res, () => {});
|
|
262
|
+
return { body, askedFor };
|
|
263
|
+
} finally {
|
|
264
|
+
pi.listPublicProjects = savedList;
|
|
265
|
+
projectStats.getProjectStats = savedStats;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
test('the feed renders all three visible states, each with its orb', async () => {
|
|
270
|
+
// Arrange
|
|
271
|
+
const rows = [
|
|
272
|
+
{ ...PUBLIC_ROW, visibility: 'public' },
|
|
273
|
+
{ ...PUBLIC_ROW, origin: 'https://venus.example', name: 'Venus', visibility: 'private' },
|
|
274
|
+
{ ...PUBLIC_ROW, origin: 'https://pluto.example', name: 'Pluto', visibility: 'stealth' },
|
|
275
|
+
];
|
|
276
|
+
|
|
277
|
+
// Act
|
|
278
|
+
const { body } = await callFeaturedFeed(rows);
|
|
279
|
+
|
|
280
|
+
// Assert
|
|
281
|
+
assert.equal(body.projects.length, 3);
|
|
282
|
+
assert.deepEqual(body.projects.map((p) => p.state), ['public', 'private', 'stealth']);
|
|
283
|
+
assert.deepEqual(body.projects.map((p) => p.orb), ['planet', 'star', 'black-hole']);
|
|
284
|
+
assert.equal(body.projects[0].name, 'Mercury');
|
|
285
|
+
assert.equal(body.projects[1].name, 'Venus');
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test('the stealth row leaves the ROUTE anonymous — redaction survives the enrichment', async () => {
|
|
289
|
+
// The one that matters: the route spreads stats, planet choices and a scheme-checked
|
|
290
|
+
// art_url onto each row AFTER the domain read, so a redaction applied any earlier
|
|
291
|
+
// would be silently undone here.
|
|
292
|
+
const { body } = await callFeaturedFeed([{ ...PUBLIC_ROW, visibility: 'stealth' }]);
|
|
293
|
+
const wire = body.projects[0];
|
|
294
|
+
for (const field of ['origin', 'name', 'tagline', 'art_url']) {
|
|
295
|
+
assert.equal(wire[field], null, `D2a: ${field} must not leave the server`);
|
|
296
|
+
}
|
|
297
|
+
const said = Object.keys(wire).filter((k) => wire[k] !== null);
|
|
298
|
+
assert.deepEqual(said.sort(), ['orb', 'state']);
|
|
299
|
+
assert.ok(Object.keys(wire).length > 5, 'and it is a full-width row, not a stub — no key was dropped');
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test('a stealth project\'s own API is never asked about its builders', async () => {
|
|
303
|
+
// The enrichment reads each project's public leaderboard for builder counts, owner
|
|
304
|
+
// and up to five AVATARS. Handing a stealth origin to it would both leak identity
|
|
305
|
+
// onto the wire and tell that project it was being enumerated.
|
|
306
|
+
const { askedFor } = await callFeaturedFeed([
|
|
307
|
+
{ ...PUBLIC_ROW, visibility: 'public' },
|
|
308
|
+
{ ...PUBLIC_ROW, origin: 'https://pluto.example', visibility: 'stealth' },
|
|
309
|
+
]);
|
|
310
|
+
assert.deepEqual(askedFor, ['https://mercury.example']);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
test('dark matter never reaches the route at all — the exclusion is in SQL', async () => {
|
|
314
|
+
// Stated as a structural fact rather than a behaviour, because that IS the decision:
|
|
315
|
+
// a client-side filter would ship the husk to anyone who opened the network tab.
|
|
316
|
+
const src = require('node:fs').readFileSync(
|
|
317
|
+
new URL('../modules/platform-identity/projects-feed.js', import.meta.url), 'utf8');
|
|
318
|
+
const fn = (src.match(/async function listPublicProjects[\s\S]*?\n\}/) || [''])[0];
|
|
319
|
+
assert.ok(fn.includes('publishableSql()'), 'the gate is applied in the query, not after it');
|
|
320
|
+
});
|