npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@synap-core/import-core

v1.0.0

Published

Shared frontend import orchestration primitives for Relay and Browser

Readme

@synap-core/import-core

Shared frontend import primitives for Browser, Relay, and channels surfaces.

This package centralizes small, reusable import building blocks that are safe to share across app surfaces:

  • File helpers (readFileAsBase64, inferMimeTypeFromFileName)
  • Lightweight preview parsers (parseCsvPreview, parseBookmarksHtmlPreview, parseJsonPreview)
  • Lifecycle vocabulary and UI labels (ImportLifecycleStatus, getStatusLabel, isTerminalStatus, toProgressPercent)

It is intentionally narrow: it does not own backend orchestration, queue workers, mutation calls, or UI flow state machines.

Why This Package Exists

Multiple import entry points exist in the product today:

  • Browser settings import flow
  • Browser workspace ImportCell
  • Channels ImportDialog
  • Relay archive/contact import hooks

Without a shared package, each surface tends to re-implement:

  • Base64 file conversion
  • MIME inference from file name
  • Preview row extraction for CSV/JSON/bookmarks
  • Lifecycle status label mapping

import-core prevents this drift while keeping the API surface minimal and framework-agnostic.

Current Package API

Exports are defined in src/index.ts and currently include:

  • src/file.ts
    • type ImportBatchItem
    • inferMimeTypeFromFileName(fileName: string): string
    • readFileAsBase64(file: File): Promise<string>
  • src/preview.ts
    • type PreviewRow
    • parseCsvPreview(text: string): PreviewRow[]
    • parseBookmarksHtmlPreview(html: string): PreviewRow[]
    • parseJsonPreview(text: string): PreviewRow[]
  • src/lifecycle.ts
    • type ImportLifecycleStatus
    • type ImportLifecycleProgress
    • toProgressPercent(progress): number
    • isTerminalStatus(status): boolean
    • getStatusLabel(status): string
  • src/identifiers.ts
    • normalizeIdentifierSlug(value): string
    • buildTelegramExternalId(name, phone?): string
    • buildLinkedInExternalId({ email, firstName, lastName, fallbackIndex? }): string

Import Path Architecture

This section describes how import paths are structured today and where import-core fits.

1) Template-based path (format-driven batch import)

Current behavior:

  • UIs choose a format template (for example CSV, JSON, bookmarks HTML).
  • UIs may parse preview rows client-side for a user confirmation step.
  • UIs submit raw file payloads to backend import.submitBatch.
  • Backend ImportOrchestrator.submitBatch() routes by MIME/extension and transforms where supported.

Where it is used now:

  • Browser settings ImportSection (format registry + preview + submit)
  • Channels ImportDialog (direct file batch submit)
  • Browser ImportCell (direct file batch submit)

import-core role:

  • Preview parser helpers
  • File reading helper

2) Guided path (step-by-step UI flow)

Current behavior:

  • Surface-specific components own staged steps (select format, select file, preview, import, done/error).
  • Step orchestration/state is local UI logic.

Where it is used now:

  • Browser settings ImportSection step machine
  • Channels ImportDialog staged import states
  • Relay hooks (useTelegramImport, useLinkedInImport) with pick/parse/confirm phases

import-core role:

  • Shared lifecycle labels/types where a flow wants common status semantics
  • Shared file helpers/parsers so guided UIs do not duplicate low-level logic

3) AI-assisted path (modeling preview)

Current behavior:

  • Backend exposes import.previewModeling and orchestrator previewModeling(...).
  • This returns profile/view suggestions from sample rows.
  • No shared import-core wrapper exists for this endpoint today.

import-core role today:

  • None directly (endpoint integration is currently done outside this package).

Lifecycle Statuses

ImportLifecycleStatus in this package is:

  • queued
  • parsing
  • normalizing
  • merging
  • writing
  • completed
  • failed

Helper behavior:

  • getStatusLabel("merging") returns "Resolving schema" (UI-friendly wording)
  • isTerminalStatus returns true only for completed and failed
  • toProgressPercent clamps to 0..100 and handles zero totals

Important alignment note:

  • Relay hook local unions currently also include idle/picking/parsing and sometimes set non-lifecycle values like done or error in demo/error branches.
  • Treat ImportLifecycleStatus as canonical for server/job lifecycle states; keep local UI-only states separate when needed.

