From d866bab0ea285d80ad342c78642ef77dfd772e17 Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Sun, 27 Apr 2025 21:00:08 -0400 Subject: [PATCH 01/21] feat: apple silicon only\n\n- Limit initial release to Apple Silicon (M1/M2/M3) Macs\n- Update CI workflow to only build for aarch64-apple-darwin\n- Update README to document platform support\n- Other platforms will be added behind a feature flag in future releases --- .github/workflows/CI.yml | 34 +--------------------------------- README.md | 6 +----- package.json | 11 +++-------- 3 files changed, 5 insertions(+), 46 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 0cab524..0f091c5 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -19,44 +19,12 @@ jobs: fail-fast: false matrix: settings: - - host: macos-latest - target: x86_64-apple-darwin - build: | - npm install - npx @napi-rs/cli build --target x86_64-apple-darwin - strip -x *.node - host: macos-latest target: aarch64-apple-darwin build: | npm install npx @napi-rs/cli build --target aarch64-apple-darwin strip -x *.node - - host: windows-latest - target: x86_64-pc-windows-msvc - build: | - npm install - npx @napi-rs/cli build --target x86_64-pc-windows-msvc - - host: windows-latest - target: aarch64-pc-windows-msvc - build: | - npm install - npx @napi-rs/cli build --target aarch64-pc-windows-msvc - - host: ubuntu-latest - target: x86_64-unknown-linux-gnu - docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian - build: | - set -e && - npm install && - npx @napi-rs/cli build --target x86_64-unknown-linux-gnu && - strip *.node - - host: ubuntu-latest - target: aarch64-unknown-linux-gnu - docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64 - build: | - set -e && - npm install && - npx @napi-rs/cli build --target aarch64-unknown-linux-gnu && - aarch64-unknown-linux-gnu-strip *.node name: stable - ${{ matrix.settings.target }} - node@18 @@ -328,7 +296,7 @@ jobs: - name: Move artifacts run: | mkdir -p dist - find . -type f -name '*.node' -exec mv {} . \; + find . -type f -name 'cel-typescript.*.node' -exec mv {} . \; - name: List files run: | diff --git a/README.md b/README.md index 28aaa2a..c393dd4 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,7 @@ npm install @kevinmichaelchen/cel-typescript - Node.js 18 or later -This package includes pre-compiled native binaries for multiple platforms: - -- macOS (x64, arm64) -- Linux (x64, arm64) -- Windows (x64) +This package currently only includes pre-compiled native binaries for Apple Silicon (M1/M2/M3) Macs. Support for other platforms (Intel Macs, Linux, Windows) is planned for future releases and will be available behind a feature flag. The appropriate binary for your platform will be automatically loaded at runtime. diff --git a/package.json b/package.json index eb7de37..68b42a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kevinmichaelchen/cel-typescript", - "version": "0.0.8", + "version": "0.0.9", "type": "module", "description": "TypeScript bindings for the Common Expression Language (CEL) using cel-rust", "repository": { @@ -44,14 +44,9 @@ "napi": { "name": "cel-typescript", "triples": { - "defaults": true, + "defaults": false, "additional": [ - "aarch64-apple-darwin", - "aarch64-unknown-linux-gnu", - "aarch64-pc-windows-msvc", - "x86_64-unknown-linux-gnu", - "x86_64-apple-darwin", - "x86_64-pc-windows-msvc" + "aarch64-apple-darwin" ] } }, From 52cb561a6351437128018140c6a6a81cad8cc0f4 Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Sun, 27 Apr 2025 21:17:07 -0400 Subject: [PATCH 02/21] fix: update CI workflow\n\n- Update test and publish jobs to only expect Apple Silicon artifacts\n- Remove other platforms from test matrix --- .github/workflows/CI.yml | 33 ++++----------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 0f091c5..4006224 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -133,34 +133,10 @@ jobs: matrix: node: - 18 - - 20 platform: - # For non-tag pushes, only test on x64 platforms - - name: Linux x64 - runner: ubuntu-latest - target: x86_64-unknown-linux-gnu - - name: macOS x64 + - name: macOS ARM64 runner: macos-latest - target: x86_64-apple-darwin - - name: Windows x64 - runner: windows-latest - target: x86_64-pc-windows-msvc - # Include all platforms for tags - include: - - if: startsWith(github.ref, 'refs/tags/v') - node: 18 - platform: - name: Linux ARM64 - runner: ubuntu-latest - target: aarch64-unknown-linux-gnu - docker: true - qemu: arm64 - - if: startsWith(github.ref, 'refs/tags/v') - node: 18 - platform: - name: macOS ARM64 - runner: macos-latest - target: aarch64-apple-darwin + target: aarch64-apple-darwin runs-on: ${{ matrix.platform.runner }} @@ -287,11 +263,10 @@ jobs: - name: Install dependencies run: npm ci - - name: Download all artifacts + - name: Download artifact uses: actions/download-artifact@v4 with: - pattern: bindings-* - merge-multiple: false + name: bindings-aarch64-apple-darwin - name: Move artifacts run: | From daaea5e40706887a26805746c460dc64c446b1cd Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Sun, 27 Apr 2025 21:28:17 -0400 Subject: [PATCH 03/21] feat: add dry-run publish to PRs\n\n- Make Publish job run on PRs with dry-run mode\n- Skip npm auth and real publishing on PRs\n- Keep real publishing for tags only --- .github/workflows/CI.yml | 69 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 4006224..d6684e6 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -222,13 +222,71 @@ jobs: npm test ' + dry-run-publish: + name: Dry Run Publish + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + needs: + - build + - test + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: 18 + check-latest: true + cache: npm + + - name: Cache NPM dependencies + uses: actions/cache@v4 + with: + path: node_modules + key: npm-cache-ubuntu-latest-${{ hashFiles('package-lock.json') }} + + - name: Install dependencies + run: npm ci + + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: bindings-aarch64-apple-darwin + + - name: Move artifacts + run: | + mkdir -p dist + find . -type f -name 'cel-typescript.*.node' -exec mv {} . \; + + - name: List files + run: | + echo "Root directory:" + ls -la + echo "\nNode files:" + find . -maxdepth 1 -name '*.node' + shell: bash + + - name: Link local package + run: npm link + + - name: Build TypeScript + run: npm run build:ts + + - name: Verify package contents + run: | + echo "Package contents:" + npm pack --dry-run + publish: - name: Publish + name: ${{ github.event_name == 'pull_request' && 'Publish (Dry Run)' || 'Publish' }} runs-on: ubuntu-latest permissions: contents: read id-token: write - if: startsWith(github.ref, 'refs/tags/v') + if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'pull_request' needs: - build - test @@ -293,11 +351,16 @@ jobs: npm pack --dry-run - name: Configure npm + if: github.event_name != 'pull_request' run: | echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc - name: Publish - if: env.NPM_TOKEN != '' + if: github.event_name != 'pull_request' && env.NPM_TOKEN != '' env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: npm publish --access public --provenance + + - name: Dry Run Publish + if: github.event_name == 'pull_request' + run: npm publish --dry-run From d38e7e6be8fa86c7e8a69b54475a6f585a392240 Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Sun, 27 Apr 2025 21:45:12 -0400 Subject: [PATCH 04/21] style: add emojis to CI workflow\n\n- Add descriptive emojis to job and step names\n- Make workflow more visually appealing --- .github/workflows/CI.yml | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index d6684e6..3f1f550 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1,4 +1,4 @@ -name: CI +name: ๐Ÿš€ CI env: DEBUG: napi:* APP_NAME: cel-typescript @@ -14,6 +14,7 @@ on: jobs: build: + name: ๐Ÿ”จ Build if: "!contains(github.event.head_commit.message, 'skip ci')" strategy: fail-fast: false @@ -27,7 +28,7 @@ jobs: strip -x *.node - name: stable - ${{ matrix.settings.target }} - node@18 + name: ๐Ÿ› ๏ธ stable - ${{ matrix.settings.target }} - node@18 runs-on: ${{ matrix.settings.host }} steps: @@ -58,7 +59,7 @@ jobs: with: targets: ${{ matrix.settings.target }} - - name: Cache cargo + - name: ๐Ÿ’พ Cache cargo uses: actions/cache@v4 with: path: | @@ -74,7 +75,7 @@ jobs: with: version: 0.11.0 - - name: Setup toolchain + - name: ๐Ÿ”ง Setup toolchain if: ${{ matrix.settings.docker }} uses: actions-rs/toolchain@v1 with: @@ -91,7 +92,7 @@ jobs: - name: Install dependencies run: npm ci - - name: Build in docker + - name: ๐Ÿณ Build in docker uses: addnab/docker-run-action@v3 if: ${{ matrix.settings.docker }} with: @@ -104,7 +105,7 @@ jobs: run: ${{ matrix.settings.build }} shell: bash - - name: Debug - List files after build + - name: ๐Ÿ” Debug - List files after build run: | echo "Current directory:" pwd @@ -114,7 +115,7 @@ jobs: find . -name "*.node" shell: bash - - name: Upload artifact + - name: ๐Ÿ“ค Upload artifact uses: actions/upload-artifact@v4 with: name: bindings-${{ matrix.settings.target }} @@ -125,7 +126,7 @@ jobs: retention-days: 1 test: - name: Test ${{ matrix.platform.name }} - node@${{ matrix.node }} + name: ๐Ÿงช Test ${{ matrix.platform.name }} - node@${{ matrix.node }} needs: - build strategy: @@ -153,7 +154,7 @@ jobs: check-latest: true cache: npm - - name: Cache dependencies + - name: ๐Ÿ’พ Cache dependencies uses: actions/cache@v4 with: path: | @@ -171,20 +172,20 @@ jobs: - name: Install dependencies run: npm ci - - name: Setup QEMU + - name: ๐Ÿ–ฅ๏ธ Setup QEMU if: matrix.platform.qemu uses: docker/setup-qemu-action@v3 with: platforms: ${{ matrix.platform.qemu }} - - name: Install Android NDK + - name: ๐Ÿ“ฑ Install Android NDK if: matrix.platform.android run: | wget https://dl.google.com/android/repository/android-ndk-r25c-linux.zip unzip android-ndk-r25c-linux.zip echo "ANDROID_NDK_HOME=$PWD/android-ndk-r25c" >> $GITHUB_ENV - - name: Download artifacts + - name: ๐Ÿ“ฅ Download artifacts uses: actions/download-artifact@v4 with: name: bindings-${{ matrix.platform.target }} @@ -281,7 +282,7 @@ jobs: npm pack --dry-run publish: - name: ${{ github.event_name == 'pull_request' && 'Publish (Dry Run)' || 'Publish' }} + name: ${{ github.event_name == 'pull_request' && '๐Ÿ” Publish (Dry Run)' || '๐Ÿ“ฆ Publish' }} runs-on: ubuntu-latest permissions: contents: read @@ -350,17 +351,17 @@ jobs: echo "Package contents:" npm pack --dry-run - - name: Configure npm + - name: โš™๏ธ Configure npm if: github.event_name != 'pull_request' run: | echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc - - name: Publish + - name: ๐Ÿš€ Publish if: github.event_name != 'pull_request' && env.NPM_TOKEN != '' env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: npm publish --access public --provenance - - name: Dry Run Publish + - name: ๐Ÿ” Dry Run Publish if: github.event_name == 'pull_request' run: npm publish --dry-run From 82c5b08bfb6feb111ee90ab03e8a9aacdb00c917 Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Sun, 27 Apr 2025 21:47:07 -0400 Subject: [PATCH 05/21] fix: yaml syntax in CI workflow\n\n- Remove duplicate name field in build job\n- Fix formatting and remove extra newlines --- .github/workflows/CI.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 3f1f550..1457901 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -14,7 +14,7 @@ on: jobs: build: - name: ๐Ÿ”จ Build + name: ๐Ÿ”จ Build - ${{ matrix.settings.target }} - node@18 if: "!contains(github.event.head_commit.message, 'skip ci')" strategy: fail-fast: false @@ -26,9 +26,6 @@ jobs: npm install npx @napi-rs/cli build --target aarch64-apple-darwin strip -x *.node - - - name: ๐Ÿ› ๏ธ stable - ${{ matrix.settings.target }} - node@18 runs-on: ${{ matrix.settings.host }} steps: From 3fa920790f9402d0a64fa2f561cc7e813dccfdbf Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Sun, 27 Apr 2025 21:59:11 -0400 Subject: [PATCH 06/21] fix: use consistent platform names\n\n- Use --platform instead of --target in build command\n- Update target names to match Node.js platform names\n- Add debug output to see binary names --- .github/workflows/CI.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 1457901..ca48a92 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -21,11 +21,15 @@ jobs: matrix: settings: - host: macos-latest - target: aarch64-apple-darwin + target: darwin-arm64 build: | npm install - npx @napi-rs/cli build --target aarch64-apple-darwin + npx @napi-rs/cli build --platform darwin-arm64 + echo "Binary after build:" + ls -la *.node strip -x *.node + echo "Binary after strip:" + ls -la *.node runs-on: ${{ matrix.settings.host }} steps: @@ -134,7 +138,7 @@ jobs: platform: - name: macOS ARM64 runner: macos-latest - target: aarch64-apple-darwin + target: darwin-arm64 runs-on: ${{ matrix.platform.runner }} From cd1061f7be520c82d0aa071bc6610597030007c3 Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Mon, 28 Apr 2025 10:46:53 -0400 Subject: [PATCH 07/21] ci: fix target --- .github/workflows/CI.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ca48a92..a6dd38d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -21,10 +21,10 @@ jobs: matrix: settings: - host: macos-latest - target: darwin-arm64 + target: aarch64-apple-darwin build: | npm install - npx @napi-rs/cli build --platform darwin-arm64 + npx @napi-rs/cli build --platform aarch64-apple-darwin echo "Binary after build:" ls -la *.node strip -x *.node @@ -138,7 +138,7 @@ jobs: platform: - name: macOS ARM64 runner: macos-latest - target: darwin-arm64 + target: aarch64-apple-darwin runs-on: ${{ matrix.platform.runner }} From 235ea1814ba2ee20ef8a90f9193fce75285922f2 Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Mon, 28 Apr 2025 11:13:21 -0400 Subject: [PATCH 08/21] refactor: simplify CI workflow and use NX for native builds --- .github/workflows/CI.yml | 155 +++++++++------------------------------ project.json | 2 +- 2 files changed, 34 insertions(+), 123 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index a6dd38d..3743a10 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1,8 +1,8 @@ -name: ๐Ÿš€ CI +name: CI env: DEBUG: napi:* - APP_NAME: cel-typescript MACOSX_DEPLOYMENT_TARGET: '10.13' + RUST_BACKTRACE: 1 on: push: @@ -14,23 +14,18 @@ on: jobs: build: - name: ๐Ÿ”จ Build - ${{ matrix.settings.target }} - node@18 + name: Build - ${{ matrix.target }} if: "!contains(github.event.head_commit.message, 'skip ci')" strategy: fail-fast: false matrix: - settings: - - host: macos-latest - target: aarch64-apple-darwin - build: | - npm install - npx @napi-rs/cli build --platform aarch64-apple-darwin - echo "Binary after build:" - ls -la *.node - strip -x *.node - echo "Binary after strip:" - ls -la *.node - runs-on: ${{ matrix.settings.host }} + target: [aarch64-apple-darwin, x86_64-unknown-linux-gnu] + include: + - target: aarch64-apple-darwin + os: macos-latest + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -38,96 +33,55 @@ jobs: submodules: recursive fetch-depth: 0 - - name: Debug - List submodules - run: | - git submodule status - ls -la - ls -la cel-rust/ - ls -la cel-rust/interpreter/ - shell: bash - - name: Setup node uses: actions/setup-node@v4 - if: ${{ !matrix.settings.docker }} with: node-version: 18 - check-latest: true cache: npm - - name: Install + - name: Install Rust uses: dtolnay/rust-toolchain@stable - if: ${{ !matrix.settings.docker }} with: - targets: ${{ matrix.settings.target }} + toolchain: stable + targets: ${{ matrix.target }} + + - name: Cache NPM dependencies + uses: actions/cache@v4 + with: + path: node_modules + key: npm-cache-${{ matrix.target }}-node@18-${{ hashFiles('package-lock.json') }} - - name: ๐Ÿ’พ Cache cargo + - name: Cache Rust dependencies uses: actions/cache@v4 with: path: | ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ - .cargo-cache target/ - key: ${{ matrix.settings.target }}-cargo-${{ matrix.settings.host }} - - - uses: goto-bus-stop/setup-zig@v2 - if: ${{ matrix.settings.target == 'armv7-unknown-linux-gnueabihf' }} - with: - version: 0.11.0 - - - name: ๐Ÿ”ง Setup toolchain - if: ${{ matrix.settings.docker }} - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: ${{ matrix.settings.target }} - override: true - - - name: Cache NPM dependencies - uses: actions/cache@v4 - with: - path: node_modules - key: npm-cache-${{ matrix.settings.target }}-${{ hashFiles('package-lock.json') }} + key: rust-cache-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }} - name: Install dependencies - run: npm ci - - - name: ๐Ÿณ Build in docker - uses: addnab/docker-run-action@v3 - if: ${{ matrix.settings.docker }} - with: - image: ${{ matrix.settings.docker }} - options: --user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build - run: ${{ matrix.settings.build }} - - - name: Build - if: ${{ !matrix.settings.docker }} - run: ${{ matrix.settings.build }} + run: npm install shell: bash - - name: ๐Ÿ” Debug - List files after build - run: | - echo "Current directory:" - pwd - echo "\nFiles in current directory:" - ls -la - echo "\nNode files:" - find . -name "*.node" + - name: Build + run: npx nx build:native -- --platform ${{ matrix.target }} shell: bash - - name: ๐Ÿ“ค Upload artifact + - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: bindings-${{ matrix.settings.target }} + name: bindings-${{ matrix.target }} path: | - *.node + **/*.node + !node_modules/**/*.node !target/**/*.node if-no-files-found: error retention-days: 1 test: - name: ๐Ÿงช Test ${{ matrix.platform.name }} - node@${{ matrix.node }} + name: Test ${{ matrix.platform.name }} - node@${{ matrix.node }} needs: - build strategy: @@ -171,59 +125,16 @@ jobs: ${{ hashFiles('package-lock.json', '**/Cargo.lock') }} - name: Install dependencies - run: npm ci - - - name: ๐Ÿ–ฅ๏ธ Setup QEMU - if: matrix.platform.qemu - uses: docker/setup-qemu-action@v3 - with: - platforms: ${{ matrix.platform.qemu }} - - - name: ๐Ÿ“ฑ Install Android NDK - if: matrix.platform.android - run: | - wget https://dl.google.com/android/repository/android-ndk-r25c-linux.zip - unzip android-ndk-r25c-linux.zip - echo "ANDROID_NDK_HOME=$PWD/android-ndk-r25c" >> $GITHUB_ENV - - - name: ๐Ÿ“ฅ Download artifacts uses: actions/download-artifact@v4 with: - name: bindings-${{ matrix.platform.target }} - path: . + name: bindings-${{ matrix.target }} - - name: List files - run: ls -R . - shell: bash + - name: Install dependencies + run: npm install - - name: Test bindings (native) - if: "!matrix.platform.docker" + - name: Test run: npm test - - name: Test bindings (Docker) - if: matrix.platform.docker - run: | - docker run --rm ${{ matrix.platform.qemu && format('--platform linux/{0}', matrix.platform.qemu) || '' }} \ - -v $(pwd):/build -w /build node:${{ matrix.node }}-slim bash -c ' - # Install Rust - apt-get update && apt-get install -y curl build-essential - curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - export PATH="/root/.cargo/bin:$PATH" - - # Fix npm optional dependencies on ARM64 - if [[ "${{ matrix.platform.target }}" == *"aarch64"* ]]; then - # Ensure clean state - rm -rf node_modules - # Install dependencies with --no-optional to skip problematic deps - npm install --no-optional - # Now install only the required optional deps - npm install @rollup/rollup-linux-arm64-gnu - fi - - # Run tests - npm test - ' - dry-run-publish: name: Dry Run Publish runs-on: ubuntu-latest diff --git a/project.json b/project.json index 5211a1b..0e0cc10 100644 --- a/project.json +++ b/project.json @@ -5,7 +5,7 @@ "build:native": { "executor": "nx:run-commands", "options": { - "command": "npx @napi-rs/cli build --platform" + "command": "npx @napi-rs/cli build --platform --release" }, "outputs": [ "{workspaceRoot}/cel-typescript.darwin-arm64.node", From f05429f9d393bd9401930e8fd80be2ff8f6ff847 Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Mon, 28 Apr 2025 11:16:57 -0400 Subject: [PATCH 09/21] refactor: update test job to use consistent matrix format --- .github/workflows/CI.yml | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 3743a10..34f103b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -81,20 +81,19 @@ jobs: retention-days: 1 test: - name: Test ${{ matrix.platform.name }} - node@${{ matrix.node }} + name: Test - ${{ matrix.target }} needs: - build strategy: fail-fast: false matrix: - node: - - 18 - platform: - - name: macOS ARM64 - runner: macos-latest - target: aarch64-apple-darwin - - runs-on: ${{ matrix.platform.runner }} + target: [aarch64-apple-darwin, x86_64-unknown-linux-gnu] + include: + - target: aarch64-apple-darwin + os: macos-latest + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -105,26 +104,10 @@ jobs: - name: Setup node uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node }} - check-latest: true + node-version: 18 cache: npm - - name: ๐Ÿ’พ Cache dependencies - uses: actions/cache@v4 - with: - path: | - node_modules - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - .cargo-cache - target/ - key: > - deps-${{ matrix.platform.target }}-${{ matrix.node }}- - ${{ contains(matrix.platform.target, 'aarch64') && 'npm-install' || 'npm-ci' }}- - ${{ hashFiles('package-lock.json', '**/Cargo.lock') }} - - - name: Install dependencies + - name: Download artifacts uses: actions/download-artifact@v4 with: name: bindings-${{ matrix.target }} From 736e68b33fa71dff92198a656a318d545a2cf6c6 Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Mon, 28 Apr 2025 11:31:13 -0400 Subject: [PATCH 10/21] feat: support more platforms (Windows, Linux, Intel Mac) --- .github/workflows/CI.yml | 134 +++++++++------------------------------ README.md | 5 +- package.json | 6 +- 3 files changed, 38 insertions(+), 107 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 34f103b..b5c0a27 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -13,18 +13,26 @@ on: pull_request: jobs: + # Builds native Node.js bindings for each target platform using napi-rs. + # The build artifacts are uploaded and used by the test job. build: name: Build - ${{ matrix.target }} if: "!contains(github.event.head_commit.message, 'skip ci')" strategy: fail-fast: false matrix: - target: [aarch64-apple-darwin, x86_64-unknown-linux-gnu] + target: [aarch64-apple-darwin, x86_64-apple-darwin, x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, x86_64-pc-windows-msvc] include: - target: aarch64-apple-darwin os: macos-latest + - target: x86_64-apple-darwin + os: macos-latest - target: x86_64-unknown-linux-gnu os: ubuntu-latest + - target: aarch64-unknown-linux-gnu + os: ubuntu-latest + - target: x86_64-pc-windows-msvc + os: windows-latest runs-on: ${{ matrix.os }} steps: @@ -80,6 +88,8 @@ jobs: if-no-files-found: error retention-days: 1 + # Downloads the built native modules and runs the test suite on each platform + # to ensure the bindings work correctly across different architectures. test: name: Test - ${{ matrix.target }} needs: @@ -87,12 +97,18 @@ jobs: strategy: fail-fast: false matrix: - target: [aarch64-apple-darwin, x86_64-unknown-linux-gnu] + target: [aarch64-apple-darwin, x86_64-apple-darwin, x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, x86_64-pc-windows-msvc] include: - target: aarch64-apple-darwin os: macos-latest + - target: x86_64-apple-darwin + os: macos-latest - target: x86_64-unknown-linux-gnu os: ubuntu-latest + - target: aarch64-unknown-linux-gnu + os: ubuntu-latest + - target: x86_64-pc-windows-msvc + os: windows-latest runs-on: ${{ matrix.os }} steps: @@ -118,70 +134,12 @@ jobs: - name: Test run: npm test - dry-run-publish: - name: Dry Run Publish - runs-on: ubuntu-latest - if: github.event_name == 'pull_request' - needs: - - build - - test - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - fetch-depth: 0 - - - name: Setup node - uses: actions/setup-node@v4 - with: - node-version: 18 - check-latest: true - cache: npm - - - name: Cache NPM dependencies - uses: actions/cache@v4 - with: - path: node_modules - key: npm-cache-ubuntu-latest-${{ hashFiles('package-lock.json') }} - - - name: Install dependencies - run: npm ci - - - name: Download artifact - uses: actions/download-artifact@v4 - with: - name: bindings-aarch64-apple-darwin - - - name: Move artifacts - run: | - mkdir -p dist - find . -type f -name 'cel-typescript.*.node' -exec mv {} . \; - - - name: List files - run: | - echo "Root directory:" - ls -la - echo "\nNode files:" - find . -maxdepth 1 -name '*.node' - shell: bash - - - name: Link local package - run: npm link - - - name: Build TypeScript - run: npm run build:ts - - - name: Verify package contents - run: | - echo "Package contents:" - npm pack --dry-run - + # Publishes the package to npm when a tag is pushed, or performs a dry-run during PRs. publish: name: ${{ github.event_name == 'pull_request' && '๐Ÿ” Publish (Dry Run)' || '๐Ÿ“ฆ Publish' }} runs-on: ubuntu-latest permissions: contents: read - id-token: write if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'pull_request' needs: - build @@ -193,50 +151,21 @@ jobs: submodules: recursive fetch-depth: 0 - - name: Debug - List submodules - run: | - git submodule status - ls -la - ls -la cel-rust/ - ls -la cel-rust/interpreter/ - shell: bash - - name: Setup node uses: actions/setup-node@v4 with: node-version: 18 - check-latest: true cache: npm - - - name: Cache NPM dependencies - uses: actions/cache@v4 - with: - path: node_modules - key: npm-cache-ubuntu-latest-${{ hashFiles('package-lock.json') }} + registry-url: 'https://registry.npmjs.org' - name: Install dependencies - run: npm ci + run: npm install - - name: Download artifact + - name: Download all artifacts uses: actions/download-artifact@v4 with: - name: bindings-aarch64-apple-darwin - - - name: Move artifacts - run: | - mkdir -p dist - find . -type f -name 'cel-typescript.*.node' -exec mv {} . \; - - - name: List files - run: | - echo "Root directory:" - ls -la - echo "\nNode files:" - find . -maxdepth 1 -name '*.node' - shell: bash - - - name: Link local package - run: npm link + pattern: bindings-* + merge-multiple: true - name: Build TypeScript run: npm run build:ts @@ -246,17 +175,12 @@ jobs: echo "Package contents:" npm pack --dry-run - - name: โš™๏ธ Configure npm - if: github.event_name != 'pull_request' - run: | - echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc - - - name: ๐Ÿš€ Publish - if: github.event_name != 'pull_request' && env.NPM_TOKEN != '' + - name: Publish + if: startsWith(github.ref, 'refs/tags/v') + run: npm publish --provenance --access public env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - run: npm publish --access public --provenance + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - name: ๐Ÿ” Dry Run Publish + - name: Publish (Dry Run) if: github.event_name == 'pull_request' run: npm publish --dry-run diff --git a/README.md b/README.md index c393dd4..fab342d 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,10 @@ npm install @kevinmichaelchen/cel-typescript - Node.js 18 or later -This package currently only includes pre-compiled native binaries for Apple Silicon (M1/M2/M3) Macs. Support for other platforms (Intel Macs, Linux, Windows) is planned for future releases and will be available behind a feature flag. +This package includes pre-compiled native binaries for the following platforms: +- macOS (Apple Silicon and Intel) +- Linux (x86_64 and ARM64) +- Windows (x86_64) The appropriate binary for your platform will be automatically loaded at runtime. diff --git a/package.json b/package.json index 68b42a5..c340ea2 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,11 @@ "triples": { "defaults": false, "additional": [ - "aarch64-apple-darwin" + "aarch64-apple-darwin", + "x86_64-apple-darwin", + "x86_64-unknown-linux-gnu", + "aarch64-unknown-linux-gnu", + "x86_64-pc-windows-msvc" ] } }, From 698c946c9edb737ed85c84491faafff1a17b8dd3 Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Mon, 28 Apr 2025 11:46:41 -0400 Subject: [PATCH 11/21] fix: validate exact number of .node files during publish --- .github/workflows/CI.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index b5c0a27..c5650d4 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -167,6 +167,23 @@ jobs: pattern: bindings-* merge-multiple: true + - name: Move artifacts + run: | + # Move .node files to root + find . -type f -name "*.node" -exec mv {} . \; + + # Count .node files + node_count=$(ls -1 *.node 2>/dev/null | wc -l) + expected_count=5 # We target 5 platforms + + echo "Found $node_count .node files (expected $expected_count)" + ls -la *.node + + if [ "$node_count" -ne "$expected_count" ]; then + echo "Error: Expected $expected_count .node files but found $node_count" + exit 1 + fi + - name: Build TypeScript run: npm run build:ts @@ -174,6 +191,8 @@ jobs: run: | echo "Package contents:" npm pack --dry-run + echo "\nVerifying .node files:" + tar -tvf *.tgz | grep "\.node" - name: Publish if: startsWith(github.ref, 'refs/tags/v') From ab8b7c1d249811973e69a3864f9f60930e00dabc Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Mon, 28 Apr 2025 12:01:04 -0400 Subject: [PATCH 12/21] fix: improve artifact handling and binary naming --- .github/workflows/CI.yml | 21 +++++++++++++-------- project.json | 1 + 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index c5650d4..b35736d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -169,18 +169,23 @@ jobs: - name: Move artifacts run: | - # Move .node files to root - find . -type f -name "*.node" -exec mv {} . \; + echo "Debug: Showing all downloaded artifacts:" + find . -type f -name "*.node" -ls - # Count .node files - node_count=$(ls -1 *.node 2>/dev/null | wc -l) - expected_count=5 # We target 5 platforms + echo "\nMoving only cel-typescript binaries to root:" + find . -type f -name "cel-typescript.*.node" -exec mv {} . \; + + echo "\nBinaries in root directory:" + ls -la cel-typescript.*.node - echo "Found $node_count .node files (expected $expected_count)" - ls -la *.node + # Count our binaries + node_count=$(ls -1 cel-typescript.*.node 2>/dev/null | wc -l) + expected_count=5 # We target 5 platforms + echo "\nFound $node_count cel-typescript binaries (expected $expected_count)" + if [ "$node_count" -ne "$expected_count" ]; then - echo "Error: Expected $expected_count .node files but found $node_count" + echo "Error: Expected $expected_count binaries but found $node_count" exit 1 fi diff --git a/project.json b/project.json index 0e0cc10..b8d066e 100644 --- a/project.json +++ b/project.json @@ -11,6 +11,7 @@ "{workspaceRoot}/cel-typescript.darwin-arm64.node", "{workspaceRoot}/cel-typescript.darwin-x64.node", "{workspaceRoot}/cel-typescript.linux-x64-gnu.node", + "{workspaceRoot}/cel-typescript.linux-arm64-gnu.node", "{workspaceRoot}/cel-typescript.win32-x64-msvc.node" ] }, From 10dd19655044f8e59071cc0a0eeebd8b9248b831 Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Mon, 28 Apr 2025 12:11:27 -0400 Subject: [PATCH 13/21] fix: use explicit target flag for napi-rs build --- .github/workflows/CI.yml | 2 +- project.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index b35736d..9c70fb3 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -74,7 +74,7 @@ jobs: shell: bash - name: Build - run: npx nx build:native -- --platform ${{ matrix.target }} + run: npx nx build:native -- --target ${{ matrix.target }} shell: bash - name: Upload artifact diff --git a/project.json b/project.json index b8d066e..a399534 100644 --- a/project.json +++ b/project.json @@ -5,7 +5,7 @@ "build:native": { "executor": "nx:run-commands", "options": { - "command": "npx @napi-rs/cli build --platform --release" + "command": "npx @napi-rs/cli build --target {args.target} --release" }, "outputs": [ "{workspaceRoot}/cel-typescript.darwin-arm64.node", From 0ce8f2fe15c3f51c550e69c06dbe54fb12150774 Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Mon, 28 Apr 2025 12:20:52 -0400 Subject: [PATCH 14/21] fix: use mlugg/setup-zig for cross-compilation --- .github/workflows/CI.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 9c70fb3..7481caf 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -53,6 +53,10 @@ jobs: toolchain: stable targets: ${{ matrix.target }} + - name: Install Zig + uses: mlugg/setup-zig@v1 + if: ${{ contains(matrix.target, 'aarch64') && runner.os != 'macOS' }} + - name: Cache NPM dependencies uses: actions/cache@v4 with: From 834b77580f76973197a8e2b615e88b1ecc8512ce Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Mon, 28 Apr 2025 12:29:30 -0400 Subject: [PATCH 15/21] fix: update build command and improve docs --- README.md | 33 +++++++++++++++------------------ project.json | 2 +- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index fab342d..62015ed 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,16 @@ # cel-typescript A TypeScript binding for the Common Expression Language (CEL) using -[cel-rust](https://github.com/clarkmcc/cel-rust). This project provides a +[cel-rust][cel-rust]. This project provides a Node.js native module that allows you to use CEL in your TypeScript/JavaScript projects. +[cel-spec]: https://github.com/google/cel-spec +[cel-rust]: https://github.com/clarkmcc/cel-rust + ## What is CEL? -[Common Expression Language (CEL)](https://github.com/google/cel-spec) is an +[Common Expression Language (CEL)][cel-spec] is an expression language created by Google that implements common semantics for expression evaluation. It's a simple language for expressing boolean conditions, calculations, and variable substitutions. CEL is used in various Google products @@ -20,17 +23,7 @@ business rule evaluation. npm install @kevinmichaelchen/cel-typescript ``` -**Requirements:** - -- Node.js 18 or later - -This package includes pre-compiled native binaries for the following platforms: -- macOS (Apple Silicon and Intel) -- Linux (x86_64 and ARM64) -- Windows (x86_64) - -The appropriate binary for your platform will be automatically loaded at -runtime. +Node.js 18 or later is required. ## Usage @@ -168,16 +161,20 @@ pre-compiled native binaries for all supported platforms: However, when you install this package, npm will only extract the `.node` file for your platform. For example: -- On an M1/M2 Mac, only `cel-typescript.darwin-arm64.node` (~7.4 MB) is used +- On Apple Silicon, only `cel-typescript.darwin-arm64.node` (~7.4 MB) is used - On Windows, only `cel-typescript.win32-x64.node` is used - On Linux, only `cel-typescript.linux-x64.node` or `cel-typescript.linux-arm64.node` is used -This is a common pattern for packages with native bindings. For comparison: +This is sometimes a pattern for packages with native bindings. For comparison: + +- [`better-sqlite3`][better-sqlite3]: 10.2 MB unpacked +- [`canvas`][canvas]: 408 kB unpacked +- [`sharp`][sharp]: 522 kB unpacked -- `sharp` (image processing): 39.7 MB unpacked -- `better-sqlite3`: 12.8 MB unpacked -- `canvas`: 8.9 MB unpacked +[better-sqlite3]: https://www.npmjs.com/package/better-sqlite3 +[canvas]: https://www.npmjs.com/package/canvas +[sharp]: https://www.npmjs.com/package/sharp #### A Note on Tree-Shaking diff --git a/project.json b/project.json index a399534..e902478 100644 --- a/project.json +++ b/project.json @@ -5,7 +5,7 @@ "build:native": { "executor": "nx:run-commands", "options": { - "command": "npx @napi-rs/cli build --target {args.target} --release" + "command": "npx @napi-rs/cli build --platform --target {args.target} --release --zig" }, "outputs": [ "{workspaceRoot}/cel-typescript.darwin-arm64.node", From ad82ea2afaacc12410aaf8a2aad2b6b7e5148aeb Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Mon, 28 Apr 2025 12:31:12 -0400 Subject: [PATCH 16/21] fix: install zig for all ARM64 builds --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 7481caf..4bb1111 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -55,7 +55,7 @@ jobs: - name: Install Zig uses: mlugg/setup-zig@v1 - if: ${{ contains(matrix.target, 'aarch64') && runner.os != 'macOS' }} + if: ${{ contains(matrix.target, 'aarch64') }} - name: Cache NPM dependencies uses: actions/cache@v4 From 63db4d17aa45c15b2b4aca928289b2a9d9a220a9 Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Mon, 28 Apr 2025 12:32:28 -0400 Subject: [PATCH 17/21] fix: install zig for all builds --- .github/workflows/CI.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 4bb1111..d725023 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -55,7 +55,6 @@ jobs: - name: Install Zig uses: mlugg/setup-zig@v1 - if: ${{ contains(matrix.target, 'aarch64') }} - name: Cache NPM dependencies uses: actions/cache@v4 From 59be1fb583b1b17211a701976a54b31e1985b579 Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Mon, 28 Apr 2025 12:39:05 -0400 Subject: [PATCH 18/21] fix: only use zig for linux builds --- .github/workflows/CI.yml | 3 +++ project.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index d725023..97bf21e 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -55,6 +55,7 @@ jobs: - name: Install Zig uses: mlugg/setup-zig@v1 + if: ${{ runner.os == 'Linux' }} - name: Cache NPM dependencies uses: actions/cache@v4 @@ -78,6 +79,8 @@ jobs: - name: Build run: npx nx build:native -- --target ${{ matrix.target }} + env: + USE_ZIG: ${{ runner.os == 'Linux' }} shell: bash - name: Upload artifact diff --git a/project.json b/project.json index e902478..cec0653 100644 --- a/project.json +++ b/project.json @@ -5,7 +5,7 @@ "build:native": { "executor": "nx:run-commands", "options": { - "command": "npx @napi-rs/cli build --platform --target {args.target} --release --zig" + "command": "npx @napi-rs/cli build --platform --target {args.target} --release ${USE_ZIG:+--zig}" }, "outputs": [ "{workspaceRoot}/cel-typescript.darwin-arm64.node", From 90027ee286aece7587b318f053de746f6ae49d7f Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Mon, 28 Apr 2025 12:44:24 -0400 Subject: [PATCH 19/21] fix: use shell script for conditional zig usage --- project.json | 2 +- scripts/build.sh | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100755 scripts/build.sh diff --git a/project.json b/project.json index cec0653..04d8380 100644 --- a/project.json +++ b/project.json @@ -5,7 +5,7 @@ "build:native": { "executor": "nx:run-commands", "options": { - "command": "npx @napi-rs/cli build --platform --target {args.target} --release ${USE_ZIG:+--zig}" + "command": "bash scripts/build.sh {args.target}" }, "outputs": [ "{workspaceRoot}/cel-typescript.darwin-arm64.node", diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..a779dea --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +if [ "$USE_ZIG" = "true" ]; then + npx @napi-rs/cli build --platform --target "$1" --release --zig +else + npx @napi-rs/cli build --platform --target "$1" --release +fi From 20c614f2b11173f103e89a7b37a821626a7d13c9 Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Mon, 28 Apr 2025 14:11:41 -0400 Subject: [PATCH 20/21] test: more unit tests --- __tests__/cel.test.ts | 265 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 262 insertions(+), 3 deletions(-) diff --git a/__tests__/cel.test.ts b/__tests__/cel.test.ts index 0360bc2..b994987 100644 --- a/__tests__/cel.test.ts +++ b/__tests__/cel.test.ts @@ -152,11 +152,13 @@ describe("Performance measurements", () => { // Allow more variation due to system noise, optimization differences, and convenience overhead const tolerance = 1.0; // Allow 100% variation const expectedEvaluateTime = compileTime + executeTime; - + // Log the actual ratios to help with debugging const ratio = evaluateTime / expectedEvaluateTime; - console.log(`One-step evaluation was ${ratio.toFixed(2)}x the separate steps`); - + console.log( + `One-step evaluation was ${ratio.toFixed(2)}x the separate steps`, + ); + // Only fail if the difference is extreme expect(evaluateTime).toBeGreaterThan( expectedEvaluateTime * (1 - tolerance), @@ -164,3 +166,260 @@ describe("Performance measurements", () => { expect(evaluateTime).toBeLessThan(expectedEvaluateTime * (2 + tolerance)); }); }); + +describe("Timestamp Functions", () => { + it("should correctly parse and compare timestamps", async () => { + const expr = `[ + // Test 1: Compare UTC timestamp with itself + timestamp('2025-04-28T12:00:00Z') == timestamp('2025-04-28T12:00:00Z'), + // Test 2: Compare UTC timestamp with equivalent EDT timestamp + timestamp('2025-04-28T12:00:00Z') == timestamp('2025-04-28T08:00:00-04:00'), + // Test 3: Compare timestamps one day apart + timestamp('2025-04-28T12:00:00Z') < timestamp('2025-04-29T12:00:00Z'), + // Test 4: Get components from UTC timestamp + timestamp('2025-04-28T12:00:00Z').getMonth(), + timestamp('2025-04-28T12:00:00Z').getDayOfMonth(), + timestamp('2025-04-28T12:00:00Z').getHours(), + timestamp('2025-04-28T12:00:00Z').getDayOfWeek() // 1==Monday + ]`; + const result = await evaluate(expr, {}); + expect(result).toEqual([true, true, true, 3, 27, 12, 1]); + }); +}); + +describe("Travel Reservation Rules", () => { + describe("Premium Discount Eligibility", () => { + // Tests a complex pricing rule that considers: + // - Total package cost across flight, hotel, and car + // - Customer loyalty tier + // - Seasonal booking (summer months) + const expr = ` + // Calculate total package cost + double(reservation.flight.price) + + double(reservation.hotel.nightlyRate) * int(reservation.hotel.nights) + + double(reservation.car.dailyRate) * int(reservation.car.days) >= 2000.0 && + // Check loyalty tier + reservation.customer.loyaltyTier in ['GOLD', 'PLATINUM'] && + // Check if booking is in summer months (0-based: 5=June, 6=July, 7=August) + timestamp(reservation.bookingDate).getMonth() in [5, 6, 7] + `; + let program: CelProgram; + + beforeEach(async () => { + program = await CelProgram.compile(expr); + }); + + it("should qualify for premium discount with valid summer booking", async () => { + const result = await program.execute({ + reservation: { + flight: { price: 1000.0 }, + hotel: { nightlyRate: 200.0, nights: 4 }, + car: { dailyRate: 100.0, days: 4 }, + customer: { loyaltyTier: "PLATINUM" }, + bookingDate: "2025-07-15T00:00:00Z", + }, + }); + expect(result).toBe(true); + }); + + it("should not qualify outside summer months", async () => { + const result = await program.execute({ + reservation: { + flight: { price: 1000.0 }, + hotel: { nightlyRate: 200.0, nights: 4 }, + car: { dailyRate: 100.0, days: 4 }, + customer: { loyaltyTier: "PLATINUM" }, + bookingDate: "2025-12-15T00:00:00Z", + }, + }); + expect(result).toBe(false); + program = await CelProgram.compile(expr); + }); + }); + + describe("Multi-condition Booking Validation", () => { + // Tests complex booking validation rules that ensure: + // - All required components are present + // - Logical time sequence of events + // - Location consistency + // - Capacity constraints + const expr = ` + has(reservation.flight) && + timestamp(reservation.flight.departureTime) < timestamp(reservation.hotel.checkIn) && + timestamp(reservation.hotel.checkIn) < timestamp(reservation.hotel.checkOut) && + (timestamp(reservation.hotel.checkOut) - timestamp(reservation.hotel.checkIn)) > duration("1h") && + timestamp(reservation.hotel.checkOut) < timestamp(reservation.flight.returnTime) && + (reservation.car.pickupLocation == reservation.flight.arrivalAirport || + reservation.car.pickupLocation == reservation.hotel.address.city) && + size(reservation.travelers) <= reservation.hotel.maxOccupancy && + size(reservation.travelers) <= reservation.car.capacity + `; + let program: CelProgram; + + beforeEach(async () => { + program = await CelProgram.compile(expr); + }); + + it("should validate a well-formed booking", async () => { + const result = await program.execute({ + reservation: { + flight: { + departureTime: "2025-05-01T10:00:00Z", + returnTime: "2025-05-05T15:00:00Z", + arrivalAirport: "LAX", + }, + hotel: { + checkIn: "2025-05-01T15:00:00Z", + checkOut: "2025-05-05T11:00:00Z", + maxOccupancy: 4, + address: { city: "Los Angeles" }, + }, + car: { + pickupLocation: "LAX", + capacity: 5, + }, + travelers: ["person1", "person2", "person3"], + }, + }); + expect(result).toBe(true); + }); + + it("should reject invalid time sequence", async () => { + const result = await program.execute({ + reservation: { + flight: { + departureTime: "2025-05-01T16:00:00Z", // Later than check-in + returnTime: "2025-05-05T15:00:00Z", + arrivalAirport: "LAX", + }, + hotel: { + checkIn: "2025-05-01T15:00:00Z", + checkOut: "2025-05-05T11:00:00Z", + maxOccupancy: 4, + address: { city: "Los Angeles" }, + }, + car: { + pickupLocation: "LAX", + capacity: 5, + }, + travelers: ["person1", "person2", "person3"], + }, + }); + expect(result).toBe(false); + }); + }); + + describe.skip("Dynamic Pricing with Seasonal Adjustments", () => { + // Tests complex pricing calculations including: + // - Base rates for all components + // - Seasonal multipliers + // - Loyalty discounts + const expr = ` + let basePrice = double(reservation.flight.price) + + double(reservation.hotel.nightlyRate) * int(reservation.hotel.nights) + + double(reservation.car.dailyRate) * int(reservation.car.days); + let seasonalPrice = basePrice * (timestamp(reservation.hotel.checkIn).getMonth() in [11, 0, 1] ? 1.25 : 1.0); + seasonalPrice * (1.0 - {'BRONZE': 0.05, 'SILVER': 0.10, 'GOLD': 0.15, 'PLATINUM': 0.20}[reservation.customer.loyaltyTier]) + `; + let program: CelProgram; + + beforeEach(async () => { + program = await CelProgram.compile(expr); + }); + + it("should calculate winter pricing with loyalty discount", async () => { + const result = await program.execute({ + reservation: { + flight: { price: 1000.0 }, + hotel: { + nightlyRate: 200.0, + nights: 4, + checkIn: "2025-01-15T15:00:00Z", // January + }, + car: { dailyRate: 100.0, days: 4 }, + customer: { loyaltyTier: "GOLD" }, + }, + }); + // Base: 1000 + (200 * 4) + (100 * 4) = 2200 + // Winter multiplier: 2200 * 1.25 = 2750 + // Gold discount (15%): 2750 * 0.85 = 2337.5 + expect(result).toBe(2337.5); + }); + + it("should calculate summer pricing with loyalty discount", async () => { + const result = await program.execute({ + reservation: { + flight: { price: 1000.0 }, + hotel: { + nightlyRate: 200.0, + nights: 4, + checkIn: "2025-07-15T15:00:00Z", // July + }, + car: { dailyRate: 100.0, days: 4 }, + customer: { loyaltyTier: "PLATINUM" }, + }, + }); + // Base: 1000 + (200 * 4) + (100 * 4) = 2200 + // No seasonal multiplier + // Platinum discount (20%): 2200 * 0.80 = 1760 + expect(result).toBe(1760.0); + }); + }); + + describe("Room Upgrade Eligibility", () => { + // Tests complex upgrade eligibility rules considering: + // - Customer loyalty tier + // - Stay duration + // - Hotel occupancy + // - Existing offers + // - Total spend + // - Current booking class + const expr = ` + reservation.customer.loyaltyTier in ['GOLD', 'PLATINUM'] && + reservation.hotel.nights >= 3 && + reservation.hotel.occupancyRate < 0.80 && + !(reservation.specialOffers.exists(o, o.type == 'ROOM_UPGRADE')) && + reservation.totalSpend > 5000.0 && + [reservation.flight.class, reservation.hotel.roomType].all(t, t != 'ECONOMY') + `; + let program: CelProgram; + + beforeEach(async () => { + program = await CelProgram.compile(expr); + }); + + it("should qualify eligible platinum member for upgrade", async () => { + const result = await program.execute({ + reservation: { + customer: { loyaltyTier: "PLATINUM" }, + hotel: { + nights: 4, + occupancyRate: 0.7, + roomType: "DELUXE", + }, + flight: { class: "BUSINESS" }, + specialOffers: [], + totalSpend: 6000.0, + }, + }); + expect(result).toBe(true); + }); + + it("should reject upgrade when conditions not met", async () => { + const result = await program.execute({ + reservation: { + customer: { loyaltyTier: "PLATINUM" }, + hotel: { + nights: 4, + occupancyRate: 0.85, // Too high occupancy + roomType: "DELUXE", + }, + flight: { class: "BUSINESS" }, + specialOffers: [], + totalSpend: 6000.0, + }, + }); + expect(result).toBe(false); + }); + }); +}); From e677a80a1c4267c242740dd37569738573b5882a Mon Sep 17 00:00:00 2001 From: Kevin Chen Date: Mon, 28 Apr 2025 14:14:06 -0400 Subject: [PATCH 21/21] chore: Formatting --- .github/workflows/CI.yml | 2 -- biome.json | 3 ++- package.json | 7 +------ src/index.ts | 4 +++- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 97bf21e..d0f55e9 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -202,8 +202,6 @@ jobs: run: | echo "Package contents:" npm pack --dry-run - echo "\nVerifying .node files:" - tar -tvf *.tgz | grep "\.node" - name: Publish if: startsWith(github.ref, 'refs/tags/v') diff --git a/biome.json b/biome.json index f5413f3..d7d0524 100644 --- a/biome.json +++ b/biome.json @@ -11,7 +11,8 @@ "enabled": true, "indentStyle": "space", "indentWidth": 2, - "lineWidth": 80 + "lineWidth": 80, + "ignore": ["index.js"] }, "linter": { "enabled": true, diff --git a/package.json b/package.json index c340ea2..d2d3544 100644 --- a/package.json +++ b/package.json @@ -13,12 +13,7 @@ "url": "https://github.com/kevinmichaelchen/cel-typescript/issues" }, "homepage": "https://github.com/kevinmichaelchen/cel-typescript#readme", - "files": [ - "dist/**/*", - "*.node", - "index.js", - "index.d.ts" - ], + "files": ["dist/**/*", "*.node", "index.js", "index.d.ts"], "keywords": [ "cel", "common-expression-language", diff --git a/src/index.ts b/src/index.ts index 49456f7..baf68e4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,7 +33,9 @@ class CelProgram { static async compile(source: string): Promise { if (!CelProgram.nativeModule) { // Use the NAPI-RS generated loader which handles platform detection - const nativeBinding = await import("@kevinmichaelchen/cel-typescript/native"); + const nativeBinding = await import( + "@kevinmichaelchen/cel-typescript/native" + ); CelProgram.nativeModule = nativeBinding.CelProgram; console.log("Imported native CelProgram:", CelProgram.nativeModule); }