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

@atscale-ps/ps-utils

v4.0.0

Published

AtScale PS utilities — CLI and library for SML generation, deployment, and analysis

Readme

AtScale PS Template CLI

CLI tool for extracting AtScale models, generating SML semantic models, and generating BI workbooks (Tableau, Excel, Power BI).

Upcoming features:

  • Google Sheets
  • Perspectives
  • -- apply plan should show command
  • graphql output not going to output
  • web interface better + REST
  • tableau, mstr conversion
    • find Hive dialect in a workbook
  • Apply style to SML
  • Add kubectl management commands; for example reading log files, updating passwords
  • Calculation groups as shared objects

Model Extraction

Connect to a live AtScale instance or read local SML files to produce a portable model.yaml capturing all metrics and dimension hierarchies.

flowchart LR
    ATS["AtScale Instance"] --> A["extract-model-from-atscale"] --> MODEL["model.yaml"]
    SML["SML Files"] --> B["extract-model-from-sml"] --> MODEL

SML Creation and Manipulation

Generate a complete AtScale SML semantic model from a live database connection or a DDL file, extract existing schema DDL for inspection, or run ad-hoc SQL against any registered connection, or run the analysis-suggestions engine against an extracted model to surface the highest-value metric combinations.

flowchart LR
    DB[("Database")] --> A["extract-ddl-from-connection"] --> DDL["DDL (.sql)"]
    ATS["AtScale Instance"] --> B["generate-ddl-from-atscale"] --> DDL
    DDL --> C["generate-sml-from-ddl"] --> SML["SML Files"]
    DB --> D["generate-sml-from-connection"] --> SML
    XML["AtScale XML"] --> G["generate-sml-from-xml"] --> SML
    BUNDLE["Support bundles"] --> GB["generate-sml-from-bundle"] --> SMLB["SML repos + summary"]
    TMSL["TMSL/XMLA Export"] --> N["generate-sml-from-tabular"] --> SML
    SSASMD["SSAS Multidimensional XMLA"] --> O["generate-sml-from-ssas-multidimensional"] --> SML
    XML --> L["generate-report-from-xml"] --> RPT["Report (.md)"]
    SML --> M["generate-report-from-sml"] --> RPT
    SML2A["SML Dir A"] --> H["generate-shared-model-plan"] --> PLAN["RECOMMENDATION.md + option-N.yml"]
    SML2B["SML Dir B"] --> H
    PLAN --> I["apply-shared-model-plan-option"] --> SHARED["shared/dimensions, datasets, models"]
    DB --> E["execute-sql-on-connection"] --> OUT["Results (stdout)"]
    MODEL["model.yaml"] --> F["generate-metrics-from-model"] --> METRICS["metrics/*.yml"]
    SML --> J["apply-style-to-sml"] --> SML
    SML --> K["generate-sml-docs"] --> DOCS["README.md (docs)"]
    SML --> CL["clean-unused-sml-objects"] --> SML
    PBIX["Power BI .pbix"] --> P["analyze-powerbi-dax-gaps"] --> GAP["Gap report (.md, .json, .csv)"]
    DB --> Q["echo-connection-metadata"] --> META["Schema metadata (JSON, stdout)"]

Synthetic Data Generation

Profile an existing database's schema and value distributions, then generate matching synthetic DDL and CSV data — either written to local files or loaded directly into a target database.

flowchart LR
    DB[("Source Database")] --> A["extract-data-shape-from-connection"] --> SHAPE["data-shape.json"]
    SHAPE --> B["generate-ddl-from-data-shape"] --> DDL["DDL (.sql)"]
    SHAPE --> C["generate-data-from-data-shape"] --> CSV["Synthetic CSVs"]
    SHAPE --> D["generate-data-from-data-shape-to-connection"] --> TARGET[("Target Database")]

Visualization and Namespace Processing

Generate a namespace definition from a model, then produce ready-to-open Tableau, Excel, and Power BI workbooks with optional field-label aliases.

flowchart LR
    MODEL["model.yaml"] --> A["generate-namespace-from-model"] --> NS["namespace.yaml"]
    NS --> B["generate-tableau-from-namespace"] --> TWB["tableau.twb"]
    NS --> C["generate-excel-from-namespace"] --> XLSX["workbook.xlsx"]
    NS --> D["generate-powerbi-from-namespace"] --> PBI["output/powerbi/"]
    CONN --> E["generate-notebook-from-connection"] --> NB["notebook.ipynb"]
    CONN["connections.yaml"] --> B & C & D
    ALIASES["aliases.yaml (opt.)"] -.-> B & C & D

Testing / Query Processing

Capture queries from AtScale's Postgres backend, replay them through a load harness, enrich results with execution metadata, and compare two runs side-by-side to detect regressions in row counts, duration, or error behavior.

flowchart TD
    SML["SML Files"] --> G["generate-queries-from-sml"] --> QJSON["queries/*.json"]
    MODEL["model.yaml"] --> H["generate-queries-from-model"] --> QJSON
    ATSDB[("AtScale Postgres")] --> A["extract-query-stats-from-atscale"] --> STATS["occurrences.csv"]
    ATSDB --> B["extract-queries-from-atscale"] --> QJSON
    QJSON --> C["execute-atscale-query-harness"] --> RUN["run_results/*.csv"]
    ATS["AtScale Instance"] --> C
    CONN["connections.yaml"] --> D["execute-query-on-connection"] --> QOUT["Query Output"]
    RUN --> E["generate-enhanced-query-results"] --> ECSV["*_enhanced.csv"]
    ATSDB --> E
    RUN --> F["execute-run-analysis"]
    ECSV --> F
    F --> SUMMARY["summary.txt"]
    F --> COMPARISON["comparison.csv"]
    F --> OUTLIERS["outliers.csv"]

AtScale Config

Bootstrap and manage an AtScale instance — generate Helm install values, register data sources and SML repositories, deploy catalogs, and inspect live configuration state.

flowchart LR
    HOSTNAME["Hostname"] --> A["generate-atscale-install-yaml"] --> VALUES["values.yaml (Helm)"]
    CONN["connections.yaml"] --> B["atscale-create-data-source"] --> ATS["AtScale Instance"]
    CONN --> C["atscale-create-repo"] --> ATS
    CONN --> D["atscale-deploy-catalog"] --> ATS
    ATS --> E["atscale-list-data-sources"] --> INFO["AtScale Info (stdout)"]
    ATS --> F["atscale-list-repos"] --> INFO
    ATS --> G["atscale-list-deployments"] --> INFO
    ATS --> H["atscale-list-model-errors"] --> INFO
    ATS --> I["get-dso-count"] --> INFO

