Skip to content
Merged
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
8 changes: 8 additions & 0 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changesets

Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)

We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
11 changes: 11 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
26 changes: 2 additions & 24 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,5 @@ on:

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 10

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

- run: pnpm install --frozen-lockfile

- run: npx tsc --noEmit

- run: pnpm run lint

- run: pnpm test

- run: npx publint

- run: npm pack --dry-run
name: Build, Lint & Test
uses: ./.github/workflows/reusable-ci.yml
31 changes: 22 additions & 9 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
name: Publish

on:
release:
types: [published]
push:
branches: [main]

permissions:
contents: read
concurrency: ${{ github.workflow }}-${{ github.ref }}

jobs:
publish:
name: Publish to npm
validate:
name: Validate
uses: ./.github/workflows/reusable-ci.yml

release:
name: Release
needs: validate
runs-on: ubuntu-latest
environment: production

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restrict production approval to the publish path

When a normal PR carrying a changeset is merged, this job must open or update the Version Packages PR, but GitHub applies required-reviewer environment protection before starting any step in the job. Putting environment: production here therefore makes PR creation and updates wait for production approval as well; approval cannot be limited to the changeset publish branch inside changesets/action, so routine merges no longer drive the advertised Changesets flow automatically. Split PR maintenance from the environment-protected publishing path.

AGENTS.md reference: AGENTS.md:L15-L15

Useful? React with 👍 / 👎.

permissions:
contents: read
contents: write
pull-requests: write
id-token: write # npm OIDC trusted publishing - no NPM_TOKEN secret needed
steps:
- uses: actions/checkout@v4
Expand All @@ -36,5 +42,12 @@ jobs:
- name: Build
run: npx tsc

- name: Publish to npm
run: npm publish --provenance --access public
- name: Create Release Pull Request or Publish to npm
uses: changesets/action@v1
with:
publish: pnpm run ci:publish
commit: "chore: version packages"
title: "chore: version packages"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_CONFIG_PROVENANCE: true
32 changes: 32 additions & 0 deletions .github/workflows/reusable-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Reusable CI

on:
workflow_call:

jobs:
build:
name: Build, Lint & Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 10

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

- run: pnpm install --frozen-lockfile

- run: npx tsc --noEmit

- run: pnpm run lint

- run: pnpm test

- run: npx publint

