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
5 changes: 5 additions & 0 deletions .cursor/rules/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Cursor (optional)

**Cursor** users: start at the repo root **[`AGENTS.md`](../../AGENTS.md)**. All conventions live in **`skills/*/SKILL.md`** (universal for any editor or tool).

This folder **only** contains this **README** — no `.mdc` or other rule files — so nothing editor-specific duplicates the canonical docs.
79 changes: 79 additions & 0 deletions .github/workflows/check-version-bump.yml
Original file line number Diff line number Diff line change
@@ -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)."
30 changes: 4 additions & 26 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
name: Release

on:
push:
branches: [master]
release:
types: [created]

jobs:
build:
Expand Down Expand Up @@ -43,35 +43,13 @@ jobs:
token: ${{ secrets.NPM_TOKEN }}
# access: public # Uncomment this line if you want to publish the package as public for first time

# Auto-tag the new version if a change is detected
- name: Auto-tag new version
id: update_tag
uses: Klemensas/action-autotag@stable
with:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
tag_prefix: "v"

# Create a new GitHub Release
- name: Create GitHub Release
if: steps.update_tag.outputs.tagname != ''
uses: actions/create-release@v1
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.update_tag.outputs.tagname }}
release_name: Release ${{ steps.update_tag.outputs.tagname }}
draft: false
prerelease: false

# Upload the packaged .tgz file as a release asset
# Upload the packaged .tgz to the release that triggered this workflow
- name: Upload Release Asset
if: steps.update_tag.outputs.tagname != ''
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
upload_url: ${{ github.event.release.upload_url }}
asset_path: "./contentstack-datasync-mongodb-sdk-${{ steps.package.outputs.version }}.tgz"
asset_name: "contentstack-datasync-mongodb-sdk-${{ steps.package.outputs.version }}.tgz"
asset_content_type: application/tgz
40 changes: 40 additions & 0 deletions .husky/post-checkout
Original file line number Diff line number Diff line change
@@ -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."
92 changes: 92 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# AI agent docs — `@contentstack/datasync-mongodb-sdk`

You are working on the **Contentstack DataSync MongoDB SDK** — a library that queries **MongoDB** (or a Mongo-compatible store) holding content synced via **Contentstack DataSync**, **not** the Content **Delivery** (CDA) or **Management** (CMA) HTTP SDKs. Core behavior uses the **MongoDB driver** and queries against **persisted sync data**, not live stack REST calls for normal reads.

## Single source of truth

| Layer | Role |
|-------|------|
| **[`.cursor/rules/README.md`](.cursor/rules/README.md)** | Optional Cursor pointer (the **only** file under `.cursor/rules/`); links to **`AGENTS.md`** and **`skills/`** |
| **`AGENTS.md`** (this file) | Universal entry: identity, out-of-scope, links, tech stack, commands, skills index |
| **`skills/<name>/SKILL.md`** | Full conventions and checklists (source of truth for depth) |

**Flow:** [`.cursor/rules/README.md`](.cursor/rules/README.md) → **`AGENTS.md`** → **`skills/<name>/SKILL.md`**

## Out of scope (unless comparing or documenting migration)

- **Not** the CDA or CMA **HTTP** client SDKs.
- **Not** live Contentstack **REST** reads for normal query paths — this SDK targets **synced data in MongoDB**.

## Repository

