From ffc3fe97b74b7476012d78123c0808995f644741 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Fri, 30 Jan 2026 10:08:05 -0600 Subject: [PATCH 01/15] Add dependencies --- packages/realm-server/package.json | 2 + pnpm-lock.yaml | 172 ++++++++++++++++++++++++--- pnpm-workspace.yaml | 182 +++++++++++++++-------------- 3 files changed, 251 insertions(+), 105 deletions(-) diff --git a/packages/realm-server/package.json b/packages/realm-server/package.json index 25cef2730d..492c8c095c 100644 --- a/packages/realm-server/package.json +++ b/packages/realm-server/package.json @@ -16,6 +16,7 @@ "@koa/router": "catalog:", "@octokit/rest": "catalog:", "@sentry/node": "catalog:", + "@types/archiver": "catalog:", "@types/flat": "catalog:", "@types/fs-extra": "catalog:", "@types/js-yaml": "catalog:", @@ -38,6 +39,7 @@ "@types/tmp": "catalog:", "@types/uuid": "catalog:", "@types/yargs": "catalog:", + "archiver": "catalog:", "concurrently": "catalog:", "content-tag": "catalog:", "cron": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e7b808080d..c31b7afc55 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,6 +135,9 @@ catalogs: '@sqlite.org/sqlite-wasm': specifier: 3.45.1-build1 version: 3.45.1-build1 + '@types/archiver': + specifier: ^7.0.0 + version: 7.0.0 '@types/babel__core': specifier: ^7.1.19 version: 7.20.5 @@ -261,6 +264,9 @@ catalogs: ajv: specifier: ^8.17.1 version: 8.17.1 + archiver: + specifier: ^7.0.0 + version: 7.0.1 awesome-phonenumber: specifier: ^7.2.0 version: 7.6.0 @@ -728,7 +734,7 @@ importers: version: 1.3.0(typescript@5.8.3) '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) '@glint/environment-ember-template-imports': specifier: 1.3.0 version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0) @@ -1004,7 +1010,7 @@ importers: version: 1.10.2 '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) '@glint/template': specifier: 1.3.0 version: 1.3.0 @@ -1125,7 +1131,7 @@ importers: version: 2.0.0 '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) '@glint/template': specifier: 1.3.0 version: 1.3.0 @@ -1318,7 +1324,7 @@ importers: version: 1.3.0(typescript@5.8.3) '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) '@glint/environment-ember-template-imports': specifier: 1.3.0 version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0) @@ -1510,7 +1516,7 @@ importers: version: 1.7.4 '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) '@glint/template': specifier: 1.3.0 version: 1.3.0 @@ -1703,7 +1709,7 @@ importers: dependencies: '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) ember-basic-dropdown: specifier: 8.0.4 version: 8.0.4(patch_hash=19b0fc5d4bd8b9aa296c4065fa5e33bdbb965db0b277810b596eacd0b9e2f428)(@ember/string@4.0.1)(@ember/test-helpers@3.3.1(@babel/core@7.28.6)(@glint/template@1.3.0)(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))(webpack@5.104.1))(@glimmer/component@2.0.0)(@glimmer/tracking@1.1.2)(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))))(@glint/template@1.3.0)(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1)) @@ -2071,7 +2077,7 @@ importers: version: link:../runtime-common '@cardstack/view-transitions': specifier: 'catalog:' - version: 0.2.0(@babel/core@7.28.6)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) + version: 0.2.0(@babel/core@7.28.6)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) '@types/lodash': specifier: 'catalog:' version: 4.17.23 @@ -2147,7 +2153,7 @@ importers: version: link:../runtime-common '@cardstack/view-transitions': specifier: 'catalog:' - version: 0.2.0(@babel/core@7.28.6)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) + version: 0.2.0(@babel/core@7.28.6)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) '@ember/optional-features': specifier: ^2.0.0 version: 2.3.0 @@ -2186,7 +2192,7 @@ importers: version: 1.3.0(typescript@5.8.3) '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) '@glint/environment-ember-template-imports': specifier: 1.3.0 version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0) @@ -2726,6 +2732,9 @@ importers: '@sentry/node': specifier: 'catalog:' version: 8.55.0 + '@types/archiver': + specifier: 'catalog:' + version: 7.0.0 '@types/flat': specifier: 'catalog:' version: 5.0.5 @@ -2792,6 +2801,9 @@ importers: '@types/yargs': specifier: 'catalog:' version: 17.0.35 + archiver: + specifier: 'catalog:' + version: 7.0.1 concurrently: specifier: 'catalog:' version: 8.2.2 @@ -3128,7 +3140,7 @@ importers: version: 1.3.0(typescript@5.8.3) '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) '@glint/environment-ember-template-imports': specifier: 1.3.0 version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0) @@ -3197,7 +3209,7 @@ importers: version: 1.3.0(typescript@5.8.3) '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) '@glint/environment-ember-template-imports': specifier: ^1.3.0 version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0) @@ -6053,6 +6065,9 @@ packages: '@types/accepts@1.3.7': resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} + '@types/archiver@7.0.0': + resolution: {integrity: sha512-/3vwGwx9n+mCQdYZ2IKGGHEFL30I96UgBlk8EtRDDFQ9uxM1l4O5Ci6r00EMAkiDaTqD9DQ6nVrWRICnBPtzzg==} + '@types/babel__code-frame@7.27.0': resolution: {integrity: sha512-Dwlo+LrxDx/0SpfmJ/BKveHf7QXWvLBLc+x03l5sbzykj3oB9nHygCpSECF1a+s+QIxbghe+KHqC90vGtxLRAA==} @@ -6276,6 +6291,9 @@ packages: '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + '@types/readdir-glob@1.1.5': + resolution: {integrity: sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==} + '@types/responselike@1.0.3': resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} @@ -6623,6 +6641,10 @@ packages: abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -6789,6 +6811,14 @@ packages: arch@2.2.0: resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} + archiver-utils@5.0.2: + resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==} + engines: {node: '>= 14'} + + archiver@7.0.1: + resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==} + engines: {node: '>= 14'} + are-we-there-yet@3.0.1: resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -7409,6 +7439,10 @@ packages: buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + buffer-crc32@1.0.0: + resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} + engines: {node: '>=8.0.0'} + buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} @@ -7768,6 +7802,10 @@ packages: component-emitter@1.3.1: resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + compress-commons@6.0.2: + resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} + engines: {node: '>= 14'} + compressible@2.0.18: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} @@ -8062,6 +8100,15 @@ packages: countries-list@3.2.2: resolution: {integrity: sha512-ABJ/RWQBrPWy+hRuZoW+0ooK8p65Eo3WmUZwHm6v4wmfSPznNAKzjy3+UUYrJK2v3182BVsgWxdB6ROidj39kw==} + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + crc32-stream@6.0.0: + resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} + engines: {node: '>= 14'} + create-ecdh@4.0.4: resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} @@ -9361,6 +9408,10 @@ packages: event-stream@3.3.4: resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} @@ -10759,6 +10810,10 @@ packages: resolution: {integrity: sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==} engines: {node: '> 0.8'} + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + lcid@3.1.1: resolution: {integrity: sha512-M6T051+5QCGLBQb8id3hdvIW8+zeFV2FyBGFS9IEK5H9Wt4MueD4bW1eWikpHgZp+5xR3l5c8pZUkQsIA0BFZg==} engines: {node: '>=8'} @@ -12258,6 +12313,13 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + recast@0.18.10: resolution: {integrity: sha512-XNvYvkfdAN9QewbrxeTOjgINkdY/odTgTS56ZNEWL9Ml0weT4T3sFtvnTuF+Gxyu46ANcRm1ntrF6F5LAJPAaQ==} engines: {node: '>= 4'} @@ -14131,6 +14193,10 @@ packages: resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} engines: {node: '>=18'} + zip-stream@6.0.1: + resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} + engines: {node: '>= 14'} + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -15086,7 +15152,7 @@ snapshots: '@cardstack/requirejs-monaco-ember-polyfill@0.0.1': {} - '@cardstack/view-transitions@0.2.0(@babel/core@7.28.6)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)))': + '@cardstack/view-transitions@0.2.0(@babel/core@7.28.6)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1)))': dependencies: '@embroider/addon-shim': 1.10.2 decorator-transforms: 2.3.1(@babel/core@7.28.6) @@ -15499,7 +15565,7 @@ snapshots: ember-cli-babel: 7.26.11 ember-source: 5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1) optionalDependencies: - '@glint/environment-ember-loose': 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) + '@glint/environment-ember-loose': 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) '@glint/template': 1.3.0 transitivePeerDependencies: - supports-color @@ -16142,7 +16208,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1)))': + '@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)))': dependencies: '@glimmer/component': 2.0.0 '@glint/template': 1.3.0 @@ -16152,7 +16218,7 @@ snapshots: '@glint/environment-ember-template-imports@1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0)': dependencies: - '@glint/environment-ember-loose': 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) + '@glint/environment-ember-loose': 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) '@glint/template': 1.3.0 ember-template-imports: 3.4.2 transitivePeerDependencies: @@ -17324,6 +17390,10 @@ snapshots: dependencies: '@types/node': 25.0.8 + '@types/archiver@7.0.0': + dependencies: + '@types/readdir-glob': 1.1.5 + '@types/babel__code-frame@7.27.0': {} '@types/babel__core@7.20.5': @@ -17594,6 +17664,10 @@ snapshots: '@types/range-parser@1.2.7': {} + '@types/readdir-glob@1.1.5': + dependencies: + '@types/node': 25.0.8 + '@types/responselike@1.0.3': dependencies: '@types/node': 25.0.8 @@ -18053,6 +18127,10 @@ snapshots: abbrev@1.1.1: {} + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -18201,6 +18279,29 @@ snapshots: arch@2.2.0: {} + archiver-utils@5.0.2: + dependencies: + glob: 10.5.0 + graceful-fs: 4.2.11 + is-stream: 2.0.1 + lazystream: 1.0.1 + lodash: 4.17.21 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + + archiver@7.0.1: + dependencies: + archiver-utils: 5.0.2 + async: 3.2.6 + buffer-crc32: 1.0.0 + readable-stream: 4.7.0 + readdir-glob: 1.1.3 + tar-stream: 3.1.7 + zip-stream: 6.0.1 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + are-we-there-yet@3.0.1: dependencies: delegates: 1.0.0 @@ -19164,6 +19265,8 @@ snapshots: buffer-crc32@0.2.13: {} + buffer-crc32@1.0.0: {} + buffer-equal-constant-time@1.0.1: {} buffer-from@1.1.2: {} @@ -19516,6 +19619,14 @@ snapshots: component-emitter@1.3.1: {} + compress-commons@6.0.2: + dependencies: + crc-32: 1.2.2 + crc32-stream: 6.0.0 + is-stream: 2.0.1 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + compressible@2.0.18: dependencies: mime-db: 1.54.0 @@ -19663,6 +19774,13 @@ snapshots: countries-list@3.2.2: {} + crc-32@1.2.2: {} + + crc32-stream@6.0.0: + dependencies: + crc-32: 1.2.2 + readable-stream: 4.7.0 + create-ecdh@4.0.4: dependencies: bn.js: 4.12.2 @@ -22242,6 +22360,8 @@ snapshots: stream-combiner: 0.0.4 through: 2.3.8 + event-target-shim@5.0.1: {} + eventemitter3@4.0.7: {} eventemitter3@5.0.1: {} @@ -24020,6 +24140,10 @@ snapshots: lazy-ass@1.6.0: {} + lazystream@1.0.1: + dependencies: + readable-stream: 2.3.8 + lcid@3.1.1: dependencies: invert-kv: 3.0.1 @@ -25609,6 +25733,18 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readdir-glob@1.1.3: + dependencies: + minimatch: 5.1.6 + recast@0.18.10: dependencies: ast-types: 0.13.3 @@ -27789,4 +27925,10 @@ snapshots: yoctocolors-cjs@2.1.3: {} + zip-stream@6.0.1: + dependencies: + archiver-utils: 5.0.2 + compress-commons: 6.0.2 + readable-stream: 4.7.0 + zod@3.25.76: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 1a255ac60e..57979045e8 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,97 +5,99 @@ packages: - vendor/* catalog: - '@actions/core': ^1.2.6 - '@actions/github': ^4.0.0 - '@aws-crypto/sha256-js': ^5.2.0 - '@babel/core': ^7.26.10 - '@babel/generator': ^7.17.8 - '@babel/helper-module-imports': ^7.18.2 - '@babel/helper-module-transforms': ^7.18.9 - '@babel/parser': 7.27.0 - '@babel/plugin-proposal-class-properties': ^7.18.6 - '@babel/plugin-proposal-decorators': ^7.23.2 - '@babel/plugin-syntax-class-properties': ^7.12.13 - '@babel/plugin-syntax-decorators': ^7.17.12 - '@babel/plugin-syntax-typescript': ^7.17.12 - '@babel/plugin-transform-class-properties': ^7.22.5 - '@babel/plugin-transform-class-static-block': ^7.22.11 - '@babel/plugin-transform-modules-amd': ^7.13.0 - '@babel/plugin-transform-typescript': ^7.16.8 - '@babel/preset-typescript': ^7.24.7 - '@babel/runtime': ^7.22.11 - '@babel/traverse': 7.27.0 - '@cardstack/requirejs-monaco-ember-polyfill': ^0.0.1 - '@cardstack/view-transitions': ^0.2.0 - '@ember/string': ^4.0.1 - '@ember/test-waiters': ^4.1.1 - '@eslint/eslintrc': ^2.1.4 - '@eslint/js': ^8.57.1 - '@floating-ui/dom': ^1.6.3 - '@glimmer/component': ^2.0.0 - '@glint/environment-ember-loose': ^1.5.2 - '@koa/cors': ^4.0.0 - '@koa/router': ^14.0.0 - '@lucide/lab': ^0.1.2 - '@lukeed/uuid': ^2.0.1 - '@octokit/rest': ^22.0.1 - '@percy/cli': ^1.31.1 - '@percy/ember': ^5.0.0 - '@playwright/test': ^1.54.0 - '@rollup/plugin-babel': ^6.0.4 - '@sentry/node': ^8.31.0 - '@simple-dom/interface': ^1.4.0 - '@simple-dom/parser': ^1.4.0 - '@simple-dom/serializer': ^1.4.0 - '@simple-dom/void-map': ^1.4.0 - '@sinonjs/fake-timers': ^11.2.2 - '@sqlite.org/sqlite-wasm': 3.45.1-build1 - '@tabler/icons': ^3.19.0 - '@types/babel__core': ^7.1.19 - '@types/babel__generator': ^7.6.4 - '@types/babel__traverse': ^7.14.2 - '@types/diff': ^5.0.2 - '@types/dompurify': ^3.0.2 - '@types/eslint': 8.56.5 - '@types/flat': ^5.0.5 - '@types/fs-extra': ^11.0.4 - '@types/htmlbars-inline-precompile': ^3.0.3 - '@types/indefinite': ^2.3.4 - '@types/js-string-escape': ^1.0.1 - '@types/js-yaml': ^4.0.9 - '@types/jsdom': ^21.1.1 - '@types/jsonwebtoken': ^9.0.5 - '@types/koa': ^2.13.5 - '@types/koa-compose': ^3.2.5 - '@types/koa__cors': ^4.0.0 - '@types/koa__router': ^12.0.0 - '@types/line-column': ^1.0.0 - '@types/lodash': ^4.17.15 - '@types/matrix-js-sdk': ^11.0.1 - '@types/mime-types': ^2.1.1 - '@types/ms': ^2.1.0 - '@types/node': ^24.3.0 - '@types/pg': ^8.11.5 - '@types/pluralize': ^0.0.30 - '@types/qs': ^6.9.17 - '@types/qunit': ^2.19.12 - '@types/rsvp': ^4.0.9 - '@types/sane': ^2.0.1 - '@types/sinon': ^17.0.3 - '@types/sinonjs__fake-timers': ^8.1.5 - '@types/statuses': ^2.0.5 - '@types/stream-chain': ^2.0.1 - '@types/stream-json': ^1.7.3 - '@types/string.prototype.matchall': ^4.0.1 - '@types/supertest': ^2.0.12 - '@types/tmp': ^0.2.3 - '@types/uuid': ^9.0.8 - '@types/yargs': ^17.0.10 - '@typescript-eslint/eslint-plugin': ^7.18.0 - '@typescript-eslint/parser': ^7.18.0 - '@universal-ember/test-support': ^0.5.1 - '@vscode/vsce': ^3.1.0 + "@actions/core": ^1.2.6 + "@actions/github": ^4.0.0 + "@aws-crypto/sha256-js": ^5.2.0 + "@babel/core": ^7.26.10 + "@babel/generator": ^7.17.8 + "@babel/helper-module-imports": ^7.18.2 + "@babel/helper-module-transforms": ^7.18.9 + "@babel/parser": 7.27.0 + "@babel/plugin-proposal-class-properties": ^7.18.6 + "@babel/plugin-proposal-decorators": ^7.23.2 + "@babel/plugin-syntax-class-properties": ^7.12.13 + "@babel/plugin-syntax-decorators": ^7.17.12 + "@babel/plugin-syntax-typescript": ^7.17.12 + "@babel/plugin-transform-class-properties": ^7.22.5 + "@babel/plugin-transform-class-static-block": ^7.22.11 + "@babel/plugin-transform-modules-amd": ^7.13.0 + "@babel/plugin-transform-typescript": ^7.16.8 + "@babel/preset-typescript": ^7.24.7 + "@babel/runtime": ^7.22.11 + "@babel/traverse": 7.27.0 + "@cardstack/requirejs-monaco-ember-polyfill": ^0.0.1 + "@cardstack/view-transitions": ^0.2.0 + "@ember/string": ^4.0.1 + "@ember/test-waiters": ^4.1.1 + "@eslint/eslintrc": ^2.1.4 + "@eslint/js": ^8.57.1 + "@floating-ui/dom": ^1.6.3 + "@glimmer/component": ^2.0.0 + "@glint/environment-ember-loose": ^1.5.2 + "@koa/cors": ^4.0.0 + "@koa/router": ^14.0.0 + "@lucide/lab": ^0.1.2 + "@lukeed/uuid": ^2.0.1 + "@octokit/rest": ^22.0.1 + "@percy/cli": ^1.31.1 + "@percy/ember": ^5.0.0 + "@playwright/test": ^1.54.0 + "@rollup/plugin-babel": ^6.0.4 + "@sentry/node": ^8.31.0 + "@simple-dom/interface": ^1.4.0 + "@simple-dom/parser": ^1.4.0 + "@simple-dom/serializer": ^1.4.0 + "@simple-dom/void-map": ^1.4.0 + "@sinonjs/fake-timers": ^11.2.2 + "@sqlite.org/sqlite-wasm": 3.45.1-build1 + "@tabler/icons": ^3.19.0 + "@types/archiver": ^7.0.0 + "@types/babel__core": ^7.1.19 + "@types/babel__generator": ^7.6.4 + "@types/babel__traverse": ^7.14.2 + "@types/diff": ^5.0.2 + "@types/dompurify": ^3.0.2 + "@types/eslint": 8.56.5 + "@types/flat": ^5.0.5 + "@types/fs-extra": ^11.0.4 + "@types/htmlbars-inline-precompile": ^3.0.3 + "@types/indefinite": ^2.3.4 + "@types/js-string-escape": ^1.0.1 + "@types/js-yaml": ^4.0.9 + "@types/jsdom": ^21.1.1 + "@types/jsonwebtoken": ^9.0.5 + "@types/koa": ^2.13.5 + "@types/koa-compose": ^3.2.5 + "@types/koa__cors": ^4.0.0 + "@types/koa__router": ^12.0.0 + "@types/line-column": ^1.0.0 + "@types/lodash": ^4.17.15 + "@types/matrix-js-sdk": ^11.0.1 + "@types/mime-types": ^2.1.1 + "@types/ms": ^2.1.0 + "@types/node": ^24.3.0 + "@types/pg": ^8.11.5 + "@types/pluralize": ^0.0.30 + "@types/qs": ^6.9.17 + "@types/qunit": ^2.19.12 + "@types/rsvp": ^4.0.9 + "@types/sane": ^2.0.1 + "@types/sinon": ^17.0.3 + "@types/sinonjs__fake-timers": ^8.1.5 + "@types/statuses": ^2.0.5 + "@types/stream-chain": ^2.0.1 + "@types/stream-json": ^1.7.3 + "@types/string.prototype.matchall": ^4.0.1 + "@types/supertest": ^2.0.12 + "@types/tmp": ^0.2.3 + "@types/uuid": ^9.0.8 + "@types/yargs": ^17.0.10 + "@typescript-eslint/eslint-plugin": ^7.18.0 + "@typescript-eslint/parser": ^7.18.0 + "@universal-ember/test-support": ^0.5.1 + "@vscode/vsce": ^3.1.0 ajv: ^8.17.1 + archiver: ^7.0.0 awesome-phonenumber: ^7.2.0 babel-eslint: ^10.1.0 babel-plugin-dynamic-import-node: ^2.3.3 From d8353cba765873bacf5c7f664496b35d727848e2 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Fri, 30 Jan 2026 10:08:20 -0600 Subject: [PATCH 02/15] Add preliminary test --- .github/workflows/ci.yaml | 1 + packages/realm-server/tests/index.ts | 1 + .../server-endpoints/download-realm-test.ts | 62 +++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 packages/realm-server/tests/server-endpoints/download-realm-test.ts diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4f0c30c537..6da671e7c4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -483,6 +483,7 @@ jobs: "types-endpoint-test.ts", "server-endpoints/authentication-test.ts", "server-endpoints/bot-registration-test.ts", + "server-endpoints/download-realm-test.ts", "server-endpoints/index-responses-test.ts", "server-endpoints/maintenance-endpoints-test.ts", "server-endpoints/queue-status-test.ts", diff --git a/packages/realm-server/tests/index.ts b/packages/realm-server/tests/index.ts index a330cf9584..f713252c9d 100644 --- a/packages/realm-server/tests/index.ts +++ b/packages/realm-server/tests/index.ts @@ -144,6 +144,7 @@ import './realm-endpoints/user-test'; import './search-prerendered-test'; import './server-endpoints/authentication-test'; import './server-endpoints/bot-registration-test'; +import './server-endpoints/download-realm-test'; import './server-endpoints/index-responses-test'; import './server-endpoints/maintenance-endpoints-test'; import './server-endpoints/queue-status-test'; diff --git a/packages/realm-server/tests/server-endpoints/download-realm-test.ts b/packages/realm-server/tests/server-endpoints/download-realm-test.ts new file mode 100644 index 0000000000..810b6d6886 --- /dev/null +++ b/packages/realm-server/tests/server-endpoints/download-realm-test.ts @@ -0,0 +1,62 @@ +import { module, test } from 'qunit'; +import { basename } from 'path'; +import { setupServerEndpointsTest, testRealm2URL } from './helpers'; + +function binaryParser( + res: NodeJS.ReadableStream, + callback: (err: Error | null, body: Buffer) => void, +) { + res.setEncoding('binary'); + let data = ''; + res.on('data', (chunk) => { + data += chunk; + }); + res.on('end', () => { + callback(null, Buffer.from(data, 'binary')); + }); +} + +module(`server-endpoints/${basename(__filename)}`, function (hooks) { + let context = setupServerEndpointsTest(hooks); + + test('downloads realm as a zip archive', async function (assert) { + let response = await context.request2 + .get('/_download-realm') + .query({ realm: testRealm2URL.href }) + .buffer(true) + .parse(binaryParser); + + assert.strictEqual(response.status, 200, 'returns 200'); + assert.strictEqual( + response.headers['content-type'], + 'application/zip', + 'serves a zip archive', + ); + assert.ok( + response.headers['content-disposition']?.includes('.zip'), + 'includes attachment filename', + ); + assert.ok(response.body instanceof Buffer, 'response body is a Buffer'); + assert.strictEqual( + response.body.subarray(0, 2).toString('utf8'), + 'PK', + 'zip file signature is present', + ); + assert.ok( + response.body.includes(Buffer.from('.realm.json')), + 'archive includes realm files', + ); + }); + + test('requires auth when realm is not public', async function (assert) { + await context.dbAdapter.execute( + `DELETE FROM realm_user_permissions WHERE realm_url = '${testRealm2URL.href}' AND username = '*'`, + ); + + let response = await context.request2 + .get('/_download-realm') + .query({ realm: testRealm2URL.href }); + + assert.strictEqual(response.status, 401, 'returns unauthorized'); + }); +}); From a3f96c9ef335ee00ee881180635369b9024fc62a Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Fri, 30 Jan 2026 10:11:38 -0600 Subject: [PATCH 03/15] Add preliminary handler --- .../handlers/handle-download-realm.ts | 267 ++++++++++++++++++ packages/realm-server/routes.ts | 2 + 2 files changed, 269 insertions(+) create mode 100644 packages/realm-server/handlers/handle-download-realm.ts diff --git a/packages/realm-server/handlers/handle-download-realm.ts b/packages/realm-server/handlers/handle-download-realm.ts new file mode 100644 index 0000000000..db45e013fe --- /dev/null +++ b/packages/realm-server/handlers/handle-download-realm.ts @@ -0,0 +1,267 @@ +import type Koa from 'koa'; +import type { DBAdapter, Realm } from '@cardstack/runtime-common'; +import { + ensureTrailingSlash, + fetchUserPermissions, + logger, + param, + query, + PUBLISHED_DIRECTORY_NAME, + RealmPaths, +} from '@cardstack/runtime-common'; +import { AuthenticationError } from '@cardstack/runtime-common/router'; +import { parseRealmsParam } from '@cardstack/runtime-common/search-utils'; +import archiver from 'archiver'; +import { existsSync, statSync } from 'fs-extra'; +import { join, resolve, sep } from 'path'; +import type { CreateRoutesArgs } from '../routes'; +import { retrieveTokenClaim } from '../utils/jwt'; +import { + buildReadableRealms, + getPublishedRealmURLs, +} from '../utils/realm-readability'; +import { + fetchRequestFromContext, + sendResponseForBadRequest, + sendResponseForForbiddenRequest, + sendResponseForNotFound, + sendResponseForSystemError, + sendResponseForUnauthorizedRequest, + setContextResponse, +} from '../middleware'; +import type { ResponseWithNodeStream } from '@cardstack/runtime-common'; + +const log = logger('download-realm'); + +type PublishedRealmRow = { + id: string; +}; + +export default function handleDownloadRealm({ + dbAdapter, + realmSecretSeed, + realms, + realmsRootPath, + serverURL, +}: CreateRoutesArgs): (ctxt: Koa.Context, next: Koa.Next) => Promise { + return async function (ctxt: Koa.Context, _next: Koa.Next) { + let request = await fetchRequestFromContext(ctxt); + let url = new URL(request.url); + + let realmList = parseRealmsParam(url); + if (realmList.length === 0) { + let realmParam = + url.searchParams.get('realm') ?? url.searchParams.get('realmURL'); + if (realmParam) { + realmList = [ensureTrailingSlash(realmParam)]; + } + } + + if (realmList.length !== 1) { + await sendResponseForBadRequest( + ctxt, + 'A single realm must be specified via ?realm= or ?realms=', + ); + return; + } + + let realmURL = ensureTrailingSlash(realmList[0]); + let parsedRealmURL: URL; + try { + parsedRealmURL = new URL(realmURL); + realmURL = ensureTrailingSlash(parsedRealmURL.href); + } catch { + await sendResponseForBadRequest( + ctxt, + `Invalid realm URL supplied: ${realmURL}`, + ); + return; + } + if (!hasRealm(realms, realmURL)) { + await sendResponseForNotFound(ctxt, `Realm not found: ${realmURL}`); + return; + } + + let publishedRealmURLs = await getPublishedRealmURLs(dbAdapter, [realmURL]); + let authorization = ctxt.req.headers['authorization']; + let readableRealms: Set; + if (!authorization) { + let publicPermissions = await fetchUserPermissions(dbAdapter, { + userId: '*', + onlyOwnRealms: false, + }); + readableRealms = buildReadableRealms( + publicPermissions, + publishedRealmURLs, + ); + if (!readableRealms.has(realmURL)) { + await sendResponseForUnauthorizedRequest( + ctxt, + `Authorization required for realm: ${realmURL}`, + ); + return; + } + } else { + try { + let token = retrieveTokenClaim(authorization, realmSecretSeed); + let permissions = await fetchUserPermissions(dbAdapter, { + userId: token.user, + onlyOwnRealms: false, + }); + readableRealms = buildReadableRealms( + permissions, + publishedRealmURLs, + ); + if (!readableRealms.has(realmURL)) { + await sendResponseForForbiddenRequest( + ctxt, + `Insufficient permissions to read realm: ${realmURL}`, + ); + return; + } + } catch (e) { + if (e instanceof AuthenticationError) { + await sendResponseForUnauthorizedRequest(ctxt, e.message); + return; + } + throw e; + } + } + + let realmPath = await resolveRealmPath({ + dbAdapter, + realmURL, + realmsRootPath, + serverURL, + }); + if (!realmPath) { + await sendResponseForNotFound( + ctxt, + `Realm is not stored in realmsRootPath: ${realmURL}`, + ); + return; + } + + if (!existsSync(realmPath) || !statSync(realmPath).isDirectory()) { + await sendResponseForNotFound( + ctxt, + `Realm files not found on disk for ${realmURL}`, + ); + return; + } + + let filename = `${buildArchiveName(parsedRealmURL)}.zip`; + let archive = archiver('zip', { zlib: { level: 9 } }); + archive.on('warning', (warning) => { + log.warn(`Zip warning for ${realmURL}: ${warning}`); + }); + archive.on('error', (error) => { + log.error(`Zip error for ${realmURL}: ${error}`); + ctxt.res.destroy(error as Error); + }); + + let response = new Response(null, { + status: 200, + headers: { + 'content-type': 'application/zip', + 'content-disposition': `attachment; filename="${filename}"`, + }, + }) as ResponseWithNodeStream; + response.nodeStream = archive; + await setContextResponse(ctxt, response); + + try { + archive.directory(realmPath, false); + await archive.finalize(); + } catch (error) { + log.error(`Failed to create archive for ${realmURL}: ${error}`); + if (!ctxt.res.headersSent) { + await sendResponseForSystemError( + ctxt, + `Failed to stream realm archive for ${realmURL}`, + ); + } else { + ctxt.res.destroy(error as Error); + } + } + }; +} + +function hasRealm(realms: Realm[], realmURL: string): boolean { + return realms.some((realm) => ensureTrailingSlash(realm.url) === realmURL); +} + +async function resolveRealmPath({ + dbAdapter, + realmURL, + realmsRootPath, + serverURL, +}: { + dbAdapter: DBAdapter; + realmURL: string; + realmsRootPath: string; + serverURL: string; +}): Promise { + let published = (await query(dbAdapter, [ + 'SELECT id FROM published_realms WHERE published_realm_url =', + param(realmURL), + ])) as PublishedRealmRow[]; + if (published.length > 0) { + return join(realmsRootPath, PUBLISHED_DIRECTORY_NAME, published[0].id); + } + + let realmPath = realmPathFromServerURL({ + realmURL, + realmsRootPath, + serverURL, + }); + if (!realmPath) { + return null; + } + + let root = resolve(realmsRootPath); + let resolvedRealmPath = resolve(realmPath); + if ( + resolvedRealmPath !== root && + !resolvedRealmPath.startsWith(`${root}${sep}`) + ) { + return null; + } + + return realmPath; +} + +function realmPathFromServerURL({ + realmURL, + realmsRootPath, + serverURL, +}: { + realmURL: string; + realmsRootPath: string; + serverURL: string; +}): string | null { + let serverRoot = new RealmPaths(new URL(ensureTrailingSlash(serverURL))); + let localPath: string; + try { + localPath = serverRoot.local(new URL(realmURL)); + } catch { + return null; + } + + let parts = localPath.split('/').filter(Boolean); + if (parts.length < 1) { + return null; + } + + return resolve(join(realmsRootPath, ...parts)); +} + +function buildArchiveName(realmURL: URL): string { + let segments = realmURL.pathname.split('/').filter(Boolean); + let base = + segments.length >= 2 + ? segments.slice(-2).join('-') + : segments[0] ?? realmURL.hostname; + base = base.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, ''); + return base.length > 0 ? base : 'realm'; +} diff --git a/packages/realm-server/routes.ts b/packages/realm-server/routes.ts index 0267aed5e0..03e52ecb8f 100644 --- a/packages/realm-server/routes.ts +++ b/packages/realm-server/routes.ts @@ -42,6 +42,7 @@ import handleSearchPrerendered from './handlers/handle-search-prerendered'; import handleRealmInfo from './handlers/handle-realm-info'; import { multiRealmAuthorization } from './middleware/multi-realm-authorization'; import handleGitHubPRRequest from './handlers/handle-github-pr'; +import handleDownloadRealm from './handlers/handle-download-realm'; import { handleBotRegistrationRequest, handleBotRegistrationsRequest, @@ -241,6 +242,7 @@ export function createRoutes(args: CreateRoutesArgs) { jwtMiddleware(args.realmSecretSeed), handleGitHubPRRequest(args), ); + router.get('/_download-realm', handleDownloadRealm(args)); router.post( '/_bot-registration', jwtMiddleware(args.realmSecretSeed), From 6e430deb3a49bcea63484041b30e01674ce80e64 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Fri, 30 Jan 2026 10:37:30 -0600 Subject: [PATCH 04/15] Fix delivery of archive --- .../handlers/handle-download-realm.ts | 16 ++++++---------- .../server-endpoints/download-realm-test.ts | 4 +++- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/realm-server/handlers/handle-download-realm.ts b/packages/realm-server/handlers/handle-download-realm.ts index db45e013fe..f77f169b72 100644 --- a/packages/realm-server/handlers/handle-download-realm.ts +++ b/packages/realm-server/handlers/handle-download-realm.ts @@ -29,7 +29,6 @@ import { sendResponseForUnauthorizedRequest, setContextResponse, } from '../middleware'; -import type { ResponseWithNodeStream } from '@cardstack/runtime-common'; const log = logger('download-realm'); @@ -160,15 +159,12 @@ export default function handleDownloadRealm({ ctxt.res.destroy(error as Error); }); - let response = new Response(null, { - status: 200, - headers: { - 'content-type': 'application/zip', - 'content-disposition': `attachment; filename="${filename}"`, - }, - }) as ResponseWithNodeStream; - response.nodeStream = archive; - await setContextResponse(ctxt, response); + ctxt.status = 200; + ctxt.set('content-type', 'application/zip'); + ctxt.set('content-disposition', `attachment; filename="${filename}"`); + ctxt.respond = false; + + archive.pipe(ctxt.res); try { archive.directory(realmPath, false); diff --git a/packages/realm-server/tests/server-endpoints/download-realm-test.ts b/packages/realm-server/tests/server-endpoints/download-realm-test.ts index 810b6d6886..c4d187db3c 100644 --- a/packages/realm-server/tests/server-endpoints/download-realm-test.ts +++ b/packages/realm-server/tests/server-endpoints/download-realm-test.ts @@ -26,7 +26,9 @@ module(`server-endpoints/${basename(__filename)}`, function (hooks) { .buffer(true) .parse(binaryParser); - assert.strictEqual(response.status, 200, 'returns 200'); + let bodyPreview = + response.body?.toString?.('utf8') ?? response.text ?? ''; + assert.strictEqual(response.status, 200, bodyPreview.slice(0, 200)); assert.strictEqual( response.headers['content-type'], 'application/zip', From ba7ebd8a3467ea6150ea2e05cf8fc2ce535f0f40 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Fri, 30 Jan 2026 12:27:06 -0600 Subject: [PATCH 05/15] Add preliminary download button --- .../code-submode/left-panel-toggle.gts | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts b/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts index f12004fbc8..72e21b8165 100644 --- a/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts +++ b/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts @@ -6,13 +6,17 @@ import Component from '@glimmer/component'; import FileCheck from '@cardstack/boxel-icons/file-check'; import FolderTree from '@cardstack/boxel-icons/folder-tree'; +import { Button as BoxelButton } from '@cardstack/boxel-ui/components'; import { cn, not } from '@cardstack/boxel-ui/helpers'; +import { Download } from '@cardstack/boxel-ui/icons'; import RealmDropdown from '@cardstack/host/components/realm-dropdown'; import RestoreScrollPosition from '@cardstack/host/modifiers/restore-scroll-position'; import type { FileView } from '@cardstack/host/services/operator-mode-state-service'; import type OperatorModeStateService from '@cardstack/host/services/operator-mode-state-service'; import type RecentFilesService from '@cardstack/host/services/recent-files-service'; +import type NetworkService from '@cardstack/host/services/network'; +import type RealmService from '@cardstack/host/services/realm'; import InnerContainer from './inner-container'; import ToggleButton from './toggle-button'; @@ -35,6 +39,8 @@ interface Signature { export default class CodeSubmodeLeftPanelToggle extends Component { @service declare operatorModeStateService: OperatorModeStateService; @service declare private recentFilesService: RecentFilesService; + @service declare private network: NetworkService; + @service declare private realm: RealmService; private notifyFileBrowserIsVisible: (() => void) | undefined; @@ -93,6 +99,68 @@ export default class CodeSubmodeLeftPanelToggle extends Component { this.switchRealm(realmItem.path); }; + private get downloadRealmURL() { + let downloadURL = new URL('/_download-realm', this.args.realmURL); + downloadURL.searchParams.set('realm', this.args.realmURL); + return downloadURL.href; + } + + private get fallbackDownloadName() { + let realmURL = new URL(this.args.realmURL); + let segments = realmURL.pathname.split('/').filter(Boolean); + let base = + segments.length >= 2 + ? segments.slice(-2).join('-') + : (segments[0] ?? realmURL.hostname); + base = base.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, ''); + return base.length > 0 ? `${base}.zip` : 'realm.zip'; + } + + private extractFilename(contentDisposition: string | null): string | null { + if (!contentDisposition) { + return null; + } + let utf8Match = contentDisposition.match(/filename\\*=UTF-8''([^;]+)/i); + if (utf8Match?.[1]) { + return decodeURIComponent(utf8Match[1]); + } + let match = contentDisposition.match(/filename=\"?([^\";]+)\"?/i); + return match?.[1] ?? null; + } + + private triggerDownload(blob: Blob, filename: string) { + let blobUrl = URL.createObjectURL(blob); + let downloadLink = document.createElement('a'); + downloadLink.href = blobUrl; + downloadLink.download = filename; + document.body.appendChild(downloadLink); + downloadLink.click(); + document.body.removeChild(downloadLink); + URL.revokeObjectURL(blobUrl); + } + + downloadRealm = async (event: Event) => { + event.preventDefault(); + try { + let token = this.realm.token(this.args.realmURL); + let response = await this.network.authedFetch(this.downloadRealmURL, { + headers: token ? { Authorization: token } : {}, + }); + if (!response.ok) { + throw new Error( + `Failed to download realm: ${response.status} ${response.statusText}`, + ); + } + let blob = await response.blob(); + let filename = + this.extractFilename(response.headers.get('content-disposition')) ?? + this.fallbackDownloadName; + this.triggerDownload(blob, filename); + } catch (error) { + console.error('Error downloading realm:', error); + } + }; + } From aa1daba2ed9650e65714bc9a3f27f3b69c87ee90 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Fri, 30 Jan 2026 16:45:17 -0600 Subject: [PATCH 06/15] Update styles a bit This is still hideous. --- .../operator-mode/code-submode/left-panel-toggle.gts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts b/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts index 72e21b8165..08cf519215 100644 --- a/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts +++ b/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts @@ -220,6 +220,7 @@ export default class CodeSubmodeLeftPanelToggle extends Component { @@ -258,7 +259,15 @@ export default class CodeSubmodeLeftPanelToggle extends Component { background-color: #f4f4f6; } .realm-download-button { - width: 100%; + --boxel-button-min-height: 1.5rem; + --boxel-button-padding: 0 var(--boxel-sp-5xs); + --boxel-button-font: 600 var(--boxel-font-xs); + justify-content: flex-start; + gap: var(--boxel-sp-xxxs); + align-self: flex-start; + text-transform: capitalize; + margin: var(--boxel-sp-xxxs); + padding: var(--boxel-sp-xs) var(--boxel-sp-sm); border-radius: var(--boxel-radius); border: 1px solid var(--boxel-400); From 4406ffa2e0039af4e0f9954a3006003b49d0e8a9 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Fri, 30 Jan 2026 21:51:28 -0600 Subject: [PATCH 07/15] Fix appearance of download button --- .../code-submode/left-panel-toggle.gts | 55 ++++++++++++------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts b/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts index 08cf519215..0e541217d4 100644 --- a/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts +++ b/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts @@ -191,6 +191,7 @@ export default class CodeSubmodeLeftPanelToggle extends Component { {{#if this.isFileTreeShowing}} + { @displayReadOnlyTag={{true}} @contentClass='realm-dropdown-menu' /> + +
+ + + Download + +
+ {{/if}} + { {{yield to='inspector'}} {{/if}} - {{#if this.isFileTreeShowing}} - - - Download Realm - - {{/if}} @@ -251,32 +256,40 @@ export default class CodeSubmodeLeftPanelToggle extends Component { border: none; border-bottom: 1px solid var(--boxel-400); padding: var(--boxel-sp-xs); + padding-bottom: var(--boxel-sp); height: fit-content; } + .realm-download { - padding: var(--boxel-sp-xs); border-bottom: var(--boxel-border); - background-color: #f4f4f6; + background-color: var(--boxel-light-100); + padding: var(--boxel-sp-xxs); } + .realm-download-button { --boxel-button-min-height: 1.5rem; - --boxel-button-padding: 0 var(--boxel-sp-5xs); + --boxel-button-padding: var(--boxel-sp-5xs) var(--boxel-sp-5xs) + var(--boxel-sp-5xs) var(--boxel-sp-xxxs); --boxel-button-font: 600 var(--boxel-font-xs); + justify-content: flex-start; gap: var(--boxel-sp-xxxs); align-self: flex-start; - text-transform: capitalize; - margin: var(--boxel-sp-xxxs); - padding: var(--boxel-sp-xs) var(--boxel-sp-sm); + border: 0; + background: transparent; border-radius: var(--boxel-radius); - border: 1px solid var(--boxel-400); - background-color: var(--boxel-light); cursor: pointer; font: inherit; + width: 100%; + + svg { + margin-bottom: var(--boxel-sp-6xs); + } } + .realm-download-button:hover { - background-color: #ededf0; + background-color: var(--boxel-light-200); } From b95881ad4a2721adadcb4945a93f57190eebc9a9 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Fri, 30 Jan 2026 21:52:34 -0600 Subject: [PATCH 08/15] Fix lint errors --- packages/realm-server/handlers/handle-download-realm.ts | 8 ++------ .../tests/server-endpoints/download-realm-test.ts | 3 +-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/realm-server/handlers/handle-download-realm.ts b/packages/realm-server/handlers/handle-download-realm.ts index f77f169b72..b2002edac7 100644 --- a/packages/realm-server/handlers/handle-download-realm.ts +++ b/packages/realm-server/handlers/handle-download-realm.ts @@ -27,7 +27,6 @@ import { sendResponseForNotFound, sendResponseForSystemError, sendResponseForUnauthorizedRequest, - setContextResponse, } from '../middleware'; const log = logger('download-realm'); @@ -107,10 +106,7 @@ export default function handleDownloadRealm({ userId: token.user, onlyOwnRealms: false, }); - readableRealms = buildReadableRealms( - permissions, - publishedRealmURLs, - ); + readableRealms = buildReadableRealms(permissions, publishedRealmURLs); if (!readableRealms.has(realmURL)) { await sendResponseForForbiddenRequest( ctxt, @@ -257,7 +253,7 @@ function buildArchiveName(realmURL: URL): string { let base = segments.length >= 2 ? segments.slice(-2).join('-') - : segments[0] ?? realmURL.hostname; + : (segments[0] ?? realmURL.hostname); base = base.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, ''); return base.length > 0 ? base : 'realm'; } diff --git a/packages/realm-server/tests/server-endpoints/download-realm-test.ts b/packages/realm-server/tests/server-endpoints/download-realm-test.ts index c4d187db3c..be52dd3bf7 100644 --- a/packages/realm-server/tests/server-endpoints/download-realm-test.ts +++ b/packages/realm-server/tests/server-endpoints/download-realm-test.ts @@ -26,8 +26,7 @@ module(`server-endpoints/${basename(__filename)}`, function (hooks) { .buffer(true) .parse(binaryParser); - let bodyPreview = - response.body?.toString?.('utf8') ?? response.text ?? ''; + let bodyPreview = response.body?.toString?.('utf8') ?? response.text ?? ''; assert.strictEqual(response.status, 200, bodyPreview.slice(0, 200)); assert.strictEqual( response.headers['content-type'], From 0bb6116c9e47f75ff9a3c896620410b480792833 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Fri, 30 Jan 2026 22:05:34 -0600 Subject: [PATCH 09/15] Fix type errors --- packages/realm-server/package.json | 1 + .../server-endpoints/download-realm-test.ts | 10 +++++-- pnpm-lock.yaml | 30 +++++++++++-------- pnpm-workspace.yaml | 1 + 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/packages/realm-server/package.json b/packages/realm-server/package.json index 492c8c095c..7fe71b0a5f 100644 --- a/packages/realm-server/package.json +++ b/packages/realm-server/package.json @@ -35,6 +35,7 @@ "@types/qunit": "catalog:", "@types/sane": "catalog:", "@types/sinon": "catalog:", + "@types/superagent": "catalog:", "@types/supertest": "catalog:", "@types/tmp": "catalog:", "@types/uuid": "catalog:", diff --git a/packages/realm-server/tests/server-endpoints/download-realm-test.ts b/packages/realm-server/tests/server-endpoints/download-realm-test.ts index be52dd3bf7..c54762e8f1 100644 --- a/packages/realm-server/tests/server-endpoints/download-realm-test.ts +++ b/packages/realm-server/tests/server-endpoints/download-realm-test.ts @@ -1,16 +1,20 @@ import { module, test } from 'qunit'; import { basename } from 'path'; import { setupServerEndpointsTest, testRealm2URL } from './helpers'; +import type { Response } from 'superagent'; function binaryParser( - res: NodeJS.ReadableStream, + res: Response, callback: (err: Error | null, body: Buffer) => void, ) { - res.setEncoding('binary'); let data = ''; - res.on('data', (chunk) => { + + res.setEncoding('binary'); + + res.on('data', (chunk: string) => { data += chunk; }); + res.on('end', () => { callback(null, Buffer.from(data, 'binary')); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c31b7afc55..dea5f3b9c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -237,6 +237,9 @@ catalogs: '@types/stream-json': specifier: ^1.7.3 version: 1.7.8 + '@types/superagent': + specifier: ^8.1.9 + version: 8.1.9 '@types/supertest': specifier: ^2.0.12 version: 2.0.16 @@ -734,7 +737,7 @@ importers: version: 1.3.0(typescript@5.8.3) '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) '@glint/environment-ember-template-imports': specifier: 1.3.0 version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0) @@ -1010,7 +1013,7 @@ importers: version: 1.10.2 '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) '@glint/template': specifier: 1.3.0 version: 1.3.0 @@ -1131,7 +1134,7 @@ importers: version: 2.0.0 '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) '@glint/template': specifier: 1.3.0 version: 1.3.0 @@ -1324,7 +1327,7 @@ importers: version: 1.3.0(typescript@5.8.3) '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) '@glint/environment-ember-template-imports': specifier: 1.3.0 version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0) @@ -1516,7 +1519,7 @@ importers: version: 1.7.4 '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) '@glint/template': specifier: 1.3.0 version: 1.3.0 @@ -1709,7 +1712,7 @@ importers: dependencies: '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) ember-basic-dropdown: specifier: 8.0.4 version: 8.0.4(patch_hash=19b0fc5d4bd8b9aa296c4065fa5e33bdbb965db0b277810b596eacd0b9e2f428)(@ember/string@4.0.1)(@ember/test-helpers@3.3.1(@babel/core@7.28.6)(@glint/template@1.3.0)(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))(webpack@5.104.1))(@glimmer/component@2.0.0)(@glimmer/tracking@1.1.2)(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))))(@glint/template@1.3.0)(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1)) @@ -2192,7 +2195,7 @@ importers: version: 1.3.0(typescript@5.8.3) '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) '@glint/environment-ember-template-imports': specifier: 1.3.0 version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0) @@ -2789,6 +2792,9 @@ importers: '@types/sinon': specifier: 'catalog:' version: 17.0.4 + '@types/superagent': + specifier: 'catalog:' + version: 8.1.9 '@types/supertest': specifier: 'catalog:' version: 2.0.16 @@ -3140,7 +3146,7 @@ importers: version: 1.3.0(typescript@5.8.3) '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) '@glint/environment-ember-template-imports': specifier: 1.3.0 version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0) @@ -3209,7 +3215,7 @@ importers: version: 1.3.0(typescript@5.8.3) '@glint/environment-ember-loose': specifier: 'catalog:' - version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) + version: 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) '@glint/environment-ember-template-imports': specifier: ^1.3.0 version: 1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0) @@ -15565,7 +15571,7 @@ snapshots: ember-cli-babel: 7.26.11 ember-source: 5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1) optionalDependencies: - '@glint/environment-ember-loose': 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) + '@glint/environment-ember-loose': 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) '@glint/template': 1.3.0 transitivePeerDependencies: - supports-color @@ -16208,7 +16214,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)))': + '@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1)))': dependencies: '@glimmer/component': 2.0.0 '@glint/template': 1.3.0 @@ -16218,7 +16224,7 @@ snapshots: '@glint/environment-ember-template-imports@1.3.0(@glint/environment-ember-loose@1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))))(@glint/template@1.3.0)': dependencies: - '@glint/environment-ember-loose': 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5))) + '@glint/environment-ember-loose': 1.5.2(@glimmer/component@2.0.0)(@glint/template@1.3.0)(ember-cli-htmlbars@6.3.0)(ember-modifier@4.1.0(ember-source@5.4.1(patch_hash=f4d53cc0efd30368b913bdc898f342658768eaf0a8fb44678c0a07cf3aac24c1)(@babel/core@7.28.6)(@glimmer/component@2.0.0)(@glint/template@1.3.0)(rsvp@4.8.5)(webpack@5.104.1))) '@glint/template': 1.3.0 ember-template-imports: 3.4.2 transitivePeerDependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 57979045e8..f92fde2f74 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -86,6 +86,7 @@ catalog: "@types/sinonjs__fake-timers": ^8.1.5 "@types/statuses": ^2.0.5 "@types/stream-chain": ^2.0.1 + "@types/superagent": ^8.1.9 "@types/stream-json": ^1.7.3 "@types/string.prototype.matchall": ^4.0.1 "@types/supertest": ^2.0.12 From 55a82c5e4c890a6e4ec38131180af02e83c50ea8 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Fri, 30 Jan 2026 22:45:23 -0600 Subject: [PATCH 10/15] Remove nesting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There’s a mysterious syntax error, this isn’t helping it seems but…? --- .../operator-mode/code-submode/left-panel-toggle.gts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts b/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts index 0e541217d4..881301afed 100644 --- a/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts +++ b/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts @@ -282,10 +282,10 @@ export default class CodeSubmodeLeftPanelToggle extends Component { cursor: pointer; font: inherit; width: 100%; + } - svg { - margin-bottom: var(--boxel-sp-6xs); - } + .realm-download-button :deep(svg) { + margin-bottom: var(--boxel-sp-6xs); } .realm-download-button:hover { From df520db73a359d840636a0d0d3d0f43663c65bcd Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Fri, 30 Jan 2026 22:45:30 -0600 Subject: [PATCH 11/15] Add whitespace --- .../components/operator-mode/code-submode/left-panel-toggle.gts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts b/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts index 881301afed..c110d17bca 100644 --- a/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts +++ b/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts @@ -190,6 +190,7 @@ export default class CodeSubmodeLeftPanelToggle extends Component { Inspector + {{#if this.isFileTreeShowing}} Date: Mon, 2 Feb 2026 10:45:25 -0600 Subject: [PATCH 12/15] Extract helper function to fix Glint failure --- .../code-submode/left-panel-toggle.gts | 35 ++++++------------- packages/host/app/lib/download-realm.ts | 23 ++++++++++++ 2 files changed, 33 insertions(+), 25 deletions(-) create mode 100644 packages/host/app/lib/download-realm.ts diff --git a/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts b/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts index c110d17bca..f08ee69c0e 100644 --- a/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts +++ b/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts @@ -11,7 +11,15 @@ import { cn, not } from '@cardstack/boxel-ui/helpers'; import { Download } from '@cardstack/boxel-ui/icons'; import RealmDropdown from '@cardstack/host/components/realm-dropdown'; + +// These were inline but caused the template to have spurious Glint errors +import { + extractFilename, + fallbackDownloadName, +} from '@cardstack/host/lib/download-realm'; + import RestoreScrollPosition from '@cardstack/host/modifiers/restore-scroll-position'; + import type { FileView } from '@cardstack/host/services/operator-mode-state-service'; import type OperatorModeStateService from '@cardstack/host/services/operator-mode-state-service'; import type RecentFilesService from '@cardstack/host/services/recent-files-service'; @@ -105,29 +113,6 @@ export default class CodeSubmodeLeftPanelToggle extends Component { return downloadURL.href; } - private get fallbackDownloadName() { - let realmURL = new URL(this.args.realmURL); - let segments = realmURL.pathname.split('/').filter(Boolean); - let base = - segments.length >= 2 - ? segments.slice(-2).join('-') - : (segments[0] ?? realmURL.hostname); - base = base.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, ''); - return base.length > 0 ? `${base}.zip` : 'realm.zip'; - } - - private extractFilename(contentDisposition: string | null): string | null { - if (!contentDisposition) { - return null; - } - let utf8Match = contentDisposition.match(/filename\\*=UTF-8''([^;]+)/i); - if (utf8Match?.[1]) { - return decodeURIComponent(utf8Match[1]); - } - let match = contentDisposition.match(/filename=\"?([^\";]+)\"?/i); - return match?.[1] ?? null; - } - private triggerDownload(blob: Blob, filename: string) { let blobUrl = URL.createObjectURL(blob); let downloadLink = document.createElement('a'); @@ -153,8 +138,8 @@ export default class CodeSubmodeLeftPanelToggle extends Component { } let blob = await response.blob(); let filename = - this.extractFilename(response.headers.get('content-disposition')) ?? - this.fallbackDownloadName; + extractFilename(response.headers.get('content-disposition')) ?? + fallbackDownloadName(new URL(this.args.realmURL)); this.triggerDownload(blob, filename); } catch (error) { console.error('Error downloading realm:', error); diff --git a/packages/host/app/lib/download-realm.ts b/packages/host/app/lib/download-realm.ts new file mode 100644 index 0000000000..882d69f195 --- /dev/null +++ b/packages/host/app/lib/download-realm.ts @@ -0,0 +1,23 @@ +export function extractFilename( + contentDisposition: string | null, +): string | null { + if (!contentDisposition) { + return null; + } + let utf8Match = contentDisposition.match(/filename\\*=UTF-8''([^;]+)/i); + if (utf8Match?.[1]) { + return decodeURIComponent(utf8Match[1]); + } + let match = contentDisposition.match(/filename=\"?([^\";]+)\"?/i); + return match?.[1] ?? null; +} + +export function fallbackDownloadName(realmURL: URL) { + let segments = realmURL.pathname.split('/').filter(Boolean); + let base = + segments.length >= 2 + ? segments.slice(-2).join('-') + : (segments[0] ?? realmURL.hostname); + base = base.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, ''); + return base.length > 0 ? `${base}.zip` : 'realm.zip'; +} From ac50cf1f10f0f96e233c86da045d83d3c6dd5350 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Mon, 2 Feb 2026 10:54:02 -0600 Subject: [PATCH 13/15] Add more tests --- .../server-endpoints/download-realm-test.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/realm-server/tests/server-endpoints/download-realm-test.ts b/packages/realm-server/tests/server-endpoints/download-realm-test.ts index c54762e8f1..22fb8c4999 100644 --- a/packages/realm-server/tests/server-endpoints/download-realm-test.ts +++ b/packages/realm-server/tests/server-endpoints/download-realm-test.ts @@ -64,4 +64,26 @@ module(`server-endpoints/${basename(__filename)}`, function (hooks) { assert.strictEqual(response.status, 401, 'returns unauthorized'); }); + + test('returns 400 when realm is missing from query params', async function (assert) { + let response = await context.request2.get('/_download-realm'); + + assert.strictEqual(response.status, 400, 'returns bad request'); + assert.ok( + response.body.errors?.[0]?.includes('single realm must be specified'), + 'explains required realm parameter', + ); + }); + + test('returns 404 when realm is not registered on the server', async function (assert) { + let response = await context.request2 + .get('/_download-realm') + .query({ realm: 'http://127.0.0.1:4445/missing/' }); + + assert.strictEqual(response.status, 404, 'returns not found'); + assert.ok( + response.body.errors?.[0]?.includes('Realm not found'), + 'explains missing realm', + ); + }); }); From 89ee663d4bd292e30bd36c4718cbbccce8769b92 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Mon, 2 Feb 2026 10:56:03 -0600 Subject: [PATCH 14/15] Remove unnecessary escapes --- packages/host/app/lib/download-realm.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/host/app/lib/download-realm.ts b/packages/host/app/lib/download-realm.ts index 882d69f195..c0238aa6a0 100644 --- a/packages/host/app/lib/download-realm.ts +++ b/packages/host/app/lib/download-realm.ts @@ -8,7 +8,7 @@ export function extractFilename( if (utf8Match?.[1]) { return decodeURIComponent(utf8Match[1]); } - let match = contentDisposition.match(/filename=\"?([^\";]+)\"?/i); + let match = contentDisposition.match(/filename="?([^";]+)"?/i); return match?.[1] ?? null; } From 099ce9f8ca1ffec30a321211de6da0e079e2ed88 Mon Sep 17 00:00:00 2001 From: Buck Doyle Date: Mon, 2 Feb 2026 10:57:07 -0600 Subject: [PATCH 15/15] Fix import order --- .../operator-mode/code-submode/left-panel-toggle.gts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts b/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts index f08ee69c0e..27d3de1f15 100644 --- a/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts +++ b/packages/host/app/components/operator-mode/code-submode/left-panel-toggle.gts @@ -20,11 +20,11 @@ import { import RestoreScrollPosition from '@cardstack/host/modifiers/restore-scroll-position'; +import type NetworkService from '@cardstack/host/services/network'; import type { FileView } from '@cardstack/host/services/operator-mode-state-service'; import type OperatorModeStateService from '@cardstack/host/services/operator-mode-state-service'; -import type RecentFilesService from '@cardstack/host/services/recent-files-service'; -import type NetworkService from '@cardstack/host/services/network'; import type RealmService from '@cardstack/host/services/realm'; +import type RecentFilesService from '@cardstack/host/services/recent-files-service'; import InnerContainer from './inner-container'; import ToggleButton from './toggle-button';