@bongos/core 1.19.395 → 1.19.396
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 +24 -19
- package/clients/bongos-client/index.d.ts +1 -1
- package/docs/api/openapi.json +4 -0
- package/docs/api-reference.md +1 -1
- package/docs/copy-inventory.md +10 -8
- package/docs/copy-registry.json +29 -11
- package/modules/hall-ui/public/my-projects.css +22 -0
- package/modules/hall-ui/public/my-projects.html +7 -1
- package/modules/hall-ui/public/my-projects.js +17 -11
- package/modules/platform-identity/routes/my-projects.js +50 -3
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +3 -1
- package/tests/join_application_note.mjs +112 -0
package/.bongos-core.json
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.19.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.396",
|
|
6
|
+
"core_contract": "1.19.396",
|
|
7
|
+
"source_commit": "b4632d6e8d92e8096e958762a36e810fc24c9c35",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-08-28T16:
|
|
9
|
+
"built_at": "2026-08-28T16:08:46.136Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 345,
|
|
13
13
|
"agent_docs_stubbed": 23,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 1635,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2003,
|
|
20
|
+
"tree_sha256": "950c8c43ac98c0e161e25a1c17cd699ad9582392105dcd0abadc8efc090a95c8",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -372,7 +372,7 @@
|
|
|
372
372
|
{
|
|
373
373
|
"path": "clients/bongos-client/index.d.ts",
|
|
374
374
|
"mode": "0000644",
|
|
375
|
-
"sha256": "
|
|
375
|
+
"sha256": "88bfa3b136c58ce104a56cb2b7c922f11b55ae9e8ca0ec0d30d67bdb78ed70cf"
|
|
376
376
|
},
|
|
377
377
|
{
|
|
378
378
|
"path": "clients/bongos-client/index.mjs",
|
|
@@ -1497,12 +1497,12 @@
|
|
|
1497
1497
|
{
|
|
1498
1498
|
"path": "docs/api-reference.md",
|
|
1499
1499
|
"mode": "0000644",
|
|
1500
|
-
"sha256": "
|
|
1500
|
+
"sha256": "567afe22acead45477a501c61b9aea2f6a99eac1cfa3cf87e143f7b5077567ca"
|
|
1501
1501
|
},
|
|
1502
1502
|
{
|
|
1503
1503
|
"path": "docs/api/openapi.json",
|
|
1504
1504
|
"mode": "0000644",
|
|
1505
|
-
"sha256": "
|
|
1505
|
+
"sha256": "e53b70240119fe856005a7627177d26bf559ab60fb003db5a7a31274fa432616"
|
|
1506
1506
|
},
|
|
1507
1507
|
{
|
|
1508
1508
|
"path": "docs/architecture.md",
|
|
@@ -1527,12 +1527,12 @@
|
|
|
1527
1527
|
{
|
|
1528
1528
|
"path": "docs/copy-inventory.md",
|
|
1529
1529
|
"mode": "0000644",
|
|
1530
|
-
"sha256": "
|
|
1530
|
+
"sha256": "52655bf8c7a2c5860a1ce4486f1715769eba8b3e7162a4e43c78d582c935b7b0"
|
|
1531
1531
|
},
|
|
1532
1532
|
{
|
|
1533
1533
|
"path": "docs/copy-registry.json",
|
|
1534
1534
|
"mode": "0000644",
|
|
1535
|
-
"sha256": "
|
|
1535
|
+
"sha256": "da37e58c70ef12e106d81be96072ee66ec52cb26ac4e624b18ca2455b90719d3"
|
|
1536
1536
|
},
|
|
1537
1537
|
{
|
|
1538
1538
|
"path": "docs/design/apex-pass-2-direction.md",
|
|
@@ -3912,17 +3912,17 @@
|
|
|
3912
3912
|
{
|
|
3913
3913
|
"path": "modules/hall-ui/public/my-projects.css",
|
|
3914
3914
|
"mode": "0000644",
|
|
3915
|
-
"sha256": "
|
|
3915
|
+
"sha256": "443e24fc3e403cfc97c36a8f81fe81a540be84cbe52b6af86a1476fb87bc44b6"
|
|
3916
3916
|
},
|
|
3917
3917
|
{
|
|
3918
3918
|
"path": "modules/hall-ui/public/my-projects.html",
|
|
3919
3919
|
"mode": "0000644",
|
|
3920
|
-
"sha256": "
|
|
3920
|
+
"sha256": "764221e2425c7430e83469fc865198e1020bcc8c5950aae6c3c1cecb49132721"
|
|
3921
3921
|
},
|
|
3922
3922
|
{
|
|
3923
3923
|
"path": "modules/hall-ui/public/my-projects.js",
|
|
3924
3924
|
"mode": "0000644",
|
|
3925
|
-
"sha256": "
|
|
3925
|
+
"sha256": "e664a08787727fcd7019fe3008e3b9d63d07b9ab8be5043a46d3fcac5218f261"
|
|
3926
3926
|
},
|
|
3927
3927
|
{
|
|
3928
3928
|
"path": "modules/hall-ui/public/not-ready.html",
|
|
@@ -4737,7 +4737,7 @@
|
|
|
4737
4737
|
{
|
|
4738
4738
|
"path": "modules/platform-identity/routes/my-projects.js",
|
|
4739
4739
|
"mode": "0000644",
|
|
4740
|
-
"sha256": "
|
|
4740
|
+
"sha256": "fc948963cef6f7ef71b488cc5627310b6b217f3f62e13030b4da42a08e92796c"
|
|
4741
4741
|
},
|
|
4742
4742
|
{
|
|
4743
4743
|
"path": "modules/platform-identity/routes/profile.js",
|
|
@@ -5212,12 +5212,12 @@
|
|
|
5212
5212
|
{
|
|
5213
5213
|
"path": "package-lock.json",
|
|
5214
5214
|
"mode": "0000644",
|
|
5215
|
-
"sha256": "
|
|
5215
|
+
"sha256": "7dd6552859e62daf201d9fdd1c17f95ea4a97bac3e5dc95d175ed0c8bbac88c7"
|
|
5216
5216
|
},
|
|
5217
5217
|
{
|
|
5218
5218
|
"path": "package.json",
|
|
5219
5219
|
"mode": "0000644",
|
|
5220
|
-
"sha256": "
|
|
5220
|
+
"sha256": "292bfaf5419d894c03339b6363173872ba73e896a9dceec60ff625568fe9ed60"
|
|
5221
5221
|
},
|
|
5222
5222
|
{
|
|
5223
5223
|
"path": "public-docs/index.html",
|
|
@@ -7207,7 +7207,7 @@
|
|
|
7207
7207
|
{
|
|
7208
7208
|
"path": "src/module-api.js",
|
|
7209
7209
|
"mode": "0000644",
|
|
7210
|
-
"sha256": "
|
|
7210
|
+
"sha256": "711a3add8803459721523364c9c993e30908475bebdf65eb83789f5520e91104"
|
|
7211
7211
|
},
|
|
7212
7212
|
{
|
|
7213
7213
|
"path": "src/module-loader/catalog.js",
|
|
@@ -8744,6 +8744,11 @@
|
|
|
8744
8744
|
"mode": "0000644",
|
|
8745
8745
|
"sha256": "d00e82184fbf7aab49c9b6efe5709b14f300ca0cea277a0eb140f073ded7ff50"
|
|
8746
8746
|
},
|
|
8747
|
+
{
|
|
8748
|
+
"path": "tests/join_application_note.mjs",
|
|
8749
|
+
"mode": "0000644",
|
|
8750
|
+
"sha256": "6db72752d69ebdfcc82bdf4f0e2445d7c8a939f6b4b88c1915c9f1d2f8eb9d76"
|
|
8751
|
+
},
|
|
8747
8752
|
{
|
|
8748
8753
|
"path": "tests/join_relay_outcome.mjs",
|
|
8749
8754
|
"mode": "0000644",
|
|
@@ -292,7 +292,7 @@ export interface PostModulesKeySubmitResponse { ok: boolean; submission: unknown
|
|
|
292
292
|
export interface PostMyProjectsClientIdLeaveResponse { ok: boolean; left: unknown }
|
|
293
293
|
export interface PostMyProjectsInvitesClientIdAcceptResponse { ok: boolean; accepted: boolean }
|
|
294
294
|
export interface PostMyProjectsInvitesClientIdDeclineResponse { ok: boolean; accepted: boolean }
|
|
295
|
-
export interface PostMyProjectsJoinRequest { origin: string }
|
|
295
|
+
export interface PostMyProjectsJoinRequest { origin: string; note?: string }
|
|
296
296
|
export interface PostMyProjectsJoinResponse { ok: boolean; status: unknown; project: unknown; message: unknown }
|
|
297
297
|
export interface PostOverrideRequestsIdDecideRequest { decision: string; note?: string }
|
|
298
298
|
export interface PostProjectsInviteRequest { client_id: string; github_login: string }
|
package/docs/api/openapi.json
CHANGED
|
@@ -18967,6 +18967,10 @@
|
|
|
18967
18967
|
"origin": {
|
|
18968
18968
|
"type": "string",
|
|
18969
18969
|
"maxLength": 512
|
|
18970
|
+
},
|
|
18971
|
+
"note": {
|
|
18972
|
+
"type": "string",
|
|
18973
|
+
"maxLength": 400
|
|
18970
18974
|
}
|
|
18971
18975
|
},
|
|
18972
18976
|
"required": [
|
package/docs/api-reference.md
CHANGED
|
@@ -395,7 +395,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
|
|
|
395
395
|
| GET | `/api/bongos/my-projects/invites` | `any-builder` | — | the caller's OWN pending invitations (ADR 0141 §5). |
|
|
396
396
|
| POST | `/api/bongos/my-projects/invites/:clientId/accept` | `any-builder` | — | accept an invite (pending → builder) for the caller's OWN account. |
|
|
397
397
|
| POST | `/api/bongos/my-projects/invites/:clientId/decline` | `any-builder` | — | decline an invite (delete the pending row) for the caller's OWN account. |
|
|
398
|
-
| POST | `/api/bongos/my-projects/join` | `any-builder` | `origin` | request to join a project. |
|
|
398
|
+
| POST | `/api/bongos/my-projects/join` | `any-builder` | `origin`, `note` | request to join a project. |
|
|
399
399
|
|
|
400
400
|
## `override-requests` (2)
|
|
401
401
|
|
package/docs/copy-inventory.md
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
> Deterministic on a given tree — regenerate and commit rather than hand-editing.
|
|
5
5
|
> Machine-readable twin: [`copy-registry.json`](copy-registry.json).
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**1535 strings** across 3 surfaces · 1336 distinct pieces of copy · 1535 certain, 0 heuristic · 154 shown in more than one place.
|
|
8
8
|
|
|
9
9
|
## Surfaces
|
|
10
10
|
|
|
11
11
|
| surface | lives at | files | strings |
|
|
12
12
|
|---|---|---|---|
|
|
13
|
-
| `builders-hall` | `modules/hall-ui/public` | 71 |
|
|
13
|
+
| `builders-hall` | `modules/hall-ui/public` | 71 | 1130 |
|
|
14
14
|
| `status-dashboard` | `modules/status-ui/public` | 4 | 98 |
|
|
15
15
|
| `landing` | `public-landing` | 5 | 307 |
|
|
16
16
|
|
|
@@ -78,7 +78,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
78
78
|
| `c10806b50c61` | 2x | Claim task {…} | modules/hall-ui/public/goals-render.js:280, modules/hall-ui/public/work.js:417 |
|
|
79
79
|
| … | | _114 more in the JSON registry_ | |
|
|
80
80
|
|
|
81
|
-
## `builders-hall` —
|
|
81
|
+
## `builders-hall` — 1130 strings
|
|
82
82
|
|
|
83
83
|
| id | conf | text | where |
|
|
84
84
|
|---|---|---|---|
|
|
@@ -135,6 +135,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
135
135
|
| `6ca31dd5eb91` | certain | Acknowledge the primer | `modules/hall-ui/public/primer.html:65` |
|
|
136
136
|
| `4461b13f95c2` | certain | Actions apply at once. Closing stops and deletes the box; you can start a fresh one anytime. | `modules/hall-ui/public/settings.html:162` |
|
|
137
137
|
| `f6036360c90c` | certain | Activity, by craft | `modules/hall-ui/public/profile.html:84` |
|
|
138
|
+
| `b8476c677e7c` | certain | Add a note (optional) | `modules/hall-ui/public/my-projects.html:52` |
|
|
138
139
|
| `62eded408097` | certain | Add {…} | `modules/hall-ui/public/profile-nudge.js:68` |
|
|
139
140
|
| `7cf0163d4cde` | certain | Aggregate totals across every recorded work session. | `modules/hall-ui/public/sessions.html:35` |
|
|
140
141
|
| `8b736e365191` | certain | All builders | `modules/hall-ui/public/hall-render.js:1039` |
|
|
@@ -148,6 +149,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
148
149
|
| `69f0ca4a4d84` | certain | Analytics scope | `modules/hall-ui/public/hall-render.js:1037` |
|
|
149
150
|
| `14bdceaac078` | certain | Answer the five questions and this spark becomes a Full Idea. | `modules/hall-ui/public/ideas.js:423` |
|
|
150
151
|
| `52ed0e2e3470` | certain | Any rank | `modules/hall-ui/public/task-lib.js:62` |
|
|
152
|
+
| `1ab0e6d5b079` | certain | Anything the project's owner should know — what you'd like to work on, who you are. | `modules/hall-ui/public/my-projects.html:54` |
|
|
151
153
|
| `c89833aa985a` | certain | Approval failed — try again. | `modules/hall-ui/public/pair.js:92` |
|
|
152
154
|
| `0832ba017c90` | certain | Approve & re-run | `modules/hall-ui/public/gate.js:100` |
|
|
153
155
|
| `222121f67525` | certain | Approve only if you just clicked “Connect” in the Dev Box app on your own machine. Approving signs that app in | `modules/hall-ui/public/pair.html:30` |
|
|
@@ -306,8 +308,8 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
306
308
|
| `8b470cebf53d` | certain | Could not remove your key. Try again. | `modules/hall-ui/public/settings.js:1093` |
|
|
307
309
|
| `4cf6bc8b377b` | certain | Could not remove. Try again. | `modules/hall-ui/public/settings.js:1092` |
|
|
308
310
|
| `7a778d188e87` | certain | Could not remove: {…} | `modules/hall-ui/public/government.js:336` |
|
|
309
|
-
| `ebeda72a6c15` | certain | Could not request access right now. | `modules/hall-ui/public/my-projects.js:
|
|
310
|
-
| `ebeda72a6c15` | certain | Could not request access right now. | `modules/hall-ui/public/my-projects.js:
|
|
311
|
+
| `ebeda72a6c15` | certain | Could not request access right now. | `modules/hall-ui/public/my-projects.js:146` |
|
|
312
|
+
| `ebeda72a6c15` | certain | Could not request access right now. | `modules/hall-ui/public/my-projects.js:148` |
|
|
311
313
|
| `bb5a7753e62b` | certain | Could not reset: {…} | `modules/hall-ui/public/government.js:275` |
|
|
312
314
|
| `b526ee0b8c48` | certain | Could not save just now. Try again. | `modules/hall-ui/public/builders.js:459` |
|
|
313
315
|
| `41a351aea8d4` | certain | Could not save the volume. Try again. | `modules/hall-ui/public/settings.js:214` |
|
|
@@ -338,7 +340,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
338
340
|
| `278e8281c82f` | certain | Could not vote: {…} | `modules/hall-ui/public/government.js:913` |
|
|
339
341
|
| `a48e9f098392` | certain | Could not {…} just now. | `modules/hall-ui/public/goals-render.js:951` |
|
|
340
342
|
| `cb09ee26014f` | certain | Could not {…}: {…} | `modules/hall-ui/public/government.js:288` |
|
|
341
|
-
| `7f600651bbf1` | certain | Couldn’t load your projects right now. | `modules/hall-ui/public/my-projects.js:
|
|
343
|
+
| `7f600651bbf1` | certain | Couldn’t load your projects right now. | `modules/hall-ui/public/my-projects.js:117` |
|
|
342
344
|
| `28ec06f6060a` | certain | Counted, not judged: each question is worth the same, and earns its share in proportion to how much real text | `modules/hall-ui/public/ideas.html:104` |
|
|
343
345
|
| `38827a954f3b` | certain | Create a custom rank first. | `modules/hall-ui/public/government.js:419` |
|
|
344
346
|
| `dd8ead7adc9a` | certain | Create rank | `modules/hall-ui/public/government.html:69` |
|
|
@@ -383,7 +385,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
383
385
|
| `7f1b1c32d360` | certain | Each slash-command can run on a different model. The system has a default for every skill; choose another to o | `modules/hall-ui/public/settings.html:245` |
|
|
384
386
|
| `854a21d2eb39` | certain | Eight achievements recognize sustained contribution. Earned ones are highlighted; the rest show what’s left to | `modules/hall-ui/public/hall-widgets.js:197` |
|
|
385
387
|
| `58d381535285` | certain | Engagement reports | `modules/hall-ui/public/watch.html:120` |
|
|
386
|
-
| `35d1ae09a6b3` | certain | Enter a project URL. | `modules/hall-ui/public/my-projects.js:
|
|
388
|
+
| `35d1ae09a6b3` | certain | Enter a project URL. | `modules/hall-ui/public/my-projects.js:124` |
|
|
387
389
|
| `bd7a40639a1c` | certain | Estimate (min) | `modules/hall-ui/public/task-detail.js:362` |
|
|
388
390
|
| `c15d1f0ddfb6` | certain | Estimation accuracy | `modules/hall-ui/public/watch.html:133` |
|
|
389
391
|
| `44f928d13699` | certain | Event sounds | `modules/hall-ui/public/settings.html:282` |
|
|
@@ -826,7 +828,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
826
828
|
| `44579eef0c8d` | certain | Tasks — {{worldName}} | `modules/hall-ui/public/work.html:6` |
|
|
827
829
|
| `44579eef0c8d` | certain | Tasks — {{worldName}} | `modules/hall-ui/public/work.html:6` |
|
|
828
830
|
| `2f4f047dc1b4` | certain | Text size | `modules/hall-ui/public/shell.js:321` |
|
|
829
|
-
| `4a8d1beb6f98` | certain | That project isn’t on the platform. | `modules/hall-ui/public/my-projects.js:
|
|
831
|
+
| `4a8d1beb6f98` | certain | That project isn’t on the platform. | `modules/hall-ui/public/my-projects.js:145` |
|
|
830
832
|
| `c0e28f30524d` | certain | That project's own standing, as reported by that project. | `modules/hall-ui/public/profile.js:230` |
|
|
831
833
|
| `50198c465225` | certain | The 16-value rank enum in three bands, which ranks are live, and the permissions each rank holds. | `modules/hall-ui/public/diagrams.html:68` |
|
|
832
834
|
| `1569b8838862` | certain | The Dev Box app now acts as | `modules/hall-ui/public/pair.js:40` |
|
package/docs/copy-registry.json
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
"generator": "scripts/gds/copy-inventory.js",
|
|
4
4
|
"goal": 1000074,
|
|
5
5
|
"counts": {
|
|
6
|
-
"strings":
|
|
7
|
-
"unique_ids":
|
|
8
|
-
"certain":
|
|
6
|
+
"strings": 1535,
|
|
7
|
+
"unique_ids": 1336,
|
|
8
|
+
"certain": 1535,
|
|
9
9
|
"heuristic": 0,
|
|
10
10
|
"duplicate_ids": 154
|
|
11
11
|
},
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"builders-hall": {
|
|
14
14
|
"at": "modules/hall-ui/public",
|
|
15
15
|
"files": 71,
|
|
16
|
-
"strings":
|
|
16
|
+
"strings": 1130
|
|
17
17
|
},
|
|
18
18
|
"status-dashboard": {
|
|
19
19
|
"at": "modules/status-ui/public",
|
|
@@ -589,8 +589,8 @@
|
|
|
589
589
|
"text": "Could not request access right now.",
|
|
590
590
|
"occurrences": 2,
|
|
591
591
|
"at": [
|
|
592
|
-
"modules/hall-ui/public/my-projects.js:
|
|
593
|
-
"modules/hall-ui/public/my-projects.js:
|
|
592
|
+
"modules/hall-ui/public/my-projects.js:146",
|
|
593
|
+
"modules/hall-ui/public/my-projects.js:148"
|
|
594
594
|
]
|
|
595
595
|
},
|
|
596
596
|
{
|
|
@@ -2102,6 +2102,15 @@
|
|
|
2102
2102
|
"origin": "html-text",
|
|
2103
2103
|
"confidence": "certain"
|
|
2104
2104
|
},
|
|
2105
|
+
{
|
|
2106
|
+
"id": "b8476c677e7c",
|
|
2107
|
+
"surface": "builders-hall",
|
|
2108
|
+
"text": "Add a note (optional)",
|
|
2109
|
+
"file": "modules/hall-ui/public/my-projects.html",
|
|
2110
|
+
"line": 52,
|
|
2111
|
+
"origin": "html-text",
|
|
2112
|
+
"confidence": "certain"
|
|
2113
|
+
},
|
|
2105
2114
|
{
|
|
2106
2115
|
"id": "62eded408097",
|
|
2107
2116
|
"surface": "builders-hall",
|
|
@@ -2219,6 +2228,15 @@
|
|
|
2219
2228
|
"origin": "js-markup",
|
|
2220
2229
|
"confidence": "certain"
|
|
2221
2230
|
},
|
|
2231
|
+
{
|
|
2232
|
+
"id": "1ab0e6d5b079",
|
|
2233
|
+
"surface": "builders-hall",
|
|
2234
|
+
"text": "Anything the project's owner should know — what you'd like to work on, who you are.",
|
|
2235
|
+
"file": "modules/hall-ui/public/my-projects.html",
|
|
2236
|
+
"line": 54,
|
|
2237
|
+
"origin": "html-attr:placeholder",
|
|
2238
|
+
"confidence": "certain"
|
|
2239
|
+
},
|
|
2222
2240
|
{
|
|
2223
2241
|
"id": "c89833aa985a",
|
|
2224
2242
|
"surface": "builders-hall",
|
|
@@ -3646,7 +3664,7 @@
|
|
|
3646
3664
|
"surface": "builders-hall",
|
|
3647
3665
|
"text": "Could not request access right now.",
|
|
3648
3666
|
"file": "modules/hall-ui/public/my-projects.js",
|
|
3649
|
-
"line":
|
|
3667
|
+
"line": 146,
|
|
3650
3668
|
"origin": "js-text-assign",
|
|
3651
3669
|
"confidence": "certain"
|
|
3652
3670
|
},
|
|
@@ -3655,7 +3673,7 @@
|
|
|
3655
3673
|
"surface": "builders-hall",
|
|
3656
3674
|
"text": "Could not request access right now.",
|
|
3657
3675
|
"file": "modules/hall-ui/public/my-projects.js",
|
|
3658
|
-
"line":
|
|
3676
|
+
"line": 148,
|
|
3659
3677
|
"origin": "js-text-assign",
|
|
3660
3678
|
"confidence": "certain"
|
|
3661
3679
|
},
|
|
@@ -3934,7 +3952,7 @@
|
|
|
3934
3952
|
"surface": "builders-hall",
|
|
3935
3953
|
"text": "Couldn’t load your projects right now.",
|
|
3936
3954
|
"file": "modules/hall-ui/public/my-projects.js",
|
|
3937
|
-
"line":
|
|
3955
|
+
"line": 117,
|
|
3938
3956
|
"origin": "js-text-assign",
|
|
3939
3957
|
"confidence": "certain"
|
|
3940
3958
|
},
|
|
@@ -4339,7 +4357,7 @@
|
|
|
4339
4357
|
"surface": "builders-hall",
|
|
4340
4358
|
"text": "Enter a project URL.",
|
|
4341
4359
|
"file": "modules/hall-ui/public/my-projects.js",
|
|
4342
|
-
"line":
|
|
4360
|
+
"line": 124,
|
|
4343
4361
|
"origin": "js-text-assign",
|
|
4344
4362
|
"confidence": "certain"
|
|
4345
4363
|
},
|
|
@@ -8326,7 +8344,7 @@
|
|
|
8326
8344
|
"surface": "builders-hall",
|
|
8327
8345
|
"text": "That project isn’t on the platform.",
|
|
8328
8346
|
"file": "modules/hall-ui/public/my-projects.js",
|
|
8329
|
-
"line":
|
|
8347
|
+
"line": 145,
|
|
8330
8348
|
"origin": "js-text-assign",
|
|
8331
8349
|
"confidence": "certain"
|
|
8332
8350
|
},
|
|
@@ -37,4 +37,26 @@
|
|
|
37
37
|
background: var(--surface);
|
|
38
38
|
color: var(--ink);
|
|
39
39
|
}
|
|
40
|
+
/* The application's optional note (task 1002330). Sits under the one-click join
|
|
41
|
+
row so the click stays the primary act and the note is plainly skippable. */
|
|
42
|
+
.join-note-label {
|
|
43
|
+
display: block;
|
|
44
|
+
margin: 10px 0 4px;
|
|
45
|
+
color: var(--ink-soft);
|
|
46
|
+
font-size: 12px;
|
|
47
|
+
}
|
|
48
|
+
.join-note {
|
|
49
|
+
display: block;
|
|
50
|
+
width: 100%;
|
|
51
|
+
box-sizing: border-box;
|
|
52
|
+
font: inherit;
|
|
53
|
+
font-size: 13px;
|
|
54
|
+
line-height: 1.4;
|
|
55
|
+
padding: 6px 10px;
|
|
56
|
+
resize: vertical;
|
|
57
|
+
border: 1px solid var(--border-strong);
|
|
58
|
+
border-radius: var(--radius-sm);
|
|
59
|
+
background: var(--surface);
|
|
60
|
+
color: var(--ink);
|
|
61
|
+
}
|
|
40
62
|
.note { margin: 10px 0 0; color: var(--ink-soft); font-size: 13px; min-height: 1.2em; }
|
|
@@ -46,7 +46,13 @@
|
|
|
46
46
|
<input id="join-origin" type="url" placeholder="https://project.cloudbongos.com" autocomplete="off">
|
|
47
47
|
<button id="join-btn" type="button" class="btn-ghost">Request to join</button>
|
|
48
48
|
</div>
|
|
49
|
-
|
|
49
|
+
<!-- An application is one click; the note is the applicant's own words,
|
|
50
|
+
and it is optional on purpose (task 1002330 — lightweight, never an
|
|
51
|
+
owner-defined form). It reaches the project owner's queue. -->
|
|
52
|
+
<label class="join-note-label" for="join-note">Add a note (optional)</label>
|
|
53
|
+
<textarea id="join-note" class="join-note" rows="2" maxlength="400"
|
|
54
|
+
placeholder="Anything the project's owner should know — what you'd like to work on, who you are."></textarea>
|
|
55
|
+
<p id="join-status" class="note"></p>
|
|
50
56
|
</section>
|
|
51
57
|
</main>
|
|
52
58
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// my-projects.js — the signed-in builder's cross-project view (ADR 0141 §3, task
|
|
2
2
|
// #1002112). Fetches /api/gds/my-projects (session cookie, same-origin) and renders
|
|
3
|
-
// owned + building-on lists with self-service leave, plus a join-by-origin form
|
|
3
|
+
// owned + building-on lists with self-service leave, plus a join-by-origin form
|
|
4
|
+
// whose application is one click plus an optional note (task 1002330).
|
|
4
5
|
// All network values are rendered via textContent — never innerHTML.
|
|
5
6
|
(function () {
|
|
6
7
|
'use strict';
|
|
@@ -8,7 +9,8 @@
|
|
|
8
9
|
var buildingEl = document.getElementById('building');
|
|
9
10
|
var joinBtn = document.getElementById('join-btn');
|
|
10
11
|
var joinInput = document.getElementById('join-origin');
|
|
11
|
-
var
|
|
12
|
+
var joinNoteInput = document.getElementById('join-note'); // the applicant's own words (optional)
|
|
13
|
+
var joinStatus = document.getElementById('join-status'); // what the project answered
|
|
12
14
|
|
|
13
15
|
function el(tag, cls) { var e = document.createElement(tag); if (cls) e.className = cls; return e; }
|
|
14
16
|
|
|
@@ -119,27 +121,31 @@
|
|
|
119
121
|
|
|
120
122
|
joinBtn.addEventListener('click', function () {
|
|
121
123
|
var origin = (joinInput.value || '').trim();
|
|
122
|
-
if (!origin) {
|
|
124
|
+
if (!origin) { joinStatus.textContent = 'Enter a project URL.'; return; }
|
|
125
|
+
var note = (joinNoteInput.value || '').trim();
|
|
123
126
|
joinBtn.disabled = true;
|
|
124
|
-
|
|
127
|
+
joinStatus.textContent = 'Requesting…';
|
|
125
128
|
fetch('/api/gds/my-projects/join', {
|
|
126
129
|
method: 'POST',
|
|
127
130
|
headers: { 'Content-Type': 'application/json' },
|
|
128
|
-
|
|
131
|
+
/* note is omitted entirely when blank — an application with nothing written
|
|
132
|
+
is still an application (task 1002330). */
|
|
133
|
+
body: JSON.stringify(note ? { origin: origin, note: note } : { origin: origin }),
|
|
129
134
|
})
|
|
130
135
|
.then(function (r) { return r.json().then(function (b) { return { ok: r.ok, body: b }; }); })
|
|
131
136
|
.then(function (res) {
|
|
132
137
|
joinBtn.disabled = false;
|
|
133
138
|
if (res.ok && res.body && res.body.message) {
|
|
134
|
-
|
|
135
|
-
/* a door refusal rides a 200 (task 1003044): keep the address
|
|
139
|
+
joinStatus.textContent = res.body.message;
|
|
140
|
+
/* a door refusal rides a 200 (task 1003044): keep the address AND the
|
|
141
|
+
note the applicant wrote, and don't repaint as if a request went out */
|
|
136
142
|
var refused = res.body.status === 'invite_only' || res.body.status === 'open_enrollment' || res.body.status === 'members_only';
|
|
137
|
-
if (!refused) { joinInput.value = ''; load(); }
|
|
143
|
+
if (!refused) { joinInput.value = ''; joinNoteInput.value = ''; load(); }
|
|
138
144
|
}
|
|
139
|
-
else if (res.body && res.body.error && res.body.error.code === 'unknown_project') {
|
|
140
|
-
else {
|
|
145
|
+
else if (res.body && res.body.error && res.body.error.code === 'unknown_project') { joinStatus.textContent = 'That project isn’t on the platform.'; }
|
|
146
|
+
else { joinStatus.textContent = 'Could not request access right now.'; }
|
|
141
147
|
})
|
|
142
|
-
.catch(function () { joinBtn.disabled = false;
|
|
148
|
+
.catch(function () { joinBtn.disabled = false; joinStatus.textContent = 'Could not request access right now.'; });
|
|
143
149
|
});
|
|
144
150
|
|
|
145
151
|
load();
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
// project "My Projects" view + self-service join / leave (ADR 0141 §3, task #1002112).
|
|
3
3
|
//
|
|
4
4
|
// GET /my-projects — projects this account builds on, owned grouped apart
|
|
5
|
-
// POST /my-projects/join —
|
|
5
|
+
// POST /my-projects/join — apply to a project: one click plus an optional
|
|
6
|
+
// note in the applicant's own words (task 1002330).
|
|
7
|
+
// Relayed; the project executes and decides.
|
|
6
8
|
// POST /my-projects/:clientId/leave — self-service offboard (reflected to the hub)
|
|
7
9
|
//
|
|
8
10
|
// All gated on a live HUB session (requireBuilder). Authority stays LOCAL: JOIN only
|
|
@@ -44,6 +46,40 @@ function joinRelayOutcome(status, body) {
|
|
|
44
46
|
return { status: outcome, message: RELAY_SENTENCE[outcome] };
|
|
45
47
|
}
|
|
46
48
|
|
|
49
|
+
// The provenance line the project's owner reads when an application arrives
|
|
50
|
+
// through the platform rather than off their own landing page. It is the HUB's
|
|
51
|
+
// sentence, not the applicant's — see relayApplicationNote.
|
|
52
|
+
const RELAY_PROVENANCE = 'via platform My Projects';
|
|
53
|
+
|
|
54
|
+
// What an applicant may write, and what the project will store. The project's
|
|
55
|
+
// public POST /access-requests caps `note` at 500 chars and `display_name` at
|
|
56
|
+
// 100, and refuses anything longer with a 400 — which joinRelayOutcome cannot
|
|
57
|
+
// read, so the hub would tell the applicant the project "could not be reached".
|
|
58
|
+
// A lie about someone else's door because our own field was too long is the bug
|
|
59
|
+
// these two caps exist to prevent: the hub asks for less than the project takes,
|
|
60
|
+
// and clamps anyway.
|
|
61
|
+
const APPLICANT_NOTE_MAX = 400;
|
|
62
|
+
const APPLICANT_NAME_MAX = 100;
|
|
63
|
+
|
|
64
|
+
// relayApplicationNote — the note that reaches the project's queue: the
|
|
65
|
+
// applicant's own words plus where the request came from (task 1002330). An
|
|
66
|
+
// application with nothing written is still an application, so an absent note
|
|
67
|
+
// relays the provenance line alone — exactly what this route sent before the
|
|
68
|
+
// applicant could write anything. Pure; clamped so the composed string always
|
|
69
|
+
// fits the project's 500-char column.
|
|
70
|
+
function relayApplicationNote(note) {
|
|
71
|
+
const written = String(note == null ? '' : note).trim().slice(0, APPLICANT_NOTE_MAX);
|
|
72
|
+
return written ? `${written} (${RELAY_PROVENANCE})` : RELAY_PROVENANCE;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// relayApplicantName — the applicant's display name as the project should show
|
|
76
|
+
// it, or null when they have none (the queue then reads as the bare @login it
|
|
77
|
+
// always did). Clamped to the project's column.
|
|
78
|
+
function relayApplicantName(displayName) {
|
|
79
|
+
const name = String(displayName == null ? '' : displayName).trim().slice(0, APPLICANT_NAME_MAX);
|
|
80
|
+
return name || null;
|
|
81
|
+
}
|
|
82
|
+
|
|
47
83
|
module.exports = function myProjectsRoutes() {
|
|
48
84
|
const router = express.Router();
|
|
49
85
|
|
|
@@ -75,7 +111,10 @@ module.exports = function myProjectsRoutes() {
|
|
|
75
111
|
// stays local). SSRF guard: only relay to an origin that is a REGISTERED, active
|
|
76
112
|
// client — never an arbitrary builder-supplied URL.
|
|
77
113
|
router.post('/my-projects/join', requireBuilder, async (req, res) => {
|
|
78
|
-
if (validateOrRespond(req, res, {
|
|
114
|
+
if (validateOrRespond(req, res, {
|
|
115
|
+
origin: { required: true, type: 'string', maxLength: 512 },
|
|
116
|
+
note: { type: 'string', maxLength: APPLICANT_NOTE_MAX },
|
|
117
|
+
})) return;
|
|
79
118
|
try {
|
|
80
119
|
const client = await pi.getClientByOrigin(req.body.origin, { pool });
|
|
81
120
|
if (!client || client.status !== 'active' || !client.origin) {
|
|
@@ -89,7 +128,11 @@ module.exports = function myProjectsRoutes() {
|
|
|
89
128
|
const r = await fetch(`${client.origin}/api/gds/access-requests`, {
|
|
90
129
|
method: 'POST',
|
|
91
130
|
headers: { 'Content-Type': 'application/json', Accept: 'application/json', 'User-Agent': userAgent() },
|
|
92
|
-
body: JSON.stringify({
|
|
131
|
+
body: JSON.stringify({
|
|
132
|
+
github_login: req.builder.github_login,
|
|
133
|
+
display_name: relayApplicantName(req.builder.display_name),
|
|
134
|
+
note: relayApplicationNote(req.body.note),
|
|
135
|
+
}),
|
|
93
136
|
});
|
|
94
137
|
const body = await r.json().catch(() => null);
|
|
95
138
|
outcome = joinRelayOutcome(r.status, body);
|
|
@@ -164,3 +207,7 @@ module.exports = function myProjectsRoutes() {
|
|
|
164
207
|
return router;
|
|
165
208
|
};
|
|
166
209
|
module.exports.joinRelayOutcome = joinRelayOutcome;
|
|
210
|
+
module.exports.relayApplicationNote = relayApplicationNote;
|
|
211
|
+
module.exports.relayApplicantName = relayApplicantName;
|
|
212
|
+
module.exports.APPLICANT_NOTE_MAX = APPLICANT_NOTE_MAX;
|
|
213
|
+
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.396",
|
|
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.396",
|
|
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.396",
|
|
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
|
@@ -1230,8 +1230,10 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
1230
1230
|
// landed since 1.19.393 with no explicit bump. run 33182535783. (task 1002620)
|
|
1231
1231
|
// 1.19.395 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1232
1232
|
// landed since 1.19.394 with no explicit bump. run 33188021384. (task 1002620)
|
|
1233
|
+
// 1.19.396 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1234
|
+
// landed since 1.19.395 with no explicit bump. run 33188525568. (task 1002620)
|
|
1233
1235
|
// ---------------------------------------------------------------------------
|
|
1234
|
-
const CORE_VERSION = '1.19.
|
|
1236
|
+
const CORE_VERSION = '1.19.396'; // CI auto-patch carrier (ADR 0161); see changelog above
|
|
1235
1237
|
|
|
1236
1238
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
1237
1239
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// tests/join_application_note.mjs — a project-level application is one click plus
|
|
2
|
+
// an optional note (task 1002330, ADR 0182 D6 / the admin-console spec's D2).
|
|
3
|
+
//
|
|
4
|
+
// The hub does not admit anyone; it relays the application to the project's own
|
|
5
|
+
// public POST /access-requests, which is what the owner's queue reads. So what
|
|
6
|
+
// this file pins is the composition of that relayed row:
|
|
7
|
+
//
|
|
8
|
+
// - the applicant's own words reach the owner, with the hub's provenance line
|
|
9
|
+
// saying where the request came from;
|
|
10
|
+
// - writing nothing is still an application — the pre-1002330 behaviour;
|
|
11
|
+
// - nothing the applicant types can push the relayed fields past the project's
|
|
12
|
+
// own column caps (500 note / 100 display_name), because a 400 from the
|
|
13
|
+
// project is a status joinRelayOutcome cannot read, and the applicant would
|
|
14
|
+
// then be told the project "could not be reached" — a lie about someone
|
|
15
|
+
// else's door caused by our own field.
|
|
16
|
+
//
|
|
17
|
+
// Pure, DB-free.
|
|
18
|
+
//
|
|
19
|
+
// Run: node --test tests/join_application_note.mjs
|
|
20
|
+
|
|
21
|
+
import assert from 'node:assert/strict';
|
|
22
|
+
import { test } from 'node:test';
|
|
23
|
+
import {
|
|
24
|
+
relayApplicationNote,
|
|
25
|
+
relayApplicantName,
|
|
26
|
+
APPLICANT_NOTE_MAX,
|
|
27
|
+
} from '../modules/platform-identity/routes/my-projects.js';
|
|
28
|
+
|
|
29
|
+
// The caps the PROJECT's public POST /access-requests enforces on the row it
|
|
30
|
+
// stores (modules/onboarding/routes/access-requests.js). Restated here so this
|
|
31
|
+
// test fails if the hub ever starts asking for more than a project will take.
|
|
32
|
+
const PROJECT_NOTE_MAX = 500;
|
|
33
|
+
const PROJECT_NAME_MAX = 100;
|
|
34
|
+
|
|
35
|
+
test('the applicant’s own words reach the owner, with where it came from', () => {
|
|
36
|
+
const note = relayApplicationNote("I'd like to help with the terrain work");
|
|
37
|
+
assert.match(note, /I'd like to help with the terrain work/);
|
|
38
|
+
assert.match(note, /via platform My Projects/);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('an application with nothing written is still an application', () => {
|
|
42
|
+
for (const empty of [undefined, null, '', ' ', '\n\t']) {
|
|
43
|
+
assert.equal(relayApplicationNote(empty), 'via platform My Projects',
|
|
44
|
+
`${JSON.stringify(empty)} relays the provenance line alone`);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('the composed note always fits the project’s column', () => {
|
|
49
|
+
const composed = relayApplicationNote('x'.repeat(5000));
|
|
50
|
+
assert.ok(composed.length <= PROJECT_NOTE_MAX,
|
|
51
|
+
`composed note is ${composed.length} chars, project stores ${PROJECT_NOTE_MAX}`);
|
|
52
|
+
assert.match(composed, /via platform My Projects/, 'provenance survives the clamp');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('the hub asks for less note than the project will take', () => {
|
|
56
|
+
const suffix = relayApplicationNote('').length + ' ()'.length;
|
|
57
|
+
assert.ok(APPLICANT_NOTE_MAX + suffix <= PROJECT_NOTE_MAX,
|
|
58
|
+
'the validator cap plus the provenance suffix must fit the project’s 500');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('a display name travels so the queue reads as a person, not a bare login', () => {
|
|
62
|
+
assert.equal(relayApplicantName(' Jane Doe '), 'Jane Doe');
|
|
63
|
+
const clamped = relayApplicantName('n'.repeat(400));
|
|
64
|
+
assert.ok(clamped.length <= PROJECT_NAME_MAX);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('no display name relays null — the queue keeps the bare @login it always had', () => {
|
|
68
|
+
for (const empty of [undefined, null, '', ' ']) {
|
|
69
|
+
assert.equal(relayApplicantName(empty), null, `${JSON.stringify(empty)}`);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// The wiring — that the composed note actually travels, and that the applicant's
|
|
75
|
+
// words survive a door refusal. Source assertions (the house pattern in
|
|
76
|
+
// tests/joinability_invite_only.mjs): the relay needs a live hub session and a
|
|
77
|
+
// second running project to exercise, which no unit test can boot.
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
import fs from 'node:fs';
|
|
81
|
+
import path from 'node:path';
|
|
82
|
+
import { fileURLToPath } from 'node:url';
|
|
83
|
+
|
|
84
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
85
|
+
const src = (rel) => fs.readFileSync(path.join(ROOT, rel), 'utf8');
|
|
86
|
+
|
|
87
|
+
test('the relay sends the composed note and the applicant’s name — not a constant', () => {
|
|
88
|
+
const route = src('modules/platform-identity/routes/my-projects.js');
|
|
89
|
+
const join = (route.match(/router\.post\('\/my-projects\/join'[\s\S]*?\n \}\);\n/) || [''])[0];
|
|
90
|
+
assert.ok(join, 'the join route must exist');
|
|
91
|
+
assert.match(join, /note: relayApplicationNote\(req\.body\.note\)/, 'the applicant’s words are composed, then relayed');
|
|
92
|
+
assert.match(join, /display_name: relayApplicantName\(req\.builder\.display_name\)/);
|
|
93
|
+
assert.doesNotMatch(join, /note: 'via platform My Projects'/, 'the hardcoded note is gone');
|
|
94
|
+
assert.match(join, /note: \{ type: 'string', maxLength: APPLICANT_NOTE_MAX \}/, 'the note is declared in the route’s schema');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('the join box offers the note as optional, and never demands it', () => {
|
|
98
|
+
const html = src('modules/hall-ui/public/my-projects.html');
|
|
99
|
+
assert.match(html, /<textarea id="join-note"/, 'the note field exists');
|
|
100
|
+
assert.match(html, /maxlength="400"/, 'the field cannot outrun the route’s cap');
|
|
101
|
+
assert.match(html, /Add a note \(optional\)/, 'and it is labelled optional');
|
|
102
|
+
assert.match(html, /id="join-status"/, 'the answer line is its own element, not the note field');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('a door refusal keeps what the applicant typed; a filed request clears it', () => {
|
|
106
|
+
const js = src('modules/hall-ui/public/my-projects.js');
|
|
107
|
+
const click = (js.match(/joinBtn\.addEventListener\('click'[\s\S]*?\n \}\);\n/) || [''])[0];
|
|
108
|
+
assert.ok(click, 'the join handler must exist');
|
|
109
|
+
assert.match(click, /note \? \{ origin: origin, note: note \} : \{ origin: origin \}/, 'a blank note is omitted, not sent as ""');
|
|
110
|
+
assert.match(click, /if \(!refused\) \{ joinInput\.value = ''; joinNoteInput\.value = ''; load\(\); \}/,
|
|
111
|
+
'cleared only when the request was actually filed — a refusal keeps the words for the next try');
|
|
112
|
+
});
|