Aggregate Management

List, analyze, rebuild, and inspect the build history of AtScale aggregates for a given catalog/model, and export/import aggregate definitions to promote them between environments (e.g. dev → prod).

Across every operation in this group, --catalog-id/--model-id are optional: when either is omitted, the deployed catalogs/models are listed and — in an interactive terminal — you're prompted to pick one; in a non-interactive session (CI, scripts), an error is raised listing the available --catalog-id/--model-id pairs to choose from.

flowchart LR
    CONN["connections.yaml"] --> A["atscale-list-aggregates"] --> INFO["Aggregates + Summary + Health (stdout / CSV)"]
    CONN --> B["atscale-rebuild-aggregates"] --> ATS["AtScale Instance"]
    CONN --> C["atscale-list-aggregate-build-history"] --> HIST["Build History + Summary (stdout)"]
    CONN --> D["atscale-export-aggregates"] --> EXP["Export JSON File"]
    EXP -->|hand-edit for target model| IMPFILE["Export JSON File (edited)"]
    CONN --> E["atscale-import-aggregates"]
    IMPFILE --> E
    E --> ATS2["AtScale Instance (target)"]

Web Services

Expose every operation as a GraphQL mutation and REST endpoint via an embedded HTTP server.

flowchart LR
    CLIENT["HTTP Client"] --> A["execute-web-services"] --> OPS["All Operations (GraphQL / REST)"]

Utilities

flowchart LR
    A["version"] --> VER["@atscale-ps/[email protected] (stdout)"]

Table of Contents


Setup

Prerequisites:

  • Node.js 18+
  • macOS only: Xcode Command Line Tools (required for native module compilation):
    xcode-select --install
    brew install npm 
    brew install tsc

Install globally from npm:

sudo npm install -g @atscale-ps/ps-utils

Build from source:

npm install
npm run build

Reference Documentation

The docs/ directory contains extended reference material:

| File | Description | |------|-------------| | docs/reference/ACTIONS.md | GitHub Actions guide — run any operation as a composite workflow step | | docs/reference/NODE.md | Node.js library API reference — typed async functions for every operation | | docs/reference/GRAPHQL.md | GraphQL API reference for the web services server (auto-generated) | | docs/reference/REST.md | REST API reference for the web services server (auto-generated) | | docs/reference/DEVELOPER.md | Developer guide — CLI framework architecture and how to add new operations | | docs/reference/README.md | API reference index — GitHub Actions, Node.js, GraphQL, REST, and developer guide | | docs/workflows/CONVERSION.md | Algorithm documentation for converting AtScale XML projects to SML | | docs/workflows/README.md | Professional Services delivery workflows for existing-model conversion and net-new SML development | | docs/system/STATISTICS.md | Statistical fingerprint algorithm used for synthetic data generation | | docs/system/VERTICALS.md | Pre-built DDL schemas and SML models for 15 industry verticals | | docs/system/README.md | System reference index — architecture, naming style guide, synthetic-data statistics, and industry verticals | | vscode-extension/README.md | VS Code extension — run operations from the Explorer context menu, plus SML schema validation and highlighting (install & usage) | | resources/sml-reference/UPSTREAM.md | Vendored SML language specification — source, pinned revision, and how to refresh it |


Operations

Model Extraction

extract-model-from-atscale

↑ Table of Contents

Connects to a live AtScale instance via MDX and extracts a model's metrics and attributes into a model.yaml file. This file is the input for generate-tableau-from-namespace.

./atscale-utils extract-model-from-atscale \
  --model "Telemetry" \
  --connection-file "./connections.yaml" \
  --connection-name "ats_connection" \
  --output-model-file "./model.yaml"

| Parameter | Required | Default | Description | |---|---|---|---| | --model | Yes | | AtScale model/cube name | | --connection-file | Yes | | Path to connections file | | --connection-name | Yes | | Connection name in the file | | --output-model-file | No | stdout | Output path for the model YAML |

GitHub Actions workflow: See Extract AtScale Model Workflow.


extract-model-from-sml

↑ Table of Contents

Reads a local SML directory (produced by generate-sml-from-connection or generate-sml-from-ddl) and outputs a model.yaml file in the same format as extract-model-from-atscale. Use this to generate a Tableau workbook without a live AtScale connection.

./atscale-utils extract-model-from-sml \
  --sml-dir "./sml-output" \
  --output-model-file "./model.yaml"

With optional overrides:

./atscale-utils extract-model-from-sml \
  --sml-dir "./sml-output" \
  --model-name "SalesModel" \
  --connection-name "snow_demo" \
  --output-model-file "./model.yaml"

| Parameter | Required | Default | Description | |---|---|---|---| | --sml-dir | Yes | | Path to the SML directory | | --model-name | No | First model found | Model label or unique_name to extract | | --connection-name | No | From connections file | Override the data_source in the output | | --output-model-file | No | stdout | Output path for the model YAML |


SML Creation and Manipulation

execute-sql-on-connection

↑ Table of Contents

Reads a SQL file, splits it into individual statements, and executes each one against a named database connection. Works with DDL (CREATE TABLE, DROP TABLE, ALTER TABLE, CREATE VIEW), DML (INSERT, UPDATE, DELETE), and mixed files.

./atscale-utils execute-sql-on-connection \
  --sql-file "./schema/migrations/001_init.sql" \
  --connection-file "./connections.yaml" \
  --connection-name "snow_demo"

Preview statements without running them:

./atscale-utils execute-sql-on-connection \
  --sql-file "./schema.sql" \
  --connection-name "snow_demo" \
  --dry-run true

Skip failed statements and continue:

./atscale-utils execute-sql-on-connection \
  --sql-file "./schema.sql" \
  --connection-name "snow_demo" \
  --on-error continue

| Parameter | Required | Default | Description | |---|---|---|---| | --sql-file | Yes | | Path to the SQL file to execute | | --connection-file | No | connections.yaml | Path to connections file | | --connection-name | Yes | | Connection name in the file | | --on-error | No | stop | stop halts on first failure; continue logs errors and proceeds | | --dry-run | No | | Pass true to print statements without executing them |


extract-ddl-from-connection

↑ Table of Contents

Connects to a live database, reads schema metadata for each table in the target schema, and writes CREATE TABLE DDL statements to a file (or stdout). Useful for capturing schema snapshots, seeding DDL files for generate-sml-from-ddl, or comparing schema drift.

