remult 0.26.0 → 0.26.2

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
@@ -1,11 +1,11 @@
1
1
  <div align="center">
2
2
  <h1>Remult</h1>
3
3
  <p>Full-stack CRUD, simplified, with SSOT TypeScript entities</p>
4
- <a href="https://circleci.com/gh/remult/remult/tree/master" rel="nofollow">
5
- <img alt="CircleCI" src="https://circleci.com/gh/remult/remult/tree/master.svg?style=shield"></a>
4
+ <a href="https://circleci.com/gh/remult/remult/tree/main" rel="nofollow">
5
+ <img alt="CircleCI" src="https://circleci.com/gh/remult/remult/tree/main.svg?style=shield"></a>
6
6
  <a href="https://codecov.io/gh/remult/remult" rel="nofollow">
7
- <img src="https://codecov.io/gh/remult/remult/branch/master/graph/badge.svg?token=LYWQRUN3D1"/></a>
8
- <a href="https://raw.githubusercontent.com/remult/remult/master/LICENSE" rel="nofollow">
7
+ <img src="https://codecov.io/gh/remult/remult/branch/main/graph/badge.svg?token=LYWQRUN3D1"/></a>
8
+ <a href="https://raw.githubusercontent.com/remult/remult/main/LICENSE" rel="nofollow">
9
9
  <img alt="GitHub license" src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
10
10
  <a href="https://www.npmjs.com/package/remult" rel="nofollow">
11
11
  <img alt="npm version" src="https://badge.fury.io/js/remult.svg"></a>
@@ -39,7 +39,7 @@ Remult uses **TypeScript entities** as a single source of truth for: ✅ CRUD +
39
39
 
40
40
  **Remult supports all major databases**, including: PostgreSQL, MySQL, SQLite, MongoDB, MSSQL and Oracle.
41
41
 
42
- **Remult is frontend and backend framework agnostic** and comes with adapters for Express, Fastify, Next.js, Nuxt, SvelteKit, Nest, Koa, Hapi and Hono.
42
+ **Remult is frontend and backend framework agnostic** and comes with adapters for Express, Fastify, Next.js, Nuxt, SvelteKit, SolidStart, Nest, Koa, Hapi and Hono.
43
43
 
44
44
  Remult promotes a **consistent query syntax for both frontend and Backend code**:
45
45
 
@@ -93,8 +93,8 @@ _Example:_
93
93
  // backend/index.ts
94
94
 
95
95
  import express from 'express'
96
- import { remultExpress } from 'remult/remult-express' // adapters for: Fastify, Next.js, Nuxt, SvelteKit, Nest, more...
97
- import { createPostgresDataProvider } from "remult/postgres" // supported: PostgreSQL, MySQL, SQLite, MongoDB, MSSQL and Oracle
96
+ import { remultExpress } from 'remult/remult-express' // adapters for: Fastify,Next.js, Nuxt, SvelteKit, SolidStart, Nest, more...
97
+ import { createPostgresDataProvider } from 'remult/postgres' // supported: PostgreSQL, MySQL, SQLite, MongoDB, MSSQL and Oracle
98
98
  import { Product } from '../shared/product'
99
99
 
100
100
  const app = express()
