@ibgib/space-gib 0.0.33 → 0.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/dist/client/bootstrap.mjs +24 -51
  2. package/dist/client/chunk-FIAGRVBY.mjs +10933 -0
  3. package/dist/client/chunk-XLQ3UCQY.mjs +53 -0
  4. package/dist/client/css/activity-bar.css +435 -0
  5. package/dist/client/css/base.css +167 -0
  6. package/dist/client/css/components.css +218 -0
  7. package/dist/client/css/cosmic.css +557 -0
  8. package/dist/client/css/dev-tools.css +163 -0
  9. package/dist/client/css/landing.css +184 -0
  10. package/dist/client/css/layout.css +1017 -0
  11. package/dist/client/css/onboarding.css +181 -0
  12. package/dist/client/css/repos.css +740 -0
  13. package/dist/client/css/variables.css +64 -0
  14. package/dist/client/index.html +208 -31
  15. package/dist/client/index.mjs +22 -45
  16. package/dist/client/privacy.html +22 -22
  17. package/dist/client/script.mjs +1 -1
  18. package/dist/client/style.css +11 -1056
  19. package/dist/respec-gib.node.mjs +5 -0
  20. package/dist/server/server.mjs +6880 -3110
  21. package/package.json +6 -6
  22. package/src/client/AUTO-GENERATED-version.mts +1 -1
  23. package/src/client/cosmos/seed-cosmos-workspace.mts +83 -0
  24. package/src/client/css/activity-bar.css +435 -0
  25. package/src/client/css/base.css +167 -0
  26. package/src/client/css/components.css +218 -0
  27. package/src/client/css/cosmic.css +557 -0
  28. package/src/client/css/dev-tools.css +163 -0
  29. package/src/client/css/landing.css +184 -0
  30. package/src/client/css/layout.css +1017 -0
  31. package/src/client/css/onboarding.css +181 -0
  32. package/src/client/css/repos.css +740 -0
  33. package/src/client/css/variables.css +64 -0
  34. package/src/client/dev-tools/vcs-workspace.mts +7 -7
  35. package/src/client/index.html +208 -31
  36. package/src/client/privacy.html +22 -22
  37. package/src/client/style.css +11 -1056
  38. package/src/client/ui/component/changes/changes.css +413 -0
  39. package/src/client/ui/component/changes/changes.html +37 -0
  40. package/src/client/ui/component/changes/changes.mts +766 -0
  41. package/src/client/ui/component/changes/index.mts +9 -0
  42. package/src/client/ui/component/chat-view/IMPLEMENTATION.md +48 -0
  43. package/src/client/ui/component/chat-view/chat-view.css +381 -0
  44. package/src/client/ui/component/chat-view/chat-view.html +52 -0
  45. package/src/client/ui/component/chat-view/chat-view.mts +264 -0
  46. package/src/client/ui/component/chat-view/index.mts +9 -0
  47. package/src/client/ui/component/clone/IMPLEMENTATION.md +68 -0
  48. package/src/client/ui/component/clone/clone-constants.mts +16 -0
  49. package/src/client/ui/component/clone/clone-types.mts +60 -0
  50. package/src/client/ui/component/clone/clone.css +383 -0
  51. package/src/client/ui/component/clone/clone.html +89 -0
  52. package/src/client/ui/component/clone/clone.mts +648 -0
  53. package/src/client/ui/component/clone/index.mts +11 -0
  54. package/src/client/ui/component/code-editor/IMPLEMENTATION.md +89 -0
  55. package/src/client/ui/component/code-editor/code-editor.css +357 -0
  56. package/src/client/ui/component/code-editor/code-editor.html +67 -0
  57. package/src/client/ui/component/code-editor/code-editor.mts +643 -0
  58. package/src/client/ui/component/code-editor/index.mts +9 -0
  59. package/src/client/ui/component/cosmos-navigator/IMPLEMENTATION.md +31 -0
  60. package/src/client/ui/component/cosmos-navigator/cosmos-navigator.css +864 -0
  61. package/src/client/ui/component/cosmos-navigator/cosmos-navigator.html +92 -0
  62. package/src/client/ui/component/cosmos-navigator/cosmos-navigator.mts +1317 -0
  63. package/src/client/ui/component/cosmos-navigator/index.mts +18 -0
  64. package/src/client/ui/component/file-explorer/IMPLEMENTATION.md +42 -0
  65. package/src/client/ui/component/file-explorer/file-explorer.css +720 -0
  66. package/src/client/ui/component/file-explorer/file-explorer.html +55 -0
  67. package/src/client/ui/component/file-explorer/file-explorer.mts +1412 -0
  68. package/src/client/ui/component/file-explorer/index.mts +9 -0
  69. package/src/client/ui/component/nested-chat/IMPLEMENTATION.md +18 -0
  70. package/src/client/ui/component/nested-chat/chat/IMPLEMENTATION.md +14 -0
  71. package/src/client/ui/component/nested-chat/chat/chat-constants.mts +14 -0
  72. package/src/client/ui/component/nested-chat/chat/chat-helpers.mts +66 -0
  73. package/src/client/ui/component/nested-chat/chat/chat.css +654 -0
  74. package/src/client/ui/component/nested-chat/chat/chat.html +90 -0
  75. package/src/client/ui/component/nested-chat/chat/chat.mts +561 -0
  76. package/src/client/ui/component/nested-chat/chat/index.mts +7 -0
  77. package/src/client/ui/component/nested-chat/index.mts +14 -0
  78. package/src/client/ui/component/nested-chat/nested-chat-constants.mts +12 -0
  79. package/src/client/ui/component/nested-chat/nested-chat-types.mts +53 -0
  80. package/src/client/ui/component/nested-chat/nested-chat.css +142 -0
  81. package/src/client/ui/component/nested-chat/nested-chat.html +11 -0
  82. package/src/client/ui/component/nested-chat/nested-chat.mts +395 -0
  83. package/src/client/ui/component/notes-explorer/index.mts +9 -0
  84. package/src/client/ui/component/notes-explorer/notes-explorer.css +313 -0
  85. package/src/client/ui/component/notes-explorer/notes-explorer.html +136 -0
  86. package/src/client/ui/component/notes-explorer/notes-explorer.mts +240 -0
  87. package/src/client/ui/component/notes-inspector/index.mts +9 -0
  88. package/src/client/ui/component/notes-inspector/notes-inspector.css +244 -0
  89. package/src/client/ui/component/notes-inspector/notes-inspector.html +97 -0
  90. package/src/client/ui/component/notes-inspector/notes-inspector.mts +152 -0
  91. package/src/client/ui/component/pitch/IMPLEMENTATION.md +48 -0
  92. package/src/client/ui/component/pitch/index.mts +9 -0
  93. package/src/client/ui/component/pitch/pitch.css +295 -0
  94. package/src/client/ui/component/pitch/pitch.html +97 -0
  95. package/src/client/ui/component/pitch/pitch.mts +135 -0
  96. package/src/client/ui/component/timeline/IMPLEMENTATION.md +33 -0
  97. package/src/client/ui/component/timeline/index.mts +9 -0
  98. package/src/client/ui/component/timeline/timeline.css +279 -0
  99. package/src/client/ui/component/timeline/timeline.html +23 -0
  100. package/src/client/ui/component/timeline/timeline.mts +261 -0
  101. package/src/client/ui/component/timeline-item-details/IMPLEMENTATION.md +28 -0
  102. package/src/client/ui/component/timeline-item-details/index.mts +9 -0
  103. package/src/client/ui/component/timeline-item-details/timeline-item-details.css +286 -0
  104. package/src/client/ui/component/timeline-item-details/timeline-item-details.html +64 -0
  105. package/src/client/ui/component/timeline-item-details/timeline-item-details.mts +194 -0
  106. package/src/client/ui/router/index.mts +3 -0
  107. package/src/client/ui/router/space-gib-router-helpers.mts +72 -0
  108. package/src/client/ui/router/space-gib-router-helpers.respec.mts +529 -0
  109. package/src/client/ui/router/space-gib-router-service.mts +63 -0
  110. package/src/client/ui/router/space-gib-router-types.mts +49 -0
  111. package/src/client/ui/shell/cosmic-big-bang.mts +853 -0
  112. package/src/client/ui/shell/space-gib-shell-constants.mts +7 -0
  113. package/src/client/ui/shell/space-gib-shell-service.mts +2297 -32
  114. package/src/server/server.mts +20 -1
  115. package/tsconfig.test.json +5 -1
  116. package/dist/client/chunk-CRJV762I.mjs +0 -3102
  117. package/dist/client/chunk-LXRCF5OS.mjs +0 -25
  118. package/dist/client/chunk-UBDQUZZV.mjs +0 -1
