DcuplAppLoader API

The DcuplAppLoader handles fetching and processing configuration and data from dcupl Console or custom sources.

Installation

npm install @dcupl/loader

Basic Usage

import { Dcupl } from '@dcupl/core';
import { DcuplAppLoader } from '@dcupl/loader';

const dcupl = new Dcupl();
const loader = new DcuplAppLoader();

// Fetch configuration from dcupl Console
await loader.config.fetch({
  projectId: 'my-project',
  apiKey: 'my-api-key',
});

// Register loader with dcupl
dcupl.loaders.add(loader, 'main');

// Initialize (processes all loaders)
await dcupl.init();

config

Manage loader configuration.

Methods

Method Signature Description
get () => ProjectConfiguration | undefined Get current configuration
set (config: ProjectConfiguration) => void Set configuration manually
fetch (options?: FetchOptions) => Promise Fetch configuration from remote

FetchOptions

type FetchOptions = {
  projectId?: string;      // dcupl Console project ID
  apiKey?: string;         // dcupl API key
  baseUrl?: string;        // Custom base URL (default: dcupl CDN)
  loaderFileName?: string; // Custom config filename
  headers?: HeadersInit;   // Custom headers for fetch
  skipApply?: boolean;     // Return config without applying
};

Examples

// Fetch from dcupl Console
await loader.config.fetch({
  projectId: 'my-project',
  apiKey: 'my-api-key',
});

// Fetch from custom URL
await loader.config.fetch({
  baseUrl: 'https://my-cdn.example.com/config',
  loaderFileName: 'dcupl.loader.json',
});

// Get current config
const config = loader.config.get();
console.log(config?.resources.length);

// Set config manually
loader.config.set({
  resources: [
    { key: 'products', type: 'data', url: '/data/products.json', model: 'Product' },
  ],
  models: [
    { key: 'Product', properties: [{ key: 'name', type: 'string' }] },
  ],
});

resources

Manage data resources (models, data files, transformers, operators, scripts).

Methods

Method Signature Description
get (key: string) => Resource | undefined Get resource by key
getAll () => Resource[] Get all resources
getWithTag (tag: string) => Resource[] Get resources with specific tag
set (resources: Resource[]) => void Replace all resources
add (resource: Resource) => void Add a resource
delete (key: string) => void Delete a resource by key

Resource Types

type Resource = {
  key: string;
  type: 'model' | 'data' | 'transformer' | 'operator' | 'script';
  url: string;
  model?: string;        // For 'data' type: target model
  tags?: string[];       // For filtering resources
  updateType?: 'set' | 'update' | 'upsert' | 'remove';
  csvParserOptions?: {
    delimiter?: string;
    quoteChar?: string;
    escapeChar?: string;
    commentChar?: string;
  };
};

Examples

// Get all resources
const resources = loader.resources.getAll();

// Get resources by tag
const criticalResources = loader.resources.getWithTag('critical');

// Add a new data resource
loader.resources.add({
  key: 'products-update',
  type: 'data',
  url: '/api/products/latest',
  model: 'Product',
  updateType: 'upsert',
  tags: ['live-data'],
});

// Delete a resource
loader.resources.delete('products-update');

variables

Access configuration variables with environment support.

Methods

Method Signature Description
get (key: string, env?: string) => Variable | undefined Get variable by key
getAll (env?: string) => Variable[] Get all variables
getAllAsObject (env?: string) => Record Get variables as key-value object

Nested Variable APIs

// Default variables (from config defaults)
loader.variables.default.get(key);
loader.variables.default.getAll();

// Environment variables (from config environments)
loader.variables.environment.get(env, key);
loader.variables.environment.getAll(env);

// Process variables (passed during processing)
loader.variables.process.get(key);
loader.variables.process.getAll();

// Global variables (set at runtime)
loader.variables.global.get(key);
loader.variables.global.getAll();
loader.variables.global.set(key, value);
loader.variables.global.delete(key);

