@bongos/core 1.21.6 → 1.21.7
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 +29 -19
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-inventory.json +5 -3
- package/docs/page-readings.json +936 -920
- package/modules/public-landing/public/projects-hall-live.states.json +216 -0
- package/modules/public-landing/public/projects.html +43 -13
- package/modules/public-landing/public/projects.states.json +1 -1
- package/modules/ui-design/kit/fixtures/provisioning-instances-hall-live.json +106 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/src/module-api.js +1 -1
- package/tests/project_invite_ui.mjs +65 -7
- package/tests/projects_hub_dns_ready.mjs +7 -1
- package/tests/ui_design_kit.mjs +1 -1
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_": "The create wizard done panel once the hall answers (task 1004503): the same walk as projects.states.json in-new-done, with the instance list aliased so the project just filed (id 102) is live and verified up. The invite box opens only now; in-new-done in the main file pictures it shut while the project is set up. Rendered by name: node modules/ui-design/kit/render.js --states modules/public-landing/public/projects-hall-live.states.json",
|
|
3
|
+
"page": "/projects",
|
|
4
|
+
"surface": "public-landing",
|
|
5
|
+
"modes": [
|
|
6
|
+
"dark"
|
|
7
|
+
],
|
|
8
|
+
"modeQuery": false,
|
|
9
|
+
"stub": {
|
|
10
|
+
"fixtures": {
|
|
11
|
+
"provisioning-instances": "provisioning-instances-hall-live"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"ignoreRequests": [
|
|
15
|
+
"/api/(gds|bongos)/me$",
|
|
16
|
+
"/my-projects",
|
|
17
|
+
"/provisioning/onboard-plan",
|
|
18
|
+
"/env-manifest$"
|
|
19
|
+
],
|
|
20
|
+
"ignoreConsole": [
|
|
21
|
+
"/api/(gds|bongos)/me",
|
|
22
|
+
"\\b401\\b",
|
|
23
|
+
"\\b404\\b",
|
|
24
|
+
"Unauthorized"
|
|
25
|
+
],
|
|
26
|
+
"states": {
|
|
27
|
+
"in-new-done-live": {
|
|
28
|
+
"auth": true,
|
|
29
|
+
"url": "/projects?view=new",
|
|
30
|
+
"actions": [
|
|
31
|
+
[
|
|
32
|
+
"wait",
|
|
33
|
+
600
|
|
34
|
+
],
|
|
35
|
+
[
|
|
36
|
+
"click",
|
|
37
|
+
"#termsAck"
|
|
38
|
+
],
|
|
39
|
+
[
|
|
40
|
+
"wait",
|
|
41
|
+
100
|
|
42
|
+
],
|
|
43
|
+
[
|
|
44
|
+
"click",
|
|
45
|
+
"#wizStart"
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
"wait",
|
|
49
|
+
400
|
|
50
|
+
],
|
|
51
|
+
[
|
|
52
|
+
"fill",
|
|
53
|
+
"#newName",
|
|
54
|
+
"Mercury"
|
|
55
|
+
],
|
|
56
|
+
[
|
|
57
|
+
"click",
|
|
58
|
+
"#typeCards .tpill[data-type=\"game\"]"
|
|
59
|
+
],
|
|
60
|
+
[
|
|
61
|
+
"fill",
|
|
62
|
+
"#f-desc",
|
|
63
|
+
"A co-op farming game where the seasons never repeat."
|
|
64
|
+
],
|
|
65
|
+
[
|
|
66
|
+
"click",
|
|
67
|
+
"#panel-what button[type=submit]"
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
"wait",
|
|
71
|
+
400
|
|
72
|
+
],
|
|
73
|
+
[
|
|
74
|
+
"select",
|
|
75
|
+
"#physCountry",
|
|
76
|
+
"US"
|
|
77
|
+
],
|
|
78
|
+
[
|
|
79
|
+
"wait",
|
|
80
|
+
100
|
|
81
|
+
],
|
|
82
|
+
[
|
|
83
|
+
"click",
|
|
84
|
+
"[data-phys=\"builder_geography\"][data-val=\"global\"]"
|
|
85
|
+
],
|
|
86
|
+
[
|
|
87
|
+
"click",
|
|
88
|
+
"[data-phys=\"minors_on_board\"][data-val=\"false\"]"
|
|
89
|
+
],
|
|
90
|
+
[
|
|
91
|
+
"click",
|
|
92
|
+
"[data-phys=\"entity\"][data-val=\"intent-solo\"]"
|
|
93
|
+
],
|
|
94
|
+
[
|
|
95
|
+
"click",
|
|
96
|
+
"#physNext"
|
|
97
|
+
],
|
|
98
|
+
[
|
|
99
|
+
"wait",
|
|
100
|
+
700
|
|
101
|
+
],
|
|
102
|
+
[
|
|
103
|
+
"click",
|
|
104
|
+
"#reviewRows .setRow[data-row=\"code\"] [data-row-ok]"
|
|
105
|
+
],
|
|
106
|
+
[
|
|
107
|
+
"wait",
|
|
108
|
+
200
|
|
109
|
+
],
|
|
110
|
+
[
|
|
111
|
+
"click",
|
|
112
|
+
"#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"
|
|
113
|
+
],
|
|
114
|
+
[
|
|
115
|
+
"wait",
|
|
116
|
+
200
|
|
117
|
+
],
|
|
118
|
+
[
|
|
119
|
+
"fill",
|
|
120
|
+
"#f-slug",
|
|
121
|
+
"mercury"
|
|
122
|
+
],
|
|
123
|
+
[
|
|
124
|
+
"wait",
|
|
125
|
+
900
|
|
126
|
+
],
|
|
127
|
+
[
|
|
128
|
+
"fill",
|
|
129
|
+
"#f-domain",
|
|
130
|
+
"mercury.example.com"
|
|
131
|
+
],
|
|
132
|
+
[
|
|
133
|
+
"wait",
|
|
134
|
+
200
|
|
135
|
+
],
|
|
136
|
+
[
|
|
137
|
+
"click",
|
|
138
|
+
"#reviewRows .setRow[data-row=\"address\"] [data-row-use]"
|
|
139
|
+
],
|
|
140
|
+
[
|
|
141
|
+
"wait",
|
|
142
|
+
200
|
|
143
|
+
],
|
|
144
|
+
[
|
|
145
|
+
"click",
|
|
146
|
+
"#reviewRows .setRow[data-row=\"app\"] [data-row-ok]"
|
|
147
|
+
],
|
|
148
|
+
[
|
|
149
|
+
"wait",
|
|
150
|
+
150
|
|
151
|
+
],
|
|
152
|
+
[
|
|
153
|
+
"click",
|
|
154
|
+
"#reviewRows .setRow[data-row=\"extras\"] [data-row-ok]"
|
|
155
|
+
],
|
|
156
|
+
[
|
|
157
|
+
"wait",
|
|
158
|
+
150
|
|
159
|
+
],
|
|
160
|
+
[
|
|
161
|
+
"click",
|
|
162
|
+
"#reviewRows .setRow[data-row=\"team\"] [data-row-ok]"
|
|
163
|
+
],
|
|
164
|
+
[
|
|
165
|
+
"wait",
|
|
166
|
+
150
|
|
167
|
+
],
|
|
168
|
+
[
|
|
169
|
+
"click",
|
|
170
|
+
"#reviewRows .setRow[data-row=\"run\"] [data-row-ok]"
|
|
171
|
+
],
|
|
172
|
+
[
|
|
173
|
+
"wait",
|
|
174
|
+
200
|
|
175
|
+
],
|
|
176
|
+
[
|
|
177
|
+
"click",
|
|
178
|
+
"#reviewGo"
|
|
179
|
+
],
|
|
180
|
+
[
|
|
181
|
+
"wait",
|
|
182
|
+
500
|
|
183
|
+
],
|
|
184
|
+
[
|
|
185
|
+
"click",
|
|
186
|
+
"#doCreate"
|
|
187
|
+
],
|
|
188
|
+
[
|
|
189
|
+
"wait",
|
|
190
|
+
1500
|
|
191
|
+
],
|
|
192
|
+
[
|
|
193
|
+
"wait",
|
|
194
|
+
6000
|
|
195
|
+
]
|
|
196
|
+
],
|
|
197
|
+
"expect": {
|
|
198
|
+
"visible": [
|
|
199
|
+
"#panel-done",
|
|
200
|
+
"#wizInvite",
|
|
201
|
+
"#wizInviteWho"
|
|
202
|
+
],
|
|
203
|
+
"text": [
|
|
204
|
+
[
|
|
205
|
+
"#done-h",
|
|
206
|
+
"Mercury is alive."
|
|
207
|
+
]
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
"ignoreControls": [
|
|
213
|
+
".skip",
|
|
214
|
+
".vh"
|
|
215
|
+
]
|
|
216
|
+
}
|
|
@@ -4873,10 +4873,45 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
4873
4873
|
show(2, true);
|
|
4874
4874
|
});
|
|
4875
4875
|
/* the done panel's invite step names the demo's teammates (task 1004420): the hub
|
|
4876
|
-
holds no invite of its own, so the founder sends each from the project's own box
|
|
4876
|
+
holds no invite of its own, so the founder sends each from the project's own box.
|
|
4877
|
+
The names ride the created record too, so a reload before the hall is up keeps them
|
|
4878
|
+
(task 1004503). */
|
|
4879
|
+
function demoTeam() {
|
|
4880
|
+
if (state.demo && state.demo.invites && state.demo.invites.length) return state.demo.invites;
|
|
4881
|
+
var created = createdRecord();
|
|
4882
|
+
return created && Array.isArray(created.demo_invites) ? created.demo_invites : [];
|
|
4883
|
+
}
|
|
4884
|
+
/* The invite step (tasks 1002346 and 1004503). A second account cannot join until the
|
|
4885
|
+
project's hall is up, so the box opens only on the done panel's own verified Live
|
|
4886
|
+
(last_verified_up), never while the project is being set up. Before that, one plain
|
|
4887
|
+
line says when inviting opens, naming a demo's teammates so they are not lost. The
|
|
4888
|
+
CREATED RECORD is the proof the widget needs: no signed-in identity, no filed instance
|
|
4889
|
+
or no slug means nothing to invite to. Opening loads the suggestions once; a later
|
|
4890
|
+
tick does not ask again. */
|
|
4891
|
+
var invitesOpen = false;
|
|
4892
|
+
function paintInviteGate(live) {
|
|
4893
|
+
var created = createdRecord();
|
|
4894
|
+
var canInvite = !!(me && created && created.id && created.slug);
|
|
4895
|
+
if (live && canInvite) {
|
|
4896
|
+
if (invitesOpen) return;
|
|
4897
|
+
invitesOpen = true;
|
|
4898
|
+
setInviteSlug('wizInvite', created.slug);
|
|
4899
|
+
paintDemoTeam();
|
|
4900
|
+
loadInviteSuggestions('wizInvite', created.id);
|
|
4901
|
+
$('wizInviteLater').innerHTML = 'You can invite builders any time from ' + projectPageLink('the project’s own page') + '.';
|
|
4902
|
+
return;
|
|
4903
|
+
}
|
|
4904
|
+
invitesOpen = false;
|
|
4905
|
+
setInviteSlug('wizInvite', '');
|
|
4906
|
+
$('wizDemoTeam').hidden = true;
|
|
4907
|
+
var team = demoTeam();
|
|
4908
|
+
$('wizInviteLater').textContent = team.length
|
|
4909
|
+
? 'You can invite ' + team.map(function (h) { return '@' + h; }).join(', ') + ' once its hall is up.'
|
|
4910
|
+
: 'You can invite builders once its hall is up.';
|
|
4911
|
+
}
|
|
4877
4912
|
function paintDemoTeam() {
|
|
4878
4913
|
var line = $('wizDemoTeam');
|
|
4879
|
-
var who =
|
|
4914
|
+
var who = demoTeam();
|
|
4880
4915
|
line.hidden = !who.length;
|
|
4881
4916
|
line.textContent = who.length ? 'Your demo teammates: ' + who.map(function (h) { return '@' + h; }).join(', ') + '. Invite each one here.' : '';
|
|
4882
4917
|
if (who.length && !$('wizInviteWho').value) $('wizInviteWho').value = who[0];
|
|
@@ -6399,7 +6434,9 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6399
6434
|
sessionStorage.setItem(CREATED_KEY, JSON.stringify({
|
|
6400
6435
|
id: data.instance && data.instance.id, slug: state.slug, domain: madeDomain,
|
|
6401
6436
|
noaddr: !madeDomain,
|
|
6402
|
-
mode: state.mode, name: state.name, at: Date.now()
|
|
6437
|
+
mode: state.mode, name: state.name, at: Date.now(),
|
|
6438
|
+
/* a demo's teammates, offered once the hall is up (task 1004503) */
|
|
6439
|
+
demo_invites: state.demo && state.demo.invites && state.demo.invites.length ? state.demo.invites.slice() : undefined
|
|
6403
6440
|
}));
|
|
6404
6441
|
sessionStorage.removeItem(DRAFT_KEY);
|
|
6405
6442
|
} catch (e) {}
|
|
@@ -6650,16 +6687,8 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6650
6687
|
skippable, so this panel names where it lives afterwards rather than
|
|
6651
6688
|
leaving an owner to go looking for it. */
|
|
6652
6689
|
$('doneModules').innerHTML = 'Add or remove modules later in ' + projectPageLink('the project’s settings') + '.';
|
|
6653
|
-
/* the invite step (task
|
|
6654
|
-
|
|
6655
|
-
no filed instance or no slug ⇒ nothing to invite to, and the step stays
|
|
6656
|
-
hidden. It is deliberately NOT keyed on instanceId, which startProgress
|
|
6657
|
-
sets only AFTER this runs. */
|
|
6658
|
-
var created = createdRecord();
|
|
6659
|
-
setInviteSlug('wizInvite', (me && created && created.id && created.slug) ? created.slug : '');
|
|
6660
|
-
paintDemoTeam();
|
|
6661
|
-
loadInviteSuggestions('wizInvite', (me && created && created.slug) ? created.id : '');
|
|
6662
|
-
$('wizInviteLater').innerHTML = 'You can invite builders any time from ' + projectPageLink('the project’s own page') + '.';
|
|
6690
|
+
/* the invite step waits for the hall (task 1004503): until it answers, one line */
|
|
6691
|
+
paintInviteGate(false);
|
|
6663
6692
|
renderSteps();
|
|
6664
6693
|
}
|
|
6665
6694
|
|
|
@@ -6912,6 +6941,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6912
6941
|
if (inst.last_verified_up) {
|
|
6913
6942
|
/* the alive moment (task 1004504): said only once the project has answered */
|
|
6914
6943
|
$('done-h').textContent = (state.name || state.slug || 'Your project') + ' is alive.';
|
|
6944
|
+
paintInviteGate(true); /* the hall answers, so others can join now */
|
|
6915
6945
|
setProgress(progressMarkup(-1, 3,
|
|
6916
6946
|
'Your project is answering at ' + link + '. Finish the steps below and the hall is yours.', 'isLive', flow));
|
|
6917
6947
|
return true;
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"in-new-review-run": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"run\"] [data-row-chg]"],["wait",300],["click","[data-phys=\"framing\"][data-val=\"hybrid\"]"],["wait",600]],"expect":{"visible":["#physSuite"]}},
|
|
57
57
|
"in-new-review-confirmed": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"code\"] [data-row-ok]"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-use]"],["wait",200],["click","#reviewRows .setRow[data-row=\"app\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"extras\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"run\"] [data-row-ok]"],["wait",200]],"expect":{"visible":["#reviewGo","#reviewRows .setRow.done"],"text":[["#reviewGo","Continue"]]}},
|
|
58
58
|
"in-new-look": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"code\"] [data-row-ok]"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-use]"],["wait",200],["click","#reviewRows .setRow[data-row=\"app\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"extras\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"run\"] [data-row-ok]"],["wait",200],["click","#reviewGo"],["wait",500]],"expect":{"visible":["#panel-look","#wizLook","#wizRecap","#doCreate"],"hidden":["#panel-review"],"attr":[["#wizRoom","data-screen","look"]]}},
|
|
59
|
-
"in-new-done": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"code\"] [data-row-ok]"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-use]"],["wait",200],["click","#reviewRows .setRow[data-row=\"app\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"extras\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"run\"] [data-row-ok]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#doCreate"],["wait",1500]],"expect":{"visible":["#panel-done","#liveProgress .prog","#humanSteps li"],"hidden":["#panel-look","#qWrap","#wizBack"]}},
|
|
59
|
+
"in-new-done": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"code\"] [data-row-ok]"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-use]"],["wait",200],["click","#reviewRows .setRow[data-row=\"app\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"extras\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"run\"] [data-row-ok]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#doCreate"],["wait",1500]],"expect":{"visible":["#panel-done","#liveProgress .prog","#humanSteps li","#wizInviteLater"],"hidden":["#panel-look","#qWrap","#wizBack","#wizInvite"]}},
|
|
60
60
|
"in-new-resume-offer": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"code\"] [data-row-ok]"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-use]"],["wait",200],["click","#reviewRows .setRow[data-row=\"app\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"extras\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"run\"] [data-row-ok]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#doCreate"],["wait",1500],["click","#projTrig"],["wait",350],["click","#menuProjects [data-nav=\"new\"]"],["wait",500]],"expect":{"visible":["#panel-fork","#wizResume","#wizResumeGo"],"hidden":["#panel-done"],"text":[["#wizResumeName","Mercury"]]}},
|
|
61
61
|
"in-new-look-custom": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["click","#termsAck"],["wait",100],["click","#wizStart"],["wait",400],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#panel-what button[type=submit]"],["wait",400],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["click","#physNext"],["wait",700],["click","#reviewRows .setRow[data-row=\"code\"] [data-row-ok]"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-use]"],["wait",200],["click","#reviewRows .setRow[data-row=\"app\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"extras\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-ok]"],["wait",150],["click","#reviewRows .setRow[data-row=\"run\"] [data-row-ok]"],["wait",200],["click","#reviewGo"],["wait",500],["click","#wizLook-custom"],["wait",200]],"expect":{"visible":["#panel-look","#wizAccent","#wizAccentNote"],"hidden":["#panel-review"]}},
|
|
62
62
|
"out-new": {"auth":false,"url":"/projects?view=new","expect":{"visible":["#panel-gate","#wizSignin"],"hidden":["#qWrap","#panel-fork"]}},
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_": "The owner's projects for the kit (task 1003345): four states the mine list and the manage page render — live, standing up, snagged, torn down. Timestamps written \"$now-3m\" / \"$now-1d\" are resolved by the stub at request time so \"checked 3m ago\" stays true. Mooring (task 1003345, the grade's finding): live but unreachable since its last probe, so the restart door renders and the restart probe has a row to drive.",
|
|
3
|
+
"instances": [
|
|
4
|
+
{
|
|
5
|
+
"id": 101,
|
|
6
|
+
"slug": "demo",
|
|
7
|
+
"status": "active",
|
|
8
|
+
"domain": "demo.cloudbongos.com",
|
|
9
|
+
"target_ref": "builder/demo",
|
|
10
|
+
"hosting_shape": "co-tenant",
|
|
11
|
+
"onboard_mode": "scaffold",
|
|
12
|
+
"created_at": "2026-08-01T10:00:00.000Z",
|
|
13
|
+
"provisioned_at": "2026-08-01T10:20:00.000Z",
|
|
14
|
+
"updated_at": "$now-3m",
|
|
15
|
+
"last_verified_at": "$now-3m",
|
|
16
|
+
"last_verified_up": true,
|
|
17
|
+
"error_note": null,
|
|
18
|
+
"dns_note": null,
|
|
19
|
+
"dns_resolves": true,
|
|
20
|
+
"torn_down_at": null,
|
|
21
|
+
"open_intent": null
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": 102,
|
|
25
|
+
"slug": "mercury",
|
|
26
|
+
"status": "active",
|
|
27
|
+
"planet_template": "blob-frost-tint",
|
|
28
|
+
"planet_accent": "sun-lit",
|
|
29
|
+
"domain": "mercury.cloudbongos.com",
|
|
30
|
+
"target_ref": "builder/mercury",
|
|
31
|
+
"hosting_shape": "cloud-host",
|
|
32
|
+
"owner_monthly_usd": null,
|
|
33
|
+
"onboard_mode": "scaffold",
|
|
34
|
+
"created_at": "$now-6m",
|
|
35
|
+
"provisioned_at": "$now-1m",
|
|
36
|
+
"updated_at": "$now-2m",
|
|
37
|
+
"last_verified_at": "$now-1m",
|
|
38
|
+
"last_verified_up": true,
|
|
39
|
+
"error_note": null,
|
|
40
|
+
"dns_note": null,
|
|
41
|
+
"torn_down_at": null,
|
|
42
|
+
"open_intent": {
|
|
43
|
+
"action": "standup",
|
|
44
|
+
"state": "running",
|
|
45
|
+
"created_at": "$now-6m"
|
|
46
|
+
},
|
|
47
|
+
"dns_resolves": true
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"id": 103,
|
|
51
|
+
"slug": "tidepool",
|
|
52
|
+
"status": "error",
|
|
53
|
+
"domain": "tidepool.cloudbongos.com",
|
|
54
|
+
"target_ref": "builder/tidepool",
|
|
55
|
+
"hosting_shape": "co-tenant",
|
|
56
|
+
"onboard_mode": "adopt",
|
|
57
|
+
"created_at": "2026-08-20T09:00:00.000Z",
|
|
58
|
+
"provisioned_at": null,
|
|
59
|
+
"updated_at": "$now-1d",
|
|
60
|
+
"last_verified_at": null,
|
|
61
|
+
"last_verified_up": null,
|
|
62
|
+
"error_note": "The deploy could not read the repository: it is private, and the deploy key only reads public ones.",
|
|
63
|
+
"dns_note": null,
|
|
64
|
+
"torn_down_at": null,
|
|
65
|
+
"open_intent": null
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": 104,
|
|
69
|
+
"slug": "old-lantern",
|
|
70
|
+
"status": "torn_down",
|
|
71
|
+
"domain": "old-lantern.cloudbongos.com",
|
|
72
|
+
"target_ref": "builder/old-lantern",
|
|
73
|
+
"hosting_shape": "co-tenant",
|
|
74
|
+
"onboard_mode": "scaffold",
|
|
75
|
+
"created_at": "2026-07-02T09:00:00.000Z",
|
|
76
|
+
"provisioned_at": "2026-07-02T09:30:00.000Z",
|
|
77
|
+
"updated_at": "2026-08-15T12:00:00.000Z",
|
|
78
|
+
"last_verified_at": "2026-08-15T11:50:00.000Z",
|
|
79
|
+
"last_verified_up": true,
|
|
80
|
+
"error_note": null,
|
|
81
|
+
"dns_note": null,
|
|
82
|
+
"dns_resolves": true,
|
|
83
|
+
"torn_down_at": "2026-08-15T12:00:00.000Z",
|
|
84
|
+
"open_intent": null
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"id": 105,
|
|
88
|
+
"slug": "mooring",
|
|
89
|
+
"status": "active",
|
|
90
|
+
"domain": "mooring.cloudbongos.com",
|
|
91
|
+
"target_ref": "builder/mooring",
|
|
92
|
+
"hosting_shape": "co-tenant",
|
|
93
|
+
"onboard_mode": "scaffold",
|
|
94
|
+
"created_at": "2026-08-10T09:00:00.000Z",
|
|
95
|
+
"provisioned_at": "2026-08-10T09:20:00.000Z",
|
|
96
|
+
"updated_at": "$now-20m",
|
|
97
|
+
"last_verified_at": "$now-20m",
|
|
98
|
+
"last_verified_up": false,
|
|
99
|
+
"error_note": null,
|
|
100
|
+
"dns_note": null,
|
|
101
|
+
"dns_resolves": true,
|
|
102
|
+
"torn_down_at": null,
|
|
103
|
+
"open_intent": null
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
}
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.7",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.21.
|
|
9
|
+
"version": "1.21.7",
|
|
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.21.
|
|
3
|
+
"version": "1.21.7",
|
|
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/release-notes.json
CHANGED
|
@@ -8788,5 +8788,11 @@
|
|
|
8788
8788
|
"id": "1004440",
|
|
8789
8789
|
"text": "A builder's profile on the Cloud Bongos hub now shows each craft's own work. An artist sees their approved pages, an ideator sees their filed, Full and board-ratified ideas, and the roles shown come from the work someone has a"
|
|
8790
8790
|
}
|
|
8791
|
+
],
|
|
8792
|
+
"1.21.7": [
|
|
8793
|
+
{
|
|
8794
|
+
"id": "1004503",
|
|
8795
|
+
"text": "When you start a project, the invite box now waits until your project is actually up, because nobody can join before then. Until it is, one line says when you can invite, and a demo's teammates are kept ready for that moment."
|
|
8796
|
+
}
|
|
8791
8797
|
]
|
|
8792
8798
|
}
|
package/src/module-api.js
CHANGED
|
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
75
75
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
76
76
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
77
77
|
|
|
78
|
-
const CORE_VERSION = '1.21.
|
|
78
|
+
const CORE_VERSION = '1.21.7'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
79
79
|
|
|
80
80
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
81
81
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
|
@@ -474,11 +474,69 @@ test('the two mounts keep their own lines — an invite on one is not drawn on t
|
|
|
474
474
|
// in" and "is this their project" — are answered by the caller, which hands over
|
|
475
475
|
// a slug or nothing. So the pins are on the calls.
|
|
476
476
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
477
|
+
// The wizard's invite step waits for the hall (task 1004503): a second account cannot join a
|
|
478
|
+
// project whose hall is not up, so the box opens only on the done panel's verified Live. The
|
|
479
|
+
// gate is RUN here: demoTeam, paintInviteGate and paintDemoTeam sliced out of the script.
|
|
480
|
+
const GATE_START = ' function demoTeam() {';
|
|
481
|
+
function gateSlice() {
|
|
482
|
+
const a = SCRIPT.indexOf(GATE_START);
|
|
483
|
+
const b = SCRIPT.indexOf('\n }\n', SCRIPT.indexOf(' function paintDemoTeam() {'));
|
|
484
|
+
return a > -1 && b > a ? SCRIPT.slice(a, b + 6) : '';
|
|
485
|
+
}
|
|
486
|
+
function bootGate({ me = { github_login: 'kestrel' }, created = { id: 7, slug: 'moonbase' }, demo = null } = {}) {
|
|
487
|
+
const els = {};
|
|
488
|
+
const $ = (id) => (els[id] = els[id] || { id, hidden: false, textContent: '', innerHTML: '', value: '' });
|
|
489
|
+
const calls = { slug: [], sugg: [] };
|
|
490
|
+
const sandbox = {
|
|
491
|
+
$, me, state: { demo },
|
|
492
|
+
createdRecord: () => created,
|
|
493
|
+
setInviteSlug: (id, slug) => calls.slug.push([id, slug]),
|
|
494
|
+
loadInviteSuggestions: (id, iid) => calls.sugg.push([id, iid]),
|
|
495
|
+
projectPageLink: (label) => '<a href="?view=manage">' + label + '</a>',
|
|
496
|
+
};
|
|
497
|
+
vm.createContext(sandbox);
|
|
498
|
+
vm.runInContext(gateSlice(), sandbox);
|
|
499
|
+
return { run: (c) => vm.runInContext(c, sandbox), $, calls };
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
test('the wizard opens the invite step only once the hall is up, and only for a signed-in owner of a project that exists (tasks 1002346, 1004503)', () => {
|
|
503
|
+
assert.ok(gateSlice().includes('function paintInviteGate(live)'), 'the gate slice is found');
|
|
504
|
+
assert.match(SCRIPT, /function renderDone\(\) \{[\s\S]*?paintInviteGate\(false\);/, 'the done panel paints the gate shut');
|
|
505
|
+
assert.match(SCRIPT, /if \(inst\.last_verified_up\) \{[\s\S]{0,400}paintInviteGate\(true\);/, 'and opens it only on the verified Live');
|
|
506
|
+
assert.equal((SCRIPT.match(/setInviteSlug\('wizInvite'/g) || []).length, 2, 'the gate is the only caller: shut, or open on Live');
|
|
507
|
+
|
|
508
|
+
const before = bootGate();
|
|
509
|
+
before.run('paintInviteGate(false)');
|
|
510
|
+
assert.deepEqual(before.calls.slug, [['wizInvite', '']], 'before the hall is up: no invite control');
|
|
511
|
+
assert.deepEqual(before.calls.sugg, [], 'and no suggestions asked for');
|
|
512
|
+
assert.equal(before.$('wizInviteLater').textContent, 'You can invite builders once its hall is up.', 'one plain line says when');
|
|
513
|
+
|
|
514
|
+
const live = bootGate();
|
|
515
|
+
live.run('paintInviteGate(true)');
|
|
516
|
+
live.run('paintInviteGate(true)');
|
|
517
|
+
assert.deepEqual(live.calls.slug, [['wizInvite', 'moonbase']], 'on Live: the box opens on the created project, once');
|
|
518
|
+
assert.deepEqual(live.calls.sugg, [['wizInvite', 7]], 'and the suggestions are asked for once, not on every tick');
|
|
519
|
+
assert.match(live.$('wizInviteLater').innerHTML, /any time from <a href="\?view=manage">the project’s own page<\/a>/);
|
|
520
|
+
|
|
521
|
+
for (const [label, opts] of [['signed out', { me: null }], ['no created id', { created: { slug: 'moonbase' } }], ['no slug', { created: { id: 7 } }]]) {
|
|
522
|
+
const g = bootGate(opts);
|
|
523
|
+
g.run('paintInviteGate(true)');
|
|
524
|
+
assert.deepEqual(g.calls.slug, [['wizInvite', '']], label + ': the created record IS the proof — no step');
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
test('a demo\'s teammates are kept until the hall is up, then offered in the box (task 1004503)', () => {
|
|
529
|
+
const before = bootGate({ demo: { invites: ['ava', 'jo'] } });
|
|
530
|
+
before.run('paintInviteGate(false)');
|
|
531
|
+
assert.equal(before.$('wizInviteLater').textContent, 'You can invite @ava, @jo once its hall is up.', 'named while they wait');
|
|
532
|
+
assert.equal(before.$('wizDemoTeam').hidden, true, 'no invite prompt before the hall is up');
|
|
533
|
+
// a reload before the hall is up: the draft is gone, the created record still carries the names
|
|
534
|
+
const reloaded = bootGate({ created: { id: 7, slug: 'moonbase', demo_invites: ['ava'] } });
|
|
535
|
+
reloaded.run('paintInviteGate(true)');
|
|
536
|
+
assert.equal(reloaded.$('wizDemoTeam').hidden, false, 'on Live the teammates line shows');
|
|
537
|
+
assert.equal(reloaded.$('wizDemoTeam').textContent, 'Your demo teammates: @ava. Invite each one here.');
|
|
538
|
+
assert.equal(reloaded.$('wizInviteWho').value, 'ava', 'and the first is ready in the box');
|
|
539
|
+
assert.match(SCRIPT, /demo_invites: state\.demo && state\.demo\.invites && state\.demo\.invites\.length \? state\.demo\.invites\.slice\(\) : undefined/, 'the created record carries them');
|
|
482
540
|
});
|
|
483
541
|
|
|
484
542
|
test('the project page opens the step from its own manage read, which the API already own-scopes', () => {
|
|
@@ -643,8 +701,8 @@ test('a suggestion never renders a github_id, and its name goes through the esca
|
|
|
643
701
|
test('the suggestion surface is mounted on BOTH the wizard and the project page', () => {
|
|
644
702
|
assert.match(HUB, /<ul class="suggList" id="wizInviteSugg"><\/ul>/, 'the wizard mount');
|
|
645
703
|
assert.match(HUB, /<ul class="suggList" id="mInviteSugg"><\/ul>/, 'and the project page mount');
|
|
646
|
-
assert.match(SCRIPT, /
|
|
647
|
-
'the wizard asks keyed on the SAME created record the slug comes from (ADR 0249 §1)');
|
|
704
|
+
assert.match(SCRIPT, /setInviteSlug\('wizInvite', created\.slug\);\n\s*paintDemoTeam\(\);\n\s*loadInviteSuggestions\('wizInvite', created\.id\);/,
|
|
705
|
+
'the wizard asks keyed on the SAME created record the slug comes from (ADR 0249 §1), and only once the gate opens (task 1004503)');
|
|
648
706
|
assert.match(SCRIPT, /loadInviteSuggestions\('mInvite', inst\.slug \? inst\.id : ''\)/,
|
|
649
707
|
'and the manage page asks with the id whose read already proved ownership');
|
|
650
708
|
assert.match(COSMOS, /\.suggList\{/, 'styled in cosmos.css like its sibling list');
|
|
@@ -48,9 +48,12 @@ function boot() {
|
|
|
48
48
|
const liveProgress = { innerHTML: '' };
|
|
49
49
|
// the done panel's heading (task 1004504): "Setting up <name>" until the project answers
|
|
50
50
|
const doneH = { textContent: 'Setting up Lantern' };
|
|
51
|
+
// the invite step's gate (task 1004503): renderProgress opens it only on a verified answer
|
|
52
|
+
const gates = [];
|
|
51
53
|
const sandbox = {
|
|
52
54
|
$: (id) => (id === 'liveProgress' ? liveProgress : id === 'done-h' ? doneH : null),
|
|
53
55
|
state: { name: 'Lantern', slug: 'lantern' },
|
|
56
|
+
paintInviteGate: (live) => gates.push(live),
|
|
54
57
|
esc: (s) => String(s == null ? '' : s).replace(/[&<>"']/g, (c) => (
|
|
55
58
|
{ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])),
|
|
56
59
|
projectPageLink: (label) => '<a href="?view=manage&id=1">' + label + '</a>',
|
|
@@ -64,7 +67,7 @@ function boot() {
|
|
|
64
67
|
};
|
|
65
68
|
vm.createContext(sandbox);
|
|
66
69
|
vm.runInContext(PROGRESS + '\n' + OVERLAY + '\n' + ADDRESS + '\n' + ORB, sandbox);
|
|
67
|
-
return { sandbox, progress: () => liveProgress.innerHTML, heading: () => doneH.textContent };
|
|
70
|
+
return { sandbox, progress: () => liveProgress.innerHTML, heading: () => doneH.textContent, gates };
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
const NOW = () => new Date().toISOString();
|
|
@@ -93,13 +96,16 @@ test('once the lookup answers, the done panel links the address', () => {
|
|
|
93
96
|
assert.equal(terminal, false, 'resolving is not yet answering — the panel still waits for the probe');
|
|
94
97
|
assert.match(h.progress(), ADDRESS_LINK);
|
|
95
98
|
assert.equal(h.heading(), 'Setting up Lantern', 'and the heading claims nothing yet (task 1004504)');
|
|
99
|
+
assert.deepEqual(h.gates, [], 'and the invite box stays shut: the hall has not answered (task 1004503)');
|
|
96
100
|
const live = boot();
|
|
97
101
|
assert.equal(live.sandbox.renderProgress({ ...base, dns_resolves: true, last_verified_up: true, last_verified_at: NOW() }), true);
|
|
98
102
|
assert.match(live.progress(), ADDRESS_LINK);
|
|
99
103
|
assert.equal(live.heading(), 'Lantern is alive.', 'the alive moment, only on a verified answer');
|
|
104
|
+
assert.deepEqual(live.gates, [true], 'and the invite box opens with it (task 1004503)');
|
|
100
105
|
const down = boot();
|
|
101
106
|
down.sandbox.renderProgress({ ...base, dns_resolves: true, last_verified_up: false, last_verified_at: NOW() });
|
|
102
107
|
assert.equal(down.heading(), 'Setting up Lantern', 'a down probe is never "alive"');
|
|
108
|
+
assert.deepEqual(down.gates, [], 'nor opens the invite box');
|
|
103
109
|
});
|
|
104
110
|
|
|
105
111
|
test('the card and the manage page: no link and no name before the lookup answers, in every pre-live state', () => {
|
package/tests/ui_design_kit.mjs
CHANGED
|
@@ -34,7 +34,7 @@ test('the platform floors are a pinned list — a floor cannot vanish quietly',
|
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
test('the shipped states files validate and sit beside the page they name', () => {
|
|
37
|
-
for (const rel of ['modules/public-landing/public/projects.states.json', 'modules/public-landing/public/projects-empty.states.json', 'modules/public-landing/public/projects-dns-pending.states.json','modules/public-landing/public/index.states.json', 'modules/public-landing/public/account.states.json', 'modules/public-landing/public/account-private.states.json', 'modules/hall-ui/public/ranks.states.json']) {
|
|
37
|
+
for (const rel of ['modules/public-landing/public/projects.states.json', 'modules/public-landing/public/projects-empty.states.json', 'modules/public-landing/public/projects-dns-pending.states.json', 'modules/public-landing/public/projects-hall-live.states.json','modules/public-landing/public/index.states.json', 'modules/public-landing/public/account.states.json', 'modules/public-landing/public/account-private.states.json', 'modules/hall-ui/public/ranks.states.json']) {
|
|
38
38
|
const json = readJson(rel);
|
|
39
39
|
const v = lib.validateStatesFile(json);
|
|
40
40
|
assert.deepEqual(v.errors, [], `${rel}: ${v.errors.join('; ')}`);
|