@cardstack/boxel-cli 0.3.0-unstable.13 → 0.3.0-unstable.87

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 (35) hide show
  1. package/bundled-types/base/card-api.d.ts +2 -2
  2. package/bundled-types/base/card-serialization.d.ts +1 -1
  3. package/bundled-types/base/field-support.d.ts +6 -0
  4. package/bundled-types/boxel-ui/icons.gts +0 -3
  5. package/bundled-types/host-app/commands/create-submission-workflow.ts +2 -2
  6. package/bundled-types/host-app/commands/get-available-realm-identifiers.ts +1 -1
  7. package/bundled-types/host-app/commands/get-catalog-realm-identifiers.ts +1 -1
  8. package/bundled-types/host-app/commands/search-cards.ts +1 -1
  9. package/bundled-types/host-app/components/card-catalog/modal.gts +3 -3
  10. package/bundled-types/host-app/components/card-search/panel.gts +1 -1
  11. package/bundled-types/host-app/components/card-search/search-content.gts +2 -2
  12. package/bundled-types/host-app/components/operator-mode/code-submode/format-chooser.gts +12 -9
  13. package/bundled-types/host-app/components/operator-mode/code-submode/module-inspector.gts +1 -1
  14. package/bundled-types/host-app/components/operator-mode/code-submode/playground/playground-panel.gts +3 -3
  15. package/bundled-types/host-app/components/operator-mode/create-file-modal.gts +2 -2
  16. package/bundled-types/host-app/components/operator-mode/stack-item.gts +9 -1
  17. package/bundled-types/host-app/components/operator-mode/workspace-chooser/index.gts +32 -24
  18. package/bundled-types/host-app/components/operator-mode/workspace-chooser/workspace.gts +36 -23
  19. package/bundled-types/host-app/components/realm-picker/index.gts +2 -2
  20. package/bundled-types/host-app/components/search-sheet/index.gts +1 -1
  21. package/bundled-types/host-app/components/with-known-realms-loaded.gts +1 -1
  22. package/bundled-types/host-app/router.ts +1 -0
  23. package/bundled-types/host-app/services/host-mode-service.ts +21 -2
  24. package/bundled-types/host-app/services/matrix-service.ts +9 -8
  25. package/bundled-types/host-app/services/operator-mode-state-service.ts +8 -6
  26. package/bundled-types/host-app/services/realm-server.ts +18 -16
  27. package/bundled-types/host-app/services/store.ts +1 -1
  28. package/bundled-types/host-app/utils/card-search/url.ts +6 -2
  29. package/bundled-types/host-tests/helpers/index.gts +2 -2
  30. package/dist/index.js +69 -69
  31. package/package.json +2 -2
  32. package/src/commands/realm/publish.ts +10 -1
  33. package/src/commands/realm/sync.ts +28 -2
  34. package/src/commands/realm/unpublish.ts +2 -3
  35. package/src/lib/describe-fetch-error.ts +25 -0
@@ -12,6 +12,7 @@ Router.map(function () {
12
12
  this.route('html', { path: '/html/:format/:ancestor_level' });
13
13
  this.route('icon');
14
14
  this.route('meta');
15
+ this.route('types');
15
16
  this.route('file-extract');
16
17
  this.route('error');
17
18
  });
@@ -19,6 +19,16 @@ function ensureSingleTitle(headHTML: string): string {
19
19
  ? headHTML
20
20
  : `${DEFAULT_HEAD_HTML}\n${headHTML}`;
21
21
  }
22
+
23
+ // Normalize trailing-slash variance for routing-map matching. `/realm/`
24
+ // and `/realm` are the same destination from the user's perspective,
25
+ // but the injected map keys and Ember's `params.path` disagree on
26
+ // the trailing slash. Stripping it on both sides makes the comparator
27
+ // robust. Preserve the root `/` since stripping it would empty the path.
28
+ function canonicalizeRoutingPath(path: string): string {
29
+ if (path === '/') return '/';
30
+ return path.replace(/\/+$/, '');
31
+ }
22
32
  import type HostModeStateService from '@cardstack/host/services/host-mode-state-service';