./atscale-utils extract-ddl-from-connection \
  --connection-file "./connections.yaml" \
  --connection-name "snow_demo" \
  --schema "PUBLIC" \
  --output-file "./schema.ddl"

Extract only specific tables or wildcard patterns:

./atscale-utils extract-ddl-from-connection \
  --connection-file "./connections.yaml" \
  --connection-name "snow_demo" \
  --schema "PUBLIC" \
  --tables "Dim*,FactInternetSales" \
  --output-file "./schema.ddl"

| Parameter | Required | Default | Description | |---|---|---|---| | --connection-name | Yes | | Connection name in the file | | --schema | Yes | | Database schema to introspect | | --connection-file | No | connections.yaml | Path to connections file | | --tables | No | All tables | Comma-separated table names or wildcard patterns (* = any chars, ? = one char). Matching is case-sensitive by default. | | --case-insensitive | No | false | Match table names case-insensitively | | --output-file | No | stdout | Output path for the DDL |


generate-sml-from-connection

↑ Table of Contents

Connects to a live database, introspects its schema, runs semantic model inference, and writes a complete set of AtScale SML files to a directory.

Inference engine capabilities:

  • Composite keys — tables with multi-column primary keys produce key_columns arrays in SML level attributes.
  • FK-based classification — fact vs. dimension classification uses foreign key graph topology. Tables that only receive FKs (high in-degree) are classified as dimensions. Tables with FK references to multiple tables plus numeric payload columns are classified as facts.
  • Bridge / cross-reference tables — junction tables with FKs to ≥2 tables and ≤1 payload column are automatically classified as shared dimensions (the AtScale pattern for bridge tables). A [BRIDGE TABLE] advisory is emitted in the model warnings.
  • Naming convention patterns — prefix/suffix patterns take priority over structural heuristics: dim_* / *_dim, fct_* / *_fct / fact_* / *_fact, lkp_* / ref_* / lookup_*, bridge_* / xref_* / junction_* / map_*, etc.
  • information_schema FK queries — foreign key metadata is read from INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS and KEY_COLUMN_USAGE for accurate composite-key support. Falls back to the driver-level API when information_schema is not accessible (e.g. Snowflake).
  • One relationship per hierarchy — when a dimension has multiple hierarchies, one model relationship is emitted per hierarchy leaf level so all hierarchies are visible in BI tools.
  • Distinct count estimate — measures inferred as distinct-countable entities use distinct count estimate aggregation (preferred over distinct count for AtScale aggregation engine compatibility).
./atscale-utils generate-sml-from-connection \
  --connection-file "./connections.yaml" \
  --connection-name "ats_connection" \
  --model-name "Telemetry" \
  --output-dir "./sml-output"

With optional overrides:

./atscale-utils generate-sml-from-connection \
  --connection-file "./connections.yaml" \
  --connection-name "snow_demo" \
  --model-name "SalesModel" \
  --output-dir "./sml-output" \
  --schema "SALES" \
  --catalog-name "Sales Analytics" \
  --pii-severity "HIGH" \
  --sample-size 500 \
  --fact-tables "FactInternetSales,FactResellerSales" \
  --camel-case-files true \
  --camel-case-measures true

Style parameters (--pii-severity, --model-mode, --fact-tables, --catalog-name, --camel-case-files, --camel-case-measures, --label-style, --sample-size, --min-hierarchies-per-dim, --max-hierarchies-per-dim) can also be set in an SML style config file. CLI flags take priority over the file. After generation, effective settings are always written to <output-dir>/sml.style.yaml regardless of the input config path.

| Parameter | Required | Default | Description | |---|---|---|---| | --connection-file | No | connections.yaml | Path to connections file | | --connection-name | Yes | | Connection name in the file | | --model-name | Yes | | Name for the generated semantic model | | --output-dir | Yes | | Directory to write SML files | | --sml-config-file | No | sml.style.yaml | Path to the SML style config to read settings from | | --schema | No | From connection config | Override the database schema to introspect | | --catalog-name | No | model-name | Display name for the generated catalog | | --pii-severity | No | MEDIUM | Minimum PII severity to exclude: HIGH, MEDIUM, LOW, or none | | --model-mode | No | Collision-time decision | new permits deterministic renaming of all colliding query names; existing preserves established names and reports a blocking compatibility conflict | | --sample-size | No | 250 | Rows to sample per table for type inference (0 to disable) | | --fact-tables | No | Auto-detected | Comma-separated table names to treat as facts, overriding automatic classification | | --camel-case-files | No | false | When true, dataset and dimension filenames use camelCase of the source table name | | --camel-case-measures | No | false | When true, metric labels use camelCase of the source column name (deprecated — use --label-style) | | --label-style | No | title-case | Label style for all SML object labels: title-case, camel-case, or none (raw source names). Overrides --camel-case-measures. | | --min-hierarchies-per-dim | No | 1 | Minimum hierarchies a dimension must have to be included; dimensions with fewer are dropped | | --max-hierarchies-per-dim | No | 4 | Maximum hierarchies kept per dimension; extras are truncated |

Output layout:

<output-dir>/
  catalog.yml
  connections/<connectionName>.yml
  datasets/<table>.yml
  dimensions/<dimension>.yml
  metrics/<metric>.yml
  models/<modelName>.yml
  sml.style.yaml   ← effective settings used for this generation

generate-sml-from-ddl

↑ Table of Contents

Parses a SQL DDL file (CREATE TABLE / CREATE VIEW statements) and generates AtScale SML files without a live database connection. Useful for offline model generation and CI pipelines.

All inference capabilities described under generate-sml-from-connection (composite keys, bridge table detection, naming patterns, one-relationship-per-hierarchy) apply equally to the DDL path. FK constraints declared in the DDL (FOREIGN KEY (col1, col2) REFERENCES …) are parsed and used for composite join inference.

./atscale-utils generate-sml-from-ddl \
  --ddl-file "./schema.sql" \
  --output-dir "./sml-output"

With optional overrides:

./atscale-utils generate-sml-from-ddl \
  --ddl-file "./schema.sql" \
  --model-name "SalesModel" \
  --output-dir "./sml-output" \
  --connection-name "my_warehouse" \
  --catalog-name "Sales Analytics" \
  --schema "SALES" \
  --pii-severity "LOW" \
  --fact-tables "FactInternetSales,FactResellerSales" \
  --camel-case-files true \
  --camel-case-measures true

Style parameters (--pii-severity, --model-mode, --fact-tables, --catalog-name, --camel-case-files, --camel-case-measures, --label-style, --min-hierarchies-per-dim, --max-hierarchies-per-dim) can also be set in an SML style config file. CLI flags take priority over the file. After generation, effective settings are always written to <output-dir>/sml.style.yaml regardless of the input config path.

