Merge branch 'devel' into release-3.5

This commit is contained in:
Italo José
2026-03-27 11:07:30 -03:00
committed by GitHub
14 changed files with 532 additions and 6 deletions

95
.coderabbit.yaml Normal file
View File

@@ -0,0 +1,95 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
language: "en-US"
reviews:
profile: "chill" # community repo — keep it welcoming
request_changes_workflow: false
high_level_summary: true
poem: false # serious OSS platform
in_progress_fortune: false # noise
review_status: false
review_details: false
commit_status: true
collapse_walkthrough: true
changed_files_summary: true
sequence_diagrams: false # overkill for package-level PRs
estimate_code_review_effort: true
assess_linked_issues: true
related_issues: true
related_prs: true
suggested_labels: true
auto_apply_labels: false
suggested_reviewers: true
auto_assign_reviewers: false
# Exclude generated, build, and Meteor-internal files
path_filters:
- "!**/node_modules/**"
- "!**/.meteor/**"
- "!**/bundle/**"
- "!**/programs/**"
- "!**/*.min.js"
- "!**/cordova-build/**"
- "!**/package-lock.json"
path_instructions:
- path: "packages/**"
instructions: >
This is a core Meteor Atmosphere package. Focus on API backwards
compatibility, DDP/reactivity correctness, and client/server split.
Avoid nitpicking style — the codebase has legacy patterns.
- path: "tools/**"
instructions: >
This is the Meteor build tool (Isobuild). Be thorough about
correctness, edge cases, and performance in the CLI/build pipeline.
- path: "npm-packages/**"
instructions: >
These are npm packages published from the Meteor monorepo.
Check for correct exports, peer dependency handling, and Node.js compatibility.
- path: "v3-docs/**"
instructions: >
Documentation for Meteor v3. Check for accuracy, clarity, and
correct code examples. Grammar and spelling matter here.
- path: "scripts/**"
instructions: >
Build and CI scripts. Focus on correctness, portability, and
error handling.
auto_review:
enabled: true
drafts: false
auto_incremental_review: true
auto_pause_after_reviewed_commits: 3
ignore_title_keywords:
- "WIP"
- "DO NOT MERGE"
base_branches: []
finishing_touches:
docstrings:
enabled: false # legacy JS — too much noise across 100s of packages
unit_tests:
enabled: true
simplify:
enabled: false
tools:
shellcheck:
enabled: true # ✅ they have .sh scripts in /scripts
markdownlint:
enabled: true # ✅ heavy docs contribution
languagetool:
enabled: true # ✅ useful for international doc contributors
level: "default"
disabled_categories:
- "TYPOGRAPHY" # too nitpicky for code comments
ruff:
enabled: false # ❌ not a Python project
biome:
enabled: false # ❌ they use ESLint already (.eslintignore exists)
ast-grep:
essential_rules: true
chat:
auto_reply: true

View File

@@ -22,8 +22,11 @@ Test patterns, commands, and utilities for the Meteor codebase.
./meteor test-packages mongo # Test specific package
TINYTEST_FILTER="collection" ./meteor test-packages # Filter specific tests
# Package tests in console (headless via Puppeteer)
# Package tests in console (headless via Puppeteer — prints results to terminal)
# Use this for automation or when you need terminal output without a browser.
PUPPETEER_DOWNLOAD_PATH=~/.npm/chromium ./packages/test-in-console/run.sh
./packages/test-in-console/run.sh # Test all core packages
./packages/test-in-console/run.sh "mongo" # Test specific package
# Modern E2E tests (Jest + Playwright)
npm run install:modern # Install dependencies

View File

@@ -17,7 +17,7 @@ jobs:
uses: actions/checkout@v3
- name: Manage inactive issues
uses: actions/github-script@v6
uses: actions/github-script@v8
with:
script: |
const script = require('./.github/scripts/inactive-issues.js')

View File

@@ -8,11 +8,17 @@ Full-stack JavaScript platform for modern web and mobile applications.
./meteor run # Run from source
./meteor create my-app # Create app
./meteor self-test # CLI tests
./meteor test-packages ./packages/<name> # Package tests
./meteor test-packages ./packages/<name> # Package tests (browser UI at localhost:3000)
./packages/test-in-console/run.sh "<name>" # Package tests (terminal output via Puppeteer)
npm run test:unit # Unit tests (Jest)
npm run test:e2e # E2E tests (Jest + Playwright)
```
> **Note:** `./meteor test-packages` starts a web server and waits for a browser —
> it produces no terminal output. For automated/headless runs, use
> `./packages/test-in-console/run.sh "<package>"` instead, which runs the same tests
> via Puppeteer and prints pass/fail results to stdout.
## Structure
```

