@bongos/core 1.19.515 → 1.19.516
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 +48 -28
- package/clients/bongos-client/index.d.ts +2 -2
- package/docs/adr/0240-a-skipped-module-picker-stores-nothing-and-resolves-the-bundle-on-read.md +157 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +12 -3
- package/docs/api-reference.md +1 -1
- package/docs/copy-inventory.md +224 -218
- package/docs/copy-registry.json +357 -291
- package/docs/module-api-changelog.md +2 -0
- package/modules/provisioning/migrations/provisioning_014_module_selection.sql +60 -0
- package/modules/provisioning/provisioning.js +24 -4
- package/modules/provisioning/routes/provisioning.js +59 -2
- package/modules/provisioning/starter-bundles.js +91 -0
- package/modules/public-landing/public/assets/cosmos.css +1 -1
- package/modules/public-landing/public/projects.html +230 -35
- package/modules/public-landing/public/projects.probes.json +2 -2
- package/modules/public-landing/public/projects.states.json +5 -4
- package/modules/ui-design/kit/serve.js +32 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +1 -1
- package/tests/projects_hub.mjs +72 -7
- package/tests/projects_hub_module_picker.mjs +369 -0
- package/tests/provisioning_module_selection.mjs +286 -0
- package/tests/provisioning_starter_bundles.mjs +131 -0
|
@@ -328,6 +328,16 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
328
328
|
.tpill:hover::before{border-color:var(--ink-faint);}
|
|
329
329
|
.tpill[aria-pressed=true]{background:none;border-color:var(--rule-soft);color:var(--ink);}
|
|
330
330
|
.tpill[aria-pressed=true]::before{background:var(--accent);border-color:var(--accent);outline:4px solid color-mix(in srgb,var(--accent) 22%,transparent);}
|
|
331
|
+
/* the module picker (task 1002339) wears the SAME band as the type and team
|
|
332
|
+
questions — one hairline cell per choice, the dot on the chosen ones. The only
|
|
333
|
+
difference is that a module carries a one-line summary under its name, so its
|
|
334
|
+
cells stack full width and align to the top instead of centring on one line. */
|
|
335
|
+
#modCards{grid-template-columns:1fr;}
|
|
336
|
+
#modCards .tpill{align-items:flex-start;border-left:0;padding-left:0;border-top:1px solid var(--rule-soft);}
|
|
337
|
+
#modCards .tpill:first-child{border-top:0;}
|
|
338
|
+
#modCards .tpill::before{margin-top:5px;}
|
|
339
|
+
#modCards .tpill b{display:block;font-weight:500;}
|
|
340
|
+
#modCards .tpill small{display:block;font-family:var(--font-body);font-size:14px;font-weight:400;color:var(--ink-soft);line-height:1.5;max-width:62ch;margin-top:3px;}
|
|
331
341
|
.typeCard{border:0;border-radius:0;padding:4px 0 0;margin-bottom:6px;background:none;}
|
|
332
342
|
.typeCard b{display:block;font-family:var(--font-body);font-size:15px;font-weight:500;color:var(--ink);margin-bottom:4px;}
|
|
333
343
|
.typeCard p{font-size:14px;font-weight:400;color:var(--ink-soft);line-height:1.5;max-width:62ch;}
|
|
@@ -877,9 +887,10 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
877
887
|
<li class="now" data-step="1" aria-current="step"><span class="snum">1</span><span class="slbl">Name</span></li>
|
|
878
888
|
<li data-step="2"><span class="snum">2</span><span class="slbl">Type</span></li>
|
|
879
889
|
<li data-step="3"><span class="snum">3</span><span class="slbl">About</span></li>
|
|
880
|
-
<li data-step="4"><span class="snum">4</span><span class="slbl">
|
|
881
|
-
<li data-step="5"><span class="snum">5</span><span class="slbl">
|
|
882
|
-
<li data-step="6"><span class="snum">6</span><span class="slbl">
|
|
890
|
+
<li data-step="4"><span class="snum">4</span><span class="slbl">Modules</span></li>
|
|
891
|
+
<li data-step="5"><span class="snum">5</span><span class="slbl">Home</span></li>
|
|
892
|
+
<li data-step="6"><span class="snum">6</span><span class="slbl">Address</span></li>
|
|
893
|
+
<li data-step="7"><span class="snum">7</span><span class="slbl">Create</span></li>
|
|
883
894
|
</ol>
|
|
884
895
|
|
|
885
896
|
<div class="createSplit">
|
|
@@ -993,11 +1004,41 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
993
1004
|
</form>
|
|
994
1005
|
</section>
|
|
995
1006
|
|
|
996
|
-
<!-- step 4 ·
|
|
1007
|
+
<!-- step 4 · modules (task 1002339, BV1.R20) — the picker. Skippable
|
|
1008
|
+
like About, and for the same reason: an owner who has met none of
|
|
1009
|
+
these modules should not have to audit a list to get a project.
|
|
1010
|
+
What Skip means here is spelled out on the panel and is TRUE by
|
|
1011
|
+
construction — the request omits the field, and the server resolves
|
|
1012
|
+
the same starter bundle this panel preselected (ADR 0237), so the
|
|
1013
|
+
set is the one they were shown either way.
|
|
1014
|
+
|
|
1015
|
+
The always-on core is stated as a clause, never as a row of dead
|
|
1016
|
+
toggles: sixteen disabled controls read as choices the owner failed
|
|
1017
|
+
to make, when in fact they were never choices. -->
|
|
1018
|
+
<section class="panel" id="panel-modules" aria-labelledby="mod-h">
|
|
1019
|
+
<h1 id="mod-h">What it comes with</h1>
|
|
1020
|
+
<p class="lede">Optional — skip it and you get the suggested set below. These are extras on top of everything every project already has, and you can switch any of them on or off later.</p>
|
|
1021
|
+
<form data-next="5">
|
|
1022
|
+
<div class="wizField"><span id="modLbl">Extras for this project</span>
|
|
1023
|
+
<div class="typeCards" id="modCards" role="group" aria-labelledby="modLbl"></div>
|
|
1024
|
+
</div>
|
|
1025
|
+
<div class="reassure" id="modCoreNote"></div>
|
|
1026
|
+
<div class="hint" id="h-modules"> </div>
|
|
1027
|
+
<div class="nav">
|
|
1028
|
+
<button type="button" class="btn ghost" data-back="3">← Back</button>
|
|
1029
|
+
<span class="navPair">
|
|
1030
|
+
<button type="button" class="btn ghost" id="skipModules">Skip for now</button>
|
|
1031
|
+
<button type="submit" class="btn">Next →</button>
|
|
1032
|
+
</span>
|
|
1033
|
+
</div>
|
|
1034
|
+
</form>
|
|
1035
|
+
</section>
|
|
1036
|
+
|
|
1037
|
+
<!-- step 5 · home (repo + mode) -->
|
|
997
1038
|
<section class="panel" id="panel-home" aria-labelledby="home-h">
|
|
998
1039
|
<h1 id="home-h">Where the code lives</h1>
|
|
999
1040
|
<p class="lede">Every project is a real GitHub repository that you own. Start from a clean slate, or bring a repo you already have.</p>
|
|
1000
|
-
<form data-next="
|
|
1041
|
+
<form data-next="6">
|
|
1001
1042
|
<div class="choices" role="group" aria-label="Starting point">
|
|
1002
1043
|
<button type="button" class="choice" id="c-green" aria-pressed="true">
|
|
1003
1044
|
<!-- token-built, not platform orange — public-landing/ ships wholesale to
|
|
@@ -1056,13 +1097,13 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1056
1097
|
</div>
|
|
1057
1098
|
<div class="hint" id="h-home"> </div>
|
|
1058
1099
|
<div class="nav">
|
|
1059
|
-
<button type="button" class="btn ghost" data-back="
|
|
1100
|
+
<button type="button" class="btn ghost" data-back="4">← Back</button>
|
|
1060
1101
|
<button type="submit" class="btn">Next →</button>
|
|
1061
1102
|
</div>
|
|
1062
1103
|
</form>
|
|
1063
1104
|
</section>
|
|
1064
1105
|
|
|
1065
|
-
<!-- step
|
|
1106
|
+
<!-- step 6 · address — a CONFIRMATION, not a form (task 1002704: every project
|
|
1066
1107
|
gets its slug subdomain assigned up front). The domainless default was a
|
|
1067
1108
|
proven dead-end (QA walk, task 1002702).
|
|
1068
1109
|
Three choices, all legible without opening anything (task 1002898 N8):
|
|
@@ -1074,7 +1115,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1074
1115
|
<section class="panel" id="panel-address" aria-labelledby="addr-h">
|
|
1075
1116
|
<h1 id="addr-h">Its address in the sky</h1>
|
|
1076
1117
|
<p class="lede">The handle names your project on the platform — and its address comes with it. Once it stands up, a few GitHub steps (the next screen walks you through them) switch sign-in on.</p>
|
|
1077
|
-
<form data-next="
|
|
1118
|
+
<form data-next="7">
|
|
1078
1119
|
<label class="wizField"><span>Project handle</span>
|
|
1079
1120
|
<input type="text" id="f-slug" placeholder="mercury" autocomplete="off" maxlength="40" spellcheck="false">
|
|
1080
1121
|
<div class="hint" id="h-slug"> </div>
|
|
@@ -1096,20 +1137,20 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1096
1137
|
</label>
|
|
1097
1138
|
</div>
|
|
1098
1139
|
<div class="nav">
|
|
1099
|
-
<button type="button" class="btn ghost" data-back="
|
|
1140
|
+
<button type="button" class="btn ghost" data-back="5">← Back</button>
|
|
1100
1141
|
<button type="submit" class="btn">Next →</button>
|
|
1101
1142
|
</div>
|
|
1102
1143
|
</form>
|
|
1103
1144
|
</section>
|
|
1104
1145
|
|
|
1105
|
-
<!-- step
|
|
1146
|
+
<!-- step 7 · review + create -->
|
|
1106
1147
|
<section class="panel" id="panel-review" aria-labelledby="rev-h">
|
|
1107
1148
|
<h1 id="rev-h">Ready to roll</h1>
|
|
1108
1149
|
<p class="lede">One tap files the request and the platform starts standing your project up — server, database, address. Here's the beat you're about to drop:</p>
|
|
1109
1150
|
<ul class="review" id="reviewList"></ul>
|
|
1110
1151
|
<div class="alert" id="createAlert" role="alert"></div>
|
|
1111
1152
|
<div class="nav">
|
|
1112
|
-
<button type="button" class="btn ghost" data-back="
|
|
1153
|
+
<button type="button" class="btn ghost" data-back="6">← Back</button>
|
|
1113
1154
|
<button type="button" class="btn big" id="doCreate">Create my project</button>
|
|
1114
1155
|
</div>
|
|
1115
1156
|
<p class="finePrint">This files the request; the control plane does the real standup and nothing touches servers from this page. You'll get the few steps only you can do right after.</p>
|
|
@@ -2690,11 +2731,16 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
2690
2731
|
reinterpreted — a stale step 4 meant Address under v1 and Home under v3,
|
|
2691
2732
|
and replaying it would land on the wrong panel with the rail already
|
|
2692
2733
|
marking the skipped ones done. Bump this key on EVERY renumbering. */
|
|
2693
|
-
var DRAFT_KEY = 'cb-create-draft-
|
|
2734
|
+
var DRAFT_KEY = 'cb-create-draft-v4';
|
|
2694
2735
|
var CREATED_KEY = 'cb-created-instance';
|
|
2695
2736
|
/* `description` + `teamShape` are the optional About step (task 1002334) —
|
|
2696
2737
|
both legitimately stay empty, and empty is what the create request omits. */
|
|
2697
|
-
|
|
2738
|
+
/* `modules` is the picker's answer (task 1002339) and is THREE-VALUED like
|
|
2739
|
+
the column behind it: null = not answered (the preselect is shown and the
|
|
2740
|
+
request omits the field), [] = the owner switched them all off, [...] =
|
|
2741
|
+
what they picked. Never seed it with the preselect — that would store an
|
|
2742
|
+
answer nobody gave. */
|
|
2743
|
+
var state = { step: 0, name: '', projType: null, description: '', teamShape: null, modules: null, mode: 'greenfield', owner: '', ownerTouched: false, repo: '', repoTouched: false, slug: '', slugTouched: false, domain: '', noAddress: false };
|
|
2698
2744
|
try { Object.assign(state, JSON.parse(sessionStorage.getItem(DRAFT_KEY) || '{}')); } catch (e) {}
|
|
2699
2745
|
var authKnown = false; /* flips true when the boot's /me settles, either way */
|
|
2700
2746
|
|
|
@@ -2708,7 +2754,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
2708
2754
|
state.name = handedName.trim().slice(0, 80);
|
|
2709
2755
|
state.slug = deriveSlug(state.name); state.slugTouched = false;
|
|
2710
2756
|
state.repo = deriveRepo(state.name); state.repoTouched = false;
|
|
2711
|
-
state.step = state.step >= 1 && state.step <=
|
|
2757
|
+
state.step = state.step >= 1 && state.step <= 7 ? state.step : 1;
|
|
2712
2758
|
try { sessionStorage.setItem(DRAFT_KEY, JSON.stringify(state)); } catch (e) {}
|
|
2713
2759
|
}
|
|
2714
2760
|
} catch (e) {}
|
|
@@ -2900,17 +2946,18 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
2900
2946
|
}
|
|
2901
2947
|
|
|
2902
2948
|
/* ── step navigation ──
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
all). About
|
|
2906
|
-
|
|
2949
|
+
Eight steps since task 1002339: gate 0 · name 1 · type 2 · about 3 ·
|
|
2950
|
+
modules 4 · home 5 · address 6 · review 7 · done 8 (the rail shows 1-7;
|
|
2951
|
+
done marks it all). About and Modules are the two steps Next can pass with
|
|
2952
|
+
nothing filled in. */
|
|
2953
|
+
var panels = { 0: 'panel-gate', 1: 'panel-name', 2: 'panel-type', 3: 'panel-detail', 4: 'panel-modules', 5: 'panel-home', 6: 'panel-address', 7: 'panel-review', 8: 'panel-done' };
|
|
2907
2954
|
/* the rail at the done panel (task 1003244): "done" is the Create step
|
|
2908
2955
|
still running — the marker stays on 5 until the project actually
|
|
2909
2956
|
answers, and only then does the whole rail read finished. aria-current
|
|
2910
2957
|
tells a screen reader the same thing. */
|
|
2911
2958
|
function paintRail(step) {
|
|
2912
|
-
var landed = step ===
|
|
2913
|
-
var cur = step ===
|
|
2959
|
+
var landed = step === 8 && !!(live && live.instStatus === 'active');
|
|
2960
|
+
var cur = step === 8 ? 7 : step;
|
|
2914
2961
|
root.querySelectorAll('.steps li').forEach(function (li) {
|
|
2915
2962
|
var n = Number(li.getAttribute('data-step'));
|
|
2916
2963
|
var now = !landed && n === cur;
|
|
@@ -2921,7 +2968,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
2921
2968
|
the button role, the tab stop and the data-goto the delegated handler
|
|
2922
2969
|
below reads. The done panel offers none — the project is filed, and
|
|
2923
2970
|
there is nothing behind it to change. */
|
|
2924
|
-
if (step <
|
|
2971
|
+
if (step < 8 && n < cur) {
|
|
2925
2972
|
li.setAttribute('role', 'button');
|
|
2926
2973
|
li.setAttribute('tabindex', '0');
|
|
2927
2974
|
li.setAttribute('data-goto', String(n));
|
|
@@ -2940,7 +2987,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
2940
2987
|
paintRail(step);
|
|
2941
2988
|
saveDraft();
|
|
2942
2989
|
/* the done step takes the whole card — the bongo pair steps aside */
|
|
2943
|
-
root.querySelector('.createSplit').classList.toggle('wizDone', step ===
|
|
2990
|
+
root.querySelector('.createSplit').classList.toggle('wizDone', step === 8);
|
|
2944
2991
|
/* focus moves ONLY on a deliberate step (the hub rule setView already
|
|
2945
2992
|
applies to this view: never on boot, never on a background settle) */
|
|
2946
2993
|
if (moveFocus) {
|
|
@@ -2950,15 +2997,18 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
2950
2997
|
/* the repo probe is authenticated — never fire it before /me settles
|
|
2951
2998
|
(a pre-auth probe 401s for strangers and, worse, could seed the
|
|
2952
2999
|
draft's owner with myLogin()'s placeholder) */
|
|
2953
|
-
|
|
3000
|
+
/* the picker repaints on entry, not once at boot: the owner can walk back
|
|
3001
|
+
to the Type step and change the answer the preselect is keyed to */
|
|
3002
|
+
if (step === 4) renderModules();
|
|
3003
|
+
if (step === 5) { syncHome(); if (authKnown) ensureRepoAccess(); }
|
|
2954
3004
|
/* a resumed draft can land PAST the Home step with the probe never run —
|
|
2955
3005
|
Create would then file a repo it promised to make but never did */
|
|
2956
|
-
else if (step >
|
|
3006
|
+
else if (step > 5 && step < 8 && authKnown && repoAccess === null) ensureRepoAccess();
|
|
2957
3007
|
/* re-check on every entry, not just on typing: a resumed draft arrives here with
|
|
2958
3008
|
a handle nobody has asked the server about, and a handle free ten minutes ago
|
|
2959
3009
|
may not be now (task 1002898 N10) */
|
|
2960
|
-
if (step ===
|
|
2961
|
-
if (step ===
|
|
3010
|
+
if (step === 6) { checkSlugAvailable(); renderAddrPanel(); }
|
|
3011
|
+
if (step === 7) renderReview();
|
|
2962
3012
|
}
|
|
2963
3013
|
|
|
2964
3014
|
/* One delegated pair on the rail, so paintRail can add and drop data-goto
|
|
@@ -3110,6 +3160,142 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
3110
3160
|
then pressed Skip meant "I'm done here", not "throw that away". */
|
|
3111
3161
|
$('skipDetail').addEventListener('click', function () { show(4, true); });
|
|
3112
3162
|
|
|
3163
|
+
/* ── the module picker (task 1002339, BV1.R20) ──
|
|
3164
|
+
ONE public read gives the panel both halves it needs: `optional` is the
|
|
3165
|
+
catalog it renders, `bundles` is what each project type preselects
|
|
3166
|
+
(GET /provisioning/starter-bundles, ADR 0237). They arrive together
|
|
3167
|
+
because a second round trip is exactly what would let a preselect name a
|
|
3168
|
+
module the panel has no row for.
|
|
3169
|
+
|
|
3170
|
+
state.modules is null until the owner SAYS something — a toggle, or Next
|
|
3171
|
+
off this panel. While it is null the panel paints the bundle and the
|
|
3172
|
+
create request omits the field, and the server resolves the same bundle
|
|
3173
|
+
from the same declaration. So "skip" and "accept what you were shown"
|
|
3174
|
+
reach the same modules by construction rather than by two copies of a
|
|
3175
|
+
list agreeing. Changing the project type while nothing has been said
|
|
3176
|
+
re-preselects for free; once the owner has answered, their answer stands.
|
|
3177
|
+
|
|
3178
|
+
No fetch failure mode is fatal: with no catalog the band renders its
|
|
3179
|
+
"couldn't load" clause, state.modules stays null, and the create still
|
|
3180
|
+
files — the server applies the bundle. A picker that could block a
|
|
3181
|
+
project from being created would be worse than no picker. */
|
|
3182
|
+
var starterBundles = null; /* the payload once it lands; null until then */
|
|
3183
|
+
var bundlesPromise = null; /* memoised like fetchPlan below — entering the step twice asks once */
|
|
3184
|
+
var bundlesFailed = false; /* asked and did not get it; distinct from not yet asked */
|
|
3185
|
+
/* Lazy, not at boot: /projects serves four views from one file, and the map
|
|
3186
|
+
has no business paying for the picker's read. */
|
|
3187
|
+
function ensureBundles() {
|
|
3188
|
+
if (bundlesPromise) return bundlesPromise;
|
|
3189
|
+
bundlesPromise = fetch(API + '/provisioning/starter-bundles', { credentials: 'same-origin' })
|
|
3190
|
+
.then(function (res) { return res.ok ? res.json() : null; })
|
|
3191
|
+
.then(function (data) {
|
|
3192
|
+
if (data && data.optional) starterBundles = data; else bundlesFailed = true;
|
|
3193
|
+
repaintForBundles();
|
|
3194
|
+
})
|
|
3195
|
+
.catch(function () { bundlesFailed = true; repaintForBundles(); });
|
|
3196
|
+
return bundlesPromise;
|
|
3197
|
+
}
|
|
3198
|
+
/* Whichever panel is asking gets repainted when the read lands. The review
|
|
3199
|
+
is in this list because a resumed draft can arrive there having never
|
|
3200
|
+
opened the picker — and a review that named the wrong extras would be the
|
|
3201
|
+
exact dishonesty the About row's "off the public map" clause exists to
|
|
3202
|
+
prevent, one screen before Create. */
|
|
3203
|
+
function repaintForBundles() {
|
|
3204
|
+
if (state.step === 4) renderModules();
|
|
3205
|
+
else if (state.step === 7) renderReview();
|
|
3206
|
+
}
|
|
3207
|
+
function preselectedModules() {
|
|
3208
|
+
if (!starterBundles) return [];
|
|
3209
|
+
var forType = (starterBundles.bundles || []).filter(function (b) { return b.type === state.projType; })[0];
|
|
3210
|
+
return forType ? forType.bundle.slice() : [];
|
|
3211
|
+
}
|
|
3212
|
+
/* What the panel paints and the review names: the owner's answer if they
|
|
3213
|
+
gave one, otherwise the type's preselect. */
|
|
3214
|
+
function pickedModules() {
|
|
3215
|
+
return state.modules === null ? preselectedModules() : state.modules.slice();
|
|
3216
|
+
}
|
|
3217
|
+
/* What rides the create request — undefined while nothing has been said, so
|
|
3218
|
+
an unanswered picker files no answer (task 1002339). */
|
|
3219
|
+
function modulesForCreate() {
|
|
3220
|
+
return state.modules === null ? undefined : state.modules.slice();
|
|
3221
|
+
}
|
|
3222
|
+
function moduleLabel(key) {
|
|
3223
|
+
var rows = (starterBundles && starterBundles.optional) || [];
|
|
3224
|
+
for (var i = 0; i < rows.length; i++) if (rows[i].key === key) return rows[i].label;
|
|
3225
|
+
return key;
|
|
3226
|
+
}
|
|
3227
|
+
function renderModules() {
|
|
3228
|
+
ensureBundles();
|
|
3229
|
+
var band = $('modCards');
|
|
3230
|
+
var note = $('modCoreNote');
|
|
3231
|
+
if (!starterBundles) {
|
|
3232
|
+
/* nothing yet. While the read is still in flight that is a blank band for
|
|
3233
|
+
a moment; only a read that ANSWERED badly gets the clause, so a slow
|
|
3234
|
+
network never tells the owner something is broken when it isn't. */
|
|
3235
|
+
band.innerHTML = '';
|
|
3236
|
+
note.textContent = bundlesFailed
|
|
3237
|
+
? 'We couldn’t load the list of extras just now. Your project is created with everything it needs either way, and you can add extras from its settings.'
|
|
3238
|
+
: '';
|
|
3239
|
+
note.classList.toggle('on', bundlesFailed);
|
|
3240
|
+
return;
|
|
3241
|
+
}
|
|
3242
|
+
var on = pickedModules();
|
|
3243
|
+
band.innerHTML = (starterBundles.optional || []).map(function (m) {
|
|
3244
|
+
return '<button type="button" class="tpill" data-mod="' + esc(m.key) + '" aria-pressed="'
|
|
3245
|
+
+ (on.indexOf(m.key) === -1 ? 'false' : 'true') + '"><span><b>' + esc(m.label)
|
|
3246
|
+
+ '</b><small>' + esc(m.summary) + '</small></span></button>';
|
|
3247
|
+
}).join('');
|
|
3248
|
+
var core = (starterBundles.core || []).length;
|
|
3249
|
+
note.textContent = core
|
|
3250
|
+
? 'Every project also starts with the same ' + core + ' core parts — the platform itself, the way it tracks work, and the builders’ hall. They are always on, so they are not part of this choice.'
|
|
3251
|
+
: '';
|
|
3252
|
+
note.classList.toggle('on', core > 0);
|
|
3253
|
+
}
|
|
3254
|
+
/* One delegated listener, because the rows are painted from the fetch. */
|
|
3255
|
+
$('modCards').addEventListener('click', function (ev) {
|
|
3256
|
+
var btn = ev.target.closest && ev.target.closest('.tpill[data-mod]');
|
|
3257
|
+
if (!btn) return;
|
|
3258
|
+
var key = btn.getAttribute('data-mod');
|
|
3259
|
+
var on = pickedModules();
|
|
3260
|
+
var want = on.indexOf(key) === -1 ? on.concat([key]) : on.filter(function (k) { return k !== key; });
|
|
3261
|
+
/* Re-derive in CATALOG order rather than appending: the draft, the review
|
|
3262
|
+
row and the array the server stores are then the same list in the same
|
|
3263
|
+
order, so a resumed draft or a re-read can never look like a change. */
|
|
3264
|
+
state.modules = (starterBundles.optional || []).map(function (m) { return m.key; })
|
|
3265
|
+
.filter(function (k) { return want.indexOf(k) !== -1; });
|
|
3266
|
+
renderModules();
|
|
3267
|
+
saveDraft();
|
|
3268
|
+
});
|
|
3269
|
+
/* Next off this panel CONFIRMS what is on screen — the owner looked at the
|
|
3270
|
+
set and accepted it, which is a different fact from never being asked. */
|
|
3271
|
+
root.querySelector('#panel-modules form').addEventListener('submit', function () {
|
|
3272
|
+
state.modules = pickedModules();
|
|
3273
|
+
saveDraft();
|
|
3274
|
+
});
|
|
3275
|
+
/* What the review ledger names. Three cases, and the third is the one worth
|
|
3276
|
+
writing down: with nothing said and the catalog not (yet) loaded we do not
|
|
3277
|
+
know the module NAMES, but we do know the owner will get the type's
|
|
3278
|
+
suggested set — so the row says that rather than "None", which would be a
|
|
3279
|
+
false promise on the last screen before Create. */
|
|
3280
|
+
function extrasRow() {
|
|
3281
|
+
ensureBundles();
|
|
3282
|
+
if (!starterBundles) {
|
|
3283
|
+
/* the names live in the catalog, so until it lands say the true thing
|
|
3284
|
+
WITHOUT them — never the raw module keys, which are jargon, and never
|
|
3285
|
+
"None", which would be a false promise */
|
|
3286
|
+
return state.modules === null
|
|
3287
|
+
? 'The suggested set for the kind of project you picked'
|
|
3288
|
+
: 'The extras you picked';
|
|
3289
|
+
}
|
|
3290
|
+
var picked = pickedModules();
|
|
3291
|
+
return picked.length ? esc(picked.map(moduleLabel).join(', ')) : 'None — just the core';
|
|
3292
|
+
}
|
|
3293
|
+
|
|
3294
|
+
/* Skip = walk past without answering. It does NOT erase a toggle already
|
|
3295
|
+
made (the About step's rule), and with nothing said it leaves state.modules
|
|
3296
|
+
null so the server applies the bundle this panel was showing. */
|
|
3297
|
+
$('skipModules').addEventListener('click', function () { show(5, true); });
|
|
3298
|
+
|
|
3113
3299
|
/* ── GitHub-integrated repo picker (adopt) + auto-create (fresh) — task 1002099 / ADR 0143.
|
|
3114
3300
|
Progressive enhancement: probe GET /github/repos once we reach the Home step.
|
|
3115
3301
|
200 → we hold repo access → show the picker (adopt) / name field (fresh);
|
|
@@ -3306,9 +3492,10 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
3306
3492
|
}
|
|
3307
3493
|
return true;
|
|
3308
3494
|
}
|
|
3309
|
-
/*
|
|
3310
|
-
empty answer is the whole point (
|
|
3311
|
-
|
|
3495
|
+
/* steps 3 (About) and 4 (Modules) have no clause: they are the optional
|
|
3496
|
+
ones, and Next past an empty answer is the whole point (tasks 1002334,
|
|
3497
|
+
1002339). */
|
|
3498
|
+
if (step === 5) {
|
|
3312
3499
|
var homeErr = function (msg) { $('h-home').textContent = msg; $('h-home').className = 'hint err'; return false; };
|
|
3313
3500
|
if (repoAccess === 'need-connect') return homeErr('Allow GitHub repo access to pick or create your repository — or choose “enter it manually”.');
|
|
3314
3501
|
if (repoAccess === true && state.mode !== 'adopt') {
|
|
@@ -3323,7 +3510,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
3323
3510
|
$('h-home').innerHTML = ' '; $('h-home').className = 'hint';
|
|
3324
3511
|
return true;
|
|
3325
3512
|
}
|
|
3326
|
-
if (step ===
|
|
3513
|
+
if (step === 6) {
|
|
3327
3514
|
if (!validSlug(state.slug)) {
|
|
3328
3515
|
$('h-slug').textContent = '2–40 lowercase letters, numbers, or dashes; no leading/trailing/double dash.';
|
|
3329
3516
|
$('h-slug').className = 'hint err'; $('f-slug').classList.add('bad');
|
|
@@ -3396,6 +3583,10 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
3396
3583
|
? esc(String(state.description).trim())
|
|
3397
3584
|
: '<em style="font-weight:600;color:var(--warn)">not said — off the public map until you add it</em>'],
|
|
3398
3585
|
['Who’s building it', teamLabel(state.teamShape) || 'Not said'],
|
|
3586
|
+
/* the picker is optional too, so this row shows what the project will
|
|
3587
|
+
ACTUALLY come with — the owner's picks, or the preselect they walked
|
|
3588
|
+
past — never a blank that a Create is about to turn into a surprise */
|
|
3589
|
+
['Extras', extrasRow()],
|
|
3399
3590
|
['Starting from', state.mode === 'adopt' ? 'A repo you already have' : 'A fresh, empty repo'],
|
|
3400
3591
|
['Repository', '<code>' + esc(state.owner + '/' + state.repo) + '</code>'],
|
|
3401
3592
|
['Handle', '<code>' + esc(state.slug) + '</code>'],
|
|
@@ -3487,6 +3678,10 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
3487
3678
|
answered-but-blank description to the publish gate */
|
|
3488
3679
|
description: String(state.description || '').trim() || undefined,
|
|
3489
3680
|
detail: state.teamShape ? { team_shape: state.teamShape } : undefined,
|
|
3681
|
+
/* the picker's answer (task 1002339) — omitted while unanswered, and
|
|
3682
|
+
the server then applies the SAME starter bundle the panel showed,
|
|
3683
|
+
so a skip and an accept land the same modules */
|
|
3684
|
+
modules: modulesForCreate(),
|
|
3490
3685
|
hosting_shape: 'standalone',
|
|
3491
3686
|
/* ONLY a bring-your-own domain travels (task 1002898 N2). The server owns
|
|
3492
3687
|
the assigned <slug>.<apex> default (task 1002704) and applies it to an
|
|
@@ -3531,7 +3726,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
3531
3726
|
sessionStorage.removeItem(DRAFT_KEY);
|
|
3532
3727
|
} catch (e) {}
|
|
3533
3728
|
renderDone();
|
|
3534
|
-
show(
|
|
3729
|
+
show(8, true);
|
|
3535
3730
|
startProgress(data.instance && data.instance.id);
|
|
3536
3731
|
}).catch(function (err) {
|
|
3537
3732
|
if (String(err && err.message) === 'signing in') return;
|
|
@@ -3539,7 +3734,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
3539
3734
|
if (err && err.gotoHome) {
|
|
3540
3735
|
/* A repo-creation problem (name taken / reconnect) — bounce back to the Home
|
|
3541
3736
|
step with the reason, so the user fixes it where the repo is chosen. */
|
|
3542
|
-
show(
|
|
3737
|
+
show(5, true);
|
|
3543
3738
|
$('h-home').textContent = err.homeMsg || 'Please revisit the repository step.';
|
|
3544
3739
|
$('h-home').className = 'hint err';
|
|
3545
3740
|
return;
|
|
@@ -4085,7 +4280,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
4085
4280
|
5s tick never clobbers an open dropdown or mid-flight button) */
|
|
4086
4281
|
if (inst && inst.status !== live.instStatus) {
|
|
4087
4282
|
live.instStatus = inst.status; renderSteps();
|
|
4088
|
-
if (state.step ===
|
|
4283
|
+
if (state.step === 8) paintRail(8); /* the rail finishes only when the project answers */
|
|
4089
4284
|
}
|
|
4090
4285
|
var terminal = renderProgress(inst);
|
|
4091
4286
|
if (!terminal) pollTimer = setTimeout(tick, 5000);
|
|
@@ -4162,7 +4357,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
4162
4357
|
manifestState = created.manifest_state; manifestPending = true;
|
|
4163
4358
|
}
|
|
4164
4359
|
renderDone();
|
|
4165
|
-
show(
|
|
4360
|
+
show(8, !!focus);
|
|
4166
4361
|
startProgress(created.id);
|
|
4167
4362
|
resumePolls();
|
|
4168
4363
|
return;
|
|
@@ -4170,7 +4365,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
4170
4365
|
/* before identity settles, never render past the name step: deeper steps
|
|
4171
4366
|
carry authenticated side effects (the Home-step repo probe), and onAuth()
|
|
4172
4367
|
re-enters to the real draft step the moment /me answers */
|
|
4173
|
-
var step = authKnown && state.step >= 1 && state.step <=
|
|
4368
|
+
var step = authKnown && state.step >= 1 && state.step <= 7 ? state.step : 1;
|
|
4174
4369
|
if (step === 1) {
|
|
4175
4370
|
show(1, false);
|
|
4176
4371
|
if (focus) $('newName').focus();
|
|
@@ -156,10 +156,10 @@
|
|
|
156
156
|
"steps": [["wait",900],["click","[data-del-ask]"],["wait",200],["fill","#mDelName","mooring"],["wait",100],["click","[data-del-yes]"],["wait",900]],
|
|
157
157
|
"expect": {"text":[["#mDel .addr","A teardown is queued for this project."],["#mName","mooring"]],"hidden":["#mDelName","[data-del-ask]"],"visible":["#mStrip .strip.flowDown"]} },
|
|
158
158
|
{ "name": "the wizard driver (task 1003346): name, type, about, home (fresh; the stub grants repo access, so the new repository is named from the project), address (the handle's verdict), the review as a ledger, then the filing — the done panel with the standup rail and the human steps", "auth": true, "width": 1440, "url": "/projects?view=new",
|
|
159
|
-
"steps": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#doCreate"],["wait",1500]],
|
|
159
|
+
"steps": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#doCreate"],["wait",1500]],
|
|
160
160
|
"expect": {"visible":["#panel-done","#liveProgress .prog","#liveProgress .prog .stages li.now","#humanSteps li"],"hidden":["#panel-review","#panel-name"],"text":[["#done-h","The drums are rolling"]],"attr":[["#panel-done","class","panel on"]]} },
|
|
161
161
|
{ "name": "the wizard driver at 390: the rail folds to two rows of three and the walk still reaches the review", "auth": true, "width": 390, "url": "/projects?view=new",
|
|
162
|
-
"steps": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400]],
|
|
162
|
+
"steps": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400]],
|
|
163
163
|
"expect": {"visible":["#panel-review","#reviewList li","#doCreate"],"hidden":["#panel-address"],"text":[["#rev-h","Ready to roll"]]} }
|
|
164
164
|
]
|
|
165
165
|
}
|
|
@@ -30,10 +30,11 @@
|
|
|
30
30
|
"out-new": { "auth": false, "url": "/projects?view=new", "expect": { "hidden": ["#qWrap"] } },
|
|
31
31
|
"in-new-type": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-type"],"hidden":["#panel-name","#qWrap"]} },
|
|
32
32
|
"in-new-about": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-detail"],"hidden":["#panel-type","#qWrap"]} },
|
|
33
|
-
"in-new-
|
|
34
|
-
"in-new-
|
|
35
|
-
"in-new-
|
|
36
|
-
"in-new-
|
|
33
|
+
"in-new-modules": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900]], "expect": {"visible":["#panel-modules","#modCards .tpill[aria-pressed=\"true\"]","#modCoreNote"],"hidden":["#panel-detail","#qWrap"]} },
|
|
34
|
+
"in-new-home": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-home"],"hidden":["#panel-detail","#qWrap"]} },
|
|
35
|
+
"in-new-address": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-address"],"hidden":["#panel-home","#qWrap"]} },
|
|
36
|
+
"in-new-review": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-review","#reviewList li"],"hidden":["#panel-address","#qWrap"]} },
|
|
37
|
+
"in-new-done": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#doCreate"],["wait",1500]], "expect": {"visible":["#panel-done","#liveProgress .prog","#humanSteps li"],"hidden":["#panel-review","#qWrap"]} },
|
|
37
38
|
"out-map-projects": { "auth": false, "actions": [["click", "#projTrig"], ["wait", 350]], "expect": { "expanded": true, "hidden": ["#menuProjects [data-nav=\"mine\"]"] } },
|
|
38
39
|
"card": { "auth": true, "actions": [["dispatchClick", "#orbField a.worldOrb[data-origin=\"https://amazonprimea.com\"]"], ["wait", 900]], "expect": { "dialog": true, "hasClass": [["#projCard", "has-art"]], "visible": ["#cardArt", "#cardFaces", "#cardBuildersFig", "#cardShippedFig", "#cardOpen", "#cardHall"], "text": [["#cardBuilders", "11"], ["#cardShipped", "0"], ["#cardRel", "you build here"], ["#cardRuns", "amazonprimea.com"], ["#cardFaces .facesMore", "+6"]], "count": [["#cardFaces img", 5]], "href": [["#cardHall", "https://builders.amazonprimea.com/"]] } },
|
|
39
40
|
"card-noart": { "auth": true, "actions": [["dispatchClick", "#orbField a.worldOrb[data-origin=\"https://emersonian-circles.cloudbongos.com\"]"], ["wait", 600]], "expect": { "dialog": true, "hasClass": [["#projCard", "has-art", false]], "hidden": ["#cardArt", "#cardFaces", "#cardHall"], "visible": ["#cardBuildersFig", "#cardShippedFig", "#cardOpen"], "text": [["#cardShipped", "30+"], ["#cardRel", "you build here"]] } },
|
|
@@ -108,6 +108,32 @@ const FEEDS = {
|
|
|
108
108
|
const AUTHED = { '/my-projects': 'my-projects' };
|
|
109
109
|
const UNAUTH = { error: { code: 'unauthenticated', message: 'unauthenticated' } };
|
|
110
110
|
|
|
111
|
+
// GET /provisioning/starter-bundles as the create wizard's module picker reads it
|
|
112
|
+
// (task 1002339 / BV1.R19's route, ADR 0237). A fixture rather than an import —
|
|
113
|
+
// the kit stub deliberately requires no module — kept honest by
|
|
114
|
+
// tests/provisioning_starter_bundles.mjs, which reads this file and compares it
|
|
115
|
+
// against the real declaration. `core` is the count the panel's clause names, so
|
|
116
|
+
// only its length matters here; `optional` and `bundles` are byte-for-byte.
|
|
117
|
+
const STARTER_BUNDLES_FIXTURE = {
|
|
118
|
+
core: [
|
|
119
|
+
'autonomy', 'builder-settings', 'copy-desk', 'economy', 'government', 'grading',
|
|
120
|
+
'hall-ui', 'ideas', 'lifecycle', 'memory', 'onboarding', 'public-landing',
|
|
121
|
+
'security', 'sessions', 'status-ui', 'ui-design',
|
|
122
|
+
],
|
|
123
|
+
bundles: [
|
|
124
|
+
{ type: 'game', bundle: ['dev-box', 'discord'] },
|
|
125
|
+
{ type: 'research', bundle: ['agents'] },
|
|
126
|
+
{ type: 'business', bundle: ['agents', 'dev-box'] },
|
|
127
|
+
{ type: 'non-profit', bundle: ['discord'] },
|
|
128
|
+
{ type: 'not-sure', bundle: [] },
|
|
129
|
+
],
|
|
130
|
+
optional: [
|
|
131
|
+
{ key: 'agents', label: 'Extra agents', summary: 'More AI agents alongside the ones every project gets, for reading, research and review.' },
|
|
132
|
+
{ key: 'dev-box', label: 'Dev boxes', summary: 'A cloud workspace where a change can be tried out before anyone else sees it. Nothing is charged until someone asks for one.' },
|
|
133
|
+
{ key: 'discord', label: 'Discord', summary: 'A Discord server that carries your build news, and lets people send ideas and bug reports back.' },
|
|
134
|
+
],
|
|
135
|
+
};
|
|
136
|
+
|
|
111
137
|
function json(res, status, body) { res.writeHead(status, { 'content-type': 'application/json', 'cache-control': 'no-store' }); res.end(JSON.stringify(body)); }
|
|
112
138
|
function fixture(name, res, origin) {
|
|
113
139
|
if (FAIL.has(name)) return json(res, 500, { error: { code: 'stub_failure', message: `FAIL=${name}` } });
|
|
@@ -277,6 +303,12 @@ const server = http.createServer((req, res) => {
|
|
|
277
303
|
}
|
|
278
304
|
if (rest.startsWith('/github/repo')) return AUTH ? json(res, 200, { repo: { private: false, admin: true } }) : json(res, 401, UNAUTH);
|
|
279
305
|
if (rest.startsWith('/provisioning/slug-available')) return json(res, 200, { available: true }); /* the wizard's handle check (task 1003346): every handle is free on the stub */
|
|
306
|
+
/* the module picker's one read (task 1002339). A FIXTURE, like everything
|
|
307
|
+
else here — the stub imports no module — but not a free-hand one:
|
|
308
|
+
tests/provisioning_starter_bundles.mjs pins it against the real
|
|
309
|
+
starter-bundles.js declaration, so the picture the kit renders and the
|
|
310
|
+
payload the route serves cannot drift apart silently. */
|
|
311
|
+
if (rest === '/provisioning/starter-bundles') return json(res, 200, STARTER_BUNDLES_FIXTURE);
|
|
280
312
|
if (rest === '/auth/logout') return json(res, 200, { ok: true });
|
|
281
313
|
if (rest.startsWith('/auth/web/start')) { res.writeHead(302, { location: PREFIX }); return res.end(); }
|
|
282
314
|
if (rest === '/instance') { const b = pages.size ? pages.values().next().value.brand : {}; return json(res, 200, { brand: { productName: (b && b.productName) || '', project: {} }, modules: {} }); }
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.516",
|
|
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.516",
|
|
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.516",
|
|
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
|
@@ -45,7 +45,7 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
45
45
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
46
46
|
// the entry to that file. Look for a version's history there, not here.
|
|
47
47
|
// ---------------------------------------------------------------------------
|
|
48
|
-
const CORE_VERSION = '1.19.
|
|
48
|
+
const CORE_VERSION = '1.19.516'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
49
49
|
|
|
50
50
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
51
51
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|