- run: npm pack --dry-run
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ This file is the project's committed home for project-intrinsic agent knowledge:
- For `DOMAIN_VEHICLE_SECURITY`, `Commands.dispatch` holds the domain's session lock across the *entire* dispatch (handshake + build + send + retry), not just message-build - VCSEC requires messages to arrive in strict counter order and the spec warns against simultaneous requests to it at all, unlike Infotainment (sliding window), which keeps the narrower build-only lock.
- `Commands`'s `#privateKey`/`#publicKey` are native private class fields (not `protected`/TS-only `private`) - the raw signing key must not be reachable off the instance at all (e.g. via `JSON.stringify` or a structured log), not merely inaccessible to outside *code*. Tests that need to observe key derivation do so through what actually goes out on the wire (a captured handshake message), not by reading the field.
- `src/tariff.ts`'s `getTariffPeriods(tariff, now, opts)` is a pure Tariff V2 rate resolver mirroring `python-tesla-fleet-api`'s sibling. The tariff object carries no timezone; the caller must pass `opts.timeZone` (an IANA string, e.g. from `site_info.installation_time_zone`) - the resolver has no other way to get site-local wall-clock parts from a JS `Date`, which is always a UTC instant. It resolves one calendar day at a time (`dayPeriods`), not a multi-day minute-of-week span, because a `tou_periods` entry's `fromDayOfWeek..toDayOfWeek` means "this daily time window recurs on each of these weekdays", not "one span from this day+time to that day+time"; `nextChange`/`upcoming` re-resolve the season fresh on each day so a horizon crossing a season boundary re-prices correctly, and a gap between scheduled periods reports the gap (not the next period found arbitrarily far out). Converting a resolved wall-clock boundary back to a `Date` goes through `wallClockToUtcMillis` (iterative, since the zone's UTC offset at the target instant is what's being solved for) rather than adding elapsed real minutes to `now` - the two diverge across a DST transition. The reverse direction - "what calendar day/weekday is N minutes of wall-clock time from now" (used to walk forward day by day, or peek at tomorrow) - must go through `wallClockAt` (pure calendar arithmetic, no `Intl` round trip), never `now.getTime() + minutes*60000`; the latter silently lands on the wrong calendar day on a DST fall-back day (25 real hours) or spring-forward day (23). Buy and sell resolve independently via `scheduleAt`, which reports both a `nextChangeGM` and a `sinceGM` even for a grid currently sitting in a gap (e.g. a sell/export window not open yet, or already closed) - `nextChange` (earlier of the two) and `currentStart` (later of the two) must fold in the sell side even when sell has no period active right now, or a differently-scheduled sell tariff gets silently ignored or backdated. Both are nullable (bounded to one day of lookahead/lookback, mirroring each other) and must be excluded from the combination via `!= null`, not treated as `0`, when a grid has nothing scheduled in that window at all. `TariffContentV2` (`src/types/site_info.ts`) types `seasons` as `Record<string, Season>` (an object keyed by season name, not an array) - that mismatch was a real bug fixed alongside the resolver; don't regress it back to an array shape.
- `.github/workflows/publish.yml` publishes to npm on GitHub Release `published` (matching this repo's own pre-2024 convention, restored) via npm trusted publishing (OIDC) - `id-token: write`, no `NPM_TOKEN` secret. It builds with `npx tsc` before `npm publish --provenance`, so a version bump landing on `main` still ships nothing by itself; publishing happens only when a GitHub Release is cut for that tag, and only once trusted publishing is enabled on the npm package side for this repo/workflow. Before assuming a version is live, check `npm view tesla-fleet-api@<version>` rather than trusting `package.json`. `npm publish --provenance` also requires `package.json`'s `repository.url` to resolve to this exact GitHub repo (`https://github.com/Teslemetry/node-tesla-fleet-api`) - sigstore provenance verification checks it against the GitHub Actions run's own repo and fails the publish (`E422`) if it's missing or mismatched.
- Releases go through Changesets, not hand-cut GitHub Releases: a PR that changes behavior includes a changeset (`pnpm changeset`); merging to `main` runs `.github/workflows/publish.yml`, whose `validate` job re-runs the full `reusable-ci.yml` suite (lint/typecheck/test/publint/pack) against the exact SHA being released, and whose `release` job (`needs: validate`) then either opens/updates a "Version Packages" PR or, if one was just merged, publishes via `changeset publish` (`pnpm run ci:publish`). `ci.yml` (PR/push) and `publish.yml`'s `validate` job both call the same `reusable-ci.yml` - keep checks there, not duplicated inline, and keep `needs: validate` so a separate/stale CI run can never be trusted in place of the one on the SHA being published. `release` runs in the `production` GitHub environment, which requires reviewer approval before `changeset publish` executes - merging the version PR only prepares the release; approving the environment deployment publishes it. Publishing uses npm trusted publishing (OIDC, `id-token: write`, no `NPM_TOKEN` secret) - this must be configured per-package on npmjs.com (package Settings → Trusted Publisher, github-actions provider, matching repo + `.github/workflows/publish.yml`); before assuming a version is live, check `npm view tesla-fleet-api@<version>` rather than trusting `package.json`. `npm publish --provenance` also requires `package.json`'s `repository.url` to resolve to this exact GitHub repo (`https://github.com/Teslemetry/node-tesla-fleet-api`) - sigstore provenance verification checks it against the GitHub Actions run's own repo and fails the publish (`E422`) if it's missing or mismatched.

## Maintaining this file

Expand Down
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"url": "https://teslemetry.com"
},
"devDependencies": {
"@changesets/cli": "^2.31.1",
"@eslint/js": "^10.0.1",
"@types/node": "^24.13.3",
"eslint": "^10.8.0",
Expand All @@ -26,7 +27,13 @@
"test": "vitest run",
"test:watch": "vitest",
"lint": "eslint .",
"pack:check": "publint"
"pack:check": "publint",
"changeset": "changeset",
"ci:publish": "changeset publish"
},
"publishConfig": {
"access": "public",
"provenance": true
},
"files": [
"dist",
Expand Down
Loading
Loading