| Parameter | Required | Default | Description | |---|---|---|---| | --ddl-file | Yes | | Path to the SQL DDL file | | --model-name | No | DDL filename stem | Name for the generated semantic model | | --output-dir | Yes | | Directory to write SML files | | --connection-name | No | my_connection | Connection name to embed in SML files | | --sml-config-file | No | sml.style.yaml | Path to the SML style config to read settings from | | --catalog-name | No | model-name | Display name for the generated catalog | | --schema | No | | Filter DDL to only tables in this schema | | --database | No | | Database name to embed in the SML connection file | | --dialect | No | Auto-detected from filename | Database dialect (snowflake, postgresql). When snowflake, dataset table names are uppercased. | | --pii-severity | No | MEDIUM | Minimum PII severity to exclude: HIGH, MEDIUM, LOW, or none | | --model-mode | No | Collision-time decision | new permits deterministic renaming of all colliding query names; existing preserves established names and reports a blocking compatibility conflict | | --fact-tables | No | Auto-detected | Comma-separated table names to treat as facts, overriding automatic classification | | --camel-case-files | No | false | When true, dataset and dimension filenames use camelCase of the source table name | | --camel-case-measures | No | false | When true, metric labels use camelCase of the source column name (deprecated — use --label-style) | | --label-style | No | title-case | Label style for all SML object labels: title-case, camel-case, or none (raw source names). Overrides --camel-case-measures. | | --min-hierarchies-per-dim | No | 1 | Minimum hierarchies a dimension must have to be included; dimensions with fewer are dropped | | --max-hierarchies-per-dim | No | 4 | Maximum hierarchies kept per dimension; extras are truncated |

Output layout: Same as generate-sml-from-connection (including sml.style.yaml).


generate-sml-from-xml

↑ Table of Contents

Reads an AtScale XML project file (schema version project_2_0) and converts it to AtScale SML YAML files. No database connection is required — the conversion runs entirely from the XML model definition.

Dimensions, metrics, datasets, catalog, connection, and model files are all emitted based on the XML structure. Relationships are inferred from the cube's key-ref logical sections: cross-table FKs (complete="false") are mapped to separate dimension datasets, and degenerate dimensions (complete="true") are mapped as self-joins within the fact table. Role-played dimensions (role_play), include_default_drillthrough, metric folders, dataset column definitions, and the immutable flag are all extracted from the XML when present. Cube-level User Defined Aggregates (<aggregates>) are converted to each model's aggregates: list, with each attribute-ref resolved to either a dimension attribute or a metric and relationships_path synthesized for attributes reached through a snowflake/embedded relationship. The connection name is auto-detected from <physical><connection id="..."> if --connection-name is not supplied. Schema-level dimensions that have no join path to the cube are omitted.

--model-mode is optional unless multiple dimensions in one model expose the same case-insensitive level-attribute query name. In an interactive terminal, ps-utils explains the compatibility choice and prompts only when such a collision occurs. In CI or another non-interactive environment, supply new to rename every collision member deterministically or existing to preserve established names and surface a blocking conflict.

./atscale-utils generate-sml-from-xml \
  --xml-file "./MyModel.xml" \
  --output-dir "./sml-output"

With optional overrides:

./atscale-utils generate-sml-from-xml \
  --xml-file "./MyModel.xml" \
  --output-dir "./sml-output" \
  --connection-name "my_bq_conn" \
  --connection-type "bigquery" \
  --connection-db "my-project-id" \
  --connection-schema "my_dataset" \
  --catalog-name "My Catalog"

| Parameter | Required | Default | Description | |---|---|---|---| | --xml-file | Yes | | Path to the AtScale XML project file (project_2_0 format) | | --output-dir | Yes | | Directory to write SML files | | --connection-name | No | Auto-detected from XML | Connection unique_name to embed in generated files | | --connection-type | No | | Database dialect written to the connection file (e.g. snowflake, bigquery) | | --connection-db | No | | Database/project name written to the connection file. When set, every dataset shares one connection instead of a separate connection per distinct database/schema pair found in the XML | | --connection-schema | No | | Schema/dataset name written to the connection file. When set, every dataset shares one connection instead of a separate connection per distinct database/schema pair found in the XML | | --catalog-name | No | XML schema name | Override the catalog label | | --model-mode | No | Collision-time decision | new permits deterministic renaming of all colliding query names; existing preserves established names and reports a blocking compatibility conflict |

Output layout:

<output-dir>/
  catalog.yml
  connections/<connection-name>.yml  (one per distinct database/schema pair, unless --connection-db/--connection-schema is set)
  datasets/<dataset-name>.yml      (one per dataset referenced by a cube or dimension)
  dimensions/<dim-name>.yml        (one per referenced dimension)
  metrics/<metric-name>.yml        (one per measure or inline expression)
  calculations/<calc-name>.yml     (one per schema-level calculated member)
  models/<cube-name>.yml           (one per XML <cube>)

generate-sml-from-bundle

↑ Table of Contents

Converts every AtScale project inside one or more support bundles to SML in one run, using the same converter as generate-sml-from-xml. Two bundle shapes are accepted: the archive the engine's Download support bundle button produces (a zip of per-area zips, with projects inside metadata.zip at metadata/<id>/project.xml), and an unpacked tree with a metadata/ directory (metadata/<id>/project.xml for the container edition, metadata/<org>/<id>/project.xml for the installer edition), or a zip of such a tree, with or without a wrapper folder. Only metadata.zip and metadata/** entries are extracted, so the log archives that dominate a real bundle are never read. No database connection is required.

Each project becomes its own SML repository under <output-dir>/<bundle>/<org>/<project>__<id8>/. Projects are named from the XML root's name attribute, with the first eight characters of the project id appended so same-named projects never collide; two bundles with the same basename get -2, -3 suffixes. Re-runs skip projects whose output already exists unless --force is given. A conversion is staged beside its destination and only replaces the previous output when it succeeds, so a failing forced re-run never destroys a good repository. Converter output is kept only while a project is failing, under <output-dir>/.logs/. summary.csv merges with the summary already in the output directory, so a filtered or partial re-run still describes everything on disk. The operation attempts every project before it reports, writes the summary either way, and exits non-zero if any project failed or a bundle held no metadata.

./atscale-utils generate-sml-from-bundle \
  --bundles    "./customer-bundle.zip,./customer-bundle-prod" \
  --output-dir "./sml-out" \
  --model-mode new

