diff --git a/.cursor/rules/README.md b/.cursor/rules/README.md deleted file mode 100644 index f5c1f87..0000000 --- a/.cursor/rules/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Cursor (optional) - -**Cursor** users: start at **[AGENTS.md](../../AGENTS.md)**. All conventions live in **`skills/*/SKILL.md`**. - -This folder only points contributors to **`AGENTS.md`** so editor-specific config does not duplicate the canonical docs. diff --git a/.github/workflows/back-merge-pr.yml b/.github/workflows/back-merge-pr.yml deleted file mode 100644 index 02b378c..0000000 --- a/.github/workflows/back-merge-pr.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Back-merge master to development - -on: - push: - branches: - - master - workflow_dispatch: - -permissions: - contents: read - pull-requests: write - -jobs: - open-back-merge-pr: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Open back-merge PR if needed - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - BASE_BRANCH="development" - SOURCE_BRANCH="master" - - git fetch origin "$BASE_BRANCH" "$SOURCE_BRANCH" - - if ! git show-ref --verify --quiet "refs/remotes/origin/$BASE_BRANCH"; then - echo "Base branch '$BASE_BRANCH' does not exist on origin; skipping." - exit 0 - fi - - SOURCE_SHA=$(git rev-parse "origin/$SOURCE_BRANCH") - BASE_SHA=$(git rev-parse "origin/$BASE_BRANCH") - - if [ "$SOURCE_SHA" = "$BASE_SHA" ]; then - echo "$SOURCE_BRANCH and $BASE_BRANCH are at the same commit; nothing to back-merge." - exit 0 - fi - - EXISTING=$(gh pr list --repo "${{ github.repository }}" --base "$BASE_BRANCH" --head "$SOURCE_BRANCH" --state open --json number --jq 'length') - - if [ "$EXISTING" -gt 0 ]; then - echo "An open PR from $SOURCE_BRANCH to $BASE_BRANCH already exists; skipping." - exit 0 - fi - - gh pr create --repo "${{ github.repository }}" --base "$BASE_BRANCH" --head "$SOURCE_BRANCH" --title "chore: back-merge $SOURCE_BRANCH into $BASE_BRANCH" --body "Automated back-merge after changes landed on \\`$SOURCE_BRANCH\\`. Review and merge to keep \\`$BASE_BRANCH\\` in sync." - - echo "Created back-merge PR $SOURCE_BRANCH -> $BASE_BRANCH." diff --git a/.github/workflows/check-version-bump.yml b/.github/workflows/check-version-bump.yml deleted file mode 100644 index 31bdf66..0000000 --- a/.github/workflows/check-version-bump.yml +++ /dev/null @@ -1,79 +0,0 @@ -# Catches when developers forget to bump package.json for release-affecting changes. -# App code changes (app.js, bin/, config/, routes/, views/, etc.) require a version bump vs latest tag. -# Skips for: test-only, docs, .github (workflows/config), dependency-only bumps without app edits. -name: Check Version Bump - -on: - pull_request: - -jobs: - version-bump: - name: Version bump - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Detect changed files and version bump - id: detect - run: | - if git rev-parse HEAD^2 >/dev/null 2>&1; then - FILES=$(git diff --name-only HEAD^1 HEAD^2) - else - FILES=$(git diff --name-only HEAD~1 HEAD) - fi - VERSION_FILES_CHANGED=false - echo "$FILES" | grep -qx 'package.json' && VERSION_FILES_CHANGED=true - echo "version_files_changed=$VERSION_FILES_CHANGED" >> $GITHUB_OUTPUT - # App source paths for this boilerplate (no lib/webpack/dist); .github/ and test/ do not count - CODE_CHANGED=false - echo "$FILES" | grep -qE '^app\.js$|^bin/|^config/|^middlewares/|^models/|^public/|^routes/|^views/|^schemaNentries/' && CODE_CHANGED=true - echo "$FILES" | grep -qx 'package.json' && CODE_CHANGED=true - echo "code_changed=$CODE_CHANGED" >> $GITHUB_OUTPUT - - - name: Skip when only test/docs/.github changed - if: steps.detect.outputs.code_changed != 'true' - run: | - echo "No release-affecting files changed (e.g. only test/docs/.github). Skipping version-bump check." - exit 0 - - - name: Fail when version bump was missed - if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_changed != 'true' - run: | - echo "::error::This PR has code changes but no version bump. Please bump the version in package.json." - exit 1 - - - name: Setup Node - if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_changed == 'true' - uses: actions/setup-node@v4 - with: - node-version: '22.x' - - - name: Check version bump - if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_changed == 'true' - run: | - set -e - PKG_VERSION=$(node -p "require('./package.json').version.replace(/^v/, '')") - if [ -z "$PKG_VERSION" ]; then - echo "::error::Could not read version from package.json" - exit 1 - fi - git fetch --tags --force 2>/dev/null || true - LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || true) - if [ -z "$LATEST_TAG" ]; then - echo "No existing tags found. Skipping version-bump check (first release)." - exit 0 - fi - LATEST_VERSION="${LATEST_TAG#v}" - LATEST_VERSION="${LATEST_VERSION%%-*}" - if [ "$(printf '%s\n' "$LATEST_VERSION" "$PKG_VERSION" | sort -V | tail -1)" != "$PKG_VERSION" ]; then - echo "::error::Version bump required: package.json version ($PKG_VERSION) is not greater than latest tag ($LATEST_TAG). Please bump the version in package.json." - exit 1 - fi - if [ "$PKG_VERSION" = "$LATEST_VERSION" ]; then - echo "::error::Version bump required: package.json version ($PKG_VERSION) equals latest tag ($LATEST_TAG). Please bump the version in package.json." - exit 1 - fi - echo "Version bump check passed: package.json is at $PKG_VERSION (latest tag: $LATEST_TAG)." diff --git a/.github/workflows/issues-jira.yml b/.github/workflows/issues-jira.yml new file mode 100644 index 0000000..7bf0469 --- /dev/null +++ b/.github/workflows/issues-jira.yml @@ -0,0 +1,31 @@ +name: Create Jira Ticket for Github Issue + +on: + issues: + types: [opened] + +jobs: + issue-jira: + runs-on: ubuntu-latest + steps: + + - name: Login to Jira + uses: atlassian/gajira-login@master + env: + JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} + JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} + JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} + + - name: Create Jira Issue + id: create_jira + uses: atlassian/gajira-create@master + with: + project: ${{ secrets.JIRA_PROJECT }} + issuetype: ${{ secrets.JIRA_ISSUE_TYPE }} + summary: Github | Issue | ${{ github.event.repository.name }} | ${{ github.event.issue.title }} + description: | + *GitHub Issue:* ${{ github.event.issue.html_url }} + + *Description:* + ${{ github.event.issue.body }} + fields: "${{ secrets.ISSUES_JIRA_FIELDS }}" \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 438078c..9ed98f5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,17 +1,17 @@ name: Release on: - release: - types: [created] + push: + branches: [master] jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: "18.x" + node-version: "22.x" - run: npm install - name: get-package-details diff --git a/.github/workflows/sca-scan.yml b/.github/workflows/sca-scan.yml index 79b6557..f09161f 100644 --- a/.github/workflows/sca-scan.yml +++ b/.github/workflows/sca-scan.yml @@ -5,9 +5,6 @@ on: jobs: security-sca: runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write steps: - uses: actions/checkout@master - name: Run Snyk to check for vulnerabilities @@ -16,6 +13,3 @@ jobs: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} with: args: --all-projects --fail-on=all - json: true - continue-on-error: true - - uses: contentstack/sca-policy@main diff --git a/.husky/post-checkout b/.husky/post-checkout deleted file mode 100755 index 302fdc6..0000000 --- a/.husky/post-checkout +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env sh -# When switching to a branch that doesn't exist on remote (e.g. newly created), -# pull and merge origin/main or origin/master into current branch. Does not push. - -# Only run on branch checkout (not file checkout) -if [ "$3" != "1" ]; then - exit 0 -fi - -# Skip if we don't have a remote -if ! git rev-parse --verify origin 2>/dev/null; then - exit 0 -fi - -CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) - -# Skip main/master - no need to merge base into these -case "$CURRENT_BRANCH" in - main|master) exit 0 ;; -esac - -# Only run when current branch does not exist on origin (treat as new local branch) -if git ls-remote --heads origin "$CURRENT_BRANCH" 2>/dev/null | grep -q .; then - echo "post-checkout: $CURRENT_BRANCH exists on origin, skipping merge." - exit 0 -fi - -# Prefer main, fallback to master -if git rev-parse --verify origin/main 2>/dev/null; then - BASE=origin/main -elif git rev-parse --verify origin/master 2>/dev/null; then - BASE=origin/master -else - exit 0 -fi - -echo "New branch detected: merging latest $BASE into $CURRENT_BRANCH (local only, not pushing)..." -git fetch origin -git merge "$BASE" --no-edit --no-ff -echo "Done. Merge is local only; push when ready." diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 2dafdca..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,48 +0,0 @@ -# Contentstack DataSync Node.js Website Boilerplate – Agent guide - -**Universal entry point** for contributors and AI agents. Detailed conventions live in **`skills/*/SKILL.md`**. - -## What this repo is - -| Field | Detail | -|--------|--------| -| **Name:** | [contentstack/datasync-nodejs-website-boilerplate](https://github.com/contentstack/datasync-nodejs-website-boilerplate) | -| **Purpose:** | Express app that serves a site from Contentstack DataSync–backed content (filesystem or MongoDB SDK), with Nunjucks views and synced data under the configured content store. | -| **Out of scope (if any):** | Not the DataSync sync pipeline itself (webhook listener, manager); this repo is the website layer on top of synced data. | - -## Tech stack (at a glance) - -| Area | Details | -|------|---------| -| Language | JavaScript (Node.js), CommonJS. Conventions: `skills/javascript-style/SKILL.md`. | -| Build | No compile step; run from source. Install dependencies with npm. | -| Tests | No automated test script in `package.json` today. | -| Lint / coverage | ESLint 5 with `.eslintrc.js` (strict rule set). No coverage tool configured. | -| Other | Express 5, Nunjucks, `@contentstack/datasync-filesystem-sdk` / `@contentstack/datasync-mongodb-sdk`, `contentstack` npm package. Optional: Husky hooks (Snyk, Talisman). | - -## Commands (quick reference) - -| Command type | Command | -|--------------|---------| -| Install deps | `npm install` | -| Run locally | `npm start` (uses `bin/www`; `NODE_ENV` selects `config/.js`, default `development`) | -| Lint | `npx eslint .` | -| Husky setup | `npm run pre-commit` | - -CI and automation: [.github/workflows/check-version-bump.yml](.github/workflows/check-version-bump.yml), [.github/workflows/sca-scan.yml](.github/workflows/sca-scan.yml), [.github/workflows/policy-scan.yml](.github/workflows/policy-scan.yml), [.github/workflows/codeql-analysis.yml](.github/workflows/codeql-analysis.yml), [.github/workflows/issues-jira.yml](.github/workflows/issues-jira.yml). - -## Where the documentation lives: skills - -| Skill | Path | What it covers | -|-------|------|----------------| -| Development workflow | `skills/dev-workflow/SKILL.md` | Install, run, hooks, CI expectations, version bumps on PRs | -| JavaScript style | `skills/javascript-style/SKILL.md` | CommonJS patterns, ESLint, matching existing file style | -| DataSync website app | `skills/datasync-website/SKILL.md` | Express entry points, Stack/content store, config and routes | -| Testing | `skills/testing/SKILL.md` | Current test posture; adding tests safely | -| Code review | `skills/code-review/SKILL.md` | PR checklist aligned with this repo | - -An index with “when to use” hints is in `skills/README.md`. - -## Using Cursor (optional) - -If you use **Cursor**, `.cursor/rules/README.md` only points to **`AGENTS.md`**—same docs as everyone else. diff --git a/package-lock.json b/package-lock.json index 4bf1231..3646f1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,23 +1,24 @@ { "name": "datasync-nodejs-sdk-boilerplate", - "version": "0.0.13", + "version": "0.0.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "datasync-nodejs-sdk-boilerplate", - "version": "0.0.13", + "version": "0.0.9", "license": "MIT", "dependencies": { - "@contentstack/datasync-filesystem-sdk": "1.5.3", - "@contentstack/datasync-mongodb-sdk": "^1.0.15", + "@contentstack/datasync-filesystem-sdk": "^1.5.1", + "@contentstack/datasync-mongodb-sdk": "^1.0.12", "async": "^3.2.6", - "contentstack": "^3.27.0", + "chokidar": "^5.0.0", + "contentstack": "^3.24.1", "debug": "~3.2.7", "express": "^5.2.1", - "express-rate-limit": "^7.5.1", + "express-rate-limit": "^7.1.5", "http-errors": "~2.0.0", - "morgan": "~1.11.0", + "morgan": "~1.10.1", "nunjucks": "^3.2.4", "path": "^0.12.7" }, @@ -27,13 +28,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -42,9 +43,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", "engines": { @@ -52,26 +53,26 @@ } }, "node_modules/@contentstack/datasync-filesystem-sdk": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@contentstack/datasync-filesystem-sdk/-/datasync-filesystem-sdk-1.5.3.tgz", - "integrity": "sha512-ll1lzRZEgAdVqentnYEfR83QMURe+QC8wKPIQFSoHL4IhM3Za/JjtshvdxFCLUrbyo24e7hEEqNT2bO6VHiKag==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@contentstack/datasync-filesystem-sdk/-/datasync-filesystem-sdk-1.5.1.tgz", + "integrity": "sha512-MSEhH/AAUtDjRxE+yrEDsAZCi/zZPQjz0n2Z0dLxEq7BKTa3FCB5sevwsEX9tnhp0lzFUSz4x5g+3xt43XbhxA==", "license": "MIT", "dependencies": { "json-mask": "2.0.0", - "lodash": "^4.18.1", + "lodash": "^4.17.21", "mkdirp": "^3.0.1", "npm-pack": "^1.0.0", "sift": "17.1.3" } }, "node_modules/@contentstack/datasync-mongodb-sdk": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/@contentstack/datasync-mongodb-sdk/-/datasync-mongodb-sdk-1.0.15.tgz", - "integrity": "sha512-eup74aFdC0GuHi7hycRyvIJ9a0fVYl555ynKLGbxg0DNtDbgJloIpkpODLtJD9vPe8CJnbdFI4LNNVIEbyrAkQ==", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@contentstack/datasync-mongodb-sdk/-/datasync-mongodb-sdk-1.0.12.tgz", + "integrity": "sha512-/P7FoW1Q9K8YCiMZFH2C57ut6euBUm3cBHlLZBprfmTTvOH0E+oHNIurGoU0aNQq1Op3cBtCkEOtEy1T+Lo5Fw==", "license": "MIT", "dependencies": { - "lodash": "^4.18.1", - "mongodb": "^6.21.0", + "lodash": "^4.17.21", + "mongodb": "^6.12.0", "npm-pack": "^1.0.0", "sift": "^17.1.3" }, @@ -80,15 +81,15 @@ } }, "node_modules/@contentstack/utils": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@contentstack/utils/-/utils-1.9.1.tgz", - "integrity": "sha512-THZM0rNuq0uOSKkKnvzp8lsPDvvdKIvJIcMa9JBv4foL9rC8RWkWffa2yMyb+9m/5HZrdAmpEWdubkGwARa8WQ==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@contentstack/utils/-/utils-1.6.3.tgz", + "integrity": "sha512-FU1hFks9vnJ5e9cwBTPgnf3obx/fuKh+c3Gtc71mq1Mrub3/z4rJZJWLJ2kublVKnXWnhz+Yt66rshxO/TT9IQ==", "license": "MIT" }, "node_modules/@mongodb-js/saslprep": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.11.tgz", - "integrity": "sha512-o9rAHc0IpIjuPSxRutWpE1F62x7n+4mVS4rCNHkzhIUMQcc18bb6xEq5wd2NdN0WjepIyXIppRshYI2kQDOZVA==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.4.tgz", + "integrity": "sha512-p7X/ytJDIdwUfFL/CLOhKgdfJe1Fa8uw9seJYvdOmnP9JBWGWHW69HkOixXS6Wy9yvGf1MbhcS6lVmrhy4jm2g==", "license": "MIT", "dependencies": { "sparse-bitfield": "^3.0.3" @@ -152,9 +153,9 @@ } }, "node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", "dependencies": { @@ -253,34 +254,21 @@ } }, "node_modules/body-parser": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", - "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", + "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", - "content-type": "^2.0.0", + "content-type": "^1.0.5", "debug": "^4.4.3", - "http-errors": "^2.0.1", - "iconv-lite": "^0.7.2", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", - "qs": "^6.15.2", - "raw-body": "^3.0.2", - "type-is": "^2.1.0" - }, - "engines": { - "node": ">=18" + "qs": "^6.14.0", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/body-parser/node_modules/content-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", - "license": "MIT", "engines": { "node": ">=18" }, @@ -307,9 +295,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -396,6 +384,21 @@ "dev": true, "license": "MIT" }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", @@ -450,9 +453,9 @@ "license": "MIT" }, "node_modules/content-disposition": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", - "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "license": "MIT", "engines": { "node": ">=18" @@ -472,9 +475,9 @@ } }, "node_modules/contentstack": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/contentstack/-/contentstack-3.27.0.tgz", - "integrity": "sha512-2ZzVk1dO4AhgaiuPjLIzeDnQky/ElI02E4+tntX7xXQXgPEDWgogghoRMT0y0dFBcZthrZe1QChwYA9aCRSGpA==", + "version": "3.26.3", + "resolved": "https://registry.npmjs.org/contentstack/-/contentstack-3.26.3.tgz", + "integrity": "sha512-mN1/Z8YV1HoIw03oEgnoHlaX/ueOLZH4unbf3zJt6uOJSO51gDFfOQEnKsTfUfWkyks9xUmED3WzPMxpnxdqcQ==", "license": "MIT", "dependencies": { "@contentstack/utils": "^1.4.1", @@ -614,9 +617,9 @@ } }, "node_modules/es-object-atoms": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -1163,7 +1166,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -1226,9 +1229,9 @@ } }, "node_modules/hasown": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", - "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -1273,9 +1276,9 @@ } }, "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", + "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -1494,9 +1497,9 @@ } }, "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, "node_modules/math-intrinsics": { @@ -1571,9 +1574,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { @@ -1665,23 +1668,19 @@ } }, "node_modules/morgan": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.11.0.tgz", - "integrity": "sha512-zSkVu3t18r39pw4ixfBKvfZi3y2UOqr7d4WYwcj3m8nXpEQK4rPO6GLzs/CExoRgmX3y9EjmmcXqv6jq0SK46g==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", + "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==", "license": "MIT", "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", "depd": "~2.0.0", - "on-finished": "~2.4.1", + "on-finished": "~2.3.0", "on-headers": "~1.1.0" }, "engines": { "node": ">= 0.8.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" } }, "node_modules/morgan/node_modules/debug": { @@ -1699,6 +1698,18 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1909,9 +1920,9 @@ } }, "node_modules/path-to-regexp": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", - "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", "funding": { "type": "opencollective", @@ -1976,9 +1987,9 @@ } }, "node_modules/qs": { - "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -2014,6 +2025,19 @@ "node": ">= 0.10" } }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/regexpp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", @@ -2232,14 +2256,14 @@ } }, "node_modules/side-channel": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", - "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.4", - "side-channel-list": "^1.0.1", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, @@ -2251,13 +2275,13 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" + "object-inspect": "^1.13.3" }, "engines": { "node": ">= 0.4" @@ -2529,34 +2553,17 @@ } }, "node_modules/type-is": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", - "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", "dependencies": { - "content-type": "^2.0.0", + "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" }, "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/type-is/node_modules/content-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">= 0.6" } }, "node_modules/unpipe": { diff --git a/package.json b/package.json index a852ae9..84332ee 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,22 @@ { "name": "datasync-nodejs-sdk-boilerplate", - "version": "0.0.13", + "version": "0.0.9", "private": true, "scripts": { "start": "node ./bin/www", "pre-commit": "husky install && husky && chmod +x .husky/pre-commit" }, "dependencies": { - "@contentstack/datasync-filesystem-sdk": "1.5.3", - "@contentstack/datasync-mongodb-sdk": "^1.0.15", + "@contentstack/datasync-filesystem-sdk": "^1.5.1", + "@contentstack/datasync-mongodb-sdk": "^1.0.12", "async": "^3.2.6", - "contentstack": "^3.27.0", + "chokidar": "^5.0.0", + "contentstack": "^3.24.1", "debug": "~3.2.7", "express": "^5.2.1", - "express-rate-limit": "^7.5.1", + "express-rate-limit": "^7.1.5", "http-errors": "~2.0.0", - "morgan": "~1.11.0", + "morgan": "~1.10.1", "nunjucks": "^3.2.4", "path": "^0.12.7" }, @@ -28,9 +29,5 @@ "url": "https://github.com/contentstack/datasync-nodejs-website-boilerplate" }, "author": "Contentstack LLC ", - "license": "MIT", - "overrides": { - "lodash": "^4.18.1", - "qs": "^6.15.2" - } + "license": "MIT" } diff --git a/skills/README.md b/skills/README.md deleted file mode 100644 index 44cb854..0000000 --- a/skills/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Skills – Contentstack DataSync Node.js Website Boilerplate - -Source of truth for detailed guidance. Read `AGENTS.md` first, then open the skill that matches your task. - -| Skill folder | Use when | -|--------------|----------| -| `dev-workflow` | Local setup, scripts, Husky/Snyk/Talisman, CI and version-bump rules | -| `javascript-style` | CommonJS, ESLint, and staying consistent with nearby code | -| `datasync-website` | Express app structure, DataSync Stack, config, routes, views, synced content paths | -| `testing` | Adding or running automated tests; credentials and fixtures | -| `code-review` | Preparing or reviewing PRs for this repository | - -Each folder contains `SKILL.md` with YAML frontmatter (`name`, `description`). diff --git a/skills/code-review/SKILL.md b/skills/code-review/SKILL.md deleted file mode 100644 index a240aa0..0000000 --- a/skills/code-review/SKILL.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: code-review -description: PR checklist for this boilerplate—version bumps, security hooks, and consistency with Express/DataSync patterns. ---- - -# Code review – Contentstack DataSync Node.js Website Boilerplate - -## When to use - -- Authoring or reviewing a pull request -- Verifying release readiness for a change that touches app code - -## Instructions - -- **Version bump:** If the PR changes release-affecting files (paths are defined in `.github/workflows/check-version-bump.yml`), `package.json` version must increase and exceed the latest tag. -- **Security / hooks:** Contributors with Husky enabled should pass Snyk and Talisman locally unless bypass is justified; align dependency changes with `.github/workflows/sca-scan.yml` expectations where relevant. -- **Code quality:** ESLint should pass on touched files; new code should match existing patterns in the same directory (CommonJS, existing error handling). -- **Product behavior:** DataSync connection and routes should remain coherent—config changes should be documented for operators (ports, `contentStore`, locales). -- **Ownership:** See `CODEOWNERS` for required reviewers. diff --git a/skills/datasync-website/SKILL.md b/skills/datasync-website/SKILL.md deleted file mode 100644 index 4ef3968..0000000 --- a/skills/datasync-website/SKILL.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -name: datasync-website -description: Express entry points, Contentstack DataSync Stack, environment config, routes, and where synced content lives in this boilerplate. ---- - -# DataSync website app – Contentstack DataSync Node.js Website Boilerplate - -## When to use - -- Adding or changing pages, routes, middleware, or views -- Connecting or switching DataSync content stores (filesystem vs MongoDB SDK) -- Locating API keys, tokens, ports, or locale settings - -## Instructions - -- **HTTP entry:** `bin/www` creates the server from `app.js` (`module.exports = app`). `app.js` registers Nunjucks (`views/`), static assets under `/static` from `public/`, rate limiting, JSON/urlencoded parsers, and `./routes`. -- **Contentstack Stack:** `models/contentstack.js` uses `require(config.sdk)` to load `Contentstack`, then `Contentstack.Stack(config)` and `Stack.connect(config.contentStore)`. The `sdk` string in `config/.js` must match an installed module name. Export is the connected `Stack` for use in routes. -- **Environment config:** Edit `config/development.js`, `config/staging.js`, or `config/production.js` for `contentstack.apikey`, `deliveryToken`, `contentStore` (e.g. `baseDir: './_contents'` for filesystem), `locales`, and `port`. Do not commit real secrets; use local overrides or secret injection patterns your team agrees on. -- **Routing:** `routes/index.js` mounts `middlewares` and `routes/home.js`. Locale and partials live under `middlewares/` (`locales.js`, `partials.js`). -- **Synced content:** Default filesystem layout uses `_contents` under the project (see `contentStore.baseDir` in config). `schemaNentries/` holds schema-related assets for the sample—treat as part of release-affecting paths when the version-bump workflow applies. -- **Dependencies:** See `package.json` for `@contentstack/datasync-filesystem-sdk`, `@contentstack/datasync-mongodb-sdk`, `contentstack`, `express`, `nunjucks`, etc. -- **Product docs:** [Contentstack DataSync](https://www.contentstack.com/docs/guide/synchronization/contentstack-datasync). diff --git a/skills/dev-workflow/SKILL.md b/skills/dev-workflow/SKILL.md deleted file mode 100644 index a07b4d8..0000000 --- a/skills/dev-workflow/SKILL.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: dev-workflow -description: Local setup, npm scripts, Husky and security hooks, CI expectations, and PR version-bump rules for this boilerplate. ---- - -# Development workflow – Contentstack DataSync Node.js Website Boilerplate - -## When to use - -- Setting up the project or onboarding someone new -- Changing how commits or CI behave -- Opening a PR that touches application code (version bump may be required) - -## Instructions - -- **Dependencies:** Run `npm install` at the repo root. There is no separate build or bundle step. -- **Run the server:** `npm start` executes `node ./bin/www`. Port and stack config come from `config/.js` (default `development` if `NODE_ENV` is unset). -- **Lint:** `npx eslint .` uses `.eslintrc.js`. There is no `lint` npm script; invoke ESLint directly. -- **Husky:** `npm run pre-commit` installs Husky and marks `.husky/pre-commit` executable. The pre-commit hook runs **Snyk** (`snyk test --all-projects`) and **Talisman**; both CLIs must be installed locally. Set `SKIP_HOOK=1` to bypass (document why if you use this in automation). -- **PR version bump:** [.github/workflows/check-version-bump.yml](../../.github/workflows/check-version-bump.yml) requires `package.json` to change when certain paths change (e.g. `app.js`, `bin/`, `config/`, `middlewares/`, `models/`, `public/`, `routes/`, `views/`, `schemaNentries/`) and the new version must be greater than the latest git tag. Doc-only or `.github`-only changes skip the check per that workflow. diff --git a/skills/javascript-style/SKILL.md b/skills/javascript-style/SKILL.md deleted file mode 100644 index e24dc5f..0000000 --- a/skills/javascript-style/SKILL.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: javascript-style -description: JavaScript and CommonJS conventions, ESLint usage, and matching existing file style in this Node.js boilerplate. ---- - -# JavaScript style – Contentstack DataSync Node.js Website Boilerplate - -## When to use - -- Writing or editing `.js` files under `app.js`, `bin/`, `config/`, `middlewares/`, `models/`, `routes/`, or `public/js/` -- Choosing `var` vs `const`/`let` or module layout in a file that mixes styles - -## Instructions - -- **Modules:** Use CommonJS (`require`, `module.exports`) consistently with the file you are editing. New code in files that already use `const`/`let` should use the same; older files under `config/` and `models/` often use `var`—do not rewrite wholesale for style alone. -- **Lint:** Run `npx eslint .`; rules live in `.eslintrc.js` (extends `eslint:recommended` with many explicit rules, including single quotes and JSDoc-related rules where enabled). -- **Paths:** Prefer `path.join(__dirname, ...)` for filesystem paths when the surrounding code does. -- **Formatting:** `.jsbeautifyrc` applies to JS/HTML formatting where your editor uses it; keep line length and indentation consistent with existing files. diff --git a/skills/testing/SKILL.md b/skills/testing/SKILL.md deleted file mode 100644 index d33986d..0000000 --- a/skills/testing/SKILL.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: testing -description: Automated testing posture for this repo (currently minimal) and how to add tests without leaking credentials. ---- - -# Testing – Contentstack DataSync Node.js Website Boilerplate - -## When to use - -- Adding unit, integration, or E2E tests -- Deciding where test files live and how CI should run them -- Handling API keys and tokens in test fixtures - -## Instructions - -- **Today:** `package.json` has no `test` script and the repository does not ship a standard `test/` tree. Validate changes by running the app (`npm start`) and ESLint (`npx eslint .`) as appropriate. -- **Adding a runner:** Prefer adding a `test` script (e.g. Node’s built-in runner, or Mocha/Jest if the team standardizes) and document the command in `AGENTS.md` when it lands. Keep test-only changes in paths that your CI treats as non–release-affecting where applicable (see version-bump workflow). -- **Credentials:** Never commit real Contentstack keys or tokens. Use environment variables or ignored local config for integration tests; mirror patterns from `config/*.js` with placeholders only in committed files.