remult 0.16.2-exp.0 → 0.16.3-exp.0

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.
package/README.md CHANGED
@@ -3,51 +3,71 @@
3
3
  <img src="https://github.com/remult/remult/raw/master/docs/.vuepress/public/logo.png" width="140" height="140">
4
4
  </a>
5
5
  <h1>Remult</h1>
6
- <a href="https://circleci.com/gh/remult/remult/tree/master">
6
+ <a href="https://circleci.com/gh/remult/remult/tree/master" rel="nofollow">
7
7
  <img alt="CircleCI" src="https://circleci.com/gh/remult/remult/tree/master.svg?style=shield">
8
8
  </a>
9
- <a href="https://codecov.io/gh/remult/remult">
9
+ <a href="https://codecov.io/gh/remult/remult" rel="nofollow">
10
10
  <img src="https://codecov.io/gh/remult/remult/branch/master/graph/badge.svg?token=LYWQRUN3D1"/>
11
11
  </a>
12
- <a href="https://raw.githubusercontent.com/remult/remult/master/LICENSE">
12
+ <a href="https://raw.githubusercontent.com/remult/remult/master/LICENSE" rel="nofollow">
13
13
  <img alt="GitHub license" src="https://img.shields.io/badge/license-MIT-blue.svg">
14
14
  </a>
15
- <a href="https://www.npmjs.com/package/remult">
15
+ <a href="https://www.npmjs.com/package/remult" rel="nofollow">
16
16
  <img alt="npm version" src="https://badge.fury.io/js/remult.svg">
17
17
  </a>
18
- <a href="https://www.npmjs.com/package/remult">
18
+ <a href="https://www.npmjs.com/package/remult" rel="nofollow">
19
19
  <img alt="npm downloads" src="https://img.shields.io/npm/dm/remult">
20
20
  </a>
21
- <a href="https://discord.gg/GXHk7ZfuG5">
22
- <img alt="chat on discord" src="https://img.shields.io/badge/chat-on%20discord-blueviolet"/>
21
+ <a href="https://discord.gg/GXHk7ZfuG5" rel="nofollow">
22
+ <img alt="Join Discord" src="https://badgen.net/discord/online-members/GXHk7ZfuG5?icon=discord&label=Discord"/>
23
23
  </a>
24
- <a href="https://twitter.com/remultjs">
25
- <img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/remultjs?style=social">
24
+ <a href="https://twitter.com/intent/tweet?text=Remult%20-%20open-source%20CRUD%20framework%20for%20full-stack%20TypeScript%0A%0A%40remultjs%0A&url=https%3A%2F%2Fgithub.com%2Fremult%2Fremult">
25
+ <img alt="Twitter URL" src="https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Ftwitter.com%2Fintent%2Ftweet%3Ftext%3DRemult%2520-%2520open-source%2520CRUD%2520framework%2520for%2520full-stack%2520TypeScript%250A%250A%2540remultjs%250A%26url%3Dhttps%253A%252F%252Fgithub.com%252Fremult%252Fremult">
26
+ </a>
27
+ <a href="https://twitter.com/remultjs" rel="nofollow">
28
+ <img alt="Twitter Follow" src="https://img.shields.io/twitter/url.svg?label=Follow%20%40remultjs&style=social&url=https%3A%2F%2Ftwitter.com%2FRemultJs">
26
29
  </a>
27
30
  </div>
28
31
  <hr/>
29
32
 