| | |
|--|--|
| **npm** | `@contentstack/datasync-mongodb-sdk` |
| **Git** | [https://github.com/contentstack/datasync-mongodb-sdk](https://github.com/contentstack/datasync-mongodb-sdk) |
| **Product docs** | [Contentstack DataSync](https://www.contentstack.com/docs/guide/synchronization/contentstack-datasync) |

## Tech stack

| Area | Details |
|------|---------|
| Language | TypeScript `^4.9.5` (`package.json`); `tsc` → `dist/`, declarations `typings/` (`tsconfig.json`) |
| Runtime | Node `>=8` (`engines`); README may suggest a newer Node for local dev |
| Build | `npm run compile` (`tsc`); `npm run build-ts` (`clean` + `tsc`) |
| Test | Jest `^29` + ts-jest, Node env, coverage on (`jest.config.js`). **`npm test` is `jest` only** — no `pretest` in `package.json` |
| Lint | **TSLint** — `npm run tslint` + `tslint.json` on `src/**/*.ts`. **No ESLint** / no `npm run lint` in this repo |
| Runtime deps | `mongodb`, `lodash`, `sift` |
| Docs | `npm run build-doc` (builds then JSDoc → `docs/`) |

## Source layout and public entry points

| Role | Path |
|------|------|
| Package entry | `dist/index.js` (`main`) |
| Public API | `src/index.ts` — `Contentstack`, `Contentstack.Stack(config, db?)` |
| Query / connection | `src/stack.ts` |
| Defaults + validation | `src/config.ts`, `src/util.ts` |
| Messages | `src/messages.ts` |
| Tests + fixtures | `test/`, `test/data/` |
| Declarations | `typings/*.d.ts` |
| Example | `example/index.js` |

## Commands

| Command | Purpose |
|---------|---------|
| `npm run build-ts` | Clean `dist/`, `typings/`, `coverage/`, then `tsc` |
| `npm run compile` | `tsc` only |
| `npm test` | Jest (coverage per `jest.config.js`) |
| `npm run tslint` | TSLint `src/**/*.ts` |
| `npm run clean` | Rimraf `dist`, `typings`, `coverage` |
| `npm run build-doc` | Full build + JSDoc to `docs/` |

## Test model and credentials

- **Integration-style** tests against a **real MongoDB** (`Stack.connect()`, inserts, queries, teardown). **Not** live Contentstack HTTP API calls.
- Default URI from merged **`src/config.ts`** (e.g. `mongodb://localhost:27017`); tests often use **`test/config.ts`** (`dbName` e.g. `sync-test`).
- **No** committed `.env` for tests; override via **`contentStore`** (including `url`) in code. **Do not** commit production Mongo URIs or secrets.
- **No** Testcontainers or CI Mongo service defined in-repo — local MongoDB is the documented path for developers running tests.

## Skills index

Canonical detail lives under **`skills/<kebab-case>/SKILL.md`**. See **[`skills/README.md`](skills/README.md)**.

| Skill | `SKILL.md` |
|-------|------------|
| Dev workflow, hooks, CI | [`skills/dev-workflow/SKILL.md`](skills/dev-workflow/SKILL.md) |
| TypeScript / TSLint / `src/` layout | [`skills/typescript/SKILL.md`](skills/typescript/SKILL.md) |
| DataSync MongoDB SDK behavior | [`skills/datasync-mongodb/SKILL.md`](skills/datasync-mongodb/SKILL.md) |
| Testing | [`skills/testing/SKILL.md`](skills/testing/SKILL.md) |
| Code review | [`skills/code-review/SKILL.md`](skills/code-review/SKILL.md) |

## Using Cursor

- Open **[`.cursor/rules/README.md`](.cursor/rules/README.md)** — the sole file in **`.cursor/rules/`** — for pointers to **`AGENTS.md`** and **`skills/`**.
- Full guidance: **`skills/<name>/SKILL.md`** (attach or `@`-reference those paths in chat per your Cursor setup); there are **no** `.cursor/rules/*.mdc` files in this repo.

## Contributor workflow (concise)

- **`.husky/pre-commit`:** **Snyk** + **Talisman** when installed; `SKIP_HOOK=1` only if your team allows.
- **CI:** `.github/workflows/` includes CodeQL, SCA, policy scans, **check-version-bump** — see each file for triggers.
- **Version bump workflow** path filters may **not** list `src/`; maintainers may need to align the workflow with this layout (see [`skills/dev-workflow/SKILL.md`](skills/dev-workflow/SKILL.md)).
Loading
Loading