diff --git a/tools/modern-tests/apps/typescript/.gitignore b/tools/modern-tests/apps/typescript/.gitignore
new file mode 100644
index 0000000000..c2658d7d1b
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/.gitignore
@@ -0,0 +1 @@
+node_modules/
diff --git a/tools/modern-tests/apps/typescript/.meteor/.gitignore b/tools/modern-tests/apps/typescript/.meteor/.gitignore
new file mode 100644
index 0000000000..4083037423
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/.meteor/.gitignore
@@ -0,0 +1 @@
+local
diff --git a/tools/modern-tests/apps/typescript/.meteor/.id b/tools/modern-tests/apps/typescript/.meteor/.id
new file mode 100644
index 0000000000..e9e2dc15a7
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/.meteor/.id
@@ -0,0 +1,7 @@
+# This file contains a token that is unique to your project.
+# Check it into your repository along with the rest of this directory.
+# It can be used for purposes such as:
+# - ensuring you don't accidentally deploy one app on top of another
+# - providing package authors with aggregated statistics
+
+k0367lnvnclor.z6cmuyuro69u
diff --git a/tools/modern-tests/apps/typescript/.meteor/packages b/tools/modern-tests/apps/typescript/.meteor/packages
new file mode 100644
index 0000000000..993162a9f0
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/.meteor/packages
@@ -0,0 +1,23 @@
+# Meteor packages used by this project, one per line.
+# Check this file (and the other files in this directory) into your repository.
+#
+# 'meteor add' and 'meteor remove' will edit this file for you,
+# but you can also edit it by hand.
+
+meteor-base # Packages every Meteor app needs to have
+mobile-experience # Packages for a great mobile UX
+mongo # The database Meteor supports right now
+reactive-var # Reactive variable for tracker
+
+standard-minifier-css # CSS minifier run for production mode
+standard-minifier-js # JS minifier run for production mode
+es5-shim # ECMAScript 5 compatibility for older browsers
+ecmascript # Enable ECMAScript2015+ syntax in app code
+typescript # Enable TypeScript syntax in .ts and .tsx modules
+shell-server # Server-side component of the `meteor shell` command
+hot-module-replacement # Update client in development without reloading the page
+
+
+static-html # Define static page content in .html files
+react-meteor-data # React higher-order component for reactively tracking Meteor data
+zodern:types # Pull in type declarations from other Meteor packages
diff --git a/tools/modern-tests/apps/typescript/.meteor/platforms b/tools/modern-tests/apps/typescript/.meteor/platforms
new file mode 100644
index 0000000000..efeba1b50c
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/.meteor/platforms
@@ -0,0 +1,2 @@
+server
+browser
diff --git a/tools/modern-tests/apps/typescript/.meteor/release b/tools/modern-tests/apps/typescript/.meteor/release
new file mode 100644
index 0000000000..621e94f0ec
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/.meteor/release
@@ -0,0 +1 @@
+none
diff --git a/tools/modern-tests/apps/typescript/.meteor/versions b/tools/modern-tests/apps/typescript/.meteor/versions
new file mode 100644
index 0000000000..ee3f71c234
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/.meteor/versions
@@ -0,0 +1,68 @@
+allow-deny@2.1.0
+autoupdate@2.0.1
+babel-compiler@7.12.1
+babel-runtime@1.5.2
+base64@1.0.13
+binary-heap@1.0.12
+boilerplate-generator@2.0.1
+caching-compiler@2.0.1
+callback-hook@1.6.1
+check@1.4.4
+core-runtime@1.0.0
+ddp@1.4.2
+ddp-client@3.1.1
+ddp-common@1.4.4
+ddp-server@3.1.2
+diff-sequence@1.1.3
+dynamic-import@0.7.4
+ecmascript@0.16.12
+ecmascript-runtime@0.8.3
+ecmascript-runtime-client@0.12.3
+ecmascript-runtime-server@0.11.1
+ejson@1.1.5
+es5-shim@4.8.1
+facts-base@1.0.2
+fetch@0.1.6
+geojson-utils@1.0.12
+hot-code-push@1.0.5
+hot-module-replacement@0.5.4
+id-map@1.2.0
+inter-process-messaging@0.1.2
+launch-screen@2.0.1
+logging@1.3.6
+meteor@2.1.1
+meteor-base@1.5.2
+minifier-css@2.0.1
+minifier-js@3.0.3
+minimongo@2.0.3
+mobile-experience@1.1.2
+mobile-status-bar@1.1.1
+modern-browsers@0.2.3
+modules@0.20.3
+modules-runtime@0.13.2
+modules-runtime-hot@0.14.3
+mongo@2.1.3
+mongo-decimal@0.2.0
+mongo-dev-server@1.1.1
+mongo-id@1.0.9
+npm-mongo@6.16.0
+ordered-dict@1.2.0
+promise@1.0.0
+random@1.2.2
+react-fast-refresh@0.2.9
+react-meteor-data@4.0.0
+reactive-var@1.0.13
+reload@1.3.2
+retry@1.1.1
+routepolicy@1.1.2
+shell-server@0.6.1
+socket-stream-client@0.6.1
+standard-minifier-css@1.9.3
+standard-minifier-js@3.1.1
+static-html@1.4.0
+static-html-tools@1.0.0
+tracker@1.3.4
+typescript@5.6.5
+webapp@2.0.7
+webapp-hashing@1.1.2
+zodern:types@1.0.13
diff --git a/tools/modern-tests/apps/typescript/client/main.css b/tools/modern-tests/apps/typescript/client/main.css
new file mode 100644
index 0000000000..7f354f0fa7
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/client/main.css
@@ -0,0 +1,4 @@
+body {
+ padding: 10px;
+ font-family: sans-serif;
+}
diff --git a/tools/modern-tests/apps/typescript/client/main.html b/tools/modern-tests/apps/typescript/client/main.html
new file mode 100644
index 0000000000..7526f5d325
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/client/main.html
@@ -0,0 +1,8 @@
+
+ typescript
+
+
+
+
+
+
diff --git a/tools/modern-tests/apps/typescript/client/main.tsx b/tools/modern-tests/apps/typescript/client/main.tsx
new file mode 100644
index 0000000000..523141b528
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/client/main.tsx
@@ -0,0 +1,10 @@
+import React from 'react';
+import { createRoot } from 'react-dom/client';
+import { Meteor } from 'meteor/meteor';
+import { App } from '/imports/ui/App';
+
+Meteor.startup(() => {
+ const container = document.getElementById('react-target');
+ const root = createRoot(container!);
+ root.render( );
+});
diff --git a/tools/modern-tests/apps/typescript/imports/api/links.ts b/tools/modern-tests/apps/typescript/imports/api/links.ts
new file mode 100644
index 0000000000..ec0bf4631f
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/imports/api/links.ts
@@ -0,0 +1,10 @@
+import { Mongo } from 'meteor/mongo';
+
+export interface Link {
+ _id?: string;
+ title: string;
+ url: string;
+ createdAt: Date;
+}
+
+export const LinksCollection = new Mongo.Collection ('links');
diff --git a/tools/modern-tests/apps/typescript/imports/ui/App.tsx b/tools/modern-tests/apps/typescript/imports/ui/App.tsx
new file mode 100644
index 0000000000..d354e1b352
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/imports/ui/App.tsx
@@ -0,0 +1,11 @@
+import React from 'react';
+import { Hello } from './Hello';
+import { Info } from './Info';
+
+export const App = () => (
+
+
Welcome to Meteor!
+
+
+
+);
diff --git a/tools/modern-tests/apps/typescript/imports/ui/Hello.tsx b/tools/modern-tests/apps/typescript/imports/ui/Hello.tsx
new file mode 100644
index 0000000000..15e0f185ac
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/imports/ui/Hello.tsx
@@ -0,0 +1,16 @@
+import React, { useState } from 'react';
+
+export const Hello = () => {
+ const [counter, setCounter] = useState(0);
+
+ const increment = () => {
+ setCounter(counter + 1);
+ };
+
+ return (
+
+
Click Me
+
You've pressed the button {counter} times.
+
+ );
+};
diff --git a/tools/modern-tests/apps/typescript/imports/ui/Info.tsx b/tools/modern-tests/apps/typescript/imports/ui/Info.tsx
new file mode 100644
index 0000000000..23cb8f07a3
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/imports/ui/Info.tsx
@@ -0,0 +1,27 @@
+import React from "react";
+import { useFind, useSubscribe } from "meteor/react-meteor-data";
+import { LinksCollection, Link } from "../api/links";
+
+export const Info = () => {
+ const isLoading = useSubscribe("links");
+ const links = useFind(() => LinksCollection.find());
+
+ if (isLoading()) {
+ return Loading...
;
+ }
+
+ const makeLink = (link: Link) => {
+ return (
+
+ { link.title }
+
+ );
+ }
+
+ return (
+
+ );
+};
diff --git a/tools/modern-tests/apps/typescript/package-lock.json b/tools/modern-tests/apps/typescript/package-lock.json
new file mode 100644
index 0000000000..c1df4295f8
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/package-lock.json
@@ -0,0 +1,1609 @@
+{
+ "name": "typescript",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "typescript",
+ "dependencies": {
+ "@babel/runtime": "^7.23.5",
+ "@swc/helpers": "^0.5.17",
+ "meteor-node-stubs": "^1.2.12",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
+ },
+ "devDependencies": {
+ "@types/mocha": "^8.2.3",
+ "@types/node": "^22.10.6",
+ "@types/react": "^18.2.5",
+ "@types/react-dom": "^18.2.4",
+ "typescript": "^5.4.5"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.24.4",
+ "license": "MIT",
+ "dependencies": {
+ "regenerator-runtime": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.17",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
+ "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@types/mocha": {
+ "version": "8.2.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "22.17.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.17.1.tgz",
+ "integrity": "sha512-y3tBaz+rjspDTylNjAX37jEC3TETEFGNJL6uQDxwF9/8GLLIjW1rvVHlynyuUKMnMr1Roq8jOv3vkopBjC4/VA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.12",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "18.2.75",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/prop-types": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "18.2.24",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "license": "MIT"
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/meteor-node-stubs": {
+ "version": "1.2.22",
+ "resolved": "https://registry.npmjs.org/meteor-node-stubs/-/meteor-node-stubs-1.2.22.tgz",
+ "integrity": "sha512-xpHq9ExdN8fshx9CxOe1nheyWslAepejdkIQgTsk+96e46UJ4ms5TSiQX1rjd6rJuBOjIjHKLqqYvdTsHQj/pw==",
+ "bundleDependencies": [
+ "@meteorjs/crypto-browserify",
+ "assert",
+ "browserify-zlib",
+ "buffer",
+ "console-browserify",
+ "constants-browserify",
+ "domain-browser",
+ "events",
+ "https-browserify",
+ "os-browserify",
+ "path-browserify",
+ "process",
+ "punycode",
+ "querystring-es3",
+ "readable-stream",
+ "stream-browserify",
+ "stream-http",
+ "string_decoder",
+ "timers-browserify",
+ "tty-browserify",
+ "url",
+ "util",
+ "vm-browserify"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@meteorjs/crypto-browserify": "^3.12.1",
+ "assert": "^2.1.0",
+ "browserify-zlib": "^0.2.0",
+ "buffer": "^5.7.1",
+ "console-browserify": "^1.2.0",
+ "constants-browserify": "^1.0.0",
+ "domain-browser": "^4.23.0",
+ "events": "^3.3.0",
+ "https-browserify": "^1.0.0",
+ "os-browserify": "^0.3.0",
+ "path-browserify": "^1.0.1",
+ "process": "^0.11.10",
+ "punycode": "^1.4.1",
+ "querystring-es3": "^0.2.1",
+ "readable-stream": "^3.6.2",
+ "stream-browserify": "^3.0.0",
+ "stream-http": "^3.2.0",
+ "string_decoder": "^1.3.0",
+ "timers-browserify": "^2.0.12",
+ "tty-browserify": "0.0.1",
+ "url": "^0.11.4",
+ "util": "^0.12.5",
+ "vm-browserify": "^1.1.2"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/@meteorjs/browserify-sign": {
+ "version": "4.2.6",
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "bn.js": "^5.2.1",
+ "brorand": "^1.1.0",
+ "browserify-rsa": "^4.1.0",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash-base": "~3.0",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.1",
+ "inherits": "^2.0.4",
+ "minimalistic-assert": "^1.0.1",
+ "minimalistic-crypto-utils": "^1.0.1",
+ "parse-asn1": "^5.1.7",
+ "readable-stream": "^2.3.8",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.12"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/@meteorjs/browserify-sign/node_modules/isarray": {
+ "version": "1.0.0",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/@meteorjs/browserify-sign/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/@meteorjs/browserify-sign/node_modules/readable-stream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/@meteorjs/browserify-sign/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/@meteorjs/browserify-sign/node_modules/string_decoder/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/@meteorjs/create-ecdh": {
+ "version": "4.0.5",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.11.9",
+ "brorand": "^1.1.0",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.1",
+ "inherits": "^2.0.4",
+ "minimalistic-assert": "^1.0.1",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/@meteorjs/create-ecdh/node_modules/bn.js": {
+ "version": "4.12.2",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/@meteorjs/crypto-browserify": {
+ "version": "3.12.4",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "@meteorjs/browserify-sign": "^4.2.3",
+ "@meteorjs/create-ecdh": "^4.0.4",
+ "browserify-cipher": "^1.0.1",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "diffie-hellman": "^5.0.3",
+ "hash-base": "~3.0.4",
+ "inherits": "^2.0.4",
+ "pbkdf2": "^3.1.2",
+ "public-encrypt": "^4.0.3",
+ "randombytes": "^2.1.0",
+ "randomfill": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/asn1.js": {
+ "version": "4.10.1",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/asn1.js/node_modules/bn.js": {
+ "version": "4.12.2",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/assert": {
+ "version": "2.1.0",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "is-nan": "^1.3.2",
+ "object-is": "^1.1.5",
+ "object.assign": "^4.1.4",
+ "util": "^0.12.5"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/base64-js": {
+ "version": "1.5.1",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/bn.js": {
+ "version": "5.2.2",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/brorand": {
+ "version": "1.1.0",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/browserify-aes": {
+ "version": "1.2.0",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-xor": "^1.0.3",
+ "cipher-base": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.3",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/browserify-cipher": {
+ "version": "1.0.1",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserify-aes": "^1.0.4",
+ "browserify-des": "^1.0.0",
+ "evp_bytestokey": "^1.0.0"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/browserify-des": {
+ "version": "1.0.2",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "cipher-base": "^1.0.1",
+ "des.js": "^1.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/browserify-rsa": {
+ "version": "4.1.1",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^5.2.1",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/browserify-sign": {
+ "version": "4.2.3",
+ "license": "ISC",
+ "dependencies": {
+ "bn.js": "^5.2.1",
+ "browserify-rsa": "^4.1.0",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "elliptic": "^6.5.5",
+ "hash-base": "~3.0",
+ "inherits": "^2.0.4",
+ "parse-asn1": "^5.1.7",
+ "readable-stream": "^2.3.8",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.12"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/browserify-sign/node_modules/bn.js": {
+ "version": "5.2.1",
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/browserify-sign/node_modules/hash-base": {
+ "version": "3.0.4",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/browserify-sign/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/browserify-sign/node_modules/readable-stream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/browserify-sign/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/browserify-sign/node_modules/string_decoder/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/browserify-zlib": {
+ "version": "0.2.0",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "pako": "~1.0.5"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/buffer": {
+ "version": "5.7.1",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/buffer-xor": {
+ "version": "1.0.3",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/builtin-status-codes": {
+ "version": "3.0.0",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/call-bind": {
+ "version": "1.0.8",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/call-bound": {
+ "version": "1.0.4",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/cipher-base": {
+ "version": "1.0.6",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/console-browserify": {
+ "version": "1.2.0",
+ "inBundle": true
+ },
+ "node_modules/meteor-node-stubs/node_modules/constants-browserify": {
+ "version": "1.0.0",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/core-util-is": {
+ "version": "1.0.3",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/create-ecdh": {
+ "version": "4.0.4",
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "elliptic": "^6.5.3"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/create-ecdh/node_modules/bn.js": {
+ "version": "4.12.0",
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/create-hash": {
+ "version": "1.2.0",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "cipher-base": "^1.0.1",
+ "inherits": "^2.0.1",
+ "md5.js": "^1.3.4",
+ "ripemd160": "^2.0.1",
+ "sha.js": "^2.4.0"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/create-hmac": {
+ "version": "1.1.7",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "cipher-base": "^1.0.3",
+ "create-hash": "^1.1.0",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/define-data-property": {
+ "version": "1.1.4",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/define-properties": {
+ "version": "1.2.1",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/des.js": {
+ "version": "1.1.0",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/diffie-hellman": {
+ "version": "5.0.3",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "miller-rabin": "^4.0.0",
+ "randombytes": "^2.0.0"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/diffie-hellman/node_modules/bn.js": {
+ "version": "4.12.2",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/domain-browser": {
+ "version": "4.23.0",
+ "inBundle": true,
+ "license": "Artistic-2.0",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://bevry.me/fund"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/elliptic": {
+ "version": "6.5.5",
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.11.9",
+ "brorand": "^1.1.0",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.1",
+ "inherits": "^2.0.4",
+ "minimalistic-assert": "^1.0.1",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/elliptic/node_modules/bn.js": {
+ "version": "4.12.0",
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/es-define-property": {
+ "version": "1.0.1",
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/es-errors": {
+ "version": "1.3.0",
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/events": {
+ "version": "3.3.0",
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/evp_bytestokey": {
+ "version": "1.0.3",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "md5.js": "^1.3.4",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/for-each": {
+ "version": "0.3.5",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/function-bind": {
+ "version": "1.1.2",
+ "inBundle": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/get-proto": {
+ "version": "1.0.1",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/gopd": {
+ "version": "1.2.0",
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/has-proto": {
+ "version": "1.0.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/has-symbols": {
+ "version": "1.1.0",
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/hash-base": {
+ "version": "3.0.5",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/hash.js": {
+ "version": "1.1.7",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/hasown": {
+ "version": "2.0.2",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/hmac-drbg": {
+ "version": "1.0.1",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/https-browserify": {
+ "version": "1.0.0",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/ieee754": {
+ "version": "1.2.1",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "inBundle": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/meteor-node-stubs/node_modules/inherits": {
+ "version": "2.0.4",
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/meteor-node-stubs/node_modules/is-arguments": {
+ "version": "1.2.0",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/is-callable": {
+ "version": "1.2.7",
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/is-generator-function": {
+ "version": "1.1.0",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.0",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/is-nan": {
+ "version": "1.3.2",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/is-regex": {
+ "version": "1.2.1",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/isarray": {
+ "version": "2.0.5",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/md5.js": {
+ "version": "1.3.5",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/miller-rabin": {
+ "version": "4.0.1",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.0.0",
+ "brorand": "^1.0.1"
+ },
+ "bin": {
+ "miller-rabin": "bin/miller-rabin"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/miller-rabin/node_modules/bn.js": {
+ "version": "4.12.2",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "inBundle": true,
+ "license": "ISC"
+ },
+ "node_modules/meteor-node-stubs/node_modules/minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/object-inspect": {
+ "version": "1.13.4",
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/object-is": {
+ "version": "1.1.6",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/object-keys": {
+ "version": "1.1.1",
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/object.assign": {
+ "version": "4.1.7",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/os-browserify": {
+ "version": "0.3.0",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/pako": {
+ "version": "1.0.11",
+ "inBundle": true,
+ "license": "(MIT AND Zlib)"
+ },
+ "node_modules/meteor-node-stubs/node_modules/parse-asn1": {
+ "version": "5.1.7",
+ "inBundle": true,
+ "license": "ISC",
+ "dependencies": {
+ "asn1.js": "^4.10.1",
+ "browserify-aes": "^1.2.0",
+ "evp_bytestokey": "^1.0.3",
+ "hash-base": "~3.0",
+ "pbkdf2": "^3.1.2",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/path-browserify": {
+ "version": "1.0.1",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/pbkdf2": {
+ "version": "3.1.3",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "create-hash": "~1.1.3",
+ "create-hmac": "^1.1.7",
+ "ripemd160": "=2.0.1",
+ "safe-buffer": "^5.2.1",
+ "sha.js": "^2.4.11",
+ "to-buffer": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/pbkdf2/node_modules/create-hash": {
+ "version": "1.1.3",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "cipher-base": "^1.0.1",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "sha.js": "^2.4.0"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/pbkdf2/node_modules/hash-base": {
+ "version": "2.0.2",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.1"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/pbkdf2/node_modules/ripemd160": {
+ "version": "2.0.1",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "hash-base": "^2.0.0",
+ "inherits": "^2.0.1"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/process": {
+ "version": "0.11.10",
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/public-encrypt": {
+ "version": "4.0.3",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "parse-asn1": "^5.0.0",
+ "randombytes": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/public-encrypt/node_modules/bn.js": {
+ "version": "4.12.2",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/punycode": {
+ "version": "1.4.1",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/qs": {
+ "version": "6.14.0",
+ "inBundle": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/querystring-es3": {
+ "version": "0.2.1",
+ "inBundle": true,
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/randombytes": {
+ "version": "2.1.0",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/randomfill": {
+ "version": "1.0.4",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "randombytes": "^2.0.5",
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/ripemd160": {
+ "version": "2.0.2",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/set-function-length": {
+ "version": "1.2.2",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/setimmediate": {
+ "version": "1.0.5",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/sha.js": {
+ "version": "2.4.11",
+ "inBundle": true,
+ "license": "(MIT AND BSD-3-Clause)",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ },
+ "bin": {
+ "sha.js": "bin.js"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/side-channel": {
+ "version": "1.1.0",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/stream-browserify": {
+ "version": "3.0.0",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "~2.0.4",
+ "readable-stream": "^3.5.0"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/stream-http": {
+ "version": "3.2.0",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "builtin-status-codes": "^3.0.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.6.0",
+ "xtend": "^4.0.2"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/string_decoder": {
+ "version": "1.3.0",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/timers-browserify": {
+ "version": "2.0.12",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "setimmediate": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/to-buffer": {
+ "version": "1.2.1",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "isarray": "^2.0.5",
+ "safe-buffer": "^5.2.1",
+ "typed-array-buffer": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/tty-browserify": {
+ "version": "0.0.1",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/url": {
+ "version": "0.11.4",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "punycode": "^1.4.1",
+ "qs": "^6.12.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/util": {
+ "version": "0.12.5",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "is-arguments": "^1.0.4",
+ "is-generator-function": "^1.0.7",
+ "is-typed-array": "^1.1.3",
+ "which-typed-array": "^1.1.2"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/vm-browserify": {
+ "version": "1.1.2",
+ "inBundle": true,
+ "license": "MIT"
+ },
+ "node_modules/meteor-node-stubs/node_modules/which-typed-array": {
+ "version": "1.1.19",
+ "inBundle": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/meteor-node-stubs/node_modules/xtend": {
+ "version": "4.0.2",
+ "inBundle": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/react": {
+ "version": "18.2.0",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.2.0",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.0"
+ },
+ "peerDependencies": {
+ "react": "^18.2.0"
+ }
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.14.1",
+ "license": "MIT"
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.0",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/typescript": {
+ "version": "5.4.5",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ }
+ }
+}
diff --git a/tools/modern-tests/apps/typescript/package.json b/tools/modern-tests/apps/typescript/package.json
new file mode 100644
index 0000000000..7150fb8af6
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "typescript",
+ "private": true,
+ "scripts": {
+ "start": "meteor run",
+ "test": "meteor test --once --driver-package meteortesting:mocha",
+ "test-app": "TEST_WATCH=1 meteor test --full-app --driver-package meteortesting:mocha",
+ "visualize": "meteor --production --extra-packages bundle-visualizer"
+ },
+ "dependencies": {
+ "@babel/runtime": "^7.23.5",
+ "@swc/helpers": "^0.5.17",
+ "meteor-node-stubs": "^1.2.12",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
+ },
+ "devDependencies": {
+ "@types/meteor": "^2.9.9",
+ "@types/mocha": "^8.2.3",
+ "@types/node": "^22.10.6",
+ "@types/react": "^18.2.5",
+ "@types/react-dom": "^18.2.4",
+ "playwright": "^1.54.2",
+ "ts-checker-rspack-plugin": "^1.1.5",
+ "typescript": "^5.4.5"
+ },
+ "meteor": {
+ "mainModule": {
+ "client": "client/main.tsx",
+ "server": "server/main.ts"
+ },
+ "testModule": "tests/main.ts",
+ "modern": true
+ }
+}
diff --git a/tools/modern-tests/apps/typescript/rspack.config.js b/tools/modern-tests/apps/typescript/rspack.config.js
new file mode 100644
index 0000000000..64e8db0052
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/rspack.config.js
@@ -0,0 +1,18 @@
+import { defineConfig } from '@meteorjs/rspack';
+import { TsCheckerRspackPlugin } from 'ts-checker-rspack-plugin';
+
+/**
+ * Rspack configuration for Meteor projects.
+ *
+ * Provides typed flags on the `Meteor` object, such as:
+ * - `Meteor.isClient` / `Meteor.isServer`
+ * - `Meteor.isDevelopment` / `Meteor.isProduction`
+ * - …and other flags available
+ *
+ * Use these flags to adjust your build settings based on environment.
+ */
+export default defineConfig(Meteor => {
+ return {
+ plugins: [new TsCheckerRspackPlugin()],
+ };
+});
diff --git a/tools/modern-tests/apps/typescript/server/main.ts b/tools/modern-tests/apps/typescript/server/main.ts
new file mode 100644
index 0000000000..5a7ca3abb0
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/server/main.ts
@@ -0,0 +1,37 @@
+import { Meteor } from 'meteor/meteor';
+import { Link, LinksCollection } from '/imports/api/links';
+
+async function insertLink({ title, url }: Pick ) {
+ await LinksCollection.insertAsync({ title, url, createdAt: new Date() });
+}
+
+Meteor.startup(async () => {
+ // If the Links collection is empty, add some data.
+ if (await LinksCollection.find().countAsync() === 0) {
+ await insertLink({
+ title: 'Do the Tutorial',
+ url: 'https://react-tutorial.meteor.com/simple-todos/01-creating-app.html',
+ });
+
+ await insertLink({
+ title: 'Follow the Guide',
+ url: 'https://guide.meteor.com',
+ });
+
+ await insertLink({
+ title: 'Read the Docs',
+ url: 'https://docs.meteor.com',
+ });
+
+ await insertLink({
+ title: 'Discussions',
+ url: 'https://forums.meteor.com',
+ });
+ }
+
+ // We publish the entire Links collection to all clients.
+ // In order to be fetched in real-time to the clients
+ Meteor.publish("links", function () {
+ return LinksCollection.find();
+ });
+});
diff --git a/tools/modern-tests/apps/typescript/tests/main.ts b/tools/modern-tests/apps/typescript/tests/main.ts
new file mode 100644
index 0000000000..455a722e9f
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/tests/main.ts
@@ -0,0 +1,21 @@
+import { Meteor } from 'meteor/meteor';
+import assert from 'assert';
+
+describe('typescript', function () {
+ it('package.json has correct name', async function () {
+ const { name } = await import('../package.json');
+ assert.strictEqual(name, 'typescript');
+ });
+
+ if (Meteor.isClient) {
+ it('client is not server', function () {
+ assert.strictEqual(Meteor.isServer, false);
+ });
+ }
+
+ if (Meteor.isServer) {
+ it('server is not client', function () {
+ assert.strictEqual(Meteor.isClient, false);
+ });
+ }
+});
diff --git a/tools/modern-tests/apps/typescript/tsconfig.json b/tools/modern-tests/apps/typescript/tsconfig.json
new file mode 100644
index 0000000000..3aa7eac546
--- /dev/null
+++ b/tools/modern-tests/apps/typescript/tsconfig.json
@@ -0,0 +1,49 @@
+{
+ "compilerOptions": {
+ /* Basic Options */
+ "target": "es2018",
+ "module": "esNext",
+ "lib": ["esnext", "dom"],
+ "allowJs": true,
+ "checkJs": false,
+ "jsx": "preserve",
+ "incremental": true,
+ "noEmit": true,
+
+ /* Strict Type-Checking Options */
+ "strict": true,
+ "noImplicitAny": true,
+ "strictNullChecks": true,
+
+ /* Additional Checks */
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noImplicitReturns": false,
+ "noFallthroughCasesInSwitch": false,
+
+ /* Module Resolution Options */
+ "baseUrl": ".",
+ "paths": {
+ /* Support absolute /imports/* with a leading '/' */
+ "/*": ["*"],
+ /* Pull in type declarations for Meteor packages from either zodern:types or @types/meteor packages */
+ "meteor/*": [
+ "node_modules/@types/meteor/*",
+ ".meteor/local/types/packages.d.ts"
+ ]
+ },
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "types": ["node", "mocha"],
+ "esModuleInterop": true,
+ "preserveSymlinks": true
+ },
+ "exclude": [
+ "./.meteor/**",
+ "./packages/**",
+ "./_build/**",
+ "./public/_build-bundles/**",
+ "./public/_build-assets/**",
+ "./private/_build-assets/**"
+ ]
+}
diff --git a/tools/modern-tests/helpers.js b/tools/modern-tests/helpers.js
index 6de016a72a..108d313cae 100644
--- a/tools/modern-tests/helpers.js
+++ b/tools/modern-tests/helpers.js
@@ -18,7 +18,7 @@ const METEOR_EXECUTABLE = path.join(REPO_ROOT, 'meteor');
export async function setupMeteorApp(appName) {
// Create a unique temporary directory
const randomSuffix = Math.random().toString(36).substring(2, 10);
- const tempDir = path.join(os.tmpdir(), `${appName}-${randomSuffix}`);
+ const tempDir = path.join(os.tmpdir(), `meteortest-${appName}-${randomSuffix}`);
// Source app directory
const sourceAppDir = path.join(__dirname, 'apps', appName);
@@ -230,7 +230,7 @@ export async function runMeteorCommand(command, args = [], cwd, options = {}) {
export async function createMeteorApp(appName, example, options = {}) {
// Create a unique temporary directory that will be the app directory directly
const randomSuffix = Math.random().toString(36).substring(2, 10);
- const tempAppName= `${appName}-${randomSuffix}`;
+ const tempAppName= `meteortest-${appName}-${randomSuffix}`;
const tempDir = path.join(os.tmpdir(), tempAppName);
console.log(`Creating new Meteor app '${appName}' with example '${example}' in ${tempDir}...`);
diff --git a/tools/modern-tests/react.test.js b/tools/modern-tests/react.test.js
index f92b6668be..62b23ae724 100644
--- a/tools/modern-tests/react.test.js
+++ b/tools/modern-tests/react.test.js
@@ -1,22 +1,12 @@
import {
killProcessByPort,
- setupMeteorApp,
- runMeteorApp,
cleanupTempDir,
killMeteorProcess,
- createMeteorApp,
- runMeteorCommand,
- wait,
- appendFileContent,
- waitForMeteorOutput,
- waitForPlaywrightConsole,
- runMeteorTests,
- buildMeteorApp
+ createMeteorApp
} from "./helpers";
-import { assertMeteorReactApp, assertRspackScriptTag, assertFileExist } from './assertions';
+import { testMeteorBundler, testMeteorRspackBundler } from './test-helpers';
import fs from 'fs-extra';
import path from 'path';
-import execa from 'execa';
describe('React App Bundling /', () => {
describe('Meteor Creator /', () => {
@@ -69,277 +59,18 @@ describe('React App Bundling /', () => {
});
});
- describe('Meteor Bundler /', () => {
- const PORT = 3101;
- let meteorProcess;
- let tempDir;
+ describe('Meteor Bundler /', testMeteorBundler({
+ appName: 'react',
+ port: 3101
+ }));
- beforeAll(async () => {
- // Ensure any process on the port is killed
- await killProcessByPort(PORT);
-
- // Setup the Meteor app
- tempDir = (await setupMeteorApp('react'))?.tempDir;
- });
-
- afterAll(async () => {
- // Clean up the temporary directory
- await cleanupTempDir(tempDir);
- });
-
- test(`"meteor run" / should start the app`, async () => {
- // Run the Meteor app
- meteorProcess = (await runMeteorApp(tempDir, PORT))?.meteorProcess;
-
- // Assert that the Meteor React app is running correctly
- await assertMeteorReactApp(PORT);
-
- // Kill the meteor process
- await killMeteorProcess(meteorProcess);
-
- // Ensure any process on the port is killed
- await killProcessByPort(PORT);
- });
- });
-
- describe('Meteor+Rspack Bundler /', () => {
- const PORT = 3102;
- let meteorProcess;
- let tempDir;
-
- beforeAll(async () => {
- // Ensure any process on the port is killed
- await killProcessByPort(PORT);
- await killProcessByPort('8080');
-
- // Setup the Meteor app
- tempDir = (await setupMeteorApp('react'))?.tempDir;
-
- // Add Rspack package
- await runMeteorCommand('add', ['rspack'], tempDir, { checkExitCode: true });
-
- // Run the Meteor app to install Rspack
- const result = await runMeteorApp(tempDir, PORT, {
- waitForOutput: "=> App running at:",
- });
- meteorProcess = result.meteorProcess;
-
- // Wait for a margin
- await wait(1000);
-
- // Assert that the config files exists
- await assertFileExist(tempDir, '.gitignore', { content: '_build' });
- await assertFileExist(tempDir, 'rspack.config.js', { content: '@meteorjs/rspack' });
-
- // Kill the meteor process
- await killMeteorProcess(meteorProcess);
-
- // Ensure any process on the port is killed
- await killProcessByPort(PORT);
- await killProcessByPort('8080');
- });
-
- afterAll(async () => {
- // Clean up the temporary directory
- await cleanupTempDir(tempDir);
- });
-
- test(`"meteor run" / should run and rebuild the app with Rspack`, async () => {
- // Run the Meteor app and wait for "restarted at" output
- const result = await runMeteorApp(tempDir, PORT, {
- waitForOutput: "=> App running at:",
- });
- meteorProcess = result.meteorProcess;
-
- // Wait for a margin
- await wait(500);
-
- // Assert that the app files exists
- await assertFileExist(tempDir, '_build/main-dev/client-entry.js');
- await assertFileExist(tempDir, '_build/main-dev/client-rspack.js');
- await assertFileExist(tempDir, '_build/main-dev/client-meteor.js');
- await assertFileExist(tempDir, '_build/main-dev/server-entry.js');
- await assertFileExist(tempDir, '_build/main-dev/server-rspack.js');
- await assertFileExist(tempDir, '_build/main-dev/server-meteor.js');
-
- // Assert that the Meteor React app is running correctly
- await assertMeteorReactApp(PORT);
-
- // Assert that the app is using Rspack
- await assertRspackScriptTag(PORT, true);
-
- // Update the client code
- await appendFileContent(tempDir, 'client/main.jsx', {
- content: 'if (Meteor.isDevelopment) console.log("Hello from dev client");',
- });
- await waitForPlaywrightConsole('Hello from dev client');
-
- // Update the server code
- await appendFileContent(tempDir, 'server/main.js', {
- content: 'if (Meteor.isDevelopment) console.log("Hello from dev server");',
- });
- await waitForMeteorOutput(
- result.outputLines,
- 'Hello from dev server'
- );
-
- // Wait for a margin
- await wait(500);
-
- // Kill the meteor process
- await killMeteorProcess(meteorProcess);
-
- // Ensure any process on the port is killed
- await killProcessByPort(PORT);
- await killProcessByPort('8080');
- });
-
- test(`"meteor run --production" / should run and rebuild the app with Rspack in production`, async () => {
- // Run the Meteor app and wait for "restarted at" output
- const result = await runMeteorApp(tempDir, PORT, {
- waitForOutput: "=> App running at:",
- commandOptions: ['--production'],
- });
- meteorProcess = result.meteorProcess;
-
- // Wait for a margin
- await wait(500);
-
- // Assert that the app files exists
- await assertFileExist(tempDir, '_build/main-prod/client-entry.js');
- await assertFileExist(tempDir, '_build/main-prod/client-rspack.js');
- await assertFileExist(tempDir, '_build/main-prod/client-meteor.js');
- await assertFileExist(tempDir, '_build/main-prod/server-entry.js');
- await assertFileExist(tempDir, '_build/main-prod/server-rspack.js');
- await assertFileExist(tempDir, '_build/main-prod/server-meteor.js');
-
- await assertFileExist(tempDir, 'server/main.js');
-
- // Assert that the Meteor React app is running correctly
- await assertMeteorReactApp(PORT);
-
- // Assert that the app is using Rspack
- await assertRspackScriptTag(PORT, false);
-
- // Update the client code
- await appendFileContent(tempDir, 'client/main.jsx', {
- content: 'if (Meteor.isProduction) console.log("Hello from prod client");',
- });
- await waitForPlaywrightConsole('Hello from prod client');
-
- // Update the server code
- await appendFileContent(tempDir, 'server/main.js', {
- content: 'if (Meteor.isProduction) console.log("Hello from prod server");',
- });
- await waitForMeteorOutput(
- result.outputLines,
- 'Hello from prod server'
- );
-
- // Wait for a margin
- await wait(500);
-
- // Kill the meteor process
- await killMeteorProcess(meteorProcess);
-
- // Ensure any process on the port is killed
- await killProcessByPort(PORT);
- await killProcessByPort('8080');
- });
-
- test(`"meteor test" / should run tests with Rspack`, async () => {
- const result = await runMeteorTests(tempDir, PORT, {
- waitForOutput: "=> App running at:",
- commandOptions: [],
- checkTestResults: false,
- });
- meteorProcess = result.meteorProcess;
-
- // Wait for a margin
- await wait(500);
-
- // Assert that the app files exists
- await assertFileExist(tempDir, '_build/test/test-entry.js');
- await assertFileExist(tempDir, '_build/test/test-rspack.js');
- await assertFileExist(tempDir, '_build/test/test-meteor.js');
-
- // Update the test code
- await appendFileContent(tempDir, 'tests/main.js', {
- content: 'console.log("Hello from test");',
- });
- await waitForMeteorOutput(
- result.outputLines,
- 'Hello from test'
- );
-
- // Kill the meteor process
- await killMeteorProcess(meteorProcess);
-
- // Ensure any process on the port is killed
- await killProcessByPort(PORT);
- });
-
- test(`"meteor test --once" / should run tests once with Rspack`, async () => {
- // Test the app with Rspack once
- await runMeteorTests(tempDir, PORT, {
- waitForOutput: "=> App running at:",
- commandOptions: ['--once'],
- checkTestResults: true,
- });
-
- // Wait for a margin
- await wait(500);
-
- // Assert that the app files exists
- await assertFileExist(tempDir, '_build/test/test-entry.js');
- await assertFileExist(tempDir, '_build/test/test-rspack.js');
- await assertFileExist(tempDir, '_build/test/test-meteor.js');
-
- // Ensure any process on the port is killed
- await killProcessByPort(PORT);
- });
-
- test(`"meteor build" / should build the app with Rspack`, async () => {
- // Build the app with Rspack
- const { buildOutputDir, processResult } = await buildMeteorApp(tempDir, {
- commandOptions: ['--directory'],
- captureOutput: true
- });
-
- // Wait for a margin
- await wait(500);
-
- try {
- // Assert that the build output directory exists
- const buildDirExists = await fs.pathExists(buildOutputDir);
- expect(buildDirExists).toBe(true);
-
- // Assert that the main.js file exists
- expect(await fs.pathExists(`${buildOutputDir}/bundle/main.js`)).toBe(true);
-
- // Assert that the server/package.json file exists
- expect(await fs.pathExists(`${buildOutputDir}/bundle/programs/server/package.json`)).toBe(true);
- expect(await fs.pathExists(`${buildOutputDir}/bundle/programs/server/program.json`)).toBe(true);
-
- // Assert that the [web.browser|web.browser.legacy]/program.json file exists
- expect(await fs.pathExists(`${buildOutputDir}/bundle/programs/web.browser/program.json`)).toBe(true);
- expect(await fs.pathExists(`${buildOutputDir}/bundle/programs/web.browser.legacy/program.json`)).toBe(true);
-
- // Run npm install in the server directory
- console.log('Running npm install in the server directory...');
- const serverDir = path.join(buildOutputDir, 'bundle', 'programs', 'server');
- const npmInstallResult = await execa('npm', ['install'], {
- cwd: serverDir,
- stdio: 'inherit',
- shell: true,
- });
-
- // Check if the npm install command was successful
- expect(npmInstallResult.exitCode).toBe(0);
- } finally {
- // Clean up the build output directory
- await cleanupTempDir(buildOutputDir);
- }
- });
- });
+ describe('Meteor+Rspack Bundler /', testMeteorRspackBundler({
+ appName: 'react',
+ port: 3102,
+ filePaths: {
+ client: 'client/main.jsx',
+ server: 'server/main.js',
+ test: 'tests/main.js'
+ },
+ }));
});
diff --git a/tools/modern-tests/test-helpers.js b/tools/modern-tests/test-helpers.js
new file mode 100644
index 0000000000..9946b4dd4e
--- /dev/null
+++ b/tools/modern-tests/test-helpers.js
@@ -0,0 +1,375 @@
+/**
+ * This file contains helper functions for testing Meteor applications.
+ * It provides reusable test patterns for both the test apps.
+ */
+
+import {
+ killProcessByPort,
+ setupMeteorApp,
+ runMeteorApp,
+ cleanupTempDir,
+ killMeteorProcess,
+ runMeteorCommand,
+ wait,
+ appendFileContent,
+ waitForMeteorOutput,
+ waitForPlaywrightConsole,
+ runMeteorTests,
+ buildMeteorApp
+} from "./helpers";
+import { assertMeteorReactApp, assertRspackScriptTag, assertFileExist } from './assertions';
+import fs from 'fs-extra';
+import path from 'path';
+import execa from 'execa';
+
+/**
+ * Helper function to set up and run tests for the Meteor Bundler
+ * @param {Object} options - Options for the test
+ * @param {string} options.appName - Name of the app ('react' or 'typescript')
+ * @param {number} options.port - Port to run the app on
+ * @param {Function} options.customAssertions - Custom assertions to run after the app is started
+ * @returns {Function} - Jest test function
+ */
+export function testMeteorBundler(options) {
+ const { appName, port, customAssertions } = options;
+
+ return () => {
+ let meteorProcess;
+ let tempDir;
+
+ beforeAll(async () => {
+ // Ensure any process on the port is killed
+ await killProcessByPort(port);
+
+ // Setup the Meteor app
+ tempDir = (await setupMeteorApp(appName))?.tempDir;
+ });
+
+ afterAll(async () => {
+ // Clean up the temporary directory
+ await cleanupTempDir(tempDir);
+ });
+
+ test(`"meteor run" / should start the app`, async () => {
+ // Run the Meteor app
+ meteorProcess = (await runMeteorApp(tempDir, port))?.meteorProcess;
+
+ // Assert that the Meteor app is running correctly
+ await assertMeteorReactApp(port, { title: appName });
+
+ // Run custom assertions if provided
+ if (customAssertions) {
+ await customAssertions({ tempDir, port, meteorProcess });
+ }
+
+ // Kill the meteor process
+ await killMeteorProcess(meteorProcess);
+
+ // Ensure any process on the port is killed
+ await killProcessByPort(port);
+ });
+ };
+}
+
+/**
+ * Helper function to set up and run tests for the Meteor+Rspack Bundler
+ * @param {Object} options - Options for the test
+ * @param {string} options.appName - Name of the app ('react', 'typescript', etc)
+ * @param {number} options.port - Port to run the app on
+ * @param {Object} options.filePaths - File paths for the app
+ * @param {string} options.filePaths.client - Client file path (e.g., 'client/main.jsx')
+ * @param {string} options.filePaths.server - Server file path (e.g., 'server/main.js')
+ * @param {string} options.filePaths.test - Test file path (e.g., 'tests/main.js')
+ * @param {Object} options.customMessages - Custom messages for console logs
+ * @param {string} options.customMessages.devClient - Message for development client
+ * @param {string} options.customMessages.devServer - Message for development server
+ * @param {string} options.customMessages.prodClient - Message for production client
+ * @param {string} options.customMessages.prodServer - Message for production server
+ * @param {string} options.customMessages.test - Message for test
+ * @param {Function} options.customAssertions - Custom assertions to run after each test
+ * @returns {Function} - Jest test function
+ */
+export function testMeteorRspackBundler(options) {
+ const {
+ appName,
+ port,
+ filePaths = {
+ client: 'client/main.jsx',
+ server: 'server/main.js',
+ test: 'tests/main.js'
+ },
+ customMessages = {
+ devClient: "Hello from dev client",
+ devServer: "Hello from dev server",
+ prodClient: "Hello from prod client",
+ prodServer: "Hello from prod server",
+ test: "Hello from test"
+ },
+ customAssertions
+ } = options;
+
+ return () => {
+ let meteorProcess;
+ let tempDir;
+
+ beforeAll(async () => {
+ // Ensure any process on the port is killed
+ await killProcessByPort(port);
+ await killProcessByPort('8080');
+
+ // Setup the Meteor app
+ tempDir = (await setupMeteorApp(appName))?.tempDir;
+
+ // Add Rspack package
+ await runMeteorCommand('add', ['rspack'], tempDir, { checkExitCode: true });
+
+ // Run the Meteor app to install Rspack
+ const result = await runMeteorApp(tempDir, port, {
+ waitForOutput: "=> App running at:",
+ });
+ meteorProcess = result.meteorProcess;
+
+ // Wait for a margin
+ await wait(1000);
+
+ // Assert that the config files exists
+ await assertFileExist(tempDir, '.gitignore', { content: '_build' });
+ await assertFileExist(tempDir, 'rspack.config.js', { content: '@meteorjs/rspack' });
+
+ // Kill the meteor process
+ await killMeteorProcess(meteorProcess);
+
+ // Ensure any process on the port is killed
+ await killProcessByPort(port);
+ await killProcessByPort('8080');
+ });
+
+ afterAll(async () => {
+ // Clean up the temporary directory
+ await cleanupTempDir(tempDir);
+ });
+
+ test.only(`"meteor run" / should run and rebuild the app with Rspack`, async () => {
+ // Run the Meteor app and wait for "restarted at" output
+ const result = await runMeteorApp(tempDir, port, {
+ waitForOutput: "=> App running at:",
+ });
+ meteorProcess = result.meteorProcess;
+
+ // Wait for a margin
+ await wait(500);
+
+ // Assert that the app files exists
+ await assertFileExist(tempDir, '_build/main-dev/client-entry.js');
+ await assertFileExist(tempDir, '_build/main-dev/client-rspack.js');
+ await assertFileExist(tempDir, '_build/main-dev/client-meteor.js');
+ await assertFileExist(tempDir, '_build/main-dev/server-entry.js');
+ await assertFileExist(tempDir, '_build/main-dev/server-rspack.js');
+ await assertFileExist(tempDir, '_build/main-dev/server-meteor.js');
+
+ // Assert that the Meteor app is running correctly
+ await assertMeteorReactApp(port, { title: appName });
+
+ // Assert that the app is using Rspack
+ await assertRspackScriptTag(port, true);
+
+ // Update the client code
+ await appendFileContent(tempDir, filePaths.client, {
+ content: `if (Meteor.isDevelopment) console.log("${customMessages.devClient}");`,
+ });
+ await waitForPlaywrightConsole(customMessages.devClient);
+
+ // Update the server code
+ await appendFileContent(tempDir, filePaths.server, {
+ content: `if (Meteor.isDevelopment) console.log("${customMessages.devServer}");`,
+ });
+ await waitForMeteorOutput(
+ result.outputLines,
+ customMessages.devServer
+ );
+
+ // Run custom assertions if provided
+ if (customAssertions && customAssertions.afterRun) {
+ await customAssertions.afterRun({ tempDir, port, meteorProcess, result });
+ }
+
+ // Wait for a margin
+ await wait(500);
+
+ // Kill the meteor process
+ await killMeteorProcess(meteorProcess);
+
+ // Ensure any process on the port is killed
+ await killProcessByPort(port);
+ await killProcessByPort('8080');
+ });
+
+ test(`"meteor run --production" / should run and rebuild the app with Rspack in production`, async () => {
+ // Run the Meteor app and wait for "restarted at" output
+ const result = await runMeteorApp(tempDir, port, {
+ waitForOutput: "=> App running at:",
+ commandOptions: ['--production'],
+ });
+ meteorProcess = result.meteorProcess;
+
+ // Wait for a margin
+ await wait(500);
+
+ // Assert that the app files exists
+ await assertFileExist(tempDir, '_build/main-prod/client-entry.js');
+ await assertFileExist(tempDir, '_build/main-prod/client-rspack.js');
+ await assertFileExist(tempDir, '_build/main-prod/client-meteor.js');
+ await assertFileExist(tempDir, '_build/main-prod/server-entry.js');
+ await assertFileExist(tempDir, '_build/main-prod/server-rspack.js');
+ await assertFileExist(tempDir, '_build/main-prod/server-meteor.js');
+
+ await assertFileExist(tempDir, filePaths.server);
+
+ // Assert that the Meteor app is running correctly
+ await assertMeteorReactApp(port, { title: appName });
+
+ // Assert that the app is using Rspack
+ await assertRspackScriptTag(port, false);
+
+ // Update the client code
+ await appendFileContent(tempDir, filePaths.client, {
+ content: `if (Meteor.isProduction) console.log("${customMessages.prodClient}");`,
+ });
+ await waitForPlaywrightConsole(customMessages.prodClient);
+
+ // Update the server code
+ await appendFileContent(tempDir, filePaths.server, {
+ content: `if (Meteor.isProduction) console.log("${customMessages.prodServer}");`,
+ });
+ await waitForMeteorOutput(
+ result.outputLines,
+ customMessages.prodServer
+ );
+
+ // Run custom assertions if provided
+ if (customAssertions && customAssertions.afterRunProduction) {
+ await customAssertions.afterRunProduction({ tempDir, port, meteorProcess, result });
+ }
+
+ // Wait for a margin
+ await wait(500);
+
+ // Kill the meteor process
+ await killMeteorProcess(meteorProcess);
+
+ // Ensure any process on the port is killed
+ await killProcessByPort(port);
+ await killProcessByPort('8080');
+ });
+
+ test(`"meteor test" / should run tests with Rspack`, async () => {
+ const result = await runMeteorTests(tempDir, port, {
+ waitForOutput: "=> App running at:",
+ commandOptions: [],
+ checkTestResults: false,
+ });
+ meteorProcess = result.meteorProcess;
+
+ // Wait for a margin
+ await wait(500);
+
+ // Assert that the app files exists
+ await assertFileExist(tempDir, '_build/test/test-entry.js');
+ await assertFileExist(tempDir, '_build/test/test-rspack.js');
+ await assertFileExist(tempDir, '_build/test/test-meteor.js');
+
+ // Update the test code
+ await appendFileContent(tempDir, filePaths.test, {
+ content: `console.log("${customMessages.test}");`,
+ });
+ await waitForMeteorOutput(
+ result.outputLines,
+ customMessages.test
+ );
+
+ // Run custom assertions if provided
+ if (customAssertions && customAssertions.afterTest) {
+ await customAssertions.afterTest({ tempDir, port, meteorProcess, result });
+ }
+
+ // Kill the meteor process
+ await killMeteorProcess(meteorProcess);
+
+ // Ensure any process on the port is killed
+ await killProcessByPort(port);
+ });
+
+ test(`"meteor test --once" / should run tests once with Rspack`, async () => {
+ // Test the app with Rspack once
+ const result = await runMeteorTests(tempDir, port, {
+ waitForOutput: "=> App running at:",
+ commandOptions: ['--once'],
+ checkTestResults: true,
+ });
+
+ // Wait for a margin
+ await wait(500);
+
+ // Assert that the app files exists
+ await assertFileExist(tempDir, '_build/test/test-entry.js');
+ await assertFileExist(tempDir, '_build/test/test-rspack.js');
+ await assertFileExist(tempDir, '_build/test/test-meteor.js');
+
+ // Run custom assertions if provided
+ if (customAssertions && customAssertions.afterTestOnce) {
+ await customAssertions.afterTestOnce({ tempDir, port, result });
+ }
+
+ // Ensure any process on the port is killed
+ await killProcessByPort(port);
+ });
+
+ test(`"meteor build" / should build the app with Rspack`, async () => {
+ // Build the app with Rspack
+ const { buildOutputDir, processResult } = await buildMeteorApp(tempDir, {
+ commandOptions: ['--directory'],
+ captureOutput: true
+ });
+
+ // Wait for a margin
+ await wait(500);
+
+ try {
+ // Assert that the build output directory exists
+ const buildDirExists = await fs.pathExists(buildOutputDir);
+ expect(buildDirExists).toBe(true);
+
+ // Assert that the main.js file exists
+ expect(await fs.pathExists(`${buildOutputDir}/bundle/main.js`)).toBe(true);
+
+ // Assert that the server/package.json file exists
+ expect(await fs.pathExists(`${buildOutputDir}/bundle/programs/server/package.json`)).toBe(true);
+ expect(await fs.pathExists(`${buildOutputDir}/bundle/programs/server/program.json`)).toBe(true);
+
+ // Assert that the [web.browser|web.browser.legacy]/program.json file exists
+ expect(await fs.pathExists(`${buildOutputDir}/bundle/programs/web.browser/program.json`)).toBe(true);
+ expect(await fs.pathExists(`${buildOutputDir}/bundle/programs/web.browser.legacy/program.json`)).toBe(true);
+
+ // Run npm install in the server directory
+ console.log('Running npm install in the server directory...');
+ const serverDir = path.join(buildOutputDir, 'bundle', 'programs', 'server');
+ const npmInstallResult = await execa('npm', ['install'], {
+ cwd: serverDir,
+ stdio: 'inherit',
+ shell: true,
+ });
+
+ // Check if the npm install command was successful
+ expect(npmInstallResult.exitCode).toBe(0);
+
+ // Run custom assertions if provided
+ if (customAssertions && customAssertions.afterBuild) {
+ await customAssertions.afterBuild({ tempDir, buildOutputDir, processResult });
+ }
+ } finally {
+ // Clean up the build output directory
+ await cleanupTempDir(buildOutputDir);
+ }
+ });
+ };
+}
diff --git a/tools/modern-tests/typescript.test.js b/tools/modern-tests/typescript.test.js
new file mode 100644
index 0000000000..ec4cda359a
--- /dev/null
+++ b/tools/modern-tests/typescript.test.js
@@ -0,0 +1,33 @@
+import {
+ waitForMeteorOutput
+} from "./helpers";
+import { testMeteorBundler, testMeteorRspackBundler } from './test-helpers';
+
+describe('TypeScript App Bundling /', () => {
+ describe('Meteor Bundler /', testMeteorBundler({
+ appName: 'typescript',
+ port: 3201
+ }));
+
+ describe('Meteor+Rspack Bundler /', testMeteorRspackBundler({
+ appName: 'typescript',
+ port: 3202,
+ filePaths: {
+ client: 'client/main.tsx',
+ server: 'server/main.ts',
+ test: 'tests/main.ts'
+ },
+ customAssertions: {
+ afterRun: async ({ result }) => {
+ await waitForMeteorOutput(
+ result.outputLines,
+ /.*\[Rspack Client].*No TypeScript errors found\./
+ );
+ await waitForMeteorOutput(
+ result.outputLines,
+ /.*\[Rspack Server].*No TypeScript errors found\./
+ );
+ },
+ }
+ }));
+});