View File

@@ -33,6 +33,14 @@ can run Meteor directly from a Git checkout using these steps:
$ ./meteor --help
```
> **Note for Windows (PowerShell):**
>
> * In PowerShell, use `.\meteor` (not `./meteor`).
> * Meteor may need `7z.exe` available in your `PATH` to download/extract binaries (dev_bundle).
> * Verify: `where.exe 7z`
> * If missing, install 7-Zip and ensure it is on your PATH (for example via `choco install 7zip -y` or `scoop install 7zip`).
3. **Ready to Go!**
Your local Meteor checkout is now ready to use! You can use this `./meteor`

View File

@@ -16,7 +16,7 @@ async function runNextUrl(browser) {
if (text.includes('Permissions policy violation')) {
return;
}
if (msg._text !== undefined) console.log(msg._text);
if (text) console.log(text);
else {
testNumber++;
const currentClientTest =

View File

@@ -3,4 +3,7 @@
/.vitepress/dist
/data/data.js
/data/names.json
/data/names.json
# Generated API reference for LLMs
/public/api-reference.json

View File

@@ -461,6 +461,14 @@ export default defineConfig({
text: "jam:offline",
link: "/community-packages/offline",
},
{
text: "dupontbertrand:cluster",
link: "/community-packages/cluster",
},
{
text: "dupontbertrand:mail-preview",
link: "/community-packages/mail-preview",
},
],
collapsed: true,
},
@@ -681,7 +689,27 @@ export default defineConfig({
vite: {
plugins: [
llmstxt({
title: "Meteor.js 3 Docs",
title: "Meteor.js 3 Documentation",
domain: "https://docs.meteor.com",
description: "Full-stack JavaScript platform for modern web and mobile applications.",
details: `
Meteor is a full-stack JavaScript platform for developing web and mobile applications.
Key capabilities:
- Real-time data synchronization with publications and subscriptions
- Built-in accounts and authentication system
- Frontend agnostic (React, Vue, Solid, Blaze, Svelte)
- Zero-config build system with modern tooling (SWC, Rspack)
- One-command deployment to Galaxy Cloud
- TypeScript support with full type inference
Current version: Meteor ${metadata.currentVersion}.
## Structured API Data
For complete API documentation in machine-readable format, see:
- [api-reference.json](/api-reference.json) - Full API reference with all functions, parameters, and types
`.trim(),
}),
],
},

View File

@@ -397,6 +397,46 @@ With the MeteorRspack integration, `zodern:melte` no longer works. Use the of
Meteor-Rspack comes with built-in CSS support. You can import any CSS file into your code, and it will be processed and included in your HTML skeleton automatically. In addition, any CSS file placed in the same folder as your Meteor entry point will be processed and added as global styles without the need for explicit imports.
### CSS Modules
[CSS Modules](https://rspack.rs/guide/tech/css#css-modules) are supported out of the box — any file named `*.module.css` is automatically scoped locally.
By default, rspack uses **named exports**, so imports look like:
``` js
import { app } from './App.module.css';
```
If you prefer **default imports** (`import styles from './App.module.css'`), disable `namedExports` on both the `css/auto` and `css/module` parsers:
``` js
module.exports = defineConfig(Meteor => ({
module: {
parser: {
'css/auto': {
namedExports: false,
},
'css/module': {
namedExports: false,
},
},
},
}));
```
#### TypeScript
When using CSS Modules with TypeScript, add a declaration file (e.g. `imports/css-modules.d.ts`) so the compiler recognizes `.module.css` imports:
``` typescript
declare module '*.module.css' {
const classes: { readonly [key: string]: string };
export default classes;
}
```
For more details, check [the official Rspack CSS Modules guide](https://rspack.rs/guide/tech/css#css-modules).
### Less
Less support is available in Meteor-Rspack. You need to replace the existing [Meteor `less` package](https://github.com/meteor/meteor/tree/master/packages/non-core/less) or similar with the Rspack configuration.

View File

@@ -0,0 +1,152 @@
# Cluster
- `Who maintains the package` [Bertrand Dupont](https://github.com/dupontbertrand)
[[toc]]
## What Is It?
A Meteor 3 compatible fork of [`meteorhacks:cluster`](https://github.com/meteorhacks/cluster), providing multi-core support, load balancing, and service discovery for Meteor apps.
The original package was abandoned around 2016. This fork ports it to Meteor 3 with async/await, modern dependencies, and updated MongoDB driver — while preserving the same public API.
::: warning
This is a **compatibility / migration bridge**, not a new recommended scaling architecture. If you only need multi-core CPU utilization, an external process manager like [PM2](https://pm2.keymetrics.io/) is simpler. This package is for apps that relied on `meteorhacks:cluster` features like service discovery, DDP-aware proxying, or `Cluster.discoverConnection()`.
:::
## How to Download It?
```bash
meteor add dupontbertrand:cluster
```
## How to Use It?
### Multi-Core (Simplest Use Case)
Just set the `CLUSTER_WORKERS_COUNT` environment variable:
```bash
# Use all available CPU cores
CLUSTER_WORKERS_COUNT=auto meteor run
# Or specify a number
CLUSTER_WORKERS_COUNT=4 meteor run
```
The package forks child processes automatically. No code changes needed.
### Service Discovery + Load Balancing
For multi-instance deployments with automatic service registration and DDP-aware load balancing:
```bash
export CLUSTER_DISCOVERY_URL="mongodb://your-mongo-host/cluster"
export CLUSTER_SERVICE="web"
export CLUSTER_WORKERS_COUNT=2
```
Instances register themselves in MongoDB and discover each other automatically. No need to reconfigure a load balancer when adding or removing instances.
### Microservices
Connect to other services in the cluster by name:
```js
// Server
const serviceConnection = Cluster.discoverConnection('payments');
// Call methods on the remote service
const result = await serviceConnection.callAsync('processPayment', data);
```
## API
### `Cluster.connect(discoveryUrl, [options])`
Connect to a discovery backend (currently MongoDB).
```js
Cluster.connect('mongodb://host/db');
```
Usually configured via the `CLUSTER_DISCOVERY_URL` environment variable instead of calling directly.
### `Cluster.register(serviceName, [options])`
Register this instance as a named service.
```js
Cluster.register('web');
```
Options:
- `endpoint` — the URL other instances use to reach this one (defaults to `ROOT_URL`)
- `balancer` — URL of the balancer (defaults to `CLUSTER_BALANCER_URL`)
- `uiService` — the service that serves the UI (defaults to the registered service name)
Usually configured via the `CLUSTER_SERVICE` environment variable.
### `Cluster.discoverConnection(serviceName, [ddpOptions])`
Get a DDP connection to a named service. The connection automatically tracks healthy instances via the discovery backend.
```js
const conn = Cluster.discoverConnection('analytics');
const data = await conn.callAsync('getReport', params);
```
### `Cluster.allowPublicAccess(serviceList)`
Allow public (non-authenticated) access to specific services.
```js
Cluster.allowPublicAccess(['web', 'api']);
```
Usually configured via the `CLUSTER_PUBLIC_SERVICES` environment variable (comma-separated).
## Environment Variables
| Variable | Description | Default |
| -------- | ----------- | ------- |
| `CLUSTER_WORKERS_COUNT` | Number of worker processes (`auto` = all cores) | 1 (no clustering) |
| `CLUSTER_DISCOVERY_URL` | MongoDB URL for service discovery | — |
| `CLUSTER_SERVICE` | Name to register this instance as | — |
| `CLUSTER_ENDPOINT_URL` | URL for other instances to reach this one | `ROOT_URL` |
| `CLUSTER_BALANCER_URL` | URL of the load balancer | — |
| `CLUSTER_PUBLIC_SERVICES` | Comma-separated list of public services | — |
| `CLUSTER_UI_SERVICE` | Service that serves the UI | same as `CLUSTER_SERVICE` |
## What Changed From meteorhacks:cluster
This fork preserves the original API. Under the hood:
- MongoDB discovery backend rewritten in **async/await** (was Fibers/wrapAsync)
- MongoDB driver updated from 1.4.x to **6.12.0**
- `underscore` replaced with native ES2015+
- npm dependencies updated (`cookies`, `http-proxy`, `portscanner`)
- Fixed `Buffer()` deprecation and a pre-existing IPC listener bug
- Balancer made transport-aware for compatibility with pluggable transports ([#14231](https://github.com/meteor/meteor/pull/14231))
- Test suite adapted for Meteor 3
Full changelog: [METEOR3-PORT.md](https://github.com/dupontbertrand/cluster/blob/master/METEOR3-PORT.md)
## Known Limitations
- The balancer uses `OverShadowServerEvent` to intercept HTTP/WS traffic at the `httpServer` level. This is invasive and could break if Meteor changes its listener initialization order.
- No Windows testing yet.
- The worker pool uses `child_process.fork` with per-worker ports (not `node:cluster`).
## Compatibility
- Meteor 3.4+
- Node 22
- Tested on Linux
## Sources
- **Atmosphere:** [dupontbertrand:cluster](https://atmospherejs.com/dupontbertrand/cluster)
- **GitHub:** [dupontbertrand/cluster](https://github.com/dupontbertrand/cluster)
- **Original package:** [meteorhacks/cluster](https://github.com/meteorhacks/cluster)
- **Forum Discussion:** [Memory usage and sessions quadruple after upgrading to Meteor 3](https://forums.meteor.com/t/memory-usage-and-sessions-quadruple-after-upgrading-to-meteor-3/64496)

View File

@@ -40,6 +40,14 @@ Please bear in mind if you are adding a package to this list, try providing as m
- [`jam:soft-delete`](./soft-delete.md), An easy way to add soft deletes to your Meteor app
- [`jam:archive`](./archive.md),
#### Scaling / Clustering
- [`dupontbertrand:cluster`](./cluster.md), Meteor 3 fork of meteorhacks:cluster — multi-core, load balancing, service discovery
#### Developer tools
- [`dupontbertrand:mail-preview`](./mail-preview.md), Zero-config dev-mode mail preview UI — view captured emails at `/__meteor_mail__/`
#### Utilities
- [`jam:offline`](./offline.md), An easy way to give your Meteor app offline capabilities and make it feel instant

View File

@@ -0,0 +1,123 @@
# Mail Preview
- `Who maintains the package` [Bertrand Dupont](https://github.com/dupontbertrand)
[[toc]]
## What Is It?
A zero-config, dev-mode mail preview UI for Meteor. Every email sent via `Email.sendAsync()` is captured and displayed in a browser UI at `/__meteor_mail__/`.
Inspired by similar features in Rails (Action Mailer Preview), Phoenix (Swoosh), Laravel (Mailtrap), and Django (console backend).
This is a `devOnly` package — it is **automatically excluded from production builds**. Zero overhead in production, no need to remove it before deploying.
## How to Download It?
```bash
meteor add dupontbertrand:mail-preview
```
That's it. No configuration needed.
## How to Use It?
1. Start your Meteor app in development mode (`meteor run`)
2. Trigger any email — password reset, verification, enrollment, or your own `Email.sendAsync()` calls
3. Open `http://localhost:3000/__meteor_mail__/` in your browser
### What You Get
- **Mail list** — live-updating list of all captured emails (polls every 2s, no page reload)
- **Mail detail** — view each email with tabs for **HTML render**, **Plain Text**, and **HTML Source**
- **Clickable links** — verification, password reset, and enrollment links work directly from the preview
- **JSON API** — programmatic access for testing and tooling
- **Clear all** — one-click button to clear captured mails
### Example: Capturing an Accounts Email
```js
// Server — nothing special needed, just use Meteor's built-in accounts
import { Accounts } from 'meteor/accounts-base';
// When a user registers, Meteor sends a verification email.
// mail-preview captures it automatically.
Accounts.sendVerificationEmail(userId);
```
Then open `/__meteor_mail__/` to see the captured email, click the verification link, and it works.
### Example: Custom Emails with MJML
```js
// Server
import { Email } from 'meteor/email';
import mjml2html from 'mjml';
const { html } = mjml2html(`
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-text font-size="20px" color="#333">Hello from Meteor!</mj-text>
<mj-button background-color="#0d6efd" href="https://meteor.com">
Visit Meteor
</mj-button>
</mj-column>
</mj-section>
</mj-body>
</mjml>
`);
await Email.sendAsync({
from: 'noreply@example.com',
to: 'user@example.com',
subject: 'Welcome!',
html,
});
```
The MJML-rendered email is captured and displayed with full HTML rendering in the preview UI.
## JSON API
For programmatic access (useful in tests or tooling):
| Method | Endpoint | Description |
| -------- | ------------------------------ | ------------------------ |
| `GET` | `/__meteor_mail__/api/mails` | List all captured mails |
| `GET` | `/__meteor_mail__/api/mails/:id` | Get a single mail |
| `DELETE` | `/__meteor_mail__/api/mails` | Clear all captured mails |
### Example: Using the API in Tests
```js
// In a test, after triggering an email:
const res = await fetch('http://localhost:3000/__meteor_mail__/api/mails');
const { mails } = await res.json();
assert.equal(mails[0].subject, 'Verify your email');
assert.ok(mails[0].text.includes('verify-email'));
```
## How It Works
The package uses `Email.hookSend()` to intercept outgoing emails and store them in memory (up to 50 — oldest are evicted). A middleware mounted via `WebApp.rawConnectHandlers` serves the preview UI.
- **Dev mode only** — guarded by `Meteor.isDevelopment` and `devOnly: true` in `package.js`
- **No SMTP needed** — emails are captured before they reach any transport
- **No external dependencies** — uses only Meteor core packages (`email`, `webapp`, `ecmascript`)
- **Works alongside `MAIL_URL`** — if set, emails are still sent normally; the hook captures a copy
## Compatibility
- Meteor 3.4+
- Works with `accounts-password` emails (verification, reset password, enrollment)
- Works with custom `Email.sendAsync()` / `Email.send()` calls
- Compatible with Rspack bundler
## Sources
- **Atmosphere:** [dupontbertrand:mail-preview](https://atmospherejs.com/dupontbertrand/mail-preview)
- **GitHub:** [dupontbertrand/meteor-mail-preview](https://github.com/dupontbertrand/meteor-mail-preview)
- **Forum Discussion:** [Built-in Mail Preview UI for Dev Mode](https://forums.meteor.com/t/built-in-mail-preview-ui-for-dev-mode/64489)

View File

@@ -0,0 +1,57 @@
/**
* Generates a public JSON file from the JSDoc API data.
* This file is accessible to LLMs at /api-reference.json
*/
const fs = require('fs');
const path = require('path');
function parseApiData(dataSource) {
const json = dataSource
.replace(/^(?:\/\/.*\n\s*)*export default\s*/, '')
.replace(/;\s*$/, '');
return JSON.parse(json);
}
exports.generateApiJson = async function generateApiJson() {
console.log("📦 Generating API reference JSON for LLMs...");
const dataPath = path.join(__dirname, '../../data/data.js');
const publicDir = path.join(__dirname, '../../public');
const outputPath = path.join(publicDir, 'api-reference.json');
// Check if data.js exists
if (!fs.existsSync(dataPath)) {
console.log("⚠️ data/data.js not found. Run 'npm run generate-jsdoc' first.");
return;
}
try {
const apiData = parseApiData(fs.readFileSync(dataPath, 'utf8'));
// Create public directory if it doesn't exist
if (!fs.existsSync(publicDir)) {
fs.mkdirSync(publicDir, { recursive: true });
}
// Add metadata
const output = {
_meta: {
generator: "Meteor Docs API Export",
generated: new Date().toISOString(),
description: "API reference for Meteor.js - for LLM consumption",
url: "https://docs.meteor.com/api-reference.json"
},
apis: apiData
};
// Write the JSON file
fs.writeFileSync(outputPath, JSON.stringify(output, null, 2));
const apiCount = Object.keys(apiData).length;
console.log(`✅ Generated api-reference.json with ${apiCount} APIs`);
} catch (err) {
console.error("❌ Error generating API JSON:", err.message);
}
};

View File

@@ -1,11 +1,14 @@
const { generateChangelog } = require("./changelog/script.js");
const { listPackages } = require("./packages-listing/script.js");
const { generateMeteorVersions } = require("./meteor-versions/script.js");
const { generateApiJson } = require("./api-export/generateApiJson.js");
async function main() {
console.log("🚂 Started codegen 🚂");
await generateChangelog();
await listPackages();
await generateMeteorVersions();
await generateApiJson();
console.log("🚀 Done codegen 🚀");
}