Examples

// Get API URL variable
const apiUrl = loader.variables.get('API_URL');
console.log(apiUrl?.value);

// Get all variables for production environment
const prodVars = loader.variables.getAll('production');

// Get variables as object for easy access
const vars = loader.variables.getAllAsObject('production');
console.log(vars.API_URL);

// Set global variable at runtime
loader.variables.global.set('AUTH_TOKEN', 'Bearer xyz');

// Use in resource URLs with interpolation
// URL: "{{API_URL}}/products" becomes "https://api.example.com/products"

transformers

Register and manage data transformers.

Methods

Method Signature Description
get (options: { applyTo: string[] }) => Transformer[] Get transformers by target
getAll () => Transformer[] Get all transformers
set (transformer: Transformer) => void Register a transformer
remove (key: string) => void Remove a transformer

Transformer Structure

type Transformer = {
  key: string;
  applyTo: string[];  // Resource keys to apply to
  fn: (data: any[], context: TransformerContext) => any[];
};

Examples

// Register a transformer to normalize data
loader.transformers.set({
  key: 'normalize-prices',
  applyTo: ['products-csv'],
  fn: (data, context) => {
    return data.map(item => ({
      ...item,
      price: parseFloat(item.price) || 0,
      currency: item.currency || 'USD',
    }));
  },
});

// Get transformers for a resource
const productTransformers = loader.transformers.get({
  applyTo: ['products-csv'],
});

// Remove a transformer
loader.transformers.remove('normalize-prices');

Processing

Process the loaded configuration and apply to dcupl.

process()

await loader.process(options?: ProcessOptions);

ProcessOptions

type ProcessOptions = {
  applicationKey?: string;  // Select specific application preset
  environmentKeys?: string[]; // Select environment configurations
  resourceTags?: string[];  // Filter resources by tags
  variables?: Record<string, any>; // Override variables
  options?: {
    defaultDataContainerUpdateType?: 'set' | 'update' | 'upsert';
    csvParserOptions?: CSVParserOptions;
  };
};

Examples

// Process with specific environment
await loader.process({
  environmentKeys: ['production'],
});

// Process only tagged resources
await loader.process({
  resourceTags: ['critical', 'models'],
});

// Process with variable overrides
await loader.process({
  variables: {
    API_URL: 'https://staging-api.example.com',
  },
});

Progress Tracking

Subscribe to loader progress events.

on()

const unsubscribe = loader.on((progress: ProgressEvent) => {
  console.log(`${progress.loaded}/${progress.total} resources loaded`);
});

// Later: unsubscribe
unsubscribe();

Progress Event

type ProgressEvent = {
  type: 'resource_loading' | 'resource_loaded' | 'resource_error';
  resourceKey: string;
  loaded: number;
  total: number;
  error?: Error;
};

Complete Example

import { Dcupl } from '@dcupl/core';
import { DcuplAppLoader } from '@dcupl/loader';

async function initializeDcupl() {
  const dcupl = new Dcupl();
  const loader = new DcuplAppLoader();

  // Track progress
  loader.on((progress) => {
    console.log(`Loading: ${progress.resourceKey} (${progress.loaded}/${progress.total})`);
  });

  // Fetch configuration
  await loader.config.fetch({
    projectId: process.env.DCUPL_PROJECT_ID,
    apiKey: process.env.DCUPL_API_KEY,
  });

  // Add custom transformer
  loader.transformers.set({
    key: 'add-computed-fields',
    applyTo: ['products'],
    fn: (data) => data.map(item => ({
      ...item,
      displayName: `${item.brand} ${item.name}`,
    })),
  });

  // Set runtime variables
  loader.variables.global.set('USER_LOCALE', navigator.language);

  // Process with production config
  await loader.process({
    environmentKeys: ['production'],
  });

  // Register with dcupl
  dcupl.loaders.add(loader, 'main');

  // Initialize
  await dcupl.init();

  return dcupl;
}