30
- [Getting Started](#getting-started) | [Documentation](#documentation) | [Example Apps](#example-apps)
33
+ [Getting Started](#getting-started) | [Documentation](#documentation) |
34
+ [Example Apps](#example-apps)
31
35
 
32
36
  ## What is Remult?
33
37
 
34
- **Remult** is a full-stack CRUD framework that uses your **TypeScript entities as a single source of truth for your API, frontend type-safe API client and backend ORM**.
38
+ **Remult** is a full-stack CRUD framework that uses your **TypeScript entities
39
+ as a single source of truth for your API, frontend type-safe API client and
40
+ backend ORM**.
35
41
 
36
42
  ### Status
37
43
 
38
- Remult is **production-ready** and, in fact, used in production apps since 2018. However, we’re keeping the major version at zero so we can use community feedback to finalize the v1 API.
44
+ Remult is **production-ready** and, in fact, used in production apps since 2018.
45
+ However, we’re keeping the major version at zero so we can use community
46
+ feedback to finalize the v1 API.
39
47
 
40
48
  ## Motivation
41
49
 
42
- Full-stack web development is (still) too complicated. **Simple CRUD, a common requirement of any business application, should be simple to build, maintain, and extend** when the need arises.
50
+ Full-stack web development is (still) too complicated. **Simple CRUD, a common
51
+ requirement of any business application, should be simple to build, maintain,
52
+ and extend** when the need arises.
43
53
 
44
- Remult abstracts away repetitive, boilerplate, error-prone, and poorly designed code on the one hand, and enables total flexibility and control on the other. **Remult helps building fullstack apps using only TypeScript code you can easily follow and safely refactor**, and fits nicely into any existing or new project by being minimalistic and completely unopinionated regarding the developer’s choice of other frameworks and tools.
54
+ Remult abstracts away repetitive, boilerplate, error-prone, and poorly designed
55
+ code on the one hand, and enables total flexibility and control on the other.
56
+ **Remult helps building fullstack apps using only TypeScript code you can easily
57
+ follow and safely refactor**, and fits nicely into any existing or new project
58
+ by being minimalistic and completely unopinionated regarding the developer’s
59
+ choice of other frameworks and tools.
45
60
 
46
- Other frameworks tend to fall into either too much abstraction (no-code, low-code, BaaS) or partial abstraction (MVC frameworks, GraphQL, ORMs, API generators, code generators), and tend to be opinionated regarding the development tool-chain, deployment environment, configuration/conventions or DSL. Remult attempts to strike a better balance.
61
+ Other frameworks tend to fall into either too much abstraction (no-code,
62
+ low-code, BaaS) or partial abstraction (MVC frameworks, GraphQL, ORMs, API
63
+ generators, code generators), and tend to be opinionated regarding the
64
+ development tool-chain, deployment environment, configuration/conventions or
65
+ DSL. Remult attempts to strike a better balance.
47
66
 
48
67
  ## Installation
49
68
 
50
- The *remult* package is one and the same for both the frontend bundle and the backend. Install it once for a monolith project or per-repo in a monorepo.
69
+ The _remult_ package is one and the same for both the frontend bundle and the
70
+ backend. Install it once for a monolith project or per-repo in a monorepo.
51
71
 
52
72
  ```sh
53
73
  npm i remult
@@ -56,17 +76,18 @@ npm i remult
56
76
  ## Usage
57
77
 
58
78
  ### Define model classes
79
+
59
80
  ```ts
60
81
  // shared/product.ts
61
82
 
62
- import { Entity, Fields } from 'remult';
83
+ import { Entity, Fields } from "remult";
63
84
 
64
- @Entity('products', {
65
- allowApiCrud: true
85
+ @Entity("products", {
86
+ allowApiCrud: true,
66
87
  })
67
88
  export class Product {
68
89
  @Fields.string()
69
- name = '';
90
+ name = "";
70
91
 
71
92
  @Fields.number()
72
93
  unitPrice = 0;
@@ -74,18 +95,19 @@ export class Product {
74
95
  ```
75
96
 
76
97
  ### Setup API backend using an Express middleware
98
+
77
99
  ```ts
78
100
  // backend/index.ts
79
101
 
80
- import express from 'express';
81
- import { remultExpress } from 'remult/remult-express';
82
- import { Product } from '../shared/product';
102
+ import express from "express";
103
+ import { remultExpress } from "remult/remult-express";
104
+ import { Product } from "../shared/product";
83
105
 
84
106
  const port = 3001;
85
107
  const app = express();
86
108
 
87
109
  app.use(remultExpress({
88
- entities: [Product]
110
+ entities: [Product],
89
111
  }));
90
112
 
91
113
  app.listen(port, () => {
@@ -94,6 +116,7 @@ app.listen(port, () => {
94
116
  ```
95
117
 
96
118
  ### :rocket: API Ready
119
+
97
120
  ```sh
98
121
  > curl http://localhost:3001/api/products
99
122
 
@@ -101,24 +124,26 @@ app.listen(port, () => {
101
124
  ```
102
125
 
103
126
  ### Find and manipulate data in type-safe frontend code
127
+
104
128
  ```ts
105
129
  // frontend/code.ts
106
130
 
107
- import { Remult } from 'remult';
108
- import { Product } from '../shared/product';
131
+ import { Remult } from "remult";
132
+ import { Product } from "../shared/product";
109
133
 
110
134
  const remult = new Remult();
111
135
 
112
136
  async function increasePriceOfTofu(priceIncrease: number) {
113
137
  const productsRepo = remult.repo(Product);
114
138
 
115
- const product = await productsRepo.findFirst({ name: 'Tofu' }); // filter is passed through API request all the way to the db
139
+ const product = await productsRepo.findFirst({ name: "Tofu" }); // filter is passed through API request all the way to the db
116
140
  product.unitPrice += priceIncrease;
117
141
  productsRepo.save(product); // mutation request updates the db with no boilerplate code
118
142
  }
119
143
  ```
120
144
 
121
- ### ...*exactly* the same way as in backend code
145
+ ### ..._exactly_ the same way as in backend code
146
+
122
147
  ```ts
123
148
  @BackendMethod({ allowed: Allow.authenticated })
124
149
  static async increasePriceOfTofu(priceIncrease: number, remult?: Remult) {
@@ -133,48 +158,51 @@ static async increasePriceOfTofu(priceIncrease: number, remult?: Remult) {
133
158
  ### :ballot_box_with_check: Data validation and constraints - defined once
134
159
 
135
160
  ```ts
136
- import { Entity, Fields, Validators } from 'remult';
161
+ import { Entity, Fields, Validators } from "remult";
137
162
 
138
- @Entity('products', {
139
- allowApiCrud: true
163
+ @Entity("products", {
164
+ allowApiCrud: true,
140
165
  })
141
166
  export class Product {
142
- @Fields.string({
143
- validate: Validators.required
144
- })
145
- name = '';
146
-
147
- @Fields.string<Product>({
148
- validate: product => {
149
- if (product.description.trim().length < 50)
150
- throw "too short";
151
- }
152
- })
153
- description = '';
154
-
155
- @Fields.number({
156
- validate: (_, field) => {
157
- if (field.value < 0)
158
- field.error = "must not be less than 0"; // or: throw "must not be less than 0";
159
- }
160
- })
161
- unitPrice = 0;
167
+ @Fields.string({
168
+ validate: Validators.required,
169
+ })
170
+ name = "";
171
+
172
+ @Fields.string<Product>({
173
+ validate: (product) => {
174
+ if (product.description.trim().length < 50) {
175
+ throw "too short";
176
+ }
177
+ },
178
+ })
179
+ description = "";
180
+
181
+ @Fields.number({
182
+ validate: (_, field) => {
183
+ if (field.value < 0) {
184
+ field.error = "must not be less than 0"; // or: throw "must not be less than 0";
185
+ }
186
+ },
187
+ })
188
+ unitPrice = 0;
162
189
  }
163
190
  ```
164
191
 
165
192
  ### Enforced in frontend:
193
+
166
194
  ```ts
167
195
  const product = productsRepo.create();
168
196
 
169
197
  try {
170
198
  await productsRepo.save(product);
171
- }
172
- catch (e: any) {
199
+ } catch (e: any) {
173
200
  console.error(e.message); // Browser console will display - "Name: required"
174
201
  }
175
202
  ```
176
203
 
177
204
  ### Enforced in backend:
205
+
178
206
  ```sh
179
207
  > curl http://localhost:3001/api/products -H "Content-Type: application/json" -d "{""unitPrice"":-1}"
180
208
 
@@ -182,58 +210,78 @@ catch (e: any) {
182
210
  ```
183
211
 
184
212
  ### :lock: Secure the API with fine-grained authorization
213
+
185
214
  ```ts
186
- @Entity<Article>('Articles', {
187
- allowApiRead: true,
188
- allowApiInsert: remult => remult.authenticated(),
189
- allowApiUpdate: (remult, article) => article.author.id == remult.user.id
215
+ @Entity<Article>("Articles", {
216
+ allowApiRead: true,
217
+ allowApiInsert: (remult) => remult.authenticated(),
218
+ allowApiUpdate: (remult, article) => article.author.id == remult.user.id,
190
219
  })
191
220
  export class Article {
192
- @Fields.string({ allowApiUpdate: false })
193
- slug = '';
194
-
195
- @Field(() => Profile, { allowApiUpdate: false })
196
- author!: Profile;
197
-
198
- @Fields.string()
199
- content = '';
221
+ @Fields.string({ allowApiUpdate: false })
222
+ slug = "";
223
+
224
+ @Field(() => Profile, { allowApiUpdate: false })
225
+ author!: Profile;
226
+
227
+ @Fields.string()
228
+ content = "";
200
229
  }
201
230
  ```
202
231
 
203
232
  ## What about complex CRUD?
204
233
 
205
- While simple CRUD shouldn’t require any backend coding, using Remult means having the ability to handle any complex scenario by controlling the backend in numerous ways:
206
-
207
- * Backend computed (read-only) fields - from simple [expressions](https://remult.dev/docs/ref_field.html#serverexpression) to complex data lookups or even direct db access (SQL)
208
- * Custom side-effects with [entity lifecycle hooks](https://remult.dev/docs/ref_entity.html#saving) (before/after saving/deleting)
209
- * Backend only updatable fields (e.g. “last updated at”)
210
- * Many-to-one [relations](https://remult.dev/docs/entity-relations.html) with [lazy/eager loading](https://remult.dev/docs/lazy-loading-of-related-entities.html)
211
- * Roll-your-own type-safe endpoints with [Backend Methods](https://remult.dev/docs/backendMethods.html)
212
- * Roll-your-own low-level endpoints (Express, Fastify, koa, others…)
234
+ While simple CRUD shouldn’t require any backend coding, using Remult means
235
+ having the ability to handle any complex scenario by controlling the backend in
236
+ numerous ways:
237
+
238
+ - Backend computed (read-only) fields - from simple
239
+ [expressions](https://remult.dev/docs/ref_field.html#serverexpression) to
240
+ complex data lookups or even direct db access (SQL)
241
+ - Custom side-effects with
242
+ [entity lifecycle hooks](https://remult.dev/docs/ref_entity.html#saving)
243
+ (before/after saving/deleting)
244
+ - Backend only updatable fields (e.g. “last updated at”)
245
+ - Many-to-one [relations](https://remult.dev/docs/entity-relations.html) with
246
+ [lazy/eager loading](https://remult.dev/docs/lazy-loading-of-related-entities.html)
247
+ - Roll-your-own type-safe endpoints with
248
+ [Backend Methods](https://remult.dev/docs/backendMethods.html)
249
+ - Roll-your-own low-level endpoints (Express, Fastify, koa, others…)
213
250
 
214
251
  ## Getting started
215
- The best way to learn Remult is by following a tutorial of a simple Todo web app with a Node.js Express backend.
216
252
 
217
- * [Tutorial with React](https://remult.dev/tutorials/react/)
218
- * [Tutorial with Angular](https://remult.dev/tutorials/angular/)
219
- * [Tutorial with Vue](https://remult.dev/tutorials/vue/)
220
- * [Tutorial with Next.js](https://remult.dev/tutorials/react-next/)
253
+ The best way to learn Remult is by following a tutorial of a simple Todo web app
254
+ with a Node.js Express backend.
255
+
256
+ - [Tutorial with React](https://remult.dev/tutorials/react/)
257
+ - [Tutorial with Angular](https://remult.dev/tutorials/angular/)
258
+ - [Tutorial with Vue](https://remult.dev/tutorials/vue/)
259
+ - [Tutorial with Next.js](https://remult.dev/tutorials/react-next/)
221
260
 
222
261
  ## Documentation
223
- The [documentation](https://remult.dev/docs) covers the main features of Remult. However, it is still a work-in-progress.
262
+
263
+ The [documentation](https://remult.dev/docs) covers the main features of Remult.
264
+ However, it is still a work-in-progress.
224
265
 
225
266
  ## Example Apps
226
267
 
227
- * Fullstack TodoMVC example with React and Express. ([Source code](https://github.com/remult/TodoMVC-eample) | [CodeSandbox](https://codesandbox.io/s/github/remult/TodoMVC-example?file=/src/frontend/App.tsx))
268
+ - Fullstack TodoMVC example with React and Express.
269
+ ([Source code](https://github.com/remult/TodoMVC-eample) |
270
+ [CodeSandbox](https://codesandbox.io/s/github/remult/TodoMVC-example?file=/src/frontend/App.tsx))
228
271
 
229
- * [CRM demo](https://github.com/remult/crm-demo) with a React + [MUI](https://mui.com) front-end and Postgres database.
272
+ - [CRM demo](https://github.com/remult/crm-demo) with a React +
273
+ [MUI](https://mui.com) front-end and Postgres database.
230
274
 
231
275
  ## Contributing
276
+
232
277
  Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md).
233
278
 
234
- * :speech_balloon: Any feedback or suggestions? Start a [discussion](https://github.com/remult/remult/discussions).
235
- * :muscle: Want to help out? Look for "help wanted" labeled [issues](https://github.com/remult/remult/issues).
236
- * :star: Give this repo a star.
279
+ - :speech_balloon: Any feedback or suggestions? Start a
280
+ [discussion](https://github.com/remult/remult/discussions).
281
+ - :muscle: Want to help out? Look for "help wanted" labeled
282
+ [issues](https://github.com/remult/remult/issues).
283
+ - :star: Give this repo a star.
237
284
 
238
285
  ## License
286
+
239
287
  Remult is [MIT Licensed](LICENSE).
@@ -309,8 +309,8 @@ export declare class EntityBase {
309
309
  get $(): FieldsRef<this>;
310
310
  }
311
311
  export declare class ControllerBase {
312
- protected remult: Remult;
313
- constructor(remult: Remult);
312
+ protected remult?: Remult;
313
+ constructor(remult?: Remult);
314
314
  assign(values: Partial<Omit<this, keyof EntityBase>>): this;
315
315
  get $(): FieldsRef<this>;
316
316
  get _(): ControllerRef<this>;
@@ -1673,8 +1673,8 @@ export class ControllerBase {
1673
1673
  assign(this, values);
1674
1674
  return this;
1675
1675
  }
1676
- get $() { return getFields(this); }
1677
- get _() { return getControllerRef(this); }
1676
+ get $() { return getFields(this, this.remult); }
1677
+ get _() { return getControllerRef(this, this.remult); }
1678
1678
  }
1679
1679
  class QueryResultImpl {
1680
1680
  constructor(options, repo) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remult",
3
- "version": "0.16.2-exp.0",
3
+ "version": "0.16.3-exp.0",
4
4
  "description": "A CRUD framework for full-stack TypeScript",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -23,51 +23,99 @@
23
23
  "exports": {
24
24
  ".": {
25
25
  "require": "./index.js",
26
- "import": "./index.js"
26
+ "node": {
27
+ "require": "./index.js",
28
+ "import": "./index.js"
29
+ },
30
+ "import": "./esm/index.js"
27
31
  },
28
32
  "./remult-express": {
29
33
  "require": "./remult-express.js",
30
- "import": "./remult-express.js"
34
+ "node": {
35
+ "require": "./remult-express.js",
36
+ "import": "./remult-express.js"
37
+ },
38
+ "import": "./esm/remult-express.js"
31
39
  },
32
40
  "./server": {
33
41
  "require": "./server/index.js",
34
- "import": "./server/index.js"
42
+ "node": {
43
+ "require": "./server/index.js",
44
+ "import": "./server/index.js"
45
+ },
46
+ "import": "./esm/server/index.js"
35
47
  },
36
48
  "./remult-fastify": {
37
49
  "require": "./remult-fastify.js",
38
- "import": "./remult-fastify.js"
50
+ "node": {
51
+ "require": "./remult-fastify.js",
52
+ "import": "./remult-fastify.js"
53
+ },
54
+ "import": "./esm/remult-fastify.js"
39
55
  },
40
56
  "./remult-fresh": {
41
57
  "require": "./remult-fresh.js",
42
- "import": "./remult-fresh.js"
58
+ "node": {
59
+ "require": "./remult-fresh.js",
60
+ "import": "./remult-fresh.js"
61
+ },
62
+ "import": "./esm/remult-fresh.js"
43
63
  },
44
64
  "./postgres": {
45
65
  "require": "./postgres/index.js",
46
- "import": "./postgres/index.js"
66
+ "node": {
67
+ "require": "./postgres/index.js",
68
+ "import": "./postgres/index.js"
69
+ },
70
+ "import": "./esm/postgres/index.js"
47
71
  },
48
72
  "./postgres/schema-builder": {
49
73
  "require": "./postgres/schema-builder.js",
50
- "import": "./postgres/schema-builder.js"
74
+ "node": {
75
+ "require": "./postgres/schema-builder.js",
76
+ "import": "./postgres/schema-builder.js"
77
+ },
78
+ "import": "./esm/postgres/schema-builder.js"
51
79
  },
52
80
  "./remult-knex": {
53
81
  "require": "./remult-knex/index.js",
54
- "import": "./remult-knex/index.js"
82
+ "node": {
83
+ "require": "./remult-knex/index.js",
84
+ "import": "./remult-knex/index.js"
85
+ },
86
+ "import": "./esm/remult-knex/index.js"
55
87
  },
56
88
  "./remult-mongo": {
57
89
  "require": "./remult-mongo.js",
58
- "import": "./remult-mongo.js"
90
+ "node": {
91
+ "require": "./remult-mongo.js",
92
+ "import": "./remult-mongo.js"
93
+ },
94
+ "import": "./esm/remult-mongo.js"
59
95
  },
60
96
  "./graphql": {
61
97
  "require": "./graphql.js",
62
- "import": "./graphql.js"
98
+ "node": {
99
+ "require": "./graphql.js",
100
+ "import": "./graphql.js"
101
+ },
102
+ "import": "./esm/graphql.js"
63
103
  },
64
104
  "./src/remult3": {
65
105
  "require": "./src/remult3/index.js",
66
- "import": "./src/remult3/index.js"
106
+ "node": {
107
+ "require": "./src/remult3/index.js",
108
+ "import": "./src/remult3/index.js"
109
+ },
110
+ "import": "./esm/src/remult3/index.js"
67
111
  },
68
112
  "./src/server-action": {
69
113
  "require": "./src/server-action.js",
70
- "import": "./src/server-action.js"
114
+ "node": {
115
+ "require": "./src/server-action.js",
116
+ "import": "./src/server-action.js"
117
+ },
118
+ "import": "./esm/src/server-action.js"
71
119
  }
72
120
  }
73
121
  }
@@ -309,8 +309,8 @@ export declare class EntityBase {
309
309
  get $(): FieldsRef<this>;
310
310
  }
311
311
  export declare class ControllerBase {
312
- protected remult: Remult;
313
- constructor(remult: Remult);
312
+ protected remult?: Remult;
313
+ constructor(remult?: Remult);
314
314
  assign(values: Partial<Omit<this, keyof EntityBase>>): this;
315
315
  get $(): FieldsRef<this>;
316
316
  get _(): ControllerRef<this>;
@@ -2745,12 +2745,12 @@ var ControllerBase = /** @class */ (function () {
2745
2745
  return this;
2746
2746
  };
2747
2747
  Object.defineProperty(ControllerBase.prototype, "$", {
2748
- get: function () { return getFields(this); },
2748
+ get: function () { return getFields(this, this.remult); },
2749
2749
  enumerable: false,
2750
2750
  configurable: true
2751
2751
  });
2752
2752
  Object.defineProperty(ControllerBase.prototype, "_", {
2753
- get: function () { return getControllerRef(this); },
2753
+ get: function () { return getControllerRef(this, this.remult); },
2754
2754
  enumerable: false,
2755
2755
  configurable: true
2756
2756
  });