From e1f310278498056f78a9b66c98b4e6a2cf427938 Mon Sep 17 00:00:00 2001 From: Peter van Vliet <108156553+petermasking@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:57:01 +0100 Subject: [PATCH] feat(runtime): add support for setting up and tearing down applications (#390) --- documentation/docs/.vitepress/config.ts | 1 + documentation/docs/deploy/segmentation.md | 18 +-- .../docs/develop/application-structure.md | 79 ++++++++----- .../docs/develop/data-consistency.md | 4 +- documentation/docs/develop/error-handling.md | 4 +- documentation/docs/develop/middleware.md | 4 +- .../docs/develop/setup-and-teardown.md | 63 ++++++++++ .../docs/develop/writing-functions.md | 4 +- .../docs/fundamentals/building-blocks.md | 8 +- .../docs/fundamentals/runtime-services.md | 9 ++ documentation/docs/integrate/rpc-api.md | 36 +++--- documentation/docs/integrate/vite-plugin.md | 2 +- .../docs/introduction/quick-start.md | 10 +- examples/apps/contact-list/index.html | 2 +- .../contact-list/segments/server.segment.json | 10 +- examples/apps/contact-list/services/node.json | 2 + .../contact-list/services/standalone.json | 2 + .../src/{shared => domain}/contact/Contact.ts | 0 .../src/domain/contact/createContact.ts | 11 ++ .../src/domain/contact/deleteContact.ts | 9 ++ .../src/domain/contact/getContacts.ts | 11 ++ .../src/integrations/database/Database.ts | 110 ++++++++++++++++++ .../database}/DatabaseError.ts | 0 examples/apps/contact-list/src/jitar.ts | 4 +- examples/apps/contact-list/src/setUpNode.ts | 6 + .../src/shared/common/createId.ts | 7 -- .../src/shared/common/getCollection.ts | 11 -- .../src/shared/common/getDatabase.ts | 28 ----- .../src/shared/contact/createContact.ts | 22 ---- .../src/shared/contact/deleteContact.ts | 13 --- .../src/shared/contact/getContacts.ts | 18 --- .../apps/contact-list/src/tearDownNode.ts | 6 + .../src/{frontend => webui}/App.tsx | 0 .../{frontend => webui}/components/Header.tsx | 2 +- .../components/contact}/ContactForm.tsx | 2 +- .../components/contact}/ContactItem.tsx | 2 +- .../components/contact}/ContactList.tsx | 2 +- .../src/{frontend => webui}/main.tsx | 0 .../{frontend => webui}/pages/ContactPage.tsx | 12 +- examples/apps/contact-list/vite.config.ts | 2 +- examples/concepts/access-protection/README.md | 2 +- examples/concepts/construction/README.md | 43 +++++++ examples/concepts/construction/package.json | 12 ++ examples/concepts/construction/requests.http | 4 + .../segments/default.segment.json | 8 ++ .../construction/services/standalone.json | 6 + .../concepts/construction/src/database.ts | 4 + examples/concepts/construction/src/getData.ts | 14 +++ examples/concepts/construction/src/jitar.ts | 9 ++ examples/concepts/construction/src/setUp.ts | 8 ++ .../concepts/construction/src/tearDown.ts | 6 + examples/concepts/construction/tsconfig.json | 19 +++ examples/concepts/cors/README.md | 2 +- .../concepts/data-transportation/README.md | 2 +- examples/concepts/error-handling/README.md | 2 +- examples/concepts/health-checks/README.md | 2 +- examples/concepts/hello-world/README.md | 2 +- examples/concepts/load-balancing/README.md | 2 +- examples/concepts/middleware/README.md | 2 +- examples/concepts/multi-version/README.md | 2 +- examples/concepts/node-client/README.md | 2 +- examples/concepts/segmentation/README.md | 2 +- package-lock.json | 21 +++- package.json | 1 + .../lit/segments/default.segment.json | 2 +- .../lit/src/{shared => domain}/sayHello.ts | 0 .../templates/lit/src/my-element.ts | 2 +- .../create-jitar/templates/lit/vite.config.ts | 2 +- .../react/segments/default.segment.json | 2 +- .../create-jitar/templates/react/src/App.tsx | 2 +- .../react/src/{shared => domain}/sayHello.ts | 0 .../templates/react/vite.config.ts | 2 +- .../solid/segments/default.segment.json | 2 +- .../create-jitar/templates/solid/src/App.tsx | 2 +- .../solid/src/{shared => domain}/sayHello.ts | 0 .../templates/solid/vite.config.ts | 2 +- .../svelte/segments/default.segment.json | 2 +- .../templates/svelte/src/App.svelte | 2 +- .../svelte/src/{shared => domain}/sayHello.ts | 0 .../templates/svelte/vite.config.ts | 2 +- .../vue/segments/default.segment.json | 2 +- .../create-jitar/templates/vue/src/App.vue | 2 +- .../vue/src/{shared => domain}/sayHello.ts | 0 .../create-jitar/templates/vue/vite.config.ts | 2 +- packages/runtime/src/services/LocalNode.ts | 10 +- .../runtime/src/services/LocalRepository.ts | 4 +- packages/runtime/src/services/Proxy.ts | 4 +- .../runtime/src/services/RemoteRepository.ts | 4 +- packages/runtime/src/services/Repository.ts | 4 +- packages/runtime/src/services/Runtime.ts | 7 ++ .../test/services/LocalRepository.spec.ts | 12 +- packages/server-nodejs/src/JitarServer.ts | 76 ++++++++++-- .../src/configuration/RuntimeConfiguration.ts | 14 ++- .../src/controllers/ModulesController.ts | 2 +- 94 files changed, 616 insertions(+), 264 deletions(-) create mode 100644 documentation/docs/develop/setup-and-teardown.md rename examples/apps/contact-list/src/{shared => domain}/contact/Contact.ts (100%) create mode 100644 examples/apps/contact-list/src/domain/contact/createContact.ts create mode 100644 examples/apps/contact-list/src/domain/contact/deleteContact.ts create mode 100644 examples/apps/contact-list/src/domain/contact/getContacts.ts create mode 100644 examples/apps/contact-list/src/integrations/database/Database.ts rename examples/apps/contact-list/src/{shared/common => integrations/database}/DatabaseError.ts (100%) create mode 100644 examples/apps/contact-list/src/setUpNode.ts delete mode 100644 examples/apps/contact-list/src/shared/common/createId.ts delete mode 100644 examples/apps/contact-list/src/shared/common/getCollection.ts delete mode 100644 examples/apps/contact-list/src/shared/common/getDatabase.ts delete mode 100644 examples/apps/contact-list/src/shared/contact/createContact.ts delete mode 100644 examples/apps/contact-list/src/shared/contact/deleteContact.ts delete mode 100644 examples/apps/contact-list/src/shared/contact/getContacts.ts create mode 100644 examples/apps/contact-list/src/tearDownNode.ts rename examples/apps/contact-list/src/{frontend => webui}/App.tsx (100%) rename examples/apps/contact-list/src/{frontend => webui}/components/Header.tsx (71%) rename examples/apps/contact-list/src/{frontend/components => webui/components/contact}/ContactForm.tsx (96%) rename examples/apps/contact-list/src/{frontend/components => webui/components/contact}/ContactItem.tsx (95%) rename examples/apps/contact-list/src/{frontend/components => webui/components/contact}/ContactList.tsx (90%) rename examples/apps/contact-list/src/{frontend => webui}/main.tsx (100%) rename examples/apps/contact-list/src/{frontend => webui}/pages/ContactPage.tsx (72%) create mode 100644 examples/concepts/construction/README.md create mode 100644 examples/concepts/construction/package.json create mode 100644 examples/concepts/construction/requests.http create mode 100644 examples/concepts/construction/segments/default.segment.json create mode 100644 examples/concepts/construction/services/standalone.json create mode 100644 examples/concepts/construction/src/database.ts create mode 100644 examples/concepts/construction/src/getData.ts create mode 100644 examples/concepts/construction/src/jitar.ts create mode 100644 examples/concepts/construction/src/setUp.ts create mode 100644 examples/concepts/construction/src/tearDown.ts create mode 100644 examples/concepts/construction/tsconfig.json rename packages/create-jitar/templates/lit/src/{shared => domain}/sayHello.ts (100%) rename packages/create-jitar/templates/react/src/{shared => domain}/sayHello.ts (100%) rename packages/create-jitar/templates/solid/src/{shared => domain}/sayHello.ts (100%) rename packages/create-jitar/templates/svelte/src/{shared => domain}/sayHello.ts (100%) rename packages/create-jitar/templates/vue/src/{shared => domain}/sayHello.ts (100%) diff --git a/documentation/docs/.vitepress/config.ts b/documentation/docs/.vitepress/config.ts index e65cf9e4..049a16dd 100644 --- a/documentation/docs/.vitepress/config.ts +++ b/documentation/docs/.vitepress/config.ts @@ -40,6 +40,7 @@ export default defineConfig({ { text: 'Error handling', link: '/develop/error-handling' }, { text: 'State management', link: '/develop/state-management' }, { text: 'Data consistency', link: '/develop/data-consistency' }, + { text: 'Set up and tear down', link: '/develop/setup-and-teardown' }, { text: 'Middleware', link: '/develop/middleware' }, { text: 'Validation', link: '/develop/validation' }, { text: 'Security', link: '/develop/security' }, diff --git a/documentation/docs/deploy/segmentation.md b/documentation/docs/deploy/segmentation.md index 766aee89..c3238baa 100644 --- a/documentation/docs/deploy/segmentation.md +++ b/documentation/docs/deploy/segmentation.md @@ -17,14 +17,14 @@ Segments are used to break applications down into distributable pieces. A segmen ```json { - "./shared/sayHello": + "./domain/sayHello": { "sayHello": { "access": "public" } } } ``` -This example includes the `sayHello` function from the `shared/sayHello.ts` module file. Now let's decompose the configuration file and dive into the details. +This example includes the `sayHello` function from the `domain/sayHello.ts` module file. Now let's decompose the configuration file and dive into the details. ### Naming and placement @@ -68,7 +68,7 @@ The import names must correspond with the export names in the module. You can al ```json { - "./shared/sayHello": + "./domain/sayHello": { "default": { "access": "public" }, "another": { "access": "public" }, @@ -86,7 +86,7 @@ Segments enable deploying application pieces on different servers. This requires ```json { - "./shared/secret": + "./domain/secret": { "getSecret": { "access": "private" }, "useSecret": { "access": "public" } @@ -107,7 +107,7 @@ Jitar generates an endpoint for each public function. These endpoints are used f For example, if we need to update our sayHello function to split the name parameter into a separate first and last parameter, we can implement it like this. ```ts -// src/shared/sayHelloV2.ts +// src/domain/sayHelloV2.ts export async function sayHello(first: string, last: string): Promise { return `Hello, ${first} ${last}!`; @@ -118,11 +118,11 @@ Now we have a separate module file per version that can be registered in the seg ```json { - "./shared/sayHello": + "./domain/sayHello": { "sayHello": { "access": "public", "version": "1.0.0" } }, - "./shared/sayHelloV2": + "./domain/sayHelloV2": { "sayHello": { "access": "public", "version": "2.0.0" } } @@ -140,7 +140,7 @@ When registering the same version multiple times, Jitar will execute the first r By default the import names are used for registering functions. In some cases you might want to expose a function under an alias name. A common use case is to create unique names in case multiple modules have equal exports or when combining multiple versions in a single module file. For example combining the sayHello functions. ```ts -// src/shared/sayHello.ts +// src/domain/sayHello.ts export async function sayHello(name: string): Promise { return `Hello, ${name}!`; @@ -156,7 +156,7 @@ In this case both versions are in the same file, but to make them unique they bo ```json { - "./shared/sayHello": + "./domain/sayHello": { "sayHello": { "access": "public", "version": "1.0.0" }, "sayHelloV2": { "access": "public", "version": "2.0.0", "as": "sayHello" } diff --git a/documentation/docs/develop/application-structure.md b/documentation/docs/develop/application-structure.md index dcbf7bee..0625e2ff 100644 --- a/documentation/docs/develop/application-structure.md +++ b/documentation/docs/develop/application-structure.md @@ -38,60 +38,75 @@ Depending on the frameworks you're using you might need to add more folders. For ## Source -The `src` folder contains all application code. Our typical structure looks like this. +The `src` folder contains all application code. Our typical (full-stack) structure looks like this. ```txt src -├─ concept 1 -│ ├─ function1.ts -│ ├─ function2.ts -│ └─ model1.ts -├─ concept 2 -│ ├─ function3_v1.ts -│ └─ function3_v2.ts +├─ domain +│ ├─ concept1 +│ ├─ concept2 +│ ├─ ... +├─ webui +│ ├─ components +│ ├─ layouts +│ ├─ pages +├─ assets +│ ├─ downloads +│ ├─ images +│ ├─ ... +├─ integrations +│ ├─ database +│ ├─ notifications +│ ├─ ... └─ jitar.ts ``` -We use the following rules for this structure: +Each folder has it's own responsibility: + +* domain - contains all business domain logic; +* webui - contains all web ui elements; +* assets - contains all assets used by the domain and webui; +* integrations - contains all integrations with external systems. -* folder per concept - we prefer using business concepts like 'account' or 'company'; -* [function](../fundamentals/building-blocks#functions) per file - we use corresponding filenames with the function name like 'searchAccounts'' or 'createMonthReport'; +For maintainability reasons it's important to get the dependencies right. We use the following rules: + +* domain - depends on assets and integrations; +* webui - depends on assets, domain and integrations. + +For setting up the domain, we use the following rules: + +* folder per concept - we prefer using business domain concepts like 'account' or 'company'; +* [function](../fundamentals/building-blocks#functions) per file - we use corresponding filenames with the function name like 'searchAccounts' or 'createMonthReport'; * [data model](./data-sharing) per file - use corresponding filenames with the model names like 'Account' or 'MonthReport'; * [version](../deploy/segmentation#versioning) per file - both functions and models, we add the version number at the end of the filename. A concept folder can be split into multiple subfolders. We do this for larger applications, but we always try to keep the structure as flat as possible to avoid complexity. -For applications with a frontend we like to separate the frontend components from the rest. This makes migrating to another (version of the) framework less painful for applications that outlive their framework (we've been there). In this case we commonly use the following structure. +## Tests + +The `test` folder contains all application tests. We always mimic the source folder structure here. ```txt -src -├─ shared +test +├─ domain │ ├─ concept1 │ ├─ concept2 -├─ frontend +│ ├─ ... +├─ webui │ ├─ components │ ├─ layouts │ ├─ pages +├─ assets +│ ├─ downloads +│ ├─ images +│ ├─ ... +├─ integrations +│ ├─ database +│ ├─ notifications +│ ├─ ... └─ jitar.ts ``` -Make sure the frontend components import the shared components, and not the other way around. - -## Tests - -The `test` folder contains all application tests. We always mimic the source folder structure here. - -```txt -test -├─ concept 1 -│ ├─ function1.spec.ts -│ ├─ function2.spec.ts -│ └─ model1.ts -└─ concept 2 - ├─ function3_v1.spec.ts - └─ function3_v2.spec.ts -``` - By keeping a 1-on-1 relation with the source files makes it easy to find the associated tests. ## Segments diff --git a/documentation/docs/develop/data-consistency.md b/documentation/docs/develop/data-consistency.md index be81577e..ead57df5 100644 --- a/documentation/docs/develop/data-consistency.md +++ b/documentation/docs/develop/data-consistency.md @@ -6,8 +6,8 @@ prev: link: /develop/state-management next: - text: Middleware - link: /develop/middleware + text: Set up and tear down + link: /develop/setup-and-teardown --- diff --git a/documentation/docs/develop/error-handling.md b/documentation/docs/develop/error-handling.md index 9761a632..45bdf099 100644 --- a/documentation/docs/develop/error-handling.md +++ b/documentation/docs/develop/error-handling.md @@ -29,7 +29,7 @@ export DatabaseError extends Error ``` ```ts -// src/account/storeAccount.ts +// src/domain/account/storeAccount.ts import { Account } from './Account'; import { DatabaseError } from '../DatabaseError'; @@ -40,7 +40,7 @@ export async function storeAccount(account: Account): Promise ``` ```ts -// src/account/createAccount.ts +// src/domain/account/createAccount.ts import { Account } from './Account'; import { storeAccount } from './storeAccount'; import { DatabaseError } from '../DatabaseError'; diff --git a/documentation/docs/develop/middleware.md b/documentation/docs/develop/middleware.md index ff29e6cb..6ff2891e 100644 --- a/documentation/docs/develop/middleware.md +++ b/documentation/docs/develop/middleware.md @@ -2,8 +2,8 @@ layout: doc prev: - text: Data consistency - link: /develop/data-consistency + text: Set up and tear down + link: /develop/setup-and-teardown next: text: Validation diff --git a/documentation/docs/develop/setup-and-teardown.md b/documentation/docs/develop/setup-and-teardown.md new file mode 100644 index 00000000..b8375aab --- /dev/null +++ b/documentation/docs/develop/setup-and-teardown.md @@ -0,0 +1,63 @@ +--- +layout: doc + +prev: + text: Data consistency + link: /develop/data-consistency + +next: + text: Middleware + link: /develop/middleware + +--- + +# Set up and tear down + +Before an application starts or stops, you might want to do some additional things like connecting and disconnecting the database. +For this, Jitar provides hooks for executing set up and tear down scripts. + +The scripts can be configured per service in its configuration. +Adding both scripts to a standalone configuration looks like this: + +```json +{ + "url": "http://standalone.example.com:3000", + "setUp": "./setUp", + "tearDown": "./tearDown", + "standalone": {} +} +``` + +Both script are optional, so you are free to use the one or the other, or none at all. + +The scripts do not have any specific requirements, so there's nothing special about them. +The following example shows a simple set up script. + +```ts +// src/setUp.ts +import { Database } from './integrations/database/Database'; + +await Database.connect(process.env.DB_CONN_STRING); +``` + +All it does is importing dependencies and performing all actions required. + +::: warning IMPORTANT +The set up script is executed before the service starts. If the script fails, Jitar will exit. +::: + +The tear down script looks almost the same in this case. + +```ts +// src/tearDown.ts +import { Database } from './integrations/database/Database'; + +await Database.disconnect(); +``` + +::: warning IMPORTANT +The tear up script is executed after the service has stopped. If the script fails, Jitar will exit. +::: + +It's common to create service specific scripts. +If the scripts overlap or get to big, we recommend breaking them up into multiple smaller scripts. diff --git a/documentation/docs/develop/writing-functions.md b/documentation/docs/develop/writing-functions.md index c07b503f..b240d0fb 100644 --- a/documentation/docs/develop/writing-functions.md +++ b/documentation/docs/develop/writing-functions.md @@ -85,7 +85,7 @@ This example works with the default Jitar setup, but will break when using [midd Functions can be imported and called as if they are locally available. ```ts -// src/shared/example.ts +// src/domain/example.ts import { sayHello } from './sayHello'; export async function example(): Promise @@ -95,7 +95,7 @@ export async function example(): Promise console.log(message); } -// src/shared/sayHello.ts +// src/domain/sayHello.ts export async function sayHello(name: string): Promise { return `Hello, ${name}!`; diff --git a/documentation/docs/fundamentals/building-blocks.md b/documentation/docs/fundamentals/building-blocks.md index 14979642..d8377e46 100644 --- a/documentation/docs/fundamentals/building-blocks.md +++ b/documentation/docs/fundamentals/building-blocks.md @@ -24,7 +24,7 @@ In this section you'll learn about using functions and creating segments to crea Plain functions are used as primary building blocks for applications. Let's see how a simple function looks like. ```ts -// src/shared/sayHello.ts +// src/domain/sayHello.ts export async function sayHello(name: string): Promise { return `Hello, ${name}!`; @@ -53,7 +53,7 @@ Every function has a unique name used for internal and external identification. { location relative to the source folder }/{ function name} ``` -For the simple sayHello function the FQN of this function is `shared/sayHello`. Note that there is no leading / in the name. +For the simple sayHello function the FQN of this function is `domain/sayHello`. Note that there is no leading / in the name. ## Segments @@ -64,7 +64,7 @@ For the definition of a segment, JSON files are used with the '.segment.json' ex ```json // default.segment.json { - "./shared/sayHello": + "./domain/sayHello": { "sayHello": { @@ -85,7 +85,7 @@ This configuration connects very well with the JavaScript module system. It incl 1. Version number per function (optional, default 0.0.0) 1. Alternative name (optional, default the name of the function) -The example configuration exposes the `sayHello` function from the `./shared/sayHello` module file. The function has public access, meaning that it's accessible from other segments. Both the version and as properties have the default value, so these can optionally be removed. +The example configuration exposes the `sayHello` function from the `./domain/sayHello` module file. The function has public access, meaning that it's accessible from other segments. Both the version and as properties have the default value, so these can optionally be removed. More in depth information on segments and the configuration can be found in the [DEPLOY section](../deploy/segmentation). diff --git a/documentation/docs/fundamentals/runtime-services.md b/documentation/docs/fundamentals/runtime-services.md index 31ae83cc..c9b38f74 100644 --- a/documentation/docs/fundamentals/runtime-services.md +++ b/documentation/docs/fundamentals/runtime-services.md @@ -26,6 +26,8 @@ Configurations are placed in JSON files. The basic structure looks like this. ```json { "url": "SERVICE_URL", + "setUp": "SET_UP_SCRIPT", + "tearDown": "TEAR_DOWN_SCRIPT", "SERVICE_TYPE": { "PROPERTY_1": "…", @@ -34,6 +36,13 @@ Configurations are placed in JSON files. The basic structure looks like this. } ``` +There are four properties at root level: + +* url - service url containing protocol, address and port (e.g. `http://service.example.com:3000`). +* setUp - optional [set up script](../develop/setup-and-teardown.md) that gets executed on startup (e.g. `./setUp`). +* tearDown - optional [tear down script](../develop/setup-and-teardown.md) that gets executed on shutdown (e.g. `./tearDown`). +* SERVICE_TYPE - configuration of the specific service (differs per type). + An instance can only run one type of service. Each service has its own configuration properties. All types and their properties are explained next. ## Repository diff --git a/documentation/docs/integrate/rpc-api.md b/documentation/docs/integrate/rpc-api.md index 4918ae27..11766119 100644 --- a/documentation/docs/integrate/rpc-api.md +++ b/documentation/docs/integrate/rpc-api.md @@ -26,7 +26,7 @@ GET http://{ server address }/rpc/{ FQN }?{ parameters } HTTP/1.1 Let's see how this works for the following simple [function](../fundamentals/building-blocks#functions). ```ts -// src/shared/sayHello.ts +// src/domain/sayHello.ts export async function sayHello(name: string): Promise { return `Hello, ${name}!`; @@ -37,7 +37,7 @@ That is added to a [segment configuration](../fundamentals/building-blocks#segme ```json { - "./shared/sayHello": + "./domain/sayHello": { "sayHello": { "access": "public" } } @@ -47,15 +47,15 @@ That is added to a [segment configuration](../fundamentals/building-blocks#segme After starting Jitar we can call the function with the following HTTP request. ```http -GET http://localhost:3000/rpc/shared/sayHello?name=John HTTP/1.1 +GET http://localhost:3000/rpc/domain/sayHello?name=John HTTP/1.1 ``` -The function's [fully qualified function name (FQN)](../fundamentals/building-blocks#fully-qualified-name-fqn) is in this case `shared/sayHello`. At the start of Jitar all registered RPC entries are listed, so you can always find the FQN you need. +The function's [fully qualified function name (FQN)](../fundamentals/building-blocks#fully-qualified-name-fqn) is in this case `domain/sayHello`. At the start of Jitar all registered RPC entries are listed, so you can always find the FQN you need. In the query string you can set the function parameter values by name. For functions that take more complex data as parameter values the POST method can be used. ```http -POST http://example.com:3000/rpc/shared/sayHello HTTP/1.1 +POST http://example.com:3000/rpc/domain/sayHello HTTP/1.1 content-type: application/json { @@ -74,7 +74,7 @@ Functions parameters can have many forms. Jitar has support for the following op Parameters with a default value are considered optional: ```ts -// src/shared/sayHello.ts +// src/domain/sayHello.ts export async function sayHello(name = 'World'): Promise { return `Hello, ${name}!`; @@ -84,7 +84,7 @@ export async function sayHello(name = 'World'): Promise This function can be called without a value for the name parameter. ```http -GET http://localhost:3000/rpc/shared/sayHello HTTP/1.1 +GET http://localhost:3000/rpc/domain/sayHello HTTP/1.1 ``` ### Destructured parameters @@ -92,9 +92,9 @@ GET http://localhost:3000/rpc/shared/sayHello HTTP/1.1 Functions can have destructured parameters. ```ts -import Person from './Person'; +import { Person } from './Person'; -// src/shared/sayHello.ts +// src/domain/sayHello.ts export async function sayHello({ name, city }: Person): Promise { return `Hello, ${name} from ${city}!`; @@ -104,7 +104,7 @@ export async function sayHello({ name, city }: Person): Promise In the end, the function only has two parameters: name and city. When making a RPC call, these are the parameter values that need to be provided. ```http -GET http://localhost:3000/rpc/shared/sayHello?name=John&city=Rome HTTP/1.1 +GET http://localhost:3000/rpc/domain/sayHello?name=John&city=Rome HTTP/1.1 ``` ::: warning IMPORTANT @@ -116,7 +116,7 @@ Nested parameter destructuring is on the [known limitations list](../internals/r Functions can use the rest operator. ```ts -// src/shared/sayHello.ts +// src/domain/sayHello.ts export async function sayHello(...names: string[]): Promise { return `Hello, ${names.join(', ')}!`; @@ -126,7 +126,7 @@ export async function sayHello(...names: string[]): Promise RPC calls require the exact name of a parameter. In this case we also need to add the rest operator and provide an array with the values. In order to send the array we need to use the POST method. ```http -POST http://example.com:3000/rpc/shared/sayHello HTTP/1.1 +POST http://example.com:3000/rpc/domain/sayHello HTTP/1.1 content-type: application/json { @@ -139,7 +139,7 @@ content-type: application/json Functions can use built-in and custom class parameters. For example this function that takes a custom Person class instance. ```ts -// src/shared/sayHello.ts +// src/domain/sayHello.ts import { Person } from './Person'; export async function sayHello(person: Person): Promise @@ -151,7 +151,7 @@ export async function sayHello(person: Person): Promise The Person class looks like this. ```ts -// src/shared/Person.ts +// src/domain/Person.ts export class Person { #name: string; @@ -171,7 +171,7 @@ export class Person This class is immutable, so it can only be constructed with constructor arguments. To call this function we need to provide all information required to construct the instance. ```http -POST http://example.com:3000/rpc/shared/sayHello?serialize=true HTTP/1.1 +POST http://example.com:3000/rpc/domain/sayHello?serialize=true HTTP/1.1 content-type: application/json { @@ -179,7 +179,7 @@ content-type: application/json { "serialized": true, "name": "Person", - "source": "shared/Person.js", + "source": "domain/Person.js", "args": ["John", 42], "fields": { } } @@ -209,7 +209,7 @@ GET http://localhost:3000/rpc/person/getPerson?serialize={true | false} HTTP/1.1 For plain objects the result will be the same in both cases, but for class instances with private fields it makes a big difference. For example returning an instance of the following class. ```ts -// src/shared/Person.ts +// src/domain/Person.ts export class Person { #name: string; @@ -240,7 +240,7 @@ With the serializer the response looks very different. { "serialized": true, "name": "Person", - "source": "shared/Person.js", + "source": "domain/Person.js", "args": ["John", 42], "fields": { } } diff --git a/documentation/docs/integrate/vite-plugin.md b/documentation/docs/integrate/vite-plugin.md index 0bb09da7..74181721 100644 --- a/documentation/docs/integrate/vite-plugin.md +++ b/documentation/docs/integrate/vite-plugin.md @@ -42,7 +42,7 @@ export default defineConfig({ The plugin takes 4 arguments: 1. srcPath - The path to the app source files. In most cases this is the `src` folder. -1. jitarPath - The path to the source files used by Jitar. This path is relative to the source root. We like to use `shared` (which points to `src/shared`), * but feel free to use something else. +1. jitarPath - The path to the source files used by Jitar. This path is relative to the source root. We like to use `domain` (which points to `src/domain`), * but feel free to use something else. 1. jitarUrl - The URL of the Jitar instance. Jitar uses by default `http://localhost:3000`, but can be configured differently in the Jitar config. 1. segments - The segments to use for the client app. This is an array of strings. The default is an empty array. diff --git a/documentation/docs/introduction/quick-start.md b/documentation/docs/introduction/quick-start.md index 22254b8d..bf3b4e88 100644 --- a/documentation/docs/introduction/quick-start.md +++ b/documentation/docs/introduction/quick-start.md @@ -37,10 +37,10 @@ Now you should be able to access the application on [http://localhost:3000](http ## 2. Add your functions -Functions are the main building blocks of Jitar applications. The created application already has one in the `src/shared` folder, so let's take a look. +Functions are the main building blocks of Jitar applications. The created application already has one in the `src/domain` folder, so let's take a look. ```ts -// src/shared/sayHello.ts +// src/domain/sayHello.ts export async function sayHello(name: string): Promise { return `Hello, ${name}!` @@ -56,7 +56,7 @@ Functions can be imported and called like any normal async function. // src/App.tsx /* other imports */ -import { sayHello } from './shared/sayHello'; +import { sayHello } from './domain/sayHello'; const message = await sayHello('World'); @@ -76,13 +76,13 @@ To tell Jitar if a function runs on the client or the server, the application is ```json // segments/default.segment.json { - "./shared/sayHello": { "sayHello": { "access": "public" } } + "./domain/sayHello": { "sayHello": { "access": "public" } } } ``` Segments are named, and their names are stored in the filename. In this case the segment is called ‘default’. The rest of the filename makes it a detectable segment configuration, because Jitar scans the project to find them. -Segment configurations work like the JavaScript module system. In this case we export the `sayHello` function from `./shared/sayHello` module file. Additionally we set the access level to public so it can be called from the client. The configuration can be extended by simply adding functions. +Segment configurations work like the JavaScript module system. In this case we export the `sayHello` function from `./domain/sayHello` module file. Additionally we set the access level to public so it can be called from the client. The configuration can be extended by simply adding functions. *Try yourself:* remove the function from the configuration and restart the application. Note that the client doesn't make a call to the server anymore. diff --git a/examples/apps/contact-list/index.html b/examples/apps/contact-list/index.html index 9b222280..a89a9791 100644 --- a/examples/apps/contact-list/index.html +++ b/examples/apps/contact-list/index.html @@ -10,6 +10,6 @@
- + diff --git a/examples/apps/contact-list/segments/server.segment.json b/examples/apps/contact-list/segments/server.segment.json index 084d77a8..2f5e4189 100644 --- a/examples/apps/contact-list/segments/server.segment.json +++ b/examples/apps/contact-list/segments/server.segment.json @@ -1,9 +1,5 @@ { - "./shared/common/createId": { "default": { "access": "private" } }, - "./shared/common/getCollection": { "default": { "access": "private" } }, - "./shared/common/getDatabase": { "default": { "access": "private" } }, - - "./shared/contact/createContact": { "default": { "access": "public" } }, - "./shared/contact/deleteContact": { "default": { "access": "public" } }, - "./shared/contact/getContacts": { "default": { "access": "public" } } + "./domain/contact/createContact": { "default": { "access": "public" } }, + "./domain/contact/deleteContact": { "default": { "access": "public" } }, + "./domain/contact/getContacts": { "default": { "access": "public" } } } \ No newline at end of file diff --git a/examples/apps/contact-list/services/node.json b/examples/apps/contact-list/services/node.json index b45b3f49..010d4ad4 100644 --- a/examples/apps/contact-list/services/node.json +++ b/examples/apps/contact-list/services/node.json @@ -1,5 +1,7 @@ { "url": "http://127.0.0.1:3001", + "setUp": "./setUpNode", + "tearDown": "./tearDownNode", "node": { "gateway": "http://127.0.0.1:3000", diff --git a/examples/apps/contact-list/services/standalone.json b/examples/apps/contact-list/services/standalone.json index 199ff518..582f0a92 100644 --- a/examples/apps/contact-list/services/standalone.json +++ b/examples/apps/contact-list/services/standalone.json @@ -1,5 +1,7 @@ { "url": "http://127.0.0.1:3000", + "setUp": "./setUpNode", + "tearDown": "./tearDownNode", "standalone": { "assets": [ "index.html", "main.js", "App.js", "vite.svg", "assets/**/*" ] diff --git a/examples/apps/contact-list/src/shared/contact/Contact.ts b/examples/apps/contact-list/src/domain/contact/Contact.ts similarity index 100% rename from examples/apps/contact-list/src/shared/contact/Contact.ts rename to examples/apps/contact-list/src/domain/contact/Contact.ts diff --git a/examples/apps/contact-list/src/domain/contact/createContact.ts b/examples/apps/contact-list/src/domain/contact/createContact.ts new file mode 100644 index 00000000..f043cc10 --- /dev/null +++ b/examples/apps/contact-list/src/domain/contact/createContact.ts @@ -0,0 +1,11 @@ + +import Database from '../../integrations/database/Database'; + +import Contact from './Contact'; + +export default async function createContact(name: string, address: string, phone: string, email: string): Promise +{ + const id = await Database.create('contacts', { name, address, phone, email }); + + return new Contact(id, name, address, phone, email); +} diff --git a/examples/apps/contact-list/src/domain/contact/deleteContact.ts b/examples/apps/contact-list/src/domain/contact/deleteContact.ts new file mode 100644 index 00000000..a72abc61 --- /dev/null +++ b/examples/apps/contact-list/src/domain/contact/deleteContact.ts @@ -0,0 +1,9 @@ + +import Database from '../../integrations/database/Database'; + +import Contact from './Contact'; + +export default async function deleteContact(contact: Contact): Promise +{ + await Database.delete('contacts', contact.id); +} diff --git a/examples/apps/contact-list/src/domain/contact/getContacts.ts b/examples/apps/contact-list/src/domain/contact/getContacts.ts new file mode 100644 index 00000000..e3d33357 --- /dev/null +++ b/examples/apps/contact-list/src/domain/contact/getContacts.ts @@ -0,0 +1,11 @@ + +import Database from '../../integrations/database/Database'; + +import Contact from './Contact'; + +export default async function getContacts(): Promise +{ + const documents = await Database.search('contacts'); + + return documents.map(document => new Contact(document.id, document.name, document.address, document.phone, document.email)); +} diff --git a/examples/apps/contact-list/src/integrations/database/Database.ts b/examples/apps/contact-list/src/integrations/database/Database.ts new file mode 100644 index 00000000..bdf59093 --- /dev/null +++ b/examples/apps/contact-list/src/integrations/database/Database.ts @@ -0,0 +1,110 @@ + +import { MongoClient, Db, ObjectId, WithId, Document, Filter } from 'mongodb'; + +import DatabaseError from './DatabaseError'; + +class Database +{ + #client?: MongoClient; + #database?: Db; + + get connected(): boolean + { + return this.#client !== undefined + && this.#database !== undefined; + } + + connect(connectionString: string, database: string): void + { + try + { + this.#client = new MongoClient(connectionString); + this.#database = this.#client.db(database); + } + catch (error: unknown) + { + throw new DatabaseError('Connection failed'); + } + } + + async disconnect(): Promise + { + if (this.#client === undefined) + { + throw new DatabaseError('Database not connected'); + } + + try + { + await this.#client.close(); + + this.#database = undefined; + this.#client = undefined; + } + catch (error: unknown) + { + throw new DatabaseError('Disconnection failed'); + } + } + + async create(collectionName: string, document: object): Promise + { + const collection = this.#getCollection(collectionName); + const _id = new ObjectId(); + + try + { + await collection.insertOne({ _id: _id, ...document } as any); + + return _id.toHexString(); + } + catch (error: unknown) + { + throw new DatabaseError('Create failed'); + } + } + + async search(collectionName: string, query?: object): Promise[]> + { + const collection = this.#getCollection(collectionName); + + try + { + const documents = await collection.find(query as Filter).toArray(); + + return documents.map(document => ({ ...document, id: document._id.toHexString() })); + } + catch (error: unknown) + { + throw new DatabaseError('Search failed'); + } + } + + async delete(collectionName: string, id: string): Promise + { + const collection = this.#getCollection(collectionName); + + try + { + await collection.deleteOne({ _id: new ObjectId(id) }); + } + catch (error: unknown) + { + throw new DatabaseError('Delete failed'); + } + } + + #getCollection(collectionName: string) + { + if (this.#database === undefined) + { + throw new DatabaseError('Database not connected'); + } + + return this.#database.collection(collectionName); + } +} + +const instance = new Database(); + +export default instance; diff --git a/examples/apps/contact-list/src/shared/common/DatabaseError.ts b/examples/apps/contact-list/src/integrations/database/DatabaseError.ts similarity index 100% rename from examples/apps/contact-list/src/shared/common/DatabaseError.ts rename to examples/apps/contact-list/src/integrations/database/DatabaseError.ts diff --git a/examples/apps/contact-list/src/jitar.ts b/examples/apps/contact-list/src/jitar.ts index 89260a34..6ae887de 100644 --- a/examples/apps/contact-list/src/jitar.ts +++ b/examples/apps/contact-list/src/jitar.ts @@ -2,6 +2,8 @@ import { buildServer } from 'jitar'; const moduleImporter = async (specifier: string) => import(specifier); - const server = await buildServer(moduleImporter); + +process.on('SIGINT', async () => server.stop()); + server.start(); diff --git a/examples/apps/contact-list/src/setUpNode.ts b/examples/apps/contact-list/src/setUpNode.ts new file mode 100644 index 00000000..d266997a --- /dev/null +++ b/examples/apps/contact-list/src/setUpNode.ts @@ -0,0 +1,6 @@ + +import Database from './integrations/database/Database'; + +Database.connect('mongodb://root:example@localhost:27017', 'react-mongodb'); + +console.log('Connected to database'); diff --git a/examples/apps/contact-list/src/shared/common/createId.ts b/examples/apps/contact-list/src/shared/common/createId.ts deleted file mode 100644 index d11adf24..00000000 --- a/examples/apps/contact-list/src/shared/common/createId.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import { ObjectId } from 'mongodb'; - -export default async function generateId(inputId?: string): Promise -{ - return new ObjectId(inputId); -} diff --git a/examples/apps/contact-list/src/shared/common/getCollection.ts b/examples/apps/contact-list/src/shared/common/getCollection.ts deleted file mode 100644 index 3b6fdd83..00000000 --- a/examples/apps/contact-list/src/shared/common/getCollection.ts +++ /dev/null @@ -1,11 +0,0 @@ - -import { Collection } from 'mongodb'; - -import getDatabase from './getDatabase'; - -export default async function getCollection(name: string): Promise> -{ - const database = await getDatabase(); - - return database.collection(name); -} diff --git a/examples/apps/contact-list/src/shared/common/getDatabase.ts b/examples/apps/contact-list/src/shared/common/getDatabase.ts deleted file mode 100644 index 11780328..00000000 --- a/examples/apps/contact-list/src/shared/common/getDatabase.ts +++ /dev/null @@ -1,28 +0,0 @@ - -import { MongoClient } from 'mongodb'; - -import DatabaseError from './DatabaseError'; - -let client: MongoClient; - -export default async function getDatabase() -{ - if (client === undefined) - { - client = await createClient(); - } - - return client.db('react-mongodb'); -} - -async function createClient(): Promise -{ - try - { - return await MongoClient.connect('mongodb://root:example@localhost:27017'); - } - catch (error: any) - { - throw new DatabaseError(error?.message); - } -} diff --git a/examples/apps/contact-list/src/shared/contact/createContact.ts b/examples/apps/contact-list/src/shared/contact/createContact.ts deleted file mode 100644 index b01f6e7f..00000000 --- a/examples/apps/contact-list/src/shared/contact/createContact.ts +++ /dev/null @@ -1,22 +0,0 @@ - -import getCollection from '../common/getCollection'; -import createId from '../common/createId'; - -import Contact from './Contact'; - -export default async function createContact(name: string, address: string, phone: string, email: string): Promise -{ - const id = await storeContact(name, address, phone, email); - - return new Contact(id, name, address, phone, email); -} - -async function storeContact(name: string, address: string, phone: string, email: string): Promise -{ - const collection = await getCollection('contacts'); - const id = await createId(); - - await collection.insertOne({ _id: id, name: name, address: address, phone: phone, email: email } as any); - - return id.toHexString(); -} diff --git a/examples/apps/contact-list/src/shared/contact/deleteContact.ts b/examples/apps/contact-list/src/shared/contact/deleteContact.ts deleted file mode 100644 index d727c418..00000000 --- a/examples/apps/contact-list/src/shared/contact/deleteContact.ts +++ /dev/null @@ -1,13 +0,0 @@ - -import getCollection from '../common/getCollection'; -import createId from '../common/createId'; - -import Contact from './Contact'; - -export default async function deleteContact(contact: Contact): Promise -{ - const collection = await getCollection('contacts'); - const mongoId = await createId(contact.id); - - await collection.deleteOne({ _id: mongoId }); -} diff --git a/examples/apps/contact-list/src/shared/contact/getContacts.ts b/examples/apps/contact-list/src/shared/contact/getContacts.ts deleted file mode 100644 index f76422a4..00000000 --- a/examples/apps/contact-list/src/shared/contact/getContacts.ts +++ /dev/null @@ -1,18 +0,0 @@ - -import getCollection from '../common/getCollection'; - -import Contact from './Contact'; - -export default async function getContacts(): Promise -{ - const collection = await getCollection('contacts'); - - const entries = await collection.find().toArray(); - - return mapContacts(entries); -} - -function mapContacts(contacts: any[]) -{ - return contacts.map(contact => new Contact(contact._id.toHexString(), contact.name, contact.address, contact.phone, contact.email)); -} diff --git a/examples/apps/contact-list/src/tearDownNode.ts b/examples/apps/contact-list/src/tearDownNode.ts new file mode 100644 index 00000000..8466b354 --- /dev/null +++ b/examples/apps/contact-list/src/tearDownNode.ts @@ -0,0 +1,6 @@ + +import Database from './integrations/database/Database'; + +await Database.disconnect(); + +console.log('Disconnected from database'); diff --git a/examples/apps/contact-list/src/frontend/App.tsx b/examples/apps/contact-list/src/webui/App.tsx similarity index 100% rename from examples/apps/contact-list/src/frontend/App.tsx rename to examples/apps/contact-list/src/webui/App.tsx diff --git a/examples/apps/contact-list/src/frontend/components/Header.tsx b/examples/apps/contact-list/src/webui/components/Header.tsx similarity index 71% rename from examples/apps/contact-list/src/frontend/components/Header.tsx rename to examples/apps/contact-list/src/webui/components/Header.tsx index 6c81e927..6fa05e25 100644 --- a/examples/apps/contact-list/src/frontend/components/Header.tsx +++ b/examples/apps/contact-list/src/webui/components/Header.tsx @@ -1,5 +1,5 @@ -export default function Header(props: any) +export default function Header() { return (
diff --git a/examples/apps/contact-list/src/frontend/components/ContactForm.tsx b/examples/apps/contact-list/src/webui/components/contact/ContactForm.tsx similarity index 96% rename from examples/apps/contact-list/src/frontend/components/ContactForm.tsx rename to examples/apps/contact-list/src/webui/components/contact/ContactForm.tsx index bd2bbb4b..8dec31cf 100644 --- a/examples/apps/contact-list/src/frontend/components/ContactForm.tsx +++ b/examples/apps/contact-list/src/webui/components/contact/ContactForm.tsx @@ -1,5 +1,5 @@ -import Contact from '../../shared/contact/Contact'; +import Contact from '../../../domain/contact/Contact'; type ContactFormProps = { diff --git a/examples/apps/contact-list/src/frontend/components/ContactItem.tsx b/examples/apps/contact-list/src/webui/components/contact/ContactItem.tsx similarity index 95% rename from examples/apps/contact-list/src/frontend/components/ContactItem.tsx rename to examples/apps/contact-list/src/webui/components/contact/ContactItem.tsx index c35d2d2c..486f27eb 100644 --- a/examples/apps/contact-list/src/frontend/components/ContactItem.tsx +++ b/examples/apps/contact-list/src/webui/components/contact/ContactItem.tsx @@ -1,5 +1,5 @@ -import Contact from '../../shared/contact/Contact'; +import Contact from '../../../domain/contact/Contact'; type ContactItemProps = { diff --git a/examples/apps/contact-list/src/frontend/components/ContactList.tsx b/examples/apps/contact-list/src/webui/components/contact/ContactList.tsx similarity index 90% rename from examples/apps/contact-list/src/frontend/components/ContactList.tsx rename to examples/apps/contact-list/src/webui/components/contact/ContactList.tsx index 5daed8e8..a02cc565 100644 --- a/examples/apps/contact-list/src/frontend/components/ContactList.tsx +++ b/examples/apps/contact-list/src/webui/components/contact/ContactList.tsx @@ -1,5 +1,5 @@ -import Contact from '../../shared/contact/Contact'; +import Contact from '../../../domain/contact/Contact'; import ContactItem from './ContactItem'; diff --git a/examples/apps/contact-list/src/frontend/main.tsx b/examples/apps/contact-list/src/webui/main.tsx similarity index 100% rename from examples/apps/contact-list/src/frontend/main.tsx rename to examples/apps/contact-list/src/webui/main.tsx diff --git a/examples/apps/contact-list/src/frontend/pages/ContactPage.tsx b/examples/apps/contact-list/src/webui/pages/ContactPage.tsx similarity index 72% rename from examples/apps/contact-list/src/frontend/pages/ContactPage.tsx rename to examples/apps/contact-list/src/webui/pages/ContactPage.tsx index 1ed78383..d3869f1b 100644 --- a/examples/apps/contact-list/src/frontend/pages/ContactPage.tsx +++ b/examples/apps/contact-list/src/webui/pages/ContactPage.tsx @@ -1,13 +1,13 @@ import { useEffect, useState } from 'react'; -import ContactForm from '../components/ContactForm'; -import ContactList from '../components/ContactList'; +import ContactForm from '../components/contact/ContactForm'; +import ContactList from '../components/contact/ContactList'; -import Contact from '../../shared/contact/Contact'; -import getContacts from '../../shared/contact/getContacts'; -import createContact from '../../shared/contact/createContact'; -import deleteContact from '../../shared/contact/deleteContact'; +import Contact from '../../domain/contact/Contact'; +import getContacts from '../../domain/contact/getContacts'; +import createContact from '../../domain/contact/createContact'; +import deleteContact from '../../domain/contact/deleteContact'; export default function ContactPage() { diff --git a/examples/apps/contact-list/vite.config.ts b/examples/apps/contact-list/vite.config.ts index d520a486..607945cd 100644 --- a/examples/apps/contact-list/vite.config.ts +++ b/examples/apps/contact-list/vite.config.ts @@ -5,6 +5,6 @@ import jitar from '@jitar/plugin-vite'; export default defineConfig({ plugins: [ react(), - jitar('src', 'shared', 'http://localhost:3000') + jitar('src', 'domain', 'http://localhost:3000') ] }) diff --git a/examples/concepts/access-protection/README.md b/examples/concepts/access-protection/README.md index 595cbea5..57c86d6b 100644 --- a/examples/concepts/access-protection/README.md +++ b/examples/concepts/access-protection/README.md @@ -29,7 +29,7 @@ The procedure to get the secret has been made private to ensure it isn't accessi npm install ``` -2\. Next build the application by running the following command. +2\. Next, build the application by running the following command. ```bash npm run build diff --git a/examples/concepts/construction/README.md b/examples/concepts/construction/README.md new file mode 100644 index 00000000..22f380c6 --- /dev/null +++ b/examples/concepts/construction/README.md @@ -0,0 +1,43 @@ + +# Jitar | Construction example + +This example demonstrates how to construct and deconstruct a Jitar application. + +The application creates and fills a database before the server starts. +When the application gets shut down, the database gets cleared. + +## Project setup + +**Functions** + +* getData (`src/getData.ts`) + +**Segments** + +* Default - contains the *getData* procedure (`segments/default.segment.json`) + +**Services** + +* Standalone (`services/standalone.json`) + +## Running the example + +1\. Install Jitar by running the following command from the root directory of the example. + +```bash +npm install +``` + +2\. Next, build the application by running the following command. + +```bash +npm run build +``` + +3\. Then start Jitar with the following command from the same directory. + +```bash +npm run standalone +``` + +The ``requests.http`` file contains an example request to call the procedure. diff --git a/examples/concepts/construction/package.json b/examples/concepts/construction/package.json new file mode 100644 index 00000000..cb8e204e --- /dev/null +++ b/examples/concepts/construction/package.json @@ -0,0 +1,12 @@ +{ + "name": "jitar-construction-example", + "private": true, + "type": "module", + "scripts": { + "build": "tsc", + "standalone": "node --experimental-network-imports dist/jitar.js --config=services/standalone.json" + }, + "dependencies": { + "jitar": "^0.4.1" + } +} \ No newline at end of file diff --git a/examples/concepts/construction/requests.http b/examples/concepts/construction/requests.http new file mode 100644 index 00000000..d8666a92 --- /dev/null +++ b/examples/concepts/construction/requests.http @@ -0,0 +1,4 @@ + +// Show the data from the database + +GET http://localhost:3000/rpc/getData HTTP/1.1 diff --git a/examples/concepts/construction/segments/default.segment.json b/examples/concepts/construction/segments/default.segment.json new file mode 100644 index 00000000..3f09435b --- /dev/null +++ b/examples/concepts/construction/segments/default.segment.json @@ -0,0 +1,8 @@ +{ + "./getData": { + "default": { + "access": "public", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/examples/concepts/construction/services/standalone.json b/examples/concepts/construction/services/standalone.json new file mode 100644 index 00000000..f270ecf4 --- /dev/null +++ b/examples/concepts/construction/services/standalone.json @@ -0,0 +1,6 @@ +{ + "url": "http://127.0.0.1:3000", + "setUp": "./setUp", + "tearDown": "./tearDown", + "standalone": {} +} \ No newline at end of file diff --git a/examples/concepts/construction/src/database.ts b/examples/concepts/construction/src/database.ts new file mode 100644 index 00000000..9aac3f0c --- /dev/null +++ b/examples/concepts/construction/src/database.ts @@ -0,0 +1,4 @@ + +const database: Map = new Map(); + +export default database; diff --git a/examples/concepts/construction/src/getData.ts b/examples/concepts/construction/src/getData.ts new file mode 100644 index 00000000..00b11779 --- /dev/null +++ b/examples/concepts/construction/src/getData.ts @@ -0,0 +1,14 @@ + +import database from './database'; + +export default async function getData(): Promise +{ + let data: string = ''; + + for (const [key, value] of database) + { + data += `${key} => ${value}\n`; + } + + return data; +} diff --git a/examples/concepts/construction/src/jitar.ts b/examples/concepts/construction/src/jitar.ts new file mode 100644 index 00000000..6ae887de --- /dev/null +++ b/examples/concepts/construction/src/jitar.ts @@ -0,0 +1,9 @@ + +import { buildServer } from 'jitar'; + +const moduleImporter = async (specifier: string) => import(specifier); +const server = await buildServer(moduleImporter); + +process.on('SIGINT', async () => server.stop()); + +server.start(); diff --git a/examples/concepts/construction/src/setUp.ts b/examples/concepts/construction/src/setUp.ts new file mode 100644 index 00000000..93dbcc76 --- /dev/null +++ b/examples/concepts/construction/src/setUp.ts @@ -0,0 +1,8 @@ + +import database from './database'; + +database.set('foo', 'bar'); +database.set('bar', 'baz'); +database.set('baz', 'foo'); + +console.log('Database initialized', database.size); diff --git a/examples/concepts/construction/src/tearDown.ts b/examples/concepts/construction/src/tearDown.ts new file mode 100644 index 00000000..008925e2 --- /dev/null +++ b/examples/concepts/construction/src/tearDown.ts @@ -0,0 +1,6 @@ + +import database from './database'; + +database.clear(); + +console.log('Database cleared', database.size); diff --git a/examples/concepts/construction/tsconfig.json b/examples/concepts/construction/tsconfig.json new file mode 100644 index 00000000..9d1aec07 --- /dev/null +++ b/examples/concepts/construction/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es2022", + "module": "es2022", + "rootDir": "./src/", + "moduleResolution": "node", + "outDir": "./dist", + "removeComments": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + }, + "exclude": [ + "cache", + "dist", + "node_modules", + "segments" + ] +} \ No newline at end of file diff --git a/examples/concepts/cors/README.md b/examples/concepts/cors/README.md index 5233bea2..3ce8fe59 100644 --- a/examples/concepts/cors/README.md +++ b/examples/concepts/cors/README.md @@ -32,7 +32,7 @@ Development npm install ``` -2\. Next build the application by running the following command. +2\. Next, build the application by running the following command. ```bash npm run build diff --git a/examples/concepts/data-transportation/README.md b/examples/concepts/data-transportation/README.md index 328cfdec..86ffff12 100644 --- a/examples/concepts/data-transportation/README.md +++ b/examples/concepts/data-transportation/README.md @@ -44,7 +44,7 @@ For production npm install ``` -2.\ Next build the application by running the following command. +2.\ Next, build the application by running the following command. ```bash npm run build diff --git a/examples/concepts/error-handling/README.md b/examples/concepts/error-handling/README.md index 150d98f1..408256ce 100644 --- a/examples/concepts/error-handling/README.md +++ b/examples/concepts/error-handling/README.md @@ -43,7 +43,7 @@ Production npm install ``` -2\. Next build the application by running the following command. +2\. Next, build the application by running the following command. ```bash npm run build diff --git a/examples/concepts/health-checks/README.md b/examples/concepts/health-checks/README.md index 51236aa3..749c1d57 100644 --- a/examples/concepts/health-checks/README.md +++ b/examples/concepts/health-checks/README.md @@ -27,7 +27,7 @@ There is no application for this example, only a health check that is registered npm install ``` -2\. Next build the application by running the following command. +2\. Next, build the application by running the following command. ```bash npm run build diff --git a/examples/concepts/hello-world/README.md b/examples/concepts/hello-world/README.md index c790fd8c..d78f5325 100644 --- a/examples/concepts/hello-world/README.md +++ b/examples/concepts/hello-world/README.md @@ -27,7 +27,7 @@ The application says hello to the given name. npm install ``` -2\. Next build the application by running the following command. +2\. Next, build the application by running the following command. ```bash npm run build diff --git a/examples/concepts/load-balancing/README.md b/examples/concepts/load-balancing/README.md index 344273c1..4fdd0cc5 100644 --- a/examples/concepts/load-balancing/README.md +++ b/examples/concepts/load-balancing/README.md @@ -40,7 +40,7 @@ Production npm install ``` -2\. Next build the application by running the following command. +2\. Next, build the application by running the following command. ```bash npm run build diff --git a/examples/concepts/middleware/README.md b/examples/concepts/middleware/README.md index 8ddcdba7..4137e6d2 100644 --- a/examples/concepts/middleware/README.md +++ b/examples/concepts/middleware/README.md @@ -27,7 +27,7 @@ The application implements a simple logging middleware. npm install ``` -2\. Next build the application by running the following command. +2\. Next, build the application by running the following command. ```bash npm run build diff --git a/examples/concepts/multi-version/README.md b/examples/concepts/multi-version/README.md index d769ef0c..4a93183e 100644 --- a/examples/concepts/multi-version/README.md +++ b/examples/concepts/multi-version/README.md @@ -29,7 +29,7 @@ Both versions can be requested independently. npm install ``` -2\. Next build the application by running the following command. +2\. Next, build the application by running the following command. ```bash npm run build diff --git a/examples/concepts/node-client/README.md b/examples/concepts/node-client/README.md index 9ebf7e5e..a8ab5ef3 100644 --- a/examples/concepts/node-client/README.md +++ b/examples/concepts/node-client/README.md @@ -28,7 +28,7 @@ The client connects to the Jitar server and gets a number. npm install ``` -2\. Next build the application by running the following command. +2\. Next, build the application by running the following command. ```bash npm run build diff --git a/examples/concepts/segmentation/README.md b/examples/concepts/segmentation/README.md index 918f625a..fa45e408 100644 --- a/examples/concepts/segmentation/README.md +++ b/examples/concepts/segmentation/README.md @@ -39,7 +39,7 @@ Production npm install ``` -2\. Next build the application by running the following command. +2\. Next, build the application by running the following command. ```bash npm run build diff --git a/package-lock.json b/package-lock.json index 30f0b581..d0fd9e2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "jitar-monorepo", - "version": "0.0.1", + "version": "0.4.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "jitar-monorepo", - "version": "0.0.1", + "version": "0.4.3", "license": "MIT", "workspaces": [ "packages/caching", @@ -19,6 +19,7 @@ "packages/server-nodejs", "tools/eslint-plugin", "examples/concepts/access-protection", + "examples/concepts/construction", "examples/concepts/cors", "examples/concepts/data-transportation", "examples/concepts/error-handling", @@ -80,6 +81,12 @@ "jitar": "^0.4.1" } }, + "examples/concepts/construction": { + "name": "jitar-construction-example", + "dependencies": { + "jitar": "^0.4.1" + } + }, "examples/concepts/cors": { "name": "jitar-cors-example", "dependencies": { @@ -7231,6 +7238,10 @@ "resolved": "examples/concepts/access-protection", "link": true }, + "node_modules/jitar-construction-example": { + "resolved": "examples/concepts/construction", + "link": true + }, "node_modules/jitar-contact-list": { "resolved": "examples/apps/contact-list", "link": true @@ -12977,9 +12988,9 @@ } }, "node_modules/vite": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", - "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.1.tgz", + "integrity": "sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA==", "dependencies": { "esbuild": "^0.18.10", "postcss": "^8.4.27", diff --git a/package.json b/package.json index b8a2853a..0fb8bffd 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "packages/server-nodejs", "tools/eslint-plugin", "examples/concepts/access-protection", + "examples/concepts/construction", "examples/concepts/cors", "examples/concepts/data-transportation", "examples/concepts/error-handling", diff --git a/packages/create-jitar/templates/lit/segments/default.segment.json b/packages/create-jitar/templates/lit/segments/default.segment.json index 5a00f143..d45b3c1e 100644 --- a/packages/create-jitar/templates/lit/segments/default.segment.json +++ b/packages/create-jitar/templates/lit/segments/default.segment.json @@ -1,3 +1,3 @@ { - "./shared/sayHello": { "sayHello": { "access": "public" } } + "./domain/sayHello": { "sayHello": { "access": "public" } } } \ No newline at end of file diff --git a/packages/create-jitar/templates/lit/src/shared/sayHello.ts b/packages/create-jitar/templates/lit/src/domain/sayHello.ts similarity index 100% rename from packages/create-jitar/templates/lit/src/shared/sayHello.ts rename to packages/create-jitar/templates/lit/src/domain/sayHello.ts diff --git a/packages/create-jitar/templates/lit/src/my-element.ts b/packages/create-jitar/templates/lit/src/my-element.ts index 3e2a81b5..e942d53b 100644 --- a/packages/create-jitar/templates/lit/src/my-element.ts +++ b/packages/create-jitar/templates/lit/src/my-element.ts @@ -2,7 +2,7 @@ import { LitElement, css, html } from 'lit' import { customElement, property } from 'lit/decorators.js' import litLogo from './assets/lit.svg' import jitarLogo from './assets/jitar.svg' -import { sayHello } from './shared/sayHello' +import { sayHello } from './domain/sayHello' @customElement('my-element') export class MyElement extends LitElement diff --git a/packages/create-jitar/templates/lit/vite.config.ts b/packages/create-jitar/templates/lit/vite.config.ts index 3d169948..14084cb1 100644 --- a/packages/create-jitar/templates/lit/vite.config.ts +++ b/packages/create-jitar/templates/lit/vite.config.ts @@ -6,7 +6,7 @@ export default defineConfig(({ mode }) => { return { plugins: [ - jitar('src', 'shared', 'http://localhost:3000') + jitar('src', 'domain', 'http://localhost:3000') ], build: { lib: { diff --git a/packages/create-jitar/templates/react/segments/default.segment.json b/packages/create-jitar/templates/react/segments/default.segment.json index 5a00f143..d45b3c1e 100644 --- a/packages/create-jitar/templates/react/segments/default.segment.json +++ b/packages/create-jitar/templates/react/segments/default.segment.json @@ -1,3 +1,3 @@ { - "./shared/sayHello": { "sayHello": { "access": "public" } } + "./domain/sayHello": { "sayHello": { "access": "public" } } } \ No newline at end of file diff --git a/packages/create-jitar/templates/react/src/App.tsx b/packages/create-jitar/templates/react/src/App.tsx index a20801b6..a23807ce 100644 --- a/packages/create-jitar/templates/react/src/App.tsx +++ b/packages/create-jitar/templates/react/src/App.tsx @@ -4,7 +4,7 @@ import './App.css' import reactLogo from './assets/react.svg' import jitarLogo from './assets/jitar.svg' -import { sayHello } from './shared/sayHello' +import { sayHello } from './domain/sayHello' function App() { diff --git a/packages/create-jitar/templates/react/src/shared/sayHello.ts b/packages/create-jitar/templates/react/src/domain/sayHello.ts similarity index 100% rename from packages/create-jitar/templates/react/src/shared/sayHello.ts rename to packages/create-jitar/templates/react/src/domain/sayHello.ts diff --git a/packages/create-jitar/templates/react/vite.config.ts b/packages/create-jitar/templates/react/vite.config.ts index dbfd2455..4f3e74d4 100644 --- a/packages/create-jitar/templates/react/vite.config.ts +++ b/packages/create-jitar/templates/react/vite.config.ts @@ -5,6 +5,6 @@ import jitar from '@jitar/plugin-vite' export default defineConfig({ plugins: [ react(), - jitar('src', 'shared', 'http://localhost:3000') + jitar('src', 'domain', 'http://localhost:3000') ] }) \ No newline at end of file diff --git a/packages/create-jitar/templates/solid/segments/default.segment.json b/packages/create-jitar/templates/solid/segments/default.segment.json index 5a00f143..d45b3c1e 100644 --- a/packages/create-jitar/templates/solid/segments/default.segment.json +++ b/packages/create-jitar/templates/solid/segments/default.segment.json @@ -1,3 +1,3 @@ { - "./shared/sayHello": { "sayHello": { "access": "public" } } + "./domain/sayHello": { "sayHello": { "access": "public" } } } \ No newline at end of file diff --git a/packages/create-jitar/templates/solid/src/App.tsx b/packages/create-jitar/templates/solid/src/App.tsx index b2301be3..c98551d3 100644 --- a/packages/create-jitar/templates/solid/src/App.tsx +++ b/packages/create-jitar/templates/solid/src/App.tsx @@ -2,7 +2,7 @@ import solidjslogo from './logo.svg' import jitarLogo from './assets/jitar.svg' import type { Component } from 'solid-js' import { createResource } from 'solid-js' -import { sayHello } from './shared/sayHello' +import { sayHello } from './domain/sayHello' const sayHelloResource = async () => { return await sayHello('Vite + Solid + Jitar') diff --git a/packages/create-jitar/templates/solid/src/shared/sayHello.ts b/packages/create-jitar/templates/solid/src/domain/sayHello.ts similarity index 100% rename from packages/create-jitar/templates/solid/src/shared/sayHello.ts rename to packages/create-jitar/templates/solid/src/domain/sayHello.ts diff --git a/packages/create-jitar/templates/solid/vite.config.ts b/packages/create-jitar/templates/solid/vite.config.ts index fc5ac07a..91d57d21 100644 --- a/packages/create-jitar/templates/solid/vite.config.ts +++ b/packages/create-jitar/templates/solid/vite.config.ts @@ -5,7 +5,7 @@ import jitar from '@jitar/plugin-vite' export default defineConfig({ plugins: [ solidPlugin(), - jitar('src', 'shared', 'http://localhost:3000') + jitar('src', 'domain', 'http://localhost:3000') ], build: { target: 'esnext', diff --git a/packages/create-jitar/templates/svelte/segments/default.segment.json b/packages/create-jitar/templates/svelte/segments/default.segment.json index 5a00f143..d45b3c1e 100644 --- a/packages/create-jitar/templates/svelte/segments/default.segment.json +++ b/packages/create-jitar/templates/svelte/segments/default.segment.json @@ -1,3 +1,3 @@ { - "./shared/sayHello": { "sayHello": { "access": "public" } } + "./domain/sayHello": { "sayHello": { "access": "public" } } } \ No newline at end of file diff --git a/packages/create-jitar/templates/svelte/src/App.svelte b/packages/create-jitar/templates/svelte/src/App.svelte index c89f3a0c..204c9575 100644 --- a/packages/create-jitar/templates/svelte/src/App.svelte +++ b/packages/create-jitar/templates/svelte/src/App.svelte @@ -2,7 +2,7 @@ import svelteLogo from './assets/svelte.svg' import jitarLogo from './assets/jitar.svg' - import { sayHello } from './shared/sayHello' + import { sayHello } from './domain/sayHello' async function runComponent() { diff --git a/packages/create-jitar/templates/svelte/src/shared/sayHello.ts b/packages/create-jitar/templates/svelte/src/domain/sayHello.ts similarity index 100% rename from packages/create-jitar/templates/svelte/src/shared/sayHello.ts rename to packages/create-jitar/templates/svelte/src/domain/sayHello.ts diff --git a/packages/create-jitar/templates/svelte/vite.config.ts b/packages/create-jitar/templates/svelte/vite.config.ts index a894d1ea..6611e6a0 100644 --- a/packages/create-jitar/templates/svelte/vite.config.ts +++ b/packages/create-jitar/templates/svelte/vite.config.ts @@ -5,6 +5,6 @@ import jitar from '@jitar/plugin-vite' export default defineConfig({ plugins: [ svelte(), - jitar('src', 'shared', 'http://localhost:3000') + jitar('src', 'domain', 'http://localhost:3000') ], }); diff --git a/packages/create-jitar/templates/vue/segments/default.segment.json b/packages/create-jitar/templates/vue/segments/default.segment.json index 5a00f143..d45b3c1e 100644 --- a/packages/create-jitar/templates/vue/segments/default.segment.json +++ b/packages/create-jitar/templates/vue/segments/default.segment.json @@ -1,3 +1,3 @@ { - "./shared/sayHello": { "sayHello": { "access": "public" } } + "./domain/sayHello": { "sayHello": { "access": "public" } } } \ No newline at end of file diff --git a/packages/create-jitar/templates/vue/src/App.vue b/packages/create-jitar/templates/vue/src/App.vue index 57604b4d..86eff425 100644 --- a/packages/create-jitar/templates/vue/src/App.vue +++ b/packages/create-jitar/templates/vue/src/App.vue @@ -1,7 +1,7 @@