Skip to content
Draft
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
48 changes: 48 additions & 0 deletions .dependency-cruiser.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Owners and constraints for this repository's actual runtime roots.
module.exports = {
forbidden: [
{
name: 'no-runtime-cycles',
severity: 'error',
from: {},
to: {
circular: true,
dependencyTypesNot: ['type-only'],
},
},
{
name: 'no-unresolved-dependencies',
severity: 'error',
from: {},
to: {
couldNotResolve: true,
},
},
{
name: 'no-runtime-test-dependencies',
severity: 'error',
from: {
path: '^(?:typescript/src)/',
pathNot: '(?:__tests__|test|tests)/|\\.(?:test|spec)\\.',
},
to: {
path: '(?:^|/)(?:__tests__|tests|test)/|(?:^|node_modules/)(?:jest|vitest|ts-jest)',
},
},
],
options: {
doNotFollow: {
path: 'node_modules|typescript/src/generated',
},
tsConfig: {
fileName: require('node:path').resolve(__dirname, 'typescript/tsconfig.quality.json'),
},
tsPreCompilationDeps: true,
enhancedResolveOptions: {
exportsFields: ['exports'],
conditionNames: ['types', 'require', 'node', 'default'],
extensions: ['.js', '.jsx', '.mjs', '.cjs', '.ts', '.tsx', '.mts', '.cts', '.json'],
},
exclude: '(?:^|/)node_modules/',
},
};
58 changes: 58 additions & 0 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Quality
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
concurrency:
group: quality-${{ github.ref }}
cancel-in-progress: true
jobs:
checks:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '24.19.0'
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '24.0.2'
- run: npm ci
- run: npm --prefix tools/quality ci
- run: npm run test:preset
- run: npm run quality:guards
- run: npm run format:check
- run: npm run lint:static
- run: npm run lint:typed
- run: npm run typecheck
- run: npm run lint:arch
- run: npm run lint:unused:production
- run: npm run lint:unused:full
- run: npm run lint:duplicates
- run: npm run lint:duplicates:full
- run: npm run lint:complexity
- run: npm run test:exceptions
- run: npm run test:generated
- run: npm run test:quality
- run: npm test
- run: npm run build
- run: npm run test:pack
- uses: actions/upload-artifact@v4
if: always()
with:
name: quality-reports
path: quality/reports/
if-no-files-found: error
quality-required:
if: always()
needs: [checks]
runs-on: ubuntu-24.04
steps:
- env:
RESULTS: ${{ toJSON(needs) }}
run: node -e 'const jobs=JSON.parse(process.env.RESULTS); if(!Object.values(jobs).every(job=>job.result==="success")) process.exit(1)'
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
node_modules/

typescript/dist/
quality/reports/

tools/quality/node_modules
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node tools/quality/node_modules/lint-staged/bin/lint-staged.js
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24.19.0
15 changes: 15 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
node_modules
**/node_modules
tools/quality
typescript/dist
typescript/src/generated
quality/reports
quality/generator/openapi.yaml
quality/generator/postprocess.cjs
java
kotlin
scala
csharp
python
rust
ruby
5 changes: 5 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100
}
24 changes: 24 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Repository quality and ownership

The TypeScript wrapper uses the strict authored TypeScript profile; generated TypeScript uses profile D. Run `npm ci`, `npm --prefix tools/quality ci`, then the scripts in `.github/workflows/quality.yml`. Build and tests are offline and must not contact a live instance. Never publish or deploy as part of quality checks.

`typescript/src/generated/src` is reproduced by `node quality/generator/generate.mjs` from the pinned public contract, OpenAPI Generator 7.22.0 JAR checksum and checksummed TypeScript producer. Edit the contract/producer and intentionally run `--write`; review the entire output inventory. Never hand-edit generated code or broaden ignores.

Authored functions and methods require useful comments explaining purpose, inputs/results and relevant edge cases or I/O. Document ownership, error handling and async lifecycles for AI readers. Keep comments accurate; do not paraphrase the method name. The wrapper owns client configuration; generated API groups own wire serialization. Runtime modules cannot import tests; cycles and unresolved dependencies fail.

Seven other language trees currently have addressed temporary exceptions in `quality/exceptions/inventory.json`, owned by 1MSG SDK maintainers and expiring 2026-12-11 under 1MSG-156. Their generator/build/native tests are **not verified**. CI verifies every byte against the adoption commit and rejects additions (including ignored files), deletions, edits, expiry and exception renewal. Replace an exception with a reviewed native generation/build/contract/package profile before changing that language. A passing exception guard does not mean native tests passed.