23
33
  import type OperatorModeStateService from '@cardstack/host/services/operator-mode-state-service';
24
34
  import type RealmService from '@cardstack/host/services/realm';
@@ -130,10 +140,19 @@ export default class HostModeService extends Service {
130
140
  // `https://host/<user>/<realm>/whitepaper`); a leading slash is added if
131
141
  // absent so the index path is matchable as either '' or '/'. The
132
142
  // server prefixes each rule's `path` with the realm's mount pathname
133
- // before injecting the map, so the two sides line up as direct equality.
143
+ // before injecting the map, so the two sides line up as direct equality
144
+ // — except for the trailing-slash variance at the realm root. A `/`
145
+ // rule's injected key is the realm's mount pathname WITH trailing
146
+ // slash (e.g. `/progressive-cheetah/`), but Ember's catch-all strips
147
+ // it (`params.path === 'progressive-cheetah'` for either visit form).
148
+ // Canonicalize both sides by stripping trailing slashes (except the
149
+ // root `/` itself) before comparing so `/realm` ↔ `/realm/` resolve.
134
150
  resolveRoutedPath(path: string): string | null {
135
151
  let normalized = path.startsWith('/') ? path : `/${path}`;
136
- let rule = this.hostRoutingMap.find((r) => r.path === normalized);
152
+ let canonical = canonicalizeRoutingPath(normalized);
153
+ let rule = this.hostRoutingMap.find(
154
+ (r) => canonicalizeRoutingPath(r.path) === canonical,
155
+ );
137
156
  return rule ? rule.id : null;
138
157
  }
139
158
 
@@ -33,6 +33,7 @@ import {
33
33
  logger,
34
34
  isCardInstance,
35
35
  Deferred,
36
+ ri,
36
37
  SEARCH_MARKER,
37
38
  REPLACE_MARKER,
38
39
  SEPARATOR_MARKER,
@@ -371,8 +372,8 @@ export default class MatrixService extends Service {
371
372
  async (e) => {
372
373
  switch (e.event.type) {
373
374
  case APP_BOXEL_REALMS_EVENT_TYPE:
374
- await this.realmServer.setAvailableRealmURLs(
375
- e.event.content.realms,
375
+ await this.realmServer.setAvailableRealmIdentifiers(
376
+ (e.event.content.realms as string[]).map(ri),
376
377
  );
377
378
  // Only do this after we've completed our overall login
378
379
  if (this.postLoginCompleted) {
@@ -654,7 +655,7 @@ export default class MatrixService extends Service {
654
655
  await this.client.setAccountData(APP_BOXEL_REALMS_EVENT_TYPE, {
655
656
  realms: newRealms,
656
657
  });
657
- await this.realmServer.setAvailableRealmURLs(newRealms);
658
+ await this.realmServer.setAvailableRealmIdentifiers(newRealms.map(ri));
658
659
  }
659
660
 
660
661
  public async removeRealmFromAccountData(realmURLString: string) {
@@ -667,7 +668,7 @@ export default class MatrixService extends Service {
667
668
  await this.client.setAccountData(APP_BOXEL_REALMS_EVENT_TYPE, {
668
669
  realms: newRealms,
669
670
  });
670
- await this.realmServer.setAvailableRealmURLs(newRealms);
671
+ await this.realmServer.setAvailableRealmIdentifiers(newRealms.map(ri));
671
672
  }
672
673
 
673
674
  public async getWorkspaceFavorites(): Promise<string[]> {
@@ -767,13 +768,13 @@ export default class MatrixService extends Service {
767
768
 
768
769
  await Promise.all([
769
770
  this.realmServer.fetchCatalogRealms(),
770
- this.realmServer.setAvailableRealmURLs(
771
- accountDataContent?.realms ?? [],
771
+ this.realmServer.setAvailableRealmIdentifiers(
772
+ (accountDataContent?.realms ?? []).map(ri),
772
773
  ),
773
774
  ]);
774
775
 
775
776
  await this.realm.prefetchRealmInfos(
776
- this.realmServer.availableRealmURLs,
777
+ this.realmServer.availableRealmIdentifiers,
777
778
  );
778
779
 
779
780
  await this.initSlidingSync(accountDataContent);
@@ -882,7 +883,7 @@ export default class MatrixService extends Service {
882
883
  async loginToRealms() {
883
884
  // This is where we would actually load user-specific choices out of the
884
885
  // user's profile based on this.client.getUserId();
885
- let activeRealms = this.realmServer.availableRealmURLs;
886
+ let activeRealms = this.realmServer.availableRealmIdentifiers;
886
887
 
887
888
  await Promise.all(
888
889
  activeRealms.map(async (realmURL: string) => {
@@ -1468,16 +1468,18 @@ export default class OperatorModeStateService extends Service {
1468
1468
  ): Promise<BoxelContext> {
1469
1469
  let codeMode: BoxelContext['codeMode'] = undefined;
1470
1470
  if (this._state.workspaceChooserOpened) {
1471
- let userWorkspaces = this.realmServer.userRealmURLs.map((url) => ({
1471
+ let userWorkspaces = this.realmServer.userRealmIdentifiers.map((url) => ({
1472
1472
  url,
1473
1473
  name: this.realm.info(url).name,
1474
1474
  type: 'user-workspace' as const,
1475
1475
  }));
1476
- let catalogWorkspaces = this.realmServer.catalogRealmURLs.map((url) => ({
1477
- url,
1478
- name: this.realm.info(url).name,
1479
- type: 'catalog-workspace' as const,
1480
- }));
1476
+ let catalogWorkspaces = this.realmServer.catalogRealmIdentifiers.map(
1477
+ (url) => ({
1478
+ url,
1479
+ name: this.realm.info(url).name,
1480
+ type: 'catalog-workspace' as const,
1481
+ }),
1482
+ );
1481
1483
  let result: BoxelContext = {
1482
1484
  agentId: this.matrixService.agentId,
1483
1485
  submode: 'workspace-chooser',
@@ -17,7 +17,9 @@ import {
17
17
  ensureTrailingSlash,
18
18
  SupportedMimeType,
19
19
  Deferred,
20
+ ri,
20
21
  testRealmURL,
22
+ type RealmIdentifier,
21
23
  type RealmInfo,
22
24
  type JWTPayload,
23
25
  } from '@cardstack/runtime-common';
@@ -253,8 +255,8 @@ export default class RealmServerService extends Service {
253
255
  }
254
256
 
255
257
  @cached
256
- get availableRealmURLs() {
257
- return this.availableRealms.map((r) => r.url);
258
+ get availableRealmIdentifiers(): RealmIdentifier[] {
259
+ return this.availableRealms.map((r) => ri(r.url));
258
260
  }
259
261
 
260
262
  assertOwnRealmServer(realmServerURLs: string[]): void {
@@ -338,27 +340,27 @@ export default class RealmServerService extends Service {
338
340
  }
339
341
 
340
342
  @cached
341
- get userRealmURLs() {
343
+ get userRealmIdentifiers(): RealmIdentifier[] {
342
344
  return this.availableRealms
343
345
  .filter((r) => r.type === 'user')
344
- .map((r) => r.url);
346
+ .map((r) => ri(r.url));
345
347
  }
346
348
 
347
349
  @cached
348
- get catalogRealmURLs() {
350
+ get catalogRealmIdentifiers(): RealmIdentifier[] {
349
351
  return this.availableRealms
350
352
  .filter((r) => r.type === 'catalog')
351
- .map((r) => r.url);
353
+ .map((r) => ri(r.url));
352
354
  }
353
355
 
354
356
  @cached
355
- get displayedCatalogRealmURLs() {
356
- return this.catalogRealmURLs;
357
+ get displayedCatalogRealmIdentifiers(): RealmIdentifier[] {
358
+ return this.catalogRealmIdentifiers;
357
359
  }
358
360
 
359
361
  @cached
360
362
  get availableRealmIndexCardIds() {
361
- return this.availableRealmURLs.map((url) => `${url}index`);
363
+ return this.availableRealmIdentifiers.map((url) => `${url}index`);
362
364
  }
363
365
 
364
366
  async authenticateToAllAccessibleRealms() {
@@ -389,22 +391,22 @@ export default class RealmServerService extends Service {
389
391
  }
390
392
  }
391
393
 
392
- async setAvailableRealmURLs(userRealmURLs: string[]) {
394
+ async setAvailableRealmIdentifiers(userRealmIdentifiers: RealmIdentifier[]) {
393
395
  await this._ready.promise;
394
- userRealmURLs.forEach((userRealmURL) => {
395
- if (!this.availableRealms.find((r) => r.url === userRealmURL)) {
396
+ userRealmIdentifiers.forEach((userRealmIdentifier) => {
397
+ if (!this.availableRealms.find((r) => r.url === userRealmIdentifier)) {
396
398
  this.availableRealms.push({
397
399
  type: 'user',
398
- url: userRealmURL,
400
+ url: userRealmIdentifier,
399
401
  });
400
402
  }
401
403
  });
402
404
 
403
- // pluck out any user realms that aren't a part of the userRealmsURLs
405
+ // pluck out any user realms that aren't a part of userRealmIdentifiers
404
406
  this.availableRealms
405
407
  .filter((r) => r.type === 'user')
406
408
  .forEach((realm) => {
407
- if (!userRealmURLs.includes(realm.url)) {
409
+ if (!userRealmIdentifiers.includes(ri(realm.url))) {
408
410
  this.availableRealms.splice(
409
411
  this.availableRealms.findIndex((r) => r.url === realm.url),
410
412
  1,
@@ -414,7 +416,7 @@ export default class RealmServerService extends Service {
414
416
  }
415
417
 
416
418
  async fetchCatalogRealms() {
417
- if (this.catalogRealmURLs.length > 0) {
419
+ if (this.catalogRealmIdentifiers.length > 0) {
418
420
  return;
419
421
  }
420
422
  let response = await this.network.fetch(
@@ -943,7 +943,7 @@ export default class StoreService extends Service implements StoreInterface {
943
943
  let searchRealms =
944
944
  normalizedRealms.length > 0
945
945
  ? normalizedRealms
946
- : this.realmServer.availableRealmURLs;
946
+ : this.realmServer.availableRealmIdentifiers;
947
947
  if (searchRealms.length === 0) {
948
948
  if (query.asData) {
949
949
  return opts?.includeMeta
@@ -1,3 +1,5 @@
1
+ import type { RealmIdentifier } from '@cardstack/runtime-common';
2
+
1
3
  export function isURLSearchKey(searchKey: string): boolean {
2
4
  try {
3
5
  new URL(searchKey);
@@ -13,11 +15,13 @@ export function isSearchKeyEmpty(searchKey: string): boolean {
13
15
 
14
16
  export function resolveSearchKeyAsURL(
15
17
  searchKey: string,
16
- availableRealmURLs: string[],
18
+ availableRealmIdentifiers: readonly RealmIdentifier[],
17
19
  ): string | undefined {
18
20
  if (!isURLSearchKey(searchKey)) {
19
21
  return undefined;
20
22
  }
21
- let maybeIndexCardURL = availableRealmURLs.find((u) => u === searchKey + '/');
23
+ let maybeIndexCardURL = availableRealmIdentifiers.find(
24
+ (u) => u === searchKey + '/',
25
+ );
22
26
  return maybeIndexCardURL ?? searchKey;
23
27
  }
@@ -1367,8 +1367,8 @@ async function setupTestRealm({
1367
1367
  }
1368
1368
 
1369
1369
  let realmServer = getService('realm-server');
1370
- if (!realmServer.availableRealmURLs.includes(realmURL)) {
1371
- await realmServer.setAvailableRealmURLs([realmURL]);
1370
+ if (!realmServer.availableRealmIdentifiers.includes(ri(realmURL))) {
1371
+ await realmServer.setAvailableRealmIdentifiers([ri(realmURL)]);
1372
1372
  }
1373
1373
 
1374
1374
  return { realm, adapter };