@storecraft/database-sql-base 1.0.14 → 1.0.16
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/kysely.aggregate.dialect.js +148 -0
- package/migrate.js +38 -2
- package/migrations.shared/00001_seed_email_templates.js +3 -3
- package/migrations.shared/00003_alter_auth_users.js +24 -18
- package/package.json +1 -1
- package/src/con.templates.js +39 -7
- package/tests/aggregate.js +38 -0
- package/tests/runner.mysql-local.test.js +3 -1
- package/tests/runner.postgres-local.test.js +3 -1
- package/tests/runner.sqlite-local.test.js +3 -1
@@ -0,0 +1,148 @@
|
|
1
|
+
/**
|
2
|
+
* @import { Driver, Dialect, DatabaseConnection, QueryResult } from 'kysely';
|
3
|
+
*/
|
4
|
+
import {
|
5
|
+
CompiledQuery,
|
6
|
+
Kysely,
|
7
|
+
} from 'kysely';
|
8
|
+
|
9
|
+
/**
|
10
|
+
* @typedef {object} Config
|
11
|
+
* @prop {Dialect} dialect
|
12
|
+
*/
|
13
|
+
|
14
|
+
/**
|
15
|
+
* @typedef {(query: CompiledQuery) => void} OnQueryInterface
|
16
|
+
*/
|
17
|
+
|
18
|
+
/**
|
19
|
+
* @description Official Storecraft Cloudflare D1 adapter on Worker
|
20
|
+
*
|
21
|
+
* @implements {Dialect}
|
22
|
+
*/
|
23
|
+
export class AggregateDialect {
|
24
|
+
|
25
|
+
/** @type {CompiledQuery[]} */
|
26
|
+
queries = [];
|
27
|
+
|
28
|
+
/** @param {Config} config */
|
29
|
+
constructor(config) {
|
30
|
+
this.config = config;
|
31
|
+
}
|
32
|
+
|
33
|
+
createAdapter() { return this.config.dialect.createAdapter?.(); }
|
34
|
+
createDriver() {
|
35
|
+
/** @type {OnQueryInterface} */
|
36
|
+
const onQuery = (query) => {
|
37
|
+
this.queries.push(query);
|
38
|
+
}
|
39
|
+
return new AggregateDriver(
|
40
|
+
this.config,
|
41
|
+
onQuery
|
42
|
+
);
|
43
|
+
}
|
44
|
+
createQueryCompiler() { return this.config.dialect.createQueryCompiler?.(); }
|
45
|
+
|
46
|
+
/**
|
47
|
+
* @param {Kysely<any>} db
|
48
|
+
*/
|
49
|
+
createIntrospector(db){
|
50
|
+
return this.config.dialect.createIntrospector?.(db);
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
|
55
|
+
/**
|
56
|
+
* @implements {Driver}
|
57
|
+
*/
|
58
|
+
class AggregateDriver {
|
59
|
+
|
60
|
+
/**
|
61
|
+
* @param {Config} config
|
62
|
+
* @param {OnQueryInterface} onQuery
|
63
|
+
*/
|
64
|
+
constructor(config, onQuery) {
|
65
|
+
this.onQuery = onQuery;
|
66
|
+
this.config = config;
|
67
|
+
}
|
68
|
+
|
69
|
+
async init() {}
|
70
|
+
|
71
|
+
async acquireConnection() {
|
72
|
+
return new AggregateConnection(
|
73
|
+
this.config,
|
74
|
+
this.onQuery
|
75
|
+
);
|
76
|
+
}
|
77
|
+
|
78
|
+
/**
|
79
|
+
* @param {AggregateConnection} conn
|
80
|
+
*/
|
81
|
+
beginTransaction(conn) { return conn.beginTransaction(); }
|
82
|
+
|
83
|
+
/**
|
84
|
+
* @param {AggregateConnection} conn
|
85
|
+
*/
|
86
|
+
commitTransaction(conn) {
|
87
|
+
return conn.commitTransaction();
|
88
|
+
}
|
89
|
+
|
90
|
+
/**
|
91
|
+
* @param {AggregateConnection} conn
|
92
|
+
*/
|
93
|
+
rollbackTransaction(conn){
|
94
|
+
return conn.rollbackTransaction();
|
95
|
+
}
|
96
|
+
|
97
|
+
/**
|
98
|
+
* @param {AggregateConnection} _conn
|
99
|
+
*/
|
100
|
+
async releaseConnection(_conn) {}
|
101
|
+
|
102
|
+
async destroy() {}
|
103
|
+
}
|
104
|
+
|
105
|
+
|
106
|
+
/**
|
107
|
+
* @implements {DatabaseConnection}
|
108
|
+
*/
|
109
|
+
class AggregateConnection {
|
110
|
+
|
111
|
+
/**
|
112
|
+
* @param {Config} config
|
113
|
+
* @param {OnQueryInterface} onQuery
|
114
|
+
*/
|
115
|
+
constructor(config, onQuery) {
|
116
|
+
this.config = config;
|
117
|
+
this.onQuery = onQuery;
|
118
|
+
}
|
119
|
+
|
120
|
+
/**
|
121
|
+
* @template R result type
|
122
|
+
* @param {CompiledQuery} compiledQuery
|
123
|
+
* @returns {Promise<QueryResult<R>>}
|
124
|
+
*/
|
125
|
+
async executeQuery(compiledQuery) {
|
126
|
+
this.onQuery(compiledQuery);
|
127
|
+
return Promise.resolve(
|
128
|
+
{
|
129
|
+
rows: []
|
130
|
+
}
|
131
|
+
)
|
132
|
+
}
|
133
|
+
|
134
|
+
async beginTransaction() {
|
135
|
+
}
|
136
|
+
|
137
|
+
async commitTransaction() {
|
138
|
+
}
|
139
|
+
|
140
|
+
async rollbackTransaction() {
|
141
|
+
return;
|
142
|
+
}
|
143
|
+
|
144
|
+
/** @type {DatabaseConnection["streamQuery"]} */
|
145
|
+
async *streamQuery(compiledQuery, chunkSize) {
|
146
|
+
throw new Error('D1 Driver does not support streaming');
|
147
|
+
}
|
148
|
+
}
|
package/migrate.js
CHANGED
@@ -1,9 +1,15 @@
|
|
1
|
+
/**
|
2
|
+
* @import { Database } from './types.sql.tables.js';
|
3
|
+
* @import { SqlDialectType } from './types.public.js';
|
4
|
+
* @import { Migration } from 'kysely';
|
5
|
+
*/
|
1
6
|
import { fileURLToPath } from "node:url";
|
2
7
|
import * as path from 'path'
|
3
8
|
import { promises as fs } from 'fs'
|
4
9
|
import {
|
5
10
|
Migrator,
|
6
11
|
FileMigrationProvider,
|
12
|
+
Kysely,
|
7
13
|
} from 'kysely'
|
8
14
|
import { SQL } from "./index.js";
|
9
15
|
|
@@ -14,6 +20,36 @@ export const current = {
|
|
14
20
|
driver: undefined
|
15
21
|
}
|
16
22
|
|
23
|
+
export const read_files_in_folder = async (folder='') => {
|
24
|
+
const files = await fs.readdir(folder);
|
25
|
+
return files.filter(file => file.endsWith('.js'));
|
26
|
+
}
|
27
|
+
|
28
|
+
/**
|
29
|
+
* @param {SqlDialectType} dialect_type
|
30
|
+
*/
|
31
|
+
export const get_migrations = async (dialect_type='SQLITE') => {
|
32
|
+
const folder = 'migrations.' + dialect_type.toLowerCase();
|
33
|
+
const files = await fs.readdir(path.join(__dirname, folder));
|
34
|
+
|
35
|
+
/** @type {Record<string, Migration>} */
|
36
|
+
const migrations = {};
|
37
|
+
|
38
|
+
for (const file of files) {
|
39
|
+
if(file.endsWith('.js')) {
|
40
|
+
const migration = await import(path.join(__dirname, folder, file));
|
41
|
+
migrations[file] = migration;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
return migrations;
|
46
|
+
}
|
47
|
+
|
48
|
+
console.log(
|
49
|
+
await get_migrations()
|
50
|
+
)
|
51
|
+
|
52
|
+
|
17
53
|
/**
|
18
54
|
*
|
19
55
|
* @param {SQL} db_driver
|
@@ -23,7 +59,7 @@ export async function migrateToLatest(db_driver, destroy_db_upon_completion=true
|
|
23
59
|
if(!db_driver?.client)
|
24
60
|
throw new Error('No Kysely client found !!!');
|
25
61
|
|
26
|
-
console.log('Resolving migrations')
|
62
|
+
console.log('Resolving migrations. This may take 2 minutes ...')
|
27
63
|
|
28
64
|
let db = db_driver.client;
|
29
65
|
|
@@ -63,7 +99,7 @@ export async function migrateToLatest(db_driver, destroy_db_upon_completion=true
|
|
63
99
|
|
64
100
|
if (error) {
|
65
101
|
console.error('failed to migrate')
|
66
|
-
console.error(error)
|
102
|
+
console.error(JSON.stringify(error, null, 2))
|
67
103
|
process.exit(1)
|
68
104
|
}
|
69
105
|
|
@@ -1,10 +1,10 @@
|
|
1
|
+
/**
|
2
|
+
* @import { Database } from '../types.sql.tables.js'
|
3
|
+
*/
|
1
4
|
import { Kysely } from 'kysely'
|
2
5
|
import { upsert } from '../src/con.templates.js'
|
3
6
|
import { templates } from '@storecraft/core/assets/seed-templates.js';
|
4
7
|
|
5
|
-
/**
|
6
|
-
* @typedef {import('../types.sql.tables.js').Database} Database
|
7
|
-
*/
|
8
8
|
|
9
9
|
/**
|
10
10
|
*
|
@@ -9,15 +9,18 @@ import { Kysely } from 'kysely'
|
|
9
9
|
*/
|
10
10
|
export async function up(db) {
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
12
|
+
try {
|
13
|
+
await db.schema
|
14
|
+
.alterTable('auth_users')
|
15
|
+
.addColumn('firstname', 'text')
|
16
|
+
.execute();
|
17
|
+
|
18
|
+
await db.schema
|
19
|
+
.alterTable('auth_users')
|
20
|
+
.addColumn('lastname', 'text')
|
21
|
+
.execute();
|
22
|
+
} catch (e) {
|
23
|
+
}
|
21
24
|
}
|
22
25
|
|
23
26
|
/**
|
@@ -25,13 +28,16 @@ export async function up(db) {
|
|
25
28
|
* @param {Kysely<Database>} db
|
26
29
|
*/
|
27
30
|
export async function down(db) {
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
31
|
+
try {
|
32
|
+
await db.schema
|
33
|
+
.alterTable('auth_users')
|
34
|
+
.dropColumn('firstname')
|
35
|
+
.execute();
|
36
|
+
|
37
|
+
await db.schema
|
38
|
+
.alterTable('auth_users')
|
39
|
+
.dropColumn('lastname')
|
40
|
+
.execute();
|
41
|
+
} catch (e) {
|
42
|
+
}
|
37
43
|
}
|
package/package.json
CHANGED
package/src/con.templates.js
CHANGED
@@ -18,12 +18,23 @@ export const table_name = 'templates'
|
|
18
18
|
* otherwise, just return the original value.
|
19
19
|
* @param {string} val
|
20
20
|
*/
|
21
|
-
const
|
21
|
+
const encode_base64_if_needed = val => {
|
22
|
+
if(val.startsWith('base64_'))
|
23
|
+
return val;
|
24
|
+
|
25
|
+
return 'base64_' + base64.encode(val);
|
26
|
+
}
|
27
|
+
|
28
|
+
/**
|
29
|
+
* @description if `base64_` prefixed then decode the postfix and return it,
|
30
|
+
* otherwise, just return the original value.
|
31
|
+
* @param {string} val
|
32
|
+
*/
|
33
|
+
const decode_base64_if_needed = val => {
|
22
34
|
if(!val.startsWith('base64_'))
|
23
35
|
return val;
|
24
36
|
|
25
|
-
|
26
|
-
return base64.decode(b64);
|
37
|
+
return base64.decode(val.split('base64_').at(-1) ?? '');
|
27
38
|
}
|
28
39
|
|
29
40
|
/**
|
@@ -45,8 +56,8 @@ export const upsert = (client) => {
|
|
45
56
|
active: item.active ? 1 : 0,
|
46
57
|
title: item.title,
|
47
58
|
handle: item.handle,
|
48
|
-
template_html:
|
49
|
-
template_text:
|
59
|
+
template_html: encode_base64_if_needed(item.template_html),
|
60
|
+
template_text: encode_base64_if_needed(item.template_text),
|
50
61
|
reference_example_input: JSON.stringify(item.reference_example_input ?? {})
|
51
62
|
});
|
52
63
|
}
|
@@ -78,7 +89,17 @@ const get = (driver) => {
|
|
78
89
|
)
|
79
90
|
.where(where_id_or_handle_table(id_or_handle))
|
80
91
|
.executeTakeFirst()
|
81
|
-
.then(sanitize)
|
92
|
+
.then(sanitize)
|
93
|
+
.then(
|
94
|
+
(item) => {
|
95
|
+
if(!item) return null;
|
96
|
+
|
97
|
+
item.template_html = decode_base64_if_needed(item.template_html);
|
98
|
+
item.template_text = decode_base64_if_needed(item.template_text);
|
99
|
+
|
100
|
+
return item;
|
101
|
+
}
|
102
|
+
)
|
82
103
|
}
|
83
104
|
}
|
84
105
|
|
@@ -134,7 +155,18 @@ const list = (driver) => {
|
|
134
155
|
)
|
135
156
|
.orderBy(query_to_sort(query, table_name))
|
136
157
|
.limit(query.limitToLast ?? query.limit ?? 10)
|
137
|
-
.execute()
|
158
|
+
.execute()
|
159
|
+
.then(
|
160
|
+
(items) => {
|
161
|
+
return items.map(
|
162
|
+
(item) => {
|
163
|
+
item.template_html = decode_base64_if_needed(item.template_html);
|
164
|
+
item.template_text = decode_base64_if_needed(item.template_text);
|
165
|
+
return item;
|
166
|
+
}
|
167
|
+
)
|
168
|
+
}
|
169
|
+
);
|
138
170
|
|
139
171
|
if(query.limitToLast) items.reverse();
|
140
172
|
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import 'dotenv/config';
|
2
|
+
import { App } from '@storecraft/core';
|
3
|
+
import { SQL } from '@storecraft/database-sql-base';
|
4
|
+
import { migrateToLatest } from '@storecraft/database-sql-base/migrate.js';
|
5
|
+
import { NodePlatform } from '@storecraft/core/platform/node';
|
6
|
+
import { api } from '@storecraft/core/test-runner'
|
7
|
+
import SQLite from 'better-sqlite3'
|
8
|
+
import { SqliteDialect } from 'kysely';
|
9
|
+
import { homedir } from 'node:os';
|
10
|
+
import { join } from 'node:path';
|
11
|
+
import { up } from '../migrations.sqlite/00000_init_tables.js'
|
12
|
+
import { AggregateDialect } from '../kysely.aggregate.dialect.js'
|
13
|
+
|
14
|
+
export const sqlite_dialect = new SqliteDialect(
|
15
|
+
{
|
16
|
+
database: async () => new SQLite(join(homedir(), 'db.sqlite')),
|
17
|
+
}
|
18
|
+
);
|
19
|
+
|
20
|
+
export const test = async () => {
|
21
|
+
const aggregate_dialect = new AggregateDialect(
|
22
|
+
{
|
23
|
+
dialect: sqlite_dialect,
|
24
|
+
}
|
25
|
+
);
|
26
|
+
|
27
|
+
const db = new SQL({
|
28
|
+
dialect: aggregate_dialect,
|
29
|
+
dialect_type: 'SQLITE'
|
30
|
+
});
|
31
|
+
|
32
|
+
await up(db.client);
|
33
|
+
|
34
|
+
const queries = aggregate_dialect.queries;
|
35
|
+
console.log({queries})
|
36
|
+
|
37
|
+
}
|
38
|
+
test();
|
@@ -22,7 +22,9 @@ export const create_app = async () => {
|
|
22
22
|
{
|
23
23
|
auth_admins_emails: ['admin@sc.com'],
|
24
24
|
auth_secret_access_token: 'auth_secret_access_token',
|
25
|
-
auth_secret_refresh_token: 'auth_secret_refresh_token'
|
25
|
+
auth_secret_refresh_token: 'auth_secret_refresh_token',
|
26
|
+
auth_secret_confirm_email_token: 'auth_secret_confirm_email_token',
|
27
|
+
auth_secret_forgot_password_token: 'auth_secret_forgot_password_token',
|
26
28
|
}
|
27
29
|
)
|
28
30
|
.withPlatform(new NodePlatform())
|
@@ -22,7 +22,9 @@ export const create_app = async () => {
|
|
22
22
|
{
|
23
23
|
auth_admins_emails: ['admin@sc.com'],
|
24
24
|
auth_secret_access_token: 'auth_secret_access_token',
|
25
|
-
auth_secret_refresh_token: 'auth_secret_refresh_token'
|
25
|
+
auth_secret_refresh_token: 'auth_secret_refresh_token',
|
26
|
+
auth_secret_confirm_email_token: 'auth_secret_confirm_email_token',
|
27
|
+
auth_secret_forgot_password_token: 'auth_secret_forgot_password_token',
|
26
28
|
}
|
27
29
|
)
|
28
30
|
.withPlatform(new NodePlatform())
|
@@ -18,7 +18,9 @@ export const create_app = async () => {
|
|
18
18
|
{
|
19
19
|
auth_admins_emails: ['admin@sc.com'],
|
20
20
|
auth_secret_access_token: 'auth_secret_access_token',
|
21
|
-
auth_secret_refresh_token: 'auth_secret_refresh_token'
|
21
|
+
auth_secret_refresh_token: 'auth_secret_refresh_token',
|
22
|
+
auth_secret_confirm_email_token: 'auth_secret_confirm_email_token',
|
23
|
+
auth_secret_forgot_password_token: 'auth_secret_forgot_password_token',
|
22
24
|
}
|
23
25
|
)
|
24
26
|
.withPlatform(new NodePlatform())
|