@bongos/core 1.19.533 → 1.19.534

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.
Files changed (51) hide show
  1. package/.bongos-core.json +106 -46
  2. package/clients/bongos-client/index.d.ts +1 -1
  3. package/config/branding.neutral.json +3 -2
  4. package/docs/adr/0245-the-way-back-into-an-optional-step-is-that-step-s-own-answer-route.md +144 -0
  5. package/docs/adr/0246-catalog-membership-is-a-projection-not-a-federation-side-effect.md +96 -0
  6. package/docs/adr/0247-the-join-door-is-one-composed-answer-and-dark-matter-has-no-live-verdict-yet.md +202 -0
  7. package/docs/adr/README.md +4 -0
  8. package/docs/api/openapi.json +8 -1
  9. package/docs/api-reference.md +1 -1
  10. package/docs/copy-inventory.md +129 -126
  11. package/docs/copy-registry.json +189 -161
  12. package/docs/file-map.md +1 -0
  13. package/docs/module-api-changelog.md +14 -0
  14. package/modules/onboarding/module.json +1 -1
  15. package/modules/onboarding/routes/access-requests.js +21 -6
  16. package/modules/platform-identity/module.json +1 -1
  17. package/modules/platform-identity/project-catalog.js +202 -0
  18. package/modules/platform-identity/routes/my-projects.js +7 -2
  19. package/modules/platform-identity/routes/projects.js +6 -0
  20. package/modules/platform-identity/routes/sso.js +10 -0
  21. package/modules/provisioning/catalog-bridge.js +105 -0
  22. package/modules/provisioning/provisioning.js +28 -2
  23. package/modules/provisioning/routes/provisioning.js +45 -11
  24. package/modules/provisioning/tests/provisioning.mjs +11 -8
  25. package/modules/public-landing/public/assets/cosmos.css +15 -0
  26. package/modules/public-landing/public/projects.html +162 -3
  27. package/modules/ui-design/kit/serve.js +1 -0
  28. package/package-lock.json +2 -2
  29. package/package.json +1 -1
  30. package/scripts/gds/fitness.js +9 -0
  31. package/scripts/gds/provision-net.js +7 -0
  32. package/src/bongos/project-door.js +138 -0
  33. package/src/branding.js +8 -0
  34. package/src/module-api.js +12 -1
  35. package/tests/access_requests_invite.mjs +2 -2
  36. package/tests/application_lifecycle.mjs +1 -0
  37. package/tests/catalog_only_client.mjs +242 -0
  38. package/tests/currency_label.mjs +8 -5
  39. package/tests/module_api.mjs +1 -0
  40. package/tests/project_catalog_bridge.mjs +355 -0
  41. package/tests/project_catalog_routes.mjs +213 -0
  42. package/tests/project_join_door.mjs +166 -0
  43. package/tests/project_join_door_route.mjs +206 -0
  44. package/tests/project_modules_ui.mjs +318 -0
  45. package/tests/projects_hub.mjs +6 -1
  46. package/tests/projects_hub_pre_uat.mjs +2 -2
  47. package/tests/provision_settings_apply.mjs +6 -5
  48. package/tests/provisioning_module_selection.mjs +164 -3
  49. package/tests/provisioning_settings.mjs +10 -7
  50. package/tests/provisioning_settings_apply.mjs +3 -3
  51. package/tests/provisioning_settings_env.mjs +7 -4
