@chidchanun/bcp 0.1.25 → 0.1.26

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,236 @@
1
+ // packages/client/src/database.ts
2
+ var MYSQL_MODULE_SPECIFIER = "mysql2/promise";
3
+ var BcpDatabase = class {
4
+ options;
5
+ poolPromise = null;
6
+ constructor(options = {}) {
7
+ this.options = {
8
+ ...options
9
+ };
10
+ }
11
+ async query(sql, parameters) {
12
+ assertSql(sql);
13
+ const pool = await this.getPool();
14
+ const [rows] = await pool.query(
15
+ sql,
16
+ parameters
17
+ );
18
+ return rows;
19
+ }
20
+ async execute(sql, parameters) {
21
+ assertSql(sql);
22
+ const pool = await this.getPool();
23
+ const [result] = await pool.execute(
24
+ sql,
25
+ parameters
26
+ );
27
+ return result;
28
+ }
29
+ async transaction(callback) {
30
+ if (typeof callback !== "function") {
31
+ throw new TypeError(
32
+ "BCP Database: transaction callback must be a function."
33
+ );
34
+ }
35
+ const pool = await this.getPool();
36
+ const connection = await pool.getConnection();
37
+ await connection.beginTransaction();
38
+ const transactionDatabase = {
39
+ query: async (sql, parameters) => {
40
+ assertSql(sql);
41
+ const [rows] = await connection.query(
42
+ sql,
43
+ parameters
44
+ );
45
+ return rows;
46
+ },
47
+ execute: async (sql, parameters) => {
48
+ assertSql(sql);
49
+ const [result] = await connection.execute(
50
+ sql,
51
+ parameters
52
+ );
53
+ return result;
54
+ }
55
+ };
56
+ try {
57
+ const result = await callback(
58
+ transactionDatabase
59
+ );
60
+ await connection.commit();
61
+ return result;
62
+ } catch (error) {
63
+ try {
64
+ await connection.rollback();
65
+ } catch {
66
+ }
67
+ throw error;
68
+ } finally {
69
+ connection.release();
70
+ }
71
+ }
72
+ async close() {
73
+ const pendingPool = this.poolPromise;
74
+ this.poolPromise = null;
75
+ if (!pendingPool) {
76
+ return;
77
+ }
78
+ const pool = await pendingPool;
79
+ await pool.end();
80
+ }
81
+ getPool() {
82
+ if (!this.poolPromise) {
83
+ this.poolPromise = createMysqlPool(
84
+ resolveDatabaseOptions(
85
+ this.options
86
+ )
87
+ );
88
+ }
89
+ return this.poolPromise;
90
+ }
91
+ };
92
+ function createDatabase(options = {}) {
93
+ return new BcpDatabase(
94
+ options
95
+ );
96
+ }
97
+ function resolveDatabaseOptions(options = {}, environment = process.env) {
98
+ const driver = options.driver ?? normalizeDriver(
99
+ environment.DB_DRIVER ?? "mysql"
100
+ );
101
+ if (driver !== "mysql") {
102
+ throw new Error(
103
+ `BCP Database: unsupported database driver "${driver}".`
104
+ );
105
+ }
106
+ return {
107
+ driver,
108
+ host: nonEmpty(
109
+ options.host ?? environment.DB_HOST
110
+ ) ?? "localhost",
111
+ port: resolvePositiveInteger(
112
+ options.port ?? environment.DB_PORT,
113
+ 3306,
114
+ "DB_PORT"
115
+ ),
116
+ user: nonEmpty(
117
+ options.user ?? environment.DB_USER
118
+ ) ?? "root",
119
+ password: String(
120
+ options.password ?? environment.DB_PASSWORD ?? ""
121
+ ),
122
+ database: nonEmpty(
123
+ options.database ?? environment.DB_NAME
124
+ ) ?? "bcp_app",
125
+ connectionLimit: resolvePositiveInteger(
126
+ options.connectionLimit ?? environment.DB_CONNECTION_LIMIT,
127
+ 10,
128
+ "DB_CONNECTION_LIMIT"
129
+ ),
130
+ waitForConnections: options.waitForConnections ?? resolveBoolean(
131
+ environment.DB_WAIT_FOR_CONNECTIONS,
132
+ true,
133
+ "DB_WAIT_FOR_CONNECTIONS"
134
+ ),
135
+ queueLimit: resolveNonNegativeInteger(
136
+ options.queueLimit ?? environment.DB_QUEUE_LIMIT,
137
+ 0,
138
+ "DB_QUEUE_LIMIT"
139
+ ),
140
+ charset: nonEmpty(
141
+ options.charset ?? environment.DB_CHARSET
142
+ ) ?? "utf8mb4"
143
+ };
144
+ }
145
+ var db = createDatabase();
146
+ async function createMysqlPool(options) {
147
+ let mysqlModule;
148
+ try {
149
+ mysqlModule = await import(MYSQL_MODULE_SPECIFIER);
150
+ } catch (error) {
151
+ throw new Error(
152
+ "BCP Database: MySQL requires the optional dependency mysql2. Install it with `npm install mysql2` or create the app with the MySQL preset.",
153
+ {
154
+ cause: error
155
+ }
156
+ );
157
+ }
158
+ return mysqlModule.createPool({
159
+ host: options.host,
160
+ port: options.port,
161
+ user: options.user,
162
+ password: options.password,
163
+ database: options.database,
164
+ waitForConnections: options.waitForConnections,
165
+ connectionLimit: options.connectionLimit,
166
+ queueLimit: options.queueLimit,
167
+ charset: options.charset
168
+ });
169
+ }
170
+ function normalizeDriver(value) {
171
+ const normalized = value.trim().toLowerCase();
172
+ if (normalized === "mysql") {
173
+ return "mysql";
174
+ }
175
+ throw new Error(
176
+ `BCP Database: unsupported database driver "${value}".`
177
+ );
178
+ }
179
+ function nonEmpty(value) {
180
+ if (typeof value !== "string") {
181
+ return null;
182
+ }
183
+ const normalized = value.trim();
184
+ return normalized || null;
185
+ }
186
+ function resolvePositiveInteger(value, fallback, label) {
187
+ if (value === void 0 || value === null || value === "") {
188
+ return fallback;
189
+ }
190
+ const parsed = Number(value);
191
+ if (!Number.isInteger(parsed) || parsed <= 0) {
192
+ throw new Error(
193
+ `BCP Database: ${label} must be a positive integer.`
194
+ );
195
+ }
196
+ return parsed;
197
+ }
198
+ function resolveNonNegativeInteger(value, fallback, label) {
199
+ if (value === void 0 || value === null || value === "") {
200
+ return fallback;
201
+ }
202
+ const parsed = Number(value);
203
+ if (!Number.isInteger(parsed) || parsed < 0) {
204
+ throw new Error(
205
+ `BCP Database: ${label} must be a non-negative integer.`
206
+ );
207
+ }
208
+ return parsed;
209
+ }
210
+ function resolveBoolean(value, fallback, label) {
211
+ if (value === void 0 || value === null || value === "") {
212
+ return fallback;
213
+ }
214
+ if (value === true || value === "1" || value === "true") {
215
+ return true;
216
+ }
217
+ if (value === false || value === "0" || value === "false") {
218
+ return false;
219
+ }
220
+ throw new Error(
221
+ `BCP Database: ${label} must be true/false or 1/0.`
222
+ );
223
+ }
224
+ function assertSql(sql) {
225
+ if (typeof sql !== "string" || sql.trim() === "") {
226
+ throw new TypeError(
227
+ "BCP Database: SQL must be a non-empty string."
228
+ );
229
+ }
230
+ }
231
+ export {
232
+ BcpDatabase,
233
+ createDatabase,
234
+ db,
235
+ resolveDatabaseOptions
236
+ };
@@ -46,23 +46,45 @@ export {
46
46
  type UploadErrorCode,
47
47
  } from "../../server/src/upload.js";
