@pdg/drizzle-mysql 1.0.2 → 1.0.4

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/dist/index.cjs CHANGED
@@ -211,15 +211,28 @@ for (const [method, policy] of Object.entries({
211
211
  });
212
212
  }
213
213
  const tablePolicies = new WeakMap();
214
+ const tableMutationPolicies = new WeakMap();
214
215
  function getColumnPolicies(table) {
215
216
  return tablePolicies.get(table) ?? {};
216
217
  }
217
- function mysqlTable(name, columns, extraConfig) {
218
+ function getTablePolicy(table) {
219
+ return tableMutationPolicies.get(table) ?? {};
220
+ }
221
+ function mysqlTable(name, columns, extraConfigOrPolicy, policy) {
222
+ const extraConfig = typeof extraConfigOrPolicy === 'function' ? extraConfigOrPolicy : undefined;
223
+ const mutationPolicy = typeof extraConfigOrPolicy === 'object' ? extraConfigOrPolicy : policy;
224
+ const nativeExtraConfig = extraConfig
225
+ ? (self) => {
226
+ const config = extraConfig(self);
227
+ return Array.isArray(config) ? config : Object.values(config);
228
+ }
229
+ : undefined;
218
230
  let builders = {};
219
231
  const table = typeof columns === 'function'
220
- ? mysqlCore.mysqlTable(name, (types) => (builders = columns(types)), extraConfig)
221
- : mysqlCore.mysqlTable(name, (builders = columns), extraConfig);
232
+ ? mysqlCore.mysqlTable(name, (types) => (builders = columns(types)), nativeExtraConfig)
233
+ : mysqlCore.mysqlTable(name, (builders = columns), nativeExtraConfig);
222
234
  tablePolicies.set(table, Object.fromEntries(Object.entries(builders).map(([key, builder]) => [key, policies.get(builder) ?? {}])));
235
+ tableMutationPolicies.set(table, { ...mutationPolicy });
223
236
  return table;
224
237
  }/********************************************************************************************************************
225
238
  * MySqlQuery
@@ -406,18 +419,25 @@ class MySqlQuery {
406
419
  const auto = 'autoIncrement' in column && column.autoIncrement === true;
407
420
  const rule = operation === 'insert' ? policy.insert : policy.update;
408
421
  const primary = column.primary;
409
- const blocked = !!policy.timestamp ||
422
+ const blocked = (operation === 'update' && policy.timestamp === 'created') ||
410
423
  rule === 'blocked' ||
411
424
  (operation === 'update' && primary && rule !== 'allowed') ||
412
425
  (auto && rule !== 'allowed' && rule !== 'required');
413
426
  if (blocked && Object.hasOwn(values, key))
414
427
  throw new Error(`${operation}: column "${key}" is blocked.`);
415
- if (operation === 'update' && !blocked && rule === 'required' && values[key] === undefined) {
428
+ if (operation === 'update' &&
429
+ !blocked &&
430
+ rule === 'required' &&
431
+ policy.timestamp !== 'updated' &&
432
+ values[key] === undefined) {
416
433
  throw new Error(`update: column "${key}" is required.`);
417
434
  }
418
- if (policy.timestamp && (operation === 'insert' || policy.timestamp === 'updated')) {
435
+ if (policy.timestamp && values[key] === undefined && (operation === 'insert' || policy.timestamp === 'updated')) {
419
436
  if (!generated) {
420
- timestamp = this.getTimestamp();
437
+ const getTimestamp = this.getTimestamp ?? this.current.getTimestamp;
438
+ if (!getTimestamp)
439
+ throw new Error('timestamp generator is not configured');
440
+ timestamp = getTimestamp();
421
441
  generated = true;
422
442
  }
423
443
  values[key] = timestamp;
@@ -426,9 +446,9 @@ class MySqlQuery {
426
446
  return values;
427
447
  }
428
448
  /********************************************************************************************************************
429
- * (PRIVATE) insertValues
449
+ * (PRIVATE) executeInsert
430
450
  * ******************************************************************************************************************/
431
- async insertValues(value) {
451
+ async executeInsert(value) {
432
452
  const tx = this.getTx();
433
453
  const autoIncrementColumn = mysqlCore.getTableConfig(this.t).columns.find((column) => 'autoIncrement' in column && column.autoIncrement === true);
434
454
  const hasAutoIncrement = !!autoIncrementColumn;
@@ -455,15 +475,25 @@ class MySqlQuery {
455
475
  }
456
476
  }
457
477
  }
478
+ insertValues(values) {
479
+ return values;
480
+ }
481
+ /********************************************************************************************************************
482
+ * updateValues
483
+ * ******************************************************************************************************************/
484
+ updateValues(values) {
485
+ return values;
486
+ }
458
487
  add(value) {
459
488
  const items = Array.isArray(value) ? value : [value];
460
489
  const values = items.map((item) => this.prepareValues(item, 'insert'));
461
- return this.insertValues((Array.isArray(value) ? values : values[0]));
490
+ return this.executeInsert((Array.isArray(value) ? values : values[0]));
462
491
  }
463
492
  /********************************************************************************************************************
464
493
  * edit
465
494
  * ******************************************************************************************************************/
466
495
  edit(set, where) {
496
+ this.assertMutationAllowed('update', 'edit');
467
497
  const condition = typeof where === 'function' ? where(this) : where;
468
498
  if (!native.is(condition, native.SQL)) {
469
499
  throw new Error('edit: a SQL where condition is required.');
@@ -478,12 +508,14 @@ class MySqlQuery {
478
508
  * editByPrimaryKey
479
509
  * ******************************************************************************************************************/
480
510
  editByPrimaryKey(set, key) {
511
+ this.assertMutationAllowed('update', 'editByPrimaryKey');
481
512
  return this.edit(set, this.primaryKeyCondition(key, 'editByPrimaryKey'));
482
513
  }
483
514
  /********************************************************************************************************************
484
515
  * remove
485
516
  * ******************************************************************************************************************/
486
517
  remove(where) {
518
+ this.assertMutationAllowed('delete', 'remove');
487
519
  const condition = typeof where === 'function' ? where(this) : where;
488
520
  if (!native.is(condition, native.SQL)) {
489
521
  throw new Error('remove: a SQL where condition is required.');
@@ -495,8 +527,15 @@ class MySqlQuery {
495
527
  * removeByPrimaryKey
496
528
  * ******************************************************************************************************************/
497
529
  removeByPrimaryKey(key) {
530
+ this.assertMutationAllowed('delete', 'removeByPrimaryKey');
498
531
  return this.remove(this.primaryKeyCondition(key, 'removeByPrimaryKey'));
499
532
  }
533
+ assertMutationAllowed(operation, method) {
534
+ const policy = getTablePolicy(this.t);
535
+ if (operation === 'update' ? policy.updateBlocked : policy.deleteBlocked) {
536
+ throw new Error(`${method}: ${operation} is blocked for table "${mysqlCore.getTableConfig(this.t).name}".`);
537
+ }
538
+ }
500
539
  /********************************************************************************************************************
501
540
  * eq
502
541
  * ******************************************************************************************************************/
@@ -623,7 +662,7 @@ const normalizeFields = (fields) => {
623
662
  const pool = mysql.createPool({ uri: options.url });
624
663
  const raw = mysql2.drizzle({ client: pool, relations: native.defineRelations(options.schema) });
625
664
  const context = new node_async_hooks.AsyncLocalStorage();
626
- const current = () => context.getStore() ?? raw;
665
+ const current = Object.assign(() => context.getStore() ?? raw, { getTimestamp: options.getTimestamp });
627
666
  const trans = (callback) => {
628
667
  const parent = context.getStore();
629
668
  const execute = (tx) => context.run(tx, callback);
@@ -676,11 +715,7 @@ const normalizeFields = (fields) => {
676
715
  sqlString: (value) => (native.is(value, native.SQL) ? value : native.sql `${value}`).mapWith(String),
677
716
  sqlBoolean: (value) => (native.is(value, native.SQL) ? value : native.sql `${value}`).mapWith((v) => v === true || v === 1),
678
717
  current,
679
- query: (table) => new MySqlQuery(table, current, () => {
680
- if (!options.timestamp)
681
- throw new Error('timestamp generator is not configured');
682
- return options.timestamp();
683
- }),
718
+ query: (table) => new MySqlQuery(table, current),
684
719
  trans,
685
720
  paginate,
686
721
  close: () => pool.end(),
package/dist/index.d.cts CHANGED
@@ -2,7 +2,7 @@ import { drizzleMySql } from './drizzleMySql.cjs';
2
2
  export default drizzleMySql;
3
3
  export * from './@types/index.cjs';
4
4
  export * from './columns.cjs';
5
- export { mysqlTable } from './mysqlTable.cjs';
5
+ export { mysqlTable, type TablePolicy } from './mysqlTable.cjs';
6
6
  export * from './conditions.cjs';
7
7
  export * from './drizzleMySql.cjs';
8
8
  export * from './MySqlQuery.cjs';
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import { drizzleMySql } from './drizzleMySql.js';
2
2
  export default drizzleMySql;
3
3
  export * from './@types/index.js';
4
4
  export * from './columns.js';
5
- export { mysqlTable } from './mysqlTable.js';
5
+ export { mysqlTable, type TablePolicy } from './mysqlTable.js';
6
6
  export * from './conditions.js';
7
7
  export * from './drizzleMySql.js';
8
8
  export * from './MySqlQuery.js';
package/dist/index.esm.js CHANGED
@@ -211,15 +211,28 @@ for (const [method, policy] of Object.entries({
211
211
  });
212
212
  }
213
213
  const tablePolicies = new WeakMap();
214
+ const tableMutationPolicies = new WeakMap();
214
215
  function getColumnPolicies(table) {
215
216
  return tablePolicies.get(table) ?? {};
216
217
  }
217
- function mysqlTable(name, columns, extraConfig) {
218
+ function getTablePolicy(table) {
219
+ return tableMutationPolicies.get(table) ?? {};
220
+ }
221
+ function mysqlTable(name, columns, extraConfigOrPolicy, policy) {
222
+ const extraConfig = typeof extraConfigOrPolicy === 'function' ? extraConfigOrPolicy : undefined;
223
+ const mutationPolicy = typeof extraConfigOrPolicy === 'object' ? extraConfigOrPolicy : policy;
224
+ const nativeExtraConfig = extraConfig
225
+ ? (self) => {
226
+ const config = extraConfig(self);
227
+ return Array.isArray(config) ? config : Object.values(config);
228
+ }
229
+ : undefined;
218
230
  let builders = {};
219
231
  const table = typeof columns === 'function'
220
- ? mysqlTable$1(name, (types) => (builders = columns(types)), extraConfig)
221
- : mysqlTable$1(name, (builders = columns), extraConfig);
232
+ ? mysqlTable$1(name, (types) => (builders = columns(types)), nativeExtraConfig)
233
+ : mysqlTable$1(name, (builders = columns), nativeExtraConfig);
222
234
  tablePolicies.set(table, Object.fromEntries(Object.entries(builders).map(([key, builder]) => [key, policies.get(builder) ?? {}])));
235
+ tableMutationPolicies.set(table, { ...mutationPolicy });
223
236
  return table;
224
237
  }/********************************************************************************************************************
225
238
  * MySqlQuery
@@ -406,18 +419,25 @@ class MySqlQuery {
406
419
  const auto = 'autoIncrement' in column && column.autoIncrement === true;
407
420
  const rule = operation === 'insert' ? policy.insert : policy.update;
408
421
  const primary = column.primary;
409
- const blocked = !!policy.timestamp ||
422
+ const blocked = (operation === 'update' && policy.timestamp === 'created') ||
410
423
  rule === 'blocked' ||
411
424
  (operation === 'update' && primary && rule !== 'allowed') ||
412
425
  (auto && rule !== 'allowed' && rule !== 'required');
413
426
  if (blocked && Object.hasOwn(values, key))
414
427
  throw new Error(`${operation}: column "${key}" is blocked.`);
415
- if (operation === 'update' && !blocked && rule === 'required' && values[key] === undefined) {
428
+ if (operation === 'update' &&
429
+ !blocked &&
430
+ rule === 'required' &&
431
+ policy.timestamp !== 'updated' &&
432
+ values[key] === undefined) {
416
433
  throw new Error(`update: column "${key}" is required.`);
417
434
  }
418
- if (policy.timestamp && (operation === 'insert' || policy.timestamp === 'updated')) {
435
+ if (policy.timestamp && values[key] === undefined && (operation === 'insert' || policy.timestamp === 'updated')) {
419
436
  if (!generated) {
420
- timestamp = this.getTimestamp();
437
+ const getTimestamp = this.getTimestamp ?? this.current.getTimestamp;
438
+ if (!getTimestamp)
439
+ throw new Error('timestamp generator is not configured');
440
+ timestamp = getTimestamp();
421
441
  generated = true;
422
442
  }
423
443
  values[key] = timestamp;
@@ -426,9 +446,9 @@ class MySqlQuery {
426
446
  return values;
427
447
  }
428
448
  /********************************************************************************************************************
429
- * (PRIVATE) insertValues
449
+ * (PRIVATE) executeInsert
430
450
  * ******************************************************************************************************************/
431
- async insertValues(value) {
451
+ async executeInsert(value) {
432
452
  const tx = this.getTx();
433
453
  const autoIncrementColumn = getTableConfig(this.t).columns.find((column) => 'autoIncrement' in column && column.autoIncrement === true);
434
454
  const hasAutoIncrement = !!autoIncrementColumn;
@@ -455,15 +475,25 @@ class MySqlQuery {
455
475
  }
456
476
  }
457
477
  }
478
+ insertValues(values) {
479
+ return values;
480
+ }
481
+ /********************************************************************************************************************
482
+ * updateValues
483
+ * ******************************************************************************************************************/
484
+ updateValues(values) {
485
+ return values;
486
+ }
458
487
  add(value) {
459
488
  const items = Array.isArray(value) ? value : [value];
460
489
  const values = items.map((item) => this.prepareValues(item, 'insert'));
461
- return this.insertValues((Array.isArray(value) ? values : values[0]));
490
+ return this.executeInsert((Array.isArray(value) ? values : values[0]));
462
491
  }
463
492
  /********************************************************************************************************************
464
493
  * edit
465
494
  * ******************************************************************************************************************/
466
495
  edit(set, where) {
496
+ this.assertMutationAllowed('update', 'edit');
467
497
  const condition = typeof where === 'function' ? where(this) : where;
468
498
  if (!is(condition, SQL)) {
469
499
  throw new Error('edit: a SQL where condition is required.');
@@ -478,12 +508,14 @@ class MySqlQuery {
478
508
  * editByPrimaryKey
479
509
  * ******************************************************************************************************************/
480
510
  editByPrimaryKey(set, key) {
511
+ this.assertMutationAllowed('update', 'editByPrimaryKey');
481
512
  return this.edit(set, this.primaryKeyCondition(key, 'editByPrimaryKey'));
482
513
  }
483
514
  /********************************************************************************************************************
484
515
  * remove
485
516
  * ******************************************************************************************************************/
486
517
  remove(where) {
518
+ this.assertMutationAllowed('delete', 'remove');
487
519
  const condition = typeof where === 'function' ? where(this) : where;
488
520
  if (!is(condition, SQL)) {
489
521
  throw new Error('remove: a SQL where condition is required.');
@@ -495,8 +527,15 @@ class MySqlQuery {
495
527
  * removeByPrimaryKey
496
528
  * ******************************************************************************************************************/
497
529
  removeByPrimaryKey(key) {
530
+ this.assertMutationAllowed('delete', 'removeByPrimaryKey');
498
531
  return this.remove(this.primaryKeyCondition(key, 'removeByPrimaryKey'));
499
532
  }
533
+ assertMutationAllowed(operation, method) {
534
+ const policy = getTablePolicy(this.t);
535
+ if (operation === 'update' ? policy.updateBlocked : policy.deleteBlocked) {
536
+ throw new Error(`${method}: ${operation} is blocked for table "${getTableConfig(this.t).name}".`);
537
+ }
538
+ }
500
539
  /********************************************************************************************************************
501
540
  * eq
502
541
  * ******************************************************************************************************************/
@@ -623,7 +662,7 @@ const normalizeFields = (fields) => {
623
662
  const pool = mysql.createPool({ uri: options.url });
624
663
  const raw = drizzle({ client: pool, relations: defineRelations(options.schema) });
625
664
  const context = new AsyncLocalStorage();
626
- const current = () => context.getStore() ?? raw;
665
+ const current = Object.assign(() => context.getStore() ?? raw, { getTimestamp: options.getTimestamp });
627
666
  const trans = (callback) => {
628
667
  const parent = context.getStore();
629
668
  const execute = (tx) => context.run(tx, callback);
@@ -676,11 +715,7 @@ const normalizeFields = (fields) => {
676
715
  sqlString: (value) => (is(value, SQL) ? value : sql `${value}`).mapWith(String),
677
716
  sqlBoolean: (value) => (is(value, SQL) ? value : sql `${value}`).mapWith((v) => v === true || v === 1),
678
717
  current,
679
- query: (table) => new MySqlQuery(table, current, () => {
680
- if (!options.timestamp)
681
- throw new Error('timestamp generator is not configured');
682
- return options.timestamp();
683
- }),
718
+ query: (table) => new MySqlQuery(table, current),
684
719
  trans,
685
720
  paginate,
686
721
  close: () => pool.end(),
@@ -8,8 +8,13 @@ export type ColumnPolicy = {
8
8
  update?: 'blocked' | 'allowed' | 'required';
9
9
  timestamp?: 'created' | 'updated';
10
10
  };
11
+ export type TablePolicy = {
12
+ updateBlocked?: boolean;
13
+ deleteBlocked?: boolean;
14
+ };
11
15
  declare const policyType: unique symbol;
12
16
  declare const tablePolicyType: unique symbol;
17
+ declare const tableMutationPolicyType: unique symbol;
13
18
  type Mark<T, P extends ColumnPolicy> = T & {
14
19
  readonly [policyType]: P;
15
20
  };
@@ -46,7 +51,10 @@ type Policies<C> = {
46
51
  export type TablePolicies<T> = T extends {
47
52
  readonly [tablePolicyType]: infer P;
48
53
  } ? P : {};
49
- type TableResult<N extends string, C extends Record<string, ColumnBuilderBase>> = MySqlTableWithColumns<{
54
+ export type TableMutationPolicy<T> = T extends {
55
+ readonly [tableMutationPolicyType]: infer P;
56
+ } ? P : {};
57
+ type TableResult<N extends string, C extends Record<string, ColumnBuilderBase>, P extends TablePolicy> = MySqlTableWithColumns<{
50
58
  name: N;
51
59
  schema: undefined;
52
60
  columns: BuildColumns<N, C, 'mysql'>;
@@ -54,7 +62,10 @@ type TableResult<N extends string, C extends Record<string, ColumnBuilderBase>>
54
62
  isAlias: false;
55
63
  }> & {
56
64
  readonly [tablePolicyType]: Policies<C>;
65
+ readonly [tableMutationPolicyType]: P;
57
66
  };
58
67
  export declare function getColumnPolicies(table: MySqlTable): Record<string, ColumnPolicy>;
59
- export declare function mysqlTable<N extends string, C extends Record<string, ColumnBuilderBase>>(name: N, columns: C | ((types: MySqlColumnBuilders) => C), extraConfig?: (self: BuildColumns<N, C, 'mysql'>) => MySqlTableExtraConfig | (MySqlTableExtraConfigValue | MySqlTableExtraConfigValue[])[]): TableResult<N, C>;
68
+ export declare function getTablePolicy(table: MySqlTable): TablePolicy;
69
+ type ExtraConfig<N extends string, C extends Record<string, ColumnBuilderBase>> = (self: BuildColumns<N, C, 'mysql'>) => MySqlTableExtraConfig | (MySqlTableExtraConfigValue | MySqlTableExtraConfigValue[])[];
70
+ export declare function mysqlTable<N extends string, C extends Record<string, ColumnBuilderBase>, const P extends TablePolicy = {}, const Q extends TablePolicy = {}>(name: N, columns: C | ((types: MySqlColumnBuilders) => C), extraConfigOrPolicy?: ExtraConfig<N, C> | (P & TablePolicy), policy?: Q & TablePolicy): TableResult<N, C, P & Q>;
60
71
  export {};
@@ -8,8 +8,13 @@ export type ColumnPolicy = {
8
8
  update?: 'blocked' | 'allowed' | 'required';
9
9
  timestamp?: 'created' | 'updated';
10
10
  };
11
+ export type TablePolicy = {
12
+ updateBlocked?: boolean;
13
+ deleteBlocked?: boolean;
14
+ };
11
15
  declare const policyType: unique symbol;
12
16
  declare const tablePolicyType: unique symbol;
17
+ declare const tableMutationPolicyType: unique symbol;
13
18
  type Mark<T, P extends ColumnPolicy> = T & {
14
19
  readonly [policyType]: P;
15
20
  };
@@ -46,7 +51,10 @@ type Policies<C> = {
46
51
  export type TablePolicies<T> = T extends {
47
52
  readonly [tablePolicyType]: infer P;
48
53
  } ? P : {};
49
- type TableResult<N extends string, C extends Record<string, ColumnBuilderBase>> = MySqlTableWithColumns<{
54
+ export type TableMutationPolicy<T> = T extends {
55
+ readonly [tableMutationPolicyType]: infer P;
56
+ } ? P : {};
57
+ type TableResult<N extends string, C extends Record<string, ColumnBuilderBase>, P extends TablePolicy> = MySqlTableWithColumns<{
50
58
  name: N;
51
59
  schema: undefined;
52
60
  columns: BuildColumns<N, C, 'mysql'>;
@@ -54,7 +62,10 @@ type TableResult<N extends string, C extends Record<string, ColumnBuilderBase>>
54
62
  isAlias: false;
55
63
  }> & {
56
64
  readonly [tablePolicyType]: Policies<C>;
65
+ readonly [tableMutationPolicyType]: P;
57
66
  };
58
67
  export declare function getColumnPolicies(table: MySqlTable): Record<string, ColumnPolicy>;
59
- export declare function mysqlTable<N extends string, C extends Record<string, ColumnBuilderBase>>(name: N, columns: C | ((types: MySqlColumnBuilders) => C), extraConfig?: (self: BuildColumns<N, C, 'mysql'>) => MySqlTableExtraConfig | (MySqlTableExtraConfigValue | MySqlTableExtraConfigValue[])[]): TableResult<N, C>;
68
+ export declare function getTablePolicy(table: MySqlTable): TablePolicy;
69
+ type ExtraConfig<N extends string, C extends Record<string, ColumnBuilderBase>> = (self: BuildColumns<N, C, 'mysql'>) => MySqlTableExtraConfig | (MySqlTableExtraConfigValue | MySqlTableExtraConfigValue[])[];
70
+ export declare function mysqlTable<N extends string, C extends Record<string, ColumnBuilderBase>, const P extends TablePolicy = {}, const Q extends TablePolicy = {}>(name: N, columns: C | ((types: MySqlColumnBuilders) => C), extraConfigOrPolicy?: ExtraConfig<N, C> | (P & TablePolicy), policy?: Q & TablePolicy): TableResult<N, C, P & Q>;
60
71
  export {};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@pdg/drizzle-mysql",
3
3
  "title": "Drizzle for MySql",
4
4
  "description": "Drizzle for MySql",
5
- "version": "1.0.2",
5
+ "version": "1.0.4",
6
6
  "type": "module",
7
7
  "main": "./dist/index.cjs",
8
8
  "module": "./dist/index.esm.js",