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

@spinekit/bd-tax

v0.2.0

Published

Arc module for Bangladesh statutory tax filing — Mushak (VAT) invoices + VDS/TDS withholding certificates as composable arc resources. Wraps the pure @classytic/bd-tax form/calc pack; owns the stateful filing models; taxable events flow in through a juris

Readme

@spinekit/bd-tax

Bangladesh statutory tax filing — Mushak 6.3 VAT invoices + VDS/TDS withholding certificates.

Part of Spine, Classytic's ERP — the arc-module layer that composes the framework-agnostic @classytic/bd-tax kernel into an @classytic/arc application via createApp({ modules }).

Install

npm install @spinekit/bd-tax @classytic/bd-tax @classytic/arc @classytic/mongokit \
  @classytic/primitives mongoose zod

Kernels and the framework are peer dependencies — the host installs a single copy; this module bundles nothing.

Usage

import { createApp } from '@classytic/arc/factory';
import { createBdTaxModule } from '@spinekit/bd-tax';

const app = await createApp({
  modules: [
    createBdTaxModule({
      connection,          // your mongoose connection
      permissions,         // { view, manage, ... } role gates
      // BYO engine + host seams (extraActions, extraRoutes, bridges) as needed
    }),
  ],
});

The module's bootstrap return is recorded at fastify.arc.modules['bd-tax'] for container-free cross-module wiring.

The one rule: the host projects, the module decides

A host resolves its own order into a buyer and raw items — and stops. Fiscal position (which SRO relief applies), rate-code remapping, VAT calculation, serial allocation, the filing window and form selection are Bangladesh.

// HOST: projection. No tax math.
const salesBridge: SalesSourceBridge = async (orderId) => {
  const order = await orders.get(orderId);
  return {
    buyer: {
      name: order.customer.name,
      countryCode: order.shippingAddress.country,  // a FACT, not "INTERNATIONAL"
    },
    items: order.lines.map((l) => ({
      description: l.name, quantity: l.qty, unitPrice: l.unitPriceMinor,
    })),
    sourceModel: 'Order',
    sourceId: orderId,
  };
};

The module then resolves the fiscal position, remaps rate codes, prices every line, builds the Mushak 6.3, allocates the serial and persists it — idempotently on (sourceModel, sourceId), enforced by a unique index rather than a read-then-write check.

POST /generate does the same from raw items directly, under the issue permission (distinct from file: BD VAT Act §51 makes the legal invoice accompany the goods, so a cashier issues one; nobody wants a cashier filing the monthly return).

If you find yourself calling calculateInvoiceTax or resolveFiscalPosition in your host, stop — that is the module's job, and a second copy of it will drift silently. The one legitimate override is bridges.fiscalPosition, for a deployment that merges its own tax classes into a tax-core TaxResolver.

The monthly return — and why you should not re-host it

GET /accounting/musok/return/:period serves the full statutory return: regime → form (STANDARD_VAT… → Mushak 9.1, SME_TOT → 9.2, COTTAGE_EXEMPT → nothing to file), output VAT bucketed per VAT rate from issued Mushak 6.3 invoices, input VAT credited from your ledger.

Two things about that return are yours, not Bangladesh's, and they are the only two you wire:

createBdTaxModule({
  connection,
  permissions,
  resolveSeller,                       // issuing party (BIN + legal identity)
  bridges: {
    sales:    orderToTaxableSale,      // order id → neutral taxable sale
    regime:   (ctx) => branchRegimeOf(ctx.organizationId),   // → 9.1 / 9.2 / none
    inputVat: (period, ctx) => ledgerInputVat(period, ctx),  // credit + VDS
  },
});

Both bridges are optional and default safely: no regime bridge means STANDARD_VAT (file a 9.1 — filing an unneeded return is recoverable, silently not filing one is not), and no inputVat bridge means zero credit, so the return still builds before you have a ledger.

Filing periods are resolved in Asia/Dhaka, not UTC (override with filingTimeZone). This matters: Bangladesh is UTC+6, so a UTC month disagrees with the filing month at both ends by six hours, reporting the start of one month as the end of the previous one. Nothing throws when this is wrong.

musokOmitBuiltinReads: true exists for a host that genuinely needs a different statutory shape. Do not reach for it merely to enrich the return — that creates a second implementation of an NBR rule, and the copies drift silently. Wire the bridges instead, or extend the module.

Cloning this for another country

@classytic/ca-tax and @classytic/us-tax already implement the same @classytic/tax-core TaxResolver contract that @classytic/bd-tax does, so a sibling spine module is a composition job, not a rewrite. What you keep and what you replace:

| concern | reuse as-is | why | |---|---|---| | period boundaries, serial year, YYYY-MM parsing | @spinekit/kit/period | zone is a parameter; already country-neutral | | fiscal position | bridges.fiscalPosition port | typed as tax-core's signature, so your pack's resolver drops straight in | | per-line tax computation | your pack (calculateInvoiceTax equivalent), or tax-core's resolver.computeLine | the only genuinely country-specific math | | ports (SellerResolver, sales/regime/input-tax bridges) | shared/ports.ts shapes | nothing in them is BD | | serial allocation | allocateMushakSerial shape | atomic per-(scope, year) counter; rename, don't re-derive | | idempotent issuance | issueMushak shape | unique index + duplicate-key read-back; see the CHANGELOG for why the naive version double-issues | | per-rate month aggregate | MushakRepository.aggregateMonthlyVat shape | groups on lines.vatRate; no BD assumption | | the MODEL, the FORMS, regime→form dispatch | rewrite | Mushak 6.3/9.1/9.2 are Bangladesh; GST/HST returns are not |

src/pack.ts is the complete list of jurisdiction literals — country code, filing timezone, currency. Nothing else in the module hardcodes any of them, so that file plus the model and forms is your diff. Amounts are minor units and the module only ever sums them, so a currency with a different exponent needs no code change.

License

SEE LICENSE IN LICENSE — Classytic Source-Available License (Community & Commercial). Evaluation and development use are free; production use requires a commercial license. Versions before 1.0.0 remain MIT.