Historical Knip, clone and maintainability debt is finding-specific, owned and dated. CI cannot add baselines; new or larger findings fail. Shared preset 1.0.12 is sealed; update it through the common producer, never by local edits.

The `quality-required` aggregate fails on unsuccessful prerequisites. Repository branch protection must separately require this job; workflow naming alone does not enable protection.

The root quality program resolves ambient types from both root and TypeScript workspace installations so the existing Jest tests retain their real declarations. No test is excluded. `knip.config.mjs` uses the same source inventory for both graphs; the Java binary prerequisite is declared only when its generator exists in the analyzed source, including trusted-base replays. No source files or issue categories are suppressed.

The current TypeScript contract and generated documentation were replayed from API
commit `929c4526d3c914984be5a9bab561890a5c610433`. The provenance release record
identifies an offline validation artifact (`offline-1msg-156-final`), not an npm
publication. All 113 generated runtime files reproduce exactly and remain
unchanged. The producer now transfers a checksummed same-run envelope, preserves
consumer tests/configuration/locks, and runs every consumer gate before a sync
commit/push. Runtime packaging or generator changes require a reviewed consumer
update; synchronization must never replace this repository's quality policy.
42 changes: 21 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

Official client SDKs for the [1MSG](https://1msg.io) WhatsApp Business API.

| | |
|---|---|
| **API docs** | [docs.1msg.io](https://docs.1msg.io/) |
| **Platform** | [platform.1msg.io](https://platform.1msg.io/) |
| | |
| --------------- | ---------------------------------------------------------------------------- |
| **API docs** | [docs.1msg.io](https://docs.1msg.io/) |
| **Platform** | [platform.1msg.io](https://platform.1msg.io/) |
| **Quick start** | [help.1msg.io](https://help.1msg.io/platform-1msg/getting-start/quick-start) |
| **Changelog** | [CHANGELOG.md](./CHANGELOG.md) |
| **Versioning** | [docs/VERSIONING.md](./docs/VERSIONING.md) |
| **Support** | support@1msg.io |
| **Changelog** | [CHANGELOG.md](./CHANGELOG.md) |
| **Versioning** | [docs/VERSIONING.md](./docs/VERSIONING.md) |
| **Support** | support@1msg.io |

Public OpenAPI contract: `1.0.0`. 1MSG API service (git tags): `v3.3.0`.
SDK packages track the public contract (`1.0.x`). Where a registry already
Expand Down Expand Up @@ -163,17 +163,17 @@ Source: [github.com/1msg/1msg-sdk-cpp](https://github.com/1msg/1msg-sdk-cpp)

## Published versions

| Language | Registry name | Version |
|----------|---------------|---------|
| TypeScript | `@1msg/sdk` | 1.0.3 |
| Python | `one-msg-sdk` | 1.0.2 |
| Java | `io.1msg:sdk` | 1.0.2 |
| Kotlin | `io.1msg:sdk-kotlin` | 1.0.3 |
| Scala | `io.1msg:sdk-scala` | 1.0.3 |
| C# | `OneMsg.Sdk` | 2.0.1 |
| Rust | `one-msg-sdk` | 2.0.2 |
| Ruby | `one_msg_sdk` | 2.0.2 |
| Go | `github.com/1msg/1msg-sdk-go` | v1.1.1 |
| Swift | `1msg/1msg-sdk-swift` | 1.1.1 |
| PHP | `1msg/sdk` | 1.1.1 (repo) |
| C++ | `one-msg-sdk` | 1.0.1 (repo) |
| Language | Registry name | Version |
| ---------- | ----------------------------- | ------------ |
| TypeScript | `@1msg/sdk` | 1.0.3 |
| Python | `one-msg-sdk` | 1.0.2 |
| Java | `io.1msg:sdk` | 1.0.2 |
| Kotlin | `io.1msg:sdk-kotlin` | 1.0.3 |
| Scala | `io.1msg:sdk-scala` | 1.0.3 |
| C# | `OneMsg.Sdk` | 2.0.1 |
| Rust | `one-msg-sdk` | 2.0.2 |
| Ruby | `one_msg_sdk` | 2.0.2 |
| Go | `github.com/1msg/1msg-sdk-go` | v1.1.1 |
| Swift | `1msg/1msg-sdk-swift` | 1.1.1 |
| PHP | `1msg/sdk` | 1.1.1 (repo) |
| C++ | `one-msg-sdk` | 1.0.1 (repo) |
10 changes: 5 additions & 5 deletions docs/VERSIONING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

`1msg-api` has three numbers that look like versions:

| Number | Where | What it is |
|--------|-------|------------|
| `v3.3.0` | git tags + `CHANGELOG.md` | Service / deploy version of 1msg-api |
| `1.0.0` | `public/openapi.bundled.yaml` `info.version` | Public API **contract** customers integrate against |
| `1.0.1` | root / Nest `package.json` | Stale npm workspace field; not the release number |
| Number | Where | What it is |
| -------- | -------------------------------------------- | --------------------------------------------------- |
| `v3.3.0` | git tags + `CHANGELOG.md` | Service / deploy version of 1msg-api |
| `1.0.0` | `public/openapi.bundled.yaml` `info.version` | Public API **contract** customers integrate against |
| `1.0.1` | root / Nest `package.json` | Stale npm workspace field; not the release number |

SDK packages track the **public contract** (`1.0.x`), not the service tag
(`v3.3.0`). Jumping every SDK to `3.3.0` would be a fake major and would not
Expand Down
28 changes: 28 additions & 0 deletions eslint.complexity.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { maintainabilityConfig, tseslint } from './tools/quality/eslint.mjs';
export default [
{ files: ['**/*.jsx'], languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } } },
{ linterOptions: { reportUnusedDisableDirectives: 'off' } },
{
ignores: [
'**/node_modules/**',
'**/dist/**',
'**/tools/quality/**',
'**/quality/reports/**',
'typescript/src/generated/**',
'quality/generator/postprocess.cjs',
'java/**',
'kotlin/**',
'scala/**',
'csharp/**',
'python/**',
'rust/**',
'ruby/**',
],
},
{
files: ['**/*.{ts,tsx,cts,mts}'],
languageOptions: { parser: tseslint.parser },
plugins: { '@typescript-eslint': tseslint.plugin },
},
...maintainabilityConfig(),
];
31 changes: 31 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { staticConfig, globals } from './tools/quality/eslint.mjs';

export default [
{ files: ['**/*.jsx'], languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } } },
{
ignores: [
'**/node_modules/**',
'**/dist/**',
'**/tools/quality/**',
'**/quality/reports/**',
'typescript/src/generated/**',
'quality/generator/postprocess.cjs',
'java/**',
'kotlin/**',
'scala/**',
'csharp/**',
'python/**',
'rust/**',
'ruby/**',
],
},
...staticConfig({ globals: { ...globals.node, ...globals.jest } }),
{
settings: {
'import/resolver': {
typescript: { project: './typescript/tsconfig.quality.json' },
node: { extensions: ['.js', '.json', '.ts', '.mjs', '.cjs'] },
},
},
},
];
24 changes: 24 additions & 0 deletions eslint.typed.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { typedConfig } from './tools/quality/eslint.mjs';
export default [
{
ignores: [
'**/node_modules/**',
'**/dist/**',
'**/tools/quality/**',
'**/quality/reports/**',
'typescript/src/generated/**',
'quality/generator/postprocess.cjs',
'java/**',
'kotlin/**',
'scala/**',
'csharp/**',
'python/**',
'rust/**',
'ruby/**',
],
},
...typedConfig({
project: './tsconfig.quality.json',
tsconfigRootDir: import.meta.dirname,
}),
];
42 changes: 42 additions & 0 deletions knip.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { existsSync } from 'node:fs';

const inventory = {
workspaces: {
'.': {
entry: [
'scripts/pack-smoke.mjs',
'scripts/quality-negative.mjs',
'scripts/quality-hook.test.mjs',
'scripts/quality-extensions.test.mjs',
'quality/generator/generate.mjs!',
'quality/generator/verify-negative.mjs',
'quality/generator/exact-optional.test.mjs',
'quality/exceptions/verify.mjs',
'quality/exceptions/verify.test.mjs',
'eslint*.mjs',
'quality.config.mjs',
'knip.config.mjs',
],
project: [
'scripts/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}!',
'quality/generator/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}!',
'quality/exceptions/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}!',
],
},
typescript: {
entry: ['src/index.ts!', 'src/__tests__/**/*.{spec,test}.{js,jsx,mjs,cjs,ts,tsx,mts,cts}'],
project: [
'src/**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}!',
'src/generated/**/*.ts!',
'src/__tests__/**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}',
],
},
},
ignore: ['tools/quality/**', 'typescript/src/generated/**'],
};

// Java belongs to the pinned generator. Trusted-base replays before that generator
// existed must not inherit an exclusion for a binary absent from their graph.
const hasGenerator = existsSync(new URL('./quality/generator/generate.mjs', import.meta.url));
const config = { ...inventory, ignoreBinaries: hasGenerator ? ['java'] : [] };
export default config;
Loading
Loading