@@ -184,7 +184,7 @@ try {
184
184
  await repo(Product).insert({ name: '', unitPrice: -1 })
185
185
  } catch (e: any) {
186
186
  console.error(e)
187
- /* Detailed error object ->
187
+ /* Detailed error object ->
188
188
  {
189
189
  "modelState": {
190
190
  "name": "Should not be empty",
@@ -306,12 +306,13 @@ with a Node.js Express backend.
306
306
  - [Tutorial with Vue](https://remult.dev/tutorials/vue/)
307
307
  - [Tutorial with Next.js](https://remult.dev/tutorials/react-next/)
308
308
  - [Tutorial with Sveltekit](https://remult.dev/tutorials/sveltekit/)
309
+ - [Tutorial with SolidStart](https://remult.dev/tutorials/react-next/)
309
310
 
310
311
  ## Demo
311
312
 
312
313
  <a href="https://www.youtube.com/watch?v=rEoScmSVNUE" target="_blank">
313
314
  <p align="center">
314
- <img src="https://github.com/remult/remult/raw/master/static/images/video-thumbnail.jpg" alt="Video thumbnail" />
315
+ <img src="https://github.com/remult/remult/raw/main/static/images/video-thumbnail.jpg" alt="Video thumbnail" />
315
316
  </p>
316
317
  </a>
317
318
 
@@ -0,0 +1,97 @@
1
+ import { getRequestEvent } from 'solid-js/web';
2
+ import { createRemultServer } from './server/index.js';
3
+ export function remultSolidStart(options) {
4
+ let result = createRemultServer(options, {
5
+ buildGenericRequestInfo: (event) => ({
6
+ url: event.request.url,
7
+ method: event.request.method,
8
+ on: (e, do1) => {
9
+ if (e === 'close') {
10
+ event.locals['_tempOnClose'] = do1;
11
+ }
12
+ },
13
+ }),
14
+ getRequestBody: (event) => event.request.json(),
15
+ });
16
+ const serverHandler = async () => {
17
+ const event = await getRequestEvent();
18
+ let sseResponse = undefined;
19
+ if (event)
20
+ event.locals['_tempOnClose'] = () => { };
21
+ const response = {
22
+ end: () => { },
23
+ json: () => { },
24
+ send: () => { },
25
+ status: () => {
26
+ return response;
27
+ },
28
+ write: () => { },
29
+ writeHead: (status, headers) => {
30
+ if (status === 200 && headers) {
31
+ const contentType = headers['Content-Type'];
32
+ if (contentType === 'text/event-stream') {
33
+ const messages = [];
34
+ response.write = (x) => messages.push(x);
35
+ const stream = new ReadableStream({
36
+ start: (controller) => {
37
+ for (const message of messages) {
38
+ controller.enqueue(message);
39
+ }
40
+ response.write = (data) => {
41
+ controller.enqueue(data);
42
+ };
43
+ },
44
+ cancel: () => {
45
+ response.write = () => { };
46
+ event?.locals?.['_tempOnClose']?.();
47
+ },
48
+ });
49
+ sseResponse = new Response(stream, { headers });
50
+ }
51
+ }
52
+ },
53
+ };
54
+ const responseFromRemultHandler = await result.handle(event, response);
55
+ if (sseResponse !== undefined) {
56
+ return sseResponse;
57
+ }
58
+ if (responseFromRemultHandler) {
59
+ if (responseFromRemultHandler.html)
60
+ return new Response(responseFromRemultHandler.html, {
61
+ status: responseFromRemultHandler.statusCode,
62
+ headers: {
63
+ 'Content-Type': 'text/html',
64
+ },
65
+ });
66
+ const res = new Response(JSON.stringify(responseFromRemultHandler.data), {
67
+ status: responseFromRemultHandler.statusCode,
68
+ });
69
+ return res;
70
+ }
71
+ return new Response('Not Found', {
72
+ status: 404,
73
+ });
74
+ };
75
+ const handler = {}; //async ({ event, resolve }) => {
76
+ // if (event.url.pathname.startsWith(options!.rootPath!)) {
77
+ // const result = await serverHandler(event)
78
+ // if (result != null && result?.status != 404) return result
79
+ // }
80
+ // return new Promise<Response>((res) => {
81
+ // result.withRemult(event, undefined!, async () => {
82
+ // res(await resolve(event))
83
+ // })
84
+ // })
85
+ // }
86
+ return Object.assign(handler, {
87
+ getRemult: (req) => result.getRemult(req),
88
+ openApiDoc: (options) => result.openApiDoc(options),
89
+ async withRemult(what) {
90
+ return result.withRemultAsync(await getRequestEvent(), what);
91
+ },
92
+ GET: serverHandler,
93
+ PUT: serverHandler,
94
+ POST: serverHandler,
95
+ DELETE: serverHandler,
96
+ });
97
+ }
@@ -764,7 +764,7 @@ class ExpressResponseBridgeToDataApiResponse {
764
764
  this.setStatus(204).end();
765
765
  }
766
766
  notFound() {
767
- this.error({ message: 'Forbidden' }, undefined, 404);
767
+ this.error({ message: 'NotFound' }, undefined, 404);
768
768
  }
769
769
  async error(exception, entity, httpStatusCode) {
770
770
  let data = serializeError(exception);
@@ -1,5 +1,5 @@
1
1
  import { AsyncLocalStorage } from 'async_hooks';
2
- import { RemultAsyncLocalStorage } from '../src/context.js';
2
+ import { RemultAsyncLocalStorage, } from '../src/context.js';
3
3
  import { remultStatic } from '../src/remult-static.js';
4
4
  let init = false;
5
5
  export function initAsyncHooks() {
@@ -11,7 +11,7 @@ export function initAsyncHooks() {
11
11
  test.run(1, async () => {
12
12
  await Promise.resolve();
13
13
  if (test.getStore() === undefined) {
14
- console.log("async_hooks.AsyncLocalStorage not working, using sequential implementation (You're probably running on stackblitz, this will work on a normal nodejs environment");
14
+ console.log("async_hooks.AsyncLocalStorage not working, using stub implementation (You're probably running on stackblitz, this will work on a normal nodejs environment");
15
15
  remultStatic.asyncContext = new RemultAsyncLocalStorage(new SequentialRemultAsyncLocalStorageCore());
16
16
  }
17
17
  });
@@ -37,16 +37,22 @@ export class AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl {
37
37
  return this.asyncLocalStorage.getStore();
38
38
  }
39
39
  }
40
- const ignoreInStack = ["AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore.run", "RemultAsyncLocalStorage.run", "Module.withRemult", "RemultServerImplementation.runWithRemult"];
40
+ const ignoreInStack = [
41
+ 'AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore.run',
42
+ 'RemultAsyncLocalStorage.run',
43
+ 'Module.withRemult',
44
+ 'RemultServerImplementation.runWithRemult',
45
+ ];
41
46
  export class SequentialRemultAsyncLocalStorageCore {
42
47
  wasImplemented;
43
48
  async run(store, callback) {
44
- let log = (msg) => {
45
- };
49
+ let log = (msg) => { };
46
50
  if (false) {
47
51
  let stack = new Error().stack;
48
52
  let y = stack.split('\n');
49
- while (y.length > 0 && (y[0].trim().startsWith("Error") || ignoreInStack.includes(y[0].trim().split(' ')[1]))) {
53
+ while (y.length > 0 &&
54
+ (y[0].trim().startsWith('Error') ||
55
+ ignoreInStack.includes(y[0].trim().split(' ')[1]))) {
50
56
  y.splice(0, 1);
51
57
  }
52
58
  stack = y.join('\n');
@@ -64,7 +70,7 @@ export class SequentialRemultAsyncLocalStorageCore {
64
70
  return result;
65
71
  });
66
72
  this.lastPromise = nextPromise.catch(() => {
67
- log("Error on ");
73
+ log('Error on ');
68
74
  return undefined;
69
75
  });
70
76
  try {
@@ -80,5 +86,17 @@ export class SequentialRemultAsyncLocalStorageCore {
80
86
  lastPromise = Promise.resolve(undefined);
81
87
  currentValue;
82
88
  }
89
+ export class StubRemultAsyncLocalStorageCore {
90
+ wasImplemented;
91
+ async run(store, callback) {
92
+ this.currentValue = store;
93
+ return await callback();
94
+ }
95
+ getStore() {
96
+ return this.currentValue;
97
+ }
98
+ lastPromise = Promise.resolve(undefined);
99
+ currentValue;
100
+ }
83
101
  export class AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore extends AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl {
84
102
  }
@@ -51,7 +51,7 @@ export class Remult {
51
51
  * @param entity - the entity to use
52
52
  * @param dataProvider - an optional alternative data provider to use. Useful for writing to offline storage or an alternative data provider
53
53
  */
54
- repo(entity, dataProvider) {
54
+ repo = (entity, dataProvider) => {
55
55
  if (dataProvider === undefined)
56
56
  dataProvider = this.dataProvider;
57
57
  let dpCache = this.repCache.get(dataProvider);
@@ -63,7 +63,7 @@ export class Remult {
63
63
  verifyFieldRelationInfo(r, this, dataProvider);
64
64
  }
65
65
  return r;
66
- }
66
+ };
67
67
  /** Returns the current user's info */
68
68
  user;
69
69
  /** Checks if a user was authenticated */
@@ -286,5 +286,5 @@ export function withRemult(callback, options) {
286
286
  const remult = new Remult();
287
287
  if (options?.dataProvider)
288
288
  remult.dataProvider = options.dataProvider;
289
- return remultStatic.asyncContext.run(remult, r => callback(r));
289
+ return remultStatic.asyncContext.run(remult, (r) => callback(r));
290
290
  }
@@ -1373,14 +1373,16 @@ export class rowHelperImplementation extends rowHelperBase {
1373
1373
  }
1374
1374
  async delete() {
1375
1375
  this.__clearErrorsAndReportChanged();
1376
- let e = this.buildLifeCycleEvent();
1376
+ let doDelete = true;
1377
+ let e = this.buildLifeCycleEvent(() => (doDelete = false));
1377
1378
  if (!this.repository._dataProvider.isProxy) {
1378
1379
  if (this.info.entityInfo.deleting)
1379
1380
  await this.info.entityInfo.deleting(this.instance, e);
1380
1381
  }
1381
1382
  this.__assertValidity();
1382
1383
  try {
1383
- await this.edp.delete(this.id);
1384
+ if (doDelete)
1385
+ await this.edp.delete(this.id);
1384
1386
  if (!this.repository._dataProvider.isProxy) {
1385
1387
  if (this.info.entityInfo.deleted)
1386
1388
  await this.info.entityInfo.deleted(this.instance, e);
@@ -1,40 +1,49 @@
1
1
  export {};
2
+ //y1 - https://discord.com/channels/975754286384418847/1230386433093533698/1230386433093533698
3
+ //y1 - fix async hooks - was it fixed?
4
+ //p1 - prevent default in delete should not delete - and check proceed from there
5
+ //p1 - release solid start with example project
2
6
  //p1 - add documentation for bun sqlite
3
- //p2 - fix dark background to also work on groups
4
- //y1 - JY suggestion of having generate as part of the ensure schema - not sure
5
- //y1 - currently for string fields we default for 255 in knex (which is their default) why not just use text (varchar max) - and only use exact length when one is provided?
6
- //y1 - consider changing the sql raw filter to return the sql, instead of setting the sql property
7
+ //p1 - fix dark background to also work on groups
8
+ //p1 - describe entity
9
+ //p1 - prepare the createEntity discussion
10
+ //p1 - fix async hooks
11
+ //p1 - write active record article
12
+ //p1 - maybe add decorator comment in the readme as well (I heard you've lost me in decorators) - and improve the describeClass function
13
+ //p1 - return error on connection error
14
+ //p1 - reconsider update many, maybe the second parameter should be in a set option - break existing
15
+ //p1 - consider upsert (where,set)
16
+ //p1 - consider id to also support keyof (id:['company','index'])
17
+ //p1 - consider including the admin in the tutorial - and maybe even in the demo when we setup the api
18
+ //p1 -
19
+ //y2 - JY suggestion of having generate as part of the ensure schema - not sure
20
+ //y2 - currently for string fields we default for 255 in knex (which is their default) why not just use text (varchar max) - and only use exact length when one is provided?
7
21
  //p2 - create foreign key constraints in user code - https://codesandbox.io/p/devbox/fk-validator-tdshcs
8
- //p1 - admin url1 - pocketbase use /_ for the admin //{allowed?:Allowed,url?:string}
9
- //p2 - article auth.js with express - played with it, requires type="module" and a few more configurations - https://github.com/noam-honig/express-auth
10
- //y1 - fix validators cache
22
+ //p1 - admin url1 - pocketbase use /_ for the admin //{allowed?:Allowed,url?:string} also checkout the admin* as it should probably be something else.
23
+ //p1 - article auth.js with express - played with it, requires type="module" and a few more configurations - https://github.com/noam-honig/express-auth
24
+ //y2 - fix validators cache
11
25
  //y2 - should we validate relations
12
- //y1 - dependency of live query tables
26
+ //y1 - dependency of live query tables live query refresh of view on table update
13
27
  //p1 - see why mongo tests do not run anymore
14
- //y1 - consider the bot that updates dependencies
15
- //y1 - consider replacing all errors with error classes that extend the base Error class
28
+ //y2 - consider replacing all errors with error classes that extend the base Error class
16
29
  //y2 - should enforce integer - currently we probably round / truncate it
17
30
  //y1 - talk about filter on objects that are not loaded - {
18
31
  //category: repo(CompoundId).create({ company: 7, index: 3, name: '' }),
19
32
  // }
20
- //y1 - consider id to also support keyof (id:['company','index'])
21
33
  /*y1 - talk about modules in init express with entities/controllers,initRequest,initApi
22
34
  - support get with backend method, with url search params as the first parameter, & url as second parameter
23
35
  - support returning redirect, and plain html (For sign in scenarios)
24
36
 
25
37
  */
26
38
  //p1 - in this video I'll use remult to turn a frontend app to a fullstack app
27
- /*y1 - Talk JYC - JYC - add some integrity checks on delete
39
+ /*y2 - Talk JYC - JYC - add some integrity checks on delete
28
40
  - soft delete
29
41
  - delete restrict (implicit, or user selected - and if so, how) (delete & update of id)
30
42
 
31
43
  */
32
44
  //y1 - consider sql expression gets a dbnames of it's own (that already has the "tableName" defined correctly) maybe also the filter translator
33
45
  //y1 - tried to upgrade vitest, nuxt tests are failing with loading uuid - sounds familiar?
34
- //y1 - I think that the tests you've setup don't cover next app router - I added to the setup, but not sure where else
35
46
  //y1 - talk about the parameter issue with backend methods
36
- //y1 - select data provider per entity https://discord.com/channels/975754286384418847/976006081748807690/1201415305885397003
37
- //y1 - live query refresh of view on table update
38
47
  //y1 - main vs master
39
48
  //y2 - livequery for findfirst (@JY)
40
49
  /*y2 -
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remult",
3
- "version": "0.26.0",
3
+ "version": "0.26.2",
4
4
  "description": "A CRUD framework for full-stack TypeScript",
5
5
  "homepage": "https://remult.dev",
6
6
  "repository": {
@@ -140,6 +140,11 @@
140
140
  "require": "./remult-nuxt.js",
141
141
  "import": "./esm/remult-nuxt.js",
142
142
  "types": "./remult-nuxt.d.ts"
143
+ },
144
+ "./remult-solid-start": {
145
+ "require": "./remult-solid-start.js",
146
+ "import": "./esm/remult-solid-start.js",
147
+ "types": "./remult-solid-start.d.ts"
143
148
  }
144
149
  },
145
150
  "main": "./index.js",
@@ -0,0 +1,12 @@
1
+ import { type RequestEvent } from 'solid-js/web';
2
+ import type { RemultServerCore, RemultServerOptions } from './server/index.js';
3
+ export declare function remultSolidStart(options: RemultServerOptions<RequestEvent>): RemultSolidStartServer;
4
+ type RequestHandler = (event: RequestEvent) => Promise<Response>;
5
+ export type RemultSolidStartServer = RemultServerCore<RequestEvent> & {
6
+ withRemult<T>(what: () => Promise<T>): Promise<T>;
7
+ GET: RequestHandler;
8
+ PUT: RequestHandler;
9
+ POST: RequestHandler;
10
+ DELETE: RequestHandler;
11
+ };
12
+ export {};
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.remultSolidStart = void 0;
4
+ var tslib_1 = require("tslib");
5
+ var web_1 = require("solid-js/web");
6
+ var index_js_1 = require("./server/index.js");
7
+ function remultSolidStart(options) {
8
+ var _this = this;
9
+ var result = (0, index_js_1.createRemultServer)(options, {
10
+ buildGenericRequestInfo: function (event) { return ({
11
+ url: event.request.url,
12
+ method: event.request.method,
13
+ on: function (e, do1) {
14
+ if (e === 'close') {
15
+ event.locals['_tempOnClose'] = do1;
16
+ }
17
+ },
18
+ }); },
19
+ getRequestBody: function (event) { return event.request.json(); },
20
+ });
21
+ var serverHandler = function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
22
+ var event, sseResponse, response, responseFromRemultHandler, res;
23
+ return tslib_1.__generator(this, function (_a) {
24
+ switch (_a.label) {
25
+ case 0: return [4 /*yield*/, (0, web_1.getRequestEvent)()];
26
+ case 1:
27
+ event = _a.sent();
28
+ sseResponse = undefined;
29
+ if (event)
30
+ event.locals['_tempOnClose'] = function () { };
31
+ response = {
32
+ end: function () { },
33
+ json: function () { },
34
+ send: function () { },
35
+ status: function () {
36
+ return response;
37
+ },
38
+ write: function () { },
39
+ writeHead: function (status, headers) {
40
+ if (status === 200 && headers) {
41
+ var contentType = headers['Content-Type'];
42
+ if (contentType === 'text/event-stream') {
43
+ var messages_1 = [];
44
+ response.write = function (x) { return messages_1.push(x); };
45
+ var stream = new ReadableStream({
46
+ start: function (controller) {
47
+ var e_1, _a;
48
+ try {
49
+ for (var messages_2 = tslib_1.__values(messages_1), messages_2_1 = messages_2.next(); !messages_2_1.done; messages_2_1 = messages_2.next()) {
50
+ var message = messages_2_1.value;
51
+ controller.enqueue(message);
52
+ }
53
+ }
54
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
55
+ finally {
56
+ try {
57
+ if (messages_2_1 && !messages_2_1.done && (_a = messages_2.return)) _a.call(messages_2);
58
+ }
59
+ finally { if (e_1) throw e_1.error; }
60
+ }
61
+ response.write = function (data) {
62
+ controller.enqueue(data);
63
+ };
64
+ },
65
+ cancel: function () {
66
+ var _a, _b;
67
+ response.write = function () { };
68
+ (_b = (_a = event === null || event === void 0 ? void 0 : event.locals) === null || _a === void 0 ? void 0 : _a['_tempOnClose']) === null || _b === void 0 ? void 0 : _b.call(_a);
69
+ },
70
+ });
71
+ sseResponse = new Response(stream, { headers: headers });
72
+ }
73
+ }
74
+ },
75
+ };
76
+ return [4 /*yield*/, result.handle(event, response)];
77
+ case 2:
78
+ responseFromRemultHandler = _a.sent();
79
+ if (sseResponse !== undefined) {
80
+ return [2 /*return*/, sseResponse];
81
+ }
82
+ if (responseFromRemultHandler) {
83
+ if (responseFromRemultHandler.html)
84
+ return [2 /*return*/, new Response(responseFromRemultHandler.html, {
85
+ status: responseFromRemultHandler.statusCode,
86
+ headers: {
87
+ 'Content-Type': 'text/html',
88
+ },
89
+ })];
90
+ res = new Response(JSON.stringify(responseFromRemultHandler.data), {
91
+ status: responseFromRemultHandler.statusCode,
92
+ });
93
+ return [2 /*return*/, res];
94
+ }
95
+ return [2 /*return*/, new Response('Not Found', {
96
+ status: 404,
97
+ })];
98
+ }
99
+ });
100
+ }); };
101
+ var handler = {}; //async ({ event, resolve }) => {
102
+ // if (event.url.pathname.startsWith(options!.rootPath!)) {
103
+ // const result = await serverHandler(event)
104
+ // if (result != null && result?.status != 404) return result
105
+ // }
106
+ // return new Promise<Response>((res) => {
107
+ // result.withRemult(event, undefined!, async () => {
108
+ // res(await resolve(event))
109
+ // })
110
+ // })
111
+ // }
112
+ return Object.assign(handler, {
113
+ getRemult: function (req) { return result.getRemult(req); },
114
+ openApiDoc: function (options) { return result.openApiDoc(options); },
115
+ withRemult: function (what) {
116
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
117
+ var _a, _b;
118
+ return tslib_1.__generator(this, function (_c) {
119
+ switch (_c.label) {
120
+ case 0:
121
+ _b = (_a = result).withRemultAsync;
122
+ return [4 /*yield*/, (0, web_1.getRequestEvent)()];
123
+ case 1: return [2 /*return*/, _b.apply(_a, [_c.sent(), what])];
124
+ }
125
+ });
126
+ });
127
+ },
128
+ GET: serverHandler,
129
+ PUT: serverHandler,
130
+ POST: serverHandler,
131
+ DELETE: serverHandler,
132
+ });
133
+ }
134
+ exports.remultSolidStart = remultSolidStart;
@@ -1086,7 +1086,7 @@ var ExpressResponseBridgeToDataApiResponse = /** @class */ (function () {
1086
1086
  this.setStatus(204).end();
1087
1087
  };
1088
1088
  ExpressResponseBridgeToDataApiResponse.prototype.notFound = function () {
1089
- this.error({ message: 'Forbidden' }, undefined, 404);
1089
+ this.error({ message: 'NotFound' }, undefined, 404);
1090
1090
  };
1091
1091
  ExpressResponseBridgeToDataApiResponse.prototype.error = function (exception, entity, httpStatusCode) {
1092
1092
  var _a;
@@ -13,5 +13,12 @@ export declare class SequentialRemultAsyncLocalStorageCore<T> implements RemultA
13
13
  lastPromise: Promise<T>;
14
14
  currentValue: T;
15
15
  }
16
+ export declare class StubRemultAsyncLocalStorageCore<T> implements RemultAsyncLocalStorageCore<T> {
17
+ wasImplemented: 'yes';
18
+ run<R>(store: T, callback: () => Promise<R>): Promise<R>;
19
+ getStore(): T;
20
+ lastPromise: Promise<T>;
21
+ currentValue: T;
22
+ }
16
23
  export declare class AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore<T> extends AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl<T> {
17
24
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore = exports.SequentialRemultAsyncLocalStorageCore = exports.AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl = exports.initAsyncHooks = void 0;
3
+ exports.AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore = exports.StubRemultAsyncLocalStorageCore = exports.SequentialRemultAsyncLocalStorageCore = exports.AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl = exports.initAsyncHooks = void 0;
4
4
  var tslib_1 = require("tslib");
5
5
  var async_hooks_1 = require("async_hooks");
6
6
  var context_js_1 = require("../src/context.js");
@@ -20,7 +20,7 @@ function initAsyncHooks() {
20
20
  case 1:
21
21
  _a.sent();
22
22
  if (test.getStore() === undefined) {
23
- console.log("async_hooks.AsyncLocalStorage not working, using sequential implementation (You're probably running on stackblitz, this will work on a normal nodejs environment");
23
+ console.log("async_hooks.AsyncLocalStorage not working, using stub implementation (You're probably running on stackblitz, this will work on a normal nodejs environment");
24
24
  remult_static_js_1.remultStatic.asyncContext = new context_js_1.RemultAsyncLocalStorage(new SequentialRemultAsyncLocalStorageCore());
25
25
  }
26
26
  return [2 /*return*/];
@@ -65,7 +65,12 @@ var AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl = /** @class */ (fu
65
65
  return AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl;
66
66
  }());
67
67
  exports.AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl = AsyncLocalStorageBridgeToRemultAsyncLocalStorageCoreImpl;
68
- var ignoreInStack = ["AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore.run", "RemultAsyncLocalStorage.run", "Module.withRemult", "RemultServerImplementation.runWithRemult"];
68
+ var ignoreInStack = [
69
+ 'AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore.run',
70
+ 'RemultAsyncLocalStorage.run',
71
+ 'Module.withRemult',
72
+ 'RemultServerImplementation.runWithRemult',
73
+ ];
69
74
  var SequentialRemultAsyncLocalStorageCore = /** @class */ (function () {
70
75
  function SequentialRemultAsyncLocalStorageCore() {
71
76
  this.lastPromise = Promise.resolve(undefined);
@@ -77,12 +82,13 @@ var SequentialRemultAsyncLocalStorageCore = /** @class */ (function () {
77
82
  return tslib_1.__generator(this, function (_a) {
78
83
  switch (_a.label) {
79
84
  case 0:
80
- log = function (msg) {
81
- };
85
+ log = function (msg) { };
82
86
  if (false) {
83
87
  stack_1 = new Error().stack;
84
88
  y = stack_1.split('\n');
85
- while (y.length > 0 && (y[0].trim().startsWith("Error") || ignoreInStack.includes(y[0].trim().split(' ')[1]))) {
89
+ while (y.length > 0 &&
90
+ (y[0].trim().startsWith('Error') ||
91
+ ignoreInStack.includes(y[0].trim().split(' ')[1]))) {
86
92
  y.splice(0, 1);
87
93
  }
88
94
  stack_1 = y.join('\n');
@@ -108,7 +114,7 @@ var SequentialRemultAsyncLocalStorageCore = /** @class */ (function () {
108
114
  });
109
115
  }); });
110
116
  this.lastPromise = nextPromise.catch(function () {
111
- log("Error on ");
117
+ log('Error on ');
112
118
  return undefined;
113
119
  });
114
120
  _a.label = 1;
@@ -130,6 +136,28 @@ var SequentialRemultAsyncLocalStorageCore = /** @class */ (function () {
130
136
  return SequentialRemultAsyncLocalStorageCore;
131
137
  }());
132
138
  exports.SequentialRemultAsyncLocalStorageCore = SequentialRemultAsyncLocalStorageCore;
139
+ var StubRemultAsyncLocalStorageCore = /** @class */ (function () {
140
+ function StubRemultAsyncLocalStorageCore() {
141
+ this.lastPromise = Promise.resolve(undefined);
142
+ }
143
+ StubRemultAsyncLocalStorageCore.prototype.run = function (store, callback) {
144
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
145
+ return tslib_1.__generator(this, function (_a) {
146
+ switch (_a.label) {
147
+ case 0:
148
+ this.currentValue = store;
149
+ return [4 /*yield*/, callback()];
150
+ case 1: return [2 /*return*/, _a.sent()];
151
+ }
152
+ });
153
+ });
154
+ };
155
+ StubRemultAsyncLocalStorageCore.prototype.getStore = function () {
156
+ return this.currentValue;
157
+ };
158
+ return StubRemultAsyncLocalStorageCore;
159
+ }());
160
+ exports.StubRemultAsyncLocalStorageCore = StubRemultAsyncLocalStorageCore;
133
161
  var AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore = /** @class */ (function (_super) {
134
162
  tslib_1.__extends(AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore, _super);
135
163
  function AsyncLocalStorageBridgeToRemultAsyncLocalStorageCore() {
package/src/context.d.ts CHANGED
@@ -26,7 +26,7 @@ export declare class Remult {
26
26
  * @param entity - the entity to use
27
27
  * @param dataProvider - an optional alternative data provider to use. Useful for writing to offline storage or an alternative data provider
28
28
  */
29
- repo<T>(entity: ClassType<T>, dataProvider?: DataProvider): Repository<T>;
29
+ repo: <T>(entity: ClassType<T>, dataProvider?: DataProvider) => Repository<T>;
30
30
  /** Returns the current user's info */
31
31
  user?: UserInfo;
32
32
  /** Checks if a user was authenticated */
@@ -102,15 +102,22 @@ export interface ApiClient {
102
102
  *
103
103
  * @example
104
104
  * // Adding bearer token authorization
105
- * remult.apiClient.httpClient = (input: RequestInfo | URL, init?: RequestInit) => {
105
+ * remult.apiClient.httpClient = (
106
+ * input: RequestInfo | URL,
107
+ * init?: RequestInit
108
+ * ) => {
106
109
  * return fetch(input, {
107
110
  * ...init,
108
- * headers: {
109
- * authorization: 'Bearer ' + sessionStorage.sessionId,
110
- * },
111
+ * headers: authToken
112
+ * ? {
113
+ * ...init?.headers,
114
+ * authorization: 'Bearer ' + authToken,
115
+ * }
116
+ * : init?.headers,
117
+ *
111
118
  * cache: 'no-store',
112
- * });
113
- * };
119
+ * })
120
+ * }
114
121
  */
115
122
  httpClient?: ExternalHttpProvider | typeof fetch;
116
123
  /**
package/src/context.js CHANGED
@@ -57,6 +57,26 @@ exports.isBackend = isBackend;
57
57
  var Remult = /** @class */ (function () {
58
58
  function Remult(provider) {
59
59
  var _this = this;
60
+ /**Return's a `Repository` of the specific entity type
61
+ * @example
62
+ * const taskRepo = remult.repo(Task);
63
+ * @see [Repository](https://remult.dev/docs/ref_repository.html)
64
+ * @param entity - the entity to use
65
+ * @param dataProvider - an optional alternative data provider to use. Useful for writing to offline storage or an alternative data provider
66
+ */
67
+ this.repo = function (entity, dataProvider) {
68
+ if (dataProvider === undefined)
69
+ dataProvider = _this.dataProvider;
70
+ var dpCache = _this.repCache.get(dataProvider);
71
+ if (!dpCache)
72
+ _this.repCache.set(dataProvider, (dpCache = new Map()));
73
+ var r = dpCache.get(entity);
74
+ if (!r) {
75
+ dpCache.set(entity, (r = new RepositoryImplementation_js_1.RepositoryImplementation(entity, _this, dataProvider, (0, RepositoryImplementation_js_1.createOldEntity)(entity, _this))));
76
+ (0, relationInfoMember_js_1.verifyFieldRelationInfo)(r, _this, dataProvider);
77
+ }
78
+ return r;
79
+ };
60
80
  /** The current data provider */
61
81
  this.dataProvider = new rest_data_provider_js_1.RestDataProvider(function () { return _this.apiClient; });
62
82
  /* @internal */
@@ -97,26 +117,6 @@ var Remult = /** @class */ (function () {
97
117
  this.apiClient.wrapMessageHandling = apiClient.wrapMessageHandling;
98
118
  }
99
119
  }
100
- /**Return's a `Repository` of the specific entity type
101
- * @example
102
- * const taskRepo = remult.repo(Task);
103
- * @see [Repository](https://remult.dev/docs/ref_repository.html)
104
- * @param entity - the entity to use
105
- * @param dataProvider - an optional alternative data provider to use. Useful for writing to offline storage or an alternative data provider
106
- */
107
- Remult.prototype.repo = function (entity, dataProvider) {
108
- if (dataProvider === undefined)
109
- dataProvider = this.dataProvider;
110
- var dpCache = this.repCache.get(dataProvider);
111
- if (!dpCache)
112
- this.repCache.set(dataProvider, (dpCache = new Map()));
113
- var r = dpCache.get(entity);
114
- if (!r) {
115
- dpCache.set(entity, (r = new RepositoryImplementation_js_1.RepositoryImplementation(entity, this, dataProvider, (0, RepositoryImplementation_js_1.createOldEntity)(entity, this))));
116
- (0, relationInfoMember_js_1.verifyFieldRelationInfo)(r, this, dataProvider);
117
- }
118
- return r;
119
- };
120
120
  /** Checks if a user was authenticated */
121
121
  Remult.prototype.authenticated = function () {
122
122
  var _a;
@@ -2316,13 +2316,14 @@ var rowHelperImplementation = /** @class */ (function (_super) {
2316
2316
  };
2317
2317
  rowHelperImplementation.prototype.delete = function () {
2318
2318
  return tslib_1.__awaiter(this, void 0, void 0, function () {
2319
- var e, _a, _b, listener, e_36_1, err_2;
2319
+ var doDelete, e, _a, _b, listener, e_36_1, err_2;
2320
2320
  var e_36, _c;
2321
2321
  return tslib_1.__generator(this, function (_d) {
2322
2322
  switch (_d.label) {
2323
2323
  case 0:
2324
2324
  this.__clearErrorsAndReportChanged();
2325
- e = this.buildLifeCycleEvent();
2325
+ doDelete = true;
2326
+ e = this.buildLifeCycleEvent(function () { return (doDelete = false); });
2326
2327
  if (!!this.repository._dataProvider.isProxy) return [3 /*break*/, 2];
2327
2328
  if (!this.info.entityInfo.deleting) return [3 /*break*/, 2];
2328
2329
  return [4 /*yield*/, this.info.entityInfo.deleting(this.instance, e)];
@@ -2333,56 +2334,59 @@ var rowHelperImplementation = /** @class */ (function (_super) {
2333
2334
  this.__assertValidity();
2334
2335
  _d.label = 3;
2335
2336
  case 3:
2336
- _d.trys.push([3, 16, , 18]);
2337
+ _d.trys.push([3, 17, , 19]);
2338
+ if (!doDelete) return [3 /*break*/, 5];
2337
2339
  return [4 /*yield*/, this.edp.delete(this.id)];
2338
2340
  case 4:
2339
2341
  _d.sent();
2340
- if (!!this.repository._dataProvider.isProxy) return [3 /*break*/, 6];
2341
- if (!this.info.entityInfo.deleted) return [3 /*break*/, 6];
2342
- return [4 /*yield*/, this.info.entityInfo.deleted(this.instance, e)];
2342
+ _d.label = 5;
2343
2343
  case 5:
2344
- _d.sent();
2345
- _d.label = 6;
2344
+ if (!!this.repository._dataProvider.isProxy) return [3 /*break*/, 7];
2345
+ if (!this.info.entityInfo.deleted) return [3 /*break*/, 7];
2346
+ return [4 /*yield*/, this.info.entityInfo.deleted(this.instance, e)];
2346
2347
  case 6:
2347
- if (!this.repository.listeners) return [3 /*break*/, 14];
2348
+ _d.sent();
2348
2349
  _d.label = 7;
2349
2350
  case 7:
2350
- _d.trys.push([7, 12, 13, 14]);
2351
- _a = tslib_1.__values(this.repository.listeners.filter(function (x) { return x.deleted; })), _b = _a.next();
2351
+ if (!this.repository.listeners) return [3 /*break*/, 15];
2352
2352
  _d.label = 8;
2353
2353
  case 8:
2354
- if (!!_b.done) return [3 /*break*/, 11];
2354
+ _d.trys.push([8, 13, 14, 15]);
2355
+ _a = tslib_1.__values(this.repository.listeners.filter(function (x) { return x.deleted; })), _b = _a.next();
2356
+ _d.label = 9;
2357
+ case 9:
2358
+ if (!!_b.done) return [3 /*break*/, 12];
2355
2359
  listener = _b.value;
2356
2360
  return [4 /*yield*/, listener.deleted(this.instance)];
2357
- case 9:
2358
- _d.sent();
2359
- _d.label = 10;
2360
2361
  case 10:
2362
+ _d.sent();
2363
+ _d.label = 11;
2364
+ case 11:
2361
2365
  _b = _a.next();
2362
- return [3 /*break*/, 8];
2363
- case 11: return [3 /*break*/, 14];
2364
- case 12:
2366
+ return [3 /*break*/, 9];
2367
+ case 12: return [3 /*break*/, 15];
2368
+ case 13:
2365
2369
  e_36_1 = _d.sent();
2366
2370
  e_36 = { error: e_36_1 };
2367
- return [3 /*break*/, 14];
2368
- case 13:
2371
+ return [3 /*break*/, 15];
2372
+ case 14:
2369
2373
  try {
2370
2374
  if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
2371
2375
  }
2372
2376
  finally { if (e_36) throw e_36.error; }
2373
2377
  return [7 /*endfinally*/];
2374
- case 14: return [4 /*yield*/, this.repository._remult.liveQueryPublisher.itemChanged(this.repository.metadata.key, [{ id: this.getId(), oldId: this.getOriginalId(), deleted: true }])];
2375
- case 15:
2378
+ case 15: return [4 /*yield*/, this.repository._remult.liveQueryPublisher.itemChanged(this.repository.metadata.key, [{ id: this.getId(), oldId: this.getOriginalId(), deleted: true }])];
2379
+ case 16:
2376
2380
  _d.sent();
2377
2381
  this._wasDeleted = true;
2378
- return [3 /*break*/, 18];
2379
- case 16:
2382
+ return [3 /*break*/, 19];
2383
+ case 17:
2380
2384
  err_2 = _d.sent();
2381
2385
  return [4 /*yield*/, this.catchSaveErrors(err_2)];
2382
- case 17:
2386
+ case 18:
2383
2387
  _d.sent();
2384
- return [3 /*break*/, 18];
2385
- case 18: return [2 /*return*/];
2388
+ return [3 /*break*/, 19];
2389
+ case 19: return [2 /*return*/];
2386
2390
  }
2387
2391
  });
2388
2392
  });
@@ -1,41 +1,50 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ //y1 - https://discord.com/channels/975754286384418847/1230386433093533698/1230386433093533698
4
+ //y1 - fix async hooks - was it fixed?
5
+ //p1 - prevent default in delete should not delete - and check proceed from there
6
+ //p1 - release solid start with example project
3
7
  //p1 - add documentation for bun sqlite
4
- //p2 - fix dark background to also work on groups
5
- //y1 - JY suggestion of having generate as part of the ensure schema - not sure
6
- //y1 - currently for string fields we default for 255 in knex (which is their default) why not just use text (varchar max) - and only use exact length when one is provided?
7
- //y1 - consider changing the sql raw filter to return the sql, instead of setting the sql property
8
+ //p1 - fix dark background to also work on groups
9
+ //p1 - describe entity
10
+ //p1 - prepare the createEntity discussion
11
+ //p1 - fix async hooks
12
+ //p1 - write active record article
13
+ //p1 - maybe add decorator comment in the readme as well (I heard you've lost me in decorators) - and improve the describeClass function
14
+ //p1 - return error on connection error
15
+ //p1 - reconsider update many, maybe the second parameter should be in a set option - break existing
16
+ //p1 - consider upsert (where,set)
17
+ //p1 - consider id to also support keyof (id:['company','index'])
18
+ //p1 - consider including the admin in the tutorial - and maybe even in the demo when we setup the api
19
+ //p1 -
20
+ //y2 - JY suggestion of having generate as part of the ensure schema - not sure
21
+ //y2 - currently for string fields we default for 255 in knex (which is their default) why not just use text (varchar max) - and only use exact length when one is provided?
8
22
  //p2 - create foreign key constraints in user code - https://codesandbox.io/p/devbox/fk-validator-tdshcs
9
- //p1 - admin url1 - pocketbase use /_ for the admin //{allowed?:Allowed,url?:string}
10
- //p2 - article auth.js with express - played with it, requires type="module" and a few more configurations - https://github.com/noam-honig/express-auth
11
- //y1 - fix validators cache
23
+ //p1 - admin url1 - pocketbase use /_ for the admin //{allowed?:Allowed,url?:string} also checkout the admin* as it should probably be something else.
24
+ //p1 - article auth.js with express - played with it, requires type="module" and a few more configurations - https://github.com/noam-honig/express-auth
25
+ //y2 - fix validators cache
12
26
  //y2 - should we validate relations
13
- //y1 - dependency of live query tables
27
+ //y1 - dependency of live query tables live query refresh of view on table update
14
28
  //p1 - see why mongo tests do not run anymore
15
- //y1 - consider the bot that updates dependencies
16
- //y1 - consider replacing all errors with error classes that extend the base Error class
29
+ //y2 - consider replacing all errors with error classes that extend the base Error class
17
30
  //y2 - should enforce integer - currently we probably round / truncate it
18
31
  //y1 - talk about filter on objects that are not loaded - {
19
32
  //category: repo(CompoundId).create({ company: 7, index: 3, name: '' }),
20
33
  // }
21
- //y1 - consider id to also support keyof (id:['company','index'])
22
34
  /*y1 - talk about modules in init express with entities/controllers,initRequest,initApi
23
35
  - support get with backend method, with url search params as the first parameter, & url as second parameter
24
36
  - support returning redirect, and plain html (For sign in scenarios)
25
37
 
26
38
  */
27
39
  //p1 - in this video I'll use remult to turn a frontend app to a fullstack app
28
- /*y1 - Talk JYC - JYC - add some integrity checks on delete
40
+ /*y2 - Talk JYC - JYC - add some integrity checks on delete
29
41
  - soft delete
30
42
  - delete restrict (implicit, or user selected - and if so, how) (delete & update of id)
31
43
 
32
44
  */
33
45
  //y1 - consider sql expression gets a dbnames of it's own (that already has the "tableName" defined correctly) maybe also the filter translator
34
46
  //y1 - tried to upgrade vitest, nuxt tests are failing with loading uuid - sounds familiar?
35
- //y1 - I think that the tests you've setup don't cover next app router - I added to the setup, but not sure where else
36
47
  //y1 - talk about the parameter issue with backend methods
37
- //y1 - select data provider per entity https://discord.com/channels/975754286384418847/976006081748807690/1201415305885397003
38
- //y1 - live query refresh of view on table update
39
48
  //y1 - main vs master
40
49
  //y2 - livequery for findfirst (@JY)
41
50
  /*y2 -