| Parameter | Required | Default | Description | |---|---|---|---| | --bundles | Yes | | Comma-separated support bundle paths: the engine's support-bundle .zip, a directory containing metadata/ or metadata.zip, or a zip of such a directory | | --output-dir | Yes | | Directory that receives one SML repository per project plus summary.csv and summary.md | | --force | No | false | Re-convert projects whose output directory already exists | | --org | No | all | Comma-separated organisation folder names to include (installer bundles) | | --connection-name | No | Auto-detected per XML | Connection unique_name to embed in generated files | | --connection-type | No | | Database dialect written to the connection files | | --connection-db | No | | Database name written to the connection files; when set, every dataset shares one connection | | --connection-schema | No | | Schema name written to the connection files; when set, every dataset shares one connection | | --model-mode | No | | Compatibility policy applied to every project when query-name collisions occur. Pass new for unattended runs; existing marks a colliding project as failed for review |

Output layout:

<output-dir>/
  summary.csv                       one row per project: bundle, org, project, id, status, counts, paths
  summary.md                        the same table, readable
  <bundle>/<org>/<project>__<id8>/  catalog.yml, connections/, datasets/, dimensions/, metrics/, calculations/, models/, README.md
  .logs/<bundle>/<org>/<project>.log   converter transcript, kept only while the project is failing

Statuses: ok, ok-no-model (the XML had no cube), skipped (already present, counts read from disk), FAILED, NO-METADATA (a bundle with neither metadata/ nor metadata.zip).


generate-sml-from-tabular

↑ Table of Contents

Reads an SSAS Tabular model export (TMSL/XMLA createOrReplace JSON) and converts it to AtScale SML YAML files. No database connection is required — the conversion runs entirely from the TMSL model definition, though every table's partition query is parsed to recover its real physical table/column names where possible.

This is a Pass 1 structural migration: every fact, dimension, and relationship is built, along with metrics for mechanically-unambiguous measures (bare SUM/AVERAGE/MIN/MAX/DISTINCTCOUNT/COUNT/COUNTROWS).

Measures that are not a bare aggregation are routed to whichever AtScale calculation path will actually publish:

| Outcome | When | Output | | --- | --- | --- | | Base metric | a bare aggregation over one column | metrics/ with a calculation_method | | Server-side DAX, verbatim | every function is on AtScale's server-side DAX whitelist | calculations/ | | Translated to MDX | at least one function is off the whitelist, but the whole expression has a faithful MDX equivalent | calculations/ | | Deferred | neither | DEFERRED_MEASURES.md, with the blocking functions and a remediation hint |

Before deferring, an expression blocked by an inline aggregation is retried with those aggregations lifted into base metrics — SUM('F'[charge]) - SUM('F'[refund]) becomes [Gross Charge] - [Sum of refund]. An existing metric over the same dataset/column/method is reused; a new hidden one is minted only when there isn't one, and only if the rewrite actually unblocks the measure, so no orphan metrics are left behind. Aggregations over anything other than a plain column (SUMX(FILTER(...), ...)) are left alone, since lifting those would relocate evaluation context rather than just the aggregation.

Note that SUM, MIN, MAX, AVERAGE, COUNT and DISTINCTCOUNT are absent from AtScale's server-side DAX whitelist — AtScale models those as base metrics with a calculation_method, so emitting them inside a calculation parses locally and then fails at publish. The base-metric check therefore runs first.

Translation is deliberately conservative: where a DAX construct has no faithful MDX equivalent the measure is deferred with an explanation rather than converted to something approximate. For example a whole-year DATEADD becomes ParallelPeriod, but a month-grain DATEADD is deferred, because ParallelPeriod shifts whole ancestor periods and would be silently wrong.

Role-play family detection is the core value-add: SSAS Tabular cannot role-play a dimension, so when the same real-world dimension is needed multiple times under different names (Order Date vs Ship Date), Tabular fakes it by importing the same source object once per role. This operation reads each table's partition query, resolves what object it actually reads from, and groups dimension tables that share the same source object into one consolidated SML dimension, wired to facts via SML role_play (when a fact has genuinely multiple distinct FK columns into the group) or an ordinary relationship (a single FK). Each role's original alias-prefix wording (e.g. "Serv", "AHP", "Refer Prov") is recovered by diffing member column aliases, so role_play labels reproduce historical naming.

Generated objects preserve their source unique_name wherever possible. When a backing dataset's source name conflicts with a dimension, only that dataset receives the technical suffix <table>.dataset; the dimension retains its query-facing name, and all generated level, metric, and relationship references use the resolved dataset name.

--model-mode behaves the same as in generate-sml-from-xml / generate-sml-from-ddl: optional unless a query-name collision occurs, at which point new renames colliding objects deterministically and existing preserves established names and reports a blocking conflict.

Getting the TMSL/XMLA export: this operation does not connect to SSAS itself — you supply the export file. To produce it:

  1. In SQL Server Management Studio (SSMS), connect to the Analysis Services Tabular instance (Object Explorer → Connect → Analysis Services).
  2. Right-click the target database (not the server) → Script Database as → Create To → File..., and save it.
  3. SSMS saves this with an .xmla extension for both Tabular and Multidimensional servers, but for a Tabular database (compatibility level 1200+) the body of the file is actually the TMSL createOrReplace JSON this operation expects — pass that file straight to --xmla-file.

Alternatives: Tabular Editor can produce the same createOrReplace script (Advanced Scripting → Create or Replace), and Invoke-ASCmd/the Microsoft.AnalysisServices.Tabular API can script this non-interactively for CI pipelines.

./atscale-utils generate-sml-from-tabular \
  --xmla-file "./Model.xmla" \
  --warehouse "Snowflake" \
  --database "MY_DB" \
  --schema "MY_SCHEMA" \
  --model-name "my_model" \
  --output-dir "./sml-output"

| Parameter | Required | Default | Description | |---|---|---|---| | --xmla-file | Yes | | Path to the TMSL/XMLA export (createOrReplace JSON) to convert | | --warehouse | Yes | | Target warehouse dialect: Snowflake, Databricks, BigQuery, or Postgres | | --database | Yes | | Primary connection database/catalog name | | --schema | Yes | | Primary connection schema name | | --model-name | Yes | | SML model_unique_name (snake_case recommended) | | --output-dir | Yes | | Directory to write SML files | | --catalog-name | No | {model-name}_catalog | Override the catalog unique_name | | --currency | No | USD | Currency code used for currency-formatted metrics | | --description | No | | Optional catalog/model description override | | --model-mode | No | Collision-time decision | new permits deterministic renaming of all colliding query names; existing preserves established names and reports a blocking compatibility conflict |

