diff --git a/.cursor/rules/README.md b/.cursor/rules/README.md new file mode 100644 index 0000000..d8f3545 --- /dev/null +++ b/.cursor/rules/README.md @@ -0,0 +1,5 @@ +# 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/check-version-bump.yml b/.github/workflows/check-version-bump.yml new file mode 100644 index 0000000..31bdf66 --- /dev/null +++ b/.github/workflows/check-version-bump.yml @@ -0,0 +1,79 @@ +# 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/release.yml b/.github/workflows/release.yml index d6c98c2..531ed76 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,27 +1,45 @@ -name: Release datasync content store filesystem +name: Release + on: - push: - branches: [master] + release: + types: [created] + jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 with: node-version: "22.x" - - name: Installing dependencies + + - name: Install dependencies run: npm install + - name: Build run: npm run build-ts - - name: Publishing datasync content store filesystem - id: publish-to-npm - uses: JS-DevTools/npm-publish@v2.2.1 + + # Tarball name matches npm’s naming for this scoped package (see npm pack output) + - name: Pack the npm package + id: pack + run: echo "filename=$(npm pack | tail -1)" >> "$GITHUB_OUTPUT" + + - name: Publish to npm + id: publish_npm + uses: JS-DevTools/npm-publish@v3 with: token: ${{ secrets.NPM_TOKEN }} - - name: github-release - id: github-release + # access: public # Uncomment if this package’s first npm publish must be public + + - name: Upload Release Asset + uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - VERSION: ${{ steps.publish-to-npm.outputs.version }} - run: gh release create v"$VERSION" --title "Release $VERSION" --generate-notes \ No newline at end of file + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ${{ steps.pack.outputs.filename }} + asset_name: ${{ steps.pack.outputs.filename }} + asset_content_type: application/gzip diff --git a/.husky/post-checkout b/.husky/post-checkout new file mode 100755 index 0000000..302fdc6 --- /dev/null +++ b/.husky/post-checkout @@ -0,0 +1,40 @@ +#!/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/.talismanrc b/.talismanrc index 02578dd..7089b7b 100644 --- a/.talismanrc +++ b/.talismanrc @@ -3,9 +3,11 @@ fileignoreconfig: ignore_detectors: - filecontent - filename: package-lock.json - checksum: 58da400518e4eb2e953d723b6278546dba596f089ffa8bb11b41ffa4c45517c6 + checksum: 24691dc7b878951e6f221154be24d78abbd1416da13427311848567e7f325195 - filename: .husky/pre-commit checksum: 1b9367d219802de2e3a8af9c5c698e0c255c00af89339d73bdbb8acf5275079f - filename: src/util/index.ts checksum: 8c4600145acb0319051df4237b7e753af93465419d14d5fe97c211050f6da405 +- filename: skills/datasync-content-store-filesystem/SKILL.md + checksum: e5113dc126bf6eb471af4754665e721e5206aa08e54e4daabba6949fa35d00da version: "" diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..44029c2 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,51 @@ +# Contentstack DataSync Content Store (Filesystem) – 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-content-store-filesystem` — [GitHub](https://github.com/contentstack/datasync-content-store-filesystem) | +| *Purpose:* | Node.js **DataSync content store** that persists synced stack content to the **local filesystem** for DataSync Manager (publish/unpublish/delete, file layout, optional branch-aware paths). Main exports: `start`, `setConfig`, `getConfig`, `setAssetConnector`, `getFilesystemClient`; implementation: `FilesystemStore` in `src/fs.ts`. | +| *Out of scope (if any):* | Not the Content **Delivery (CDA)** or **Management (CMA)** HTTP SDKs as this package’s primary API. **No** runtime HTTP client dependency; core behavior is filesystem I/O and validation, not live stack REST calls for normal store operations. | + +Product docs: [Contentstack DataSync](https://www.contentstack.com/docs/guide/synchronization/contentstack-datasync). + +## Tech stack (at a glance) + +| Area | Details | +|------|---------| +| Language | TypeScript (`typescript` in `package.json`; `tsconfig.json` → CommonJS, `dist/`, declarations in `typings/`) | +| Build | `tsc` via `npm run build-ts` (runs `clean` then `tsc`) or `npm run compile` (`tsc` only) | +| Tests | Jest + ts-jest (`jest.config.js`), Node; tests under `test/**/*.ts`; `test/mock/*` ignored as test files | +| Lint / coverage | TSLint: `npm run tslint`, `tslint.json`, `src/**/*.ts`; Jest `collectCoverage`, output `coverage/` (json, html reporters) | +| Runtime | `debug`, `lodash`, `mkdirp`, `rimraf`, `write-file-atomic` | + +## Commands (quick reference) + +| Command type | Command | +|--------------|---------| +| Build | `npm run build-ts` (clean `dist`/`coverage` + compile) or `npm run compile` | +| Test | `npm test` (`pretest` runs `build-ts` and `rimraf _contents coverage`, then `jest --coverage`) | +| Lint | `npm run tslint` | + +**CI:** workflows under [`.github/workflows/`](.github/workflows/) (e.g. `check-version-bump.yml`, `release.yml`, CodeQL, SCA, policy scans — see each file for triggers). + +**Debug:** optional `DEBUG=*` or include `core-fs` (see `src/fs.ts`). + +## Where the documentation lives: skills + +| Skill | Path | What it covers | +|-------|------|----------------| +| Dev workflow | [skills/dev-workflow/SKILL.md](skills/dev-workflow/SKILL.md) | Branches, npm scripts, Husky (Talisman/Snyk), CI, version bumps | +| TypeScript | [skills/typescript/SKILL.md](skills/typescript/SKILL.md) | `tsconfig`, TSLint, `src/` / `typings/` layout, `core-fs` logging | +| DataSync content store | [skills/datasync-content-store-filesystem/SKILL.md](skills/datasync-content-store-filesystem/SKILL.md) | `FilesystemStore`, config, asset connector, branch/locale — not CDA/CMA as primary API | +| Testing | [skills/testing/SKILL.md](skills/testing/SKILL.md) | Jest, `pretest`, `test/mock`, coverage, env | +| Code review | [skills/code-review/SKILL.md](skills/code-review/SKILL.md) | PR checklist, terminology, semver, tests | + +An index with “when to use” hints is in [skills/README.md](skills/README.md). + +## Using Cursor (optional) + +If you use *Cursor*, [`.cursor/rules/README.md`](.cursor/rules/README.md) only points to *AGENTS.md*—same docs as everyone else. This repo keeps no other files under `.cursor/rules/`. diff --git a/package-lock.json b/package-lock.json index 7f30f7d..93089c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "@contentstack/datasync-content-store-filesystem", - "version": "2.1.10", + "version": "2.1.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@contentstack/datasync-content-store-filesystem", - "version": "2.1.10", + "version": "2.1.11", "license": "MIT", "dependencies": { "debug": "^4.4.3", - "lodash": "^4.17.23", + "lodash": "^4.18.1", "mkdirp": "^3.0.1", "rimraf": "^6.1.3", "write-file-atomic": "^7.0.0" @@ -200,23 +200,23 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/types": "^7.29.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "dev": true, "license": "MIT", "dependencies": { @@ -1268,9 +1268,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.7", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.7.tgz", - "integrity": "sha512-1ghYO3HnxGec0TCGBXiDLVns4eCSx4zJpxnHrlqFQajmhfKMQBzUGDdkMK7fUW7PTHTeLf+j87aTuKuuwWzMGw==", + "version": "2.10.13", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.13.tgz", + "integrity": "sha512-BL2sTuHOdy0YT1lYieUxTw/QMtPBC3pmlJC6xk8BBYVv6vcw3SGdKemQ+Xsx9ik2F/lYDO9tqsFQH1r9PFuHKw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1281,9 +1281,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -1305,9 +1305,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", "dev": true, "funding": [ { @@ -1325,11 +1325,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -1399,9 +1399,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001778", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001778.tgz", - "integrity": "sha512-PN7uxFL+ExFJO61aVmP1aIEG4i9whQd4eoSCebav62UwDyp5OHh06zN4jqKSMePVgxHifCw1QJxdRkA1Pisekg==", + "version": "1.0.30001784", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001784.tgz", + "integrity": "sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw==", "dev": true, "funding": [ { @@ -1653,9 +1653,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.313", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.313.tgz", - "integrity": "sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==", + "version": "1.5.331", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.331.tgz", + "integrity": "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==", "dev": true, "license": "ISC" }, @@ -1922,9 +1922,9 @@ "license": "ISC" }, "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2896,9 +2896,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, "node_modules/lodash.memoize": { @@ -3070,9 +3070,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.36", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", - "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", "dev": true, "license": "MIT" }, @@ -3259,9 +3259,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", - "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -3275,9 +3275,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -3469,9 +3469,9 @@ } }, "node_modules/rimraf/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -3498,12 +3498,12 @@ } }, "node_modules/rimraf/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^5.0.5" }, "engines": { "node": "18 || 20 || >=22" @@ -3747,19 +3747,19 @@ } }, "node_modules/ts-jest": { - "version": "29.4.6", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", - "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", + "version": "29.4.9", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.9.tgz", + "integrity": "sha512-LTb9496gYPMCqjeDLdPrKuXtncudeV1yRZnF4Wo5l3SFi0RYEnYRNgMrFIdg+FHvfzjCyQk1cLncWVqiSX+EvQ==", "dev": true, "license": "MIT", "dependencies": { "bs-logger": "^0.2.6", "fast-json-stable-stringify": "^2.1.0", - "handlebars": "^4.7.8", + "handlebars": "^4.7.9", "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", - "semver": "^7.7.3", + "semver": "^7.7.4", "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, @@ -3776,7 +3776,7 @@ "babel-jest": "^29.0.0 || ^30.0.0", "jest": "^29.0.0 || ^30.0.0", "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" + "typescript": ">=4.3 <7" }, "peerDependenciesMeta": { "@babel/core": { diff --git a/package.json b/package.json index fd37be8..182f487 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@contentstack/datasync-content-store-filesystem", - "version": "2.1.10", + "version": "2.1.11", "description": "Datasync content store library - saves data in filesystem", "main": "./dist", "types": "./typings", @@ -42,7 +42,7 @@ "license": "MIT", "dependencies": { "debug": "^4.4.3", - "lodash": "^4.17.23", + "lodash": "^4.18.1", "mkdirp": "^3.0.1", "rimraf": "^6.1.3", "write-file-atomic": "^7.0.0" diff --git a/skills/README.md b/skills/README.md new file mode 100644 index 0000000..8f43447 --- /dev/null +++ b/skills/README.md @@ -0,0 +1,17 @@ +# Skills – `@contentstack/datasync-content-store-filesystem` + +Source of truth for detailed guidance. Read [`AGENTS.md`](../AGENTS.md) first, then open the skill that matches your task. + +## When to use which skill + +| Skill folder | Use when | +|--------------|----------| +| [`dev-workflow/`](dev-workflow/SKILL.md) | Branching, CI, build/test/lint, Husky, PRs, version bumps | +| [`typescript/`](typescript/SKILL.md) | `tsconfig`, TSLint, `src/` layout, typings, logging | +| [`datasync-content-store-filesystem/`](datasync-content-store-filesystem/SKILL.md) | Store API, `FilesystemStore`, config, DataSync vs CDA/CMA | +| [`testing/`](testing/SKILL.md) | Jest layout, mocks, coverage, credentials policy | +| [`code-review/`](code-review/SKILL.md) | PR checklist, Blocker/Major/Minor, terminology | + +Each folder contains `SKILL.md` with YAML frontmatter (`name`, `description`). + +There is no separate **framework** skill: this package has no standalone HTTP client layer. diff --git a/skills/code-review/SKILL.md b/skills/code-review/SKILL.md new file mode 100644 index 0000000..12f4d96 --- /dev/null +++ b/skills/code-review/SKILL.md @@ -0,0 +1,59 @@ +--- +name: code-review +description: PR review checklist for DataSync filesystem content store — API, compatibility, tests, terminology (not CDA/CMA HTTP SDK) +--- + +# Code review — `@contentstack/datasync-content-store-filesystem` + +## When to use + +- Reviewing a PR that touches `src/`, `test/`, or public-facing docs +- Self-checking before requesting review +- Assessing semver or regression risk for store behavior + +## Instructions + +### Scope and terminology + +- This package is the **DataSync filesystem content store** — not the **CDA** or **CMA** HTTP client unless the change explicitly compares or documents integration. +- Prefer **DataSync**, **content store**, **asset connector**, **baseDir**, **locale**, **branch** (when configured). + +### Public API and docs + +- Exports from **`src/index.ts`**: `start`, `setConfig`, `getConfig`, `setAssetConnector`, `getFilesystemClient`. +- **`FilesystemStore`** in **`src/fs.ts`** — preserve or extend **JSDoc** on public exports when behavior changes. + +### Compatibility + +- Avoid breaking export signatures or on-disk layout without **semver** and changelog intent. +- Validation in **`src/util/validations.ts`** — do not silently change throw conditions for the same inputs. + +### Errors + +- No separate SDK error-code enum; ensure messages stay actionable and new throws are covered by tests where appropriate. + +### Null safety + +- Config and payloads may omit fields; match existing guards in **`src/fs.ts`** and validations. + +### Dependencies and security + +- New **`package.json`** dependencies need justification (SCA, maintenance). Align with org policy. + +### Tests + +- **`npm test`** should pass for `src/` / `test/` changes; extend **`test/`** and **`test/mock/`** for new behavior. No live stack API tests in this repo. + +### Optional severity + +| Level | Examples | +|-------|----------| +| **Blocker** | Breaks DataSync Manager contract, data loss/corruption, security | +| **Major** | Breaking API, wrong persistence layout, missing tests for risky logic | +| **Minor** | Style, non-user-facing refactors, doc nits | + +## References + +- [`../testing/SKILL.md`](../testing/SKILL.md) +- [`../datasync-content-store-filesystem/SKILL.md`](../datasync-content-store-filesystem/SKILL.md) +- [`../../AGENTS.md`](../../AGENTS.md) diff --git a/skills/datasync-content-store-filesystem/SKILL.md b/skills/datasync-content-store-filesystem/SKILL.md new file mode 100644 index 0000000..682e94e --- /dev/null +++ b/skills/datasync-content-store-filesystem/SKILL.md @@ -0,0 +1,58 @@ +--- +name: datasync-content-store-filesystem +description: DataSync filesystem content store — FilesystemStore, config, asset connector, branch/locale; not CDA/CMA HTTP SDKs +--- + +# DataSync content store (filesystem) — `@contentstack/datasync-content-store-filesystem` + +## When to use + +- Implementing or changing publish/unpublish/delete or file layout in **`src/fs.ts`** +- Adjusting defaults, patterns, or locale/branch handling (`src/config.ts`, `src/util/`) +- Adding exports or validation (`src/index.ts`, `src/util/validations.ts`) + +## Instructions + +### Product model + +- This package is the **DataSync content store** that **persists** synced content types, entries, and asset references to the **filesystem** for **DataSync Manager**. It is **not** the Contentstack **CDA** or **CMA** HTTP SDK; core behavior is **local file I/O** and validation, not stack REST calls for normal writes. + +### Entry flow + +1. **`start(connector, config?)`** in **`src/index.ts`** merges config with internal defaults and constructs **`FilesystemStore`**. +2. Use **`setAssetConnector`**, **`setConfig`**, **`getConfig`**, **`getFilesystemClient`** as documented in **`src/index.ts`**. +3. **`FilesystemStore`** exposes **`publish`**, **`unpublish`**, **`delete`**, and related methods expected by DataSync Manager. + +### Configuration + +- **`contentStore.baseDir`**: root for stored files (default **`./_contents`** in **`src/config.ts`**). +- **`patterns`** vs **`patternsWithBranch`**: when **`config.contentstack.branch`** is set, branch-aware path patterns apply (see **`src/fs.ts`** constructor). +- **`unwanted`**: strips keys from persisted JSON per asset, content type, or entry. +- **`preserveAssetInReferencedEntries`**: optional flag in internal defaults (`src/config.ts`). +- User config merges with internal config via **`lodash`** **`merge`** in **`start`**. + +### Where to change code + +| Concern | Location | +|---------|----------| +| Bootstrap, exports | **`src/index.ts`** | +| Defaults, patterns | **`src/config.ts`** | +| Store behavior | **`src/fs.ts`** — **`FilesystemStore`** | +| Validation | **`src/util/validations.ts`** | +| Helpers | **`src/util/index.ts`**, **`get-file-fields.ts`**, **`fs.ts`**, **`messages.ts`** | + +### Async and errors + +- Store methods return **Promises**. **`debug`** (`core-fs`) logs key steps when enabled. +- Invalid inputs throw from **`src/util/validations.ts`** — preserve clear messages and backward-compatible behavior for the same inputs. + +### Scope + +- **`src/`** is the SDK core; **`example/`** is illustrative only. + +## References + +- [Contentstack DataSync](https://www.contentstack.com/docs/guide/synchronization/contentstack-datasync) +- [`../typescript/SKILL.md`](../typescript/SKILL.md) +- [`../testing/SKILL.md`](../testing/SKILL.md) +- [`../../AGENTS.md`](../../AGENTS.md) diff --git a/skills/dev-workflow/SKILL.md b/skills/dev-workflow/SKILL.md new file mode 100644 index 0000000..d4f7b17 --- /dev/null +++ b/skills/dev-workflow/SKILL.md @@ -0,0 +1,54 @@ +--- +name: dev-workflow +description: Branches, npm scripts, Husky hooks, CI, and version bumps for datasync-content-store-filesystem +--- + +# Dev workflow — `@contentstack/datasync-content-store-filesystem` + +## When to use + +- Setting up locally or onboarding +- Before opening or updating a PR +- Planning a release or changing `package.json` version + +## Instructions + +### Branches + +- Use **feature branches** for new work; open pull requests against **`development`** to integrate changes. +- For a **release**, open a pull request from **`development`** into **`master`**. Releases to npm are driven from **`master`** (confirm with your team if policy differs). + +### Install and build + +```bash +npm install +npm run build-ts +``` + +`npm run build-ts` runs `clean` (rimraf `dist`, `coverage`) and `tsc` (`package.json`). + +### Quality gates + +```bash +npm run tslint # TSLint on src/**/*.ts +npm test # pretest: build-ts + rimraf _contents + coverage, then Jest with coverage +``` + +### Hooks (`.husky/`) + +- **`.husky/pre-commit`** runs **Talisman** and **Snyk** when those tools are installed; use **`SKIP_HOOK=1`** only when your team allows bypassing checks. + +### Pull requests and version bumps + +- Keep changes focused; match existing TypeScript and TSLint conventions. +- CI includes workflows under **`.github/workflows/`** (e.g. CodeQL, SCA, policy scans, **`check-version-bump.yml`**, **`release.yml`**). New dependencies should be justified for SCA and consumers. +- **Release-affecting** changes: bump **`package.json`** version per team policy. If **`check-version-bump.yml`** path filters omit `src/`, version checks may not run for library-only edits — **maintainers: align workflow paths with this repo.** + +### Docs + +- There is no `build-doc` script in **`package.json`** for this repo; API docs are JSDoc on sources and published **`typings/`** after build. + +## References + +- [`../testing/SKILL.md`](../testing/SKILL.md) +- [`../../AGENTS.md`](../../AGENTS.md) diff --git a/skills/testing/SKILL.md b/skills/testing/SKILL.md new file mode 100644 index 0000000..9df6d23 --- /dev/null +++ b/skills/testing/SKILL.md @@ -0,0 +1,45 @@ +--- +name: testing +description: Jest tests for datasync-content-store-filesystem — pretest, test/mock ignores, coverage, no live API +--- + +# Testing — `@contentstack/datasync-content-store-filesystem` + +## When to use + +- Adding or changing tests under `test/` +- Debugging failures after `src/` changes +- Understanding Jest ignores or `pretest` behavior + +## Instructions + +### Commands + +- **`npm test`**: runs **`pretest`** (`npm run build-ts` and `rimraf _contents coverage`), then **`jest --coverage`**. +- **`npm run build-ts`**: clean + full TypeScript build (needed before tests if you skip `pretest`). +- **`npm run compile`**: `tsc` only. + +### Runner and config + +- **Jest** + **ts-jest** (`jest.config.js`), **`testEnvironment: node`**, verbose output. +- **`testMatch`**: `**/test/**/*.ts` (and `.js`); **`testPathIgnorePatterns`**: **`/test/mock/*`** only. +- **`collectCoverage`**: true; reports under **`coverage/`** (json, html). No **`coverageThreshold`** in the checked-in config unless the team adds it. + +### Layout + +- Tests: **`test/*.ts`** (e.g. `index.ts`, `publishing.ts`, `deletion.ts`, `validations.ts`, `unpublishing.ts`). +- Mocks: **`test/mock/`** (`config.ts`, `assetStore.ts`, `data/**`) — excluded from test file discovery. + +### Credentials / env + +- **No** Contentstack API keys or stack credentials for default tests — local filesystem and mocks only. +- Optional: **`DEBUG`** including **`core-fs`** for store logging (`src/fs.ts`). + +### Notifications + +- Jest has **`notify: true`** — optional peer **`node-notifier`**; missing notifier can cause local failures until installed or `notify` is disabled in config. + +## References + +- [`../datasync-content-store-filesystem/SKILL.md`](../datasync-content-store-filesystem/SKILL.md) +- [`../../AGENTS.md`](../../AGENTS.md) diff --git a/skills/typescript/SKILL.md b/skills/typescript/SKILL.md new file mode 100644 index 0000000..01392fd --- /dev/null +++ b/skills/typescript/SKILL.md @@ -0,0 +1,37 @@ +--- +name: typescript +description: TypeScript, tsconfig, TSLint, and src/typings conventions for datasync-content-store-filesystem +--- + +# TypeScript — `@contentstack/datasync-content-store-filesystem` + +## When to use + +- Editing or adding files under `src/` or generated `typings/` +- Aligning with compiler and TSLint settings + +## Instructions + +### Tooling + +- **Compiler:** `tsconfig.json` — CommonJS modules, ES6 target, declarations to **`typings/`**, strict unused locals/parameters, `noImplicitReturns`, `include`: `src/**/*`. +- **Lint:** `npm run tslint` — config in **`tslint.json`** (e.g. single quotes, no semicolons, 2-space indent, max line length 120, `I`-prefixed interfaces, `no-default-export`). +- **No ESLint** in this repo’s `package.json` — use TSLint only unless the team migrates. + +### Layout + +- Source: **`src/`** only; do not hand-edit emitted **`typings/*.d.ts`** — change `src/` and rebuild. +- Prefer **named exports** from **`src/index.ts`**; avoid default exports (TSLint). + +### Logging + +- Debug namespace **`core-fs`** via **`debug`** in **`src/fs.ts`**. Enable with `DEBUG=*` or include `core-fs` in `DEBUG`. + +### Dependencies + +- Use only dependencies declared in **`package.json`**; new deps need maintainer/SCA alignment. + +## References + +- [`../datasync-content-store-filesystem/SKILL.md`](../datasync-content-store-filesystem/SKILL.md) +- [`../../AGENTS.md`](../../AGENTS.md)