mercur-cli 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/cli/backend-setup.js +9 -13
  2. package/cli/data.js +121 -0
  3. package/cli/pull-and-install.js +38 -2
  4. package/cli/start.js +1 -1
  5. package/index.js +1 -1
  6. package/mercur/backend/.env.template +8 -0
  7. package/mercur/backend/.env.test +0 -0
  8. package/mercur/backend/.github/dependabot.yml +21 -0
  9. package/mercur/backend/.github/scripts/wait-for-server-live.sh +29 -0
  10. package/mercur/backend/.github/workflows/test-cli.yml +222 -0
  11. package/mercur/backend/.github/workflows/update-preview-deps-ci.yml +69 -0
  12. package/mercur/backend/.github/workflows/update-preview-deps.yml +67 -0
  13. package/mercur/backend/.vscode/settings.json +2 -0
  14. package/mercur/backend/.yarnrc.yml +1 -0
  15. package/mercur/backend/README.md +62 -0
  16. package/mercur/backend/instrumentation.ts +24 -0
  17. package/mercur/backend/integration-tests/http/README.md +29 -0
  18. package/mercur/backend/integration-tests/http/health.spec.ts +15 -0
  19. package/mercur/backend/integration-tests/setup.js +3 -0
  20. package/mercur/backend/jest.config.js +27 -0
  21. package/mercur/backend/medusa-config.ts +88 -0
  22. package/mercur/backend/package.json +65 -0
  23. package/mercur/backend/src/admin/README.md +33 -0
  24. package/mercur/backend/src/admin/tsconfig.json +24 -0
  25. package/mercur/backend/src/admin/vite-env.d.ts +1 -0
  26. package/mercur/backend/src/api/README.md +135 -0
  27. package/mercur/backend/src/api/admin/custom/route.ts +8 -0
  28. package/mercur/backend/src/api/store/custom/route.ts +8 -0
  29. package/mercur/backend/src/jobs/README.md +38 -0
  30. package/mercur/backend/src/links/README.md +26 -0
  31. package/mercur/backend/src/modules/README.md +117 -0
  32. package/mercur/backend/src/scripts/README.md +63 -0
  33. package/mercur/backend/src/scripts/seed/seed-functions.ts +519 -0
  34. package/mercur/backend/src/scripts/seed/seed-products.ts +601 -0
  35. package/mercur/backend/src/scripts/seed.ts +75 -0
  36. package/mercur/backend/src/subscribers/README.md +61 -0
  37. package/mercur/backend/src/workflows/README.md +81 -0
  38. package/mercur/backend/tsconfig.json +35 -0
  39. package/mercur/backend/yarn.lock +11469 -0
  40. package/package.json +10 -8