Output layout:

<output-dir>/
  catalog.yml
  connections/<connection-name>.yml    (primary + one per cross-database source)
  datasets/<dataset-name>.yml          (one per kept table/role-play family)
  dimensions/<dim-name>.yml            (one per dimension table/role-play family)
  metrics/<metric-name>.yml            (one per SIMPLE measure)
  models/<model-name>.yml
  README.md                            (build params, assumptions, generation summary)
  calculations/                        (server-side DAX kept verbatim, plus DAX translated to MDX)
  DEFERRED_MEASURES.md                 (measures with no cube-side equivalent, for manual follow-up)
  CONVERSION_REPORT.md / .json         (complete account of what converted vs. what needs follow-up)
  context/
    <source file>                      (verbatim copy of the TMSL/XMLA export)
    ddl.sql                            (derived CREATE TABLE statements, CONFIRMED or GUESSED per table)
    erd.mmd                            (derived Mermaid ERD)
    use_case.md                        (derived from the source model's own measures/hierarchies)
    build.yaml                         (effective build parameters, incl. detected role-play families)

What to expect: this is a Pass 1 structural migration, not a finished, deploy-ready model — treat the output as a strong first draft, not the final word. After it runs:

  1. Read the generated README.md first — it documents every assumption the conversion made (role-play families detected, any table whose physical source couldn't be confirmed and had to be guessed from its Tabular display name, cross-database connections it created automatically) and a summary of what got built.
  2. Check DEFERRED_MEASURES.md — the measures that could be converted neither as server-side DAX nor as MDX are listed here with their original DAX, the functions that blocked them, and where the logic belongs instead (dataset SQL, a semi_additive block, a dimension level, ...). These need a human to design the equivalent SML object.
  3. Review calculations/ — measures translated to MDX record their source DAX in the object's description, so a reviewer can check the translation without going back to the TMSL.
  4. Check CONVERSION_REPORT.md/.json for the full list of issues by severity — error means something was dropped and likely needs a fix, action_needed means it's usable but a human should confirm something (e.g. a guessed physical table name) before trusting it in production.
  5. Verify any table/column marked "GUESSED" (rather than "CONFIRMED") in context/ddl.sql against real DDL or a data profile before deploying — a guess is a naming-convention fallback, not a confirmed physical source.
  6. Run the result through atscale-list-model-errors (or your normal SML validation step) before deploying, the same as any other generated SML.

Keeping the AtScale capability lists current

Three hand-transcribed whitelists drive every DAX verdict ps-utils produces. They are snapshots of AtScale documentation, so they go stale when AtScale adds or removes support:

| What | File | Source page | | --- | --- | --- | | Server-side DAX | src/operations/generate-sml-from-tabular/dax/capabilities.ts | Server-Side DAX Reference | | MDX | same file | MDX Reference | | Client-side DAX | src/operations/analyze-powerbi-dax-gaps/client-dax-capabilities.ts | Supported Client-Side DAX Language Elements |

Checking for drift
npm run build                    # the script runs from dist/
npm run check:atscale-capabilities

This only reports. It fetches the two container pages, parses their function lists, diffs them against the sets encoded in the capability files, and prints what changed:

Server-side DAX — up to date

Client-side DAX — DRIFT
  + added upstream:   CONCATENATEX, VALUES
  - removed upstream: ISSUBTOTAL

Exit codes: 0 no drift, 1 drift found, 2 the check itself failed (no network, or the page could not be parsed).

Applying an update
npm run check:atscale-capabilities -- --write

--write rewrites the function sets inside the // <generated:...> markers. Everything outside those markers — comments, remediation hints, exports — is left alone. It is not the whole job; four steps remain, and the script prints them:

  1. Bump captured in each file you changed.
  2. Update the count assertion in the matching parity test (generate-sml-from-tabular-dax.test.ts for server-side, analyze-powerbi-dax-gaps.test.ts for client-side).
  3. Add a REMEDIATION_HINTS / CLIENT_REMEDIATION entry for any newly unsupported function, or the gap report shows a blocker with an empty "what to do instead" cell.
  4. Run npm test.

--json emits the same drift report as machine-readable output.

When it refuses to run

The check is manual on purpose — it is not part of npm run build or CI, so an AtScale docs outage or a page redesign can never break a build. Two ways it declines to act:

  • No network. It needs outbound access to documentation.atscale.com. Behind an egress allowlist or proxy that host must be permitted, otherwise it exits 2 with an explanation.
  • Unrecognised page. Each source declares sentinel functions that must be found. If a redesign breaks the parser, the alternative would be reporting every function as "removed upstream" and, under --write, emptying the whitelist. Instead it exits 2 and asks you to fix parseFunctionList() in src/scripts/check-atscale-capabilities.ts.

The parser is unit-tested against fixtures shaped like the Docusaurus pages, but has not yet been run against the live HTML. Give it one run on a networked machine before relying on it; if the selectors are wrong the sentinel check will say so rather than corrupting the lists.

What expanding a whitelist does and does not do

Adding a function makes ps-utils accept it. The gap analysis and the server-side passthrough classification follow immediately, with no code change — adding VALUES and CONCATENATEX to the client-side list moves 33 measures out of "needs redesign" on a real report.

Translating a structurally new function from DAX to MDX is separate work. A one-to-one mapping is a single entry in the IDENTITY map in dax/mdx.ts; anything that changes shape — as CALCULATE, DATEADD and ALL do — needs a hand-written rule. The whitelist says "allowed"; the translator has to know how.

Always refresh from the container docs. AtScale publishes an installer copy of the same pages, and they are not interchangeable — the installer copy of the client-side page omits SELECTEDVALUE, ALLSELECTED, AVERAGEX, HASONEVALUE, ISINSCOPE, DATEADD, DISTINCT and EXCEPT. Transcribing it reported 16% of a real customer report as supported instead of 62%, and would have recommended pushing logic into the model that never needed to move.

The parity tests pin each list's size and a sample of entries, so an accidental partial edit fails loudly rather than silently changing every verdict.

analyze-powerbi-dax-gaps

↑ Table of Contents

Analyse a Power BI .pbix and report which of its report-scoped DAX measures AtScale can evaluate. No connection is required — everything is read from the file.

atscale-utils analyze-powerbi-dax-gaps \
  --pbix-file "./Average Daily Anesthetizing Locations.pbix" \
  --output-dir ./gap-report

| Parameter | Required | Description | | --- | --- | --- | | --pbix-file | Yes | Path to the .pbix to analyse | | --output-dir | Yes | Directory for the gap report |

Writes PBIX_GAP_REPORT.md (for people), .json and .csv (for tooling).

Two surfaces, judged separately. A measure can be valid in one and broken in the other, so every measure gets both verdicts:

Pairing them turns the report into a work list rather than a pass/fail:

| Recommendation | Meaning | | --- | --- | | Keep in the report | Supported client-side; no change needed | | Move into the AtScale model | Unsupported client-side, but converts as a model calculation | | Needs redesign | Supported by neither surface | | Could not parse | Malformed DAX in the source report |

The report also lists caveats — measures that pass the whitelist but hit a documented limitation a function check alone would miss. The clearest example is DATEADD: it is on the client-side list, but on the DAX Tabular dialect it only works with the DAY interval, so MONTH/QUARTER/YEAR error out at query time.

What can and cannot be read. A .pbix is a zip, but what is inside depends on how the report connects:

  • Live connection reports (pointing at SSAS or AtScale) have no embedded model. Their report-scoped measures sit in Report/Layout in plain text, and those are exactly what this operation analyses.
  • Import / composite reports carry a DataModel part, which is an XPress9-compressed Analysis Services backup and cannot be read without Microsoft's tooling. The operation says so rather than pretending to parse it, and analyses only the report-scoped measures. For the model side, extract it with pbi-tools and run generate-sml-from-tabular on the result.

generate-sml-from-ssas-multidimensional

↑ Table of Contents

Reads an SSAS Multidimensional (classic OLAP cube) XMLA export and converts it to AtScale SML YAML files. No database connection is required — the conversion runs entirely from the XMLA model definition, using each cube's DataSourceView to recover physical table/column names. Internally this operation converts the XMLA to an AtScale project XML first, then reuses generate-sml-from-xml's own converter to produce the SML — the intermediate XML is written to context/generated-project.xml for traceability.

This is a Pass 1 structural migration: regular fact-to-dimension relationships, role-played dimensions (multiple cube-dimension usages sharing one underlying dimension — the naming prefix for each role is recovered from the distinguishing part of its name, e.g. "Order"/"Ship" from "Order Date"/"Ship Date"), degenerate dimensions (attributes hosted directly on the fact table, detected by physical table identity rather than assumed from SSAS's own type label), synthesized hierarchies (when a dimension declares none, one is built from its Key attribute and AttributeRelationships), and simple measures (Sum/Count/DistinctCount/Min/Max/Average) all convert. Many-to-many measure-group dimensions, reference (snowflaked/chained) dimensions, and parent-child dimensions are detected and reported — not converted — matching or improving on the conservative behavior of the reference SSAS-to-AtScale converter for these same cases.

--model-mode behaves the same as in generate-sml-from-xml / generate-sml-from-tabular.

Getting the XMLA export: this operation does not connect to SSAS itself — you supply the export file. In SQL Server Management Studio (SSMS), connect to the Analysis Services Multidimensional instance (Object Explorer → Connect → Analysis Services), right-click the target database (not the server) → Script Database as → Create To → File..., and save it. For a Multidimensional database this is genuine XML containing the full <Create><ObjectDefinition><Database> script this operation expects — pass that file straight to --xmla-file.

./atscale-utils generate-sml-from-ssas-multidimensional \
  --xmla-file "./Cube.xml" \
  --output-dir "./sml-output"

| Parameter | Required | Default | Description | |---|---|---|---| | --xmla-file | Yes | | Path to the SSAS Multidimensional XMLA export (Create/ObjectDefinition/Database script) | | --output-dir | Yes | | Directory to write SML files | | --catalog-name | No | Derived from the XMLA file | Override the catalog label | | --connection-type | No | | Database dialect written to the connection file (e.g. snowflake, postgresql) | | --connection-db | No | | Database name written into the connection file | | --connection-schema | No | | Schema name written into the connection file | | --model-mode | No | Collision-time decision | new permits deterministic renaming of all colliding query names; existing preserves established names and reports a blocking compatibility conflict |

Output layout: same as generate-sml-from-xml, plus context/generated-project.xml (the intermediate AtScale project XML this was derived from) and an "SSAS Multidimensional Import Notes" section appended to README.md listing every many-to-many/reference/parent-child relationship that was detected but not converted.

What to expect: a Pass 1 structural migration, not a deploy-ready model. After it runs:

  1. Read the generated README.md — the "SSAS Multidimensional Import Notes" section lists every issue found, sorted by severity (error > action_needed > warning > info): error means something was dropped and likely needs a fix, action_needed means it's usable but a human should confirm something (e.g. a many-to-many or reference dimension that needs a manual relationship, or a table whose physical source couldn't be confirmed).
  2. Any many-to-many, reference (snowflaked/chained), or parent-child dimension is deliberately not converted — the notes list each one so a human can design the relationship manually. This matches (or improves on) the reference converter's own conservative behavior for these cases, not a shortcut unique to this operation.
  3. Inspect context/generated-project.xml (the intermediate AtScale project XML) if you need to trace exactly how a specific SSAS construct was translated before it reached SML.
  4. Run the result through atscale-list-model-errors (or your normal SML validation step) before deploying, the same as any other generated SML.

generate-report-from-xml

↑ Table of Contents

Reads an AtScale XML project file (schema version project_2_0 — the same source format generate-sml-from-xml converts) and writes a single, human-readable Markdown report describing every object found in the model, as-is. No database connection is required.

Unlike generate-sml-from-xml, this is a read-only report: nothing is renamed, deduplicated, or reshaped for SML compatibility, and objects the converter deliberately skips (perspectives, roles, translations, named sets, KPIs) are still listed, so the report is a complete inventory of the source model. The report covers:

  • Connections — every connection id referenced by a dataset, and how many datasets use it
  • Datasets — physical table or SQL query, columns with data types, and every key-ref/attribute-ref count
  • Attribute Library — every schema-level keyed attribute, resolved to the dataset.column(s) it's bound to
  • Dimensions — every dimension definition found anywhere (schema-level and per-cube inline), with hierarchies, levels, secondary attributes, and a callout when the same name is defined more than once across different scopes (a common source of real modeling bugs)
  • Cubes — datasets used, the actual fact-to-dimension join graph (resolved independently of any SML shaping), dimensions used, measures (aggregation, semi-additive info, resolved or inferred dataset/column binding), calculated members used, User Defined Aggregates, named sets, KPIs, and drillthrough settings
  • Calculated Member Library — every schema-level calculated-member formula, whether or not a cube currently uses it
./atscale-utils generate-report-from-xml \
  --xml-file "./MyModel.xml" \
  --output-file "./MyModel-report.md"

Omit --output-file to print the report to stdout:

./atscale-utils generate-report-from-xml --xml-file "./MyModel.xml"

| Parameter | Required | Default | Description | |---|---|---|---| | --xml-file | Yes | | Path to the AtScale XML project file (project_2_0 format) | | --output-file | No | stdout | Output Markdown file path | | --title | No | XML schema name | H1 title for the report |


generate-report-from-sml

↑ Table of Contents

Reads an SML directory (catalog.yml plus datasets/, dimensions/, metrics/, models/, and optionally connections/ and calculations/) and writes a single, human-readable Markdown report describing every object found in the model, as-is. No database connection is required.

Mirrors generate-report-from-xml's report shape and read-only intent — nothing is renamed, deduplicated, or reshaped — sourced from an SML directory instead of an AtScale XML project file. The report covers:

  • Connections — every SML connection object
  • Datasets — physical table or SQL query, columns with data types, and how many level attributes / relationships reference it
  • Dimensions — every dimension file, with hierarchies, levels, secondary attributes, and snowflake/embedded joins
  • Models — datasets used, the fact-to-dimension join graph, dimensions used, metrics used and calculations used (resolved against their libraries, same as measures resolve in the XML report), perspectives, aggregates, query-name overrides, and drillthrough settings
  • Metrics — the metrics library: every metric defined, whether or not a model currently references it
  • Calculations — the calculations library: every calculated-metric formula defined, whether or not a model currently references it
  • Security — any row_security/dimension_security definitions found
./atscale-utils generate-report-from-sml \
  --sml-dir "./sml-output" \
  --output-file "./sml-report.md"

Omit --output-file to print the report to stdout:

./atscale-utils generate-report-from-sml --sml-dir "./sml-output"

| Parameter | Required | Default | Description | |---|---|---|---| | --sml-dir | Yes | | Path to the SML directory to report on | | --output-file | No | stdout | Output Markdown file path | | --title | No | catalog label / unique_name | H1 title for the report |


generate-shared-model-plan

↑ Table of Contents

Analyses one or more SML output directories to identify opportunities for sharing or reusing dimensions, datasets, and model structures across projects. Uses Jaccard-based fuzzy subtree matching to compute similarity scores and emits a human-readable recommendation report plus machine-readable option YAML files.

Each option falls into one of three categories:

| Kind | What it does | |------|-------------| | shared-dimension-library | Extract near-identical dimensions from multiple projects into a shared library | | dataset-consolidation | Merge datasets that reference the same physical table or share similar column sets | | base-model-extraction | Factor out a common base model when two models share most dimensions and metrics |

./atscale-utils generate-shared-model-plan \
  --input-dirs "./project-a,./project-b" \
  --output-dir "./plan-output"

With threshold override:

./atscale-utils generate-shared-model-plan \
  --input-dirs "./project-a,./project-b,./project-c" \
  --output-dir "./plan-output" \
  --threshold 0.5

| Parameter | Required | Default | Description | |---|---|---|---| | --input-dirs | Yes | | Comma-separated list of SML output directories to analyse | | --output-dir | Yes | | Directory where output files are written | | --threshold | No | 0.5 | Similarity threshold 0–1; lower values surface more options | | --max-per-subject | No | 3 | Maximum recommendations per subject entity (dataset, dimension, or model pair); prevents flooding output with near-duplicate options for the same entity |

Output layout:

<output-dir>/
  RECOMMENDATION.md              — options with diagrams, change lists, and pros/cons
  option-1-<kind>.yml            — machine-readable changes for option 1
  option-2-<kind>.yml            — machine-readable changes for option 2
  ...

apply-shared-model-plan-option

↑ Table of Contents

Applies a machine-readable recommendation YAML produced by generate-shared-model-plan to create shared SML files. Three kinds are supported:

| Kind | What is written | |------|----------------| | dataset-consolidation | <shared-dir>/datasets/<name>.yml — merged column union of all source copies | | shared-dimension-library | <shared-dir>/dimensions/<name>.yml — merged dimension preserving all SML attributes | | base-model-extraction | <shared-dir>/models/<base>.yml — common core; slim models for any project-specific content |

./atscale-utils apply-shared-model-plan-option \
  --plan-file "./shared-plan/option-1-dataset-consolidation.yml" \
  --shared-dir "./shared"

To also delete the local source copies after writing the shared file:

./atscale-utils apply-shared-model-plan-option \
  --plan-file "./shared-plan/option-11-shared-dimension-library.yml" \
  --shared-dir "./shared" \
  --remove-sources

Preview without touching disk:

./atscale-utils apply-shared-model-plan-option \
  --plan-file "./shared-plan/option-31-base-model-extraction.yml" \
  --shared-dir "./shared" \
  --dry-run

| Parameter | Required | Default | Description | |---|---|---|---| | --plan-file | Yes | | Path to the option YAML file from generate-shared-model-plan | | --shared-dir | Yes | | Base directory where shared files are written | | --remove-sources | No | false | Delete local source copies after writing the shared version | | --dry-run | No | false | Print all actions without writing or deleting any files |

An APPLY_REPORT.md is written to <shared-dir> summarising every action taken and the deployment steps required.


apply-style-to-sml

↑ Table of Contents

Re-applies display labels to an existing SML directory using a style config. Reads datasets, dimensions, and metrics YAML files in-place and rewrites their label fields according to the active style, then writes STYLE.md and STYLE_CHANGES.md summarising the conventions and every label change made.

./atscale-utils apply-style-to-sml \
  --sml-dir "./sml-output"

With optional overrides:

./atscale-utils apply-style-to-sml \
  --sml-dir "./sml-output" \
  --sml-config-file "./sml-output/sml.style.yaml" \
  --label-style camel-case \
  --catalog-name "Sales Analytics"

| Parameter | Required | Default | Description | |---|---|---|---| | --sml-dir | Yes | | Path to the SML output directory to update | | --sml-config-file | No | <sml-dir>/sml.style.yaml | Path to the SML style config to read settings from | | --label-style | No | title-case | Label style for all SML object labels: title-case, camel-case, or none (raw source names). Overrides --camel-case-measures. | | --catalog-name | No | | Catalog display name for STYLE.md |

Output: Updates datasets/*.yml, dimensions/*.yml, and metrics/*.yml labels in-place; writes STYLE.md and `S