@@ -0,0 +1,529 @@
1
+ import {
2
+ respecfully, iReckon, ifWe, ifWeMight // don't remove unused ifWe or ifWeMight
3
+ } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
4
+ const sir = `[${import.meta.url}]`;
5
+
6
+ import {
7
+ parseSpaceGibRoute,
8
+ formatSpaceGibRoute,
9
+ isSameRoute,
10
+ } from './space-gib-router-helpers.mjs';
11
+ import {
12
+ SpaceGibRouteInfo,
13
+ PolycosmosRouteInfo,
14
+ CosmosRouteInfo,
15
+ GalaxyRouteInfo,
16
+ RawAddrRouteInfo,
17
+ RawTjpRouteInfo,
18
+ } from './space-gib-router-types.mjs';
19
+ import { CosmosIbGib_V1 } from '@ibgib/core-gib/dist/cosmology/cosmos/cosmos-types.mjs';
20
+ import { IbGibRouterServiceBase, IbGibCosmosRouterServiceBase } from '@ibgib/web-gib/dist/ui/router/index.mjs';
21
+ import { getSpaceGibRouterSvc, SpaceGibRouterService } from './space-gib-router-service.mjs';
22
+
23
+ await respecfully(sir, 'SpaceGibRouterHelpers specs', async () => {
24
+
25
+ await respecfully(sir, 'Polycosmos Routes', async () => {
26
+ await ifWe(sir, 'parses root / as polycosmos', async () => {
27
+ const route = parseSpaceGibRoute('/');
28
+ iReckon(sir, route.kind).willEqual('polycosmos');
29
+ iReckon(sir, (route as PolycosmosRouteInfo).tenant).willEqual(undefined);
30
+ });
31
+
32
+ await ifWe(sir, 'parses empty path as polycosmos', async () => {
33
+ const route = parseSpaceGibRoute('');
34
+ iReckon(sir, route.kind).willEqual('polycosmos');
35
+ });
36
+
37
+ await ifWe(sir, 'parses /polycosmos as polycosmos', async () => {
38
+ const route = parseSpaceGibRoute('/polycosmos');
39
+ iReckon(sir, route.kind).willEqual('polycosmos');
40
+ });
41
+
42
+ await ifWe(sir, 'parses /@bill as polycosmos with username tenant', async () => {
43
+ const route = parseSpaceGibRoute('/@bill');
44
+ iReckon(sir, route.kind).willEqual('polycosmos');
45
+ const tenant = (route as PolycosmosRouteInfo).tenant;
46
+ iReckon(sir, tenant?.type).willEqual('username');
47
+ if (tenant && tenant.type === 'username') {
48
+ iReckon(sir, tenant.siteUsername).willEqual('bill');
49
+ }
50
+ });
51
+
52
+ await ifWe(sir, 'parses /@bill/polycosmos as polycosmos with username tenant', async () => {
53
+ const route = parseSpaceGibRoute('/@bill/polycosmos');
54
+ iReckon(sir, route.kind).willEqual('polycosmos');
55
+ const tenant = (route as PolycosmosRouteInfo).tenant;
56
+ iReckon(sir, tenant?.type).willEqual('username');
57
+ if (tenant && tenant.type === 'username') {
58
+ iReckon(sir, tenant.siteUsername).willEqual('bill');
59
+ }
60
+ });
61
+
62
+ await ifWe(sir, 'parses /keystone/tjp123 as polycosmos with keystone tenant', async () => {
63
+ const route = parseSpaceGibRoute('/keystone/tjp123');
64
+ iReckon(sir, route.kind).willEqual('polycosmos');
65
+ const tenant = (route as PolycosmosRouteInfo).tenant;
66
+ iReckon(sir, tenant?.type).willEqual('keystone');
67
+ if (tenant && tenant.type === 'keystone') {
68
+ iReckon(sir, tenant.keystoneTjp).willEqual('tjp123');
69
+ }
70
+ });
71
+
72
+ await ifWe(sir, 'parses /keystone/tjp123/polycosmos as polycosmos with keystone tenant', async () => {
73
+ const route = parseSpaceGibRoute('/keystone/tjp123/polycosmos');
74
+ iReckon(sir, route.kind).willEqual('polycosmos');
75
+ const tenant = (route as PolycosmosRouteInfo).tenant;
76
+ iReckon(sir, tenant?.type).willEqual('keystone');
77
+ if (tenant && tenant.type === 'keystone') {
78
+ iReckon(sir, tenant.keystoneTjp).willEqual('tjp123');
79
+ }
80
+ });
81
+ });
82
+
83
+ await respecfully(sir, 'Cosmos Routes', async () => {
84
+ await ifWe(sir, 'parses /my-cosmos as cosmos', async () => {
85
+ const route = parseSpaceGibRoute('/my-cosmos');
86
+ iReckon(sir, route.kind).willEqual('cosmos');
87
+ iReckon(sir, (route as CosmosRouteInfo).cosmos).willEqual('my-cosmos');
88
+ iReckon(sir, (route as CosmosRouteInfo).tenant).willEqual(undefined);
89
+ });
90
+
91
+ await ifWe(sir, 'parses /@bill/my-cosmos as cosmos with username tenant', async () => {
92
+ const route = parseSpaceGibRoute('/@bill/my-cosmos');
93
+ iReckon(sir, route.kind).willEqual('cosmos');
94
+ iReckon(sir, (route as CosmosRouteInfo).cosmos).willEqual('my-cosmos');
95
+ const tenant = (route as CosmosRouteInfo).tenant;
96
+ iReckon(sir, tenant?.type).willEqual('username');
97
+ if (tenant && tenant.type === 'username') {
98
+ iReckon(sir, tenant.siteUsername).willEqual('bill');
99
+ }
100
+ });
101
+
102
+ await ifWe(sir, 'parses /keystone/tjp123/my-cosmos as cosmos with keystone tenant', async () => {
103
+ const route = parseSpaceGibRoute('/keystone/tjp123/my-cosmos');
104
+ iReckon(sir, route.kind).willEqual('cosmos');
105
+ iReckon(sir, (route as CosmosRouteInfo).cosmos).willEqual('my-cosmos');
106
+ const tenant = (route as CosmosRouteInfo).tenant;
107
+ iReckon(sir, tenant?.type).willEqual('keystone');
108
+ if (tenant && tenant.type === 'keystone') {
109
+ iReckon(sir, tenant.keystoneTjp).willEqual('tjp123');
110
+ }
111
+ });
112
+ });
113
+
114
+ await respecfully(sir, 'Universal Galaxy Routes (Branching / Code Archetype)', async () => {
115
+ await ifWe(sir, 'parses /my-cosmos/code-1 with galaxy', async () => {
116
+ const route = parseSpaceGibRoute('/my-cosmos/code-1');
117
+ iReckon(sir, route.kind).willEqual('galaxy');
118
+ const g = route as GalaxyRouteInfo;
119
+ iReckon(sir, g.cosmos).willEqual('my-cosmos');
120
+ iReckon(sir, g.galaxy).willEqual('code-1');
121
+ iReckon(sir, g.topology).willEqual('branching');
122
+ iReckon(sir, g.branch).willEqual(undefined);
123
+ iReckon(sir, g.filePath).willEqual(undefined);
124
+ });
125
+
126
+ await ifWe(sir, 'parses /my-cosmos/code-1/main with branch', async () => {
127
+ const route = parseSpaceGibRoute('/my-cosmos/code-1/main');
128
+ iReckon(sir, route.kind).willEqual('galaxy');
129
+ const g = route as GalaxyRouteInfo;
130
+ iReckon(sir, g.cosmos).willEqual('my-cosmos');
131
+ iReckon(sir, g.galaxy).willEqual('code-1');
132
+ iReckon(sir, g.topology).willEqual('branching');
133
+ iReckon(sir, g.branch).willEqual('main');
134
+ iReckon(sir, g.filePath).willEqual(undefined);
135
+ });
136
+
137
+ await ifWe(sir, 'parses /my-cosmos/code-1/main/src/index.ts with branch and deep filePath', async () => {
138
+ const route = parseSpaceGibRoute('/my-cosmos/code-1/main/src/index.ts');
139
+ iReckon(sir, route.kind).willEqual('galaxy');
140
+ const g = route as GalaxyRouteInfo;
141
+ iReckon(sir, g.cosmos).willEqual('my-cosmos');
142
+ iReckon(sir, g.galaxy).willEqual('code-1');
143
+ iReckon(sir, g.topology).willEqual('branching');
144
+ iReckon(sir, g.branch).willEqual('main');
145
+ iReckon(sir, g.filePath).willEqual('src/index.ts');
146
+ });
147
+
148
+ await ifWe(sir, 'parses /@bill/my-cosmos/code-1/feat-x/src/deep/nested/file.mts with tenant and deep path', async () => {
149
+ const route = parseSpaceGibRoute('/@bill/my-cosmos/code-1/feat-x/src/deep/nested/file.mts');
150
+ iReckon(sir, route.kind).willEqual('galaxy');
151
+ const g = route as GalaxyRouteInfo;
152
+ const tenant = g.tenant;
153
+ iReckon(sir, tenant?.type).willEqual('username');
154
+ if (tenant && tenant.type === 'username') {
155
+ iReckon(sir, tenant.siteUsername).willEqual('bill');
156
+ }
157
+ iReckon(sir, g.cosmos).willEqual('my-cosmos');
158
+ iReckon(sir, g.galaxy).willEqual('code-1');
159
+ iReckon(sir, g.topology).willEqual('branching');
160
+ iReckon(sir, g.branch).willEqual('feat-x');
161
+ iReckon(sir, g.filePath).willEqual('src/deep/nested/file.mts');
162
+ });
163
+ });
164
+
165
+ await respecfully(sir, 'Universal Galaxy Routes (Linear / Notes Archetype)', async () => {
166
+ await ifWe(sir, 'parses /my-cosmos/notes-1 with galaxy', async () => {
167
+ const route = parseSpaceGibRoute('/my-cosmos/notes-1');
168
+ iReckon(sir, route.kind).willEqual('galaxy');
169
+ const g = route as GalaxyRouteInfo;
170
+ iReckon(sir, g.cosmos).willEqual('my-cosmos');
171
+ iReckon(sir, g.galaxy).willEqual('notes-1');
172
+ iReckon(sir, g.topology).willEqual('linear');
173
+ iReckon(sir, g.topicTjp).willEqual(undefined);
174
+ });
175
+
176
+ await ifWe(sir, 'parses /my-cosmos/notes-1/topic_abc with topicTjp shorthand', async () => {
177
+ const route = parseSpaceGibRoute('/my-cosmos/notes-1/topic_abc');
178
+ iReckon(sir, route.kind).willEqual('galaxy');
179
+ const g = route as GalaxyRouteInfo;
180
+ iReckon(sir, g.cosmos).willEqual('my-cosmos');
181
+ iReckon(sir, g.galaxy).willEqual('notes-1');
182
+ iReckon(sir, g.topology).willEqual('linear');
183
+ iReckon(sir, g.topicTjp).willEqual('topic_abc');
184
+ });
185
+
186
+ await ifWe(sir, 'parses /@bill/my-cosmos/notes-1/topic_abc with tenant and topicTjp', async () => {
187
+ const route = parseSpaceGibRoute('/@bill/my-cosmos/notes-1/topic_abc');
188
+ iReckon(sir, route.kind).willEqual('galaxy');
189
+ const g = route as GalaxyRouteInfo;
190
+ const tenant = g.tenant;
191
+ iReckon(sir, tenant?.type).willEqual('username');
192
+ if (tenant && tenant.type === 'username') {
193
+ iReckon(sir, tenant.siteUsername).willEqual('bill');
194
+ }
195
+ iReckon(sir, g.cosmos).willEqual('my-cosmos');
196
+ iReckon(sir, g.galaxy).willEqual('notes-1');
197
+ iReckon(sir, g.topology).willEqual('linear');
198
+ iReckon(sir, g.topicTjp).willEqual('topic_abc');
199
+ iReckon(sir, g.trailMode).willEqual('tjp');
200
+ iReckon(sir, g.trail?.length).willEqual(1);
201
+ iReckon(sir, g.trail?.[0]).willEqual('topic_abc');
202
+ });
203
+
204
+ await ifWe(sir, 'parses /@bill/my-cosmos/notes-1/tjp/topic_1/reply_2/reply_3 as n-depth tjp trail', async () => {
205
+ const route = parseSpaceGibRoute('/@bill/my-cosmos/notes-1/tjp/topic_1/reply_2/reply_3');
206
+ iReckon(sir, route.kind).willEqual('galaxy');
207
+ const g = route as GalaxyRouteInfo;
208
+ iReckon(sir, g.cosmos).willEqual('my-cosmos');
209
+ iReckon(sir, g.galaxy).willEqual('notes-1');
210
+ iReckon(sir, g.topology).willEqual('linear');
211
+ iReckon(sir, g.trailMode).willEqual('tjp');
212
+ iReckon(sir, g.trail?.length).willEqual(3);
213
+ iReckon(sir, g.trail?.[0]).willEqual('topic_1');
214
+ iReckon(sir, g.trail?.[1]).willEqual('reply_2');
215
+ iReckon(sir, g.trail?.[2]).willEqual('reply_3');
216
+ iReckon(sir, g.topicTjp).willEqual('reply_3');
217
+ });
218
+
219
+ await ifWe(sir, 'parses /my-cosmos/notes-1/addr/hash1/hash2 as n-depth reified addr trail', async () => {
220
+ const route = parseSpaceGibRoute('/my-cosmos/notes-1/addr/hash1/hash2');
221
+ iReckon(sir, route.kind).willEqual('galaxy');
222
+ const g = route as GalaxyRouteInfo;
223
+ iReckon(sir, g.cosmos).willEqual('my-cosmos');
224
+ iReckon(sir, g.galaxy).willEqual('notes-1');
225
+ iReckon(sir, g.topology).willEqual('linear');
226
+ iReckon(sir, g.trailMode).willEqual('addr');
227
+ iReckon(sir, g.trail?.length).willEqual(2);
228
+ iReckon(sir, g.trail?.[0]).willEqual('hash1');
229
+ iReckon(sir, g.trail?.[1]).willEqual('hash2');
230
+ });
231
+
232
+ await ifWe(sir, 'parses query params for right-panel inspector and lenses', async () => {
233
+ const route = parseSpaceGibRoute('/@bill/my-cosmos/notes-1/tjp/topic_1?inspect=src%2Findex.ts%23L10&lens=issues');
234
+ iReckon(sir, route.kind).willEqual('galaxy');
235
+ const g = route as GalaxyRouteInfo;
236
+ iReckon(sir, g.query?.inspect).willEqual('src/index.ts#L10');
237
+ iReckon(sir, g.query?.lens).willEqual('issues');
238
+ });
239
+ });
240
+
241
+ await respecfully(sir, 'Fail-Fast Legacy Route Rejection', async () => {
242
+ await ifWe(sir, 'rejects obsolete /code/ intermediate segment', async () => {
243
+ let threw = false;
244
+ let errorMsg = '';
245
+ try {
246
+ parseSpaceGibRoute('/my-cosmos/code/code-1');
247
+ } catch (e: any) {
248
+ threw = true;
249
+ errorMsg = e.message;
250
+ }
251
+ iReckon(sir, threw).willEqual(true);
252
+ iReckon(sir, errorMsg.includes('Legacy route rejected')).willEqual(true);
253
+ iReckon(sir, errorMsg.includes('FAIL FAST')).willEqual(true);
254
+ });
255
+
256
+ await ifWe(sir, 'rejects obsolete /notes/ intermediate segment', async () => {
257
+ let threw = false;
258
+ let errorMsg = '';
259
+ try {
260
+ parseSpaceGibRoute('/my-cosmos/notes/notes-1');
261
+ } catch (e: any) {
262
+ threw = true;
263
+ errorMsg = e.message;
264
+ }
265
+ iReckon(sir, threw).willEqual(true);
266
+ iReckon(sir, errorMsg.includes('Legacy route rejected')).willEqual(true);
267
+ iReckon(sir, errorMsg.includes('FAIL FAST')).willEqual(true);
268
+ });
269
+
270
+ await ifWe(sir, 'rejects tenant-prefixed legacy /code/ route', async () => {
271
+ let threw = false;
272
+ let errorMsg = '';
273
+ try {
274
+ parseSpaceGibRoute('/@bill/my-cosmos/code/code-1/main');
275
+ } catch (e: any) {
276
+ threw = true;
277
+ errorMsg = e.message;
278
+ }
279
+ iReckon(sir, threw).willEqual(true);
280
+ iReckon(sir, errorMsg.includes('Legacy route rejected')).willEqual(true);
281
+ });
282
+ });
283
+
284
+ await respecfully(sir, 'Option B: Cosmos Manifest Policy Retrieval', async () => {
285
+ const mockCosmos: CosmosIbGib_V1 = {
286
+ ib: 'cosmos mock',
287
+ gib: 'gib_mock',
288
+ data: {
289
+ cosmosName: 'custom-cosmos',
290
+ uuid: 'u-1',
291
+ timestamp: '2026-09-18T00:00:00Z',
292
+ galaxies: {
293
+ 'custom-canvas': {
294
+ galaxyName: 'custom-canvas',
295
+ discriminator: 'canvas',
296
+ policy: {
297
+ role: 'intrinsic',
298
+ topology: 'branching',
299
+ storageMode: 'text',
300
+ },
301
+ },
302
+ 'custom-chat': {
303
+ galaxyName: 'custom-chat',
304
+ discriminator: 'chat',
305
+ policy: {
306
+ role: 'extrinsic',
307
+ topology: 'linear',
308
+ storageMode: 'text',
309
+ },
310
+ },
311
+ },
312
+ },
313
+ rel8ns: {},
314
+ };
315
+
316
+ await ifWe(sir, 'resolves custom branching galaxy from cosmos manifest', async () => {
317
+ const route = parseSpaceGibRoute('/custom-cosmos/custom-canvas/branchA/canvas.json', { cosmosManifest: mockCosmos });
318
+ iReckon(sir, route.kind).willEqual('galaxy');
319
+ const g = route as GalaxyRouteInfo;
320
+ iReckon(sir, g.galaxy).willEqual('custom-canvas');
321
+ iReckon(sir, g.topology).willEqual('branching');
322
+ iReckon(sir, g.discriminator).willEqual('canvas');
323
+ iReckon(sir, g.branch).willEqual('branchA');
324
+ iReckon(sir, g.filePath).willEqual('canvas.json');
325
+ });
326
+
327
+ await ifWe(sir, 'resolves custom linear galaxy from cosmos manifest', async () => {
328
+ const route = parseSpaceGibRoute('/custom-cosmos/custom-chat/tjp/topicA/msg1', { cosmosManifest: mockCosmos });
329
+ iReckon(sir, route.kind).willEqual('galaxy');
330
+ const g = route as GalaxyRouteInfo;
331
+ iReckon(sir, g.galaxy).willEqual('custom-chat');
332
+ iReckon(sir, g.topology).willEqual('linear');
333
+ iReckon(sir, g.discriminator).willEqual('chat');
334
+ iReckon(sir, g.trailMode).willEqual('tjp');
335
+ iReckon(sir, g.trail?.length).willEqual(2);
336
+ iReckon(sir, g.trail?.[0]).willEqual('topicA');
337
+ iReckon(sir, g.trail?.[1]).willEqual('msg1');
338
+ });
339
+ });
340
+
341
+ await respecfully(sir, 'Mechanism 1: Space-Qualified Raw Addrs', async () => {
342
+ await ifWe(sir, 'parses /:cosmos/:spaceId/addr/:ibGibAddr snapshot route', async () => {
343
+ const route = parseSpaceGibRoute('/my-cosmos/space-1/addr/comment%20hello^gib_1');
344
+ iReckon(sir, route.kind).willEqual('raw-addr');
345
+ const ra = route as RawAddrRouteInfo;
346
+ iReckon(sir, ra.cosmos).willEqual('my-cosmos');
347
+ iReckon(sir, ra.spaceId).willEqual('space-1');
348
+ iReckon(sir, ra.ibGibAddr).willEqual('comment hello^gib_1');
349
+ iReckon(sir, ra.tenant).willEqual(undefined);
350
+ });
351
+
352
+ await ifWe(sir, 'parses /@bill/:cosmos/:spaceId/addr/:ibGibAddr with tenant', async () => {
353
+ const route = parseSpaceGibRoute('/@bill/my-cosmos/space-1/addr/comment%20hello^gib_1');
354
+ iReckon(sir, route.kind).willEqual('raw-addr');
355
+ const ra = route as RawAddrRouteInfo;
356
+ const tenant = ra.tenant;
357
+ iReckon(sir, tenant?.type).willEqual('username');
358
+ if (tenant && tenant.type === 'username') {
359
+ iReckon(sir, tenant.siteUsername).willEqual('bill');
360
+ }
361
+ iReckon(sir, ra.cosmos).willEqual('my-cosmos');
362
+ iReckon(sir, ra.spaceId).willEqual('space-1');
363
+ iReckon(sir, ra.ibGibAddr).willEqual('comment hello^gib_1');
364
+ });
365
+
366
+ await ifWe(sir, 'parses /:cosmos/:spaceId/tjp/:tjpAddr living tip route', async () => {
367
+ const route = parseSpaceGibRoute('/my-cosmos/space-1/tjp/tjp_living_tip');
368
+ iReckon(sir, route.kind).willEqual('raw-tjp');
369
+ const rt = route as RawTjpRouteInfo;
370
+ iReckon(sir, rt.cosmos).willEqual('my-cosmos');
371
+ iReckon(sir, rt.spaceId).willEqual('space-1');
372
+ iReckon(sir, rt.tjpAddr).willEqual('tjp_living_tip');
373
+ iReckon(sir, rt.tenant).willEqual(undefined);
374
+ });
375
+
376
+ await ifWe(sir, 'parses /keystone/tjp123/:cosmos/:spaceId/tjp/:tjpAddr with keystone tenant', async () => {
377
+ const route = parseSpaceGibRoute('/keystone/tjp123/my-cosmos/space-1/tjp/tjp_living_tip');
378
+ iReckon(sir, route.kind).willEqual('raw-tjp');
379
+ const rt = route as RawTjpRouteInfo;
380
+ const tenant = rt.tenant;
381
+ iReckon(sir, tenant?.type).willEqual('keystone');
382
+ if (tenant && tenant.type === 'keystone') {
383
+ iReckon(sir, tenant.keystoneTjp).willEqual('tjp123');
384
+ }
385
+ iReckon(sir, rt.cosmos).willEqual('my-cosmos');
386
+ iReckon(sir, rt.spaceId).willEqual('space-1');
387
+ iReckon(sir, rt.tjpAddr).willEqual('tjp_living_tip');
388
+ });
389
+ });
390
+
391
+ await respecfully(sir, 'Admin Review Routes', async () => {
392
+ await ifWe(sir, 'parses /admin/review', async () => {
393
+ const route = parseSpaceGibRoute('/admin/review');
394
+ iReckon(sir, route.kind).willEqual('admin-review');
395
+ });
396
+
397
+ await ifWe(sir, 'parses /admin/review/:tjpGib', async () => {
398
+ const route = parseSpaceGibRoute('/admin/review/tjp_challenge_999');
399
+ iReckon(sir, route.kind).willEqual('admin-review');
400
+ if (route.kind === 'admin-review') {
401
+ iReckon(sir, route.tjpGib).willEqual('tjp_challenge_999');
402
+ }
403
+ });
404
+ });
405
+
406
+ await respecfully(sir, 'Formatting and Round-Trip Equivalence', async () => {
407
+ const testCases: SpaceGibRouteInfo[] = [
408
+ { kind: 'polycosmos' },
409
+ { kind: 'polycosmos', tenant: { type: 'username', siteUsername: 'bill' } },
410
+ { kind: 'polycosmos', tenant: { type: 'keystone', keystoneTjp: 'tjp_genesis' } },
411
+ { kind: 'cosmos', cosmos: 'cosmos-alpha' },
412
+ { kind: 'cosmos', tenant: { type: 'username', siteUsername: 'bill' }, cosmos: 'cosmos-alpha' },
413
+ { kind: 'galaxy', cosmos: 'my-cosmos', galaxy: 'code-1', topology: 'branching' },
414
+ { kind: 'galaxy', cosmos: 'my-cosmos', galaxy: 'code-1', topology: 'branching', branch: 'main' },
415
+ { kind: 'galaxy', cosmos: 'my-cosmos', galaxy: 'code-1', topology: 'branching', branch: 'main', filePath: 'src/core/index.ts' },
416
+ { kind: 'galaxy', tenant: { type: 'username', siteUsername: 'bill' }, cosmos: 'my-cosmos', galaxy: 'code-1', topology: 'branching', branch: 'dev', filePath: 'lib/utils.ts' },
417
+ { kind: 'galaxy', cosmos: 'my-cosmos', galaxy: 'notes-1', topology: 'linear' },
418
+ { kind: 'galaxy', cosmos: 'my-cosmos', galaxy: 'notes-1', topology: 'linear', topicTjp: 'topic_welcome', trailMode: 'tjp', trail: ['topic_welcome'] },
419
+ { kind: 'galaxy', tenant: { type: 'username', siteUsername: 'bill' }, cosmos: 'my-cosmos', galaxy: 'notes-1', topology: 'linear', topicTjp: 'topic_welcome', trailMode: 'tjp', trail: ['topic_welcome'] },
420
+ { kind: 'galaxy', cosmos: 'my-cosmos', galaxy: 'notes-1', topology: 'linear', topicTjp: 't3', trailMode: 'tjp', trail: ['t1', 't2', 't3'] },
421
+ { kind: 'galaxy', cosmos: 'my-cosmos', galaxy: 'notes-1', topology: 'linear', trailMode: 'addr', trail: ['a1', 'a2'] },
422
+ { kind: 'galaxy', cosmos: 'my-cosmos', galaxy: 'notes-1', topology: 'linear', topicTjp: 't1', trailMode: 'tjp', trail: ['t1'], query: { inspect: 'src/index.ts#L10', lens: 'issues' } },
423
+ { kind: 'raw-addr', cosmos: 'my-cosmos', spaceId: 'branch_space_main', ibGibAddr: 'comment hello world^gib_1' },
424
+ { kind: 'raw-addr', tenant: { type: 'username', siteUsername: 'bill' }, cosmos: 'my-cosmos', spaceId: 'branch_space_main', ibGibAddr: 'comment hello world^gib_1' },
425
+ { kind: 'raw-tjp', cosmos: 'my-cosmos', spaceId: 'branch_space_main', tjpAddr: 'tjp_dna_123' },
426
+ { kind: 'admin-review' },
427
+ { kind: 'admin-review', tjpGib: 'tjp_review_abc' },
428
+ ];
429
+
430
+ for (const originalRoute of testCases) {
431
+ await ifWe(sir, `round-trips ${originalRoute.kind} correctly`, async () => {
432
+ const formatted = formatSpaceGibRoute(originalRoute);
433
+ const parsed = parseSpaceGibRoute(formatted);
434
+ iReckon(sir, isSameRoute(originalRoute, parsed)).willEqual(true);
435
+ });
436
+ }
437
+ });
438
+
439
+ await respecfully(sir, 'Edge Cases & Robustness', async () => {
440
+ await ifWe(sir, 'handles trailing slashes', async () => {
441
+ const route = parseSpaceGibRoute('/my-cosmos/code-1/');
442
+ iReckon(sir, route.kind).willEqual('galaxy');
443
+ iReckon(sir, (route as GalaxyRouteInfo).galaxy).willEqual('code-1');
444
+ });
445
+
446
+ await ifWe(sir, 'strips query parameters', async () => {
447
+ const route = parseSpaceGibRoute('/my-cosmos/code-1?branch=main&debug=true');
448
+ iReckon(sir, route.kind).willEqual('galaxy');
449
+ iReckon(sir, (route as GalaxyRouteInfo).galaxy).willEqual('code-1');
450
+ });
451
+
452
+ await ifWe(sir, 'strips hash fragments', async () => {
453
+ const route = parseSpaceGibRoute('/my-cosmos#section2');
454
+ iReckon(sir, route.kind).willEqual('cosmos');
455
+ iReckon(sir, (route as CosmosRouteInfo).cosmos).willEqual('my-cosmos');
456
+ });
457
+
458
+ await ifWe(sir, 'handles multiple consecutive slashes gracefully', async () => {
459
+ const route = parseSpaceGibRoute('///my-cosmos///code-1///');
460
+ iReckon(sir, route.kind).willEqual('galaxy');
461
+ iReckon(sir, (route as GalaxyRouteInfo).galaxy).willEqual('code-1');
462
+ });
463
+ });
464
+
465
+ await respecfully(sir, 'SpaceGibRouterService Subclass Specs', async () => {
466
+ await ifWeMight(sir, 'provides singleton subclassing IbGibCosmosRouterServiceBase and IbGibRouterServiceBase', async () => {
467
+ const router = getSpaceGibRouterSvc();
468
+ iReckon(sir, router instanceof SpaceGibRouterService).isGonnaBe(true);
469
+ iReckon(sir, router instanceof IbGibRouterServiceBase).isGonnaBe(true);
470
+ iReckon(sir, router instanceof IbGibCosmosRouterServiceBase).isGonnaBe(true);
471
+ });
472
+
473
+ await ifWeMight(sir, 'parses, formats and navigates cleanly via cosmos base class', async () => {
474
+ const router = getSpaceGibRouterSvc();
475
+ const received: SpaceGibRouteInfo[] = [];
476
+ const unsubscribe = router.subscribe(async (r) => {
477
+ received.push(r);
478
+ });
479
+
480
+ await router.navigate({
481
+ kind: 'galaxy',
482
+ cosmos: 'test-cosmos',
483
+ galaxy: 'notes-1',
484
+ topology: 'linear',
485
+ });
486
+
487
+ iReckon(sir, router.getCurrentRoute()?.kind).willEqual('galaxy');
488
+ iReckon(sir, (router.getCurrentRoute() as GalaxyRouteInfo).galaxy).willEqual('notes-1');
489
+ iReckon(sir, received.length > 0).isGonnaBe(true);
490
+
491
+ unsubscribe();
492
+ });
493
+
494
+ await ifWeMight(sir, 'auto-loads galaxy policy from manifest registered on router', async () => {
495
+ const router = getSpaceGibRouterSvc();
496
+ const mockManifest: CosmosIbGib_V1 = {
497
+ ib: 'cosmos test-auto-cosmos',
498
+ data: {
499
+ cosmosName: 'test-auto-cosmos',
500
+ galaxies: {
501
+ 'auto-canvas': {
502
+ galaxyName: 'auto-canvas',
503
+ discriminator: 'canvas',
504
+ policy: {
505
+ topology: 'branching',
506
+ role: 'intrinsic',
507
+ storageMode: 'text',
508
+ },
509
+ },
510
+ },
511
+ },
512
+ rel8ns: {},
513
+ };
514
+ router.setCosmosManifest('test-auto-cosmos', mockManifest);
515
+
516
+ const received: SpaceGibRouteInfo[] = [];
517
+ const unsubscribe = router.subscribe((r) => { received.push(r); });
518
+
519
+ await router.navigatePath('/test-auto-cosmos/auto-canvas/branchX/art.png');
520
+
521
+ const current = router.getCurrentRoute() as GalaxyRouteInfo;
522
+ iReckon(sir, current.kind).willEqual('galaxy');
523
+ iReckon(sir, current.topology).willEqual('branching');
524
+ iReckon(sir, current.discriminator).willEqual('canvas');
525
+
526
+ unsubscribe();
527
+ });
528
+ });
529
+ });
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @module client/ui/router/space-gib-router-service
3
+ *
4
+ * Singleton HTML5 History API Cosmos router service for space-gib.
5
+ * Extends the Tier 2 IbGibCosmosRouterServiceBase from @ibgib/web-gib.
6
+ */
7
+
8
+ import { IbGibCosmosRouterServiceBase, safeDecode } from '@ibgib/web-gib/dist/ui/router/index.mjs';
9
+
10
+ import {
11
+ SpaceGibRouteInfo,
12
+ RouterNavigateOptions,
13
+ SpaceGibRouteListener,
14
+ AdminReviewRouteInfo,
15
+ } from './space-gib-router-types.mjs';
16
+
17
+ export class SpaceGibRouterService extends IbGibCosmosRouterServiceBase<SpaceGibRouteInfo> {
18
+ protected override lc: string = `[SpaceGibRouterService]`;
19
+
20
+ protected override parseCustomRoute(pathname: string): SpaceGibRouteInfo | undefined {
21
+ let cleanPath = (pathname || '').trim();
22
+ const qIdx = cleanPath.indexOf('?');
23
+ if (qIdx !== -1) { cleanPath = cleanPath.substring(0, qIdx); }
24
+ const hIdx = cleanPath.indexOf('#');
25
+ if (hIdx !== -1) { cleanPath = cleanPath.substring(0, hIdx); }
26
+
27
+ if (cleanPath === '/admin/review') {
28
+ return { kind: 'admin-review' };
29
+ }
30
+ if (cleanPath.startsWith('/admin/review/')) {
31
+ const tjpGib = safeDecode(cleanPath.slice('/admin/review/'.length)).trim();
32
+ return { kind: 'admin-review', ...(tjpGib ? { tjpGib } : {}) };
33
+ }
34
+ return undefined;
35
+ }
36
+
37
+ protected override formatCustomRoute(route: SpaceGibRouteInfo): string | undefined {
38
+ if (route.kind === 'admin-review') {
39
+ return `/admin/review${route.tjpGib ? `/${encodeURIComponent(route.tjpGib)}` : ''}`;
40
+ }
41
+ return undefined;
42
+ }
43
+
44
+ protected override isSameCustomRoute(a?: SpaceGibRouteInfo, b?: SpaceGibRouteInfo): boolean | undefined {
45
+ if (a?.kind === 'admin-review' || b?.kind === 'admin-review') {
46
+ if (a?.kind !== b?.kind) { return false; }
47
+ return (a as AdminReviewRouteInfo).tjpGib === (b as AdminReviewRouteInfo).tjpGib;
48
+ }
49
+ return undefined;
50
+ }
51
+ }
52
+
53
+ let routerSingleton: SpaceGibRouterService | undefined;
54
+
55
+ /**
56
+ * Returns the singleton SpaceGibRouterService instance.
57
+ */
58
+ export function getSpaceGibRouterSvc(): SpaceGibRouterService {
59
+ if (!routerSingleton) {
60
+ routerSingleton = new SpaceGibRouterService();
61
+ }
62
+ return routerSingleton;
63
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @module client/ui/router/space-gib-router-types
3
+ *
4
+ * Types for the Space-Gib Client Router.
5
+ * Re-exports universal Cosmos & Galaxy route types from @ibgib/web-gib,
6
+ * and adds space-gib specific routes (e.g. AdminReviewRouteInfo).
7
+ */
8
+
9
+ import {
10
+ CosmosRouteInfo as WebGibCosmosRouteInfo,
11
+ CosmosTenant,
12
+ PolycosmosRouteInfo,
13
+ CosmosOnlyRouteInfo,
14
+ CosmosGalaxyRouteInfo,
15
+ CosmosRawAddrRouteInfo,
16
+ CosmosRawTjpRouteInfo,
17
+ RouterNavigateOptions,
18
+ RouteListener,
19
+ } from '@ibgib/web-gib/dist/ui/router/index.mjs';
20
+
21
+ export {
22
+ RouterNavigateOptions,
23
+ RouteListener,
24
+ PolycosmosRouteInfo,
25
+ CosmosGalaxyRouteInfo,
26
+ CosmosRawAddrRouteInfo,
27
+ CosmosRawTjpRouteInfo,
28
+ };
29
+
30
+ export type SpaceGibTenant = CosmosTenant;
31
+ export type CosmosRouteInfo = CosmosOnlyRouteInfo;
32
+ export type GalaxyRouteInfo = CosmosGalaxyRouteInfo;
33
+ export type RawAddrRouteInfo = CosmosRawAddrRouteInfo;
34
+ export type RawTjpRouteInfo = CosmosRawTjpRouteInfo;
35
+
36
+ export interface AdminReviewRouteInfo {
37
+ kind: 'admin-review';
38
+ tjpGib?: string;
39
+ }
40
+
41
+ export type SpaceGibRouteInfo =
42
+ | CosmosRouteInfo
43
+ | GalaxyRouteInfo
44
+ | PolycosmosRouteInfo
45
+ | RawAddrRouteInfo
46
+ | RawTjpRouteInfo
47
+ | AdminReviewRouteInfo;
48
+
49
+ export type SpaceGibRouteListener = RouteListener<SpaceGibRouteInfo>;