48
48
 
49
+ export {
50
+ storeMultipartFile,
51
+
52
+ type MultipartStorageFileInfo,
53
+ type MultipartStorageUploadOptions,
54
+ type StoredMultipartFile,
55
+ } from "../../server/src/upload-stream.js";
56
+
49
57
  export {
50
58
  createLocalStorage,
59
+ getStorageCapabilities,
51
60
  normalizeStorageKey,
61
+ putStorageStream,
62
+ readStorageStream,
52
63
  StorageError,
53
64
  storeUploadedFile,
54
65
 
55
66
  type LocalStorageOptions,
56
67
  type StorageAdapter,
68
+ type StorageAdapterCapabilities,
57
69
  type StorageErrorCode,
58
70
  type StorageObjectMetadata,
59
71
  type StoragePutOptions,
72
+ type StoragePutStreamOptions,
73
+ type StorageReadableStream,
60
74
  type StorageReadOptions,
61
75
  type StorageWriteValue,
62
76
  type StoredUploadedObject,
63
77
  type StoreUploadedFileOptions,
64
78
  } from "../../server/src/storage.js";
65
79
 
80
+ export {
81
+ createS3Storage,
82
+
83
+ type S3StorageAdapter,
84
+ type S3StorageMultipartOptions,
85
+ type S3StorageOptions,
86
+ } from "../../server/src/storage-s3.js";
87
+
66
88
  export {
67
89
  createStorageResponse,
68
90
  isStorageRangeError,
@@ -1,6 +1,7 @@
1
1
  import path from "node:path";
2
2
 
3
3
  import {
4
+ readStorageStream,
4
5
  StorageError,
5
6
  type StorageAdapter,
6
7
  type StorageObjectMetadata,
@@ -144,11 +145,10 @@ export async function createStorageResponse(
144
145
  const body =
145
146
  method === "HEAD"
146
147
  ? null
147
- : toResponseBody(
148
- await storage.read(
149
- key,
150
- range
151
- )
148
+ : await readStorageStream(
149
+ storage,
150
+ key,
151
+ range
152
152
  );
153
153
 
154
154
  return new Response(
@@ -171,10 +171,9 @@ export async function createStorageResponse(
171
171
  const body =
172
172
  method === "HEAD"
173
173
  ? null
174
- : toResponseBody(
175
- await storage.read(
176
- key
177
- )
174
+ : await readStorageStream(
175
+ storage,
176
+ key
178
177
  );
179
178
 
180
179
  return new Response(
@@ -602,21 +601,6 @@ function stripEntityLengthHeaders(
602
601
  );
603
602
  }
604
603
 
605
- function toResponseBody(
606
- bytes: Uint8Array
607
- ): ArrayBuffer {
608
- const body =
609
- new Uint8Array(
610
- bytes.byteLength
611
- );
612
-
613
- body.set(
614
- bytes
615
- );
616
-
617
- return body.buffer;
618
- }
619
-
620
604
  export function isStorageRangeError(
621
605
  error: unknown
622
606
  ): error is StorageError {