@bongos/core 1.19.538 → 1.19.539
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 +30 -20
- package/docs/adr/0249-the-invite-step-is-the-done-panel-s-first-act-not-a-rail-step.md +115 -0
- package/docs/adr/README.md +2 -0
- package/docs/copy-inventory.md +131 -123
- package/docs/copy-registry.json +244 -160
- package/docs/module-api-changelog.md +2 -0
- package/modules/public-landing/public/assets/cosmos.css +18 -0
- package/modules/public-landing/public/projects.html +220 -1
- package/modules/public-landing/public/projects.states.json +1 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +1 -1
- package/tests/project_invite_ui.mjs +502 -0
- package/tests/projects_hub.mjs +1 -1
- package/tests/projects_hub_pre_uat.mjs +2 -2
|
@@ -1525,5 +1525,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1525
1525
|
landed since 1.19.536 with no explicit bump. run 33888812605. (task 1002620)
|
|
1526
1526
|
1.19.538 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1527
1527
|
landed since 1.19.537 with no explicit bump. run 33890191596. (task 1002620)
|
|
1528
|
+
1.19.539 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1529
|
+
landed since 1.19.538 with no explicit bump. run 33892948885. (task 1002620)
|
|
1528
1530
|
---------------------------------------------------------------------------
|
|
1529
1531
|
```
|
|
@@ -411,6 +411,16 @@ h1{font-family:var(--font-display);font-weight:700;font-size:clamp(24px,3.4vw,32
|
|
|
411
411
|
task 1003355 moved it to the project's own page, where the section's heading
|
|
412
412
|
is the disclosure and the row it left carries only its two doors. */
|
|
413
413
|
.attachNote{margin:8px 0 0;font-size:13px;line-height:1.45;color:var(--ink-faint);}
|
|
414
|
+
/* invites filed in this session (task 1002346): a hairline ledger under the pair
|
|
415
|
+
above — one line per person, the name in the body voice, what happened next to
|
|
416
|
+
it in the faint ink. Shared by the wizard's step and the project's own page. */
|
|
417
|
+
.inviteList{list-style:none;display:grid;margin:12px 0 0;padding:0;}
|
|
418
|
+
.inviteList:empty{display:none;}
|
|
419
|
+
.inviteList>li{display:grid;grid-template-columns:auto 1fr;gap:2px 10px;padding:10px 0 11px;border-top:1px solid var(--rule-soft);}
|
|
420
|
+
.inviteList>li:first-child{border-top:0;}
|
|
421
|
+
.inviteList>li::before{content:"";grid-column:1;grid-row:1;width:7px;height:7px;margin-top:7px;border-radius:50%;border:1px solid var(--accent);background:var(--bg);}
|
|
422
|
+
.inviteList b{grid-column:2;font-family:var(--font-body);font-weight:500;font-size:15px;color:var(--ink);}
|
|
423
|
+
.inviteList small{grid-column:2;font-family:var(--font-body);font-size:13px;font-weight:400;line-height:1.45;color:var(--ink-faint);}
|
|
414
424
|
.attachNote code{font-family:var(--font-mono);}
|
|
415
425
|
.projMsg{font-size:13.5px;font-weight:600;margin-top:10px;min-height:18px;color:var(--ink-soft);line-height:1.5;}
|
|
416
426
|
.projMsg.ok{color:var(--ok);}
|
|
@@ -1516,6 +1526,14 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
|
|
|
1516
1526
|
#view-new #wizSignin:hover{background:color-mix(in srgb,var(--ink) 88%,var(--bg));}
|
|
1517
1527
|
/* the error alert: the error ink between two hairlines, no plate */
|
|
1518
1528
|
#view-new .alert{background:none;border:0;border-top:1px solid var(--err);border-bottom:1px solid var(--err);border-radius:0;padding:12px 0;font-weight:400;}
|
|
1529
|
+
/* the invite step (task 1002346): the page's own input+button pair, wearing the
|
|
1530
|
+
wizard's hairline field rather than the plated one — the .attach width has to
|
|
1531
|
+
give way to the row's flex, or #view-new's own width:100% overflows it */
|
|
1532
|
+
#view-new .attach{gap:10px 14px;margin-top:14px;align-items:center;}
|
|
1533
|
+
#view-new .attach input[type=text]{width:auto;flex:1;min-width:220px;}
|
|
1534
|
+
#view-new .inviteBox{margin-top:34px;}
|
|
1535
|
+
#view-new .inviteBox .finePrint{margin-top:10px;}
|
|
1536
|
+
#view-new #wizInviteLater{margin-top:14px;}
|
|
1519
1537
|
/* one fold for the whole wizard: 840 is where the page's bands and ledger stack
|
|
1520
1538
|
(cosmos's createSplit collapse), so the rail folds with them, not before them */
|
|
1521
1539
|
@media (max-width:840px){
|
|
@@ -849,6 +849,27 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
849
849
|
</details>
|
|
850
850
|
</div>
|
|
851
851
|
|
|
852
|
+
<!-- live since task 1002346 (BV1.R27): the way to invite a builder AFTER
|
|
853
|
+
creation. The wizard's invite step is skippable, and — the mModules
|
|
854
|
+
argument beside it — an optional step is a default rather than a dead end
|
|
855
|
+
only if there is a way back. The SAME widget on the SAME route
|
|
856
|
+
(POST /projects/invite), admitted here by the project's own ownership
|
|
857
|
+
(ADR 0248); like its neighbour above, nothing is pushed to the project and
|
|
858
|
+
nothing restarts, so there is no "what your project runs" read-back line. -->
|
|
859
|
+
<div class="card mSec" id="mInvite">
|
|
860
|
+
<h2>Invite a builder</h2>
|
|
861
|
+
<div id="mInv">
|
|
862
|
+
<p class="finePrint">By GitHub username. They get an invitation to accept from their own projects page, and they need to have signed in here at least once.</p>
|
|
863
|
+
<div class="attach">
|
|
864
|
+
<label class="vh" for="mInviteWho">GitHub username</label>
|
|
865
|
+
<input type="text" id="mInviteWho" placeholder="GitHub username" autocomplete="off" autocapitalize="off" spellcheck="false" maxlength="200">
|
|
866
|
+
<button type="button" class="btn small" data-invite-send="mInvite">Invite</button>
|
|
867
|
+
</div>
|
|
868
|
+
<ul class="inviteList" id="mInviteList"></ul>
|
|
869
|
+
<div class="projMsg" id="mInviteMsg" role="status" aria-live="polite"></div>
|
|
870
|
+
</div>
|
|
871
|
+
</div>
|
|
872
|
+
|
|
852
873
|
<!-- the planet picker (task 1003347; direction record §16, v1 the bands): the
|
|
853
874
|
owner's own choice of how the platform draws this project — the sky,
|
|
854
875
|
the list, this page's head and the card read the same row. The pick is
|
|
@@ -1203,7 +1224,29 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1203
1224
|
<!-- the STABLE live region: the strip inside is rebuilt every 5s tick,
|
|
1204
1225
|
and a live region that is itself replaced never announces -->
|
|
1205
1226
|
<div id="liveProgress" role="status" aria-live="polite"></div>
|
|
1206
|
-
|
|
1227
|
+
<!-- the invite step (task 1002346, BV1.R27) — OPTIONAL, and it stands HERE
|
|
1228
|
+
rather than in the numbered rail because an invite needs a project that
|
|
1229
|
+
already exists: the catalog row a client_id resolves is projected at
|
|
1230
|
+
create (ADR 0246), so before the Create step there is nothing to invite
|
|
1231
|
+
anyone TO. The whole reasoning is ADR 0249. Hidden until the created
|
|
1232
|
+
record carries an id and a slug; the script is the only thing that
|
|
1233
|
+
shows it. -->
|
|
1234
|
+
<div class="inviteBox" id="wizInvite" hidden>
|
|
1235
|
+
<h2 class="stepsHead">Invite builders</h2>
|
|
1236
|
+
<p class="finePrint">Optional — invite people by GitHub username. Each one gets an invitation to accept from their own projects page, and they need to have signed in here at least once. Skip this and invite anyone later; nothing about your project waits on it.</p>
|
|
1237
|
+
<div class="attach">
|
|
1238
|
+
<label class="vh" for="wizInviteWho">GitHub username</label>
|
|
1239
|
+
<input type="text" id="wizInviteWho" placeholder="GitHub username" autocomplete="off" autocapitalize="off" spellcheck="false" maxlength="200">
|
|
1240
|
+
<button type="button" class="btn small" data-invite-send="wizInvite">Invite</button>
|
|
1241
|
+
<button type="button" class="btn small ghost" data-invite-skip="wizInvite">Skip</button>
|
|
1242
|
+
</div>
|
|
1243
|
+
<ul class="inviteList" id="wizInviteList"></ul>
|
|
1244
|
+
<div class="projMsg" id="wizInviteMsg" role="status" aria-live="polite"></div>
|
|
1245
|
+
</div>
|
|
1246
|
+
<!-- the way back, the doneModules idiom: an optional step is a default
|
|
1247
|
+
rather than a dead end only if the panel names where it lives after -->
|
|
1248
|
+
<p class="samePath" id="wizInviteLater"></p>
|
|
1249
|
+
<h2 class="stepsHead" id="doneStepsHead">Your steps — the parts only you can play</h2>
|
|
1207
1250
|
<ol class="humanSteps" id="humanSteps"></ol>
|
|
1208
1251
|
<!-- the modules step was optional (task 1002342, BV1.R23) — one line
|
|
1209
1252
|
saying where it lives afterwards, so a skip is never a dead end -->
|
|
@@ -4098,6 +4141,14 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
4098
4141
|
skippable, so this panel names where it lives afterwards rather than
|
|
4099
4142
|
leaving an owner to go looking for it. */
|
|
4100
4143
|
$('doneModules').innerHTML = 'Add or remove modules later in ' + projectPageLink('the project’s settings') + '.';
|
|
4144
|
+
/* the invite step (task 1002346, BV1.R27). The CREATED RECORD is the proof
|
|
4145
|
+
the widget needs and the only one this panel has: no signed-in identity,
|
|
4146
|
+
no filed instance or no slug ⇒ nothing to invite to, and the step stays
|
|
4147
|
+
hidden. It is deliberately NOT keyed on instanceId, which startProgress
|
|
4148
|
+
sets only AFTER this runs. */
|
|
4149
|
+
var created = createdRecord();
|
|
4150
|
+
setInviteSlug('wizInvite', (me && created && created.id && created.slug) ? created.slug : '');
|
|
4151
|
+
$('wizInviteLater').innerHTML = 'You can invite builders any time from ' + projectPageLink('the project’s own page') + '.';
|
|
4101
4152
|
renderSteps();
|
|
4102
4153
|
}
|
|
4103
4154
|
|
|
@@ -5280,6 +5331,11 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5280
5331
|
progress survives the tick because only a different project re-renders it */
|
|
5281
5332
|
renderPick(inst);
|
|
5282
5333
|
renderCardPick(inst);
|
|
5334
|
+
/* invite a builder (task 1002346): fetch-free too — the widget needs only the
|
|
5335
|
+
slug, and the API is the wall. This read is own-scoped and 404s for anyone
|
|
5336
|
+
who is not the owner, so reaching renderManage at all IS the ownership
|
|
5337
|
+
answer the widget does not ask for itself. */
|
|
5338
|
+
setInviteSlug('mInvite', inst.slug || '');
|
|
5283
5339
|
/* the page names its project — the heading holds #mName; and in the tab */
|
|
5284
5340
|
document.title = (inst.slug ? inst.slug + ' — ' : '') + BASE_TITLE;
|
|
5285
5341
|
/* the card's own story, on the project's own page (task 1003232): the
|
|
@@ -6692,6 +6748,169 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6692
6748
|
goToManage(m.getAttribute('data-manage'));
|
|
6693
6749
|
});
|
|
6694
6750
|
|
|
6751
|
+
/* ══ invite a builder (task 1002346, BV1.R27; goal 1000046 criterion C6, ADR 0249)
|
|
6752
|
+
ONE widget, mounted twice: the creation wizard's optional invite step (on the
|
|
6753
|
+
done panel — an invite needs a project that already exists) and the project's
|
|
6754
|
+
own page, so a skipped step is a default rather than a dead end.
|
|
6755
|
+
|
|
6756
|
+
THE ROUTE IS USED AS IT STANDS: POST /projects/invite { client_id, github_login },
|
|
6757
|
+
where client_id IS the project's slug — ADR 0246 projects a control-plane project
|
|
6758
|
+
onto the catalog under its slug — and the project's owner is admitted by ADR 0248's
|
|
6759
|
+
ownership arm. Nothing here is a second gate; the server owns the answer.
|
|
6760
|
+
|
|
6761
|
+
TWO QUESTIONS THIS WIDGET DELIBERATELY DOES NOT ASK: "is anyone signed in" and
|
|
6762
|
+
"is this their project". Each caller already knows, and hands over a slug or
|
|
6763
|
+
nothing — which is the ONE door in (setInviteSlug). A widget that re-derived
|
|
6764
|
+
ownership on the page would be a second, weaker copy of a server rule.
|
|
6765
|
+
|
|
6766
|
+
THE REFUSALS ARE SAID IN THE ROUTE'S OWN VOCABULARY, and the 403 is said
|
|
6767
|
+
CAREFULLY: it is byte-identical whether the project is real, stealth, or absent
|
|
6768
|
+
(ADR 0248), so this page may never word it as "it exists but you may not" — that
|
|
6769
|
+
would hand back the membership oracle the route equalised away.
|
|
6770
|
+
|
|
6771
|
+
IT NEVER BLOCKS CREATION. The project is filed before the step is shown at all;
|
|
6772
|
+
a refusal leaves the box exactly as it was, ready to try again.
|
|
6773
|
+
|
|
6774
|
+
WHAT IT PUBLISHES OF AN INVITEE is the 201 row's own membership_kind and the
|
|
6775
|
+
login the inviter typed — never the github_id the row carries, which belongs to
|
|
6776
|
+
someone who has accepted nothing yet.
|
|
6777
|
+
Proof: ../../../tests/project_invite_ui.mjs ══ */
|
|
6778
|
+
|
|
6779
|
+
var INVITE_MOUNTS = {
|
|
6780
|
+
wizInvite: { who: 'wizInviteWho', list: 'wizInviteList', msg: 'wizInviteMsg', after: 'doneStepsHead' },
|
|
6781
|
+
mInvite: { who: 'mInviteWho', list: 'mInviteList', msg: 'mInviteMsg', after: null }
|
|
6782
|
+
};
|
|
6783
|
+
var inviteSlug = {}; /* mount id → the slug it invites to; '' means no step at all */
|
|
6784
|
+
var inviteRows = {}; /* mount id → the invites filed on THIS project this session */
|
|
6785
|
+
var inviteBusy = {}; /* a send is in flight — the mount is inert until it answers */
|
|
6786
|
+
|
|
6787
|
+
/* setInviteSlug(id, slug) — the only door in. A falsy slug closes the mount, which
|
|
6788
|
+
is how "signed out", "not the owner" and "no project yet" all reach this widget.
|
|
6789
|
+
A DIFFERENT slug starts clean: the pending lines belonged to the project they
|
|
6790
|
+
were filed on, and following them to another one would be a lie. */
|
|
6791
|
+
function setInviteSlug(id, slug) {
|
|
6792
|
+
var m = INVITE_MOUNTS[id];
|
|
6793
|
+
if (!m) return;
|
|
6794
|
+
var next = slug || '';
|
|
6795
|
+
if (inviteSlug[id] !== next) {
|
|
6796
|
+
inviteSlug[id] = next;
|
|
6797
|
+
inviteRows[id] = [];
|
|
6798
|
+
inviteSay(id, '', '');
|
|
6799
|
+
}
|
|
6800
|
+
var box = $(id);
|
|
6801
|
+
if (box) box.hidden = !next;
|
|
6802
|
+
renderInviteRows(id);
|
|
6803
|
+
}
|
|
6804
|
+
|
|
6805
|
+
/* One line per invite this session filed: the login the inviter typed, and the
|
|
6806
|
+
WORD THE ROW ITSELF CARRIES — so a membership_kind other than 'pending' shows
|
|
6807
|
+
up here rather than being papered over as one. */
|
|
6808
|
+
function renderInviteRows(id) {
|
|
6809
|
+
var m = INVITE_MOUNTS[id];
|
|
6810
|
+
var list = m && $(m.list);
|
|
6811
|
+
if (!list) return;
|
|
6812
|
+
list.innerHTML = (inviteRows[id] || []).map(function (r) {
|
|
6813
|
+
var said = r.kind === 'pending' ? 'invited — waiting for them to accept.' : r.kind;
|
|
6814
|
+
return '<li><b>' + esc(r.login) + '</b><small>' + esc(said) + '</small></li>';
|
|
6815
|
+
}).join('');
|
|
6816
|
+
}
|
|
6817
|
+
|
|
6818
|
+
function inviteSay(id, text, cls, isHtml) {
|
|
6819
|
+
var m = INVITE_MOUNTS[id];
|
|
6820
|
+
var el = m && $(m.msg);
|
|
6821
|
+
if (!el) return;
|
|
6822
|
+
el.className = 'projMsg' + (cls ? ' ' + cls : '');
|
|
6823
|
+
if (isHtml) el.innerHTML = text; else el.textContent = text;
|
|
6824
|
+
}
|
|
6825
|
+
|
|
6826
|
+
/* inviteRefusal(code, status, message) — the route's refusals as one sentence
|
|
6827
|
+
each. PURE, so the harness reads it directly. A code this page does not know
|
|
6828
|
+
falls through to the SERVER'S own words rather than an invented reason. */
|
|
6829
|
+
function inviteRefusal(code, status, message) {
|
|
6830
|
+
if (status === 404 && code === 'account_not_found') return 'That builder hasn’t signed in here yet — ask them to sign in once, then invite them.';
|
|
6831
|
+
if (status === 404 && code === 'unknown_project') return 'This project isn’t on the map yet — give it a moment and try again.';
|
|
6832
|
+
/* deliberately says nothing about the project: see the header */
|
|
6833
|
+
if (status === 403) return 'This account can’t invite people here.';
|
|
6834
|
+
return message || 'The invitation couldn’t be sent — try again in a moment.';
|
|
6835
|
+
}
|
|
6836
|
+
|
|
6837
|
+
/* doInvite(id) — one builder, one request. */
|
|
6838
|
+
function doInvite(id) {
|
|
6839
|
+
var m = INVITE_MOUNTS[id];
|
|
6840
|
+
var slug = inviteSlug[id] || '';
|
|
6841
|
+
if (!m || !slug || inviteBusy[id]) return;
|
|
6842
|
+
var field = $(m.who);
|
|
6843
|
+
var login = String((field && field.value) || '').replace(/^\s+|\s+$/g, '');
|
|
6844
|
+
if (!login) {
|
|
6845
|
+
inviteSay(id, 'Type the GitHub username of the person you want to invite.', 'err');
|
|
6846
|
+
if (field && field.focus) field.focus();
|
|
6847
|
+
return;
|
|
6848
|
+
}
|
|
6849
|
+
inviteBusy[id] = true;
|
|
6850
|
+
inviteSay(id, 'Sending the invitation…', '');
|
|
6851
|
+
fetch(API + '/projects/invite', {
|
|
6852
|
+
method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' },
|
|
6853
|
+
body: JSON.stringify({ client_id: slug, github_login: login })
|
|
6854
|
+
}).then(function (res) {
|
|
6855
|
+
return jsonOrEmpty(res).then(function (data) {
|
|
6856
|
+
inviteBusy[id] = false;
|
|
6857
|
+
if (res.ok) {
|
|
6858
|
+
if (data && data.invited && data.row) {
|
|
6859
|
+
inviteRows[id] = (inviteRows[id] || []).concat([{ login: login, kind: String(data.row.membership_kind || 'pending') }]);
|
|
6860
|
+
renderInviteRows(id);
|
|
6861
|
+
if (field) { field.value = ''; if (field.focus) field.focus(); }
|
|
6862
|
+
inviteSay(id, login + ' is invited — they accept it from their own projects page.', 'ok');
|
|
6863
|
+
return;
|
|
6864
|
+
}
|
|
6865
|
+
/* 200 { invited: false, alreadyMember: true } — not a failure, and not a
|
|
6866
|
+
pending invite either: they are already on the project */
|
|
6867
|
+
if (field) field.value = '';
|
|
6868
|
+
inviteSay(id, login + ' is already on this project.', 'ok');
|
|
6869
|
+
return;
|
|
6870
|
+
}
|
|
6871
|
+
if (res.status === 401) { inviteSay(id, signInAgain(), 'err', true); return; }
|
|
6872
|
+
var e = apiError(data, res.status);
|
|
6873
|
+
inviteSay(id, inviteRefusal(e.code, res.status, e.message), 'err');
|
|
6874
|
+
});
|
|
6875
|
+
}).catch(function () {
|
|
6876
|
+
inviteBusy[id] = false;
|
|
6877
|
+
inviteSay(id, 'Couldn’t reach the platform — check your connection and try again.', 'err');
|
|
6878
|
+
});
|
|
6879
|
+
}
|
|
6880
|
+
|
|
6881
|
+
/* skipInvite(id) — the step steps aside having sent NOTHING. Focus moves on
|
|
6882
|
+
rather than being stranded inside the box that just went away. */
|
|
6883
|
+
function skipInvite(id) {
|
|
6884
|
+
var m = INVITE_MOUNTS[id];
|
|
6885
|
+
if (!m || inviteBusy[id]) return;
|
|
6886
|
+
var box = $(id);
|
|
6887
|
+
if (box) box.hidden = true;
|
|
6888
|
+
inviteSay(id, '', '');
|
|
6889
|
+
var after = m.after && $(m.after);
|
|
6890
|
+
if (after && after.focus) { after.setAttribute('tabindex', '-1'); after.focus(); }
|
|
6891
|
+
}
|
|
6892
|
+
|
|
6893
|
+
/* One delegated pair per mount: the buttons name their own mount, so the two
|
|
6894
|
+
surfaces share every line of behaviour above without sharing state. */
|
|
6895
|
+
Object.keys(INVITE_MOUNTS).forEach(function (id) {
|
|
6896
|
+
var box = $(id);
|
|
6897
|
+
if (box) {
|
|
6898
|
+
box.addEventListener('click', function (ev) {
|
|
6899
|
+
var t = ev.target.closest && ev.target.closest('[data-invite-send],[data-invite-skip]');
|
|
6900
|
+
if (!t) return;
|
|
6901
|
+
if (t.hasAttribute('data-invite-skip')) skipInvite(id); else doInvite(id);
|
|
6902
|
+
});
|
|
6903
|
+
}
|
|
6904
|
+
var field = $(INVITE_MOUNTS[id].who);
|
|
6905
|
+
if (field) {
|
|
6906
|
+
field.addEventListener('keydown', function (ev) {
|
|
6907
|
+
if (ev.key !== 'Enter') return;
|
|
6908
|
+
ev.preventDefault();
|
|
6909
|
+
doInvite(id);
|
|
6910
|
+
});
|
|
6911
|
+
}
|
|
6912
|
+
});
|
|
6913
|
+
|
|
6695
6914
|
/* ── boot: who am I → my instances ────────────────────────────────────── */
|
|
6696
6915
|
|
|
6697
6916
|
function renderWho() {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"in-manage-address": { "auth": true, "url": "/projects?view=manage&id=101", "expect": { "visible": ["#mAddress", "#mAddress h2", "#mDom .plain", "#mDomName", "#mDom [data-m-attach]", "#mDomWhat summary"], "count": [["#mDom .attach input[type=text]", 1], ["#mDom .attach button", 1]] } },
|
|
23
23
|
"in-manage-planet": { "auth": true, "url": "/projects?view=manage&id=101", "expect": { "visible": ["#mPlanetPick", "#mPick .pick .pickPreview .worldOrb .disc", "#mPick [data-pick-save]", "#mPick [data-pick-reset]", "#mPickWhat summary"], "count": [["#mPick .pickCells[role=\"radiogroup\"]", 3], ["#mPick .pickCell", 10], ["#mPick .pickCell.on", 3], ["#mPick .pickSw", 5], ["#mPick .pickSw.on", 1]], "attr": [["#mPick .pickPreview .worldOrb", "data-template", "pair-obsidian-tint"], ["#mOrb .worldOrb", "data-template", "pair-obsidian-tint"]], "checked": "#mPick input[name=\"mPickAccent\"][value=\"sea\"]", "text": [["#mPlanet .v small", "your choice."]] } },
|
|
24
24
|
"in-manage-card": { "auth": true, "url": "/projects?view=manage&id=101", "expect": { "visible": ["#mCard", "#mCardPick .pick .pickPreview .cardBand", "#mCardPick [data-card-save]", "#mCardWhat summary"], "count": [["#mCardPick .pickCells[role=\"radiogroup\"]", 1], ["#mCardPick .pickCell", 5], ["#mCardPick .pickCell.on", 1]], "checked": "#mCardPick input[name=\"mCardBackdrop\"][value=\"\"]", "attr": [["#mCardPick .cardBand", "data-own", ""]] } },
|
|
25
|
+
"in-manage-invite": { "auth": true, "url": "/projects?view=manage&id=101", "expect": { "visible": ["#mInvite", "#mInvite h2", "#mInviteWho", "#mInvite [data-invite-send]"] } },
|
|
25
26
|
"in-manage-snag": { "auth": true, "url": "/projects?view=manage&id=103", "expect": { "visible": ["#mMain", "#mErr", "#mActs"], "hidden": ["#mAddress"], "text": [["#mName", "tidepool"]] } },
|
|
26
27
|
"in-manage-gone": { "auth": true, "url": "/projects?view=manage&id=104", "expect": { "visible": ["#mMain", "#mActs"], "hidden": ["#mErr", "#mAddress"], "text": [["#mName", "old-lantern"]] } },
|
|
27
28
|
"in-manage-missing": { "auth": true, "url": "/projects?view=manage&id=999", "expect": { "visible": ["#mMissing"], "hidden": ["#mMain", "#mGate"] } },
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.539",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.539",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.539",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/src/module-api.js
CHANGED
|
@@ -49,7 +49,7 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
49
49
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
50
50
|
// the entry to that file. Look for a version's history there, not here.
|
|
51
51
|
// ---------------------------------------------------------------------------
|
|
52
|
-
const CORE_VERSION = '1.19.
|
|
52
|
+
const CORE_VERSION = '1.19.539'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
53
53
|
|
|
54
54
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
55
55
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|