@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.
@@ -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
- await db.schema
13
- .alterTable('auth_users')
14
- .addColumn('firstname', 'text')
15
- .execute();
16
-
17
- await db.schema
18
- .alterTable('auth_users')
19
- .addColumn('lastname', 'text')
20
- .execute();
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
- await db.schema
29
- .alterTable('auth_users')
30
- .dropColumn('firstname')
31
- .execute();
32
-
33
- await db.schema
34
- .alterTable('auth_users')
35
- .dropColumn('lastname')
36
- .execute();
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storecraft/database-sql-base",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "Official SQL Database driver for storecraft",
5
5
  "license": "MIT",
6
6
  "author": "Tomer Shalev (https://github.com/store-craft)",
@@ -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 decode_if_base64 = val => {
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
- const b64 = val.split('base64_').at(1) ?? '';
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: decode_if_base64(item.template_html),
49
- template_text: decode_if_base64(item.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())