Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 23 additions & 40 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ on:
branches: [main]
pull_request:
branches: [main]
workflow_call:
secrets:
NPM_STERFIVE_TOKEN:
required: false

env:
NODE_ENV: development

jobs:
# -- Install -------------------------------------------------
# -- Install & Build ------------------------------------------
install:
runs-on: ubuntu-latest
steps:
Expand All @@ -35,13 +39,17 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Cache node_modules
- name: Build all packages
run: npm run build

- name: Cache workspace
uses: actions/cache/save@v4
with:
path: |
node_modules
packages/*/node_modules
key: node-modules-${{ hashFiles('package-lock.json') }}
packages/*/dist
key: workspace-${{ github.sha }}

# -- Check ---------------------------------------------------
typecheck:
Expand All @@ -54,13 +62,14 @@ jobs:
with:
node-version: 22

- name: Restore node_modules
- name: Restore workspace
uses: actions/cache/restore@v4
with:
path: |
node_modules
packages/*/node_modules
key: node-modules-${{ hashFiles('package-lock.json') }}
packages/*/dist
key: workspace-${{ github.sha }}

- name: TypeScript type check
run: npm run typecheck
Expand All @@ -75,13 +84,14 @@ jobs:
with:
node-version: 22

- name: Restore node_modules
- name: Restore workspace
uses: actions/cache/restore@v4
with:
path: |
node_modules
packages/*/node_modules
key: node-modules-${{ hashFiles('package-lock.json') }}
packages/*/dist
key: workspace-${{ github.sha }}

- name: Biome lint & format check
run: npx biome check .
Expand All @@ -97,13 +107,14 @@ jobs:
with:
node-version: 22

- name: Restore node_modules
- name: Restore workspace
uses: actions/cache/restore@v4
with:
path: |
node_modules
packages/*/node_modules
key: node-modules-${{ hashFiles('package-lock.json') }}
packages/*/dist
key: workspace-${{ github.sha }}

- name: Run tests
run: npm test
Expand All @@ -126,13 +137,14 @@ jobs:
with:
node-version: 22

- name: Restore node_modules
- name: Restore workspace
uses: actions/cache/restore@v4
with:
path: |
node_modules
packages/*/node_modules
key: node-modules-${{ hashFiles('package-lock.json') }}
packages/*/dist
key: workspace-${{ github.sha }}

- name: Remove @sterfive/opcua-optimized-client
run: |
Expand All @@ -142,32 +154,3 @@ jobs:

- name: Run tests without optimized client
run: npm test