@@ -164,7 +164,7 @@ await ta('owner reads DEFAULTS (public / apply) on a row with empty settings —
164
164
  reset();
165
165
  const r = await get('/provisioning/instances/18/settings');
166
166
  assert.equal(r.status, 200, r.raw);
167
- assert.deepEqual(r.body.settings, { platform_visibility: 'public', joinability: 'apply', visibility: 'public' });
167
+ assert.deepEqual(r.body.settings, { platform_visibility: 'public', joinability: 'apply', visibility: 'public', join_grant: 'full' });
168
168
  // the planet rides beside the settings (task 1003347): no default — null = the platform's own pick
169
169
  assert.deepEqual(r.body.planet, { planet_template: null, planet_accent: null, card_backdrop: null });
170
170
  assert.deepEqual(r.body.options, {
@@ -172,6 +172,9 @@ await ta('owner reads DEFAULTS (public / apply) on a row with empty settings —
172
172
  // the join door (task 1002324, ADR 0182 D1) — the third policy axis, offered
173
173
  // to the form the same way as its two neighbours
174
174
  visibility: ['public', 'private', 'stealth'],
175
+ // what a PUBLIC join grants (task 1002331, ADR 0247) — the fourth policy
176
+ // axis, offered to the form the same way
177
+ join_grant: ['view', 'apply', 'full'],
175
178
  planet_template: [...provisioning.PLANET_TEMPLATES], planet_accent: ['sea', 'terracotta', 'gold-soft', 'sun-lit', 'accent'],
176
179
  // the card's backdrop (task 1003352) is the same kind of key: platform-drawn,
177
180
  // closed list, no default, no push — so it rides the same vocab
@@ -184,14 +187,14 @@ await ta('a stored planet reads back; a name that left the vocabulary reads null
184
187
  const r = await get('/provisioning/instances/18/settings');
185
188
  assert.equal(r.status, 200, r.raw);
186
189
  assert.deepEqual(r.body.planet, { planet_template: 'bead-obsidian-tint', planet_accent: null, card_backdrop: null });
187
- assert.deepEqual(r.body.settings, { platform_visibility: 'public', joinability: 'apply', visibility: 'public' }, 'the planet is not a setting: the policy read is untouched');
190
+ assert.deepEqual(r.body.settings, { platform_visibility: 'public', joinability: 'apply', visibility: 'public', join_grant: 'full' }, 'the planet is not a setting: the policy read is untouched');
188
191
  });
189
192
 
190
193
  await ta('stored keys overlay the defaults', async () => {
191
194
  reset({ inst: { owner_builder_id: 7, slug: 'spike', status: 'active', settings: { joinability: 'invite_only' } } });
192
195
  const r = await get('/provisioning/instances/18/settings');
193
196
  assert.equal(r.status, 200, r.raw);
194
- assert.deepEqual(r.body.settings, { platform_visibility: 'public', joinability: 'invite_only', visibility: 'public' });
197
+ assert.deepEqual(r.body.settings, { platform_visibility: 'public', joinability: 'invite_only', visibility: 'public', join_grant: 'full' });
195
198
  });
196
199
 
197
200
  await ta('ANOTHER builder\'s GET → 404 (no existence leak)', async () => {
@@ -219,7 +222,7 @@ await ta('owner changes one key — merged, returned effective, audited api:self
219
222
  reset();
220
223
  const r = await patch('/provisioning/instances/18/settings', { joinability: 'open' });
221
224
  assert.equal(r.status, 200, r.raw);
222
- assert.deepEqual(r.body.saved, { platform_visibility: 'public', joinability: 'open', visibility: 'public' }, 'the SAVED value — the response never claims the instance runs it (task 1003140)');
225
+ assert.deepEqual(r.body.saved, { platform_visibility: 'public', joinability: 'open', visibility: 'public', join_grant: 'full' }, 'the SAVED value — the response never claims the instance runs it (task 1003140)');
223
226
  assert.equal(r.body.push.queued, true, 'an active co-tenant gets the live push (task 1003140)');
224
227
  assert.deepEqual(updateCalls, [{ id: 18, patch: { joinability: 'open' } }], 'exactly the sent key, nothing else');
225
228
  assert.equal(events.length, 1);
@@ -232,7 +235,7 @@ await ta('owner changes both keys in one call', async () => {
232
235
  reset();
233
236
  const r = await patch('/provisioning/instances/18/settings', { platform_visibility: 'gated', joinability: 'invite_only' });
234
237
  assert.equal(r.status, 200, r.raw);
235
- assert.deepEqual(r.body.saved, { platform_visibility: 'gated', joinability: 'invite_only', visibility: 'public' });
238
+ assert.deepEqual(r.body.saved, { platform_visibility: 'gated', joinability: 'invite_only', visibility: 'public', join_grant: 'full' });
236
239
  });
237
240
 
238
241
  await ta('the OWN-SCOPE WALL: another builder\'s PATCH → 404 and the write path is never reached', async () => {
@@ -271,7 +274,7 @@ await ta('the owner sets the door to stealth — stored, echoed effective, audit
271
274
  reset();
272
275
  const r = await patch('/provisioning/instances/18/settings', { visibility: 'stealth' });
273
276
  assert.equal(r.status, 200, r.raw);
274
- assert.deepEqual(r.body.saved, { platform_visibility: 'public', joinability: 'apply', visibility: 'stealth' });
277
+ assert.deepEqual(r.body.saved, { platform_visibility: 'public', joinability: 'apply', visibility: 'stealth', join_grant: 'full' });
275
278
  assert.deepEqual(updateCalls, [{ id: 18, patch: { visibility: 'stealth' } }], 'exactly the sent key, nothing else');
276
279
  assert.equal(events[0].actor, 'api:self');
277
280
  assert.match(events[0].detail, /visibility=stealth/);
@@ -331,7 +334,7 @@ await ta('the owner picks a planet: saved, echoed as `planet`, audited — and N
331
334
  const r = await patch('/provisioning/instances/18/settings', { planet_template: 'bead-obsidian-tint', planet_accent: 'sea' });
332
335
  assert.equal(r.status, 200, r.raw);
333
336
  assert.deepEqual(r.body.planet, { planet_template: 'bead-obsidian-tint', planet_accent: 'sea', card_backdrop: null });
334
- assert.deepEqual(r.body.saved, { platform_visibility: 'public', joinability: 'apply', visibility: 'public' }, 'the policy half is untouched');
337
+ assert.deepEqual(r.body.saved, { platform_visibility: 'public', joinability: 'apply', visibility: 'public', join_grant: 'full' }, 'the policy half is untouched');
335
338
  assert.deepEqual(updateCalls, [{ id: 18, patch: { planet_template: 'bead-obsidian-tint', planet_accent: 'sea' } }]);
336
339
  assert.equal(r.body.push.queued, false);
337
340
  assert.equal(r.body.push.restart, false, 'an active co-tenant would get a push for a POLICY change — never for the planet');
@@ -212,7 +212,7 @@ await ta('ACTIVE standalone: the row is written FIRST, then a settings-apply int
212
212
  reset();
213
213
  const r = await patch('/provisioning/instances/18/settings', { platform_visibility: 'gated' });
214
214
  assert.equal(r.status, 200, r.raw);
215
- assert.deepEqual(r.body.saved, { platform_visibility: 'gated', joinability: 'apply', visibility: 'public' }, 'the SAVED (requested) value — never `settings`, which a form would render as live');
215
+ assert.deepEqual(r.body.saved, { platform_visibility: 'gated', joinability: 'apply', visibility: 'public', join_grant: 'full' }, 'the SAVED (requested) value — never `settings`, which a form would render as live');
216
216
  assert.equal(r.body.settings, undefined);
217
217
  assert.deepEqual(updateCalls.map((c) => c.patch), [{ platform_visibility: 'gated' }]);
218
218
  assert.equal(updateCalls[0].enqueuedSoFar, 0, 'the runner reads the row — the write must precede the intent');
@@ -261,7 +261,7 @@ await ta('an enqueue that THROWS still answers 200 "saved" — never a 500 that
261
261
  enqueueThrows = new Error('db down');
262
262
  const r = await patch('/provisioning/instances/18/settings', { joinability: 'open' });
263
263
  assert.equal(r.status, 200, r.raw);
264
- assert.deepEqual(r.body.saved, { platform_visibility: 'public', joinability: 'open', visibility: 'public' });
264
+ assert.deepEqual(r.body.saved, { platform_visibility: 'public', joinability: 'open', visibility: 'public', join_grant: 'full' });
265
265
  assert.equal(updateCalls.length, 1, 'the write landed');
266
266
  receiptShape(r.body.push, { queued: false, restart: true, reachable: true });
267
267
  assert.match(r.body.push.message, /Saved, but the restart could not be queued/);
@@ -274,7 +274,7 @@ await ta('same values as already stored → no write, no event, no intent, "No c
274
274
  reset({ inst: { ...ACTIVE, settings: { platform_visibility: 'gated', joinability: 'open' } } });
275
275
  const r = await patch('/provisioning/instances/18/settings', { platform_visibility: 'gated', joinability: 'open' });
276
276
  assert.equal(r.status, 200, r.raw);
277
- assert.deepEqual(r.body.saved, { platform_visibility: 'gated', joinability: 'open', visibility: 'public' });
277
+ assert.deepEqual(r.body.saved, { platform_visibility: 'gated', joinability: 'open', visibility: 'public', join_grant: 'full' });
278
278
  assert.equal(updateCalls.length, 0, 'nothing written (no updated_at reshuffle)');
279
279
  assert.equal(events.length, 0, 'no "settings changed" event for a change that is not one');
280
280
  assert.equal(enqueueCalls.length, 0, 'a whole-form save must not bounce a live project');
@@ -57,7 +57,10 @@ t('a row with NO stored settings emits today\'s behavior EXPLICITLY, never nothi
57
57
  assert.equal(v.CLOUDBONGOS_JOINABILITY, 'apply');
58
58
  // the join door rides the same transport (task 1002324, ADR 0182 D1)
59
59
  assert.equal(v.CLOUDBONGOS_VISIBILITY, 'public');
60
- assert.equal(linesOf(provisioning.settingsEnvLines({}, 'X')).length, 3, 'every policy key always present');
60
+ // what a public join grants rides it too (task 1002331, ADR 0182 D6)
61
+ assert.equal(v.CLOUDBONGOS_JOIN_GRANT, 'full');
62
+ assert.equal(linesOf(provisioning.settingsEnvLines({}, 'X')).length,
63
+ Object.keys(provisioning.SETTINGS_VOCAB).length, 'every policy key always present');
61
64
  });
62
65
 
63
66
  t('an off-vocabulary stored value falls back to the default, never reaches the instance', () => {
@@ -96,11 +99,11 @@ t('it names the live push by the intent the runner actually dispatches (task 100
96
99
  });
97
100
 
98
101
  t('settingsEnvVars is the single source both legs derive from — same keys, same values as the lines', () => {
99
- const row = { settings: { platform_visibility: 'gated', joinability: 'open', visibility: 'stealth' } };
102
+ const row = { settings: { platform_visibility: 'gated', joinability: 'open', visibility: 'stealth', join_grant: 'view' } };
100
103
  const vars = provisioning.settingsEnvVars(row, 'CLOUDBONGOS');
101
- assert.deepEqual(vars, { CLOUDBONGOS_PLATFORM_VISIBILITY: 'gated', CLOUDBONGOS_JOINABILITY: 'open', CLOUDBONGOS_VISIBILITY: 'stealth' });
104
+ assert.deepEqual(vars, { CLOUDBONGOS_PLATFORM_VISIBILITY: 'gated', CLOUDBONGOS_JOINABILITY: 'open', CLOUDBONGOS_VISIBILITY: 'stealth', CLOUDBONGOS_JOIN_GRANT: 'view' });
102
105
  assert.deepEqual(varsOf(provisioning.settingsEnvLines(row, 'CLOUDBONGOS')), vars, 'the standup lines ARE the vars');
103
- assert.deepEqual(provisioning.settingsEnvVars({}, 'X'), { X_PLATFORM_VISIBILITY: 'public', X_JOINABILITY: 'apply', X_VISIBILITY: 'public' }, 'defaults, explicitly');
106
+ assert.deepEqual(provisioning.settingsEnvVars({}, 'X'), { X_PLATFORM_VISIBILITY: 'public', X_JOINABILITY: 'apply', X_VISIBILITY: 'public', X_JOIN_GRANT: 'full' }, 'defaults, explicitly');
104
107
  });
105
108
 
106
109
  console.log('\nwebEnvBody — the settings ride the real per-instance env file:');