Guides

Limits and errors

Restrict what users can do per plan, react to refusals, and handle KymoscopeError codes.

Setting limits

TypeScript
const engine = createEngine({
  limits: {
    maxChartsPerWorksheet: 4,
    maxChannelsPerChart: 4,
    maxWorksheets: 4,
    maxMathChannels: 4,
    canImportExportWorkspaces: false,
  },
})

// When the user's plan loads or changes:
engine.setOptions({ limits: planLimits })

null means unlimited. setOptions({ limits }) merges into the current limits: keys you leave out keep their value.

When a limit is hit

The engine refuses the action, emits limit and throws KymoscopeError('limit_reached') with detail.reason.

ReasonTriggered by
charts-per-worksheetcharts.add or io.importChart on a full worksheet.
channels-per-chartcharts.addSeries on a full multi-band chart.
worksheetsworksheets.add or io.importWorksheet at the limit.
workspaces-ioAny io export or import when canImportExportWorkspaces is false.
math-channelsmathChannels.create, importMany or importFile with no room left.
TypeScript
engine.on('limit', ({ reason }) => openUpgradeDialog(reason))

try {
  engine.charts.add('histogram')
} catch (e) {
  if (e instanceof KymoscopeError && e.code === 'limit_reached') return   // the dialog is already open
  throw e
}

After a downgrade

Lowering limits never deletes anything. state.mathChannels only lists the channels within the limit, and the active worksheet is moved into the allowed range. Worksheets and charts beyond the limit stay in state.worksheets: slice them in your UI (the first maxWorksheets worksheets and the first maxChartsPerWorksheet charts), and they come back when the limit rises.

Error codes

Every failure is a KymoscopeError with a code and, where useful, a detail object. The full list is in Errors.

TypeScript
import { KymoscopeError } from '@kymoscope/sdk'   // Kymoscope.KymoscopeError with the script tag

function run(fn: () => unknown) {
  Promise.resolve().then(fn).catch((e) => {
    if (e instanceof KymoscopeError && (e.code === 'limit_reached' || e.code === 'import_vetoed')) return
    showError(e instanceof KymoscopeError ? e.code : String(e))
  })
}