# -- Build ---------------------------------------------------
build:
needs: [typecheck, lint, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 22

- name: Restore node_modules
uses: actions/cache/restore@v4
with:
path: |
node_modules
packages/*/node_modules
key: node-modules-${{ hashFiles('package-lock.json') }}

- name: Build all packages
run: npm run build

- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: packages/*/dist/
retention-days: 7
11 changes: 11 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
# 2. On merge to main, the workflow either:
# a) Opens/updates a "Version Packages" PR
# b) If that PR is merged, publishes to npm with provenance
#
# IMPORTANT: Publish only happens after CI (tests/lint) passes.

name: Release

Expand All @@ -33,8 +35,14 @@ permissions:
pull-requests: write # to create/update the "Version Packages" PR

jobs:
# Gate: wait for CI to pass before releasing
ci:
uses: ./.github/workflows/ci.yml
secrets: inherit

release:
name: Version & Publish
needs: ci
runs-on: ubuntu-latest
timeout-minutes: 30

Expand All @@ -59,6 +67,9 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Build all packages
run: npm run build

- name: Create Release PR or Publish
id: changesets
uses: changesets/action@v1
Expand Down
Binary file modified .gitignore
Binary file not shown.
2 changes: 2 additions & 0 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
npx biome check .
npm test
22 changes: 22 additions & 0 deletions packages/app/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# @node-i3x/app

## 0.2.0

### Minor Changes

- feat: publishable CLI, turbo builds, and demo-embedded

- @node-i3x/app: evolved into publishable CLI with commander,
cosmiconfig, layered config (defaults → yml → env → args)
- @node-i3x/demo-embedded: publishable with i3x-demo and
i3x-demo-client bin entries, CLI args for ports
- All library packages now export from dist/ (no tsx required)
- Added turbo for dependency-ordered cached builds
- Added VHS recording scripts for demo GIF

### Patch Changes

- Updated dependencies
- @node-i3x/core@0.2.0
- @node-i3x/opcua-connector@0.2.0
- @node-i3x/rest-server@0.2.0
3 changes: 2 additions & 1 deletion packages/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "@node-i3x/app",
"version": "0.1.0",
"private": true,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

App should now be public

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- "private": true,
+ "private": false,

"version": "0.2.0",
"description": "i3X CLI -- expose OPC UA servers as i3X REST APIs",
"license": "AGPL-3.0-or-later OR LicenseRef-Sterfive-Commercial",
"author": "Sterfive SAS <contact@sterfive.com> (https://sterfive.com)",
Expand Down
14 changes: 7 additions & 7 deletions packages/app/src/banner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ export function printBanner(
): void {
const lines = [
'',
' i3X Server v' + version,
` i3X Server v${version}`,
'',
' OPC UA: ' + config.endpoint,
' REST: http://' + config.host + ':' + config.port,
` OPC UA: ${config.endpoint}`,
` REST: http://${config.host}:${config.port}`,
];

if (nodeCount !== undefined) {
lines.push(' Model: ' + nodeCount + ' nodes');
lines.push(` Model: ${nodeCount} nodes`);
}

lines.push('');
Expand All @@ -26,12 +26,12 @@ export function printBanner(
const width = maxLen + 2;

const hr = '-'.repeat(width);
const top = ' +' + hr + '+';
const bot = ' +' + hr + '+';
const top = ` +${hr}+`;
const bot = ` +${hr}+`;

console.log(top);
for (const line of lines) {
console.log(' |' + line.padEnd(width) + '|');
console.log(` |${line.padEnd(width)}|`);
}
console.log(bot);
console.log();
Expand Down
8 changes: 2 additions & 6 deletions packages/app/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@ program
.option('-p, --port <port>', 'REST API port', parseInt)
.option('-H, --host <host>', 'REST API bind address')
.option('--security-mode <mode>', 'OPC UA security mode')
.option(
'--optimized-client <mode>',
'Optimized client: auto | disabled',
)
.option('--optimized-client <mode>', 'Optimized client: auto | disabled')
.option(
'--subscription-interval <seconds>',
'Subscription interval in seconds',
Expand All @@ -36,8 +33,7 @@ program
if (opts.port !== undefined) cliArgs.port = opts.port;
if (opts.host) cliArgs.host = opts.host;
if (opts.securityMode) cliArgs.securityMode = opts.securityMode;
if (opts.optimizedClient)
cliArgs.optimizedClient = opts.optimizedClient;
if (opts.optimizedClient) cliArgs.optimizedClient = opts.optimizedClient;
if (opts.subscriptionInterval !== undefined)
cliArgs.subscriptionInterval = opts.subscriptionInterval;
if (opts.logLevel) cliArgs.logLevel = opts.logLevel;
Expand Down
4 changes: 1 addition & 3 deletions packages/app/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,7 @@ export async function resolveConfig(
});

try {
const result = configPath
? await explorer.load(configPath)
: await explorer.search();
const result = configPath ? await explorer.load(configPath) : await explorer.search();
if (result && !result.isEmpty) {
fileConfig = result.config as Partial<I3xConfig>;
}
Expand Down
9 changes: 3 additions & 6 deletions packages/app/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@ import {
} from '@node-i3x/core';
import { OpcUaClient, OpcUaDataSourceAdapter } from '@node-i3x/opcua-connector';
import { createApp } from '@node-i3x/rest-server';
import type { I3xConfig } from './config.js';
import { printBanner } from './banner.js';
import type { I3xConfig } from './config.js';

export async function startServer(
config: I3xConfig,
version: string,
): Promise<void> {
export async function startServer(config: I3xConfig, version: string): Promise<void> {
const logger = consoleLogger;

// 1. Outbound adapter (OPC UA)
Expand Down Expand Up @@ -58,7 +55,7 @@ export async function startServer(
const model = await modelService.preloadModel();
nodeCount = model.nodesById.size;
} catch (err) {
logger.error('Model preload failed: ' + String(err));
logger.error(`Model preload failed: ${String(err)}`);
if (config.failOnPreloadError) process.exit(1);
}
}
Expand Down
15 changes: 15 additions & 0 deletions packages/core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# @node-i3x/core

## 0.2.0

### Minor Changes

- feat: publishable CLI, turbo builds, and demo-embedded

- @node-i3x/app: evolved into publishable CLI with commander,
cosmiconfig, layered config (defaults → yml → env → args)
- @node-i3x/demo-embedded: publishable with i3x-demo and
i3x-demo-client bin entries, CLI args for ports
- All library packages now export from dist/ (no tsx required)
- Added turbo for dependency-ordered cached builds
- Added VHS recording scripts for demo GIF
25 changes: 22 additions & 3 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@node-i3x/core",
"version": "0.1.0",
"version": "0.2.0",
"description": "i3X domain core — models, ports, and services (zero dependencies)",
"license": "AGPL-3.0-or-later OR LicenseRef-Sterfive-Commercial",
"author": "Sterfive SAS <contact@sterfive.com> (https://sterfive.com)",
Expand All @@ -18,7 +18,21 @@
"type": "commercial",
"url": "https://sterfive.com"
},
"keywords": ["opcua", "opc-ua", "i3x", "i3x2ua", "industrial", "iiot", "iot", "domain", "hexagonal-architecture", "ports-and-adapters", "digital-twin", "asset-management", "sterfive"],
"keywords": [
"opcua",
"opc-ua",
"i3x",
"i3x2ua",
"industrial",
"iiot",
"iot",
"domain",
"hexagonal-architecture",
"ports-and-adapters",
"digital-twin",
"asset-management",
"sterfive"
],
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand All @@ -31,7 +45,12 @@
"publishConfig": {
"provenance": true
},
"files": ["dist", "LICENSE", "LICENSE-AGPL-3.0", "LICENSING.md"],
"files": [
"dist",
"LICENSE",
"LICENSE-AGPL-3.0",
"LICENSING.md"
],
"scripts": {
"build": "tsup",
"typecheck": "tsc --noEmit"
Expand Down
Loading
Loading