Capability Boundaries

import-core should own:

  • Small pure helpers usable across Browser/Relay/channels
  • Shared frontend lifecycle labels/types
  • Lightweight preview extraction for UX (not authoritative ingestion parsing)

import-core should not own:

  • tRPC calls (import.submitBatch, import.telegramContacts, import.linkedInContacts, import.previewModeling)
  • Backend parsing/transform orchestration logic
  • Queue/job processing logic
  • Workspace access checks, auth, or persistence
  • Product-specific UI state machines/components

Backend authority remains in:

  • synap-backend/packages/api/src/routers/import.ts
  • synap-backend/packages/api/src/services/import-orchestrator.ts
  • synap-backend/packages/api/src/utils/import-parsers.ts

Integration Points (Current State)

  • Relay hooks
    • useTelegramImport uses ImportLifecycleStatus and getStatusLabel
    • useLinkedInImport uses ImportLifecycleStatus and getStatusLabel
  • Browser
    • ImportSection uses preview parsers
    • ImportCell uses readFileAsBase64
  • Channels
    • ImportDialog uses readFileAsBase64
  • Backend
    • import router routes to ImportOrchestrator
    • Orchestrator handles submit batch, queue-based Telegram/LinkedIn imports, and modeling preview

Practical Examples

Read files for submitBatch payload

import { readFileAsBase64 } from "@synap-core/import-core";

const contentBase64 = await readFileAsBase64(file);
const item = {
  path: file.name,
  contentBase64,
  mimeType: file.type || "application/octet-stream",
};

Infer MIME type from a filename

import { inferMimeTypeFromFileName } from "@synap-core/import-core";

const mimeType = inferMimeTypeFromFileName("contacts.csv"); // "text/csv"

Build stable external IDs for archive imports

import {
  buildLinkedInExternalId,
  buildTelegramExternalId,
} from "@synap-core/import-core";

const tg = buildTelegramExternalId("Ada Lovelace", "+15550101");
const li = buildLinkedInExternalId({
  email: null,
  firstName: "Ada",
  lastName: "Lovelace",
  fallbackIndex: 0,
});

Generate preview rows from source text

import {
  parseCsvPreview,
  parseBookmarksHtmlPreview,
  parseJsonPreview,
} from "@synap-core/import-core";

const csvRows = parseCsvPreview(csvText);
const bookmarkRows = parseBookmarksHtmlPreview(bookmarksHtml);
const jsonRows = parseJsonPreview(jsonText);

Show consistent lifecycle labels/progress

import {
  getStatusLabel,
  isTerminalStatus,
  toProgressPercent,
  type ImportLifecycleProgress,
} from "@synap-core/import-core";

const progress: ImportLifecycleProgress = {
  status: "writing",
  totalItems: 100,
  processedItems: 64,
  createdItems: 50,
  updatedItems: 10,
  skippedItems: 2,
  failedItems: 2,
};

const label = getStatusLabel(progress.status); // "Writing"
const percent = toProgressPercent(progress); // 64
const done = isTerminalStatus(progress.status); // false

Do / Do Not

Do:

  • Use readFileAsBase64 instead of re-implementing FileReader conversion in each surface.
  • Use getStatusLabel for consistent lifecycle wording in UI.
  • Use preview parsers for fast user feedback before submit.
  • Keep backend parsing/orchestration as source of truth for actual import behavior.
  • Add new shared helpers here only when used by more than one import surface.

Do not:

  • Do not duplicate parser logic in every app package when an equivalent helper already exists here.
  • Do not move backend ingestion logic into this package.
  • Do not treat preview parser output as authoritative ingestion output.
  • Do not add tRPC client wrappers unless there is a clear cross-surface need and ownership agreement.
  • Do not add UI components/state machines to import-core; keep this package utility-focused.

Long-term Usage Guidance

When adding a new import surface:

  1. Start with import-core utilities for file handling and preview.
  2. Keep surface-specific UX flow local to that app package.
  3. Send canonical payloads to backend import router/orchestrator.
  4. Reuse lifecycle status vocabulary for user-visible job states.

When adding a new shared primitive:

  1. Verify at least two surfaces need it.
  2. Keep API browser-safe and dependency-light.
  3. Document behavior and constraints in this README.
  4. Avoid overlap with backend parser/orchestrator contracts.