blackcat.js-database 1.0.6 → 1.0.7

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.d.mts CHANGED
@@ -82,6 +82,21 @@ interface DatabaseConfiguration {
82
82
  * (ví dụ: memory, file, json, mongo, redis...).
83
83
  */
84
84
  driver: DatabaseDriver;
85
+ /**
86
+ * Ký tự phân tách dùng để tách các phần của đường dẫn (key).
87
+ *
88
+ * @defaultValue "."
89
+ *
90
+ * @example
91
+ * ```ts
92
+ * // "user.profile.name"
93
+ * delimiter: "."
94
+ *
95
+ * // "user/profile/name"
96
+ * delimiter: "/"
97
+ * ```
98
+ */
99
+ delimiter?: string;
85
100
  /**
86
101
  * Bật chế độ debug.
87
102
  *
@@ -595,23 +610,64 @@ declare class Database<V = any> {
595
610
  */
596
611
  set<P extends AutocompletableString<ObjectPath<V>>>(key: AutocompletableString<P>, value: ObjectValue<V, P>): Promise<void>;
597
612
  /**
598
- * Xóa một key khỏi database.
613
+ * Đảm bảo một key tồn tại với giá trị mặc định.
614
+ * Nếu key đã tồn tại → giữ nguyên và trả về giá trị hiện tại.
615
+ * Nếu chưa tồn tại → set giá trị default và trả về default đó.
616
+ *
617
+ * Khác với `set()` — không bao giờ ghi đè dữ liệu đã có.
618
+ *
619
+ * @template P Key path trong database.
620
+ *
621
+ * @param key Đường dẫn cần đảm bảo tồn tại.
622
+ * @param defaultValue Giá trị mặc định nếu key chưa có.
623
+ *
624
+ * @returns Giá trị tại key sau khi ensure (current hoặc default).
625
+ *
626
+ * @example
627
+ * // "config.maxRetries" chưa tồn tại → set về 3, trả về 3
628
+ * const retries = await db.ensure("config.maxRetries", 3);
629
+ *
630
+ * @example
631
+ * // "config.maxRetries" đã là 10 → không đổi, trả về 10
632
+ * const retries = await db.ensure("config.maxRetries", 3);
599
633
  *
600
- * Hỗ trợ nested path bằng dot notation.
634
+ * @example
635
+ * // Dùng với object — đảm bảo cấu trúc user tồn tại
636
+ * const user = await db.ensure("users.123", {
637
+ * balance: 0,
638
+ * level: 1,
639
+ * joinedAt: Date.now(),
640
+ * });
641
+ */
642
+ ensure<P extends AutocompletableString<ObjectPath<V>>>(key: AutocompletableString<P>, defaultValue: ObjectValue<V, P>): Promise<NonNullable<ObjectValue<V, P>>>;
643
+ /**
644
+ * Xóa dữ liệu khỏi database.
645
+ * Hỗ trợ truy cập dữ liệu lồng nhau bằng dot notation.
646
+ * Nếu key không tồn tại hoặc đường dẫn không hợp lệ, hàm sẽ trả về `false`.
601
647
  *
602
648
  * @typeParam P - Path của object.
603
649
  *
604
- * @param key - Đường dẫn key cần xóa.
650
+ * @param key - Đường dẫn dữ liệu cần xóa.
605
651
  *
606
652
  * @returns
607
- * - `true` nếu xóa thành công
608
- * - `false` nếu key không tồn tại
653
+ * - `true` nếu xóa thành công.
654
+ * - `false` nếu key không tồn tại hoặc đường dẫn không hợp lệ.
609
655
  *
610
656
  * @throws BlackCatError
611
- * - INVALID_TYPE nếu key không phải string
657
+ * - INVALID_TYPE nếu `key` không phải là chuỗi.
612
658
  *
613
659
  * @example
660
+ * // Xóa một thuộc tính
614
661
  * await db.delete("user.name");
662
+ *
663
+ * @example
664
+ * // Xóa object lồng nhau
665
+ * await db.delete("users.0.profile");
666
+ *
667
+ * @example
668
+ * // Key không tồn tại
669
+ * const deleted = await db.delete("user.address");
670
+ * console.log(deleted); // false
615
671
  */
616
672
  delete<P extends AutocompletableString<ObjectPath<V>>>(key?: AutocompletableString<P>): Promise<boolean>;
617
673
  /**
@@ -670,25 +726,57 @@ declare class Database<V = any> {
670
726
  */
671
727
  deleteMany<P extends AutocompletableString<ObjectPath<V>>>(key: AutocompletableString<P>, query: Partial<ArrayElement<ObjectValue<V, P>>>): Promise<number>;
672
728
  /**
673
- * Cập nhật dữ liệu trong database theo đường dẫn key.
729
+ * Cập nhật dữ liệu trong database theo đường dẫn (`key`).
730
+ *
731
+ * ## Chức năng
732
+ * - Cập nhật giá trị tại một key hoặc nested key trong database.
733
+ * - Tự động tạo object trung gian nếu đường dẫn chưa tồn tại.
734
+ * - Hỗ trợ gộp dữ liệu (merge) đối với object và mảng nhằm hạn chế ghi đè dữ liệu đã có.
735
+ *
736
+ * ## Mô tả hoạt động
737
+ * Hàm sẽ đọc toàn bộ dữ liệu database, tìm đến vị trí tương ứng với `key`
738
+ * và cập nhật giá trị mới theo các quy tắc sau:
739
+ *
740
+ * - **Đối với Array**
741
+ * - Nếu cả giá trị hiện tại và giá trị mới đều là mảng thì sẽ **không ghi đè**.
742
+ * - Các phần tử mới sẽ được thêm vào cuối mảng.
743
+ * - Chỉ thêm những phần tử chưa tồn tại ví dụ dựa trên cặp thuộc tính:
744
+ * - `username`
745
+ * - `guild`
746
+ * - Điều này giúp tránh lưu trùng dữ liệu thành viên giữa nhiều lần cập nhật.
674
747
  *
675
- * Hàm này sẽ tìm đến vị trí của `key` trong database và gán `value` vào đó.
676
- * Nếu `value` giá trị hiện tại tại key đều là object thì sẽ thực hiện
677
- * **shallow merge** thay ghi đè toàn bộ object.
748
+ * - **Đối với Object**
749
+ * - Nếu cả hai đều là object sẽ thực hiện **shallow merge** (`...old, ...new`).
750
+ * - Các thuộc tính mới sẽ ghi đè thuộc tính cùng tên.
751
+ * - Những thuộc tính khác vẫn được giữ nguyên.
678
752
  *
679
- * Điều này giúp tránh việc làm mất các thuộc tính đã tồn tại.
753
+ * - **Các kiểu dữ liệu khác**
754
+ * - Giá trị mới sẽ ghi đè hoàn toàn giá trị cũ.
680
755
  *
681
- * @template P - Đường dẫn key hợp lệ trong object database.
756
+ * Sau khi cập nhật xong, toàn bộ database sẽ được ghi lại thông qua driver.
682
757
  *
683
- * @param key - Đường dẫn tới giá trị cần cập nhật.
684
- * Hỗ trợ nested path dạng `"a.b.c"`.
758
+ * @template P
759
+ * Đường dẫn hợp lệ của object trong database.
760
+ *
761
+ * @param key
762
+ * Đường dẫn cần cập nhật.
763
+ *
764
+ * Hỗ trợ nested path:
765
+ * ```ts
766
+ * "guilds.123.settings.prefix"
767
+ * "users.456.profile.name"
768
+ * ```
685
769
  *
686
- * @param value - Giá trị mới sẽ được cập nhật tại key.
770
+ * @param value
771
+ * Giá trị mới cần cập nhật.
687
772
  *
688
773
  * @returns
689
- * Trả về giá trị cuối cùng sau khi cập nhật.
774
+ * Trả về giá trị cuối cùng sau khi được lưu vào database.
775
+ *
776
+ * ## Cách sử dụng
777
+ *
778
+ * ### Ví dụ 1 - Cập nhật object
690
779
  *
691
- * @example
692
780
  * Database ban đầu:
693
781
  * ```json
694
782
  * {
@@ -700,10 +788,9 @@ declare class Database<V = any> {
700
788
  * }
701
789
  * ```
702
790
  *
703
- * Cập nhật:
704
791
  * ```ts
705
792
  * await db.update("123", {
706
- * history: [1,2,3]
793
+ * history: [1, 2, 3]
707
794
  * });
708
795
  * ```
709
796
  *
@@ -713,16 +800,211 @@ declare class Database<V = any> {
713
800
  * "123": {
714
801
  * "guildID": "123",
715
802
  * "guildName": "BlackCat",
716
- * "history": [1,2,3]
803
+ * "history": [1, 2, 3]
717
804
  * }
718
805
  * }
719
806
  * ```
720
807
  *
808
+ * ---
809
+ *
810
+ * ### Ví dụ 2 - Cập nhật nested key
811
+ *
812
+ * ```ts
813
+ * await db.update("guilds.123.settings.prefix", "!");
814
+ * ```
815
+ *
816
+ * Nếu đường dẫn chưa tồn tại:
817
+ *
818
+ * ```json
819
+ * {}
820
+ * ```
821
+ *
822
+ * Sau khi cập nhật:
823
+ *
824
+ * ```json
825
+ * {
826
+ * "guilds": {
827
+ * "123": {
828
+ * "settings": {
829
+ * "prefix": "!"
830
+ * }
831
+ * }
832
+ * }
833
+ * }
834
+ * ```
835
+ *
836
+ * ---
837
+ *
838
+ * ### Ví dụ 3 - Merge object
839
+ *
840
+ * Giá trị hiện tại:
841
+ *
842
+ * ```json
843
+ * {
844
+ * "name": "BlackCat",
845
+ * "language": "vi",
846
+ * "prefix": "!"
847
+ * }
848
+ * ```
849
+ *
850
+ * ```ts
851
+ * await db.update("config", {
852
+ * prefix: "?",
853
+ * color: "#5865F2"
854
+ * });
855
+ * ```
856
+ *
857
+ * Kết quả:
858
+ *
859
+ * ```json
860
+ * {
861
+ * "name": "BlackCat",
862
+ * "language": "vi",
863
+ * "prefix": "?",
864
+ * "color": "#5865F2"
865
+ * }
866
+ * ```
867
+ *
868
+ * ---
869
+ *
870
+ * ### Ví dụ 4 - Merge mảng và loại bỏ phần tử trùng
871
+ *
872
+ * Giá trị hiện tại:
873
+ *
874
+ * ```json
875
+ * [
876
+ * {
877
+ * "username": "Alice",
878
+ * "guild": "123"
879
+ * }
880
+ * ]
881
+ * ```
882
+ *
883
+ * ```ts
884
+ * await db.update("history", [
885
+ * {
886
+ * username: "Alice",
887
+ * guild: "123"
888
+ * },
889
+ * {
890
+ * username: "Bob",
891
+ * guild: "123"
892
+ * }
893
+ * ]);
894
+ * ```
895
+ *
896
+ * Kết quả:
897
+ *
898
+ * ```json
899
+ * [
900
+ * {
901
+ * "username": "Alice",
902
+ * "guild": "123"
903
+ * },
904
+ * {
905
+ * "username": "Bob",
906
+ * "guild": "123"
907
+ * }
908
+ * ]
909
+ * ```
910
+ *
911
+ * Phần tử `"Alice"` sẽ không được thêm lần nữa vì đã tồn tại với cùng
912
+ * `username` và `guild`.
913
+ *
721
914
  * @throws {BlackCatError}
722
- * - `REQUIRED_PARAMETER_MISSING` nếu key không được cung cấp
723
- * - `INVALID_TYPE` nếu key không phải string
915
+ * - `REQUIRED_PARAMETER_MISSING` nếu không truyền `key`.
916
+ * - `INVALID_TYPE` nếu `key` không phải kiểu `string`.
724
917
  */
725
918
  update<P extends AutocompletableString<ObjectPath<V>>>(key: AutocompletableString<P>, value: ObjectValue<V, P>): Promise<If<IsObject<V>, ObjectValue<V, P>>>;
919
+ /**
920
+ * ## Đồng bộ dữ liệu với cơ sở dữ liệu
921
+ *
922
+ * ### Chức năng
923
+ * Đồng bộ dữ liệu tại một đường dẫn (`keys`) với giá trị mới (`value`).
924
+ *
925
+ * Hàm sẽ tự động:
926
+ * - Tạo mới dữ liệu nếu chưa tồn tại.
927
+ * - So sánh dữ liệu cũ và mới.
928
+ * - Chỉ cập nhật các trường thực sự thay đổi đối với Object.
929
+ * - Không thực hiện ghi nếu dữ liệu không thay đổi.
930
+ *
931
+ * Điều này giúp giảm số lần ghi xuống cơ sở dữ liệu và tối ưu hiệu năng.
932
+ *
933
+ * ---
934
+ *
935
+ * ### Cơ chế hoạt động
936
+ *
937
+ * 1. Lấy dữ liệu hiện tại bằng `get()`.
938
+ * 2. Nếu chưa tồn tại (`undefined` hoặc `null`)
939
+ * → gọi `set()` để tạo mới.
940
+ * 3. Nếu truyền hàm `compare` và hàm này trả về `true`
941
+ * → bỏ qua đồng bộ.
942
+ * 4. Nếu dữ liệu không phải Object (hoặc là Array)
943
+ * → so sánh bằng `Object.is()`, nếu khác thì gọi `update()`.
944
+ * 5. Nếu dữ liệu là Object
945
+ * → chỉ lấy các thuộc tính thay đổi và cập nhật đúng các trường đó.
946
+ * 6. Nếu không có trường nào thay đổi thì không ghi dữ liệu.
947
+ *
948
+ * ---
949
+ *
950
+ * ### Giá trị trả về
951
+ *
952
+ * - `undefined`
953
+ * - Khi tạo mới dữ liệu.
954
+ * - Khi không có thay đổi.
955
+ * - Khi dữ liệu nguyên thủy được cập nhật.
956
+ * - `Partial<ObjectValue<V, P>>`
957
+ * - Trả về object chỉ chứa các trường đã được cập nhật khi đồng bộ Object.
958
+ *
959
+ * ---
960
+ *
961
+ * ### Ví dụ
962
+ *
963
+ * ```ts
964
+ * // Tạo mới nếu chưa tồn tại
965
+ * await db.syncDatabase({
966
+ * keys: "users.1",
967
+ * value: {
968
+ * name: "BlackCat",
969
+ * age: 20
970
+ * }
971
+ * });
972
+ *
973
+ * // Chỉ cập nhật trường age
974
+ * await db.syncDatabase({
975
+ * keys: "users.1",
976
+ * value: {
977
+ * name: "BlackCat",
978
+ * age: 21
979
+ * }
980
+ * });
981
+ *
982
+ * // Sử dụng compare để bỏ qua đồng bộ
983
+ * await db.syncDatabase({
984
+ * keys: "users.1",
985
+ * value: newData,
986
+ * compare(oldData, newData) {
987
+ * return oldData.version >= newData.version;
988
+ * }
989
+ * });
990
+ * ```
991
+ *
992
+ * @template P Đường dẫn tới dữ liệu trong cơ sở dữ liệu.
993
+ *
994
+ * @param options Thông tin đồng bộ.
995
+ * @param options.keys Đường dẫn của dữ liệu cần đồng bộ.
996
+ * @param options.value Giá trị mới cần ghi.
997
+ * @param options.compare Hàm so sánh tùy chỉnh. Nếu trả về `true` thì sẽ không thực hiện đồng bộ.
998
+ *
999
+ * @returns
1000
+ * - `undefined` nếu không có thay đổi hoặc dữ liệu được tạo mới.
1001
+ * - `Partial<ObjectValue<V, P>>` chứa các trường đã thay đổi khi cập nhật Object.
1002
+ */
1003
+ syncDatabase<P extends AutocompletableString<ObjectPath<V>>>({ keys, value, compare }: {
1004
+ keys: AutocompletableString<P>;
1005
+ value: ObjectValue<V, P>;
1006
+ compare?: (oldData: ObjectValue<V, P>, newData: ObjectValue<V, P>) => boolean;
1007
+ }): Promise<Partial<ObjectValue<V, P>> | undefined>;
726
1008
  /**
727
1009
  * Cộng thêm giá trị vào một key dạng number.
728
1010
  *
@@ -759,6 +1041,22 @@ declare class Database<V = any> {
759
1041
  * ```
760
1042
  */
761
1043
  subtract<P extends AutocompletableString<ObjectPath<V>>>(key: AutocompletableString<P>, numberToSubtract: number): Promise<number>;
1044
+ /**
1045
+ * Thêm một hoặc nhiều phần tử vào đầu array.
1046
+ *
1047
+ * Chỉ thêm những phần tử chưa tồn tại trong array.
1048
+ *
1049
+ * @typeParam P - Path trỏ tới array.
1050
+ *
1051
+ * @param key - Đường dẫn array.
1052
+ * @param values - Một hoặc nhiều phần tử cần thêm.
1053
+ *
1054
+ * @returns Array sau khi thêm.
1055
+ *
1056
+ * @example
1057
+ * await db.unshift("users", newUser);
1058
+ */
1059
+ unshift<P extends ObjectPath<V>>(key: AutocompletableString<P>, ...values: RestOrArray<ExtractFromArray<ObjectValue<V, P>>>): Promise<ExtractFromArray<ObjectValue<V, P>>[]>;
762
1060
  /**
763
1061
  * Thêm một hoặc nhiều phần tử vào cuối array tại key-path.
764
1062
  *