From e91222af2069a6049dd7809dc3e9b77e10e4d519 Mon Sep 17 00:00:00 2001 From: Omar Salah Date: Wed, 8 Jan 2025 16:00:22 +0200 Subject: [PATCH 1/8] Add validation for exported ActivityPub tarballs --- out/test-export-2024-01-01.tar | Bin 12288 -> 5120 bytes src/verify.ts | 89 ++++++++++++++++++ test/fixtures/account2.tar | Bin 43008 -> 0 bytes .../tarball-samples/missing-actor.tar | Bin 0 -> 10081 bytes .../tarball-samples/missing-manifest.tar | Bin 0 -> 9097 bytes .../fixtures/tarball-samples/valid-export.tar | Bin 0 -> 20992 bytes test/index.spec.ts | 6 +- test/verify.spec.ts | 76 +++++++++++++++ 8 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 src/verify.ts delete mode 100644 test/fixtures/account2.tar create mode 100644 test/fixtures/tarball-samples/missing-actor.tar create mode 100644 test/fixtures/tarball-samples/missing-manifest.tar create mode 100644 test/fixtures/tarball-samples/valid-export.tar create mode 100644 test/verify.spec.ts diff --git a/out/test-export-2024-01-01.tar b/out/test-export-2024-01-01.tar index 12c677283ff971b17f0f3e4fdea1e91b201b8c9b..e592d65ba54d5bc255944689dd11d8ea5410f9f7 100644 GIT binary patch delta 287 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 { + const extract = tar.extract() + const errors: string[] = [] + const requiredFiles = [ + 'manifest.yaml', + 'activitypub/actor.json', + 'activitypub/outbox.json' + ] + const foundFiles = new Set() + + return await new Promise((resolve) => { + extract.on('entry', (header, stream, next) => { + const fileName = header.name + foundFiles.add(fileName) + + let content = '' + stream.on('data', (chunk) => { + content += chunk.toString() + }) + + stream.on('end', () => { + try { + // Validate JSON files + if (fileName.endsWith('.json')) { + JSON.parse(content) // Throws an error if content is not valid JSON + } + + // Validate manifest file + if (fileName === 'manifest.yaml') { + const manifest = YAML.parse(content) + if (!manifest['ubc-version']) { + errors.push('Manifest is missing required field: ubc-version') + } + if (!manifest.contents?.activitypub) { + errors.push( + 'Manifest is missing required field: contents.activitypub' + ) + } + } + } catch (error: any) { + errors.push(`Error processing file ${fileName}: ${error.message}`) + } + next() + }) + + stream.on('error', (error) => { + errors.push(`Stream error on file ${fileName}: ${error.message}`) + next() + }) + }) + + extract.on('finish', () => { + // Check if all required files are present + for (const file of requiredFiles) { + if (!foundFiles.has(file)) { + errors.push(`Missing required file: ${file}`) + } + } + + resolve({ + valid: errors.length === 0, + errors + }) + }) + + extract.on('error', (error) => { + errors.push(`Error during extraction: ${error.message}`) + resolve({ + valid: false, + errors + }) + }) + + // Convert Buffer to a Readable stream and pipe it to the extractor + const stream = Readable.from(tarBuffer) + stream.pipe(extract) + }) +} diff --git a/test/fixtures/account2.tar b/test/fixtures/account2.tar deleted file mode 100644 index 47e8cca1371956ca5e1120fdfc7ed118f875ae06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43008 zcmeHQ&93COmG;_CA=Emm(!Z+iYMeGOm_?Goq+<+X8yA$ssw-P1mGx77djbQ4Sr0JV z*-X62X5L_)Cod2`!W>e6B#Y{4 z+PaCeMZ3WC-5@eOCkf2R_8im4TWkkgNZCuw!c(EI1Umfxc6jFO@>euZNE&9}uljoW&%BK{T=^Y$pKFx4Ivq~`JW zxWuT&$upt~9n0}d%Z0yh?eNKSpR920+Trip>hhwdR=H6Rdb(QS3h?Z3ALt4Ky5$VRb z$ZMR}>$o^>nvLUlzU#VOH@8Rr7*Tf;lrzX$;<6pI(Z(s5TYG$>!IN z^Dg_Q`PYrJ4YnIMbo{mr?adM!Zm45lWLfbJm(_DzG&vb1ZWz#^)Jnj2gT(fIDj|&q z|BSbI4lO?xXF4277t(?ZRW&uQsoD`ZNbs@vCapl*mk=|CK_ZR(SyeAZS;s{}2ckur zNz0qIU%wnnTF*aU3eIVjMj3ui^JnnGx752sK+wu!2#G^cz6bThAgcR&V(^q9_Ncbg z_lL2BH-r(~!4PLu+ZpoJYAoU{VI_Ak#U1r_rc`4IZwVu~gCX9iwlm~7mhgr!f;$+} zMkPC3(?7Ne@G9?be;H zL~d*;6WBv-Q#EfXQWg)>XOm7<~wk#&*m?w)0w!d#QPZkz)%##HMyHvZ)JV!Q7u&PPGGv%S|$Ra~Me>$?rP@gKghj48s@13Wfj~!WHoYj3Ni;G6RI$2z(PmGQ% zEDUAqTv=GCH`bMfg?i7rvZ#pE8!L+n^{#MbQ8A+{WYdLu%(}9uP@ijESyasESy@!f zs7@9Y>gyCw78M8Oap%dRLcJ?ISyZTxJ5LrB>M6jJMTL6Ida|gP(X+CsnBhBFRLp3s z-l*_oQK3F<`?9D|pJ#npRH*l?FN+HGD*{)B)}w(X%3jg0`QSwLJd&Os%|m@m_!MP(yA_rxKDN~`erlD0Ok(W zuTEtOFvvdbuK@XZXwZSaj@*}Beq1$WO0MlemZzpJ)5HX@-wUqRX}&KuT1a?gNQ@Gg1_3j4m>gz%8v~oVFe#XMe*$sl>&?w zE zbNgG|WUaJV8=nF4^+Y-iCF${$0YDbN0{%25=+YvGenn6-xf;LVBMb)vyquxqk#;oc zbAqoeN$&yzH--KtDhPl#0?;iP6m`lf#i)dF0j)U~1b|yW?>9-Y{wjZqq60vGNwr}A z5&o$F#|wa4sPVWq-lU)$V6(Kcq6COA!QYmz0M~^X=>+y?2sEZegdHG%X;3 zYXllTi(4#k4#Us@q;s9-fU++P7#K~G0-6{Bi1-bmT_9*B0MG#|48DaC)*MeSq!Bo8 z%^}S?Kva|!^+H}mU`$+q6%{rbg4PGC_81_-;0Xgo4b%;uYig(>0jCszTqfw_(+Ou8 z2AJ~TFNm8{Q_(gqE2nm08^*+OhVNesRXW0r3CXNfvi)R2wSIxlhAc472xZn ztdCqOpMbGbSvM9&bWeK+B@$f1*{SfO=p%ZS4AxEr9kiYNciPBtZ%VF%DW38uM$ix~ z<&k58@Bv1SJ6_gEW}_vsQj|Wx%yB2pnpq9oONo_&@Bv1S`*YUF5g{)zQV>4C$Z^-t z8aY0zlNc!oA7JG8EP*w0MAl1;6od~ja(qI;8rd)ONf|^vAK!xT0Y;9GJ*YWd5)vbr zw^Ecoz|4M+N1Zn0$`KSL@ueVqfRPM{?kDX`TR!s$rF@?Y!bf=XZ)ECWCrtd!3_V#48vrYL=co$?7tAR9q+=f3tFp24RL6+HjE()Kv%@i$TRmpJ?5uu0cr5g9dResIEas zs)Gh`2c)h+NQZ-l@kQ`lF9`{3(9l02C-uW}dd6@SQqG`3+})^qNm#@V8czH+bw(S+ z73iR%)~8}Bto8Ycp?kEQ+GmdO?epBu78h-UqegJRY!y?1tuZ?>R1w}&;^s$v&@%K9 zUWLwPOhvai)@C$gD!g?zV=BPK<-U3|7&;2y8{%4hh6>T?ee`Mc6hm9#n=w*X+VUCA zn3}ou%}ETsg>Qwvshgp^@GA7pdkhVRS8>)hXE;;w-RM&>72o<6IShq{chrcUYs=LK zEkmo}Rp@gRL$%>m=o?WC{f1XDBHU*?YAU+*bvHxP;aj1xyQfy>`f?OQ=iytSZ!N`8 ze0UWaN7PhwYg92cdFxx(GV~zcQ6osREmuz<3}uK{p|86c8WFETUtBQMB3^~QG|146 zcoq6`6hlGcRcIVhQyXuMeK9q6Yjo88qnn{L@qM9hsm9Qpcoq8AQ4IBoSD|l2F?1+i zg}#ZEp-Ay6^bJ~uHpQ#Z_Z)J@j;N{V*7uNtpKR1wYFK0p}_GfG(M%s)6Y6LKqJ8Lw)qaec&uo0Em6AtVRd!clg9PVU{pNmk}Y{*F#Hy5%p|ON3dIZ4lM)P0Qo{V5FN(v7!m=v2q(KOihN}5JPhP0_|=Tu#5VCZh;0k) z_t-|OuCauo*xok0vcIDJI;`{~KiP&TGNbJdzTqElcP0vA$J}q@aPN9a;K2g)CM*B^ zKY#km|NR&1YaMnb;WpX@&fW~Sgk@%fBO}WR%mgKSJG8JBCyB&PYI#4{PViZGen9S1 zLP{FG&kk2p|F^#H_-!6?cD@6uV#f~*lSwDQ~D;0#A45UU6!3HWVA2E3jGs5)6iIZhQNJ-r|}OrXze0=q3qOSrY` zFOJ-X`yHi7Zo{u9$R;=k&X8H1QlO&I;{f_PT8s@7_dgqg+b>ZWA>de@(gggPp$6Df zz#;?M7_iAO_&rM_a%=+(O7JOleF?0oweh<;{jMRWG=MmVat!A}P!1QR%jF<}0~-pU zvgN=+G=7)GEkUbB$0Tilg@ux)B{0Hua69nfAUgwT3+P!GIC4(!R<+2Pu%) z5d7GuF)eNgNxB22l6D*2P`+jv`d>aIk)17>nb%Olo4_okijY#OcA7Ua!Uj! zFc_y2NOvUn!~g3hrNpx}oWTM00jDn`EIiaP{o$l;Q}7U3`Hu!VCm*86Djv473hNbn>><*`e)&<5jTh|oQwU_$Bws|?@O0i(Ski)@b@bu zYNTkTAe@OK9(Nt%)_^#MJsilB8YxiJ_^pp1e1MU{EA`Hk!ox@2NJ01jBZWtQosj}X zjW<#dKEO!f#b0Nn@Suk`QV>4CNa68gXQUXxyp^K#0cHx1Fgr5^lqPSbAbfz4B6|3z z>db||)*e0BP79Ew=S}1CxktdP@?%pFK4NUjCm#X!%3CQ2A7Q0@_7M{fZ>A`Hgq`vU zNXAYRY~>Hzp$nf!*ejol7XI4Y*q6^s!oz)j2nfQNz280j?H-~+E?f9(#_A#?eBkZM z#KOIQt05%!;WgC4UxVB=y!#Q-;ov2WZ9o{!ikdPPt+SQ(#3U7^bg~(t*RFKb!e6`AQB&)3@u|Lgs$KYN*Y<_*aABDC^sUX^so7hjimBDPzV-jY zU%PTdE&R1>9W}M_)`uSVrlPxt)wQP1`ErbR|L9)$Ygdk_g}-*Kql8x#!>lL1!&P5_ z_*2o%La52sW`>HX=wA41bQ}!Nq!#|#m2t7~*RFNcRCM=f@3x*f(Y^53uI!72zjm#o zruN>!CGysQ45_Xt%T);Yl9`*-G5D^Ll6 zlQolTXUBHnIEicSqR=uu*R#yXaj+SI(R&wLkp&agC5!ELcG#z1-+W-&&+G>F{J#Ob zulWeT51;>M*{&Oi=l}VRwVeO=h@nOzdi4+V(6{!4y`KN_?%lifJ9k}_hfQ8>`e6bm z>tS?kpP6@rN`BPPRIsYwpXhmdzbO;K$9w}!O(4*bGr>qESru*ZKh+qjdNRJGf!pVn zRY$@lL||gf(`EzgC6v7sRsH0`SlWO!l%`v4Lff^szU7-Z*!w_tMaV>uaZMbf-IknK zX#3+jW7S!j{1(n*qvko5fXP=hm+r7?G zYJq1VCk!3ai6YnZ;NVLWxi~O)+il{z!!>a1A?<>mw>+agp1<{Me`lLX5C8$$vVC(GW6!j0Xj|xDZ_j+rOxhmk5tp1H zX>FhE-IE)vEoXask3f5tV`8sJAbnt;nrPD>6j?ns|68u-i22_Om;8T4@6Er2%C-R|N00lR>3_hCXX zcgTiJ9&CLS`jKTJpX9V92Yh6Ru<_r{kK}m4eu7=c)%hb8Tp|9*`VYk7`Okr4FY$kc IXXjt?{}H@W-v9sr diff --git a/test/fixtures/tarball-samples/missing-actor.tar b/test/fixtures/tarball-samples/missing-actor.tar new file mode 100644 index 0000000000000000000000000000000000000000..64ac15bc3378a0855742e3b4f7bbcc2e00a5d877 GIT binary patch literal 10081 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 G-Twd(ORiS{ literal 0 HcmV?d00001 diff --git a/test/fixtures/tarball-samples/valid-export.tar b/test/fixtures/tarball-samples/valid-export.tar new file mode 100644 index 0000000000000000000000000000000000000000..7825814682fd103596453055de8dee67fc9455b5 GIT binary patch literal 20992 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 literal 0 HcmV?d00001 diff --git a/test/index.spec.ts b/test/index.spec.ts index 97a4228..6165093 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -35,13 +35,15 @@ describe('exportActorProfile', () => { describe('importActorProfile', () => { it('extracts and verifies contents from account2.tar', async () => { // Load the tar file as a buffer - const tarBuffer = fs.readFileSync('test/fixtures/account2.tar') + const tarBuffer = fs.readFileSync( + 'test/fixtures/tarball-samples/valid-export.tar' + ) // Use the importActorProfile function to parse the tar contents const importedData = await importActorProfile(tarBuffer) // Log or inspect the imported data structure - console.log('Imported Data:', importedData) + // console.log('Imported Data:', importedData) // Example assertions to check specific files and content expect(importedData).to.have.property('activitypub/actor.json') diff --git a/test/verify.spec.ts b/test/verify.spec.ts new file mode 100644 index 0000000..a524c61 --- /dev/null +++ b/test/verify.spec.ts @@ -0,0 +1,76 @@ +import { expect } from 'chai' +import { readFileSync } from 'fs' +import { validateExportStream } from '../src/verify' + +describe('validateExportStream', () => { + it('should validate a valid tarball', async () => { + // Load a valid tarball (e.g., exported-profile-valid.tar) + const tarBuffer = readFileSync( + 'test/fixtures/tarball-samples/valid-export.tar' + ) + const result = await validateExportStream(tarBuffer) + + expect(result.valid).to.be.true + expect(result.errors).to.be.an('array').that.is.empty + }) + + it('should fail if manifest.yaml is missing', async () => { + // Load a tarball with missing manifest.yaml + const tarBuffer = readFileSync( + 'test/fixtures/tarball-samples/missing-manifest.tar' + ) + const result = await validateExportStream(tarBuffer) + + expect(result.valid).to.be.false + }) + + it('should fail if actor.json is missing', async () => { + // Load a tarball with missing actor.json + const tarBuffer = readFileSync( + 'test/fixtures/tarball-samples/missing-actor.tar' + ) + const result = await validateExportStream(tarBuffer) + + expect(result.valid).to.be.false + console.log(JSON.stringify(result.errors)) + }) + + // it('should fail if outbox.json is missing', async () => { + // // Load a tarball with missing outbox.json + // const tarBuffer = readFileSync( + // 'test/fixtures/exported-profile-missing-outbox.tar' + // ) + // const result = await validateExportStream(tarBuffer) + + // expect(result.valid).to.be.false + // expect(result.errors).to.include( + // 'Missing required file: activitypub/outbox.json' + // ) + // }) + + // it('should fail if actor.json contains invalid JSON', async () => { + // // Load a tarball with invalid JSON in actor.json + // const tarBuffer = readFileSync( + // 'test/fixtures/exported-profile-invalid-actor-json.tar' + // ) + // const result = await validateExportStream(tarBuffer) + + // expect(result.valid).to.be.false + // expect(result.errors).to.include( + // 'Error processing file activitypub/actor.json: Unexpected token } in JSON at position 42' + // ) + // }) + + // it('should fail if manifest.yaml is invalid', async () => { + // // Load a tarball with invalid manifest.yaml + // const tarBuffer = readFileSync( + // 'test/fixtures/exported-profile-invalid-manifest.tar' + // ) + // const result = await validateExportStream(tarBuffer) + + // expect(result.valid).to.be.false + // expect(result.errors).to.include( + // 'Manifest is missing required field: ubc-version' + // ) + // }) +}) From ac6fb51b7153efc19e491b166f244cd90628e92a Mon Sep 17 00:00:00 2001 From: Omar Salah Date: Thu, 9 Jan 2025 16:32:28 +0200 Subject: [PATCH 2/8] Add validateExportStream to ESM build script --- build-dist.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build-dist.sh b/build-dist.sh index 2f28cae..ac0812a 100755 --- a/build-dist.sh +++ b/build-dist.sh @@ -1,8 +1,9 @@ -mkdir ./dist/esm +mkdir -p ./dist/esm cat >dist/esm/index.js <dist/esm/package.json < Date: Thu, 9 Jan 2025 16:32:38 +0200 Subject: [PATCH 3/8] Export verify module from index.ts --- src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/index.ts b/src/index.ts index fc7e808..b7a86e1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -254,3 +254,5 @@ function addMediaFile( lastModified: new Date().toISOString() } } + +export * from './verify.js' From e7ef58f411bd353851142cbe734a800d08f7a431 Mon Sep 17 00:00:00 2001 From: Omar Salah Date: Thu, 9 Jan 2025 18:43:56 +0200 Subject: [PATCH 4/8] Refactor importActorProfile to use ReadableStream and improve error handling --- src/index.ts | 49 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/index.ts b/src/index.ts index b7a86e1..90f90b1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,7 +4,7 @@ import * as tar from 'tar-stream' import { type Pack } from 'tar-stream' import YAML from 'yaml' -import { Readable } from 'stream' +import { type Readable } from 'stream' export interface ActorProfileOptions { actorProfile?: any @@ -178,14 +178,21 @@ export async function exportActorProfile({ } } -export async function importActorProfile(tarBuffer: Buffer): Promise { +/** + * Imports an ActivityPub profile from a .tar archive stream. + * @param tarStream - A ReadableStream containing the .tar archive. + * @returns A promise that resolves to the parsed profile data. + */ +export async function importActorProfile( + tarStream: Readable +): Promise> { const extract = tar.extract() const result: Record = {} return await new Promise((resolve, reject) => { extract.on('entry', (header, stream, next) => { + const fileName = header.name let content = '' - console.log(`Extracting file: ${header.name}`) stream.on('data', (chunk) => { content += chunk.toString() @@ -193,42 +200,34 @@ export async function importActorProfile(tarBuffer: Buffer): Promise { stream.on('end', () => { try { - if (header.name.endsWith('.json')) { - result[header.name] = JSON.parse(content) - } else if ( - header.name.endsWith('.yaml') || - header.name.endsWith('.yml') - ) { - result[header.name] = YAML.parse(content) - } else if (header.name.endsWith('.csv')) { - result[header.name] = content + if (fileName.endsWith('.json')) { + result[fileName] = JSON.parse(content) + } else if (fileName.endsWith('.yaml') || fileName.endsWith('.yml')) { + result[fileName] = YAML.parse(content) + } else if (fileName.endsWith('.csv')) { + result[fileName] = content } - console.log(`Successfully parsed: ${header.name}`) - } catch (error) { - console.error(`Error processing file ${header.name}:`, error) - reject(error) + } catch (error: any) { + reject(new Error(`Error processing file ${fileName}: ${error}`)) } next() }) - stream.on('error', (error) => { - console.error(`Stream error on file ${header.name}:`, error) - reject(error) + stream.on('error', (error: any) => { + reject(new Error(`Stream error on file ${fileName}: ${error}`)) }) }) extract.on('finish', () => { - console.log('Extraction complete', result) resolve(result) }) extract.on('error', (error) => { - console.error('Error during extraction:', error) - reject(error) + reject(new Error(`Error during extraction: ${error}`)) }) - const stream = Readable.from(tarBuffer) - stream.pipe(extract) + // Pipe the ReadableStream into the extractor + tarStream.pipe(extract) }) } @@ -255,4 +254,4 @@ function addMediaFile( } } -export * from './verify.js' +export * from './verify' From c86922797a2d84f62a3310bd6f2caabd5ce778dd Mon Sep 17 00:00:00 2001 From: Omar Salah Date: Thu, 9 Jan 2025 18:44:07 +0200 Subject: [PATCH 5/8] Refactor validateExportStream to accept ReadableStream and improve file validation logic --- src/verify.ts | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/verify.ts b/src/verify.ts index d8a6479..8fab645 100644 --- a/src/verify.ts +++ b/src/verify.ts @@ -1,27 +1,30 @@ import * as tar from 'tar-stream' -import { Readable } from 'stream' +import { type Readable } from 'stream' import YAML from 'yaml' /** * Validates the structure and content of an exported ActivityPub tarball. - * @param tarBuffer - A Buffer containing the .tar archive. + * @param tarStream - A ReadableStream containing the .tar archive. * @returns A promise that resolves to an object with `valid` (boolean) and `errors` (string[]). */ export async function validateExportStream( - tarBuffer: Buffer + tarStream: Readable ): Promise<{ valid: boolean; errors: string[] }> { + console.log('Validating export stream...') + console.log('length of tarStream: ', tarStream) const extract = tar.extract() const errors: string[] = [] const requiredFiles = [ - 'manifest.yaml', + 'manifest.yaml', // or 'manifest.yml' 'activitypub/actor.json', 'activitypub/outbox.json' - ] - const foundFiles = new Set() + ].map((file) => file.toLowerCase()) // Normalize to lowercase for consistent comparison + const foundFiles = new Set() return await new Promise((resolve) => { extract.on('entry', (header, stream, next) => { - const fileName = header.name + const fileName = header.name.toLowerCase() // Normalize file name + console.log(`Processing file: ${fileName}`) // Log the file name foundFiles.add(fileName) let content = '' @@ -37,7 +40,7 @@ export async function validateExportStream( } // Validate manifest file - if (fileName === 'manifest.yaml') { + if (fileName === 'manifest.yaml' || fileName === 'manifest.yml') { const manifest = YAML.parse(content) if (!manifest['ubc-version']) { errors.push('Manifest is missing required field: ubc-version') @@ -61,6 +64,9 @@ export async function validateExportStream( }) extract.on('finish', () => { + console.log('Found files:', Array.from(foundFiles)) // Debug log + console.log('Required files:', requiredFiles) // Debug log + // Check if all required files are present for (const file of requiredFiles) { if (!foundFiles.has(file)) { @@ -82,8 +88,7 @@ export async function validateExportStream( }) }) - // Convert Buffer to a Readable stream and pipe it to the extractor - const stream = Readable.from(tarBuffer) - stream.pipe(extract) + // Pipe the ReadableStream into the extractor + tarStream.pipe(extract) }) } From 6afa20dcbce496d0a0feb01404d394b3c65e60a9 Mon Sep 17 00:00:00 2001 From: Omar Salah Date: Thu, 9 Jan 2025 18:54:35 +0200 Subject: [PATCH 6/8] Add ReadableStream support for importActorProfile and validateExportStream; update dependencies --- out/test-export-2024-01-01.tar | Bin 5120 -> 5120 bytes package.json | 2 + .../tarball-samples/invalid-actor.tar | Bin 0 -> 11112 bytes .../tarball-samples/invalid-manifest.tar | Bin 0 -> 10306 bytes .../tarball-samples/missing-outbox.tar | Bin 0 -> 11044 bytes test/index.spec.ts | 4 +- test/verify.spec.ts | 74 +++++++++--------- 7 files changed, 42 insertions(+), 38 deletions(-) create mode 100644 test/fixtures/tarball-samples/invalid-actor.tar create mode 100644 test/fixtures/tarball-samples/invalid-manifest.tar create mode 100644 test/fixtures/tarball-samples/missing-outbox.tar diff --git a/out/test-export-2024-01-01.tar b/out/test-export-2024-01-01.tar index e592d65ba54d5bc255944689dd11d8ea5410f9f7..46bc8660163cf0d763e0cec1f31236717bc90773 100644 GIT binary patch delta 136 zcmZqBXwaC@D`0MJZenJjU|?uyYHX&!U^+RGF=aC&V-F)j#=ywb2rk2nEMshDXs*Cu d0MWzR!;H{lVqjzlm*GT~F*G+p=;8g(1^`0i9=HGi delta 136 zcmZqBXwaC@D`0GFVrXopU|?uyYGkayU^+RGF=aC&V-F)j#=ywf94^C*EMshDV4}ca d0MWzR!;H{lVqjnfm*GT~F*G+u=;8g(1^_V79(w=) diff --git a/package.json b/package.json index 38e5b79..ce42ab6 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,8 @@ "./package.json": "./package.json" }, "dependencies": { + "eslint-plugin-n": "^17.15.1", + "stream": "^0.0.3", "tar-stream": "^3.1.7", "yaml": "^2.5.1" }, diff --git a/test/fixtures/tarball-samples/invalid-actor.tar b/test/fixtures/tarball-samples/invalid-actor.tar new file mode 100644 index 0000000000000000000000000000000000000000..4c2553c71746565e026c242794695651006fbb05 GIT binary patch literal 11112 zcmeHNc{r4B_aDn3JK2|zB_d1q3L(3(hwS@4lzk~0d&p9DWgU_wA$uXaY}rz2$P#7W zLYBPG)Yse86|cVU?{{6_Ki=aS^E}VY+~=In^SRG`?sFbBB@9e55KXpYed8b^CRnTSCM9j!ji~^kg%%RJv4p zDM=|0vXdX==$@xw)#nF+)ReGs65qA!h61P|-TS@cm{cIpNx<=gyyfA(q-m__>f~(s zZ@*o3zk~aW-}4DQijBNf^>qW?miQUiV3NR_L!!bv!pj;DPCPx=+Zg-iZC>*uYWoT% z^T*gPt(O-9HqRxOk6O%@2QWI=_!9dJS zNa;E#n1*yBt(R4jDlT*s5gMjbCqJ~cM>yJUFt!RQHJkCYRyQ|vyGPDL7dO1At4oUA zJdJ#uXHWRuqE*FNZ_vopF?vu@WN7Qny>WtZYf8q-u*vJ>_z8z483UtBG?}j;Yoq#^ zdm1NOu#_xlvSKX6NH}8TS?UP7eFe@B4@JwfxX6o0@IHxLXl3&>l+2eFrAf|kbCnQ! zE&?vhXkRyMnGka+p81bD?yuI@_QuiZIeng5*bP$N`FlIwd9c`^FU0k`nOrZ|n*gZe(XRY}0`|B5Wm)&{p*uaY)ym%ul zG_~JB>TO1=Si=)adCdx{JC%y>CfETFxMf*sC~>=5Enoq*q%RpV*3{!)dwaCKmDnHnU=HKh}4eq1lTN z#_GCo(Y9u z(xO<@^471UR>&^cce)5V@mv;{=p^Wgr7X;`B2w8hdLrN~-8v0RvP@>Ompi8XNj~_b zFj>(%m6HM;ayw$xOy@9D2={IpRkysBkgAx33z*^y@t2YDTyDW{$4tKD@WS%Ec2#R9 zkYkYaZ(#d53fWYgW+ zR5t+}Ksxc=J>UR@N;c#<>tSRwHFvdjw{`Wp>Sp$Ri8_R92P4RHLWJkmEQaa^Lj)GciNvEU29G1{QhV!!Sc(0gscLtC^E0x1EcV<4@ip!`&~T zZZO%vhhWtg8 zxHQX;d7XQ&*cKQ_rmZbrjn}eyT_$`7PYn+v?ffU>o3K1qQ)m~ypaChul^|%K z$%HurY*Im%StzfNuh;FqMf=F>Iarv;J^K!)iPg|4n`d^P@=Bj+)x6TaHvCv9r?+Bg zNN{PeIy!8K^Ie{x4S&8Zc)NGpFQQ!kxg<}bgZ;LRNfPrxLy{y zo}D>H&|BPAGY0OjST3mz$5S@hZIsL(nayN067Zl=%6ZT_%B>_XlS)eq*${=S&~!~) ztp7Yj*45dThO4OXcBVF){9F-@IRk@>>ifD%8gaiNlk}LxK}3s5a(Z=H-u2rZAW zcl4p2I@oHR(gehJX1!6Z>$-W{Bk^4{#jH2A&n9X3KbaP5ej&GG^t;BiE*)qg7wEc_ zm{ne!ObyAJGYkbE@30CMC-h|e6O4~6d zRJONITT=~xOUm0|2A8X^ypu^I5u%!R?{+H|hlZ-0lm41Rb652l2VI8z_K5bG>Sx^( z7kUCj=pPloEFUjTI8%nD*8ro3-PBf6MyTec#PyWU5?k*&nD1%|sQR-vOPEGmuTI3t zDvNnMXFZ{}!qC}XTi-gCeq6X}5r0~8)E@G_f6-c2a>PU186#xV9XnMheEtbE_{5so zvAq;~hux*uQr_+r-u9iuY#rRD8xi7Nw~0auhoO2A9`2_~>rZR)(7Y?>7RTm=!ldqL zYlvM)p0BAj+Qq&*mfOzrK-ssdvfXB2vbCKfp>YK7b%Y#rMw9zR-*JMXj1D3g(7BV?lF%YE-7JR z%+VzufpJn_zi4v~I*Eh@l{ZgUXI7RU)a>e0W2>ins=mE(hZp}e`I(4k*s$?Riz+|n z?J2@}vS7Iu71K@9)$2iR8CPSE^(2JSlS&7t6-A4X1$#W7=7bXljnTebC7&V$PeGVm z*x8+MtE5dg-GLv_hDc2iiBb->qy^&H`)vz6{qmW#FD)>9_APM1A!^Xp(|ATOm`8#q z`fBm~DxZNXk;^=ol>6xo@nF2AF}FSNsZ`+Ia2L2%FSejHd2**v;uz)F05HT=zd<2q zw1q`?;wsRF4lXn0=n3<&@s!QGSI6cTPmtFvIqoV{F0Fg$gd4j0#9r;4j|zV+Q&E@i zHV>BIE7h#3B6&22^Uh6;R_V z(;j>1$Xv9xjw+%C3j{(gj|XQ`=Z};Rpst|Y#s{sT|Cp=_nITZb|JVYxvVuU!l<~lw z8mfQ5v6C}Of&lFDcm8_)k|1;?8ir3G6NC+&M1$$Z(REBkTc6&1DIQ!r1c79+Dyf@g z2WNXfq3Ks2OCRw&pO<4#4A1crv)$>Q)#>w!oHP&@>Gq!vFD%7r3`p?|adlf7tHdp> zH_TR`D7+b@?mo>*e6vPZXu_T#&jKu)?s+x(bqmvdfm0_3Vhn;Qt zrtE`(j=$JkTAW`H>Afg}&`0`hb9e%>QOY(pDTICRuJ@4fVth$0i&{67FsS_v5jW(Rq~uG~hkSc7v*s^BX6_cpX$ z5az)_g0xska}lxtEH{b9;K}-Jg}pbF0xpc#rZo4t#EVBvYjN?%u3) zspYF&SYHAE6^9uJ4cvAHtJiJEh}OzeHbE4lW2_}O8aFKK{Ts?bS@^Y*WrwDyF5j>3 zTQg?m)_Yv~S!yp!cB(Z+YT$gS=qkOzHe1pjQC~CjIQ&cBShM4B|Ap7xYl00P{pDHF z8&KT%GGarGv(RdJYTM63vQpCLS6m-SyLfO3P_RgoW?3Ayy zqt|)_C2(G^Ivc;qwi((6`@3JV;uBH$ykwCJ^);w9ZJMR7!l$n4v};Q{*%`qf z5SMN(LnPW;N^{IdHjIh6xj;5El|8VR zrc=XS;d*sXeJf;^x5!`7mW+4JPSVHe78#o}gcMxpo`oA*BuJ~oub%s)^JcL^VpnRr zs=J?&VQm$%NSC@LI3!MPutA_I{LIDETkgZq?9eD8Iu2oTYTacBks}&PSG6c!*JbDg zIxcwK=K;96@X}ZBa9QiEf+;3D%97-$T*O@8-d#N6s(S?#C+#k3hZ74zw8KftA`0Re z?ZOJ;Pukrpa1Y77ytdYC!|N)FkG*9e7J;ph(CEqj$(f3n+2%7ZOS!9P#4Wyc-qRPV z>4KZ7e5k0REJPVF%zs;E!B@&Ee4~|Ma>3$ za_kpMtkcsotQ2IE@R<)R6bZ2u4|3ov@$l2wn9_s3ATviSEcL)@|ID3|fu~JRC$*w? zoPfeZ49|~J!{%y*I6Yjx`jS4y+)1|JDVG$F@(TzJom?jq6H^S53YSR-xYHj<|Pi^*uj17 z!oI9T+Wzq317*VtWo;MxPbI;WX;|n7aaKl^lp^QIb}xpU>_FQX@p&*I_OioJy+E9L zk@UJM=j8%-4*JZzylZqFF>&H+ap7@7?c&3(VH`I{S_gNlx*U55?lWLXNQHjTTyuN` z??>w{<1xiUD^Dv0uLM?&juK5&7Xv z+V`BUDhZ)O*uUdZI zQZ#7dGX0a;sOpf}7&hv6XL+m*E4xZI?p)wDAHfk*F8WfD)%EPvXBYm90fJMQ3h_h@ z97FPoG18E#o8_#xSys`DHW;MiOq!OG@IB`<2K`Oh3qLRTI@6C@#iPZeYtOZ;!lt3z1xM|11{voQB-kpw769z6weuyt`g+LQ>7 z1Own=<8ag{6971akZKMF`|kv?18n)n0*!1l9XbA(IXT%wO`YwJ26q0GhMJnQe0LA0 zLLs|)iH6z>b`B2q^1dM+9-bji;a43zibFk|uT+(}+qpWp9&&MWC_*~VX!oJnb||CS z%D?F8>4tE<;C|I{y1OT!Emn&H?mxK`uAvdGp?PoB#aRQ!Ru5lgYDi^$crmMhEYrNo z?%M*DZW+7c0(gb=?BIL*cP7#y?*C)&@84KoY#putx4!&uebMDP(*Bd%`1|_uTh{y| zrUe>08OFg+Bmhz3o777p-wt&7TlIba*#zYKFPm#MrSEF?pHM(;;Uk|=*q3^k4siH? z@VvsW3Q;@3$R`8#1rAd~JH)Tww|tM_AZjNU*D zM7E0e1r9q6Eb0epJh(skn{LXlI#GLx$cE^?z+si(qq%#q`rpiwef3B+sM`hPIkI87 zFY}TKK+Wtxmr)Bv|EYhpuW~;XL-iP0joO!a*cPDj-wZxbH@7j0_Mephp})umu&C?^bAAbcHwc?3v2<{6UhIi}`A5l>KllI`R z+EJZGR#o=}4!cBr6sJ+m_@f#tsv*d-0ct@3m1l6+8ImKy0N8TG8tAXeQ8(zwbbnvq zusQNSN8tcF4&nR$WJBHkA=B}Ffy1aNj|jpc+q^%FZ^+zuUjRB#|2Ytc*m49gP{tE}h@Tm8o9@qn xIK-ACfN}8j@S;B=7>8u3{}mW;hCc`65L&qZ$v(F15mLs95QiwT$*d@%tYc=&4k<#m>`^G> z_Z;QZsSl0U_n*%nzuW8ZJoj^+>$>09{l2ezys0Z=Vo`ulCFCxr-tNV>U$`J9kcFp{ zql+8gd2J#PZrvAOtG&-mwKh+I?Al*9aa6?*}7fz+*DNh_SJ%l#H5|E+4P) zS9?F08wAY$WX{Zl(#*=l)MQCjMQ4dbCs^GNBZ3B#gOnb;?9GI;LW@lsj1kVvEJaGC z+epO}E~{08MY(Qb@=mS2qgR<5(xlkiqN>skZ`uKyYA{JRQQl>zVw;RMpUgY~6>Lx( z^yWA25wdE&cJ-$4fPh0?DFFk;3_Xo5jb1`r!u`y6JzZTljenL4P)1g!grKzs!X}2Iu#l=X0s=_omR^W{u`&`i-yf-i~O1FtbIs*>_2RgUtezxi+o2`1IM;ka)CcqFP+Q>ahE(d3R1~&QVQiL7e5OdW{Ln)zxz3dqxu>{+?@f9&ytNw5v1KT)Au|F2gHd*y)O0(Kb&&v;zQ8tUV z$RZRh>#0$^AjKe&L;{>Z_5S|6@B~;xk52zRf*AjsBB(*|R|6rRp45MX@=Pm8YlNxV z5A$ST0G3o7CDd^GD{BdYt4|66{!wlC-nwwK5k83l0#STttv$@a#?r#ojrR)7-fow< zZ9tQ{zI}dTZsG0cmujJefs0r0W!l3@ZZNpKWkFQ5jB8J=R}d?4Bj&IrL1>S-!6iezQf``*z^5 z2mR_7l|H3DaoyBRCFI{0N3xv0%FwanHD^#0RIj1MLnl8J#EpL2b7Qv?pN;%Nea3aF6oA8bCt_uaB$rJiVn--yLpl3 zdtSt35hlx(zK0my;A6l6!@eO#v%O2~J=1>`U}qs2Q4y8Kg%9`ULt+SU^NgS*Y;I6L)T$CnSxtYUqO4)TxXkzMis8)LY6;0Wo*C z;V?O|V(C(t^qfK+bK-iu1N*r|ocDq+hHs46)4dH8d3v(sN)^35Bi)lRBd*#wKSO~$ zYJ9roCs3EJix2df88-!c;^`~%SMO?Dzb+KHO`uMInIih$*bkn~2D5J?6fz(mbR)Xa zQ)j}GifvL%k@i?YF-Nc6{WE>;mFL(ZB<`8FxlAnkIIJr!z0ZDHpO|G5-jtex$qwbZZ?Ug>EEmF=UE68 zAH(C$EbSEzqAvSxlMO^0xJ*|+W~&n0l$D)0+oUc|s1fV2<K17fXgmTNkbT1&(Srk3}hcaUF+UnZB45w<1?TA^zj zJ5&9!kD{%$DFt6iv45sAlk#*Po!QZ&u4?b9O6Vkf`%Lad#`X?2n8e>h7KU%*TfHG0 zB&Njf4H`&|8vGFGBR9~gE79L^k{sfGy#SVa-mK8ACAQF%I$o7{Wg6cX^PGDZ$9Opg=wVVU7dpE2xLmu%b05~5MD(2B{|>Bh&k zJM1lI#1gPBe2TO^QyVY9!P&uZP4>3NBl6f2!R9QKlXbJ?a}R2y10Q)i+^YjOap*WC z1j}{3)X`EW{4DKdFoVzivZ$3=GgehCJ29*ghf`Bc-ckR9U40vp$4>WXPBWr;23gTQ zCf4D9k|8(0vuN~b3{N4BUJaZ9?x&-yGN_iF5Z&=~megw7&TLytQ0+RTUJ@2zwK^6p zry}n0oQ+X$^WNOIiU$E z@~SIZ?4RmY+vQb5wAkzGRV~xmN1_x7Pqtr zDc11PRl!kmAKwTw&LfgB3+LXHug!yR~mK1h0u

#pLrMUFt?#B_`KOQMFH-_SXZwK)yIZ|Bp2jmmfqasD z5l;E@tNaFTB(C$=<)OKTn%LJi%nMmcrO-wJ&#TU?dn zJC`;vEX(2kt)emGa@Ty`%LZQL_Mq)y>cRCdtOjnIp|YcC14q4XhC28vCX`ICd9bnp zUtoB2Gjt(Z^5$xHQpq&qm-YpqS;%d1*%`p%M<&3|tI4U8AzcWpCBgSul3Xw9@B5C}CH9=rnL+-I&wpD%fh51KLmGpnJWXa6R)uM4|JN0&dE z{M%v;L&s2)|N4bk0}fi>fy0o@dD%St3JABG2v)s>Vox+r9;=#Yz}EVUwQH=iW1Qke zZ(vL+tVw-&8LLN{Px(3}Vs}u}Tag7`6g2PR0L8&YNhdF0zQ z^wDWI<6Ecsn9Vd%#D0{2rx(-eCZl^dP6XLz)+@I|tkhCq+ftf~b@i<{*A710$)lyD zhQ$$YsI3ucek~vLqRv2uL&X@U=xNJwKY>kxCcMP0uKJ7FpDO#!-zs4!) zIuChp1`o2TC#rIxZCvGXBI10Zv)@Cc)7-U7ZTO|gD+;E=$9J#&vR`90dc~Sw2 ze18h*FZQc;G0jN36D&))r{_#7-8}OL)&xn41iZtR2A|DC#$^(&TkxEXiBd^06e({( z#xO$0jSU9ws;woAB@T4RZJK+C-no)+jX}+Rvs=Q~%l>^<)vSk@Q`b*?wtJ} zec<;PA}gs93p4h?$bcvh#DQ(4PhaqK zx0MiyhUlH%YiH*zW4@#q_%%(K7m{jV7U%^$uqn_(Jg_h?#M00*FQm{IGcUY)_Ho*M zJ~{u?7TC$osTr7<0`f?MjABf!AB|K_5OY3IuY$-A(irKQfO0pyuB|SKn7BrWU^67f zG^?}oMj(u*U1(?#yg`vtdB-JiaJI*n`JvMm)r}=I`R+8-#D>JR*L~V?XsCpCuN-h` zC|Sm#$EUAQn`n3DnStf7x19*T-6Iqz5cpoQhk)DlDAM#%E<@t<>y_@za@gWwGQg9x z02;~Ry_&KtLWBR9 zMwWM;LRN(oTeoJU%2j_Q=r8j+pl%U*2J3LmB#YcI+CSm!9_?R+SAP_1i9h`(A zzfHmiB(DE#4(0|hGw;6xvvjnxbM&xqas8t_Tz^O0aR5phxW0w|+!eB{Oy2zP%zYI@ zvBIV^-5han6*^XiUc8lIW##ZWitRH&$6CPaLs##QiFdXbYCMvlT_nGz#&!OYJ10Y0 zcDD18mdI#{57D8~!p#x`ZXuk0Lyf)LrELx!L`g?+B&CBVv_3fGW^{ww3;AFK;G&eL z*ee0W1t}9jSXZKsS6V!V7-hxg^wapdwVb#T^PFCM?wU#n43QJctdr>Iq%4eK4{-~o z4~(={4Lh^iukaEBQ_R~r-gaf?P*xIW))5#SYJ5j0Qjh=QtBfyRLbJ8Z8&mWmslhLW z*zk)(gxMS&2Zdh`49JT5DMR3{!gm`M^V&;*#@t%?KnjaYQY#X*ICW#}Jj1iuTgaf< zNOsz{C-|%kOWF$7Z;SDo4dIEaxCq(N*A#ztAL>;$4y&&OQaDNEy{qzaq9U z)>VwW^`kP;CUr~k3g@lPJ&MxZ3OlZUI18#36{Q(0sjezD;1kn1gd4+mVw=8-X_I&a zV@-TTpk z5D3-%2h*MVw{-snNB*;v2hQ?#&y)69cG%gty8Rj5L&LFwz~SO?mZ=Z{F$AHE9vsNu z=ZynA|7U}U5>5`t8>Wtqw)QX=+dm6r&ZMTgx{E@42bbbwh(>{?`U?mrC#2|QA0HoI zAD76h7CxoE4z5>fD!k3yE!+>ec{!C-TX}R|f^Aw)1(Hj3PG`xSVX`;hG36kjQ8A_1cD;e-nz|lAR2g_tAiSidv zP#)0LD3l7fS2Eyg*!w{_*x5eJ-GhTNiY{89?ok5QUYVDI0jD?n43*#M7JCl%&PCAm zBb4B^SLWdbfP;Tn^-ss39sm5`>MyaN1gX6;FXfH<50*cbr@qq*6()2PD1mCP%)^@i z)Bi^CM{?EAp+Y}1LkUQGB?BIY_fPxL;`29h($Buo0ijOe_DTl)8Ogr^p*#JnGr6DL zqVGu2*^mJbCEXX|0b9>~1hb!Ap(8{sRrg8;{1oNy5FX(57aZK9JNh03wUpc|8E_J+ zeF6Srv+-P;6j|0fDH1AK^nF&>CPL1o}VP8ZM#$ literal 0 HcmV?d00001 diff --git a/test/fixtures/tarball-samples/missing-outbox.tar b/test/fixtures/tarball-samples/missing-outbox.tar new file mode 100644 index 0000000000000000000000000000000000000000..95b56e72d750bfa62e61f6c019de0315b78e6990 GIT binary patch literal 11044 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 literal 0 HcmV?d00001 diff --git a/test/index.spec.ts b/test/index.spec.ts index 6165093..ce805f6 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -5,6 +5,7 @@ import { exportActorProfile, importActorProfile } from '../src' import { outbox } from './fixtures/outbox' import { actorProfile } from './fixtures/actorProfile' import { expect } from 'chai' +import { Readable } from 'node:stream' describe('exportActorProfile', () => { it('calls function', async () => { @@ -40,7 +41,8 @@ describe('importActorProfile', () => { ) // Use the importActorProfile function to parse the tar contents - const importedData = await importActorProfile(tarBuffer) + const tarStream = Readable.from(tarBuffer) + const importedData = await importActorProfile(tarStream) // Log or inspect the imported data structure // console.log('Imported Data:', importedData) diff --git a/test/verify.spec.ts b/test/verify.spec.ts index a524c61..5de6e4a 100644 --- a/test/verify.spec.ts +++ b/test/verify.spec.ts @@ -1,6 +1,7 @@ import { expect } from 'chai' import { readFileSync } from 'fs' -import { validateExportStream } from '../src/verify' +import { validateExportStream } from '../dist' +import { Readable } from 'stream' describe('validateExportStream', () => { it('should validate a valid tarball', async () => { @@ -8,7 +9,9 @@ describe('validateExportStream', () => { const tarBuffer = readFileSync( 'test/fixtures/tarball-samples/valid-export.tar' ) - const result = await validateExportStream(tarBuffer) + const tarStream = Readable.from(tarBuffer) + const result = await validateExportStream(tarStream) + console.log('🚀 ~ it ~ valid result:', result) expect(result.valid).to.be.true expect(result.errors).to.be.an('array').that.is.empty @@ -19,7 +22,9 @@ describe('validateExportStream', () => { const tarBuffer = readFileSync( 'test/fixtures/tarball-samples/missing-manifest.tar' ) - const result = await validateExportStream(tarBuffer) + const tarStream = Readable.from(tarBuffer) + const result = await validateExportStream(tarStream) + console.log('🚀 ~ it ~ miss mani result:', result) expect(result.valid).to.be.false }) @@ -29,48 +34,43 @@ describe('validateExportStream', () => { const tarBuffer = readFileSync( 'test/fixtures/tarball-samples/missing-actor.tar' ) - const result = await validateExportStream(tarBuffer) + const tarStream = Readable.from(tarBuffer) + const result = await validateExportStream(tarStream) expect(result.valid).to.be.false console.log(JSON.stringify(result.errors)) }) - // it('should fail if outbox.json is missing', async () => { - // // Load a tarball with missing outbox.json - // const tarBuffer = readFileSync( - // 'test/fixtures/exported-profile-missing-outbox.tar' - // ) - // const result = await validateExportStream(tarBuffer) + it('should fail if outbox.json is missing', async () => { + // Load a tarball with missing outbox.json + const tarBuffer = readFileSync( + 'test/fixtures/tarball-samples/missing-outbox.tar' + ) + const tarStream = Readable.from(tarBuffer) + const result = await validateExportStream(tarStream) - // expect(result.valid).to.be.false - // expect(result.errors).to.include( - // 'Missing required file: activitypub/outbox.json' - // ) - // }) + expect(result.valid).to.be.false + }) - // it('should fail if actor.json contains invalid JSON', async () => { - // // Load a tarball with invalid JSON in actor.json - // const tarBuffer = readFileSync( - // 'test/fixtures/exported-profile-invalid-actor-json.tar' - // ) - // const result = await validateExportStream(tarBuffer) + it('should fail if actor.json contains invalid JSON', async () => { + // Load a tarball with invalid JSON in actor.json + const tarBuffer = readFileSync( + 'test/fixtures/tarball-samples/invalid-actor.tar' + ) + const tarStream = Readable.from(tarBuffer) + const result = await validateExportStream(tarStream) - // expect(result.valid).to.be.false - // expect(result.errors).to.include( - // 'Error processing file activitypub/actor.json: Unexpected token } in JSON at position 42' - // ) - // }) + expect(result.valid).to.be.false + }) - // it('should fail if manifest.yaml is invalid', async () => { - // // Load a tarball with invalid manifest.yaml - // const tarBuffer = readFileSync( - // 'test/fixtures/exported-profile-invalid-manifest.tar' - // ) - // const result = await validateExportStream(tarBuffer) + it('should fail if manifest.yaml is invalid', async () => { + // Load a tarball with invalid manifest.yaml + const tarBuffer = readFileSync( + 'test/fixtures/tarball-samples/invalid-manifest.tar' + ) + const tarStream = Readable.from(tarBuffer) + const result = await validateExportStream(tarStream) - // expect(result.valid).to.be.false - // expect(result.errors).to.include( - // 'Manifest is missing required field: ubc-version' - // ) - // }) + expect(result.valid).to.be.false + }) }) From 7bfa1458a2f619d0cd53f98b0bd97de55a0be2e9 Mon Sep 17 00:00:00 2001 From: Omar Salah Date: Thu, 9 Jan 2025 18:56:14 +0200 Subject: [PATCH 7/8] Remove eslint-plugin-n dependency from package.json --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index ce42ab6..037872e 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "./package.json": "./package.json" }, "dependencies": { - "eslint-plugin-n": "^17.15.1", "stream": "^0.0.3", "tar-stream": "^3.1.7", "yaml": "^2.5.1" From 3a13b9eb85cac763a12e65342d67781ba7feb4d1 Mon Sep 17 00:00:00 2001 From: Omar Salah Date: Thu, 9 Jan 2025 19:14:26 +0200 Subject: [PATCH 8/8] Remove debug logging from validateExportStream function --- src/verify.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/verify.ts b/src/verify.ts index 8fab645..f47e943 100644 --- a/src/verify.ts +++ b/src/verify.ts @@ -11,7 +11,6 @@ export async function validateExportStream( tarStream: Readable ): Promise<{ valid: boolean; errors: string[] }> { console.log('Validating export stream...') - console.log('length of tarStream: ', tarStream) const extract = tar.extract() const errors: string[] = [] const requiredFiles = [ @@ -24,7 +23,6 @@ export async function validateExportStream( return await new Promise((resolve) => { extract.on('entry', (header, stream, next) => { const fileName = header.name.toLowerCase() // Normalize file name - console.log(`Processing file: ${fileName}`) // Log the file name foundFiles.add(fileName) let content = '' @@ -64,9 +62,6 @@ export async function validateExportStream( }) extract.on('finish', () => { - console.log('Found files:', Array.from(foundFiles)) // Debug log - console.log('Required files:', requiredFiles) // Debug log - // Check if all required files are present for (const file of requiredFiles) { if (!foundFiles.has(file)) {