Skip to content
Open
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
9 changes: 9 additions & 0 deletions .agents/hooks/git-policy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash
# PreToolUse hook, matcher: Bash. Blocks force-push (incl. +refspec) and skipped git hooks. Allows --force-with-lease.
# Known false positive: a commit message containing " -n " is also blocked; the agent can rephrase.
cmd=$(jq -r '.tool_input.command // empty')
if echo "$cmd" | grep -Eq 'git +(push|commit).* --no-verify|git +push.*( -f| --force)( |$)|git +push.* \+[^ ]|git +commit.* -n( |$)'; then
echo "Blocked by repo policy: force-push and --no-verify are not allowed. Ask a human." >&2
exit 2
fi
exit 0
23 changes: 23 additions & 0 deletions .agents/hooks/protect-files.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash
# PreToolUse hook, matcher: Edit|Write|MultiEdit|NotebookEdit. Blocks agent edits to protected paths.
# Known gap: edits made through Bash (sed -i, redirects) bypass this hook.
file=$(jq -r '.tool_input.file_path // .tool_input.notebook_path // empty')
[ -n "$file" ] || exit 0
root=${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}
rel=${file#"$root"/}
name=${rel##*/}
why=
case "$rel" in
lib/*|dist/*) why="generated build output; change src/ and run npm run build:node / build:web" ;;
package-lock.json) why="change it through npm install, not by hand" ;;
CHANGELOG.md|.pubnub.yml) why="written by the release bot from the PR description" ;;
test/specs/*) why="external checkout replaced by npm run test:contract:prepare" ;;
.agents/hooks/*|.pre-commit-config.yaml|.github/workflows/*) why="guardrail/CI configuration" ;;
secrets/*|*/secrets/*) why="may contain secrets" ;;
esac
case "$name" in .env|.env.*|*.env|*.pem|*.key) why="may contain secrets" ;; esac
if [ -n "$why" ]; then
echo "Blocked: $rel is protected ($why). Ask a human to change it." >&2
exit 2
fi
exit 0
16 changes: 16 additions & 0 deletions .agents/hooks/quick-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash
# Stop hook. Don't let the agent finish with a failing type check. Keep it under ~30 s.
# Stop hooks receive JSON on stdin; if this run was already continued by a hook, let the agent finish to avoid a loop.
[ "$(jq -r ".stop_hook_active // false" 2>/dev/null)" = true ] && exit 0
cd "${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}" || exit 0
[ -n "$(git status --porcelain -- src test 2>/dev/null)" ] || exit 0 # no TypeScript changed
if [ ! -x node_modules/.bin/tsc ]; then
echo "quick-check skipped: node_modules missing (run npm ci)" >&2
exit 0
fi
if ! out=$(npm run -s typecheck 2>&1); then
echo "Type check failed. Fix it before finishing:" >&2
echo "$out" | tail -n 40 >&2
exit 2
fi
exit 0
47 changes: 47 additions & 0 deletions .agents/skills/sdk-architecture/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
name: sdk-architecture
description: Use before changing, debugging, refactoring or reviewing SDK source under src/. Covers the request pipeline, platform and build boundaries, and per-module invariants and tests for publish, subscribe, presence, event engine and access manager.
---

# SDK architecture

Load the module guide for the area you touch. For lifecycle changes, read subscribe and presence together.

| Module | Read when the change touches | Guide |
| --- | --- | --- |
| Publish / signal | payload encoding, GET vs POST, encryption, store/TTL flags | [publish](modules/publish.md) |
| Subscriptions | entities, listeners, cursors, reconnect, SharedWorker | [subscribe](modules/subscribe.md) |
| Presence | heartbeat, leave, state, occupancy | [presence](modules/presence.md) |
| Event engine | state transitions, effects, cancellation | [event-engine](modules/event-engine.md) |
| Access Manager | grant/revoke, tokens, signatures | [access-manager](modules/access-manager.md) |

## Request pipeline
`PubNubCore.sendRequest` (`src/core/pubnub-common.ts`) → an `AbstractRequest` subclass in `src/core/endpoints/`, which owns
validation, the operation identifier, path/query/body and response parsing → `PubNubMiddleware`
(`src/transport/middleware.ts`) → the platform transport.
- `sendRequest` owns validation, operation timeouts, status conversion and callback/Promise delivery.
- Middleware adds `instanceid`/`requestid`/`pnsdk`, auth key or token, signatures, and **request retries**
(`retryConfiguration`). Auth is skipped for `/v2/auth`, `/v3/pam` and `/time`; signing is skipped for `/time`.
- Copy a neighboring endpoint (`publish.ts`, `time.ts`) rather than inventing a new pipeline.
- Grouped APIs use facades: `pubnub-objects.ts`, `pubnub-data-sync.ts`, `pubnub-channel-groups.ts`, `pubnub-push.ts`.
- Public types live both in endpoint modules and in `src/core/types/api/`. Follow the actual export chain into
`lib/types/index.d.ts`, which `build:node-types` aggregates under the `PubNub` namespace.

## Subscriptions
- `enableEventEngine` defaults to `true` (`src/event-engine/`). With `false`, the legacy `SubscriptionManager` runs
with the reconnection and deduplication managers. Both paths emit through `EventDispatcher`.
- Entities (`src/entities/`) and subscription sets can share one client subscription, so removing one owner must not
break another.
- In browsers, `subscriptionWorkerUrl` routes subscribe, heartbeat and leave through the SharedWorker
(`src/transport/subscription-worker/`), which is shared across tabs and clients.

## Build boundaries
| Config | Compiles | Excludes |
| --- | --- | --- |
| `tsconfig.json` (`build:node`, `typecheck`) | core, node, react_native | web, worker, titanium, nativescript |
| `tsconfig.rollup.json` (`build:web`) | web + worker bundles | node, react_native, titanium, nativescript |

- `process.env.*_MODULE` flags are replaced by `replaceConfiguration` (`rollup.config.js`) in **web bundles only**.
Worker bundles skip it, and the Node build evaluates the flags at runtime. A new flag needs an entry there; check the
build with the module both enabled and disabled.
- Node checks can't prove browser behavior. A browser-affecting change needs `build:web` plus the web suites.
7 changes: 7 additions & 0 deletions .agents/skills/sdk-architecture/modules/access-manager.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Access Manager

Trace [access endpoints](../../../../src/core/endpoints/access_manager), [token manager](../../../../src/core/components/token_manager.ts), and [transport middleware](../../../../src/transport/middleware.ts). Distinguish legacy grant/audit from token grant/revoke and token consumption on normal requests. Signing credentials, tokens, auth keys, resource permissions, and authorized user identity are different concepts.

Check permission serialization, TTL validation, signatures, error/status mapping, token precedence, and how auth changes affect subsequent subscribe/reconnect traffic. Do not introduce secret-key requirements into clients that only consume tokens; never put signing secrets in browser examples or diagnostics.

Tests: [unit access tests](../../../../test/unit/access_manager), [access integration](../../../../test/integration/endpoints/access.test.ts), [grant token](../../../../test/integration/endpoints/grant_token.test.ts), [token manager](../../../../test/integration/components/token_manager.test.ts), and [contract definitions](../../../../test/contract/definitions/grant.ts). Contract setup: [contract.md](../../sdk-test/contract.md).
7 changes: 7 additions & 0 deletions .agents/skills/sdk-architecture/modules/event-engine.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Event engine

The [core engine](../../../../src/event-engine/core/engine.ts) processes transitions with context and queued events. [State definitions](../../../../src/event-engine/states) define subscribe states (handshaking, receiving, handshake_failed, receive_failed, handshake_stopped, receive_stopped, unsubscribed); [presence states](../../../../src/event-engine/presence) handle heartbeat/leave. [Dispatchers](../../../../src/event-engine/core/dispatcher.ts) execute managed effects and cancel them by invocation type. Core injects service calls, delays, and emitters.

For a bug, capture initial state/context → incoming event → transition → effects → emitted status/message. Compare expected and actual traces. Review cancellation of obsolete requests/delays, retry termination (request retries live in src/transport/middleware.ts, not in states), retained cursors/channel sets, and disposal. Do not fix a state symptom without following the triggering API and effect callback.

Use [event-engine tests](../../../../test/unit/event_engine.test.ts) and [presence-engine tests](../../../../test/unit/event_engine_presence.test.ts); despite their unit location, they exercise mocked HTTP flows. Add coverage for the failing transition and relevant interrupted/recovery path. Compare [legacy subscriptions](subscribe.md) when a public behavior must stay consistent.
7 changes: 7 additions & 0 deletions .agents/skills/sdk-architecture/modules/presence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Presence

Presence spans [REST endpoints](../../../../src/core/endpoints/presence), core state/heartbeat APIs, [legacy subscriptions](../../../../src/core/components/subscription-manager.ts), [presence engine](../../../../src/event-engine/presence), and [worker managers](../../../../src/transport/subscription-worker). Occupancy queries, presence event subscriptions, heartbeat participation, and leave requests are distinct behaviors.

Trace how channels/groups, presence timeout, heartbeat interval, state payloads, subscription changes, and disconnect/destroy affect traffic. In particular, offline cleanup and explicit unsubscribe need not produce the same leave behavior. Review [subscribe](subscribe.md) for shared ownership and worker coordination; a timing change can alter occupancy observed by other clients.

Tests: [presence units](../../../../test/unit/presence), [presence endpoint](../../../../test/integration/endpoints/presence.test.ts), [heartbeat](../../../../test/integration/operations/heartbeat.test.ts), [state setting](../../../../test/integration/operations/stateSetting.test.ts), [subscribe heartbeat](../../../../test/integration/endpoints/subscribe_heartbeat.test.ts), and [presence engine](../../../../test/unit/event_engine_presence.test.ts). Reuse [presence mock helpers](../../../../test/utils.ts) and ensure timers/clients are cleaned up.
7 changes: 7 additions & 0 deletions .agents/skills/sdk-architecture/modules/publish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Publish and signal

Trace [PubNubCore](../../../../src/core/pubnub-common.ts) → [PublishRequest](../../../../src/core/endpoints/publish.ts) or [SignalRequest](../../../../src/core/endpoints/signal.ts) → [middleware](../../../../src/transport/middleware.ts) → platform transport. Preserve the distinction between GET URL payloads and POST bodies, encoding/serialization, encryption, history/TTL flags, metadata, custom message types, and string timetokens.

Read the supplied endpoint contract before changing accepted inputs: existing truthiness checks and documented payload types may disagree, and fixing that is an observable behavior change. Check callback and Promise delivery as well as request bytes.

Start with [unit publish tests](../../../../test/unit/publish/publish.test.ts), [integration publish tests](../../../../test/integration/endpoints/publish.test.ts), and [signal tests](../../../../test/integration/endpoints/signal.test.ts). Add boundary/error cases to the nearest suite; use mocked HTTP before considering live publish traffic.
9 changes: 9 additions & 0 deletions .agents/skills/sdk-architecture/modules/subscribe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Subscriptions

Trace [PubNubCore](../../../../src/core/pubnub-common.ts), [entities](../../../../src/entities), [EventDispatcher](../../../../src/core/components/event-dispatcher.ts), and the selected implementation. `enableEventEngine` (default `true`) selects [event engine](../../../../src/event-engine); otherwise [SubscriptionManager](../../../../src/core/components/subscription-manager.ts) uses reconnection and deduplication helpers.

Preserve channel/group ownership, subscribe cursors, reconnect behavior, message categorization, listener delivery, and unsubscribe cleanup. Entity subscriptions and subscription sets can share client subscriptions; removing one owner's listener/subscription must be assessed against others. Review both implementations rather than assuming behavior is identical.

Browser `subscriptionWorkerUrl` introduces [worker middleware and managers](../../../../src/transport/subscription-worker), which coordinate subscribe/heartbeat/leave across clients. Consult [presence](presence.md) whenever changing lifecycle or subscription state.

Tests: [subscribe endpoint](../../../../test/integration/endpoints/subscribe.test.ts), [legacy manager](../../../../test/integration/components/subscription_manager.test.ts), [event engine](../../../../test/unit/event_engine.test.ts), [unsubscribe](../../../../test/integration/operations/unsubscribe.test.ts), and [SharedWorker](../../../../test/integration/shared-worker/shared-worker.test.ts). The latter uses real demo-key traffic; see the [sdk-test](../../sdk-test/SKILL.md) skill.
19 changes: 19 additions & 0 deletions .agents/skills/sdk-debug/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
name: sdk-debug
description: Use when investigating a PubNub JavaScript SDK bug, regression, flaky behavior or customer-reported issue. Covers reproduction, root-cause classification, regression test first, then the narrow fix.
---

# sdk-debug

1. **Evidence first.** Gather the SDK version, platform/runtime, configuration (with secrets removed), and expected vs
actual behavior. Work from the evidence provided; ask only for inputs that block diagnosis.
2. **Classify the root cause** as one of: service or configuration, contract mismatch, timing/resource leak, or SDK
defect. Don't present an unexecuted reproduction as confirmed.
3. **Trace** with the module guide from [sdk-architecture](../sdk-architecture/SKILL.md):
public API → endpoint or subscribe/presence path → middleware → transport. For the event engine, record
`state + context → event → transition → effects → emitted status` and compare the expected trace with the actual one.
Check the legacy manager and SharedWorker paths only where they're implicated.
4. **Regression test first** ([sdk-test](../sdk-test/SKILL.md)), then the narrowest fix. Record the run before and after
the fix. If tests can't run, say the hypothesis is unverified.
5. A fix can break customers who rely on the old behavior. Report the root cause, the behavior change and who it affects,
using the four report fields from `AGENTS.md`.
19 changes: 19 additions & 0 deletions .agents/skills/sdk-deps/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
name: sdk-deps
description: Use when upgrading, adding or auditing PubNub JavaScript SDK npm dependencies (npm outdated, npm audit, Dependabot-style bumps). Covers lockfile handling, bundle and platform impact, and validation.
---

# sdk-deps

1. A new runtime dependency needs approval first. Bumps of existing dependencies don't.
2. Change dependencies only through npm, e.g. `npm install <pkg>@<version>`. `package-lock.json` is hook-protected
from hand edits. Check whether the `overrides` block in `package.json` still applies.
3. Check platform impact:
- Runtime dependencies ship to Node, browsers and React Native.
- Verify browser/RN compatibility (no Node built-ins in shared code) and any polyfill requirements.
4. Check bundle impact: run `npm run build:web` and compare the `dist/web/pubnub.min.js` size against `master`. Report
any growth.
5. Validate with `.scripts/agent-test.sh node`, and with `web` when browser code is affected. Each source PR also
commits rebuilt artifacts ([sdk-release](../sdk-release/SKILL.md)).
6. Triaging `npm outdated` or `npm audit` output is mechanical; give it to a light-model subagent. Report the
versions changed and the risk.
24 changes: 24 additions & 0 deletions .agents/skills/sdk-feature/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
name: sdk-feature
description: Use when adding or enhancing a PubNub JavaScript SDK feature, endpoint, parameter or public API. Contract-first steps, public-surface checklist and validation.
---

# sdk-feature

1. **Contract first.** There are no OpenAPI specs in this repo. Before changing request or response semantics, ask
for the operation's spec (operationId/path and revision) and record it in the PR. If the spec is missing or contradicts
current behavior, stop and surface the gap. Gherkin scenarios in `sdk-specifications` describe SDK behavior; they
aren't schemas.
2. Load [sdk-architecture](../sdk-architecture/SKILL.md) and the relevant module guide. Copy the nearest existing endpoint
or facade method rather than designing a new shape.
3. Public surface:
- callback and Promise overloads
- JSDoc with defaults, an example and any deprecation
- `@internal` on anything non-public
- export chain through to `lib/types/index.d.ts`
- validation and error messages consistent with neighboring APIs
4. Optional feature module? Add the `process.env.<NAME>_MODULE` guard and its `replaceConfiguration` entry.
5. Tests, via [sdk-test](../sdk-test/SKILL.md): validation boundaries, the encoded request, response/error mapping,
and both callback and Promise forms.
6. `npm run build:node`, then `.scripts/agent-test.sh release` (declaration checks).
7. Report with the four report fields from `AGENTS.md`. Call out new network traffic, bundle size and any platform restrictions.
22 changes: 22 additions & 0 deletions .agents/skills/sdk-refactor/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
name: sdk-refactor
description: Use when restructuring PubNub JavaScript SDK code without intending behavior change. Covers the invariant checklist, characterization tests and a deterministic public-type-surface gate.
---

# sdk-refactor

1. **Public type surface must not change.** Run `npm run build:node && git diff --exit-code lib/types/index.d.ts`
before you finish. A non-empty diff means a public-API change: stop and report it, or revert it.
2. Map callers and the affected modules ([sdk-architecture](../sdk-architecture/SKILL.md)). Keep platform boundaries
and module guards intact.
3. Write down the invariants that apply:
- overloads and types
- defaults, validation, error categories and messages
- event order and timing
- retry and cancellation
- network traffic (heartbeat, leave)
- cleanup of timers and listeners
4. Rely on existing tests. Add characterization tests only for invariants that nothing currently covers, and run them
before and after the change.
5. Don't mix behavior fixes into a refactor; list any you find as follow-ups.
6. Report every observable difference, however small. "Refactor" alone isn't a compatibility claim.
19 changes: 19 additions & 0 deletions .agents/skills/sdk-release/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
name: sdk-release
description: Use when finalizing a PubNub JavaScript SDK PR for merge or release. Covers rebuilding the committed lib/ and dist/web artifacts, verifying release consistency, and writing the PR description the changelog bot reads.
---

# sdk-release

1. **Never edit versions or changelogs.** A GitHub bot writes the version strings, `CHANGELOG.md` and `.pubnub.yml`
from the PR description. Its version targets are listed in `.github/workflows/release/versions.json`.
`.agents/hooks/protect-files.sh` blocks edits to the changelog files. Feature work never bumps the version.
2. Run `npm run build`. It regenerates `lib/` and `dist/web/`, and every source PR commits both.
Review the source diff first, then sanity-check `git diff --stat lib dist` for unexpected files.
3. Run `.scripts/agent-test.sh release`. It checks that `package.json`, `getVersion()` and the README CDN links agree,
and type-checks the aggregated declarations.
4. Write the PR title and body in the format from `.github/pull_request_template.md`: `type(scope): subject`, a body,
then a footer. The bot builds the changelog from them, so state the user-visible change precisely. Use `Closes #n`
for issues addressed and `BREAKING CHANGES:` for any breaking change.
5. Publishing to npm, S3 and GitHub releases happens on merge (`release.yml`). Never run
`.github/workflows/release/*.sh` or `npm publish`.
Loading
Loading