remult 0.22.6-exp.1 → 0.22.6-exp.3
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/async-hooks.d.ts +26 -0
- package/async-hooks.js +34 -0
- package/esm/async-hooks.js +31 -0
- package/esm/internals.js +3 -1
- package/esm/migrations/generate-migrations.js +5 -1
- package/esm/remult-d1-http.js +32 -0
- package/esm/remult-d1.js +90 -0
- package/esm/remult-duckdb.js +82 -38
- package/esm/remult-hapi.js +11 -8
- package/esm/remult-hono.js +19 -15
- package/esm/remult-nuxt.js +11 -6
- package/esm/remult-solid-start.js +12 -7
- package/esm/remult-sqlite-core.js +33 -5
- package/esm/remult-sqlite3.js +5 -0
- package/esm/server/get-remult-admin-html.js +1 -1
- package/esm/server/module.js +38 -0
- package/esm/server/remult-admin.js +91 -60
- package/esm/server/remult-api-server.js +124 -50
- package/esm/server/test-api-data-provider.js +107 -0
- package/esm/src/CompoundIdField.js +1 -0
- package/esm/src/data-api.js +4 -2
- package/esm/src/data-providers/json-entity-indexed-db-data-provider.js +64 -0
- package/esm/src/data-providers/rest-data-provider-using-fetch.js +78 -0
- package/esm/src/data-providers/retry.js +25 -0
- package/esm/src/data-providers/sql-relations.js +125 -0
- package/esm/src/filter/entityDbName.js +18 -0
- package/esm/src/filter/fieldDbName.js +40 -0
- package/esm/src/remult-static.js +3 -1
- package/esm/src/remult3/Fields.js +84 -34
- package/esm/src/remult3/IdEntity.js +1 -1
- package/esm/src/remult3/QueryResultImpl.js +147 -0
- package/esm/src/remult3/SubscribableImp.js +26 -0
- package/esm/src/remult3/entity.js +11 -7
- package/esm/src/remult3/getEntityRef.js +2 -0
- package/esm/src/remult3/relationInfoMember.js +4 -2
- package/esm/src/server-action.d.ts +7 -0
- package/esm/src/server-action.js +8 -2
- package/esm/src/standard-schema/StandardSchemaV1.js +1 -0
- package/esm/src/standard-schema/index.js +74 -0
- package/internals.d.ts +5 -3
- package/internals.js +7 -1
- package/migrations/generate-migrations.js +5 -1
- package/package.json +1 -1
- package/remult-d1-http.d.ts +16 -0
- package/remult-d1-http.js +46 -0
- package/remult-d1.d.ts +27 -0
- package/remult-d1.js +128 -0
- package/remult-duckdb.d.ts +4 -3
- package/remult-duckdb.js +87 -42
- package/remult-hapi.d.ts +3 -1
- package/remult-hapi.js +10 -10
- package/remult-hono.d.ts +3 -1
- package/remult-hono.js +21 -16
- package/remult-nuxt.d.ts +3 -1
- package/remult-nuxt.js +13 -7
- package/remult-solid-start.d.ts +3 -1
- package/remult-solid-start.js +14 -8
- package/remult-sqlite-core.d.ts +2 -1
- package/remult-sqlite-core.js +116 -29
- package/remult-sqlite3.d.ts +2 -0
- package/remult-sqlite3.js +16 -0
- package/server/get-remult-admin-html.js +1 -1
- package/server/initDataProviderOrJson.d.ts +1 -1
- package/server/module.d.ts +26 -0
- package/server/module.js +40 -0
- package/server/remult-admin.d.ts +15 -5
- package/server/remult-admin.js +77 -44
- package/server/remult-api-server.d.ts +43 -5
- package/server/remult-api-server.js +163 -69
- package/server/test-api-data-provider.d.ts +9 -0
- package/server/test-api-data-provider.js +174 -0
- package/src/CompoundIdField.d.ts +1 -0
- package/src/CompoundIdField.js +1 -0
- package/src/data-api.js +4 -2
- package/src/data-providers/json-entity-indexed-db-data-provider.d.ts +10 -0
- package/src/data-providers/json-entity-indexed-db-data-provider.js +119 -0
- package/src/data-providers/rest-data-provider-using-fetch.d.ts +13 -0
- package/src/data-providers/rest-data-provider-using-fetch.js +119 -0
- package/src/data-providers/retry.d.ts +1 -0
- package/src/data-providers/retry.js +44 -0
- package/src/data-providers/sql-relations.d.ts +40 -0
- package/src/data-providers/sql-relations.js +167 -0
- package/src/filter/entityDbName.d.ts +2 -0
- package/src/filter/entityDbName.js +32 -0
- package/src/filter/fieldDbName.d.ts +3 -0
- package/src/filter/fieldDbName.js +58 -0
- package/src/remult-static.d.ts +3 -1
- package/src/remult-static.js +3 -1
- package/src/remult3/Fields.d.ts +57 -5
- package/src/remult3/Fields.js +86 -44
- package/src/remult3/IdEntity.js +1 -1
- package/src/remult3/QueryResultImpl.d.ts +16 -0
- package/src/remult3/QueryResultImpl.js +268 -0
- package/src/remult3/SubscribableImp.d.ts +11 -0
- package/src/remult3/SubscribableImp.js +35 -0
- package/src/remult3/entity.d.ts +1 -1
- package/src/remult3/entity.js +11 -7
- package/src/remult3/getEntityRef.js +2 -0
- package/src/remult3/relationInfoMember.js +4 -2
- package/src/server-action.d.ts +7 -0
- package/src/server-action.js +19 -2
- package/src/standard-schema/StandardSchemaV1.d.ts +56 -0
- package/src/standard-schema/StandardSchemaV1.js +2 -0
- package/src/standard-schema/index.d.ts +22 -0
- package/src/standard-schema/index.js +110 -0
package/async-hooks.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initializes and enables async context tracking for the server.
|
|
3
|
+
*
|
|
4
|
+
* This should be called before handling any incoming requests or calling `withRemult()`.
|
|
5
|
+
* @example
|
|
6
|
+
* import { remult, repo, withRemult } from 'remult';
|
|
7
|
+
* import { initAsyncHooks } from 'remult/async-hooks';
|
|
8
|
+
*
|
|
9
|
+
* import { Task } from './entities/Task.js';
|
|
10
|
+
*
|
|
11
|
+
* initAsyncHooks();
|
|
12
|
+
*
|
|
13
|
+
* // Thx to the `initAsyncHooks` above,
|
|
14
|
+
* // we have isolated async contexts with multiple `withRemult()`,
|
|
15
|
+
* // without needing to initialize a `remultApi` all the time!
|
|
16
|
+
* withRemult(async () => {
|
|
17
|
+
* remult.user = { id: '42' };
|
|
18
|
+
* repo(Task).find()
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* withRemult(async () => {
|
|
22
|
+
* remult.user = { id: '21' };
|
|
23
|
+
* repo(Task).find()
|
|
24
|
+
* });
|
|
25
|
+
*/
|
|
26
|
+
export declare function initAsyncHooks(): void;
|
package/async-hooks.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.initAsyncHooks = initAsyncHooks;
|
|
4
|
+
var context_js_1 = require("./src/context.js");
|
|
5
|
+
var initAsyncHooks_js_1 = require("./server/initAsyncHooks.js");
|
|
6
|
+
/**
|
|
7
|
+
* Initializes and enables async context tracking for the server.
|
|
8
|
+
*
|
|
9
|
+
* This should be called before handling any incoming requests or calling `withRemult()`.
|
|
10
|
+
* @example
|
|
11
|
+
* import { remult, repo, withRemult } from 'remult';
|
|
12
|
+
* import { initAsyncHooks } from 'remult/async-hooks';
|
|
13
|
+
*
|
|
14
|
+
* import { Task } from './entities/Task.js';
|
|
15
|
+
*
|
|
16
|
+
* initAsyncHooks();
|
|
17
|
+
*
|
|
18
|
+
* // Thx to the `initAsyncHooks` above,
|
|
19
|
+
* // we have isolated async contexts with multiple `withRemult()`,
|
|
20
|
+
* // without needing to initialize a `remultApi` all the time!
|
|
21
|
+
* withRemult(async () => {
|
|
22
|
+
* remult.user = { id: '42' };
|
|
23
|
+
* repo(Task).find()
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* withRemult(async () => {
|
|
27
|
+
* remult.user = { id: '21' };
|
|
28
|
+
* repo(Task).find()
|
|
29
|
+
* });
|
|
30
|
+
*/
|
|
31
|
+
function initAsyncHooks() {
|
|
32
|
+
(0, initAsyncHooks_js_1.initAsyncHooks)();
|
|
33
|
+
context_js_1.RemultAsyncLocalStorage.enable();
|
|
34
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { RemultAsyncLocalStorage } from './src/context.js';
|
|
2
|
+
import { initAsyncHooks as _initAsyncHooks } from './server/initAsyncHooks.js';
|
|
3
|
+
/**
|
|
4
|
+
* Initializes and enables async context tracking for the server.
|
|
5
|
+
*
|
|
6
|
+
* This should be called before handling any incoming requests or calling `withRemult()`.
|
|
7
|
+
* @example
|
|
8
|
+
* import { remult, repo, withRemult } from 'remult';
|
|
9
|
+
* import { initAsyncHooks } from 'remult/async-hooks';
|
|
10
|
+
*
|
|
11
|
+
* import { Task } from './entities/Task.js';
|
|
12
|
+
*
|
|
13
|
+
* initAsyncHooks();
|
|
14
|
+
*
|
|
15
|
+
* // Thx to the `initAsyncHooks` above,
|
|
16
|
+
* // we have isolated async contexts with multiple `withRemult()`,
|
|
17
|
+
* // without needing to initialize a `remultApi` all the time!
|
|
18
|
+
* withRemult(async () => {
|
|
19
|
+
* remult.user = { id: '42' };
|
|
20
|
+
* repo(Task).find()
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* withRemult(async () => {
|
|
24
|
+
* remult.user = { id: '21' };
|
|
25
|
+
* repo(Task).find()
|
|
26
|
+
* });
|
|
27
|
+
*/
|
|
28
|
+
export function initAsyncHooks() {
|
|
29
|
+
_initAsyncHooks();
|
|
30
|
+
RemultAsyncLocalStorage.enable();
|
|
31
|
+
}
|
package/esm/internals.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { remultStatic } from './src/remult-static.js';
|
|
2
|
+
export { sqlRelations, sqlRelationsFilter, } from './src/data-providers/sql-relations.js';
|
|
2
3
|
export { getRelationInfo, getRelationFieldInfo, } from './src/remult3/relationInfoMember.js';
|
|
3
|
-
export { decorateColumnSettings, controllerRefImpl, getControllerRef, } from './src/remult3/RepositoryImplementation.js';
|
|
4
|
+
export { decorateColumnSettings, controllerRefImpl, getControllerRef, fieldOptionsEnricher, } from './src/remult3/RepositoryImplementation.js';
|
|
4
5
|
export { getEntitySettings } from './src/remult3/getEntityRef.js';
|
|
5
6
|
export { __updateEntityBasedOnWhere } from './src/filter/filter-interfaces.js';
|
|
6
7
|
export const actionInfo = remultStatic.actionInfo;
|
|
8
|
+
export { isOfType } from './src/isOfType.js';
|
|
7
9
|
export { flags } from './src/remult3/remult3.js';
|
|
@@ -36,7 +36,7 @@ export async function generateMigrations(options) {
|
|
|
36
36
|
const code = {
|
|
37
37
|
//https://marketplace.visualstudio.com/items?itemName=qufiwefefwoyn.inline-sql-syntax
|
|
38
38
|
addSql: (sql) => {
|
|
39
|
-
steps.push('await sql(`--sql\n' + sql + '`)');
|
|
39
|
+
steps.push('await sql(`--sql\n' + escapeForTpl(sql) + '`)');
|
|
40
40
|
hasSql = true;
|
|
41
41
|
},
|
|
42
42
|
addTypescriptCode: (code) => steps.push(code),
|
|
@@ -66,3 +66,7 @@ export async function generateMigrations(options) {
|
|
|
66
66
|
}
|
|
67
67
|
return steps.length > 0;
|
|
68
68
|
}
|
|
69
|
+
function escapeForTpl(str) {
|
|
70
|
+
// escape backticks, dollar-signs and backslashes
|
|
71
|
+
return str.replace(/[`\\$]/g, (m) => "\\" + m);
|
|
72
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import Cloudflare from 'cloudflare';
|
|
2
|
+
import { SqlDatabase } from './index.js';
|
|
3
|
+
import { D1DataProvider } from './remult-d1.js';
|
|
4
|
+
export { D1DataProvider };
|
|
5
|
+
export function createD1HttpDataProvider(creds) {
|
|
6
|
+
return new SqlDatabase(new D1DataProvider(new D1HttpClient(creds)));
|
|
7
|
+
}
|
|
8
|
+
export class D1HttpClient {
|
|
9
|
+
d1;
|
|
10
|
+
accountId;
|
|
11
|
+
databaseId;
|
|
12
|
+
constructor({ accountId, databaseId, apiToken }) {
|
|
13
|
+
this.d1 = new Cloudflare({ apiToken }).d1.database;
|
|
14
|
+
this.accountId = accountId;
|
|
15
|
+
this.databaseId = databaseId;
|
|
16
|
+
}
|
|
17
|
+
async execute(sql, params = []) {
|
|
18
|
+
return this.d1
|
|
19
|
+
.query(this.databaseId, {
|
|
20
|
+
sql,
|
|
21
|
+
params: params,
|
|
22
|
+
account_id: this.accountId,
|
|
23
|
+
})
|
|
24
|
+
.then(({ result: pages }) => pages.flatMap((page) => page.results));
|
|
25
|
+
// NOTE: d1 query endpoint returns the result as array of object with keys as column names.
|
|
26
|
+
// We're returning that now because the rest of remult seems to expect that.
|
|
27
|
+
// D1 also has a more efficient raw() endpoint that returns {columns: string[], rows: any[][]} that we may want
|
|
28
|
+
// to use eventually, and write code like fromRowToSql()
|
|
29
|
+
// at https://github.com/tursodatabase/libsql-client-ts/blob/main/packages/libsql-client/src/sqlite3.ts#L398
|
|
30
|
+
// to adapt the data
|
|
31
|
+
}
|
|
32
|
+
}
|
package/esm/remult-d1.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/// <reference types="@cloudflare/workers-types" />
|
|
2
|
+
import { SqlDatabase, } from './index.js';
|
|
3
|
+
import { SqliteCoreDataProvider } from './remult-sqlite-core.js';
|
|
4
|
+
export function createD1DataProvider(d1) {
|
|
5
|
+
return new SqlDatabase(new D1DataProvider(new D1BindingClient(d1)));
|
|
6
|
+
}
|
|
7
|
+
export class D1DataProvider extends SqliteCoreDataProvider {
|
|
8
|
+
d1;
|
|
9
|
+
/**
|
|
10
|
+
* For production or local d1 using binding
|
|
11
|
+
*
|
|
12
|
+
* const dataProvider = new SqlDatabase(new D1DataProvider(new D1BindingClient(d1)))
|
|
13
|
+
*/
|
|
14
|
+
constructor(d1) {
|
|
15
|
+
super(() => new D1Command(this.d1), async () => {
|
|
16
|
+
// afaik: d1 connection doesn't need closing,
|
|
17
|
+
// so this is just a noop
|
|
18
|
+
});
|
|
19
|
+
this.d1 = d1;
|
|
20
|
+
}
|
|
21
|
+
async transaction(action) {
|
|
22
|
+
// As of June 2025, D1 doesn't support transactions
|
|
23
|
+
// Here we simply run the action without wrapping it in a transaction
|
|
24
|
+
// It means queries in action() will be run individually (this is the same decision prisma made)
|
|
25
|
+
await action(this);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export class D1BindingClient {
|
|
29
|
+
d1;
|
|
30
|
+
/**
|
|
31
|
+
* Simple d1 client that wraps the d1 binding directly
|
|
32
|
+
*
|
|
33
|
+
* const d1 = new D1BindingClient(env.DB)
|
|
34
|
+
*/
|
|
35
|
+
constructor(d1) {
|
|
36
|
+
this.d1 = d1;
|
|
37
|
+
}
|
|
38
|
+
async execute(sql, params = []) {
|
|
39
|
+
// https://developers.cloudflare.com/d1/worker-api/d1-database/
|
|
40
|
+
// https://developers.cloudflare.com/d1/worker-api/prepared-statements/
|
|
41
|
+
//
|
|
42
|
+
// Note: see if we should eventually take advantage of the raw() end point too.
|
|
43
|
+
const { results } = await this.d1
|
|
44
|
+
.prepare(sql)
|
|
45
|
+
.bind(...params)
|
|
46
|
+
.run();
|
|
47
|
+
return results;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
class D1Command {
|
|
51
|
+
d1;
|
|
52
|
+
params = [];
|
|
53
|
+
constructor(d1) {
|
|
54
|
+
this.d1 = d1;
|
|
55
|
+
}
|
|
56
|
+
async execute(sql) {
|
|
57
|
+
const results = await this.d1.execute(sql, this.params);
|
|
58
|
+
return new D1SqlResult(results);
|
|
59
|
+
}
|
|
60
|
+
/** @deprecated use `param` instead*/
|
|
61
|
+
addParameterAndReturnSqlToken(val) {
|
|
62
|
+
return this.param(val);
|
|
63
|
+
}
|
|
64
|
+
param(val) {
|
|
65
|
+
let p;
|
|
66
|
+
if (val instanceof Date)
|
|
67
|
+
p = val.valueOf();
|
|
68
|
+
else if (typeof val === 'boolean')
|
|
69
|
+
p = val ? 1 : 0;
|
|
70
|
+
else
|
|
71
|
+
p = val;
|
|
72
|
+
this.params.push(p);
|
|
73
|
+
// According to https://developers.cloudflare.com/d1/worker-api/prepared-statements/
|
|
74
|
+
// only the ordered "?NNN" and anonymous "?" param types are supported (although :AAAA seems to work too)
|
|
75
|
+
const key = `?${this.params.length}`;
|
|
76
|
+
return key;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
class D1SqlResult {
|
|
80
|
+
rows;
|
|
81
|
+
columns;
|
|
82
|
+
constructor(rows = []) {
|
|
83
|
+
this.rows = rows;
|
|
84
|
+
// NOTE: are we guaranteed that when this is reached, rows is not empty?
|
|
85
|
+
this.columns = rows.length === 0 ? [] : Object.keys(rows[0]);
|
|
86
|
+
}
|
|
87
|
+
getColumnKeyInResultForIndexInSelect(index) {
|
|
88
|
+
return this.columns[index];
|
|
89
|
+
}
|
|
90
|
+
}
|
package/esm/remult-duckdb.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import { DuckDBTypeId } from '@duckdb/node-api';
|
|
1
2
|
import { SqliteCoreDataProvider } from './remult-sqlite-core.js';
|
|
2
3
|
import { ValueConverters, dbNamesOf, } from './index.js';
|
|
3
4
|
import { shouldNotCreateField } from './src/filter/filter-consumer-bridge-to-sql-request.js';
|
|
4
5
|
import { isAutoIncrement } from './src/remult3/RepositoryImplementation.js';
|
|
5
6
|
export class DuckDBDataProvider extends SqliteCoreDataProvider {
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
connection;
|
|
8
|
+
constructor(connection) {
|
|
9
|
+
super(() => new DuckDBCommand(this.connection), async () => this.connection.disconnectSync(), false, true);
|
|
10
|
+
this.connection = connection;
|
|
8
11
|
}
|
|
9
12
|
wrapIdentifier(name) {
|
|
10
13
|
return `"${name}"`;
|
|
@@ -12,7 +15,8 @@ export class DuckDBDataProvider extends SqliteCoreDataProvider {
|
|
|
12
15
|
async getCreateTableSql(entity) {
|
|
13
16
|
let result = '';
|
|
14
17
|
let e = await dbNamesOf(entity, this.wrapIdentifier);
|
|
15
|
-
|
|
18
|
+
const createSequenceStatements = [];
|
|
19
|
+
let createTableSql = '';
|
|
16
20
|
for (const x of entity.fields) {
|
|
17
21
|
if (!shouldNotCreateField(x, e) || isAutoIncrement(x)) {
|
|
18
22
|
if (result.length != 0)
|
|
@@ -20,7 +24,7 @@ export class DuckDBDataProvider extends SqliteCoreDataProvider {
|
|
|
20
24
|
result += '\r\n ';
|
|
21
25
|
if (isAutoIncrement(x)) {
|
|
22
26
|
const sequenceName = `${entity.dbName}_${x.dbName}_seq`;
|
|
23
|
-
|
|
27
|
+
createSequenceStatements.push(`create sequence if not exists "${sequenceName}";`);
|
|
24
28
|
result += `${e.$dbNameOf(x)} integer default nextval('${sequenceName}')`;
|
|
25
29
|
}
|
|
26
30
|
else {
|
|
@@ -31,15 +35,15 @@ export class DuckDBDataProvider extends SqliteCoreDataProvider {
|
|
|
31
35
|
result += `,\r\n primary key (${entity.idMetadata.fields
|
|
32
36
|
.map((f) => e.$dbNameOf(f))
|
|
33
37
|
.join(',')})`;
|
|
34
|
-
|
|
38
|
+
createTableSql =
|
|
35
39
|
'create table if not exists ' + e.$entityName + ' (' + result + '\r\n)';
|
|
36
|
-
return
|
|
40
|
+
return [...createSequenceStatements, createTableSql];
|
|
37
41
|
}
|
|
38
42
|
addColumnSqlSyntax(x, dbName, isAlterColumn) {
|
|
39
43
|
let result = dbName;
|
|
40
44
|
var allowNull = x.allowNull;
|
|
41
|
-
if (allowNull && isAlterColumn) {
|
|
42
|
-
allowNull =
|
|
45
|
+
if (!allowNull && isAlterColumn) {
|
|
46
|
+
allowNull = true;
|
|
43
47
|
console.log('DuckDB does not support altering columns to allow null on existing tables');
|
|
44
48
|
}
|
|
45
49
|
if (x.valueType == Number) {
|
|
@@ -49,7 +53,7 @@ export class DuckDBDataProvider extends SqliteCoreDataProvider {
|
|
|
49
53
|
result +=
|
|
50
54
|
' ' +
|
|
51
55
|
x.valueConverter.fieldTypeInDb +
|
|
52
|
-
(
|
|
56
|
+
(allowNull ? '' : ' default 0 not null');
|
|
53
57
|
}
|
|
54
58
|
else if (x.valueType == Date) {
|
|
55
59
|
if (!x.valueConverter.fieldTypeInDb)
|
|
@@ -61,56 +65,96 @@ export class DuckDBDataProvider extends SqliteCoreDataProvider {
|
|
|
61
65
|
result += ' ' + x.valueConverter.fieldTypeInDb;
|
|
62
66
|
}
|
|
63
67
|
else if (x.valueType == Boolean)
|
|
64
|
-
result +=
|
|
65
|
-
' boolean' +
|
|
66
|
-
(x.allowNull || isAlterColumn ? '' : ' default false not null');
|
|
68
|
+
result += ' boolean' + (allowNull ? '' : ' default false not null');
|
|
67
69
|
else if (x.valueConverter.fieldTypeInDb) {
|
|
68
70
|
result += ' ' + x.valueConverter.fieldTypeInDb;
|
|
69
|
-
if (!
|
|
70
|
-
x.valueConverter.fieldTypeInDb == 'integer') {
|
|
71
|
+
if (!allowNull && x.valueConverter.fieldTypeInDb == 'integer') {
|
|
71
72
|
result += ' default 0 not null';
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
75
|
else
|
|
75
|
-
result +=
|
|
76
|
-
' varchar' +
|
|
77
|
-
(x.allowNull || isAlterColumn ? '' : " default '' not null");
|
|
76
|
+
result += ' varchar' + (allowNull ? '' : " default '' not null");
|
|
78
77
|
return result;
|
|
79
78
|
}
|
|
80
79
|
}
|
|
81
80
|
class DuckDBCommand {
|
|
82
|
-
|
|
81
|
+
connection;
|
|
83
82
|
values = [];
|
|
84
|
-
constructor(
|
|
85
|
-
this.
|
|
83
|
+
constructor(connection) {
|
|
84
|
+
this.connection = connection;
|
|
86
85
|
}
|
|
87
86
|
async execute(sql) {
|
|
88
|
-
|
|
89
|
-
this.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
87
|
+
try {
|
|
88
|
+
const result = await this.connection.run(sql, this.values);
|
|
89
|
+
this.values = [];
|
|
90
|
+
return new DuckDBSqlResult(result);
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
this.values = [];
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
addParamInternal(val) {
|
|
98
|
+
if (val === undefined) {
|
|
99
|
+
this.values.push(null);
|
|
100
|
+
}
|
|
101
|
+
else if (val instanceof Date) {
|
|
102
|
+
this.values.push(val.toISOString());
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
this.values.push(val);
|
|
106
|
+
}
|
|
107
|
+
return '?';
|
|
96
108
|
}
|
|
97
109
|
addParameterAndReturnSqlToken(val) {
|
|
98
|
-
return this.
|
|
110
|
+
return this.addParamInternal(val);
|
|
99
111
|
}
|
|
100
112
|
param(val) {
|
|
101
|
-
|
|
102
|
-
this.values.push(val);
|
|
103
|
-
return key;
|
|
113
|
+
return this.addParamInternal(val);
|
|
104
114
|
}
|
|
105
115
|
}
|
|
106
116
|
class DuckDBSqlResult {
|
|
107
|
-
|
|
108
|
-
constructor(result) {
|
|
109
|
-
this.result = result;
|
|
110
|
-
this.rows = result;
|
|
111
|
-
}
|
|
117
|
+
duckDbResult;
|
|
112
118
|
rows;
|
|
119
|
+
constructor(duckDbResult) {
|
|
120
|
+
this.duckDbResult = duckDbResult;
|
|
121
|
+
this.rows = [];
|
|
122
|
+
const columnNames = this.duckDbResult.columnNames();
|
|
123
|
+
const columnTypes = this.duckDbResult.columnTypes();
|
|
124
|
+
const chunkCount = this.duckDbResult.chunkCount;
|
|
125
|
+
for (let i = 0; i < chunkCount; i++) {
|
|
126
|
+
const chunk = this.duckDbResult.getChunk(i);
|
|
127
|
+
const chunkRowObjects = chunk.getRowObjects(columnNames).map((row) => {
|
|
128
|
+
const convertedRow = {};
|
|
129
|
+
columnNames.forEach((colName, colIndex) => {
|
|
130
|
+
const value = row[colName];
|
|
131
|
+
if (value === null) {
|
|
132
|
+
convertedRow[colName] = null;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
const colType = columnTypes[colIndex];
|
|
136
|
+
const typeId = colType.typeId;
|
|
137
|
+
if (typeId === DuckDBTypeId.TIMESTAMP) {
|
|
138
|
+
const timestampValue = value;
|
|
139
|
+
const parts = timestampValue.toParts();
|
|
140
|
+
convertedRow[colName] = new Date(Date.UTC(parts.date.year, parts.date.month - 1, parts.date.day, parts.time.hour, parts.time.min, parts.time.sec, Math.floor(parts.time.micros / 1000)));
|
|
141
|
+
}
|
|
142
|
+
else if (typeId === DuckDBTypeId.DATE) {
|
|
143
|
+
const dateValue = value;
|
|
144
|
+
const parts = dateValue.toParts();
|
|
145
|
+
convertedRow[colName] = new Date(Date.UTC(parts.year, parts.month - 1, parts.day));
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
convertedRow[colName] = value;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
return convertedRow;
|
|
153
|
+
});
|
|
154
|
+
this.rows.push(...chunkRowObjects);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
113
157
|
getColumnKeyInResultForIndexInSelect(index) {
|
|
114
|
-
return
|
|
158
|
+
return this.duckDbResult.columnName(index);
|
|
115
159
|
}
|
|
116
160
|
}
|
package/esm/remult-hapi.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { createRemultServer, } from './server/index.js';
|
|
2
2
|
import { PassThrough } from 'stream';
|
|
3
|
-
export function
|
|
3
|
+
export function remultApi(options) {
|
|
4
4
|
const api = createRemultServer(options, {
|
|
5
5
|
buildGenericRequestInfo: (req) => ({
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
internal: {
|
|
7
|
+
...req,
|
|
8
|
+
url: req.url.pathname,
|
|
9
|
+
on: (e, do1) => {
|
|
10
|
+
req.raw.req.on('close', do1);
|
|
11
|
+
},
|
|
12
12
|
},
|
|
13
|
+
public: { headers: new Headers(req.headers) },
|
|
13
14
|
}),
|
|
14
15
|
getRequestBody: async (req) => req.payload,
|
|
15
16
|
});
|
|
@@ -33,7 +34,7 @@ export function remultHapi(options) {
|
|
|
33
34
|
res(h.response(html).code(status));
|
|
34
35
|
},
|
|
35
36
|
json(data) {
|
|
36
|
-
res(h.response(data).code(status));
|
|
37
|
+
res(h.response(data === null ? 'null' : data).code(status));
|
|
37
38
|
},
|
|
38
39
|
write(data) {
|
|
39
40
|
stream.write(data);
|
|
@@ -112,3 +113,5 @@ export function remultHapi(options) {
|
|
|
112
113
|
withRemult: (req, what) => api.withRemultAsync(req, what),
|
|
113
114
|
});
|
|
114
115
|
}
|
|
116
|
+
/** @deprecated use remultApi instead */
|
|
117
|
+
export const remultHapi = remultApi;
|
package/esm/remult-hono.js
CHANGED
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import { streamSSE } from 'hono/streaming';
|
|
3
3
|
import { createRemultServer, } from './server/index.js';
|
|
4
|
-
export function
|
|
4
|
+
export function remultApi(options) {
|
|
5
5
|
let app = new Hono();
|
|
6
6
|
const api = createRemultServer(options, {
|
|
7
7
|
buildGenericRequestInfo: (c) => {
|
|
8
8
|
return {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
internal: {
|
|
10
|
+
method: c.req.method,
|
|
11
|
+
params: c.req.param(),
|
|
12
|
+
query: new Proxy(c.req, {
|
|
13
|
+
get: (target, prop) => {
|
|
14
|
+
const r = c.req.queries(prop);
|
|
15
|
+
if (r?.length == 1)
|
|
16
|
+
return r[0];
|
|
17
|
+
return r;
|
|
18
|
+
},
|
|
19
|
+
}),
|
|
20
|
+
url: c.req.url,
|
|
21
|
+
on: (e, do1) => {
|
|
22
|
+
;
|
|
23
|
+
c['_tempOnClose'](() => do1());
|
|
17
24
|
},
|
|
18
|
-
}),
|
|
19
|
-
url: c.req.url,
|
|
20
|
-
on: (e, do1) => {
|
|
21
|
-
;
|
|
22
|
-
c['_tempOnClose'](() => do1());
|
|
23
|
-
// c.req.on('close', do1)
|
|
24
25
|
},
|
|
26
|
+
public: { headers: new Headers(c.req.header()) },
|
|
25
27
|
};
|
|
26
28
|
},
|
|
27
29
|
getRequestBody: async (c) => {
|
|
@@ -102,3 +104,5 @@ export function remultHono(options) {
|
|
|
102
104
|
withRemult: async (c, what) => api.withRemultAsync(c, what),
|
|
103
105
|
});
|
|
104
106
|
}
|
|
107
|
+
/** @deprecated use remultApi instead */
|
|
108
|
+
export const remultHono = remultApi;
|
package/esm/remult-nuxt.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { readBody, setResponseStatus } from 'h3';
|
|
2
2
|
import { createRemultServer } from './server/index.js';
|
|
3
|
-
export function
|
|
3
|
+
export function remultApi(options) {
|
|
4
4
|
const result = createRemultServer(options, {
|
|
5
|
-
buildGenericRequestInfo: (event) => {
|
|
6
|
-
|
|
5
|
+
buildGenericRequestInfo: (event) => ({
|
|
6
|
+
internal: {
|
|
7
7
|
method: event.node.req.method,
|
|
8
8
|
url: event.node.req.url,
|
|
9
9
|
on: (a, b) => event.node.req.on('close', () => {
|
|
10
10
|
b();
|
|
11
11
|
}),
|
|
12
|
-
}
|
|
13
|
-
|
|
12
|
+
},
|
|
13
|
+
public: {
|
|
14
|
+
headers: new Headers(event.node.req.headers),
|
|
15
|
+
},
|
|
16
|
+
}),
|
|
14
17
|
getRequestBody: async (event) => await readBody(event),
|
|
15
18
|
});
|
|
16
19
|
const handler = async (event) => {
|
|
@@ -41,7 +44,7 @@ export function remultNuxt(options) {
|
|
|
41
44
|
setResponseStatus(event, r.statusCode);
|
|
42
45
|
if (r.html)
|
|
43
46
|
return r.html;
|
|
44
|
-
return r.data;
|
|
47
|
+
return r.data == null ? 'null' : r.data;
|
|
45
48
|
}
|
|
46
49
|
};
|
|
47
50
|
return Object.assign(handler, {
|
|
@@ -52,3 +55,5 @@ export function remultNuxt(options) {
|
|
|
52
55
|
},
|
|
53
56
|
});
|
|
54
57
|
}
|
|
58
|
+
/** @deprecated use remultApi instead */
|
|
59
|
+
export const remultNuxt = remultApi;
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { getRequestEvent } from 'solid-js/web';
|
|
2
2
|
import { createRemultServer } from './server/index.js';
|
|
3
|
-
export function
|
|
3
|
+
export function remultApi(options) {
|
|
4
4
|
let result = createRemultServer(options, {
|
|
5
5
|
buildGenericRequestInfo: (event) => ({
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
internal: {
|
|
7
|
+
url: event.request.url,
|
|
8
|
+
method: event.request.method,
|
|
9
|
+
on: (e, do1) => {
|
|
10
|
+
if (e === 'close') {
|
|
11
|
+
event.locals['_tempOnClose'] = do1;
|
|
12
|
+
}
|
|
13
|
+
},
|
|
12
14
|
},
|
|
15
|
+
public: { headers: new Headers(event.request.headers) },
|
|
13
16
|
}),
|
|
14
17
|
getRequestBody: (event) => event.request.json(),
|
|
15
18
|
});
|
|
@@ -95,3 +98,5 @@ export function remultSolidStart(options) {
|
|
|
95
98
|
DELETE: serverHandler,
|
|
96
99
|
});
|
|
97
100
|
}
|
|
101
|
+
/** @deprecated use remultApi instead */
|
|
102
|
+
export const remultSolidStart = remultApi;
|
|
@@ -21,7 +21,7 @@ export class SqliteCoreDataProvider {
|
|
|
21
21
|
let self = this;
|
|
22
22
|
return {
|
|
23
23
|
createTable: async (entity) => {
|
|
24
|
-
|
|
24
|
+
await (await self.getCreateTableSql(entity)).map(builder.addSql);
|
|
25
25
|
},
|
|
26
26
|
addColumn: async (entity, field) => {
|
|
27
27
|
let e = await dbNamesOf(entity, this.wrapIdentifier);
|
|
@@ -45,6 +45,27 @@ export class SqliteCoreDataProvider {
|
|
|
45
45
|
async ensureSchema(entities) {
|
|
46
46
|
for (const entity of entities) {
|
|
47
47
|
await this.createTableIfNotExist(entity);
|
|
48
|
+
await this.verifyAllColumns(entity);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async verifyAllColumns(entity) {
|
|
52
|
+
try {
|
|
53
|
+
let cmd = this.createCommand();
|
|
54
|
+
let e = await dbNamesOf(entity, this.wrapIdentifier);
|
|
55
|
+
let cols = (await cmd.execute(`PRAGMA table_info(${e.$entityName})`)).rows.map((x) => this.wrapIdentifier(x.name.toLocaleLowerCase()));
|
|
56
|
+
for (const col of entity.fields) {
|
|
57
|
+
if (!shouldNotCreateField(col, e)) {
|
|
58
|
+
let colName = e.$dbNameOf(col).toLocaleLowerCase();
|
|
59
|
+
if (!cols.includes(colName)) {
|
|
60
|
+
let sql = `ALTER table ${e.$entityName} ` +
|
|
61
|
+
`add column ${this.addColumnSqlSyntax(col, e.$dbNameOf(col), true)}`;
|
|
62
|
+
await this.createCommand().execute(sql);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
console.error(err);
|
|
48
69
|
}
|
|
49
70
|
}
|
|
50
71
|
async dropTable(entity) {
|
|
@@ -73,9 +94,9 @@ export class SqliteCoreDataProvider {
|
|
|
73
94
|
}
|
|
74
95
|
async createTableIfNotExist(entity) {
|
|
75
96
|
let sql = await this.getCreateTableSql(entity);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
97
|
+
for (const element of sql) {
|
|
98
|
+
await this.createCommand().execute(element);
|
|
99
|
+
}
|
|
79
100
|
}
|
|
80
101
|
supportsJsonColumnType;
|
|
81
102
|
async getCreateTableSql(entity) {
|
|
@@ -99,7 +120,14 @@ export class SqliteCoreDataProvider {
|
|
|
99
120
|
}
|
|
100
121
|
}
|
|
101
122
|
}
|
|
102
|
-
let sql =
|
|
123
|
+
let sql = [
|
|
124
|
+
'create table if not exists ' + e.$entityName + ' (' + result + '\r\n)',
|
|
125
|
+
];
|
|
126
|
+
if (entity.idMetadata.fields.length > 1) {
|
|
127
|
+
sql.push(`create unique index if not exists ${this.wrapIdentifier(entity.dbName + '_primary_key')} on ${e.$entityName} (${entity.idMetadata.fields
|
|
128
|
+
.map((x) => e.$dbNameOf(x))
|
|
129
|
+
.join(',')})`);
|
|
130
|
+
}
|
|
103
131
|
return sql;
|
|
104
132
|
}
|
|
105
133
|
wrapIdentifier(name) {
|