From 413a0c84ada08640ce2e702f56ecec3199f5ce6d Mon Sep 17 00:00:00 2001 From: Omar Salah Date: Sun, 3 Nov 2024 21:39:32 +0200 Subject: [PATCH 1/2] Feat(core): add importActorProfile --- src/index.ts | 95 +++++++++++++++++++++++++++++++++---- test/fixtures/account2.tar | Bin 0 -> 43008 bytes test/index.spec.ts | 21 ++++++-- 3 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 test/fixtures/account2.tar diff --git a/src/index.ts b/src/index.ts index 4e7b998..79c26d2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ import * as tar from 'tar-stream' import { type Pack } from 'tar-stream' import YAML from 'yaml' +import { Readable } from 'stream' export type ActorProfileOptions = { actorProfile?: any @@ -18,9 +19,17 @@ export type ActorProfileOptions = { mutedAccounts?: any } -export function exportActorProfile ({ - actorProfile, outbox, followers, followingAccounts, lists, bookmarks, likes, - blockedAccounts, blockedDomains, mutedAccounts +export function exportActorProfile({ + actorProfile, + outbox, + followers, + followingAccounts, + lists, + bookmarks, + likes, + blockedAccounts, + blockedDomains, + mutedAccounts }: ActorProfileOptions): tar.Pack { const pack: Pack = tar.pack() // pack is a stream @@ -53,7 +62,10 @@ export function exportActorProfile ({ manifest.contents.activitypub.contents['actor.json'] = { url: 'https://www.w3.org/TR/activitypub/#actor-objects' } - pack.entry({ name: 'activitypub/actor.json' }, JSON.stringify(actorProfile, null, 2)) + pack.entry( + { name: 'activitypub/actor.json' }, + JSON.stringify(actorProfile, null, 2) + ) } if (outbox) { @@ -61,7 +73,10 @@ export function exportActorProfile ({ manifest.contents.activitypub.contents['outbox.json'] = { url: 'https://www.w3.org/TR/activitystreams-core/#collections' } - pack.entry({ name: 'activitypub/outbox.json' }, JSON.stringify(outbox, null, 2)) + pack.entry( + { name: 'activitypub/outbox.json' }, + JSON.stringify(outbox, null, 2) + ) } if (followers) { @@ -69,7 +84,10 @@ export function exportActorProfile ({ manifest.contents.activitypub.contents['followers.json'] = { url: 'https://www.w3.org/TR/activitystreams-core/#collections' } - pack.entry({ name: 'activitypub/followers.json' }, JSON.stringify(followers, null, 2)) + pack.entry( + { name: 'activitypub/followers.json' }, + JSON.stringify(followers, null, 2) + ) } if (likes) { @@ -77,7 +95,10 @@ export function exportActorProfile ({ manifest.contents.activitypub.contents['likes.json'] = { url: 'https://www.w3.org/TR/activitystreams-core/#collections' } - pack.entry({ name: 'activitypub/likes.json' }, JSON.stringify(likes, null, 2)) + pack.entry( + { name: 'activitypub/likes.json' }, + JSON.stringify(likes, null, 2) + ) } if (bookmarks) { @@ -85,7 +106,10 @@ export function exportActorProfile ({ manifest.contents.activitypub.contents['bookmarks.json'] = { url: 'https://www.w3.org/TR/activitystreams-core/#collections' } - pack.entry({ name: 'activitypub/bookmarks.json' }, JSON.stringify(bookmarks, null, 2)) + pack.entry( + { name: 'activitypub/bookmarks.json' }, + JSON.stringify(bookmarks, null, 2) + ) } if (followingAccounts) { @@ -94,7 +118,10 @@ export function exportActorProfile ({ manifest.contents.activitypub.contents['following_accounts.csv'] = { url: 'https://docs.joinmastodon.org/user/moving/#export' } - pack.entry({ name: 'activitypub/following_accounts.csv' }, followingAccounts) + pack.entry( + { name: 'activitypub/following_accounts.csv' }, + followingAccounts + ) } if (lists) { @@ -130,3 +157,53 @@ export function exportActorProfile ({ return pack } +export async function importActorProfile(tarBuffer: Buffer): Promise { + const extract = tar.extract() + const result: Record = {} + + return new Promise((resolve, reject) => { + extract.on('entry', async (header, stream, next) => { + let content = '' + + stream.on('data', (chunk) => { + content += chunk.toString() + }) + + stream.on('end', () => { + // Handle JSON files + if (header.name.endsWith('.json')) { + try { + result[header.name] = JSON.parse(content) + } catch (error) { + console.error(`Error parsing JSON from ${header.name}:`, error) + } + } + // Handle YAML files + else if ( + header.name.endsWith('.yaml') || + header.name.endsWith('.yml') + ) { + try { + result[header.name] = YAML.parse(content) + } catch (error) { + console.error(`Error parsing YAML from ${header.name}:`, error) + } + } + // Handle CSV files + else if (header.name.endsWith('.csv')) { + result[header.name] = content // Return raw CSV as a string or implement CSV parsing here if needed + } + + next() // Process the next file in the tar archive + }) + + stream.on('error', (error) => reject(error)) + }) + + extract.on('finish', () => resolve(result)) + + // Stream the buffer data into the tar extractor + const stream = Readable.from(tarBuffer) + stream.pipe(extract) + }) +} diff --git a/test/fixtures/account2.tar b/test/fixtures/account2.tar new file mode 100644 index 0000000000000000000000000000000000000000..47e8cca1371956ca5e1120fdfc7ed118f875ae06 GIT binary patch literal 43008 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 IXXjt?{}H@W-v9sr literal 0 HcmV?d00001 diff --git a/test/index.spec.ts b/test/index.spec.ts index 5f718f5..9c18027 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -1,18 +1,33 @@ // import { expect } from 'chai' import * as fs from 'node:fs' - -import { exportActorProfile } from '../src' +import * as path from 'path' +import { exportActorProfile, importActorProfile } from '../src' import { outbox } from './fixtures/outbox' import { actorProfile } from './fixtures/actorProfile' +import { expect } from 'chai' describe('exportActorProfile', () => { it('calls function', async () => { const filename = 'out/test-export-2024-01-01.tar' const tarball = fs.createWriteStream(filename) - const packStream = exportActorProfile({ actorProfile, outbox }) packStream.pipe(tarball) }) }) +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') + + // 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) + + // Example assertions to check specific files and content + expect(importedData).to.have.property('activitypub/actor.json') + }) +}) From ec2a868e950932223e790539aea70c4f79484d78 Mon Sep 17 00:00:00 2001 From: Omar Salah Date: Sun, 3 Nov 2024 21:40:26 +0200 Subject: [PATCH 2/2] add impioty fn to build script --- build-dist.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/build-dist.sh b/build-dist.sh index 70d6653..2f28cae 100755 --- a/build-dist.sh +++ b/build-dist.sh @@ -2,6 +2,7 @@ mkdir ./dist/esm cat >dist/esm/index.js <dist/esm/package.json <