---
title: "Libraries"
description: "Sharing code between Indicators is publishing a package: the unit of reuse on the registry is a whole Indicator, addressed as @scope/name, that anyone installs…"
order: 46
section: "functions"
---

<!-- source: docs/indicators/functions/libraries.md; generated by packages/cli/scripts/gen-indicator-docs.ts, do not edit -->

# Libraries

Sharing code between Indicators is publishing a package: the unit of
reuse on the registry is a whole Indicator, addressed as `@scope/name`,
that anyone installs, mounts on a chart, or feeds into another package
as a metric composition input. Inside one workspace, shared code is an
ordinary AssemblyScript module beside `src/indicator.ts`, imported by
relative path. kScript (legacy) had a third thing, a `library()` header
imported as `@owner/name` and frozen into every consumer at save; that
cross-package source import is not in Indicators yet, and this page is
what the two existing forms give you instead. The registry side (dry
run, publish, versions, visibility, deleting) is
[Publishing](publishing.md).

## A shared module in the workspace

Put functions, classes, and constants in a second file under `src/` and
import it by path. The build compiles every file `src/indicator.ts`
reaches; the only rule is that declarations (`param`, `input`, `output`,
and the rest) live in `src/indicator.ts`, because the sheet is derived
from the entry file alone (a declaration elsewhere is the named build
error `code-first declarations must live in src/indicator.ts`).

The kScript `library("norm")` example as `src/lib/norm.ts`:

```typescript
// A shared helper module: plain AssemblyScript functions and a class, no declarations.
export function zscore(sample: f64, mean: f64, dev: f64): f64 {
  if (dev == 0.0) return 0.0;
  return (sample - mean) / dev;
}

// +1 stretched up, -1 stretched down, 0 neutral.
export function regime(z: f64, hi: f64, lo: f64): f64 {
  if (z > hi) return 1.0;
  if (z < lo) return -1.0;
  return 0.0;
}

export class Band {
  top: f64;
  bottom: f64;

  constructor(top: f64, bottom: f64) {
    this.top = top;
    this.bottom = bottom;
  }

  contains(price: f64): bool {
    return price <= this.top && price >= this.bottom;
  }
}
```

And the consumer, `src/indicator.ts`, which owns the data, the TA
objects, and the declarations, and imports the shared logic beside the
shipped `./sdk/ta`:

```typescript
import { input, line, lower, ohlcv, output, param } from "./sdk/declare";
import { in_close } from "./gen/inputs";
import { emitRow, out_regime, out_z } from "./gen/outputs";
import { p_period } from "./gen/params";
import { Sma, Stdev } from "./sdk/ta";
import { Band, regime, zscore } from "./lib/norm";

param("period", 20, { min: 2, max: 400 });
input("close", ohlcv.close);
output("z", line, lower, { color: "#4f8cff", description: "Z-score of the close" });
output("regime", line, lower, { color: "#f59e0b", description: "+1 stretched up, -1 down, 0 inside the band" });

let sma = new Sma(20);
let stdev = new Stdev(20);
let z: f64 = NaN;
let band = new Band(0.0, 0.0);

export function init(): void {
  sma = new Sma(i32(p_period()));
  stdev = new Stdev(i32(p_period()));
}

export function state(): i32 {
  const close = in_close();
  const mean = sma.update(close);
  const dev = stdev.update(close);
  z = zscore(close, mean, dev);
  band = new Band(mean + dev, mean - dev);
  return isNaN(mean) || isNaN(dev) ? 0 : 1;
}

export function finalize(): void {
  out_z(z);
  out_regime(band.contains(z) ? 0.0 : regime(z, 2.0, -2.0));
  emitRow();
}

export function reset(): void {
  sma.reset();
  stdev.reset();
  z = NaN;
}
```

Both files build with `om indicator build`, and the exported package
carries the compiled module and the sheet, not the source: the helper is
part of your workspace, kept beside `src/indicator.ts`. Types travel too:
`Band` is a class the consumer constructs, the port of a kScript shared
`type`. What the kScript placement rules were for (aliased calls inside a
`timeseries` initializer) has no counterpart, because there are no series
expressions: a function is a function everywhere.

The `new Band(...)` on every bar allocates, and the module's runtime
never frees; keep one `Band` and update its fields when the history is
long ([Collections](../core-concepts/collections.md)).

Two shared-module rules the compiler enforces for you: an import that
collides with a local name is a compile error (no silent precedence), and
a shared function calling its sibling always gets its own sibling. Errors
inside the helper carry the helper's own file, line, and column.

## What a library was for, as a package

The rest of what a kScript library offered maps onto packages:

| kScript library | Indicator package |
| --- | --- |
| `import "@you/norm"` | `om install @you/norm`, then mount it or read its metric |
| `import "@you/norm@1.0.0"` pin | installs pin the exact version and hash; alerts lock to what they were armed on |
| a shared `type` | a class in a workspace module; across packages, values travel as metrics |
| a consumer reusing a computed series | a `metric` composition input: `{ "source": "metric", "metric": "wrun/@you/norm/z" }` in a hand-written sheet feeds one package's output into another ([Script definition](script-definition.md)) |
| draft resolves live in the editor | `om indicator install --replace` and the editor's Run rebuild from the current source |
| publish an immutable version | `om publish`; versions are permanent ([Publishing](publishing.md)) |

**Not in Indicators yet.** `import "@scope/lib"` from another package's
source at build time. Cross-package reuse is a metric today (the
consumer reads the published package's computed output) or a copy of the
module into `src/lib`; package-to-package source imports are product
work.

```bash
om install @you/norm
om indicator install ./my-indicator --replace
```