@@ -0,0 +1,62 @@
1
+ <p align="center">
2
+ <a href="https://www.medusajs.com">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/59018053/229103275-b5e482bb-4601-46e6-8142-244f531cebdb.svg">
5
+ <source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/59018053/229103726-e5b529a3-9b3f-4970-8a1f-c6af37f087bf.svg">
6
+ <img alt="Medusa logo" src="https://user-images.githubusercontent.com/59018053/229103726-e5b529a3-9b3f-4970-8a1f-c6af37f087bf.svg">
7
+ </picture>
8
+ </a>
9
+ </p>
10
+ <h1 align="center">
11
+ Medusa
12
+ </h1>
13
+
14
+ <h4 align="center">
15
+ <a href="https://docs.medusajs.com">Documentation</a> |
16
+ <a href="https://www.medusajs.com">Website</a>
17
+ </h4>
18
+
19
+ <p align="center">
20
+ Building blocks for digital commerce
21
+ </p>
22
+ <p align="center">
23
+ <a href="https://github.com/medusajs/medusa/blob/master/CONTRIBUTING.md">
24
+ <img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat" alt="PRs welcome!" />
25
+ </a>
26
+ <a href="https://www.producthunt.com/posts/medusa"><img src="https://img.shields.io/badge/Product%20Hunt-%231%20Product%20of%20the%20Day-%23DA552E" alt="Product Hunt"></a>
27
+ <a href="https://discord.gg/xpCwq3Kfn8">
28
+ <img src="https://img.shields.io/badge/chat-on%20discord-7289DA.svg" alt="Discord Chat" />
29
+ </a>
30
+ <a href="https://twitter.com/intent/follow?screen_name=medusajs">
31
+ <img src="https://img.shields.io/twitter/follow/medusajs.svg?label=Follow%20@medusajs" alt="Follow @medusajs" />
32
+ </a>
33
+ </p>
34
+
35
+ ## Compatibility
36
+
37
+ This starter is compatible with versions >= 2 of `@medusajs/medusa`.
38
+
39
+ ## Getting Started
40
+
41
+ Visit the [Quickstart Guide](https://docs.medusajs.com/learn/installation) to set up a server.
42
+
43
+ Visit the [Docs](https://docs.medusajs.com/learn/installation#get-started) to learn more about our system requirements.
44
+
45
+ ## What is Medusa
46
+
47
+ Medusa is a set of commerce modules and tools that allow you to build rich, reliable, and performant commerce applications without reinventing core commerce logic. The modules can be customized and used to build advanced ecommerce stores, marketplaces, or any product that needs foundational commerce primitives. All modules are open-source and freely available on npm.
48
+
49
+ Learn more about [Medusa’s architecture](https://docs.medusajs.com/learn/introduction/architecture) and [commerce modules](https://docs.medusajs.com/learn/fundamentals/modules/commerce-modules) in the Docs.
50
+
51
+ ## Community & Contributions
52
+
53
+ The community and core team are available in [GitHub Discussions](https://github.com/medusajs/medusa/discussions), where you can ask for support, discuss roadmap, and share ideas.
54
+
55
+ Join our [Discord server](https://discord.com/invite/medusajs) to meet other community members.
56
+
57
+ ## Other channels
58
+
59
+ - [GitHub Issues](https://github.com/medusajs/medusa/issues)
60
+ - [Twitter](https://twitter.com/medusajs)
61
+ - [LinkedIn](https://www.linkedin.com/company/medusajs)
62
+ - [Medusa Blog](https://medusajs.com/blog/)
@@ -0,0 +1,24 @@
1
+ // Uncomment this file to enable instrumentation and observability using OpenTelemetry
2
+ // Refer to the docs for installation instructions: https://docs.medusajs.com/learn/debugging-and-testing/instrumentation
3
+
4
+ // import { registerOtel } from "@medusajs/medusa"
5
+ // // If using an exporter other than Zipkin, require it here.
6
+ // import { ZipkinExporter } from "@opentelemetry/exporter-zipkin"
7
+
8
+ // // If using an exporter other than Zipkin, initialize it here.
9
+ // const exporter = new ZipkinExporter({
10
+ // serviceName: 'my-medusa-project',
11
+ // })
12
+
13
+ // export function register() {
14
+ // registerOtel({
15
+ // serviceName: 'medusajs',
16
+ // // pass exporter
17
+ // exporter,
18
+ // instrument: {
19
+ // http: true,
20
+ // workflows: true,
21
+ // query: true
22
+ // },
23
+ // })
24
+ // }
@@ -0,0 +1,29 @@
1
+ # Integration Tests
2
+
3
+ The `medusa-test-utils` package provides utility functions to create integration tests for your API routes and workflows.
4
+
5
+ For example:
6
+
7
+ ```ts
8
+ import { medusaIntegrationTestRunner } from "medusa-test-utils"
9
+
10
+ medusaIntegrationTestRunner({
11
+ testSuite: ({ api, getContainer }) => {
12
+ describe("Custom endpoints", () => {
13
+ describe("GET /store/custom", () => {
14
+ it("returns correct message", async () => {
15
+ const response = await api.get(
16
+ `/store/custom`
17
+ )
18
+
19
+ expect(response.status).toEqual(200)
20
+ expect(response.data).toHaveProperty("message")
21
+ expect(response.data.message).toEqual("Hello, World!")
22
+ })
23
+ })
24
+ })
25
+ }
26
+ })
27
+ ```
28
+
29
+ Learn more in [this documentation](https://docs.medusajs.com/learn/debugging-and-testing/testing-tools/integration-tests).
@@ -0,0 +1,15 @@
1
+ import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
2
+ jest.setTimeout(60 * 1000)
3
+
4
+ medusaIntegrationTestRunner({
5
+ inApp: true,
6
+ env: {},
7
+ testSuite: ({ api }) => {
8
+ describe("Ping", () => {
9
+ it("ping the server health endpoint", async () => {
10
+ const response = await api.get('/health')
11
+ expect(response.status).toEqual(200)
12
+ })
13
+ })
14
+ },
15
+ })
@@ -0,0 +1,3 @@
1
+ const { MetadataStorage } = require("@mikro-orm/core")
2
+
3
+ MetadataStorage.clear()
@@ -0,0 +1,27 @@
1
+ const { loadEnv } = require("@medusajs/utils");
2
+ loadEnv("test", process.cwd());
3
+
4
+ module.exports = {
5
+ transform: {
6
+ "^.+\\.[jt]s$": [
7
+ "@swc/jest",
8
+ {
9
+ jsc: {
10
+ parser: { syntax: "typescript", decorators: true },
11
+ },
12
+ },
13
+ ],
14
+ },
15
+ testEnvironment: "node",
16
+ moduleFileExtensions: ["js", "ts", "json"],
17
+ modulePathIgnorePatterns: ["dist/", "<rootDir>/.medusa/"],
18
+ setupFiles: ["./integration-tests/setup.js"],
19
+ };
20
+
21
+ if (process.env.TEST_TYPE === "integration:http") {
22
+ module.exports.testMatch = ["**/integration-tests/http/*.spec.[jt]s"];
23
+ } else if (process.env.TEST_TYPE === "integration:modules") {
24
+ module.exports.testMatch = ["**/src/modules/*/__tests__/**/*.[jt]s"];
25
+ } else if (process.env.TEST_TYPE === "unit") {
26
+ module.exports.testMatch = ["**/src/**/__tests__/**/*.unit.spec.[jt]s"];
27
+ }
@@ -0,0 +1,88 @@
1
+
2
+ import { defineConfig, loadEnv } from '@medusajs/framework/utils'
3
+
4
+ loadEnv(process.env.NODE_ENV || 'development', process.cwd())
5
+
6
+ module.exports = defineConfig({
7
+ projectConfig: {
8
+ databaseUrl: process.env.DATABASE_URL,
9
+ http: {
10
+ storeCors: process.env.STORE_CORS!,
11
+ adminCors: process.env.ADMIN_CORS!,
12
+ // @ts-expect-error: vendorCors is not a valid config
13
+ vendorCors: process.env.VENDOR_CORS!,
14
+ authCors: process.env.AUTH_CORS!,
15
+ jwtSecret: process.env.JWT_SECRET || 'supersecret',
16
+ cookieSecret: process.env.COOKIE_SECRET || 'supersecret'
17
+ }
18
+ },
19
+ plugins: [
20
+ {
21
+ resolve: '@mercurjs/b2c-core',
22
+ options: {}
23
+ },
24
+ {
25
+ resolve: '@mercurjs/commission',
26
+ options: {}
27
+ },
28
+ {
29
+ resolve: '@mercurjs/algolia',
30
+ options: {
31
+ apiKey: process.env.ALGOLIA_API_KEY,
32
+ appId: process.env.ALGOLIA_APP_ID
33
+ }
34
+ },
35
+ {
36
+ resolve: '@mercurjs/reviews',
37
+ options: {}
38
+ },
39
+ {
40
+ resolve: '@mercurjs/requests',
41
+ options: {}
42
+ },
43
+ {
44
+ resolve: '@mercurjs/resend',
45
+ options: {}
46
+ }
47
+ ],
48
+ modules: [
49
+ {
50
+ resolve: '@medusajs/medusa/payment',
51
+ options: {
52
+ providers: [
53
+ {
54
+ resolve:
55
+ '@mercurjs/payment-stripe-connect/providers/stripe-connect',
56
+ id: 'stripe-connect',
57
+ options: {
58
+ apiKey: process.env.STRIPE_SECRET_API_KEY
59
+ }
60
+ }
61
+ ]
62
+ }
63
+ },
64
+ {
65
+ resolve: '@medusajs/medusa/notification',
66
+ options: {
67
+ providers: [
68
+ {
69
+ resolve: '@mercurjs/resend/providers/resend',
70
+ id: 'resend',
71
+ options: {
72
+ channels: ['email'],
73
+ api_key: process.env.RESEND_API_KEY,
74
+ from: process.env.RESEND_FROM_EMAIL
75
+ }
76
+ },
77
+ {
78
+ resolve: '@medusajs/medusa/notification-local',
79
+ id: 'local',
80
+ options: {
81
+ channels: ['feed', 'seller_feed']
82
+ }
83
+ }
84
+ ]
85
+ }
86
+ }
87
+ ]
88
+ })
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "medusa-starter-default",
3
+ "version": "0.0.1",
4
+ "description": "A starter for Medusa projects.",
5
+ "author": "Medusa (https://medusajs.com)",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "sqlite",
9
+ "postgres",
10
+ "typescript",
11
+ "ecommerce",
12
+ "headless",
13
+ "medusa"
14
+ ],
15
+ "scripts": {
16
+ "build": "medusa build",
17
+ "seed": "medusa exec ./src/scripts/seed.ts",
18
+ "start": "medusa start",
19
+ "dev": "medusa develop",
20
+ "test:integration:http": "TEST_TYPE=integration:http NODE_OPTIONS=--experimental-vm-modules jest --silent=false --runInBand --forceExit",
21
+ "test:integration:modules": "TEST_TYPE=integration:modules NODE_OPTIONS=--experimental-vm-modules jest --silent=false --runInBand --forceExit",
22
+ "test:unit": "TEST_TYPE=unit NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit"
23
+ },
24
+ "dependencies": {
25
+ "@medusajs/admin-sdk": "2.10.3",
26
+ "@medusajs/cli": "2.10.3",
27
+ "@medusajs/framework": "2.10.3",
28
+ "@medusajs/medusa": "2.10.3",
29
+ "@mercurjs/algolia": "^1.3.0",
30
+ "@mercurjs/b2c-core": "^1.3.0",
31
+ "@mercurjs/commission": "^1.3.0",
32
+ "@mercurjs/framework": "^1.3.0",
33
+ "@mercurjs/payment-stripe-connect": "^1.3.0",
34
+ "@mercurjs/requests": "^1.3.0",
35
+ "@mercurjs/resend": "^1.3.0",
36
+ "@mercurjs/reviews": "^1.3.0",
37
+ "@mikro-orm/core": "6.4.3",
38
+ "@mikro-orm/knex": "6.4.3",
39
+ "@mikro-orm/migrations": "6.4.3",
40
+ "@mikro-orm/postgresql": "6.4.3",
41
+ "awilix": "^8.0.1",
42
+ "pg": "^8.13.0"
43
+ },
44
+ "devDependencies": {
45
+ "@medusajs/test-utils": "2.10.3",
46
+ "@mikro-orm/cli": "6.4.3",
47
+ "@swc/core": "1.5.7",
48
+ "@swc/jest": "^0.2.36",
49
+ "@types/jest": "^29.5.13",
50
+ "@types/node": "^20.0.0",
51
+ "@types/react": "^18.3.2",
52
+ "@types/react-dom": "^18.2.25",
53
+ "jest": "^29.7.0",
54
+ "prop-types": "^15.8.1",
55
+ "react": "^18.2.0",
56
+ "react-dom": "^18.2.0",
57
+ "ts-node": "^10.9.2",
58
+ "typescript": "^5.6.2",
59
+ "vite": "^5.2.11",
60
+ "yalc": "^1.0.0-pre.53"
61
+ },
62
+ "engines": {
63
+ "node": ">=20"
64
+ }
65
+ }
@@ -0,0 +1,33 @@
1
+ # Admin Customizations
2
+
3
+ You can extend the Medusa Admin to add widgets and new pages. Your customizations interact with API routes to provide merchants with custom functionalities.
4
+
5
+ > Learn more about Admin Extensions in [this documentation](https://docs.medusajs.com/learn/fundamentals/admin).
6
+
7
+ ## Example: Create a Widget
8
+
9
+ A widget is a React component that can be injected into an existing page in the admin dashboard.
10
+
11
+ For example, create the file `src/admin/widgets/product-widget.tsx` with the following content:
12
+
13
+ ```tsx title="src/admin/widgets/product-widget.tsx"
14
+ import { defineWidgetConfig } from "@medusajs/admin-sdk"
15
+
16
+ // The widget
17
+ const ProductWidget = () => {
18
+ return (
19
+ <div>
20
+ <h2>Product Widget</h2>
21
+ </div>
22
+ )
23
+ }
24
+
25
+ // The widget's configurations
26
+ export const config = defineWidgetConfig({
27
+ zone: "product.details.after",
28
+ })
29
+
30
+ export default ProductWidget
31
+ ```
32
+
33
+ This inserts a widget with the text “Product Widget” at the end of a product’s details page.
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+
9
+ /* Bundler mode */
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "noEmit": true,
15
+ "jsx": "react-jsx",
16
+
17
+ /* Linting */
18
+ "strict": true,
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "noFallthroughCasesInSwitch": true
22
+ },
23
+ "include": ["."]
24
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,135 @@
1
+ # Custom API Routes
2
+
3
+ An API Route is a REST API endpoint.
4
+
5
+ An API Route is created in a TypeScript or JavaScript file under the `/src/api` directory of your Medusa application. The file’s name must be `route.ts` or `route.js`.
6
+
7
+ > Learn more about API Routes in [this documentation](https://docs.medusajs.com/learn/fundamentals/api-routes)
8
+
9
+ For example, to create a `GET` API Route at `/store/hello-world`, create the file `src/api/store/hello-world/route.ts` with the following content:
10
+
11
+ ```ts
12
+ import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
13
+
14
+ export async function GET(req: MedusaRequest, res: MedusaResponse) {
15
+ res.json({
16
+ message: "Hello world!",
17
+ });
18
+ }
19
+ ```
20
+
21
+ ## Supported HTTP methods
22
+
23
+ The file based routing supports the following HTTP methods:
24
+
25
+ - GET
26
+ - POST
27
+ - PUT
28
+ - PATCH
29
+ - DELETE
30
+ - OPTIONS
31
+ - HEAD
32
+
33
+ You can define a handler for each of these methods by exporting a function with the name of the method in the paths `route.ts` file.
34
+
35
+ For example:
36
+
37
+ ```ts
38
+ import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
39
+
40
+ export async function GET(req: MedusaRequest, res: MedusaResponse) {
41
+ // Handle GET requests
42
+ }
43
+
44
+ export async function POST(req: MedusaRequest, res: MedusaResponse) {
45
+ // Handle POST requests
46
+ }
47
+
48
+ export async function PUT(req: MedusaRequest, res: MedusaResponse) {
49
+ // Handle PUT requests
50
+ }
51
+ ```
52
+
53
+ ## Parameters
54
+
55
+ To create an API route that accepts a path parameter, create a directory within the route's path whose name is of the format `[param]`.
56
+
57
+ For example, if you want to define a route that takes a `productId` parameter, you can do so by creating a file called `/api/products/[productId]/route.ts`:
58
+
59
+ ```ts
60
+ import type {
61
+ MedusaRequest,
62
+ MedusaResponse,
63
+ } from "@medusajs/framework/http"
64
+
65
+ export async function GET(req: MedusaRequest, res: MedusaResponse) {
66
+ const { productId } = req.params;
67
+
68
+ res.json({
69
+ message: `You're looking for product ${productId}`
70
+ })
71
+ }
72
+ ```
73
+
74
+ To create an API route that accepts multiple path parameters, create within the file's path multiple directories whose names are of the format `[param]`.
75
+
76
+ For example, if you want to define a route that takes both a `productId` and a `variantId` parameter, you can do so by creating a file called `/api/products/[productId]/variants/[variantId]/route.ts`.
77
+
78
+ ## Using the container
79
+
80
+ The Medusa container is available on `req.scope`. Use it to access modules' main services and other registered resources:
81
+
82
+ ```ts
83
+ import type {
84
+ MedusaRequest,
85
+ MedusaResponse,
86
+ } from "@medusajs/framework/http"
87
+
88
+ export const GET = async (
89
+ req: MedusaRequest,
90
+ res: MedusaResponse
91
+ ) => {
92
+ const productModuleService = req.scope.resolve("product")
93
+
94
+ const [, count] = await productModuleService.listAndCount()
95
+
96
+ res.json({
97
+ count,
98
+ })
99
+ }
100
+ ```
101
+
102
+ ## Middleware
103
+
104
+ You can apply middleware to your routes by creating a file called `/api/middlewares.ts`. This file must export a configuration object with what middleware you want to apply to which routes.
105
+
106
+ For example, if you want to apply a custom middleware function to the `/store/custom` route, you can do so by adding the following to your `/api/middlewares.ts` file:
107
+
108
+ ```ts
109
+ import { defineMiddlewares } from "@medusajs/framework/http"
110
+ import type {
111
+ MedusaRequest,
112
+ MedusaResponse,
113
+ MedusaNextFunction,
114
+ } from "@medusajs/framework/http";
115
+
116
+ async function logger(
117
+ req: MedusaRequest,
118
+ res: MedusaResponse,
119
+ next: MedusaNextFunction
120
+ ) {
121
+ console.log("Request received");
122
+ next();
123
+ }
124
+
125
+ export default defineMiddlewares({
126
+ routes: [
127
+ {
128
+ matcher: "/store/custom",
129
+ middlewares: [logger],
130
+ },
131
+ ],
132
+ })
133
+ ```
134
+
135
+ The `matcher` property can be either a string or a regular expression. The `middlewares` property accepts an array of middleware functions.
@@ -0,0 +1,8 @@
1
+ import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
2
+
3
+ export async function GET(
4
+ req: MedusaRequest,
5
+ res: MedusaResponse
6
+ ) {
7
+ res.sendStatus(200);
8
+ }
@@ -0,0 +1,8 @@
1
+ import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
2
+
3
+ export async function GET(
4
+ req: MedusaRequest,
5
+ res: MedusaResponse
6
+ ) {
7
+ res.sendStatus(200);
8
+ }
@@ -0,0 +1,38 @@
1
+ # Custom scheduled jobs
2
+
3
+ A scheduled job is a function executed at a specified interval of time in the background of your Medusa application.
4
+
5
+ > Learn more about scheduled jobs in [this documentation](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs).
6
+
7
+ A scheduled job is created in a TypeScript or JavaScript file under the `src/jobs` directory.
8
+
9
+ For example, create the file `src/jobs/hello-world.ts` with the following content:
10
+
11
+ ```ts
12
+ import {
13
+ MedusaContainer
14
+ } from "@medusajs/framework/types";
15
+
16
+ export default async function myCustomJob(container: MedusaContainer) {
17
+ const productService = container.resolve("product")
18
+
19
+ const products = await productService.listAndCountProducts();
20
+
21
+ // Do something with the products
22
+ }
23
+
24
+ export const config = {
25
+ name: "daily-product-report",
26
+ schedule: "0 0 * * *", // Every day at midnight
27
+ };
28
+ ```
29
+
30
+ A scheduled job file must export:
31
+
32
+ - The function to be executed whenever it’s time to run the scheduled job.
33
+ - A configuration object defining the job. It has three properties:
34
+ - `name`: a unique name for the job.
35
+ - `schedule`: a [cron expression](https://crontab.guru/).
36
+ - `numberOfExecutions`: an optional integer, specifying how many times the job will execute before being removed
37
+
38
+ The `handler` is a function that accepts one parameter, `container`, which is a `MedusaContainer` instance used to resolve services.
@@ -0,0 +1,26 @@
1
+ # Module Links
2
+
3
+ A module link forms an association between two data models of different modules, while maintaining module isolation.
4
+
5
+ > Learn more about links in [this documentation](https://docs.medusajs.com/learn/fundamentals/module-links)
6
+
7
+ For example:
8
+
9
+ ```ts
10
+ import BlogModule from "../modules/blog"
11
+ import ProductModule from "@medusajs/medusa/product"
12
+ import { defineLink } from "@medusajs/framework/utils"
13
+
14
+ export default defineLink(
15
+ ProductModule.linkable.product,
16
+ BlogModule.linkable.post
17
+ )
18
+ ```
19
+
20
+ This defines a link between the Product Module's `product` data model and the Blog Module (custom module)'s `post` data model.
21
+
22
+ Then, in the Medusa application, run the following command to sync the links to the database:
23
+
24
+ ```bash
25
+ npx medusa db:migrate
26
+ ```